Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions apps/desktop/src/main/ai/logging/task-log-writer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,48 @@
import { writeFileSync, readFileSync, existsSync, mkdirSync, renameSync } from 'node:fs';
import { join, dirname } from 'node:path';
import type { TaskLogs, TaskLogPhase, TaskLogPhaseStatus, TaskLogEntry, TaskLogEntryType } from '../../../shared/types';
import type { StreamEvent } from '../session/types';
import type { StreamEvent, SessionError } from '../session/types';
import type { Phase } from '../config/types';

// =============================================================================
// Error formatting
// =============================================================================

function sanitizeErrorMessage(message: string): string {
return message
.replace(/sk-[a-zA-Z0-9-_]{20,}/g, 'sk-***')
.replace(/Bearer [a-zA-Z0-9\-_.+/=]+/gi, 'Bearer ***')
.replace(/token[=:]\s*[a-zA-Z0-9\-_.+/=]+/gi, 'token=***');
}

function extractErrorMessage(error: SessionError): string {
const direct = error?.message?.trim();
if (direct) return sanitizeErrorMessage(direct);

const cause = error?.cause as unknown;
if (cause instanceof Error && cause.message?.trim()) {
return sanitizeErrorMessage(cause.message);
}

if (typeof cause === 'string' && cause.trim()) {
return sanitizeErrorMessage(cause);
}

if (cause && typeof cause === 'object') {
const maybeMessage = (cause as { message?: unknown }).message;
if (typeof maybeMessage === 'string' && maybeMessage.trim()) {
return sanitizeErrorMessage(maybeMessage);
}
const maybeError = (cause as { error?: unknown }).error;
if (typeof maybeError === 'string' && maybeError.trim()) {
return sanitizeErrorMessage(maybeError);
}
}
Comment on lines +48 to +57
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While this approach works, using type assertions like (cause as { message?: unknown }) can be brittle. A more robust and type-safe way to check for properties on an unknown or object type is to use the in operator. This avoids unsafe casting and makes the code's intent clearer.

  if (cause && typeof cause === 'object') {
    if ('message' in cause && typeof cause.message === 'string' && cause.message.trim()) {
      return sanitizeErrorMessage(cause.message);
    }
    if ('error' in cause && typeof cause.error === 'string' && cause.error.trim()) {
      return sanitizeErrorMessage(cause.error);
    }
  }


if (error?.code) return `Error: ${error.code}`;
return 'Unknown error';
}
Comment on lines +35 to +61
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add focused regression tests for extraction and redaction branches.

Given this is a bug fix for lost diagnostics, please add tests for blank message with: cause as Error, string, object { message }, object { error }, plus token redaction cases to prevent regressions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/ai/logging/task-log-writer.ts` around lines 35 - 61,
Add focused unit tests for extractErrorMessage to cover every extraction branch
and redaction: create tests that pass a SessionError with an empty/blank message
and (1) cause as an Error whose message should be returned via
sanitizeErrorMessage, (2) cause as a string, (3) cause as an object with a
message property, (4) cause as an object with an error property, and (5) where
message and cause are blank but error.code is present (expect "Error: <code>").
Also add tests verifying sanitizeErrorMessage redacts tokens and sensitive
strings in returned messages; reference the extractErrorMessage function and
sanitizeErrorMessage to locate the code under test. Ensure each test asserts the
exact sanitized output for regressions.


// =============================================================================
// Phase Mapping
// =============================================================================
Expand Down Expand Up @@ -147,7 +186,7 @@ export class TaskLogWriter {

case 'error':
this.flushPendingText();
this.addEntry(logPhase, 'error', event.error.message);
this.addEntry(logPhase, 'error', extractErrorMessage(event.error));
this.save();
break;

Expand Down
Loading