@jun133/kitty 0.0.15 → 0.0.16

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.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  官网:https://agentjz.github.io/kitty/
6
6
 
7
7
  <p align="center">
8
- <strong>🐾 一个本地 agent 编程工作台:搜得到,看得懂,改得准,跑得通,记得住,能继续。</strong>
8
+ <strong>🐾 一个 agent 编程工作台:搜得到,看得懂,改得准,跑得通,记得住,能继续。</strong>
9
9
  </p>
10
10
 
11
11
  <p align="center">
@@ -29,7 +29,7 @@
29
29
  - 可恢复的 session 现场
30
30
  - CLI、TUI、Telegram 三种入口
31
31
  - 当前现场、后台任务、会话事件、memory 和 eval
32
- - 省 token 的上下文与输出治理
32
+ - 省 token 的上下文、缓存事实和工具输出治理
33
33
 
34
34
  ## ⚡ 快速开始
35
35
 
@@ -101,14 +101,16 @@ kitty "检查这个仓库并修复失败测试"
101
101
  | `kitty events [sessionId]` | 查看最近会话或指定会话的机器事件 |
102
102
  | `kitty config show` | 查看从 `.kitty/.env` 解析出的当前运行配置 |
103
103
  | `kitty config path` | 查看当前项目 `.kitty/.env` 路径 |
104
- | `kitty status` | 查看当前项目现场:当前目标、下一步、阻塞、后台、恢复、成本、session、context budget、memory、skills、project map、execution、wake |
104
+ | `kitty status` | 查看当前项目现场:先显示 Current scene,再显示 session、context budget、memory files、skills、project orientation、execution、wake 等 Runtime facts |
105
105
  | `kitty memory` | 创建、查看、读取、搜索、删除 runtime memory assets,或把 memory 沉淀到 skill references |
106
106
  | `kitty changes` | 查看记录的文件变更 |
107
107
  | `kitty undo [changeId]` | 撤销最近一次或指定变更 |
108
108
  | `kitty diff [path]` | 查看当前 git diff |
109
- | `kitty doctor` | 检查 `.kitty` 文件、env contract、provider preset、runtime、provider 连接和下一步 |
110
- | `kitty eval` | 查看产品验收场景;`kitty eval --run` 运行本地机器验收 |
111
- | `kitty telegram serve` | 启动 Telegram 私聊服务 |
109
+ | `kitty doctor` | 检查 `.kitty` 文件、env contract、provider preset、runtime、provider 连接和下一步 |
110
+ | `kitty eval` | 查看产品验收场景;`kitty eval --run-local` 运行本地机器验收;`kitty eval --run-production` 显式运行生产路径验收 |
111
+ | `kitty telegram serve` | 启动 Telegram 私聊服务 |
112
+
113
+ `npm.cmd test` 只跑日常确定性测试,不跑 eval。产品验收独立运行:`npm.cmd run test:eval` 验证 eval harness,`npm.cmd run eval:local` 跑本地验收,`npm.cmd run eval:production` 使用当前 `.kitty/.env` 显式跑生产路径验收。运行 eval 前先执行 `npm.cmd run build`。
112
114
 
113
115
  查看配置:
114
116
 
@@ -2,7 +2,7 @@ import {
2
2
  TRANSCRIPT_OUTER_PADDING_X,
3
3
  TUI_COLORS,
4
4
  renderTranscriptLineViews
5
- } from "./chunk-NBKU7KA4.mjs";
5
+ } from "./chunk-L27HOXD2.mjs";
6
6
 
7
7
  // src/shell/tui/layout.ts
8
8
  var TUI_MIN_WIDTH = 48;
@@ -33,7 +33,7 @@ import {
33
33
  sliceCurrentUserInputFrame,
34
34
  takeLastUnique,
35
35
  updateSessionMemory
36
- } from "./chunk-S4QTRPZ7.mjs";
36
+ } from "./chunk-C3MFBHV3.mjs";
37
37
 
38
38
  // src/context/projectContext.ts
39
39
  import fs6 from "fs/promises";
@@ -1535,133 +1535,7 @@ function toChatCompletionMessages(messages) {
1535
1535
  });
1536
1536
  }
1537
1537
 
1538
- // src/provider/responsesAdapter.ts
1539
- var responsesAdapter = {
1540
- wireApi: "responses",
1541
- async fetchStreaming(client, request) {
1542
- const startedAt = Date.now();
1543
- let usage;
1544
- throwIfAborted(request.abortSignal, "Streaming request aborted");
1545
- try {
1546
- const stream = await client.responses.create(
1547
- {
1548
- ...buildResponsesRequestBody(request),
1549
- stream: true
1550
- },
1551
- {
1552
- signal: request.abortSignal
1553
- }
1554
- );
1555
- if (request.abortSignal?.aborted) {
1556
- abortStream2(stream);
1557
- throw createAbortError("Streaming aborted");
1558
- }
1559
- let content = "";
1560
- let reasoningContent = "";
1561
- const toolCalls = /* @__PURE__ */ new Map();
1562
- for await (const event of stream) {
1563
- if (request.abortSignal?.aborted) {
1564
- abortStream2(stream);
1565
- throw createAbortError("Streaming aborted");
1566
- }
1567
- usage = normalizeProviderUsage(event.response?.usage) ?? usage;
1568
- if (event.type === "response.output_text.delta" && typeof event.delta === "string") {
1569
- content += event.delta;
1570
- request.callbacks?.onAssistantDelta?.(event.delta);
1571
- continue;
1572
- }
1573
- if ((event.type === "response.reasoning_text.delta" || event.type === "response.reasoning_summary_text.delta") && typeof event.delta === "string") {
1574
- reasoningContent += event.delta;
1575
- request.callbacks?.onReasoningDelta?.(event.delta);
1576
- continue;
1577
- }
1578
- if (event.type === "response.function_call_arguments.delta" && typeof event.delta === "string") {
1579
- const index = typeof event.output_index === "number" ? event.output_index : 0;
1580
- const existing = toolCalls.get(index) ?? {
1581
- id: event.item_id ?? `tool-${index}`,
1582
- name: "",
1583
- arguments: ""
1584
- };
1585
- existing.arguments += event.delta;
1586
- toolCalls.set(index, existing);
1587
- continue;
1588
- }
1589
- if (event.type === "response.function_call_arguments.done") {
1590
- const index = typeof event.output_index === "number" ? event.output_index : 0;
1591
- const existing = toolCalls.get(index) ?? {
1592
- id: event.item_id ?? `tool-${index}`,
1593
- name: "",
1594
- arguments: ""
1595
- };
1596
- if (typeof event.name === "string") {
1597
- existing.name = event.name;
1598
- }
1599
- if (typeof event.arguments === "string" && event.arguments.length > 0) {
1600
- existing.arguments = event.arguments;
1601
- }
1602
- toolCalls.set(index, existing);
1603
- continue;
1604
- }
1605
- if (event.type === "response.output_item.done" && event.item?.type === "function_call") {
1606
- const index = typeof event.output_index === "number" ? event.output_index : 0;
1607
- toolCalls.set(index, {
1608
- id: event.item.call_id ?? event.item.id ?? `tool-${index}`,
1609
- name: event.item.name ?? "",
1610
- arguments: event.item.arguments ?? ""
1611
- });
1612
- }
1613
- }
1614
- return {
1615
- content: content.length > 0 ? content : null,
1616
- reasoningContent: reasoningContent.length > 0 ? reasoningContent : void 0,
1617
- streamedAssistantContent: content.length > 0,
1618
- streamedReasoningContent: reasoningContent.length > 0,
1619
- toolCalls: [...toolCalls.entries()].sort((left, right) => left[0] - right[0]).map(([, toolCall]) => ({
1620
- id: toolCall.id,
1621
- type: "function",
1622
- function: {
1623
- name: toolCall.name,
1624
- arguments: toolCall.arguments
1625
- }
1626
- }))
1627
- };
1628
- } finally {
1629
- request.onRequestMetric?.({
1630
- durationMs: Date.now() - startedAt,
1631
- usage
1632
- });
1633
- }
1634
- },
1635
- async fetchNonStreaming(client, request) {
1636
- const startedAt = Date.now();
1637
- let usage;
1638
- throwIfAborted(request.abortSignal, "Request aborted");
1639
- try {
1640
- const response = await client.responses.create(
1641
- {
1642
- ...buildResponsesRequestBody(request),
1643
- stream: false
1644
- },
1645
- {
1646
- signal: request.abortSignal
1647
- }
1648
- );
1649
- usage = normalizeProviderUsage(response.usage);
1650
- return {
1651
- content: normalizeOutputText(response),
1652
- reasoningContent: readResponseReasoning(response),
1653
- streamedAssistantContent: false,
1654
- streamedReasoningContent: false,
1655
- toolCalls: readResponseToolCalls(response)
1656
- };
1657
- } finally {
1658
- request.onRequestMetric?.({
1659
- durationMs: Date.now() - startedAt,
1660
- usage
1661
- });
1662
- }
1663
- }
1664
- };
1538
+ // src/provider/responsesRequest.ts
1665
1539
  function buildResponsesRequestBody(request) {
1666
1540
  const capabilities = resolveProviderCapabilities({
1667
1541
  provider: request.provider,
@@ -1745,7 +1619,9 @@ function toResponsesInput(messages) {
1745
1619
  }
1746
1620
  return items;
1747
1621
  }
1748
- function normalizeOutputText(response) {
1622
+
1623
+ // src/provider/responsesResponse.ts
1624
+ function normalizeResponsesOutputText(response) {
1749
1625
  const outputText = response.output_text;
1750
1626
  if (typeof outputText === "string" && outputText.trim().length > 0) {
1751
1627
  return outputText;
@@ -1771,7 +1647,7 @@ function normalizeOutputText(response) {
1771
1647
  });
1772
1648
  return fragments.length > 0 ? fragments.join("") : null;
1773
1649
  }
1774
- function readResponseToolCalls(response) {
1650
+ function readResponsesToolCalls(response) {
1775
1651
  const output = response.output;
1776
1652
  if (!Array.isArray(output)) {
1777
1653
  return [];
@@ -1785,7 +1661,7 @@ function readResponseToolCalls(response) {
1785
1661
  }
1786
1662
  }));
1787
1663
  }
1788
- function readResponseReasoning(response) {
1664
+ function readResponsesReasoning(response) {
1789
1665
  const output = response.output;
1790
1666
  if (!Array.isArray(output)) {
1791
1667
  return void 0;
@@ -1801,6 +1677,134 @@ function readResponseReasoning(response) {
1801
1677
  });
1802
1678
  return fragments.length > 0 ? fragments.join("") : void 0;
1803
1679
  }
1680
+
1681
+ // src/provider/responsesAdapter.ts
1682
+ var responsesAdapter = {
1683
+ wireApi: "responses",
1684
+ async fetchStreaming(client, request) {
1685
+ const startedAt = Date.now();
1686
+ let usage;
1687
+ throwIfAborted(request.abortSignal, "Streaming request aborted");
1688
+ try {
1689
+ const stream = await client.responses.create(
1690
+ {
1691
+ ...buildResponsesRequestBody(request),
1692
+ stream: true
1693
+ },
1694
+ {
1695
+ signal: request.abortSignal
1696
+ }
1697
+ );
1698
+ if (request.abortSignal?.aborted) {
1699
+ abortStream2(stream);
1700
+ throw createAbortError("Streaming aborted");
1701
+ }
1702
+ let content = "";
1703
+ let reasoningContent = "";
1704
+ const toolCalls = /* @__PURE__ */ new Map();
1705
+ for await (const event of stream) {
1706
+ if (request.abortSignal?.aborted) {
1707
+ abortStream2(stream);
1708
+ throw createAbortError("Streaming aborted");
1709
+ }
1710
+ usage = normalizeProviderUsage(event.response?.usage) ?? usage;
1711
+ if (event.type === "response.output_text.delta" && typeof event.delta === "string") {
1712
+ content += event.delta;
1713
+ request.callbacks?.onAssistantDelta?.(event.delta);
1714
+ continue;
1715
+ }
1716
+ if ((event.type === "response.reasoning_text.delta" || event.type === "response.reasoning_summary_text.delta") && typeof event.delta === "string") {
1717
+ reasoningContent += event.delta;
1718
+ request.callbacks?.onReasoningDelta?.(event.delta);
1719
+ continue;
1720
+ }
1721
+ if (event.type === "response.function_call_arguments.delta" && typeof event.delta === "string") {
1722
+ const index = typeof event.output_index === "number" ? event.output_index : 0;
1723
+ const existing = toolCalls.get(index) ?? {
1724
+ id: event.item_id ?? `tool-${index}`,
1725
+ name: "",
1726
+ arguments: ""
1727
+ };
1728
+ existing.arguments += event.delta;
1729
+ toolCalls.set(index, existing);
1730
+ continue;
1731
+ }
1732
+ if (event.type === "response.function_call_arguments.done") {
1733
+ const index = typeof event.output_index === "number" ? event.output_index : 0;
1734
+ const existing = toolCalls.get(index) ?? {
1735
+ id: event.item_id ?? `tool-${index}`,
1736
+ name: "",
1737
+ arguments: ""
1738
+ };
1739
+ if (typeof event.name === "string") {
1740
+ existing.name = event.name;
1741
+ }
1742
+ if (typeof event.arguments === "string" && event.arguments.length > 0) {
1743
+ existing.arguments = event.arguments;
1744
+ }
1745
+ toolCalls.set(index, existing);
1746
+ continue;
1747
+ }
1748
+ if (event.type === "response.output_item.done" && event.item?.type === "function_call") {
1749
+ const index = typeof event.output_index === "number" ? event.output_index : 0;
1750
+ toolCalls.set(index, {
1751
+ id: event.item.call_id ?? event.item.id ?? `tool-${index}`,
1752
+ name: event.item.name ?? "",
1753
+ arguments: event.item.arguments ?? ""
1754
+ });
1755
+ }
1756
+ }
1757
+ return {
1758
+ content: content.length > 0 ? content : null,
1759
+ reasoningContent: reasoningContent.length > 0 ? reasoningContent : void 0,
1760
+ streamedAssistantContent: content.length > 0,
1761
+ streamedReasoningContent: reasoningContent.length > 0,
1762
+ toolCalls: [...toolCalls.entries()].sort((left, right) => left[0] - right[0]).map(([, toolCall]) => ({
1763
+ id: toolCall.id,
1764
+ type: "function",
1765
+ function: {
1766
+ name: toolCall.name,
1767
+ arguments: toolCall.arguments
1768
+ }
1769
+ }))
1770
+ };
1771
+ } finally {
1772
+ request.onRequestMetric?.({
1773
+ durationMs: Date.now() - startedAt,
1774
+ usage
1775
+ });
1776
+ }
1777
+ },
1778
+ async fetchNonStreaming(client, request) {
1779
+ const startedAt = Date.now();
1780
+ let usage;
1781
+ throwIfAborted(request.abortSignal, "Request aborted");
1782
+ try {
1783
+ const response = await client.responses.create(
1784
+ {
1785
+ ...buildResponsesRequestBody(request),
1786
+ stream: false
1787
+ },
1788
+ {
1789
+ signal: request.abortSignal
1790
+ }
1791
+ );
1792
+ usage = normalizeProviderUsage(response.usage);
1793
+ return {
1794
+ content: normalizeResponsesOutputText(response),
1795
+ reasoningContent: readResponsesReasoning(response),
1796
+ streamedAssistantContent: false,
1797
+ streamedReasoningContent: false,
1798
+ toolCalls: readResponsesToolCalls(response)
1799
+ };
1800
+ } finally {
1801
+ request.onRequestMetric?.({
1802
+ durationMs: Date.now() - startedAt,
1803
+ usage
1804
+ });
1805
+ }
1806
+ }
1807
+ };
1804
1808
  function abortStream2(stream) {
1805
1809
  try {
1806
1810
  stream?.controller?.abort();
@@ -2254,25 +2258,25 @@ function buildSessionConversationBriefBlock(brief) {
2254
2258
  if (!brief?.modelSummary) {
2255
2259
  return void 0;
2256
2260
  }
2257
- return buildFieldBlock("Internal continuity state", [
2261
+ return buildFieldBlock("Conversation continuity evidence", [
2258
2262
  {
2259
2263
  label: "Purpose",
2260
2264
  value: "Use these facts as private continuity state. Answer the current request directly. Quote prior turns only when the user asks."
2261
2265
  },
2262
2266
  brief.modelSummary ? {
2263
- label: "Session memory",
2267
+ label: "Model-written session memory",
2264
2268
  value: brief.modelSummary
2265
- } : { label: "Session memory", value: void 0 },
2269
+ } : { label: "Model-written session memory", value: void 0 },
2266
2270
  brief.modelSummaryUpdatedAt ? {
2267
- label: "Memory updated at",
2271
+ label: "Updated",
2268
2272
  value: brief.modelSummaryUpdatedAt
2269
- } : { label: "Memory updated at", value: void 0 },
2273
+ } : { label: "Updated", value: void 0 },
2270
2274
  {
2271
- label: "Visible turns",
2275
+ label: "Near-field visible turns",
2272
2276
  value: `${brief.userTurnCount} user turn(s) with current input / ${brief.assistantTurnCount} assistant response(s)`
2273
2277
  },
2274
2278
  {
2275
- label: "Tool activity",
2279
+ label: "Recent tool activity",
2276
2280
  value: formatSignals(brief.toolActivity)
2277
2281
  }
2278
2282
  ]);
@@ -2400,7 +2404,7 @@ function buildHistoryBoundaryBlock(memory) {
2400
2404
  return buildFieldBlock("History boundary", [
2401
2405
  {
2402
2406
  label: "Policy",
2403
- value: "Raw session history stays out of the current request. Internal continuity state and current workset are automatic facts for judgment, not text to narrate."
2407
+ value: "Raw session history stays out of the current request. Conversation continuity evidence and current workset are automatic facts for judgment, not text to narrate."
2404
2408
  }
2405
2409
  ]);
2406
2410
  }
@@ -2643,6 +2647,10 @@ function buildTaskLifecyclePromptBlock(lifecycle) {
2643
2647
  return void 0;
2644
2648
  }
2645
2649
  const fields = [
2650
+ {
2651
+ label: "Purpose",
2652
+ value: "Current task-state evidence. Use it to orient the next action; do not treat it as a new user request."
2653
+ },
2646
2654
  { label: "Stage", value: lifecycle.stage },
2647
2655
  lifecycle.scope ? { label: "Scope", value: lifecycle.scope } : void 0,
2648
2656
  lifecycle.boundary ? { label: "Boundary", value: lifecycle.boundary } : void 0,
@@ -2653,14 +2661,14 @@ function buildTaskLifecyclePromptBlock(lifecycle) {
2653
2661
  lifecycle.completionFacts.length > 0 ? { label: "Completion facts", value: formatLimitedList(lifecycle.completionFacts, 4) } : void 0,
2654
2662
  { label: "Updated", value: lifecycle.updatedAt }
2655
2663
  ];
2656
- return buildFieldBlock("Task lifecycle", fields.filter((field) => Boolean(field)));
2664
+ return buildFieldBlock("Current task scene evidence", fields.filter((field) => Boolean(field)));
2657
2665
  }
2658
2666
  function buildProjectMapPromptBlock(projectMap) {
2659
2667
  if (!projectMap) {
2660
2668
  return void 0;
2661
2669
  }
2662
2670
  const fields = [
2663
- { label: "Purpose", value: "Machine facts for orientation. Use as evidence, not as a route command." },
2671
+ { label: "Purpose", value: "Project orientation evidence. Use as facts for the current turn; do not treat this block as a task route." },
2664
2672
  { label: "Root", value: projectMap.rootDir },
2665
2673
  { label: "Top-level dirs", value: formatLimitedList(projectMap.topLevelDirectories, 10) },
2666
2674
  { label: "Entries", value: formatLimitedList(projectMap.entryFiles, 8) },
@@ -2674,7 +2682,7 @@ function buildProjectMapPromptBlock(projectMap) {
2674
2682
  projectMap.git.recentChanges.length > 0 ? { label: "Recent changes", value: formatLimitedList(projectMap.git.recentChanges, 6) } : void 0,
2675
2683
  { label: "Updated", value: projectMap.updatedAt }
2676
2684
  ];
2677
- return buildFieldBlock("Project map", fields.filter((field) => Boolean(field)));
2685
+ return buildFieldBlock("Project orientation evidence", fields.filter((field) => Boolean(field)));
2678
2686
  }
2679
2687
 
2680
2688
  // src/agent/prompt/metrics.ts
@@ -5117,7 +5125,7 @@ function pathDepth(relativePath) {
5117
5125
  return relativePath.split("/").filter(Boolean).length;
5118
5126
  }
5119
5127
 
5120
- // src/tools/outputKernel/classifier.ts
5128
+ // src/tools/outputGovernance/classifier.ts
5121
5129
  function classifyToolOutput(source) {
5122
5130
  const output = source.output.trim();
5123
5131
  if (!output) {
@@ -5158,7 +5166,7 @@ function looksLikeBuild(command, text) {
5158
5166
  return /\b(build|compile|cargo check|cargo clippy|npm run build|pnpm build)\b/.test(command) || /\b(compilation failed|build failed|compiled successfully|error\[e\d+\])\b/.test(text);
5159
5167
  }
5160
5168
 
5161
- // src/tools/outputKernel/metrics.ts
5169
+ // src/tools/outputGovernance/metrics.ts
5162
5170
  function estimateTextTokens(value) {
5163
5171
  const trimmed = value.trim();
5164
5172
  if (!trimmed) {
@@ -5183,7 +5191,7 @@ function computeSavings(input) {
5183
5191
  };
5184
5192
  }
5185
5193
 
5186
- // src/tools/outputKernel/projectors/shared.ts
5194
+ // src/tools/outputGovernance/projectors/shared.ts
5187
5195
  function buildHeader(source, label) {
5188
5196
  return [
5189
5197
  `${source.toolName}: ${label}`,
@@ -5209,7 +5217,7 @@ function dedupeProjectedLines(lines) {
5209
5217
  return result;
5210
5218
  }
5211
5219
 
5212
- // src/tools/outputKernel/projectors/diagnostic.ts
5220
+ // src/tools/outputGovernance/projectors/diagnostic.ts
5213
5221
  var STRUCTURED_MAX_LINES = 28;
5214
5222
  function buildDiagnosticProjection(source, label) {
5215
5223
  const lines = splitOutputLines(source.output);
@@ -5228,7 +5236,7 @@ function isSummaryLine(line) {
5228
5236
  return /\b(\d+\s+(passed|failed|skipped|errors?|warnings?)|test result|found \d+ errors?|failed tests?|build failed|compiled successfully)\b/i.test(line);
5229
5237
  }
5230
5238
 
5231
- // src/tools/outputKernel/projectors/gitDiff.ts
5239
+ // src/tools/outputGovernance/projectors/gitDiff.ts
5232
5240
  var DIFF_MAX_FILES = 24;
5233
5241
  function buildGitDiffProjection(source) {
5234
5242
  const lines = splitOutputLines(source.output);
@@ -5243,7 +5251,7 @@ function buildGitDiffProjection(source) {
5243
5251
  ].filter((line) => Boolean(line)).join("\n");
5244
5252
  }
5245
5253
 
5246
- // src/tools/outputKernel/projectors/generic.ts
5254
+ // src/tools/outputGovernance/projectors/generic.ts
5247
5255
  var GENERIC_MAX_CHARS = 1500;
5248
5256
  function projectEmptyOutput(source) {
5249
5257
  return {
@@ -5285,7 +5293,7 @@ function buildGenericPreview(source) {
5285
5293
  ].filter(Boolean).join("\n");
5286
5294
  }
5287
5295
 
5288
- // src/tools/outputKernel/projectors/search.ts
5296
+ // src/tools/outputGovernance/projectors/search.ts
5289
5297
  var SEARCH_MAX_MATCHES = 24;
5290
5298
  function buildSearchProjection(source) {
5291
5299
  const nonEmptyLines = splitOutputLines(source.output).filter((line) => line.trim().length > 0);
@@ -5298,7 +5306,7 @@ function buildSearchProjection(source) {
5298
5306
  ].join("\n");
5299
5307
  }
5300
5308
 
5301
- // src/tools/outputKernel/projectors/recovery.ts
5309
+ // src/tools/outputGovernance/projectors/recovery.ts
5302
5310
  function appendRecoveryHint(projection, governance) {
5303
5311
  if (!governance.recoveryHint) {
5304
5312
  return projection;
@@ -5310,7 +5318,7 @@ function appendRecoveryHint(projection, governance) {
5310
5318
  ${governance.recoveryHint}`;
5311
5319
  }
5312
5320
 
5313
- // src/tools/outputKernel/projectors.ts
5321
+ // src/tools/outputGovernance/projectors.ts
5314
5322
  function projectOutputByKind(kind, source) {
5315
5323
  switch (kind) {
5316
5324
  case "empty":
@@ -5330,7 +5338,7 @@ function projectOutputByKind(kind, source) {
5330
5338
  }
5331
5339
  }
5332
5340
 
5333
- // src/tools/outputKernel/index.ts
5341
+ // src/tools/outputGovernance/index.ts
5334
5342
  function governToolOutput(source) {
5335
5343
  const kind = classifyToolOutput(source);
5336
5344
  const projected = projectOutputByKind(kind, source);
@@ -10156,6 +10164,53 @@ function looksLikeToolProtocolText(content) {
10156
10164
  return text.includes("<\uFF5C\uFF5CDSML\uFF5C\uFF5Ctool_calls>") || text.includes("<tool_call>") || text.includes('"tool_calls"');
10157
10165
  }
10158
10166
 
10167
+ // src/host/delegatedCloseout.ts
10168
+ async function completeExactDelegatedCloseout(input) {
10169
+ const answer = resolveExactDelegatedAnswer(input.executions);
10170
+ if (!answer) {
10171
+ return void 0;
10172
+ }
10173
+ const transition = createFinalizeTransition({
10174
+ changedPaths: []
10175
+ });
10176
+ const sessionWithAnswer = await input.sessionStore.appendMessages(input.session, [
10177
+ createMessage("assistant", answer)
10178
+ ]);
10179
+ const session = await input.sessionStore.save(noteCheckpointCompleted(sessionWithAnswer, transition));
10180
+ const ledger = new ControlPlaneLedger(input.stateRootDir);
10181
+ try {
10182
+ ledger.taskLifecycle.complete({
10183
+ sessionId: session.id,
10184
+ reason: "finalize.delegated_exact_output",
10185
+ completionFacts: [answer]
10186
+ });
10187
+ } finally {
10188
+ ledger.close();
10189
+ }
10190
+ input.callbacks?.onAssistantText?.(answer);
10191
+ input.callbacks?.onAssistantDone?.(answer);
10192
+ return buildRunTurnResult({
10193
+ session,
10194
+ changedPaths: [],
10195
+ transition
10196
+ });
10197
+ }
10198
+ function resolveExactDelegatedAnswer(executions) {
10199
+ if (executions.length !== 1) {
10200
+ return void 0;
10201
+ }
10202
+ const [execution] = executions;
10203
+ if (!execution || execution.status !== "completed") {
10204
+ return void 0;
10205
+ }
10206
+ const expected = execution.assignment?.expectedOutput?.trim();
10207
+ const output = execution.output?.trim();
10208
+ if (!expected || !output || expected !== output) {
10209
+ return void 0;
10210
+ }
10211
+ return output;
10212
+ }
10213
+
10159
10214
  // src/observability/crashRecorder.ts
10160
10215
  import fs24 from "fs";
10161
10216
  import path26 from "path";
@@ -10432,51 +10487,6 @@ async function runHostTurn(options, dependencies = {}) {
10432
10487
  await toolRegistry?.close?.().catch(() => void 0);
10433
10488
  }
10434
10489
  }
10435
- async function completeExactDelegatedCloseout(input) {
10436
- const answer = resolveExactDelegatedAnswer(input.executions);
10437
- if (!answer) {
10438
- return void 0;
10439
- }
10440
- const transition = createFinalizeTransition({
10441
- changedPaths: []
10442
- });
10443
- const sessionWithAnswer = await input.sessionStore.appendMessages(input.session, [
10444
- createMessage("assistant", answer)
10445
- ]);
10446
- const session = await input.sessionStore.save(noteCheckpointCompleted(sessionWithAnswer, transition));
10447
- const ledger = new ControlPlaneLedger(input.stateRootDir);
10448
- try {
10449
- ledger.taskLifecycle.complete({
10450
- sessionId: session.id,
10451
- reason: "finalize.delegated_exact_output",
10452
- completionFacts: [answer]
10453
- });
10454
- } finally {
10455
- ledger.close();
10456
- }
10457
- input.callbacks?.onAssistantText?.(answer);
10458
- input.callbacks?.onAssistantDone?.(answer);
10459
- return buildRunTurnResult({
10460
- session,
10461
- changedPaths: [],
10462
- transition
10463
- });
10464
- }
10465
- function resolveExactDelegatedAnswer(executions) {
10466
- if (executions.length !== 1) {
10467
- return void 0;
10468
- }
10469
- const [execution] = executions;
10470
- if (!execution || execution.status !== "completed") {
10471
- return void 0;
10472
- }
10473
- const expected = execution.assignment?.expectedOutput?.trim();
10474
- const output = execution.output?.trim();
10475
- if (!expected || !output || expected !== output) {
10476
- return void 0;
10477
- }
10478
- return output;
10479
- }
10480
10490
  function createToollessRegistry(registry) {
10481
10491
  return {
10482
10492
  ...registry,