zidane 5.3.0 → 5.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +2 -0
  2. package/dist/{agent-CYpPKn5Z.d.ts → agent-BXRCCHeq.d.ts} +557 -5
  3. package/dist/agent-BXRCCHeq.d.ts.map +1 -0
  4. package/dist/chat.d.ts +310 -6
  5. package/dist/chat.d.ts.map +1 -1
  6. package/dist/chat.js +2 -2
  7. package/dist/{errors-COmsomd5.js → errors-Byb0F8B9.js} +44 -2
  8. package/dist/errors-Byb0F8B9.js.map +1 -0
  9. package/dist/{index-D-cTScN3.d.ts → index-BPk8-Slm.d.ts} +81 -10
  10. package/dist/index-BPk8-Slm.d.ts.map +1 -0
  11. package/dist/{index-Cc-q1hLT.d.ts → index-CT5_p-3P.d.ts} +2 -2
  12. package/dist/{index-Cc-q1hLT.d.ts.map → index-CT5_p-3P.d.ts.map} +1 -1
  13. package/dist/index.d.ts +4 -4
  14. package/dist/index.js +10 -10
  15. package/dist/{interpolate-BhmHKD6x.js → interpolate-ERgZUxgg.js} +2 -2
  16. package/dist/{interpolate-BhmHKD6x.js.map → interpolate-ERgZUxgg.js.map} +1 -1
  17. package/dist/{login-BXVt5wuA.js → login-DrBZ15G7.js} +3 -3
  18. package/dist/{login-BXVt5wuA.js.map → login-DrBZ15G7.js.map} +1 -1
  19. package/dist/{mcp-B1psg7jf.js → mcp-DhmmJfxK.js} +16 -3
  20. package/dist/mcp-DhmmJfxK.js.map +1 -0
  21. package/dist/mcp.d.ts +1 -1
  22. package/dist/mcp.js +1 -1
  23. package/dist/{messages-DsbMYNmt.js → messages-D0xT979U.js} +631 -68
  24. package/dist/messages-D0xT979U.js.map +1 -0
  25. package/dist/{presets-tvD28pCu.js → presets-0_IRJAYF.js} +29 -10
  26. package/dist/presets-0_IRJAYF.js.map +1 -0
  27. package/dist/presets.d.ts +2 -2
  28. package/dist/presets.js +1 -1
  29. package/dist/{providers-v1Rn2rqG.js → providers-x3LZByR5.js} +38 -6
  30. package/dist/providers-x3LZByR5.js.map +1 -0
  31. package/dist/providers.d.ts +2 -2
  32. package/dist/providers.js +3 -3
  33. package/dist/session/sqlite.d.ts +1 -1
  34. package/dist/session/sqlite.js +1 -1
  35. package/dist/{session-DOJgRXvF.js → session-BHZwxmfr.js} +2 -2
  36. package/dist/{session-DOJgRXvF.js.map → session-BHZwxmfr.js.map} +1 -1
  37. package/dist/session.d.ts +1 -1
  38. package/dist/session.js +2 -2
  39. package/dist/skills.d.ts +2 -2
  40. package/dist/skills.js +1 -1
  41. package/dist/{tools-CMVruxF0.js → tools-CCsL5SCO.js} +516 -140
  42. package/dist/tools-CCsL5SCO.js.map +1 -0
  43. package/dist/tools.d.ts +3 -3
  44. package/dist/tools.js +2 -2
  45. package/dist/{transcript-anchors-eyhlGeBI.d.ts → transcript-anchors-DSk8LlWt.d.ts} +28 -4
  46. package/dist/transcript-anchors-DSk8LlWt.d.ts.map +1 -0
  47. package/dist/tui.d.ts +29 -3
  48. package/dist/tui.d.ts.map +1 -1
  49. package/dist/tui.js +365 -80
  50. package/dist/tui.js.map +1 -1
  51. package/dist/{turn-operations-Y7e15gJf.js → turn-operations-CutZin8X.js} +678 -33
  52. package/dist/turn-operations-CutZin8X.js.map +1 -0
  53. package/dist/types-IcokUOyC.js.map +1 -1
  54. package/dist/types.d.ts +2 -2
  55. package/dist/types.js +1 -1
  56. package/docs/ARCHITECTURE.md +1 -1
  57. package/docs/SKILL.md +23 -1
  58. package/package.json +1 -1
  59. package/dist/agent-CYpPKn5Z.d.ts.map +0 -1
  60. package/dist/errors-COmsomd5.js.map +0 -1
  61. package/dist/index-D-cTScN3.d.ts.map +0 -1
  62. package/dist/mcp-B1psg7jf.js.map +0 -1
  63. package/dist/messages-DsbMYNmt.js.map +0 -1
  64. package/dist/presets-tvD28pCu.js.map +0 -1
  65. package/dist/providers-v1Rn2rqG.js.map +0 -1
  66. package/dist/tools-CMVruxF0.js.map +0 -1
  67. package/dist/transcript-anchors-eyhlGeBI.d.ts.map +0 -1
  68. package/dist/turn-operations-Y7e15gJf.js.map +0 -1
@@ -14,7 +14,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
14
14
  * unclassified errors in `AgentProviderError` automatically.
15
15
  */
16
16
  /** Kind of classified provider error */
17
- type ClassifiedErrorKind = 'context_exceeded' | 'provider_error' | 'aborted';
17
+ type ClassifiedErrorKind = 'context_exceeded' | 'provider_error' | 'aborted' | 'tool_pairing_corruption';
18
18
  /** Structured classification returned by `Provider.classifyError` */
19
19
  interface ClassifiedError {
20
20
  kind: ClassifiedErrorKind;
@@ -75,6 +75,49 @@ declare class AgentAbortedError extends Error {
75
75
  cause?: unknown;
76
76
  });
77
77
  }
78
+ /**
79
+ * Thrown by the pre-send pairing repair when {@link AgentBehavior.strictToolPairing}
80
+ * is `true` and {@link ensureToolResultPairing} would otherwise patch over
81
+ * corruption. Strict mode opts into fail-fast for training-data collectors and
82
+ * any consumer that prefers to bail out rather than ship a transcript carrying
83
+ * a {@link SYNTHETIC_TOOL_RESULT_PLACEHOLDER}.
84
+ *
85
+ * The `repairs` field carries every repair the loop would have made — useful
86
+ * for postmortems and for surfacing exactly which transcripts were rejected.
87
+ *
88
+ * Note: callers consuming the chat layer (TUI/SDK presets) typically leave
89
+ * `strictToolPairing` off so user-facing sessions auto-heal instead of
90
+ * crashing.
91
+ */
92
+ declare class AgentToolPairingError extends Error {
93
+ readonly code: "tool_pairing_corruption";
94
+ /** Provider whose wire format the corruption would have hit. */
95
+ readonly provider?: string;
96
+ /** Upstream error code when the corruption was detected from a 400 response. */
97
+ readonly providerCode?: string;
98
+ /**
99
+ * Repairs the harness would have performed had strict mode been off.
100
+ * Preserves the full diagnostic so consumers can route to telemetry,
101
+ * `/rewind` flows, or training-data quarantine without re-walking the
102
+ * transcript.
103
+ */
104
+ readonly repairs: ReadonlyArray<{
105
+ mode: string;
106
+ callId?: string;
107
+ messageIndex: number;
108
+ }>;
109
+ constructor(options: {
110
+ message: string;
111
+ provider?: string;
112
+ providerCode?: string;
113
+ repairs: ReadonlyArray<{
114
+ mode: string;
115
+ callId?: string;
116
+ messageIndex: number;
117
+ }>;
118
+ cause?: unknown;
119
+ });
120
+ }
78
121
  /**
79
122
  * Thrown (well — constructed; attach via the `tool:gate` block signal) when the
80
123
  * union of `allowed-tools` across active skills does not permit a tool call.
@@ -123,7 +166,7 @@ declare function errorMessage(err: unknown): string;
123
166
  /**
124
167
  * Convert a `ClassifiedError` + underlying cause into the matching typed error instance.
125
168
  */
126
- declare function toTypedError(classification: ClassifiedError, provider: string, cause: unknown): AgentContextExceededError | AgentProviderError | AgentAbortedError;
169
+ declare function toTypedError(classification: ClassifiedError, provider: string, cause: unknown): AgentContextExceededError | AgentProviderError | AgentAbortedError | AgentToolPairingError;
127
170
  //#endregion
128
171
  //#region src/types.d.ts
129
172
  /**
@@ -437,7 +480,7 @@ interface AgentBehavior {
437
480
  *
438
481
  * ```ts
439
482
  * // Always cache by full input — every identical re-call dedups.
440
- * dedupTools: { todowrite: input => JSON.stringify(input) }
483
+ * dedupTools: { my_pure_tool: input => JSON.stringify(input) }
441
484
  *
442
485
  * // Cache by a normalized subset; non-cacheable shapes opt out.
443
486
  * dedupTools: {
@@ -655,6 +698,26 @@ interface AgentBehavior {
655
698
  * Default: `undefined`.
656
699
  */
657
700
  persistDir?: string;
701
+ /**
702
+ * Fail-fast instead of repair when the pre-send pairing pass detects
703
+ * corruption (orphan `tool_use` / `tool_result`, duplicate ids,
704
+ * compaction-stranded blocks). Throws {@link AgentToolPairingError} from
705
+ * the next `agent.run()` turn carrying the structured repair list the
706
+ * loop would have performed.
707
+ *
708
+ * Use case: training-data collectors that must reject any transcript
709
+ * containing the synthetic `SYNTHETIC_TOOL_RESULT_PLACEHOLDER` rather
710
+ * than ship poisoned data to the fine-tuning pipeline. User-facing chat
711
+ * sessions should leave this off (the repair-on-the-fly behavior is the
712
+ * point of the pass).
713
+ *
714
+ * Telemetry note: `pairing:repair` still fires for every repair before
715
+ * the throw, so observability handlers see exactly what would have
716
+ * happened.
717
+ *
718
+ * Default: `false`.
719
+ */
720
+ strictToolPairing?: boolean;
658
721
  }
659
722
  /**
660
723
  * One block of a multimodal user prompt.
@@ -1040,6 +1103,28 @@ interface ToolHookContext {
1040
1103
  /** Aliased (wire) name — equal to `name` when no alias is defined. */
1041
1104
  displayName: string;
1042
1105
  input: Record<string, unknown>;
1106
+ /**
1107
+ * The run this tool call belongs to (the `SessionRun.id`). Lets a single
1108
+ * `tool:*` listener disambiguate calls across parallel runs / subagent
1109
+ * trees without subscribing to the `child:tool:*` bubble events.
1110
+ */
1111
+ runId?: string;
1112
+ /**
1113
+ * Parent run id when this tool call's agent is a subagent — i.e. the
1114
+ * `SessionRun.parentRunId` of the run that owns the call. Absent on
1115
+ * top-level runs. Useful for observability stitching: a UI grouping
1116
+ * subagent-scoped state (e.g. todowrite, edit batches) by parent
1117
+ * run can read this directly off `tool:before` / `tool:after`
1118
+ * without resolving the run row.
1119
+ */
1120
+ parentRunId?: string;
1121
+ /**
1122
+ * Subagent depth for this tool call. 0 = top-level, 1 = first-level
1123
+ * child, etc. Mirrors `ToolContext.depth` so hook consumers don't
1124
+ * have to cross-reference the tool context. Omitted on top-level
1125
+ * runs (treated as 0).
1126
+ */
1127
+ depth?: number;
1043
1128
  }
1044
1129
  /**
1045
1130
  * Base context for MCP tool hooks.
@@ -1059,6 +1144,12 @@ interface McpToolHookContext {
1059
1144
  /** Aliased wire name for this MCP tool, or the canonical `mcp_{server}_{tool}` name. */
1060
1145
  displayName: string;
1061
1146
  input: Record<string, unknown>;
1147
+ /** Owning run id — same semantics as `ToolHookContext.runId`. */
1148
+ runId?: string;
1149
+ /** Parent run id when this tool call's agent is a subagent — see `ToolHookContext.parentRunId`. */
1150
+ parentRunId?: string;
1151
+ /** Subagent depth — see `ToolHookContext.depth`. */
1152
+ depth?: number;
1062
1153
  }
1063
1154
  /** Base context for session hooks */
1064
1155
  interface SessionHookContext {
@@ -1357,6 +1448,100 @@ interface OpenRouterParams {
1357
1448
  */
1358
1449
  declare function openrouter(params?: OpenRouterParams): Provider;
1359
1450
  //#endregion
1451
+ //#region src/providers/schema-sanitize.d.ts
1452
+ /**
1453
+ * JSON Schema sanitizer for tool `inputSchema` forwarded to LLM providers.
1454
+ *
1455
+ * Why this exists: MCP servers ship arbitrary JSON Schema in `tools/list`.
1456
+ * Anthropic and (to a lesser extent) OpenAI reject specific keyword
1457
+ * combinations with a 400 on `messages.create`, and the error message
1458
+ * lands deep inside an SDK exception that consumers blame on their own
1459
+ * code. The most common failure modes in the wild:
1460
+ *
1461
+ * - Root `$ref` (server-published schema lives under `#/$defs/X`).
1462
+ * - Root `oneOf` / `anyOf` / `allOf` (Anthropic allows neither at the
1463
+ * top level; expects `type: 'object'`).
1464
+ * - `type: ['object', 'null']` at the root (nullable variants of an
1465
+ * otherwise valid object schema).
1466
+ * - Missing `properties` on an object root.
1467
+ * - `nullable: true` (Swagger / OpenAPI 3.0 idiom — not standard JSON
1468
+ * Schema, ignored by every modern provider but Anthropic warns).
1469
+ * - Nested `$ref` pointing at `#/definitions/X` without the schema
1470
+ * actually inlining the referenced definition.
1471
+ *
1472
+ * Pure rewrite. Returns a new schema (or the same reference when nothing
1473
+ * needed to change — the hot path on stable tool registries) and a list
1474
+ * of `warnings` describing each rewrite. Providers call
1475
+ * `sanitizeToolSpecs` from `formatTools` so every tool — eager MCP, lazy
1476
+ * MCP, native, skill — gets the same treatment, and warnings surface
1477
+ * through `console.warn` once per batch (the seen-set in
1478
+ * `sanitizeToolSpecs` is local to one call, so a stable bad schema logs
1479
+ * once per request rather than every turn).
1480
+ *
1481
+ * Scope:
1482
+ * - Pure function. No I/O, no globals.
1483
+ * - Bounded recursion (`MAX_DEPTH = 32`) so a malicious server can't
1484
+ * wedge the agent with a deeply self-referential schema.
1485
+ * - Bounded `$ref` resolution (`MAX_REF_HOPS = 16`) for the same
1486
+ * reason. Cycles fall back to `{}` (permissive) rather than
1487
+ * throwing — the request still succeeds, the schema is just looser.
1488
+ *
1489
+ * Non-goals:
1490
+ * - Validating that the schema is internally consistent.
1491
+ * - Cleaning up tool *descriptions* (handled elsewhere).
1492
+ * - Provider-side feature detection (handled by the provider's own
1493
+ * `formatTools`; this module is a syntactic guard, not a semantic one).
1494
+ */
1495
+ /** Strictness profile — picks which transformations to apply. */
1496
+ type SchemaSanitizeProfile = 'anthropic' | 'openai' | 'permissive';
1497
+ interface SchemaSanitizeOptions {
1498
+ /** Strictness profile. Defaults to `'permissive'`. */
1499
+ profile?: SchemaSanitizeProfile;
1500
+ /**
1501
+ * Tool name for context in warnings. Optional — when set, every warning
1502
+ * is prefixed with `[tool:<name>] ` so log lines correlate to the tool.
1503
+ */
1504
+ toolName?: string;
1505
+ }
1506
+ interface SchemaSanitizeResult {
1507
+ /** Sanitized schema, safe to forward to the provider. */
1508
+ schema: Record<string, unknown>;
1509
+ /**
1510
+ * Human-readable strings describing each rewrite the sanitizer performed.
1511
+ * Empty on a clean schema. Consumers may log these (recommended) or
1512
+ * thread them through a hook for observability.
1513
+ */
1514
+ warnings: string[];
1515
+ }
1516
+ /**
1517
+ * Sanitize a single tool's `inputSchema` for safe forwarding to the
1518
+ * provider. Returns the rewritten schema + a list of warnings describing
1519
+ * everything that changed.
1520
+ *
1521
+ * Never mutates the input. Returns the **same reference** when no rewrite
1522
+ * was needed (clean-schema fast path) — `sanitizeToolSpecs` relies on
1523
+ * this to keep the formatTools hot loop allocation-free across turns
1524
+ * when the registered tool set is already wire-valid.
1525
+ */
1526
+ declare function sanitizeToolSchema(input: unknown, options?: SchemaSanitizeOptions): SchemaSanitizeResult;
1527
+ /**
1528
+ * Convenience: sanitize a batch of tools and emit a single de-duped
1529
+ * `console.warn` per unique warning line. Returns the rewritten tools
1530
+ * preserving original ordering and reference identity for clean schemas
1531
+ * (no reallocation when nothing needed to change).
1532
+ *
1533
+ * The sanitiser runs every request, so log noise from a stable bad
1534
+ * schema would multiply across turns; the de-dupe keeps the signal
1535
+ * useful in production logs without dropping the first occurrence.
1536
+ */
1537
+ declare function sanitizeToolSpecs<T extends {
1538
+ name: string;
1539
+ inputSchema?: unknown;
1540
+ }>(tools: readonly T[], options?: {
1541
+ profile?: SchemaSanitizeProfile;
1542
+ onWarning?: (line: string) => void;
1543
+ }): T[];
1544
+ //#endregion
1360
1545
  //#region src/providers/index.d.ts
1361
1546
  interface ToolSpec {
1362
1547
  name: string;
@@ -1378,6 +1563,25 @@ interface ToolResult {
1378
1563
  * Use `toolResultToText(content)` when a downstream consumer only handles strings.
1379
1564
  */
1380
1565
  content: string | ToolResultContent[];
1566
+ /**
1567
+ * Marks this result as an error. Propagates to `tool_result.is_error: true`
1568
+ * on the Anthropic wire (and to the equivalent on every other provider that
1569
+ * exposes the field). Models treat error-flagged results as "the tool tried
1570
+ * but failed" — distinct from "the tool returned this string" — so they can
1571
+ * decide whether to retry, fall back, or surface the error to the user.
1572
+ *
1573
+ * Set by the loop on:
1574
+ * - Tool body throws (`tool:error` path).
1575
+ * - Gate refusal (`Blocked: <reason>`).
1576
+ * - Validation rejection (`Validation error: …`).
1577
+ * - Mid-tool abort (`INTERRUPT_MESSAGE_FOR_TOOL_USE`).
1578
+ * - Sequential-batch skips when a sibling threw / a steering message
1579
+ * arrived.
1580
+ *
1581
+ * Custom tools may set it via the `tool:error` hook's `ctx.result`
1582
+ * substitute when they want the framework to flag a structured error.
1583
+ */
1584
+ isError?: boolean;
1381
1585
  }
1382
1586
  /**
1383
1587
  * Provider-level capability flags used by the agent loop to route tool results
@@ -1539,6 +1743,151 @@ declare function toOpenAI(msg: SessionMessage): {
1539
1743
  role: string;
1540
1744
  content: unknown;
1541
1745
  };
1746
+ /**
1747
+ * Placeholder content inserted into a synthetic `tool_result` when the harness
1748
+ * has to repair an orphan `tool_use`. Exported so downstream consumers
1749
+ * (training-data collectors, HFI submission) can reject any payload containing
1750
+ * it — the marker satisfies the wire-level pairing contract structurally but
1751
+ * the content itself is fake and would poison fine-tuning data.
1752
+ */
1753
+ declare const SYNTHETIC_TOOL_RESULT_PLACEHOLDER = "[Tool result missing due to internal error]";
1754
+ /**
1755
+ * Replacement text for an assistant message whose every content block was
1756
+ * stripped during pairing repair (e.g. the only blocks were orphan
1757
+ * `tool_call`s). Providers reject empty `content` arrays — the marker keeps
1758
+ * the turn shape valid while signaling "this assistant turn lost its
1759
+ * outputs" to the model so it can recover.
1760
+ */
1761
+ declare const TOOL_USE_INTERRUPTED_MARKER = "[Tool use interrupted]";
1762
+ /**
1763
+ * Replacement text for a user message whose every content block was a
1764
+ * `tool_result` with no matching upstream `tool_call` (e.g. the assistant
1765
+ * pair was stripped by an earlier compaction). Same role as
1766
+ * {@link TOOL_USE_INTERRUPTED_MARKER} on the user side.
1767
+ */
1768
+ declare const ORPHANED_TOOL_RESULT_MARKER = "[Orphaned tool result removed due to conversation resume]";
1769
+ /**
1770
+ * Classification of a single repair the pairing pass performed. Surfaced via
1771
+ * {@link EnsureToolResultPairingOptions.onRepair} so consumers can wire
1772
+ * telemetry, debug logs, or strict-mode rejection without re-implementing the
1773
+ * walk.
1774
+ *
1775
+ * Each entry corresponds to one of the corruption modes documented on
1776
+ * {@link ensureToolResultPairing}.
1777
+ */
1778
+ type PairingRepairMode = 'orphan-tool-use-prepend' | 'orphan-tool-use-append' | 'orphan-tool-result-strip' | 'duplicate-tool-use-strip' | 'duplicate-tool-result-strip' | 'empty-assistant-marker';
1779
+ interface PairingRepair {
1780
+ mode: PairingRepairMode;
1781
+ /** Tool-use / tool-result id this repair concerns. Absent for empty-marker repairs. */
1782
+ callId?: string;
1783
+ /** Zero-based index into the INPUT `messages` array where the corruption was found. */
1784
+ messageIndex: number;
1785
+ }
1786
+ interface EnsureToolResultPairingOptions {
1787
+ /**
1788
+ * Fired once per repair the pass performed. Synchronous so the loop can
1789
+ * stay on its hot path. Throwing from the callback aborts the pass —
1790
+ * consumers wanting fail-fast (strict mode) can re-throw with their own
1791
+ * typed error.
1792
+ */
1793
+ onRepair?: (repair: PairingRepair) => void;
1794
+ }
1795
+ /**
1796
+ * Defensive repair pass that rewrites a message list so it satisfies the
1797
+ * wire-level `tool_use` ↔ `tool_result` adjacency contract every modern
1798
+ * provider enforces.
1799
+ *
1800
+ * Anthropic 400s on orphans with `'tool_use' ids were found without
1801
+ * 'tool_result' blocks immediately after` (and its inverse, `tool_result
1802
+ * must be preceded by a tool_call with the same toolCallId`). OpenAI's
1803
+ * Chat Completions API rejects most mismatches with a 400 too.
1804
+ *
1805
+ * Six repair modes — modeled after Anthropic's Claude Code defenses:
1806
+ *
1807
+ * | # | Corruption | Repair |
1808
+ * |---|------------|--------|
1809
+ * | 1 | assistant `tool_use` whose next user msg lacks a matching `tool_result` | **Prepend** a synthetic `tool_result` block carrying {@link SYNTHETIC_TOOL_RESULT_PLACEHOLDER} with `isError: true` |
1810
+ * | 2 | assistant `tool_use` followed by nothing (or by a non-user msg) | **Insert** a new synthetic user message with the same placeholder |
1811
+ * | 3 | user `tool_result` with no preceding assistant `tool_call` | **Strip** the orphan block; if it empties the msg, replace with {@link ORPHANED_TOOL_RESULT_MARKER} text block |
1812
+ * | 4 | duplicate `tool_call.id` across assistant messages (CC-1212) | **Strip** later instances + their matching tool_results |
1813
+ * | 5 | duplicate `tool_result.callId` within a single user message | **Dedupe** by `callId`, keep first |
1814
+ * | 6 | assistant message emptied by mode-4 stripping | **Replace** content with {@link TOOL_USE_INTERRUPTED_MARKER} text block |
1815
+ *
1816
+ * **Repair, not drop.** Earlier versions of this pass simply dropped orphan
1817
+ * blocks, which (a) silently rewrote history the model had reasoned over and
1818
+ * (b) cascaded — dropping an assistant tool_call would orphan its
1819
+ * tool_result, which would then get dropped too, removing any trace of
1820
+ * "what the model tried to do" from the transcript. The repair-based pass
1821
+ * preserves the model's tool_use shape and patches the dangling result with
1822
+ * an `is_error` placeholder so the model sees "I tried X, the result was
1823
+ * lost" and can retry intelligently.
1824
+ *
1825
+ * Adjacency contract: `tool_result` blocks must live in the user message
1826
+ * IMMEDIATELY following the assistant message that emitted the matching
1827
+ * `tool_use`. The pass enforces strict adjacency — a tool_result two
1828
+ * messages downstream of its tool_call is still an orphan.
1829
+ *
1830
+ * Idempotent: returns the input reference unchanged when no repairs were
1831
+ * necessary. Re-running on already-repaired output is a no-op.
1832
+ *
1833
+ * Pure: does not mutate input messages or their content arrays — every
1834
+ * repair allocates a fresh array / object.
1835
+ */
1836
+ declare function ensureToolResultPairing(messages: SessionMessage[], options?: EnsureToolResultPairingOptions): SessionMessage[];
1837
+ /**
1838
+ * Drop ASSISTANT turns whose every `tool_call` block is unresolved
1839
+ * (no matching `tool_result` block anywhere later in the transcript).
1840
+ * Tool_call blocks with at least one matching tool_result are kept — modes
1841
+ * 1/3 of {@link ensureToolResultPairing} handle the partial-pair case at
1842
+ * wire-send time.
1843
+ *
1844
+ * Use case: session resume. A turn that emitted three `tool_use` blocks but
1845
+ * never persisted the matching `tool_result` turn (process death, crash,
1846
+ * `kill -9`) leaves the transcript with an orphan that Anthropic rejects on
1847
+ * the FIRST API call after reload. Dropping the whole assistant turn — not
1848
+ * just the orphan blocks — preserves text-only turns that legitimately
1849
+ * carry reasoning the model wants to see again.
1850
+ *
1851
+ * Does NOT mint fresh ids: re-id'ing on every resume would cause
1852
+ * exponential transcript growth across repeated resumes of an interrupted
1853
+ * session.
1854
+ *
1855
+ * Pure: returns the input reference unchanged when no turn was dropped.
1856
+ */
1857
+ declare function filterUnresolvedToolUses<T extends {
1858
+ role: string;
1859
+ content: SessionContentBlock[];
1860
+ }>(turns: T[]): T[];
1861
+ /**
1862
+ * Classification of a session's tail state for resume purposes.
1863
+ *
1864
+ * - `'clean'` — the trailing turn is a user message (the model would respond
1865
+ * next), or the transcript is empty. Safe to resume by appending a fresh
1866
+ * user prompt.
1867
+ * - `'interrupted'` — the trailing turn is an assistant message that has at
1868
+ * least one `tool_call` block with no matching `tool_result` anywhere
1869
+ * later. The run was almost certainly killed between persisting the
1870
+ * assistant turn and persisting its tool-results turn. Hosts that want
1871
+ * Claude-Code-style auto-resume can detect this and inject a
1872
+ * `"Continue from where you left off."` user message before the next run.
1873
+ * - `'completed'` — the trailing turn is an assistant message with no
1874
+ * orphan tool_calls. The model considers itself done. Hosts that want to
1875
+ * resume must supply a new prompt; auto-continuation here would be a
1876
+ * non-sequitur.
1877
+ */
1878
+ type TurnInterruptionState = 'clean' | 'interrupted' | 'completed';
1879
+ /**
1880
+ * Inspect a session's trailing turn to classify whether the run was
1881
+ * interrupted mid-tool-call. Pure / synchronous — does not modify the input.
1882
+ *
1883
+ * Pair with {@link filterUnresolvedToolUses} on the resume path: filter
1884
+ * first so the result reflects the post-cleanup state (a turn whose orphans
1885
+ * were stripped reads as 'completed', not 'interrupted').
1886
+ */
1887
+ declare function detectTurnInterruption<T extends {
1888
+ role: string;
1889
+ content: SessionContentBlock[];
1890
+ }>(turns: T[]): TurnInterruptionState;
1542
1891
  declare function autoDetectAndConvert(msg: {
1543
1892
  role: string;
1544
1893
  content: unknown;
@@ -1866,6 +2215,76 @@ interface SkillsConfig {
1866
2215
  trustProjectSkills?: boolean;
1867
2216
  }
1868
2217
  //#endregion
2218
+ //#region src/tools/read-state.d.ts
2219
+ interface ReadStateEntry {
2220
+ contentHash: string;
2221
+ /** Slice parameters for the last read. */
2222
+ offset: number;
2223
+ limit: number;
2224
+ maxBytes: number;
2225
+ /**
2226
+ * Whether the prior read emitted line-number prefixes. Tracked so a
2227
+ * read with `lineNumbers: true` doesn't dedup against one with
2228
+ * `lineNumbers: false` (or vice versa) — the dedup stub would mislead
2229
+ * the model about the prior output's shape.
2230
+ */
2231
+ lineNumbers?: boolean;
2232
+ /** Wall-clock at last read — diagnostic only, not used for invalidation. */
2233
+ mtimeMs: number;
2234
+ }
2235
+ type ReadStateMap = Map<string, ReadStateEntry>;
2236
+ /**
2237
+ * Get or lazily create the per-session read-state map. Returns `undefined`
2238
+ * when no session is provided.
2239
+ *
2240
+ * Most tool callers should prefer {@link resolveReadStateMap}, which
2241
+ * additionally honors an explicit `ctx.readState` (the path
2242
+ * `spawn`'s `shareReadState: true` uses to forward the parent's map
2243
+ * into a sessionless child). Use this helper directly only when the
2244
+ * Session-keyed map is exactly what you want and you don't need to
2245
+ * accept an override.
2246
+ */
2247
+ declare function getReadState(session: Session | undefined): ReadStateMap | undefined;
2248
+ /**
2249
+ * Resolve the active read-state map from a tool context. An explicit
2250
+ * `ctx.readState` wins (used by `spawn`'s `shareReadState` opt-in to
2251
+ * forward the parent's map into a sessionless child); otherwise the
2252
+ * usual `Session`-keyed lookup applies.
2253
+ *
2254
+ * Tools should call this helper instead of `getReadState(ctx.session)`
2255
+ * directly so the `shareReadState` plumbing is honored uniformly.
2256
+ */
2257
+ declare function resolveReadStateMap(ctx: {
2258
+ session?: Session;
2259
+ readState?: ReadStateMap;
2260
+ }): ReadStateMap | undefined;
2261
+ /**
2262
+ * Canonical read-state key for a `(cwd, path)` pair.
2263
+ *
2264
+ * `ctx.execution.readFile(handle, path)` resolves the model-provided
2265
+ * path against `handle.cwd` before touching disk — so `src/App.tsx`,
2266
+ * `./src/App.tsx`, and `/abs/cwd/src/App.tsx` all read the **same**
2267
+ * bytes. The read-state map MUST mirror that resolution: without it,
2268
+ * a model that reads `src/App.tsx` and then edits `./src/App.tsx`
2269
+ * trips the `requireReadBeforeEdit` gate for a file it demonstrably
2270
+ * already saw (the gate's "has not been read in this session" message
2271
+ * fires on a stale key).
2272
+ *
2273
+ * `node:path`'s `resolve(cwd, path)` short-circuits when `path` is
2274
+ * already absolute, so absolute and relative shapes converge on the
2275
+ * same canonical form. We don't `realpath()` symlinks — the file IS
2276
+ * the path the model addressed, not the realpath behind it; the host's
2277
+ * execution context decides how to dereference.
2278
+ */
2279
+ declare function readStateKey(cwd: string, path: string): string;
2280
+ /**
2281
+ * FNV-1a 32-bit hash, hex-encoded. Fast, non-cryptographic — we only need
2282
+ * collision resistance against accidental matches between different file
2283
+ * contents within one session (~1 in 4 billion). Cheaper than allocating
2284
+ * a Buffer and pulling in `crypto`.
2285
+ */
2286
+ declare function hashContent(text: string): string;
2287
+ //#endregion
1869
2288
  //#region src/tools/types.d.ts
1870
2289
  /**
1871
2290
  * Runtime context passed to every tool execution.
@@ -1921,12 +2340,30 @@ interface ToolContext {
1921
2340
  * the subagent tree can be reconstructed from a persisted session.
1922
2341
  */
1923
2342
  runId?: string;
2343
+ /**
2344
+ * Parent run id when the agent owning this tool call is a subagent.
2345
+ * Mirrors `SessionRun.parentRunId`; absent on top-level runs. Tools
2346
+ * that need to walk up to the spawning run (telemetry, scoped
2347
+ * caches, todowrite-style state keyed by the parent thread) read it
2348
+ * directly off the context instead of resolving via the session.
2349
+ */
2350
+ parentRunId?: string;
1924
2351
  /**
1925
2352
  * The agent's session, when one was provided to `createAgent`. Tools that
1926
2353
  * want to persist their own state (or, in the case of `spawn`, inherit the
1927
2354
  * parent's session for child persistence) can read from here.
1928
2355
  */
1929
2356
  session?: Session;
2357
+ /**
2358
+ * Explicit read-state map forwarded from the agent's options. When set,
2359
+ * tools should prefer it over the `Session`-keyed lookup so the
2360
+ * `requireReadBeforeEdit` gate and `dedupReads` cache honor cross-context
2361
+ * sharing — `spawn`'s `shareReadState` opt-in is the canonical caller.
2362
+ * Resolve uniformly via `resolveReadStateMap(ctx)` (see
2363
+ * `tools/read-state.ts`); custom tools that need read tracking should do
2364
+ * the same.
2365
+ */
2366
+ readState?: ReadStateMap;
1930
2367
  /**
1931
2368
  * Subagent depth for the agent owning this tool call. 0 = top-level,
1932
2369
  * 1 = first-level child, … Used by spawn to enforce a `maxDepth` cap.
@@ -2139,6 +2576,23 @@ interface AgentHooks {
2139
2576
  delta: string;
2140
2577
  thinking: string;
2141
2578
  }) => void;
2579
+ /**
2580
+ * Fires when the provider's stream rejects BEFORE `stream:end` — provider
2581
+ * errors, network blips, malformed `tools` payloads (400 invalid_request_error).
2582
+ *
2583
+ * `err` is the original thrown value, not the typed `AgentProviderError`
2584
+ * the loop subsequently constructs; subscribe here to capture the native
2585
+ * SDK exception (status codes, request ids) for telemetry without having
2586
+ * to walk `cause` chains. Observational — the loop still wraps + throws
2587
+ * after this hook resolves, so handlers cannot suppress the failure.
2588
+ *
2589
+ * Does NOT fire on user-initiated aborts; those land on `agent:abort`
2590
+ * instead. Use `err instanceof Error && err.name === 'AbortError'` to
2591
+ * distinguish if you also subscribe here for raw error logging.
2592
+ */
2593
+ 'stream:error': (ctx: StreamHookContext & {
2594
+ err: unknown;
2595
+ }) => void;
2142
2596
  'oauth:refresh': (ctx: OAuthRefreshHookContext) => void;
2143
2597
  /**
2144
2598
  * Fires before validation, `tool:before`, and `execute`. Two ways to
@@ -2190,6 +2644,54 @@ interface AgentHooks {
2190
2644
  */
2191
2645
  priorContent?: string;
2192
2646
  }) => void;
2647
+ /**
2648
+ * Symmetric notification fired ONCE per call the model dispatched,
2649
+ * regardless of the path the call ultimately took through the loop.
2650
+ * Pair with `tool:after` (which also fires uniformly across paths) to
2651
+ * get a guaranteed `tool` ↔ `tool-result` event pairing for downstream
2652
+ * consumers reconstructing message history from live events.
2653
+ *
2654
+ * Distinct from `tool:before`: `tool:before` only fires when the tool
2655
+ * body is about to execute (the legacy "I'm running this tool" notification),
2656
+ * skipping gate-block / gate-substitute / unknown-tool / validation-reject
2657
+ * paths. `tool:dispatched` fires on all five paths with an `outcome`
2658
+ * discriminator so live consumers never see a `tool:after` whose
2659
+ * matching dispatch event was suppressed.
2660
+ *
2661
+ * Strict ordering: `tool:dispatched` fires AFTER `tool:gate` /
2662
+ * `validation:reject` / `tool:unknown` so the resolved `outcome` is
2663
+ * always accurate. Fires BEFORE `tool:before` on the execute path so
2664
+ * consumers binding both events see dispatched-then-before.
2665
+ *
2666
+ * `reason` is set only when `outcome === 'gate-block'` and carries the
2667
+ * gate's refusal text (so consumers can render denied-diff badges, etc.
2668
+ * without a side channel).
2669
+ */
2670
+ 'tool:dispatched': (ctx: ToolHookContext & {
2671
+ outcome: 'execute' | 'gate-substitute' | 'gate-block' | 'unknown' | 'invalid-input';
2672
+ reason?: string;
2673
+ runToolCounts: Readonly<Record<string, number>>;
2674
+ }) => void;
2675
+ /**
2676
+ * Fires after a tool body produced a result (the execute path) or a
2677
+ * `tool:gate` listener substituted one via `ctx.result` (the Z20
2678
+ * cache-substitute path). Carries the post-`tool:transform` payload —
2679
+ * what the model actually sees on the wire.
2680
+ *
2681
+ * Does NOT fire on gate-block / unknown-tool / validation-reject
2682
+ * paths: the loop synthesizes their `tool_result` text inline and
2683
+ * sends it back to the model, but no tool body produced output and
2684
+ * no `tool:transform` ran. Consumers wanting symmetric per-call
2685
+ * notification across every dispatch path should listen to
2686
+ * `tool:dispatched` instead (which fires once per call regardless of
2687
+ * path, with an `outcome` discriminator).
2688
+ *
2689
+ * This narrow contract is deliberate: tracing spans opened in
2690
+ * `tool:before` only get closed where they were opened, byte budgets
2691
+ * only count real tool output, and chat-layer transcript renderers
2692
+ * keep their `tool` ↔ `tool-result` pairing without special-casing
2693
+ * the synthesized paths.
2694
+ */
2193
2695
  'tool:after': (ctx: ToolHookContext & {
2194
2696
  result: string | ToolResultContent[];
2195
2697
  outputBytes: number;
@@ -2301,6 +2803,12 @@ interface AgentHooks {
2301
2803
  childId: string;
2302
2804
  depth: number;
2303
2805
  }) => void;
2806
+ /** Bubbled `stream:error` from a subagent's turn. See {@link AgentHooks['stream:error']}. */
2807
+ 'child:stream:error': (ctx: StreamHookContext & {
2808
+ err: unknown;
2809
+ childId: string;
2810
+ depth: number;
2811
+ }) => void;
2304
2812
  /**
2305
2813
  * Gate-style child events. Unlike the other `child:*` events, the bubble
2306
2814
  * passes the **same `ctx` reference** the subagent's loop is awaiting on:
@@ -2342,6 +2850,19 @@ interface AgentHooks {
2342
2850
  */
2343
2851
  priorContent?: string;
2344
2852
  }) => void;
2853
+ /**
2854
+ * Bubbled sibling of {@link AgentHooks['tool:dispatched']} — fires when a
2855
+ * subagent dispatches a call. Symmetric with `child:tool:after` so live
2856
+ * UI rendering of subagent transcripts stays paired across all five
2857
+ * dispatch paths (gate-block/gate-substitute/unknown/invalid-input/execute).
2858
+ */
2859
+ 'child:tool:dispatched': (ctx: ToolHookContext & {
2860
+ outcome: 'execute' | 'gate-substitute' | 'gate-block' | 'unknown' | 'invalid-input';
2861
+ reason?: string;
2862
+ runToolCounts: Readonly<Record<string, number>>;
2863
+ childId: string;
2864
+ depth: number;
2865
+ }) => void;
2345
2866
  'child:tool:after': (ctx: ToolHookContext & {
2346
2867
  result: string | ToolResultContent[];
2347
2868
  outputBytes: number;
@@ -2539,6 +3060,26 @@ interface AgentHooks {
2539
3060
  skill: SkillConfig;
2540
3061
  reason: DeactivationReason;
2541
3062
  }) => void;
3063
+ /**
3064
+ * Fires once per repair performed by the pre-send pairing pass
3065
+ * ({@link ensureToolResultPairing}). The pass runs immediately before every
3066
+ * provider call and patches over the wire-level corruption modes that
3067
+ * Anthropic / OpenAI 400 on (orphan `tool_use` / `tool_result`, duplicate
3068
+ * ids, compaction-stranded blocks).
3069
+ *
3070
+ * Observational — handlers can mirror to Sentry/PostHog/etc. for
3071
+ * postmortems on new corruption sources but cannot suppress the repair.
3072
+ * The companion `behavior.strictToolPairing` flag is the consumer escape
3073
+ * hatch for "throw instead of repair" (training-data collectors).
3074
+ *
3075
+ * `turnId` lets handlers correlate a repair burst with the run-loop turn
3076
+ * that observed it. Absent on repairs detected from the synthetic
3077
+ * fallback path in `agent.run()`'s error catch (the orphan-tool_use
3078
+ * fallback for crash recovery), where no turn is in flight.
3079
+ */
3080
+ 'pairing:repair': (ctx: PairingRepair & {
3081
+ turnId?: string;
3082
+ }) => void;
2542
3083
  'usage': (ctx: {
2543
3084
  turn: number;
2544
3085
  turnId: string;
@@ -2645,6 +3186,16 @@ interface AgentOptions {
2645
3186
  mcpServers?: McpServerConfig[];
2646
3187
  /** Session for identity, turn persistence, and run tracking */
2647
3188
  session?: Session;
3189
+ /**
3190
+ * Explicit read-state map for `read_file` dedup + `requireReadBeforeEdit`
3191
+ * tracking. When omitted, the read-state lives in a `WeakMap<Session, …>`
3192
+ * keyed by the agent's session; providing an explicit map lets a parent
3193
+ * agent share its tracking with a sessionless child (the canonical caller
3194
+ * is `spawn`'s `shareReadState: true` option). Tools resolve uniformly
3195
+ * via `resolveReadStateMap(ctx)` so the explicit map (when present)
3196
+ * wins over the session-keyed lookup.
3197
+ */
3198
+ readState?: ReadStateMap;
2648
3199
  /** Skills configuration */
2649
3200
  skills?: SkillsConfig;
2650
3201
  /**
@@ -2751,11 +3302,12 @@ declare function createAgent({
2751
3302
  execution,
2752
3303
  mcpServers,
2753
3304
  session,
3305
+ readState: agentReadState,
2754
3306
  skills: agentSkills,
2755
3307
  mcpConnector,
2756
3308
  eager,
2757
3309
  hooks: initialHooks
2758
3310
  }: AgentOptions): Agent;
2759
3311
  //#endregion
2760
- export { openrouter as $, SessionStore as A, SpawnHookContext as At, createMemoryStore as B, toolOutputByteLength as Bt, SkillResource as C, PromptTextPart as Ct, Session as D, SessionHookContext as Dt, CreateSessionOptions as E, SessionEndStatus as Et, autoDetectAndConvert as F, ToolResultContent as Ft, ProviderCapabilities as G, AgentToolNotAllowedError as Gt, FileMapStoreOptions as H, AgentAbortedError as Ht, fromAnthropic as I, ToolResultImageContent as It, ToolCall as J, ClassifiedErrorKind as Jt, StreamCallbacks as K, CONTEXT_EXCEEDED_MESSAGE_PATTERNS as Kt, fromOpenAI as L, ToolResultTextContent as Lt, loadSession as M, ThinkingLevel as Mt, RemoteStoreOptions as N, ToolExecutionMode as Nt, SessionData as O, SessionMessage as Ot, createRemoteStore as P, ToolHookContext as Pt, OpenRouterParams as Q, toAnthropic as R, TurnFinishReason as Rt, SkillDiagnostic as S, PromptPart as St, SkillsConfig as T, SessionContentBlock as Tt, createFileMapStore as U, AgentContextExceededError as Ut, FileMapAdapter as V, toolResultToText as Vt, Provider as W, AgentProviderError as Wt, ToolSpec as X, matchesContextExceeded as Xt, ToolResult as Y, errorMessage as Yt, TurnResult as Z, toTypedError as Zt, resultToString as _, McpToolHookContext as _t, createAgent as a, openaiCompat as at, ToolMap as b, PromptDocumentPart as bt, DeactivationReason as c, CerebrasParams as ct, createSkillActivationState as d, anthropic as dt, OpenAICompatAuthHeader as et, ConnectMcpServersOptions as f, AgentBehavior as ft, normalizeMcpServers as g, McpServerConfig as gt, normalizeMcpBlocks as h, ChildRunStats as ht, AgentOptions as i, mapOAIFinishReason as it, createSession as j, StreamHookContext as jt, SessionRun as k, SessionTurn as kt, SkillActivationState as l, cerebras as lt, connectMcpServers as m, AgentStats as mt, AgentHookMap as n, OpenAICompatParams as nt, ActivationVia as o, OpenAIParams as ot, McpConnection as p, AgentRunOptions as pt, StreamOptions as q, ClassifiedError as qt, AgentHooks as r, classifyOpenAICompatError as rt, ActiveSkill as s, openai as st, Agent as t, OpenAICompatHttpError as tt, SkillActivationStateOptions as u, AnthropicParams as ut, ToolContext as v, McpToolSchema as vt, SkillSource as w, RunHookMap as wt, SkillConfig as x, PromptImagePart as xt, ToolDef as y, OAuthRefreshHookContext as yt, toOpenAI as z, TurnUsage as zt };
2761
- //# sourceMappingURL=agent-CYpPKn5Z.d.ts.map
3312
+ export { fromOpenAI as $, ThinkingLevel as $t, SkillSource as A, cerebras as At, createRemoteStore as B, OAuthRefreshHookContext as Bt, getReadState as C, OpenAICompatParams as Ct, SkillConfig as D, OpenAIParams as Dt, resolveReadStateMap as E, openaiCompat as Et, SessionRun as F, AgentStats as Ft, SYNTHETIC_TOOL_RESULT_PLACEHOLDER as G, RunHookMap as Gt, ORPHANED_TOOL_RESULT_MARKER as H, PromptImagePart as Ht, SessionStore as I, ChildRunStats as It, autoDetectAndConvert as J, SessionHookContext as Jt, TOOL_USE_INTERRUPTED_MARKER as K, SessionContentBlock as Kt, createSession as L, McpServerConfig as Lt, CreateSessionOptions as M, anthropic as Mt, Session as N, AgentBehavior as Nt, SkillDiagnostic as O, openai as Ot, SessionData as P, AgentRunOptions as Pt, fromAnthropic as Q, StreamHookContext as Qt, loadSession as R, McpToolHookContext as Rt, ReadStateMap as S, OpenAICompatHttpError as St, readStateKey as T, mapOAIFinishReason as Tt, PairingRepair as U, PromptPart as Ut, EnsureToolResultPairingOptions as V, PromptDocumentPart as Vt, PairingRepairMode as W, PromptTextPart as Wt, ensureToolResultPairing as X, SessionTurn as Xt, detectTurnInterruption as Y, SessionMessage as Yt, filterUnresolvedToolUses as Z, SpawnHookContext as Zt, resultToString as _, errorMessage as _n, sanitizeToolSchema as _t, createAgent as a, TurnFinishReason as an, createFileMapStore as at, ToolMap as b, openrouter as bt, DeactivationReason as c, toolResultToText as cn, StreamCallbacks as ct, createSkillActivationState as d, AgentProviderError as dn, ToolResult as dt, ToolExecutionMode as en, toAnthropic as et, ConnectMcpServersOptions as f, AgentToolNotAllowedError as fn, ToolSpec as ft, normalizeMcpServers as g, ClassifiedErrorKind as gn, SchemaSanitizeResult as gt, normalizeMcpBlocks as h, ClassifiedError as hn, SchemaSanitizeProfile as ht, AgentOptions as i, ToolResultTextContent as in, FileMapStoreOptions as it, SkillsConfig as j, AnthropicParams as jt, SkillResource as k, CerebrasParams as kt, SkillActivationState as l, AgentAbortedError as ln, StreamOptions as lt, connectMcpServers as m, CONTEXT_EXCEEDED_MESSAGE_PATTERNS as mn, SchemaSanitizeOptions as mt, AgentHookMap as n, ToolResultContent as nn, createMemoryStore as nt, ActivationVia as o, TurnUsage as on, Provider as ot, McpConnection as p, AgentToolPairingError as pn, TurnResult as pt, TurnInterruptionState as q, SessionEndStatus as qt, AgentHooks as r, ToolResultImageContent as rn, FileMapAdapter as rt, ActiveSkill as s, toolOutputByteLength as sn, ProviderCapabilities as st, Agent as t, ToolHookContext as tn, toOpenAI as tt, SkillActivationStateOptions as u, AgentContextExceededError as un, ToolCall as ut, ToolContext as v, matchesContextExceeded as vn, sanitizeToolSpecs as vt, hashContent as w, classifyOpenAICompatError as wt, ReadStateEntry as x, OpenAICompatAuthHeader as xt, ToolDef as y, toTypedError as yn, OpenRouterParams as yt, RemoteStoreOptions as z, McpToolSchema as zt };
3313
+ //# sourceMappingURL=agent-BXRCCHeq.d.ts.map