| ... | @@ -972,6 +972,52 @@ function sdkNativeMethod(message: SDKMessage): string { | ... | @@ -972,6 +972,52 @@ function sdkNativeMethod(message: SDKMessage): string { |
| 972 | return `claude/${message.type}`; | 972 | return `claude/${message.type}`; |
| 973 | } | 973 | } |
| 974 | | 974 | |
| | 975 | // Discriminator/identity keys carry no human-readable content; everything else |
| | 976 | // on an unmodeled SDK message is potentially worth surfacing in the work log. |
| | 977 | const SDK_MESSAGE_NOISE_KEYS = new Set([ |
| | 978 | "type", |
| | 979 | "subtype", |
| | 980 | "uuid", |
| | 981 | "parent_uuid", |
| | 982 | "session_id", |
| | 983 | "parent_tool_use_id", |
| | 984 | "request_id", |
| | 985 | ]); |
| | 986 | |
| | 987 | // Pull the salient scalar content out of a message the adapter doesn't model |
| | 988 | // yet, so the work-log row shows what actually arrived (e.g. a notification's |
| | 989 | // text) instead of an opaque "unhandled subtype" placeholder. Nested structures |
| | 990 | // are left to the full payload retained in the event's `detail`. |
| | 991 | function previewUnknownSdkContent(message: unknown): string | undefined { |
| | 992 | if (!message || typeof message !== "object") { |
| | 993 | return undefined; |
| | 994 | } |
| | 995 | const parts: string[] = []; |
| | 996 | for (const [key, value] of Object.entries(message as Record<string, unknown>)) { |
| | 997 | if (SDK_MESSAGE_NOISE_KEYS.has(key)) { |
| | 998 | continue; |
| | 999 | } |
| | 1000 | if (typeof value === "string") { |
| | 1001 | const trimmed = value.trim(); |
| | 1002 | if (trimmed.length > 0) { |
| | 1003 | parts.push(`${key}: ${trimmed}`); |
| | 1004 | } |
| | 1005 | } else if (typeof value === "number" || typeof value === "boolean") { |
| | 1006 | parts.push(`${key}: ${String(value)}`); |
| | 1007 | } |
| | 1008 | } |
| | 1009 | if (parts.length === 0) { |
| | 1010 | return undefined; |
| | 1011 | } |
| | 1012 | const joined = parts.join(" · "); |
| | 1013 | return joined.length > 280 ? `${joined.slice(0, 279)}…` : joined; |
| | 1014 | } |
| | 1015 | |
| | 1016 | function describeUnknownSdkMessage(kind: string, message: unknown): string { |
| | 1017 | const preview = previewUnknownSdkContent(message); |
| | 1018 | return preview ? `${kind} — ${preview}` : `${kind} (no displayable text content)`; |
| | 1019 | } |
| | 1020 | |
| 975 | function sdkNativeItemId(message: SDKMessage): string | undefined { | 1021 | function sdkNativeItemId(message: SDKMessage): string | undefined { |
| 976 | if (message.type === "assistant") { | 1022 | if (message.type === "assistant") { |
| 977 | const maybeId = (message.message as { id?: unknown }).id; | 1023 | const maybeId = (message.message as { id?: unknown }).id; |
| ... | @@ -2291,7 +2337,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* ( | ... | @@ -2291,7 +2337,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* ( |
| 2291 | default: | 2337 | default: |
| 2292 | yield* emitRuntimeWarning( | 2338 | yield* emitRuntimeWarning( |
| 2293 | context, | 2339 | context, |
| 2294 | `Unhandled Claude system message subtype '${message.subtype}'.`, | 2340 | describeUnknownSdkMessage(`Claude system message '${message.subtype}'`, message), |
| 2295 | message, | 2341 | message, |
| 2296 | ); | 2342 | ); |
| 2297 | return; | 2343 | return; |
| ... | @@ -2405,7 +2451,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* ( | ... | @@ -2405,7 +2451,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* ( |
| 2405 | default: | 2451 | default: |
| 2406 | yield* emitRuntimeWarning( | 2452 | yield* emitRuntimeWarning( |
| 2407 | context, | 2453 | context, |
| 2408 | `Unhandled Claude SDK message type '${message.type}'.`, | 2454 | describeUnknownSdkMessage(`Claude SDK message '${message.type}'`, message), |
| 2409 | message, | 2455 | message, |
| 2410 | ); | 2456 | ); |
| 2411 | return; | 2457 | return; |