@jun133/kitty 0.0.11 → 0.0.13

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/dist/cli.js CHANGED
@@ -40,7 +40,7 @@ var init_package = __esm({
40
40
  "package.json"() {
41
41
  package_default = {
42
42
  name: "@jun133/kitty",
43
- version: "0.0.11",
43
+ version: "0.0.13",
44
44
  description: "Agent",
45
45
  license: "MIT",
46
46
  keywords: [
@@ -9928,7 +9928,7 @@ var init_projectContext = __esm({
9928
9928
 
9929
9929
  // src/runtime/scene.ts
9930
9930
  function buildRuntimeScene(status) {
9931
- const executions = status.executions.active.map(buildExecutionScene);
9931
+ const executions = readSceneExecutions(status);
9932
9932
  const blockedExecutions = executions.filter((execution) => execution.risk === "blocked");
9933
9933
  const watchExecutions = executions.filter((execution) => execution.risk === "watch");
9934
9934
  const activeBackground = executions.filter((execution) => execution.kind === "background");
@@ -9939,6 +9939,7 @@ function buildRuntimeScene(status) {
9939
9939
  nextAction: readNextAction(status, blockedExecutions, watchExecutions),
9940
9940
  blocked: readBlocked(blockedExecutions),
9941
9941
  cost: readCost(status),
9942
+ toolOutputs: readToolOutputs(status),
9942
9943
  recovery: readRecovery(status, executions),
9943
9944
  skills: {
9944
9945
  ready: status.skills.ready,
@@ -9958,6 +9959,19 @@ function buildRuntimeScene(status) {
9958
9959
  executions
9959
9960
  };
9960
9961
  }
9962
+ function readSceneExecutions(status) {
9963
+ const byId = /* @__PURE__ */ new Map();
9964
+ for (const execution of status.executions.active) {
9965
+ byId.set(execution.id, buildExecutionScene(execution));
9966
+ }
9967
+ for (const execution of status.executions.recent) {
9968
+ const scene = buildExecutionScene(execution);
9969
+ if (scene.risk !== "none" && !byId.has(scene.id)) {
9970
+ byId.set(scene.id, scene);
9971
+ }
9972
+ }
9973
+ return [...byId.values()];
9974
+ }
9961
9975
  function buildExecutionScene(execution) {
9962
9976
  const risk = readExecutionRisk(execution);
9963
9977
  return {
@@ -9972,9 +9986,6 @@ function buildExecutionScene(execution) {
9972
9986
  };
9973
9987
  }
9974
9988
  function buildHeadline(status, blockedExecutions, watchExecutions) {
9975
- if (!status.sessions.latest) {
9976
- return "No active session yet.";
9977
- }
9978
9989
  if (blockedExecutions.length > 0) {
9979
9990
  return `${blockedExecutions.length} execution(s) need attention.`;
9980
9991
  }
@@ -9984,6 +9995,9 @@ function buildHeadline(status, blockedExecutions, watchExecutions) {
9984
9995
  if (status.executions.active.length > 0) {
9985
9996
  return `${status.executions.active.length} execution(s) are running.`;
9986
9997
  }
9998
+ if (!status.sessions.latest) {
9999
+ return "No active session yet.";
10000
+ }
9987
10001
  return "Ready to continue the latest session.";
9988
10002
  }
9989
10003
  function readFocus(status) {
@@ -10025,6 +10039,23 @@ function readCost(status) {
10025
10039
  const usageText = latest?.usage ? readUsageCost(latest.usage) : latest ? "provider usage unavailable" : "no model request yet";
10026
10040
  return `${budgetText}; ${layoutText}; ${usageText}`;
10027
10041
  }
10042
+ function readToolOutputs(status) {
10043
+ const recent = status.toolOutputs.recent;
10044
+ if (recent.length === 0) {
10045
+ return "no tool output governance yet";
10046
+ }
10047
+ const saved = recent.reduce((total, item) => total + (item.savedTokens ?? 0), 0);
10048
+ const truncated = recent.filter((item) => item.truncated).length;
10049
+ const degraded = recent.filter((item) => item.degraded).length;
10050
+ const best = recent.filter((item) => typeof item.savedTokens === "number").sort((a, b) => (b.savedTokens ?? 0) - (a.savedTokens ?? 0))[0];
10051
+ return [
10052
+ `${recent.length} recent`,
10053
+ `${saved} tokens saved est.`,
10054
+ truncated > 0 ? `${truncated} recoverable` : void 0,
10055
+ degraded > 0 ? `${degraded} degraded` : void 0,
10056
+ best ? `top=${best.toolName ?? "tool"}:${best.kind ?? "output"}` : void 0
10057
+ ].filter(Boolean).join("; ");
10058
+ }
10028
10059
  function readStableRatio(stableChars, volatileChars) {
10029
10060
  const total = stableChars + volatileChars;
10030
10061
  return total > 0 ? `${Math.round(stableChars / total * 100)}%` : "unknown";
@@ -10152,13 +10183,14 @@ async function buildRuntimeStatus(rootDir) {
10152
10183
  const sessionStore = new SessionStore(paths.sessionsDir, {
10153
10184
  memorySessionsDir: paths.sessionMemoryDir
10154
10185
  });
10155
- const [sessionRead, memoryAssets, control, projectMap, projectContext, modelRequests] = await Promise.all([
10186
+ const [sessionRead, memoryAssets, control, projectMap, projectContext, modelRequests, toolOutputs] = await Promise.all([
10156
10187
  sessionStore.listReadable?.(DEFAULT_RECENT_LIMIT) ?? sessionStore.list(DEFAULT_RECENT_LIMIT).then((sessions2) => ({ sessions: sessions2, skipped: [] })),
10157
10188
  listRuntimeMemoryAssets(paths.rootDir),
10158
10189
  readControlPlaneStatus(paths.rootDir),
10159
10190
  buildProjectMap(paths.rootDir),
10160
10191
  loadProjectContext(paths.rootDir, { projectDocMaxBytes: 24576 }),
10161
- readRecentModelRequests(paths.observabilityEventsDir)
10192
+ readRecentModelRequests(paths.observabilityEventsDir),
10193
+ readRecentToolOutputs(paths.observabilityEventsDir)
10162
10194
  ]);
10163
10195
  const sessions = sessionRead.sessions.map(summarizeSession);
10164
10196
  const taskLifecycle = sessions[0] ? readTaskLifecycleStatus(paths.rootDir, sessions[0].id) : void 0;
@@ -10179,6 +10211,9 @@ async function buildRuntimeStatus(rootDir) {
10179
10211
  modelRequests: {
10180
10212
  recent: modelRequests
10181
10213
  },
10214
+ toolOutputs: {
10215
+ recent: toolOutputs
10216
+ },
10182
10217
  taskLifecycle,
10183
10218
  executions: control.executions,
10184
10219
  wakeSignals: control.wakeSignals
@@ -10253,6 +10288,25 @@ async function readRecentModelRequests(eventsDir) {
10253
10288
  }
10254
10289
  return records.slice(-DEFAULT_RECENT_LIMIT).reverse();
10255
10290
  }
10291
+ async function readRecentToolOutputs(eventsDir) {
10292
+ const files = await import_promises24.default.readdir(eventsDir).catch(() => []);
10293
+ const jsonlFiles = files.filter((file) => file.endsWith(".jsonl")).sort().slice(-3);
10294
+ const records = [];
10295
+ for (const file of jsonlFiles) {
10296
+ const content = await import_promises24.default.readFile(import_node_path37.default.join(eventsDir, file), "utf8").catch(() => "");
10297
+ for (const line of content.split(/\r?\n/)) {
10298
+ if (!line.trim()) {
10299
+ continue;
10300
+ }
10301
+ const record = parseObservabilityRecord(line);
10302
+ if (!record || record.event !== "tool.output") {
10303
+ continue;
10304
+ }
10305
+ records.push(summarizeToolOutput(record));
10306
+ }
10307
+ }
10308
+ return records.slice(-DEFAULT_RECENT_LIMIT).reverse();
10309
+ }
10256
10310
  function parseObservabilityRecord(line) {
10257
10311
  try {
10258
10312
  const parsed = JSON.parse(line);
@@ -10273,6 +10327,25 @@ function summarizeModelRequest(record) {
10273
10327
  usage
10274
10328
  };
10275
10329
  }
10330
+ function summarizeToolOutput(record) {
10331
+ const details = record.details ?? {};
10332
+ return {
10333
+ timestamp: record.timestamp,
10334
+ toolName: record.toolName,
10335
+ kind: readString4(details.kind),
10336
+ mode: readString4(details.mode),
10337
+ rawChars: readNumber(details.rawChars),
10338
+ projectedChars: readNumber(details.projectedChars),
10339
+ rawTokens: readNumber(details.rawTokens),
10340
+ projectedTokens: readNumber(details.projectedTokens),
10341
+ savedTokens: readNumber(details.savedTokens),
10342
+ savingsRatio: readNumber(details.savingsRatio),
10343
+ truncated: details.truncated === true,
10344
+ outputPath: readString4(details.outputPath),
10345
+ degraded: details.degraded === true,
10346
+ reason: readString4(details.reason)
10347
+ };
10348
+ }
10276
10349
  function readUsageSummary(value) {
10277
10350
  if (!value || typeof value !== "object") {
10278
10351
  return void 0;
@@ -10294,6 +10367,9 @@ function readUsageSummary(value) {
10294
10367
  function readNumber(value) {
10295
10368
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
10296
10369
  }
10370
+ function readString4(value) {
10371
+ return typeof value === "string" && value.length > 0 ? value : void 0;
10372
+ }
10297
10373
  function summarizeSkills(skills) {
10298
10374
  const summaries = skills.map((skill) => ({
10299
10375
  name: skill.name,
@@ -12009,6 +12085,7 @@ function buildToolBlock() {
12009
12085
  function buildCommunicationBlock() {
12010
12086
  return [
12011
12087
  "Always reply in Simplified Chinese.",
12088
+ "No Markdown in user-facing conversational replies; prefer plain text whenever possible.",
12012
12089
  "Provide concise progress updates during multi-step work.",
12013
12090
  "Make every sentence carry decision, execution, evidence, or understanding.",
12014
12091
  "Claim changed files, passed commands, and successful tools only when tool evidence supports them.",
@@ -12443,11 +12520,15 @@ function formatRuntimeStatusText(status) {
12443
12520
  lines.push(`- Skills: ${status.scene.skills.ready}/${status.scene.skills.total} ready; ${status.scene.skills.nextAction}`);
12444
12521
  lines.push(`- Memory: ${status.scene.memory.assets} asset(s), session=${status.scene.memory.latestSessionMemory ? "yes" : "no"}; ${status.scene.memory.nextAction}`);
12445
12522
  lines.push(`- Cost: ${status.scene.cost}`);
12523
+ lines.push(`- Tool output: ${status.scene.toolOutputs}`);
12446
12524
  lines.push(`- Recovery: ${status.scene.recovery}`);
12447
12525
  lines.push("");
12448
12526
  lines.push("Current workspace:");
12449
12527
  lines.push(`- Focus: ${status.scene.focus}`);
12450
12528
  lines.push(`- Session: ${readSessionLine(status)}`);
12529
+ if (status.sessions.skipped > 0) {
12530
+ lines.push(`- Sessions: ${status.sessions.total} total, ${status.sessions.skipped} skipped`);
12531
+ }
12451
12532
  lines.push(`- Next: ${status.scene.nextAction}`);
12452
12533
  lines.push(`- Blocked: ${status.scene.blocked}`);
12453
12534
  lines.push(`- Context budget: ${readContextBudgetLine(status)}`);
@@ -12554,9 +12635,27 @@ function formatRuntimeStatusText(status) {
12554
12635
  ].filter(Boolean).join(" "));
12555
12636
  }
12556
12637
  }
12557
- if (status.executions.active.length > 0) {
12638
+ if (status.toolOutputs.recent.length > 0) {
12639
+ lines.push("");
12640
+ lines.push("Recent tool output:");
12641
+ for (const output of status.toolOutputs.recent.slice(0, 5)) {
12642
+ lines.push([
12643
+ output.toolName ?? "tool",
12644
+ output.kind ? `kind=${output.kind}` : void 0,
12645
+ output.mode ? `mode=${output.mode}` : void 0,
12646
+ output.rawTokens === void 0 ? void 0 : `raw=${output.rawTokens}`,
12647
+ output.projectedTokens === void 0 ? void 0 : `projected=${output.projectedTokens}`,
12648
+ output.savedTokens === void 0 ? void 0 : `saved=${output.savedTokens}`,
12649
+ output.savingsRatio === void 0 ? void 0 : `savedRatio=${Math.round(output.savingsRatio * 100)}%`,
12650
+ output.truncated ? "recoverable=yes" : void 0,
12651
+ output.degraded ? "degraded=yes" : void 0,
12652
+ output.outputPath ? `full=${truncateCliValue(output.outputPath, 80)}` : void 0
12653
+ ].filter(Boolean).join(" "));
12654
+ }
12655
+ }
12656
+ if (status.scene.executions.length > 0) {
12558
12657
  lines.push("");
12559
- lines.push("Active executions:");
12658
+ lines.push("Scene executions:");
12560
12659
  for (const execution of status.scene.executions) {
12561
12660
  lines.push([
12562
12661
  execution.id,
@@ -13339,6 +13438,9 @@ var init_leadWait2 = __esm({
13339
13438
 
13340
13439
  // src/agent/toolResults/modelProjection.ts
13341
13440
  function projectToolResultForModel(input) {
13441
+ if (input.result.metadata?.outputGovernance) {
13442
+ return input.result.metadata.outputGovernance.projection;
13443
+ }
13342
13444
  const parsed = parseObject(input.result.output);
13343
13445
  if (!input.result.ok) {
13344
13446
  return projectFailure(input.toolName, input.result.output, parsed);
@@ -13370,14 +13472,14 @@ function projectToolResultForModel(input) {
13370
13472
  function projectExecutionAction(payload) {
13371
13473
  const execution = readObject2(payload.execution) ?? payload;
13372
13474
  return joinLines([
13373
- readString4(execution.id) ?? "execution",
13374
- readString4(execution.kind),
13375
- readString4(execution.status),
13376
- readString4(execution.command),
13377
- readString4(execution.summary),
13378
- readString4(execution.outputPreview),
13379
- readObject2(execution.health) ? readString4(readObject2(execution.health)?.message) : void 0,
13380
- readString4(execution.error)
13475
+ readString5(execution.id) ?? "execution",
13476
+ readString5(execution.kind),
13477
+ readString5(execution.status),
13478
+ readString5(execution.command),
13479
+ readString5(execution.summary),
13480
+ readString5(execution.outputPreview),
13481
+ readObject2(execution.health) ? readString5(readObject2(execution.health)?.message) : void 0,
13482
+ readString5(execution.error)
13381
13483
  ]);
13382
13484
  }
13383
13485
  function projectExecutionCheck(payload) {
@@ -13387,12 +13489,12 @@ function projectExecutionCheck(payload) {
13387
13489
  const stale = readArray(payload.stale) ?? [];
13388
13490
  const entries = [...active, ...recent].filter((item) => Boolean(item) && typeof item === "object" && !Array.isArray(item)).slice(0, 8).map((item) => {
13389
13491
  const parts = [
13390
- readString4(item.id) ?? "execution",
13391
- readString4(item.kind),
13392
- readString4(item.status),
13393
- readString4(item.summary),
13394
- readString4(item.outputPreview),
13395
- readString4(item.error)
13492
+ readString5(item.id) ?? "execution",
13493
+ readString5(item.kind),
13494
+ readString5(item.status),
13495
+ readString5(item.summary),
13496
+ readString5(item.outputPreview),
13497
+ readString5(item.error)
13396
13498
  ].filter((part) => Boolean(part));
13397
13499
  return parts.join(" ");
13398
13500
  });
@@ -13403,17 +13505,17 @@ function projectExecutionCheck(payload) {
13403
13505
  ]);
13404
13506
  }
13405
13507
  function projectRead(payload) {
13406
- const path59 = readString4(payload.path) ?? readString4(payload.requestedPath) ?? "file";
13508
+ const path59 = readString5(payload.path) ?? readString5(payload.requestedPath) ?? "file";
13407
13509
  if (payload.readable === false) {
13408
13510
  return joinLines([
13409
13511
  `${path59}: not readable`,
13410
- readString4(payload.reason),
13411
- readString4(payload.detectedCapability) ? `capability: ${readString4(payload.detectedCapability)}` : void 0
13512
+ readString5(payload.reason),
13513
+ readString5(payload.detectedCapability) ? `capability: ${readString5(payload.detectedCapability)}` : void 0
13412
13514
  ]);
13413
13515
  }
13414
13516
  const startLine = readNumber2(payload.startLine);
13415
13517
  const endLine = readNumber2(payload.endLine);
13416
- const content = readString4(payload.content) ?? "";
13518
+ const content = readString5(payload.content) ?? "";
13417
13519
  const continuation = readObject2(payload.continuation);
13418
13520
  const continuationArgs = readObject2(continuation?.continuationArgs);
13419
13521
  return joinLines([
@@ -13423,29 +13525,34 @@ function projectRead(payload) {
13423
13525
  ]);
13424
13526
  }
13425
13527
  function projectEdit(payload) {
13426
- const path59 = readString4(payload.path) ?? "file";
13528
+ const path59 = readString5(payload.path) ?? "file";
13427
13529
  const applied = readNumber2(payload.appliedEdits) ?? readNumber2(payload.requestedEdits);
13428
- const diff = readString4(payload.diff) ?? readString4(payload.preview);
13530
+ const diff = readString5(payload.diff) ?? readString5(payload.preview);
13429
13531
  return joinLines([
13430
13532
  `edited ${path59}${applied ? ` (${applied} replacement${applied === 1 ? "" : "s"})` : ""}`,
13431
13533
  diff ? truncateText(diff, DIFF_MAX_CHARS) : void 0
13432
13534
  ]);
13433
13535
  }
13434
13536
  function projectWrite(payload) {
13435
- const path59 = readString4(payload.path) ?? "file";
13537
+ const path59 = readString5(payload.path) ?? "file";
13436
13538
  const bytes = readNumber2(payload.bytes);
13437
13539
  const existed = payload.existed === true;
13438
- const diff = readString4(payload.diff) ?? readString4(payload.preview);
13540
+ const diff = readString5(payload.diff) ?? readString5(payload.preview);
13439
13541
  return joinLines([
13440
13542
  `${existed ? "wrote" : "created"} ${path59}${bytes !== void 0 ? ` (${bytes} bytes)` : ""}`,
13441
13543
  diff ? truncateText(diff, DIFF_MAX_CHARS) : void 0
13442
13544
  ]);
13443
13545
  }
13444
13546
  function projectBash(payload) {
13547
+ const governance = readObject2(payload.outputGovernance);
13548
+ const projection = readString5(governance?.projection);
13549
+ if (projection) {
13550
+ return projection;
13551
+ }
13445
13552
  const exitCode = readNumber2(payload.exitCode);
13446
13553
  const durationMs = readNumber2(payload.durationMs);
13447
- const status = readString4(payload.status);
13448
- const output = readString4(payload.output);
13554
+ const status = readString5(payload.status);
13555
+ const output = readString5(payload.output);
13449
13556
  const lines = [
13450
13557
  `exit ${exitCode ?? "?"}${durationMs !== void 0 ? ` in ${durationMs}ms` : ""}${status && status !== "completed" ? ` (${status})` : ""}`
13451
13558
  ];
@@ -13459,10 +13566,10 @@ function projectBash(payload) {
13459
13566
  }
13460
13567
  function projectSkillLoad(payload) {
13461
13568
  const skill = readObject2(payload.skill);
13462
- const name = readString4(skill?.name) ?? "skill";
13463
- const description = readString4(skill?.description);
13464
- const path59 = readString4(skill?.path);
13465
- const body = readString4(payload.body) ?? "";
13569
+ const name = readString5(skill?.name) ?? "skill";
13570
+ const description = readString5(skill?.description);
13571
+ const path59 = readString5(skill?.path);
13572
+ const body = readString5(payload.body) ?? "";
13466
13573
  return joinLines([
13467
13574
  `loaded skill: ${name}${path59 ? ` (${path59})` : ""}`,
13468
13575
  description,
@@ -13471,10 +13578,10 @@ function projectSkillLoad(payload) {
13471
13578
  }
13472
13579
  function projectGenericSuccess(payload, rawOutput) {
13473
13580
  const lines = [
13474
- readString4(payload.summary),
13475
- readString4(payload.preview),
13476
- readString4(payload.output),
13477
- readString4(payload.content)
13581
+ readString5(payload.summary),
13582
+ readString5(payload.preview),
13583
+ readString5(payload.output),
13584
+ readString5(payload.content)
13478
13585
  ].filter((line) => Boolean(line));
13479
13586
  if (lines.length > 0) {
13480
13587
  return truncateText(lines.join("\n"), DEFAULT_MAX_CHARS);
@@ -13498,9 +13605,9 @@ function projectFailure(toolName, rawOutput, payload) {
13498
13605
  const readArgs = readObject2(details?.readArgs);
13499
13606
  const suggestions = readArray(details?.suggestions);
13500
13607
  const lines = [
13501
- `${toolName} failed: ${readString4(payload.error) ?? "unknown error"}`,
13502
- readString4(payload.code) ? `code: ${readString4(payload.code)}` : void 0,
13503
- readString4(payload.hint) ? `hint: ${readString4(payload.hint)}` : void 0,
13608
+ `${toolName} failed: ${readString5(payload.error) ?? "unknown error"}`,
13609
+ readString5(payload.code) ? `code: ${readString5(payload.code)}` : void 0,
13610
+ readString5(payload.hint) ? `hint: ${readString5(payload.hint)}` : void 0,
13504
13611
  readArgs ? `read: read ${JSON.stringify(readArgs)}` : void 0,
13505
13612
  suggestions && suggestions.length > 0 ? `suggestions: ${suggestions.slice(0, 5).map((item) => String(item)).join(", ")}` : void 0
13506
13613
  ];
@@ -13523,7 +13630,7 @@ function readObject2(value) {
13523
13630
  function readArray(value) {
13524
13631
  return Array.isArray(value) ? value : void 0;
13525
13632
  }
13526
- function readString4(value) {
13633
+ function readString5(value) {
13527
13634
  return typeof value === "string" && value.length > 0 ? value : void 0;
13528
13635
  }
13529
13636
  function readNumber2(value) {
@@ -13550,6 +13657,322 @@ var init_modelProjection = __esm({
13550
13657
  }
13551
13658
  });
13552
13659
 
13660
+ // src/tools/outputKernel/classifier.ts
13661
+ function classifyToolOutput(source) {
13662
+ const output = source.output.trim();
13663
+ if (!output) {
13664
+ return "empty";
13665
+ }
13666
+ const command = (source.command ?? "").toLowerCase();
13667
+ const text = output.toLowerCase();
13668
+ if (looksLikeGitDiff(command, text)) {
13669
+ return "git_diff";
13670
+ }
13671
+ if (looksLikeSearch(command)) {
13672
+ return "search";
13673
+ }
13674
+ if (looksLikeTypecheck(command, text)) {
13675
+ return "typecheck";
13676
+ }
13677
+ if (looksLikeTest(command, text)) {
13678
+ return "test";
13679
+ }
13680
+ if (looksLikeBuild(command, text)) {
13681
+ return "build";
13682
+ }
13683
+ return "generic";
13684
+ }
13685
+ function looksLikeGitDiff(command, text) {
13686
+ return /\bgit\s+(diff|show)\b/.test(command) || text.includes("diff --git ");
13687
+ }
13688
+ function looksLikeSearch(command) {
13689
+ return /(^|\s)(rg|grep)(\s|$)/.test(command);
13690
+ }
13691
+ function looksLikeTypecheck(command, text) {
13692
+ return /\b(tsc|typecheck|mypy)\b/.test(command) || /\b(error|warning)\s+ts\d+:/i.test(text);
13693
+ }
13694
+ function looksLikeTest(command, text) {
13695
+ return /\b(test|vitest|jest|pytest|playwright|node --test)\b/.test(command) || /\b(test result|tests? failed|tests? passed|failing tests?|failures?)\b/.test(text);
13696
+ }
13697
+ function looksLikeBuild(command, text) {
13698
+ 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);
13699
+ }
13700
+ var init_classifier = __esm({
13701
+ "src/tools/outputKernel/classifier.ts"() {
13702
+ "use strict";
13703
+ }
13704
+ });
13705
+
13706
+ // src/tools/outputKernel/metrics.ts
13707
+ function estimateTextTokens(value) {
13708
+ const trimmed = value.trim();
13709
+ if (!trimmed) {
13710
+ return 0;
13711
+ }
13712
+ return Math.max(1, Math.ceil(trimmed.length / 4));
13713
+ }
13714
+ function computeSavings(input) {
13715
+ const rawChars = input.raw.length;
13716
+ const projectedChars = input.projected.length;
13717
+ const rawTokens = estimateTextTokens(input.raw);
13718
+ const projectedTokens = estimateTextTokens(input.projected);
13719
+ const savedTokens = Math.max(0, rawTokens - projectedTokens);
13720
+ const savingsRatio = rawTokens > 0 ? Math.round(savedTokens / rawTokens * 1e4) / 1e4 : 0;
13721
+ return {
13722
+ rawChars,
13723
+ projectedChars,
13724
+ rawTokens,
13725
+ projectedTokens,
13726
+ savedTokens,
13727
+ savingsRatio
13728
+ };
13729
+ }
13730
+ var init_metrics2 = __esm({
13731
+ "src/tools/outputKernel/metrics.ts"() {
13732
+ "use strict";
13733
+ }
13734
+ });
13735
+
13736
+ // src/tools/outputKernel/projectors/shared.ts
13737
+ function buildHeader(source, label) {
13738
+ return [
13739
+ `${source.toolName}: ${label}`,
13740
+ source.exitCode === void 0 ? void 0 : `exit=${source.exitCode ?? "null"}`,
13741
+ source.durationMs === void 0 ? void 0 : `duration=${source.durationMs}ms`,
13742
+ source.status ? `status=${source.status}` : void 0
13743
+ ].filter(Boolean).join(" ");
13744
+ }
13745
+ function splitOutputLines(value) {
13746
+ return value.split(/\r?\n/).map((line) => line.trimEnd());
13747
+ }
13748
+ function dedupeProjectedLines(lines) {
13749
+ const seen = /* @__PURE__ */ new Set();
13750
+ const result = [];
13751
+ for (const rawLine of lines) {
13752
+ const line = truncateText(rawLine.trim(), 260);
13753
+ if (!line || seen.has(line)) {
13754
+ continue;
13755
+ }
13756
+ seen.add(line);
13757
+ result.push(line);
13758
+ }
13759
+ return result;
13760
+ }
13761
+ var init_shared6 = __esm({
13762
+ "src/tools/outputKernel/projectors/shared.ts"() {
13763
+ "use strict";
13764
+ init_fs();
13765
+ }
13766
+ });
13767
+
13768
+ // src/tools/outputKernel/projectors/diagnostic.ts
13769
+ function buildDiagnosticProjection(source, label) {
13770
+ const lines = splitOutputLines(source.output);
13771
+ const evidence = lines.filter(isDiagnosticEvidenceLine).slice(0, STRUCTURED_MAX_LINES);
13772
+ const summaryLines = lines.filter(isSummaryLine).slice(0, 8);
13773
+ return dedupeProjectedLines([
13774
+ buildHeader(source, label),
13775
+ ...summaryLines,
13776
+ ...evidence
13777
+ ]).join("\n");
13778
+ }
13779
+ function isDiagnosticEvidenceLine(line) {
13780
+ return /(^|\b)(error|warning|fail|failed|failure|panic|exception|traceback|expected|received|cannot find|not assignable|mismatched|undefined|denied)(\b|:)/i.test(line) || /^\s*(at\s|file\s|src\/|tests?\/|[A-Za-z]:\\|\.\/)/.test(line) || /\(\d+,\d+\):\s+(error|warning)\s+/i.test(line) || /error\[e\d+\]/i.test(line);
13781
+ }
13782
+ function isSummaryLine(line) {
13783
+ return /\b(\d+\s+(passed|failed|skipped|errors?|warnings?)|test result|found \d+ errors?|failed tests?|build failed|compiled successfully)\b/i.test(line);
13784
+ }
13785
+ var STRUCTURED_MAX_LINES;
13786
+ var init_diagnostic = __esm({
13787
+ "src/tools/outputKernel/projectors/diagnostic.ts"() {
13788
+ "use strict";
13789
+ init_shared6();
13790
+ STRUCTURED_MAX_LINES = 28;
13791
+ }
13792
+ });
13793
+
13794
+ // src/tools/outputKernel/projectors/gitDiff.ts
13795
+ function buildGitDiffProjection(source) {
13796
+ const lines = splitOutputLines(source.output);
13797
+ const files = lines.filter((line) => line.startsWith("diff --git ")).map((line) => line.replace(/^diff --git a\//, "").replace(/ b\//, " -> ")).slice(0, DIFF_MAX_FILES);
13798
+ const stats = lines.filter((line) => /(\d+ files? changed|\d+ insertions?\(\+\)|\d+ deletions?\(-\))/.test(line)).slice(0, 8);
13799
+ const hunks = lines.filter((line) => line.startsWith("@@") || line.startsWith("+++ ") || line.startsWith("--- ")).slice(0, 18).map((line) => truncateText(line, 220));
13800
+ return [
13801
+ buildHeader(source, "git diff"),
13802
+ files.length > 0 ? `files: ${files.join(", ")}` : void 0,
13803
+ ...stats,
13804
+ ...hunks
13805
+ ].filter((line) => Boolean(line)).join("\n");
13806
+ }
13807
+ var DIFF_MAX_FILES;
13808
+ var init_gitDiff = __esm({
13809
+ "src/tools/outputKernel/projectors/gitDiff.ts"() {
13810
+ "use strict";
13811
+ init_fs();
13812
+ init_shared6();
13813
+ DIFF_MAX_FILES = 24;
13814
+ }
13815
+ });
13816
+
13817
+ // src/tools/outputKernel/projectors/generic.ts
13818
+ function projectEmptyOutput(source) {
13819
+ return {
13820
+ mode: "empty",
13821
+ projection: buildHeader(source, "no output"),
13822
+ degraded: false,
13823
+ reason: "empty_output"
13824
+ };
13825
+ }
13826
+ function projectGenericOutput(source, reason = "generic_output") {
13827
+ return {
13828
+ mode: "generic",
13829
+ projection: buildGenericPreview(source),
13830
+ degraded: false,
13831
+ reason
13832
+ };
13833
+ }
13834
+ function projectStructuredOutput(source, body) {
13835
+ const trimmed = body.trim();
13836
+ if (!trimmed) {
13837
+ return {
13838
+ mode: "generic",
13839
+ projection: buildGenericPreview(source),
13840
+ degraded: true,
13841
+ reason: "structured_projection_empty"
13842
+ };
13843
+ }
13844
+ return {
13845
+ mode: "structured",
13846
+ projection: trimmed,
13847
+ degraded: false,
13848
+ reason: "structured_projection"
13849
+ };
13850
+ }
13851
+ function buildGenericPreview(source) {
13852
+ return [
13853
+ buildHeader(source, "output"),
13854
+ truncateText(source.output.trim(), GENERIC_MAX_CHARS)
13855
+ ].filter(Boolean).join("\n");
13856
+ }
13857
+ var GENERIC_MAX_CHARS;
13858
+ var init_generic = __esm({
13859
+ "src/tools/outputKernel/projectors/generic.ts"() {
13860
+ "use strict";
13861
+ init_fs();
13862
+ init_shared6();
13863
+ GENERIC_MAX_CHARS = 1500;
13864
+ }
13865
+ });
13866
+
13867
+ // src/tools/outputKernel/projectors/search.ts
13868
+ function buildSearchProjection(source) {
13869
+ const nonEmptyLines = splitOutputLines(source.output).filter((line) => line.trim().length > 0);
13870
+ const matches = nonEmptyLines.slice(0, SEARCH_MAX_MATCHES).map((line) => truncateText(line, 220));
13871
+ const omitted = Math.max(0, nonEmptyLines.length - matches.length);
13872
+ return [
13873
+ buildHeader(source, "search"),
13874
+ `matches shown: ${matches.length}${omitted > 0 ? `, omitted: ${omitted}` : ""}`,
13875
+ ...matches
13876
+ ].join("\n");
13877
+ }
13878
+ var SEARCH_MAX_MATCHES;
13879
+ var init_search2 = __esm({
13880
+ "src/tools/outputKernel/projectors/search.ts"() {
13881
+ "use strict";
13882
+ init_fs();
13883
+ init_shared6();
13884
+ SEARCH_MAX_MATCHES = 24;
13885
+ }
13886
+ });
13887
+
13888
+ // src/tools/outputKernel/projectors/recovery.ts
13889
+ function appendRecoveryHint(projection, governance) {
13890
+ if (!governance.recoveryHint) {
13891
+ return projection;
13892
+ }
13893
+ if (projection.includes(governance.recoveryHint)) {
13894
+ return projection;
13895
+ }
13896
+ return `${projection.trimEnd()}
13897
+ ${governance.recoveryHint}`;
13898
+ }
13899
+ var init_recovery = __esm({
13900
+ "src/tools/outputKernel/projectors/recovery.ts"() {
13901
+ "use strict";
13902
+ }
13903
+ });
13904
+
13905
+ // src/tools/outputKernel/projectors.ts
13906
+ function projectOutputByKind(kind, source) {
13907
+ switch (kind) {
13908
+ case "empty":
13909
+ return projectEmptyOutput(source);
13910
+ case "test":
13911
+ return projectStructuredOutput(source, buildDiagnosticProjection(source, "test"));
13912
+ case "build":
13913
+ return projectStructuredOutput(source, buildDiagnosticProjection(source, "build"));
13914
+ case "typecheck":
13915
+ return projectStructuredOutput(source, buildDiagnosticProjection(source, "typecheck"));
13916
+ case "search":
13917
+ return projectStructuredOutput(source, buildSearchProjection(source));
13918
+ case "git_diff":
13919
+ return projectStructuredOutput(source, buildGitDiffProjection(source));
13920
+ case "generic":
13921
+ return projectGenericOutput(source);
13922
+ }
13923
+ }
13924
+ var init_projectors = __esm({
13925
+ "src/tools/outputKernel/projectors.ts"() {
13926
+ "use strict";
13927
+ init_diagnostic();
13928
+ init_gitDiff();
13929
+ init_generic();
13930
+ init_search2();
13931
+ init_recovery();
13932
+ }
13933
+ });
13934
+
13935
+ // src/tools/outputKernel/index.ts
13936
+ function governToolOutput(source) {
13937
+ const kind = classifyToolOutput(source);
13938
+ const projected = projectOutputByKind(kind, source);
13939
+ const recoveryHint = source.outputPath ? `[full output: ${source.outputPath}]` : void 0;
13940
+ const projection = appendRecoveryHint(projected.projection, {
13941
+ outputPath: source.outputPath,
13942
+ recoveryHint
13943
+ });
13944
+ const metrics = computeSavings({
13945
+ raw: source.output,
13946
+ projected: projection
13947
+ });
13948
+ return {
13949
+ version: 1,
13950
+ kind,
13951
+ mode: projected.mode,
13952
+ projection,
13953
+ rawChars: source.outputChars ?? metrics.rawChars,
13954
+ projectedChars: metrics.projectedChars,
13955
+ rawTokens: metrics.rawTokens,
13956
+ projectedTokens: metrics.projectedTokens,
13957
+ savedTokens: metrics.savedTokens,
13958
+ savingsRatio: metrics.savingsRatio,
13959
+ truncated: Boolean(source.truncated),
13960
+ outputPath: source.outputPath,
13961
+ recoveryHint,
13962
+ degraded: projected.degraded,
13963
+ reason: projected.reason
13964
+ };
13965
+ }
13966
+ var init_outputKernel = __esm({
13967
+ "src/tools/outputKernel/index.ts"() {
13968
+ "use strict";
13969
+ init_classifier();
13970
+ init_metrics2();
13971
+ init_projectors();
13972
+ init_metrics2();
13973
+ }
13974
+ });
13975
+
13553
13976
  // src/tools/bash.ts
13554
13977
  var SHELL_RUNTIME, bashToolDefinition;
13555
13978
  var init_bash = __esm({
@@ -13559,6 +13982,7 @@ var init_bash = __esm({
13559
13982
  init_shellRuntime();
13560
13983
  init_fs();
13561
13984
  init_shared2();
13985
+ init_outputKernel();
13562
13986
  SHELL_RUNTIME = getShellRuntimeInfo();
13563
13987
  bashToolDefinition = {
13564
13988
  definition: {
@@ -13607,6 +14031,18 @@ var init_bash = __esm({
13607
14031
  }
13608
14032
  });
13609
14033
  const status = result.aborted ? "aborted" : result.stalled ? "stalled" : result.timedOut ? "timed_out" : result.exitCode === 0 ? "completed" : "failed";
14034
+ const outputGovernance = governToolOutput({
14035
+ toolName: "bash",
14036
+ command,
14037
+ status,
14038
+ exitCode: result.exitCode,
14039
+ durationMs: result.durationMs,
14040
+ output: result.output,
14041
+ outputPath: result.outputPath,
14042
+ truncated: result.truncated,
14043
+ outputChars: result.outputChars,
14044
+ outputBytes: result.outputBytes
14045
+ });
13610
14046
  const metadata = {
13611
14047
  runtime: {
13612
14048
  status,
@@ -13619,7 +14055,8 @@ var init_bash = __esm({
13619
14055
  truncated: result.truncated,
13620
14056
  outputPath: result.outputPath,
13621
14057
  outputPreview: result.output
13622
- }
14058
+ },
14059
+ outputGovernance
13623
14060
  };
13624
14061
  return okResult(
13625
14062
  JSON.stringify(
@@ -13634,6 +14071,7 @@ var init_bash = __esm({
13634
14071
  outputPath: result.outputPath,
13635
14072
  outputChars: result.outputChars,
13636
14073
  outputBytes: result.outputBytes,
14074
+ outputGovernance,
13637
14075
  output: truncateText(result.output, 4e3),
13638
14076
  ...status === "completed" ? {} : {
13639
14077
  shell: shell.shell,
@@ -226060,6 +226498,31 @@ async function processToolCallBatch(input) {
226060
226498
  changedPathCount: metadata?.changedPaths?.length ?? 0
226061
226499
  }
226062
226500
  });
226501
+ if (metadata?.outputGovernance) {
226502
+ await recordObservabilityEvent(projectContext.stateRootDir, {
226503
+ event: "tool.output",
226504
+ status: result.ok ? "completed" : "failed",
226505
+ sessionId: session.id,
226506
+ identityKind: identity.kind,
226507
+ identityName: identity.name,
226508
+ toolName: toolCall.function.name,
226509
+ durationMs,
226510
+ details: {
226511
+ kind: metadata.outputGovernance.kind,
226512
+ mode: metadata.outputGovernance.mode,
226513
+ rawChars: metadata.outputGovernance.rawChars,
226514
+ projectedChars: metadata.outputGovernance.projectedChars,
226515
+ rawTokens: metadata.outputGovernance.rawTokens,
226516
+ projectedTokens: metadata.outputGovernance.projectedTokens,
226517
+ savedTokens: metadata.outputGovernance.savedTokens,
226518
+ savingsRatio: metadata.outputGovernance.savingsRatio,
226519
+ truncated: metadata.outputGovernance.truncated,
226520
+ outputPath: metadata.outputGovernance.outputPath,
226521
+ degraded: metadata.outputGovernance.degraded,
226522
+ reason: metadata.outputGovernance.reason
226523
+ }
226524
+ });
226525
+ }
226063
226526
  if (result.ok) {
226064
226527
  options.callbacks?.onToolResult?.(toolCall.function.name, result.output);
226065
226528
  } else {
@@ -229745,7 +230208,7 @@ function readStringField(payload, key) {
229745
230208
  const value = payload[key];
229746
230209
  return typeof value === "string" && value.length > 0 ? value : void 0;
229747
230210
  }
229748
- var init_shared6 = __esm({
230211
+ var init_shared7 = __esm({
229749
230212
  "src/runtime-ui/toolDisplay/shared.ts"() {
229750
230213
  "use strict";
229751
230214
  }
@@ -229867,7 +230330,7 @@ var init_call = __esm({
229867
230330
  init_json();
229868
230331
  init_pathDisplay2();
229869
230332
  init_previewPolicy();
229870
- init_shared6();
230333
+ init_shared7();
229871
230334
  }
229872
230335
  });
229873
230336
 
@@ -229913,7 +230376,7 @@ function buildToolResultVisiblePreview(name, rawOutput, cwd) {
229913
230376
  function buildToolFailureDetail(name, rawOutput, cwd) {
229914
230377
  const display = buildToolResultDisplay(name, rawOutput, cwd);
229915
230378
  const parsed = parseJsonObject(display.preview ?? rawOutput);
229916
- const error = readString5(parsed?.error) ?? readString5(parsed?.reason) ?? readString5(parsed?.hint);
230379
+ const error = readString6(parsed?.error) ?? readString6(parsed?.reason) ?? readString6(parsed?.hint);
229917
230380
  if (error) {
229918
230381
  return truncateVisiblePreview(error);
229919
230382
  }
@@ -229959,7 +230422,7 @@ function parseJsonObject(value) {
229959
230422
  return null;
229960
230423
  }
229961
230424
  }
229962
- function readString5(value) {
230425
+ function readString6(value) {
229963
230426
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
229964
230427
  }
229965
230428
  var init_result = __esm({
@@ -229968,7 +230431,7 @@ var init_result = __esm({
229968
230431
  init_json();
229969
230432
  init_pathDisplay2();
229970
230433
  init_previewPolicy();
229971
- init_shared6();
230434
+ init_shared7();
229972
230435
  }
229973
230436
  });
229974
230437