* Serialize a single transcript block as a JSONL dict line: `{"Bash":"ls"}` * for tool calls, `{"user":"text"}` for user text. The tool value is the * per-tool `toAutoClassifierInput` projection. JSON escaping means hostile * content can't break out of its string context to forge a `{"user":...}`
( block: TranscriptBlock, role: TranscriptEntry['role'], lookup: ToolLookup, )
| 382 | * Returns '' for tool_use blocks whose tool encodes to ''. |
| 383 | */ |
| 384 | function toCompactBlock( |
| 385 | block: TranscriptBlock, |
| 386 | role: TranscriptEntry['role'], |
| 387 | lookup: ToolLookup, |
| 388 | ): string { |
| 389 | if (block.type === 'tool_use') { |
| 390 | const tool = lookup.get(block.name) |
| 391 | if (!tool) return '' |
| 392 | const input = (block.input ?? {}) as Record<string, unknown> |
| 393 | // block.input is unvalidated model output from history — a tool_use rejected |
| 394 | // for bad params (e.g. array emitted as JSON string) still lands in the |
| 395 | // transcript and would crash toAutoClassifierInput when it assumes z.infer<Input>. |
| 396 | // On throw or undefined, fall back to the raw input object — it gets |
| 397 | // single-encoded in the jsonStringify wrap below (no double-encode). |
| 398 | let encoded: unknown |
| 399 | try { |
| 400 | encoded = tool.toAutoClassifierInput(input) ?? input |
| 401 | } catch (e) { |
| 402 | logForDebugging( |
| 403 | `toAutoClassifierInput failed for ${block.name}: ${errorMessage(e)}`, |
| 404 | ) |
| 405 | logEvent('tengu_auto_mode_malformed_tool_input', { |
| 406 | toolName: |
| 407 | block.name as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, |
| 408 | }) |
| 409 | encoded = input |
| 410 | } |
| 411 | if (encoded === '') return '' |
| 412 | if (isJsonlTranscriptEnabled()) { |
| 413 | return jsonStringify({ [block.name]: encoded }) + '\n' |
| 414 | } |
| 415 | const s = typeof encoded === 'string' ? encoded : jsonStringify(encoded) |
| 416 | return `${block.name} ${s}\n` |
| 417 | } |
| 418 | if (block.type === 'text' && role === 'user') { |
| 419 | return isJsonlTranscriptEnabled() |
| 420 | ? jsonStringify({ user: block.text }) + '\n' |
| 421 | : `User: ${block.text}\n` |
| 422 | } |
| 423 | return '' |
| 424 | } |
| 425 | |
| 426 | function toCompact(entry: TranscriptEntry, lookup: ToolLookup): string { |
| 427 | return entry.content.map(b => toCompactBlock(b, entry.role, lookup)).join('') |
no test coverage detected