@dotobokuri/fleet-console 1.66.0 → 1.67.0

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 (62) hide show
  1. package/dist/client/assets/{_baseUniq-rDf4uMQX.js → _baseUniq-DG32CEpb.js} +1 -1
  2. package/dist/client/assets/{arc-btPQ2GHn.js → arc-CoHkSCMF.js} +1 -1
  3. package/dist/client/assets/{architectureDiagram-Q4EWVU46-DG6KDx34.js → architectureDiagram-Q4EWVU46-f0RKHqJg.js} +1 -1
  4. package/dist/client/assets/{blockDiagram-DXYQGD6D-XUrLsQey.js → blockDiagram-DXYQGD6D-Dxb3hLAV.js} +1 -1
  5. package/dist/client/assets/{c4Diagram-AHTNJAMY-DuXkZ8b1.js → c4Diagram-AHTNJAMY-C5q_ahSd.js} +1 -1
  6. package/dist/client/assets/channel-C-Qx-Oi-.js +1 -0
  7. package/dist/client/assets/{chunk-4BX2VUAB-C2tTKvHv.js → chunk-4BX2VUAB-BS0W4Fpt.js} +1 -1
  8. package/dist/client/assets/{chunk-4TB4RGXK-C5bwM8_h.js → chunk-4TB4RGXK-p03eWKTO.js} +1 -1
  9. package/dist/client/assets/{chunk-55IACEB6-BhcOy6C-.js → chunk-55IACEB6-BB1ALVX2.js} +1 -1
  10. package/dist/client/assets/{chunk-EDXVE4YY-TOZwStuB.js → chunk-EDXVE4YY-DV4N-Npm.js} +1 -1
  11. package/dist/client/assets/{chunk-FMBD7UC4-B8JSJHVV.js → chunk-FMBD7UC4-Da8VspnD.js} +1 -1
  12. package/dist/client/assets/{chunk-OYMX7WX6-BsNJ9iub.js → chunk-OYMX7WX6-CWTmAQhy.js} +1 -1
  13. package/dist/client/assets/{chunk-QZHKN3VN-BhHSM1D6.js → chunk-QZHKN3VN-DE9xUsy3.js} +1 -1
  14. package/dist/client/assets/{chunk-YZCP3GAM-B9d83zwz.js → chunk-YZCP3GAM-BxriNNdB.js} +1 -1
  15. package/dist/client/assets/classDiagram-6PBFFD2Q-DA7e8Mft.js +1 -0
  16. package/dist/client/assets/classDiagram-v2-HSJHXN6E-DA7e8Mft.js +1 -0
  17. package/dist/client/assets/clone-Rpb38n4i.js +1 -0
  18. package/dist/client/assets/{cose-bilkent-S5V4N54A-D5mwO2NV.js → cose-bilkent-S5V4N54A-B4sjskIn.js} +1 -1
  19. package/dist/client/assets/{dagre-KV5264BT-5fSh4g2E.js → dagre-KV5264BT-oJms5aS3.js} +1 -1
  20. package/dist/client/assets/{diagram-5BDNPKRD-CtCGKgkm.js → diagram-5BDNPKRD-BAbLGddM.js} +1 -1
  21. package/dist/client/assets/{diagram-G4DWMVQ6-CmGVsepF.js → diagram-G4DWMVQ6-BrhRQwe-.js} +1 -1
  22. package/dist/client/assets/{diagram-MMDJMWI5-BR9Rfnad.js → diagram-MMDJMWI5-Buxu7Ng3.js} +1 -1
  23. package/dist/client/assets/{diagram-TYMM5635-C9kuBFny.js → diagram-TYMM5635-Cnp__7LJ.js} +1 -1
  24. package/dist/client/assets/{erDiagram-SMLLAGMA-BlL5tH_h.js → erDiagram-SMLLAGMA-ctmZHrVO.js} +1 -1
  25. package/dist/client/assets/{flowDiagram-DWJPFMVM-IUe6EFlH.js → flowDiagram-DWJPFMVM-DsQXJqF_.js} +1 -1
  26. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-G-CTHnXi.js → ganttDiagram-T4ZO3ILL-SAFJNY9M.js} +1 -1
  27. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-COUsDQ0n.js → gitGraphDiagram-UUTBAWPF-Bd5Dq0vQ.js} +1 -1
  28. package/dist/client/assets/{graph-BCiainCT.js → graph-DzMOPhFp.js} +1 -1
  29. package/dist/client/assets/index-BLA_qnPk.css +1 -0
  30. package/dist/client/assets/index-CGE8u6Es.js +465 -0
  31. package/dist/client/assets/{infoDiagram-42DDH7IO-DbNkXGyd.js → infoDiagram-42DDH7IO-BrEfG9P4.js} +1 -1
  32. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-Iqt-Zs6v.js → ishikawaDiagram-UXIWVN3A-Dg22orvH.js} +1 -1
  33. package/dist/client/assets/{journeyDiagram-VCZTEJTY-m7TrTD61.js → journeyDiagram-VCZTEJTY-DEcjEc6c.js} +1 -1
  34. package/dist/client/assets/{kanban-definition-6JOO6SKY-Df5KWqsi.js → kanban-definition-6JOO6SKY-C2Xa4znN.js} +1 -1
  35. package/dist/client/assets/{layout-BtA7fX7y.js → layout-B7E_-0ev.js} +1 -1
  36. package/dist/client/assets/{linear-DVL8mywl.js → linear-BXJsh3FL.js} +1 -1
  37. package/dist/client/assets/{mermaid.core-CmjUZw2x.js → mermaid.core-CdZpLnyY.js} +4 -4
  38. package/dist/client/assets/{min-BTu70auM.js → min-CEAFFcJY.js} +1 -1
  39. package/dist/client/assets/{mindmap-definition-QFDTVHPH-BYpo-Mnc.js → mindmap-definition-QFDTVHPH-iXzcnu1C.js} +1 -1
  40. package/dist/client/assets/{pieDiagram-DEJITSTG-B8zFmcim.js → pieDiagram-DEJITSTG-CdRN75ad.js} +1 -1
  41. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-CMYVBi7F.js → quadrantDiagram-34T5L4WZ-Dscl0EFH.js} +1 -1
  42. package/dist/client/assets/{requirementDiagram-MS252O5E-BFeVMKvJ.js → requirementDiagram-MS252O5E-CLmSCaQs.js} +1 -1
  43. package/dist/client/assets/{sankeyDiagram-XADWPNL6-D2I70l9E.js → sankeyDiagram-XADWPNL6-BfPk87jf.js} +1 -1
  44. package/dist/client/assets/{sequenceDiagram-FGHM5R23-zawVvGJJ.js → sequenceDiagram-FGHM5R23-DE8sU8ej.js} +1 -1
  45. package/dist/client/assets/{stateDiagram-FHFEXIEX-DnBtIbuv.js → stateDiagram-FHFEXIEX-BmaQc1Nm.js} +1 -1
  46. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-TcoFpvuT.js +1 -0
  47. package/dist/client/assets/{timeline-definition-GMOUNBTQ-0gfoVpmY.js → timeline-definition-GMOUNBTQ-C4Z0umzx.js} +1 -1
  48. package/dist/client/assets/{vennDiagram-DHZGUBPP-CuaEOGH2.js → vennDiagram-DHZGUBPP-BKlcRXlE.js} +1 -1
  49. package/dist/client/assets/{wardley-RL74JXVD-C-H0Svof.js → wardley-RL74JXVD-B0RT7bAW.js} +1 -1
  50. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-MQjjZFwh.js → wardleyDiagram-NUSXRM2D-mx28kvam.js} +1 -1
  51. package/dist/client/assets/{xychartDiagram-5P7HB3ND-BhTAVqtf.js → xychartDiagram-5P7HB3ND-DU6qIPI0.js} +1 -1
  52. package/dist/client/index.html +2 -2
  53. package/dist/fleet-plugins/terminal/routes.mjs +403 -76
  54. package/dist/fleet.mjs +233 -53
  55. package/package.json +1 -1
  56. package/dist/client/assets/channel-CXR6clX_.js +0 -1
  57. package/dist/client/assets/classDiagram-6PBFFD2Q-4x7ZfFx0.js +0 -1
  58. package/dist/client/assets/classDiagram-v2-HSJHXN6E-4x7ZfFx0.js +0 -1
  59. package/dist/client/assets/clone-lkppJrH0.js +0 -1
  60. package/dist/client/assets/index-B1tQI7A5.js +0 -465
  61. package/dist/client/assets/index-D3b63Dhe.css +0 -1
  62. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-D7rH7_-p.js +0 -1
@@ -20603,18 +20603,25 @@ function withheldClaudeSkillStub(name, tokens, budget) {
20603
20603
  var CLAUDE_WEB_SEARCH_TOOL_NAME = "WebSearch";
20604
20604
  var ANTHROPIC_WEB_SEARCH_TOOL_NAME = "web_search";
20605
20605
  var ANTHROPIC_WEB_SEARCH_TOOL_TYPE = "web_search_20250305";
20606
- function isClaudeWebSearchTool(tool) {
20607
- return tool.type === ANTHROPIC_WEB_SEARCH_TOOL_TYPE || tool.name === CLAUDE_WEB_SEARCH_TOOL_NAME || tool.name === ANTHROPIC_WEB_SEARCH_TOOL_NAME;
20606
+ var CLAUDE_WEB_SEARCH_TOOL_IDENTIFIERS = [
20607
+ CLAUDE_WEB_SEARCH_TOOL_NAME,
20608
+ ANTHROPIC_WEB_SEARCH_TOOL_NAME,
20609
+ ANTHROPIC_WEB_SEARCH_TOOL_TYPE
20610
+ ];
20611
+ function matchesToolIdentifier(tool, identifiers) {
20612
+ return typeof tool.name === "string" && identifiers.has(tool.name) || typeof tool.type === "string" && identifiers.has(tool.type);
20608
20613
  }
20609
- function isClaudeWebSearchToolChoice(choice) {
20610
- return choice?.type === "tool" && (choice.name === CLAUDE_WEB_SEARCH_TOOL_NAME || choice.name === ANTHROPIC_WEB_SEARCH_TOOL_NAME);
20614
+ function matchesToolChoice(choice, identifiers) {
20615
+ return choice?.type === "tool" && typeof choice.name === "string" && identifiers.has(choice.name);
20611
20616
  }
20612
- function omitClaudeWebSearchTools(request) {
20617
+ function omitClaudeClientTools(request, names) {
20618
+ const identifiers = new Set(names);
20619
+ if (identifiers.size === 0) return { request, changed: false };
20613
20620
  const tools = request.tools;
20614
- const kept = tools?.filter((tool) => !isClaudeWebSearchTool(tool));
20621
+ const kept = tools?.filter((tool) => !matchesToolIdentifier(tool, identifiers));
20615
20622
  const toolsChanged = tools !== void 0 && kept !== void 0 && kept.length !== tools.length;
20616
20623
  const choice = request.tool_choice;
20617
- const choiceChanged = isClaudeWebSearchToolChoice(choice);
20624
+ const choiceChanged = matchesToolChoice(choice, identifiers);
20618
20625
  if (!toolsChanged && !choiceChanged) {
20619
20626
  return { request, changed: false };
20620
20627
  }
@@ -20633,6 +20640,42 @@ function omitClaudeWebSearchTools(request) {
20633
20640
  changed: true
20634
20641
  };
20635
20642
  }
20643
+ var CLAUDE_USAGE_LIMIT_DIRECTIVE = /^\s*\[Usage limit [^\]]*\]\s*$/;
20644
+ function stripClaudeUsageLimitDirectives(messages) {
20645
+ let removed = 0;
20646
+ const kept = [];
20647
+ for (const message of messages) {
20648
+ const stripped = stripUsageLimitBlocks(message);
20649
+ removed += stripped.removed;
20650
+ if (stripped.message !== null) kept.push(stripped.message);
20651
+ }
20652
+ if (removed === 0) return { messages, changed: false, removed: 0 };
20653
+ const last = kept[kept.length - 1];
20654
+ if (last === void 0 || last.role === "assistant") {
20655
+ return { messages, changed: false, removed: 0 };
20656
+ }
20657
+ return { messages: kept, changed: true, removed };
20658
+ }
20659
+ function stripUsageLimitBlocks(message) {
20660
+ const content = message.content;
20661
+ if (typeof content === "string") {
20662
+ return isClaudeUsageLimitDirective(content) ? { message: null, removed: 1 } : { message, removed: 0 };
20663
+ }
20664
+ if (!Array.isArray(content)) return { message, removed: 0 };
20665
+ const kept = content.filter(
20666
+ (block) => !(isClaudeTextBlock(block) && isClaudeUsageLimitDirective(block.text))
20667
+ );
20668
+ const removed = content.length - kept.length;
20669
+ if (removed === 0) return { message, removed: 0 };
20670
+ if (kept.length === 0) return { message: null, removed };
20671
+ return { message: { ...message, content: kept }, removed };
20672
+ }
20673
+ function isClaudeUsageLimitDirective(text2) {
20674
+ return CLAUDE_USAGE_LIMIT_DIRECTIVE.test(text2);
20675
+ }
20676
+ function omitClaudeWebSearchTools(request) {
20677
+ return omitClaudeClientTools(request, CLAUDE_WEB_SEARCH_TOOL_IDENTIFIERS);
20678
+ }
20636
20679
  var benchmarks_default = {
20637
20680
  version: 2,
20638
20681
  sources: {
@@ -26241,22 +26284,42 @@ function parseXaiEventStream(body, options, functionCallTimeoutMs) {
26241
26284
  functionCallTimeoutMs
26242
26285
  );
26243
26286
  }
26287
+ var XAI_ASSEMBLY_DEADLINE = /* @__PURE__ */ Symbol("xai-assembly-deadline");
26244
26288
  async function* assembleXaiFunctionCalls(source, controller, timeoutMs) {
26245
26289
  const iterator = source[Symbol.asyncIterator]();
26246
26290
  const pending = /* @__PURE__ */ new Map();
26291
+ let snapshot;
26247
26292
  let completedNormally = false;
26248
26293
  try {
26249
26294
  while (true) {
26250
26295
  const deadlineAt = earliestXaiFunctionCallDeadline(pending);
26251
- const result = deadlineAt === void 0 ? await iterator.next() : await nextXaiEventBeforeDeadline(iterator, deadlineAt, controller, timeoutMs);
26296
+ const result = deadlineAt === void 0 ? await iterator.next() : await nextXaiEventBeforeDeadline(iterator, deadlineAt);
26297
+ if (result === XAI_ASSEMBLY_DEADLINE) {
26298
+ yield* salvageXaiPendingCalls(
26299
+ pending,
26300
+ snapshot,
26301
+ `function call assembly exceeded ${timeoutMs}ms`,
26302
+ controller
26303
+ );
26304
+ completedNormally = true;
26305
+ return;
26306
+ }
26252
26307
  if (result.done) {
26253
26308
  if (pending.size > 0) {
26254
- throw incompleteXaiFunctionCall("stream ended before function call output_item.done");
26309
+ yield* salvageXaiPendingCalls(
26310
+ pending,
26311
+ snapshot,
26312
+ "stream ended before function call output_item.done",
26313
+ controller
26314
+ );
26255
26315
  }
26256
26316
  completedNormally = true;
26257
26317
  return;
26258
26318
  }
26259
26319
  const event = result.value;
26320
+ if (event.type === "response.created") {
26321
+ snapshot = event.response;
26322
+ }
26260
26323
  if (event.type === "response.completed" && pending.size > 0) {
26261
26324
  throw incompleteXaiFunctionCall("response.completed arrived before function call output_item.done");
26262
26325
  }
@@ -26267,10 +26330,18 @@ async function* assembleXaiFunctionCalls(source, controller, timeoutMs) {
26267
26330
  // Each call gets a full timeout from its own added event. Waiting uses the
26268
26331
  // earliest active deadline, so a later parallel call is never shortened by
26269
26332
  // an earlier call's age.
26270
- deadlineAt: Date.now() + timeoutMs
26333
+ deadlineAt: Date.now() + timeoutMs,
26334
+ argumentsBuffer: ""
26271
26335
  });
26272
26336
  continue;
26273
26337
  }
26338
+ if (event.type === "response.function_call_arguments.delta") {
26339
+ const call = pending.get(xaiFunctionCallKey(event.output_index, event.item_id));
26340
+ if (call !== void 0) {
26341
+ call.argumentsBuffer += event.delta;
26342
+ }
26343
+ continue;
26344
+ }
26274
26345
  if (event.type === "response.function_call_arguments.done") {
26275
26346
  const call = pending.get(xaiFunctionCallKey(event.output_index, event.item_id));
26276
26347
  if (call !== void 0) {
@@ -26316,24 +26387,60 @@ function earliestXaiFunctionCallDeadline(pending) {
26316
26387
  }
26317
26388
  return earliest;
26318
26389
  }
26319
- async function nextXaiEventBeforeDeadline(iterator, deadlineAt, controller, timeoutMs) {
26320
- const remainingMs = deadlineAt - Date.now();
26321
- if (remainingMs <= 0) {
26322
- const error51 = incompleteXaiFunctionCall(`function call assembly exceeded ${timeoutMs}ms`);
26390
+ async function* salvageXaiPendingCalls(pending, snapshot, reason, controller) {
26391
+ const salvageable = [...pending.values()].every(
26392
+ (call) => completeXaiArguments(call) !== void 0
26393
+ );
26394
+ if (snapshot === void 0 || !salvageable) {
26395
+ const error51 = incompleteXaiFunctionCall(reason);
26323
26396
  controller.abort(error51);
26324
26397
  throw error51;
26325
26398
  }
26399
+ wireLog("xai-responses.function_call.salvaged", {
26400
+ reason,
26401
+ calls: pending.size
26402
+ });
26403
+ controller.abort(incompleteXaiFunctionCall(reason));
26404
+ for (const [key, call] of pending) {
26405
+ const args = completeXaiArguments(call) ?? "";
26406
+ const item = { ...call.added.item, arguments: args };
26407
+ yield call.added;
26408
+ yield {
26409
+ type: "response.function_call_arguments.done",
26410
+ item_id: call.added.item.id,
26411
+ output_index: call.added.output_index,
26412
+ arguments: args
26413
+ };
26414
+ yield { type: "response.output_item.done", output_index: call.added.output_index, item };
26415
+ pending.delete(key);
26416
+ }
26417
+ yield { type: "response.completed", response: { ...snapshot, usage: null } };
26418
+ }
26419
+ function completeXaiArguments(call) {
26420
+ const candidate = call.argumentsDone?.arguments ?? call.argumentsBuffer;
26421
+ if (candidate.length === 0) return void 0;
26422
+ let parsed;
26423
+ try {
26424
+ parsed = JSON.parse(candidate);
26425
+ } catch {
26426
+ return void 0;
26427
+ }
26428
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? candidate : void 0;
26429
+ }
26430
+ async function nextXaiEventBeforeDeadline(iterator, deadlineAt) {
26431
+ const remainingMs = deadlineAt - Date.now();
26432
+ if (remainingMs <= 0) {
26433
+ return XAI_ASSEMBLY_DEADLINE;
26434
+ }
26326
26435
  let timeout;
26327
26436
  const next = iterator.next();
26328
26437
  void next.catch(() => void 0);
26329
26438
  try {
26330
26439
  return await Promise.race([
26331
26440
  next,
26332
- new Promise((_, reject) => {
26441
+ new Promise((resolve3) => {
26333
26442
  timeout = setTimeout(() => {
26334
- const error51 = incompleteXaiFunctionCall(`function call assembly exceeded ${timeoutMs}ms`);
26335
- controller.abort(error51);
26336
- reject(error51);
26443
+ resolve3(XAI_ASSEMBLY_DEADLINE);
26337
26444
  }, remainingMs);
26338
26445
  })
26339
26446
  ]);
@@ -26368,6 +26475,10 @@ function parseEventFrame3(frame) {
26368
26475
  }
26369
26476
  return canonicalEvent3(parsed);
26370
26477
  }
26478
+ var XAI_EOS_SENTINEL = "<|eos|>";
26479
+ function withoutXaiSentinel(text2) {
26480
+ return text2.includes(XAI_EOS_SENTINEL) ? text2.split(XAI_EOS_SENTINEL).join("") : text2;
26481
+ }
26371
26482
  function canonicalEvent3(value) {
26372
26483
  if (!isRecord7(value) || typeof value.type !== "string") {
26373
26484
  throw new UpstreamProtocolError("Grok CLI SSE event was not an object with a type");
@@ -26387,25 +26498,30 @@ function canonicalEvent3(value) {
26387
26498
  content_index: number32(value.content_index, "content_index"),
26388
26499
  part: {
26389
26500
  type: "output_text",
26390
- text: typeof part.text === "string" ? part.text : ""
26501
+ text: typeof part.text === "string" ? withoutXaiSentinel(part.text) : ""
26391
26502
  }
26392
26503
  };
26393
26504
  }
26394
- case "response.output_text.delta":
26505
+ case "response.output_text.delta": {
26506
+ const delta = withoutXaiSentinel(string32(value.delta, "delta"));
26507
+ if (delta.length === 0) {
26508
+ return void 0;
26509
+ }
26395
26510
  return {
26396
26511
  type: value.type,
26397
26512
  item_id: string32(value.item_id, "item_id"),
26398
26513
  output_index: number32(value.output_index, "output_index"),
26399
26514
  content_index: number32(value.content_index, "content_index"),
26400
- delta: string32(value.delta, "delta")
26515
+ delta
26401
26516
  };
26517
+ }
26402
26518
  case "response.output_text.done":
26403
26519
  return {
26404
26520
  type: value.type,
26405
26521
  item_id: string32(value.item_id, "item_id"),
26406
26522
  output_index: number32(value.output_index, "output_index"),
26407
26523
  content_index: number32(value.content_index, "content_index"),
26408
- text: string32(value.text, "text")
26524
+ text: withoutXaiSentinel(string32(value.text, "text"))
26409
26525
  };
26410
26526
  case "response.reasoning_summary_text.delta":
26411
26527
  case "response.reasoning_text.delta":
@@ -26427,11 +26543,17 @@ function canonicalEvent3(value) {
26427
26543
  item
26428
26544
  };
26429
26545
  }
26430
- // Hold argument fragments until the Grok CLI proxy's partial-JSON behavior is observed.
26431
- // The `.done` event carries the complete argument object, which downstream treats as the
26432
- // remainder when nothing was streamed ahead of it.
26546
+ // Fragments still never reach the client the assembler absorbs them and the `.done`
26547
+ // event carries the complete argument object. They are decoded rather than discarded
26548
+ // because this proxy has been measured ending a stream with the whole argument object
26549
+ // delivered as fragments and no terminal frame; the buffer is that turn's only copy.
26433
26550
  case "response.function_call_arguments.delta":
26434
- return void 0;
26551
+ return {
26552
+ type: value.type,
26553
+ item_id: string32(value.item_id, "item_id"),
26554
+ output_index: number32(value.output_index, "output_index"),
26555
+ delta: string32(value.delta, "delta")
26556
+ };
26435
26557
  case "response.function_call_arguments.done":
26436
26558
  return {
26437
26559
  type: value.type,
@@ -26901,6 +27023,10 @@ function isCursorNativeRedirectToolName(name) {
26901
27023
  const leaf = toolLeafName3(name).replace(/[_-]/g, "").toLowerCase();
26902
27024
  return ["grep", "bash", "shellcommand", "execcommand"].includes(leaf);
26903
27025
  }
27026
+ function isCursorWithheldToolName(name) {
27027
+ const leaf = toolLeafName3(name).replace(/[_-]/g, "").toLowerCase();
27028
+ return ["bash", "shellcommand", "execcommand"].includes(leaf);
27029
+ }
26904
27030
  function cursorNativeExecRedirect(exec2, tools, providerIdentifier) {
26905
27031
  const messageId = numberValue2(exec2.id ?? 0);
26906
27032
  const execId = stringValue2(exec2.execId) || `redirect-${messageId}`;
@@ -27112,22 +27238,23 @@ function grepArguments(tool, input) {
27112
27238
  args.path = input.path;
27113
27239
  }
27114
27240
  const optionMappings = [
27115
- [input.glob || void 0, "glob"],
27116
- [input.outputMode, "output_mode"],
27117
- [input.caseInsensitive ? true : void 0, "case_insensitive"],
27118
- [input.contextBefore, "context_before"],
27119
- [input.contextAfter, "context_after"],
27120
- [input.context, "context"],
27121
- [input.type, "type"],
27122
- [input.headLimit, "head_limit"],
27123
- [input.multiline ? true : void 0, "multiline"],
27124
- [input.sort, "sort"],
27125
- [input.sortAscending, "sort_ascending"],
27126
- [input.offset, "offset"]
27241
+ [input.glob || void 0, ["glob"]],
27242
+ [input.outputMode, ["output_mode"]],
27243
+ [input.caseInsensitive ? true : void 0, ["-i", "case_insensitive"]],
27244
+ [input.contextBefore, ["-B", "context_before"]],
27245
+ [input.contextAfter, ["-A", "context_after"]],
27246
+ [input.context, ["context", "-C"]],
27247
+ [input.type, ["type"]],
27248
+ [input.headLimit, ["head_limit"]],
27249
+ [input.multiline ? true : void 0, ["multiline"]],
27250
+ [input.sort, ["sort"]],
27251
+ [input.sortAscending, ["sort_ascending"]],
27252
+ [input.offset, ["offset"]]
27127
27253
  ];
27128
- for (const [value, key] of optionMappings) {
27254
+ for (const [value, candidates] of optionMappings) {
27129
27255
  if (value === void 0) continue;
27130
- if (!schemaHasProperty(tool.inputSchemaValue, key)) return null;
27256
+ const key = firstSchemaProperty(tool.inputSchemaValue, candidates);
27257
+ if (key === void 0) return null;
27131
27258
  args[key] = value;
27132
27259
  }
27133
27260
  return args;
@@ -28746,7 +28873,7 @@ function applyCursorToolBudget(request) {
28746
28873
  const supportsDeferredLoading = declaredTools.some((tool) => isCursorToolSearchName(tool.name));
28747
28874
  const sourceTools = declaredTools.filter((tool) => {
28748
28875
  const explicitlySelected = referencedNames.has(tool.name) || cursorToolMatches(tool.name, selectedName);
28749
- if (isCursorNativeRedirectToolName(tool.name) && !explicitlySelected) return false;
28876
+ if (isCursorWithheldToolName(tool.name) && !explicitlySelected) return false;
28750
28877
  return !supportsDeferredLoading || tool.defer_loading !== true || isCursorToolSearchName(tool.name) || explicitlySelected;
28751
28878
  });
28752
28879
  const wireTools = sourceTools.map(toCursorWireTool);
@@ -31146,6 +31273,63 @@ function bareModelName(model) {
31146
31273
  const prefix = `${GATEWAY_PROVIDER_NAMES[model.provider]}-`;
31147
31274
  return model.displayName.startsWith(prefix) ? model.displayName.slice(prefix.length) : model.displayName;
31148
31275
  }
31276
+ var codexRequestPolicy = {
31277
+ provider: "codex",
31278
+ shapeRequest: (request, steps) => steps.withholdWebSearchTools(steps.pruneSkillPayloads(request))
31279
+ };
31280
+ var cursorRequestPolicy = {
31281
+ provider: "cursor",
31282
+ shapeRequest: (request, steps) => steps.withholdWebSearchTools(steps.pruneSkillPayloads(request))
31283
+ };
31284
+ var kimiRequestPolicy = {
31285
+ provider: "kimi",
31286
+ shapeRequest: (request, steps) => steps.pruneSkillPayloads(request)
31287
+ };
31288
+ var opencodeGoRequestPolicy = {
31289
+ provider: "opencode",
31290
+ shapeRequest: (request, steps) => steps.withholdWebSearchTools(steps.pruneSkillPayloads(request))
31291
+ };
31292
+ var xaiRequestPolicy = {
31293
+ provider: "xai",
31294
+ shapeRequest: (request, steps) => steps.withholdWebSearchTools(steps.pruneSkillPayloads(request))
31295
+ };
31296
+ var POLICIES = {
31297
+ codex: codexRequestPolicy,
31298
+ cursor: cursorRequestPolicy,
31299
+ kimi: kimiRequestPolicy,
31300
+ // The provider id is `opencode`; its folder carries the `-go` subscription name.
31301
+ opencode: opencodeGoRequestPolicy,
31302
+ xai: xaiRequestPolicy
31303
+ };
31304
+ function resolveGatewayRequestPolicy(provider) {
31305
+ return POLICIES[provider];
31306
+ }
31307
+ function applyGatewayRequestPolicy(request, target2, withheldSkills) {
31308
+ const context = {
31309
+ target: target2,
31310
+ upstreamModel: upstreamModelId(target2),
31311
+ withheldSkills
31312
+ };
31313
+ const policy = resolveGatewayRequestPolicy(target2.provider);
31314
+ return policy.shapeRequest(request, buildPolicySteps(context), context);
31315
+ }
31316
+ function buildPolicySteps(context) {
31317
+ return {
31318
+ pruneSkillPayloads: (request) => {
31319
+ const pruned = pruneClaudeSkillPayloads(request.messages, {
31320
+ ...typeof context.target.contextWindow === "number" ? { contextWindow: context.target.contextWindow } : {},
31321
+ model: context.upstreamModel,
31322
+ withheld: context.withheldSkills
31323
+ });
31324
+ for (const skill of pruned.withheld) context.withheldSkills.add(skill.name);
31325
+ return pruned.changed ? { ...request, messages: [...pruned.messages] } : request;
31326
+ },
31327
+ withholdWebSearchTools: (request) => {
31328
+ const omitted = omitClaudeWebSearchTools(request);
31329
+ return omitted.changed ? omitted.request : request;
31330
+ }
31331
+ };
31332
+ }
31149
31333
  var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
31150
31334
  "connection",
31151
31335
  "content-encoding",
@@ -31364,19 +31548,11 @@ function createAiGatewayRouter(deps) {
31364
31548
  writeAnthropicError(res, 403, "permission_error", `AI gateway model is not enabled: ${requested}`);
31365
31549
  return true;
31366
31550
  }
31367
- if (target2) {
31368
- const pruned = pruneClaudeSkillPayloads(body.messages, {
31369
- ...typeof target2.contextWindow === "number" ? { contextWindow: target2.contextWindow } : {},
31370
- model: upstreamModelId(target2),
31371
- withheld: withheldSkills
31372
- });
31373
- for (const skill of pruned.withheld) withheldSkills.add(skill.name);
31374
- if (pruned.changed) body = { ...body, messages: [...pruned.messages] };
31375
- }
31376
- if (target2 && target2.provider !== "kimi") {
31377
- const omitted = omitClaudeWebSearchTools(body);
31378
- if (omitted.changed) body = omitted.request;
31551
+ const withoutUsageLimitDirectives = stripClaudeUsageLimitDirectives(body.messages);
31552
+ if (withoutUsageLimitDirectives.changed) {
31553
+ body = { ...body, messages: [...withoutUsageLimitDirectives.messages] };
31379
31554
  }
31555
+ if (target2) body = applyGatewayRequestPolicy(body, target2, withheldSkills);
31380
31556
  let credential = "";
31381
31557
  let chatgptAccountId = "";
31382
31558
  if (target2?.provider === "cursor") {
@@ -32338,9 +32514,13 @@ function buildClaudeGatewayArgs(context) {
32338
32514
  ]),
32339
32515
  ...context.mcpServers.length > 0 ? ["--mcp-config", buildClaudeMcpConfig(context.mcpServers)] : [],
32340
32516
  ...buildSettingsArgs(context.skillOverrides),
32517
+ ...buildSearchToolArgs(),
32341
32518
  "--dangerously-skip-permissions"
32342
32519
  ];
32343
32520
  }
32521
+ function buildSearchToolArgs() {
32522
+ return ["--allowedTools", "Grep,Glob"];
32523
+ }
32344
32524
  function buildSettingsArgs(skillOverrides) {
32345
32525
  if (skillOverrides === void 0 || Object.keys(skillOverrides).length === 0) return [];
32346
32526
  return ["--settings", JSON.stringify({ skillOverrides })];
@@ -45850,7 +46030,8 @@ function normalizeAttentionReason(value) {
45850
46030
  }
45851
46031
 
45852
46032
  // ../fleet-plugins/terminal/server/agent-api/background-report.ts
45853
- var NON_AGENT_BACKGROUND_TASK_TYPES = /* @__PURE__ */ new Set(["shell"]);
46033
+ var AGENT_BACKGROUND_TASK_TYPES = /* @__PURE__ */ new Set(["subagent", "teammate", "workflow"]);
46034
+ var NON_AGENT_BACKGROUND_TASK_TYPES = /* @__PURE__ */ new Set(["shell", "monitor"]);
45854
46035
  var NO_SETTLED_AGENT_IDS = /* @__PURE__ */ new Set();
45855
46036
  var NO_OPINION = { pending: void 0, settledAgentIds: void 0 };
45856
46037
  function readBackgroundHookReport(input, settledAgentIds = NO_SETTLED_AGENT_IDS) {
@@ -45867,29 +46048,36 @@ function readBackgroundHookReport(input, settledAgentIds = NO_SETTLED_AGENT_IDS)
45867
46048
  const selfAgentId = typeof payload.agent_id === "string" ? payload.agent_id : void 0;
45868
46049
  const listedIds = /* @__PURE__ */ new Set();
45869
46050
  let live = false;
45870
- let unreadable = false;
46051
+ let workUnknown = false;
46052
+ let listIncomplete = false;
45871
46053
  for (const task of tasks) {
45872
46054
  const entry = readTaskEntry(task);
45873
46055
  if (!entry) {
45874
- unreadable = true;
46056
+ workUnknown = true;
46057
+ listIncomplete = true;
45875
46058
  continue;
45876
46059
  }
45877
46060
  if (typeof entry.id === "string") listedIds.add(entry.id);
45878
- if (isLiveAgentWork(entry, selfAgentId, settledAgentIds)) live = true;
46061
+ else listIncomplete = true;
46062
+ const verdict = classifyBackgroundTask(entry, selfAgentId, settledAgentIds);
46063
+ if (verdict === "live") live = true;
46064
+ else if (verdict === "unreadable") workUnknown = true;
45879
46065
  }
45880
- if (!live && unreadable) return NO_OPINION;
46066
+ if (!live && workUnknown) return NO_OPINION;
45881
46067
  return {
45882
46068
  pending: live,
45883
- settledAgentIds: nextSettledAgentIds({ settledAgentIds, selfAgentId, listedIds, listComplete: !unreadable })
46069
+ settledAgentIds: nextSettledAgentIds({ settledAgentIds, selfAgentId, listedIds, listComplete: !listIncomplete })
45884
46070
  };
45885
46071
  }
45886
46072
  function readTaskEntry(task) {
45887
46073
  if (typeof task !== "object" || task === null || Array.isArray(task)) return null;
45888
46074
  return task;
45889
46075
  }
45890
- function isLiveAgentWork(entry, selfAgentId, settledAgentIds) {
45891
- if (typeof entry.id === "string" && (entry.id === selfAgentId || settledAgentIds.has(entry.id))) return false;
45892
- return !(typeof entry.type === "string" && NON_AGENT_BACKGROUND_TASK_TYPES.has(entry.type));
46076
+ function classifyBackgroundTask(entry, selfAgentId, settledAgentIds) {
46077
+ if (typeof entry.id === "string" && (entry.id === selfAgentId || settledAgentIds.has(entry.id))) return "not-agent-work";
46078
+ if (typeof entry.type !== "string") return "unreadable";
46079
+ if (AGENT_BACKGROUND_TASK_TYPES.has(entry.type)) return "live";
46080
+ return NON_AGENT_BACKGROUND_TASK_TYPES.has(entry.type) ? "not-agent-work" : "unreadable";
45893
46081
  }
45894
46082
  function nextSettledAgentIds({
45895
46083
  settledAgentIds,
@@ -47643,6 +47831,16 @@ function createConsoleObservabilityStore(deps = {}) {
47643
47831
  if (clearsAttention) delete session.attentionPending;
47644
47832
  return toTerminalSessionInfo(session);
47645
47833
  }
47834
+ function setTerminalSessionChatBackgroundPending(sessionId, pending) {
47835
+ const session = terminalSessionsById.get(sessionId);
47836
+ if (!session || session.chatActive !== true) return null;
47837
+ if (session.backgroundPendingExpiry) {
47838
+ clearTimeout(session.backgroundPendingExpiry);
47839
+ delete session.backgroundPendingExpiry;
47840
+ }
47841
+ session.backgroundPending = pending;
47842
+ return toTerminalSessionInfo(session);
47843
+ }
47646
47844
  function setTerminalSessionChatAwaiting(sessionId, awaiting) {
47647
47845
  const session = terminalSessionsById.get(sessionId);
47648
47846
  if (!session || session.chatActive !== true) return null;
@@ -47764,6 +47962,7 @@ function createConsoleObservabilityStore(deps = {}) {
47764
47962
  getTerminalSessionSettledAgentIds,
47765
47963
  setTerminalSessionModelActivity,
47766
47964
  setTerminalSessionChatActive,
47965
+ setTerminalSessionChatBackgroundPending,
47767
47966
  setTerminalSessionChatWorking,
47768
47967
  setTerminalSessionChatAwaiting,
47769
47968
  transitionTerminalSessionToDormant,
@@ -47971,7 +48170,9 @@ function eventsFromTranscriptLine(line, options) {
47971
48170
  const results = toolResultsFrom(line.message?.content, options);
47972
48171
  if (results.length > 0) return results;
47973
48172
  const text2 = readUserText(line.message?.content);
47974
- return text2 ? [{ kind: "dispatch", text: text2, ...atField }] : [];
48173
+ if (text2 === null) return [];
48174
+ if (isInjectedCarrier(line, text2)) return [{ kind: "turn-start", ...atField }];
48175
+ return [{ kind: "dispatch", text: text2, ...atField }];
47975
48176
  }
47976
48177
  if (line.type === "assistant") {
47977
48178
  return eventsFromAssistantContent(line.message?.content, options);
@@ -48287,11 +48488,29 @@ function readUserText(content) {
48287
48488
  }
48288
48489
  return normalizeDispatchText(parts.join("\n"));
48289
48490
  }
48491
+ var INJECTED_TRANSCRIPT_TAGS = /* @__PURE__ */ new Set([
48492
+ "task-notification",
48493
+ "local-command-stdout",
48494
+ "local-command-caveat",
48495
+ "command-name",
48496
+ "command-message",
48497
+ "system-reminder",
48498
+ "bash-input",
48499
+ "bash-stdout"
48500
+ ]);
48501
+ var LEADING_TAG_NAME = /^<([a-zA-Z][a-zA-Z0-9-]*)(?=[\s>])/;
48502
+ function isInjectedOrigin(origin) {
48503
+ const kind = origin?.kind;
48504
+ return typeof kind === "string" && kind.length > 0 && kind !== "human";
48505
+ }
48506
+ function isInjectedCarrier(line, text2) {
48507
+ if (isInjectedOrigin(line.origin)) return true;
48508
+ const tag = LEADING_TAG_NAME.exec(text2.trimStart())?.[1];
48509
+ return tag !== void 0 && INJECTED_TRANSCRIPT_TAGS.has(tag);
48510
+ }
48290
48511
  function normalizeDispatchText(text2) {
48291
48512
  const trimmed = text2.trim();
48292
- if (trimmed.length === 0) return null;
48293
- if (trimmed.startsWith("<system-reminder>") || trimmed.startsWith("<command-name>")) return null;
48294
- return capText(trimmed);
48513
+ return trimmed.length === 0 ? null : capText(trimmed);
48295
48514
  }
48296
48515
  function agentChatAskFromToolInput(name, id, input) {
48297
48516
  if (!input || typeof input !== "object" || Array.isArray(input)) return null;
@@ -48588,6 +48807,14 @@ var AgentChatSession = class {
48588
48807
  * 으로 남겨 두지 않고 거둬진 것으로 닫는다 — 화면이 없는 작업을 기다리게 두지 않기 위해서다.
48589
48808
  */
48590
48809
  liveJobs = /* @__PURE__ */ new Set();
48810
+ /**
48811
+ * 이미 결말을 낸 잡. 이름 붙은 에이전트는 다음 지시를 기다리며 세션에 상주하므로, 자기 결말을 낸
48812
+ * 뒤에도 살아 있는 작업 목록에 계속 실려 온다(PTY 쪽 hook payload에서 실측된 것과 같은 registry다).
48813
+ * 그 항목을 목록에 있다는 이유로 다시 살아 있는 작업으로 읽으면, 이 축에는 시한이 없으므로 세션이
48814
+ * 끝날 때까지 유휴·입력 대기 전이가 통째로 막힌다. 기억은 목록이 지워 준다 — 더 이상 실리지 않는
48815
+ * id는 registry에서 사라진 것이므로 버린다.
48816
+ */
48817
+ settledJobs = /* @__PURE__ */ new Set();
48591
48818
  /**
48592
48819
  * 중지 세대. 중지는 도는 턴 하나가 아니라 **그 시점의 큐 전체**를 끊어야 한다 — 큐에 밀려
48593
48820
  * 있던 턴이 중지 직후 태연히 시작하면, 사용자가 멈춘 것은 화면에서 멈추지 않는다. 각 턴은
@@ -48656,21 +48883,38 @@ var AgentChatSession = class {
48656
48883
  turnAt = null;
48657
48884
  lastAt = null;
48658
48885
  };
48886
+ let pendingOpenAt;
48887
+ const openPendingTurn = () => {
48888
+ if (pendingOpenAt === void 0) return;
48889
+ const at = pendingOpenAt;
48890
+ pendingOpenAt = void 0;
48891
+ closeReplayedTurn();
48892
+ turns += 1;
48893
+ turnAt = at;
48894
+ this.push(at === null ? { kind: "turn-start" } : { kind: "turn-start", at });
48895
+ };
48659
48896
  try {
48660
48897
  const raw = await promises.readFile(transcriptPath, "utf8");
48661
48898
  for (const line of raw.split("\n")) {
48662
48899
  if (line.trim().length === 0) continue;
48663
48900
  const mapped = chatReplayFromTranscriptLine(line, { cwd: this.seed.cwd, toolNames: this.toolNames });
48664
48901
  for (const event of mapped.events) {
48902
+ if (event.kind === "turn-start") {
48903
+ if (pendingOpenAt === void 0) pendingOpenAt = mapped.at ?? null;
48904
+ continue;
48905
+ }
48665
48906
  if (event.kind === "dispatch") {
48907
+ pendingOpenAt = void 0;
48666
48908
  closeReplayedTurn();
48667
48909
  turns += 1;
48668
48910
  turnAt = mapped.at ?? null;
48911
+ } else {
48912
+ openPendingTurn();
48669
48913
  }
48670
48914
  this.rememberTool(event);
48671
48915
  this.push(event);
48672
48916
  }
48673
- if (mapped.events.length > 0 && mapped.at !== void 0) lastAt = mapped.at;
48917
+ if (mapped.events.length > 0 && mapped.at !== void 0 && pendingOpenAt === void 0) lastAt = mapped.at;
48674
48918
  }
48675
48919
  closeReplayedTurn();
48676
48920
  } catch {
@@ -48839,11 +49083,7 @@ var AgentChatSession = class {
48839
49083
  /** 도구 이벤트가 지나갈 때 id→이름을 적어 둔다. 결과 요약 정책이 이 축 위에서 결정된다. */
48840
49084
  rememberTool(event) {
48841
49085
  if (event.kind === "job") {
48842
- this.jobKinds.set(event.id, event.jobKind);
48843
- if (this.jobKinds.size > JOB_KIND_CAP) {
48844
- const oldest = this.jobKinds.keys().next();
48845
- if (!oldest.done) this.jobKinds.delete(oldest.value);
48846
- }
49086
+ this.rememberJobKind(event.id, event.jobKind);
48847
49087
  return;
48848
49088
  }
48849
49089
  if (event.kind !== "tool" && event.kind !== "tool-start") return;
@@ -48854,6 +49094,46 @@ var AgentChatSession = class {
48854
49094
  if (!oldest.done) this.toolNames.delete(oldest.value);
48855
49095
  }
48856
49096
  }
49097
+ /**
49098
+ * 잡 하나의 종류를 적는다. 긴 세션에서 무한히 자라지 않게 상한을 두고 오래된 것부터 버리되, 아직
49099
+ * 살아 있는 잡은 건너뛴다 — 그 종류는 활동축이 지금 읽고 있고, 잃으면 축이 무의견으로 굳는다.
49100
+ */
49101
+ rememberJobKind(jobId, kind, incoming) {
49102
+ this.jobKinds.set(jobId, kind);
49103
+ if (this.jobKinds.size <= JOB_KIND_CAP) return;
49104
+ for (const oldest of this.jobKinds.keys()) {
49105
+ if (this.liveJobs.has(oldest) || incoming?.has(oldest) === true) continue;
49106
+ this.jobKinds.delete(oldest);
49107
+ return;
49108
+ }
49109
+ }
49110
+ /**
49111
+ * 살아 있는 작업 목록이 알려 준 종류를 적어 둔다.
49112
+ *
49113
+ * `background_tasks_changed`는 `task_type`을 싣고 오지만 스트림 이벤트(`jobs`)는 id만 나른다 —
49114
+ * 화면은 종류를 이미 첫 등장(`job`)에서 받았기 때문이다. 활동축은 사정이 다르다: 셸을 세지
49115
+ * 않으려면 이 목록에서 처음 본 잡의 종류도 알아야 하고, 그 값은 원본 메시지에만 있다.
49116
+ */
49117
+ rememberJobKinds(message) {
49118
+ if (message.type !== "system" || message.subtype !== "background_tasks_changed") return;
49119
+ const tasks = message.tasks;
49120
+ if (!Array.isArray(tasks)) return;
49121
+ const incoming = /* @__PURE__ */ new Set();
49122
+ for (const task of tasks) {
49123
+ if (!task || typeof task !== "object") continue;
49124
+ const id = task.task_id;
49125
+ if (typeof id !== "string" || id.length === 0) continue;
49126
+ incoming.add(id);
49127
+ }
49128
+ for (const task of tasks) {
49129
+ if (!task || typeof task !== "object") continue;
49130
+ const id = task.task_id;
49131
+ if (typeof id !== "string" || id.length === 0) continue;
49132
+ const rawKind = task.task_type;
49133
+ if (typeof rawKind !== "string") continue;
49134
+ this.rememberJobKind(id, readJobKind(rawKind), incoming);
49135
+ }
49136
+ }
48857
49137
  /**
48858
49138
  * 결말 알림이 알려 준 출력 파일 경로를 적어 둔다.
48859
49139
  *
@@ -49236,6 +49516,7 @@ var AgentChatSession = class {
49236
49516
  if (this.latestSessionId !== this.reportedSessionId) this.syncProviderSessionOnce();
49237
49517
  }
49238
49518
  this.rememberJobOutput(message);
49519
+ this.rememberJobKinds(message);
49239
49520
  this.trackLiveContext(message);
49240
49521
  for (const event of chatEventsFromSdkMessage(message, { cwd: this.seed.cwd, toolNames: this.toolNames })) {
49241
49522
  this.ingest(event);
@@ -49274,12 +49555,50 @@ var AgentChatSession = class {
49274
49555
  }
49275
49556
  /** 살아 있는 잡의 집합을 이벤트대로 따라간다. `jobs`는 세는 것이 아니라 통째로 갈아 끼운다. */
49276
49557
  trackJob(event) {
49277
- if (event.kind === "job") this.liveJobs.add(event.id);
49278
- else if (event.kind === "job-end") this.liveJobs.delete(event.id);
49279
- else if (event.kind === "jobs") {
49558
+ if (event.kind === "job") {
49559
+ this.liveJobs.add(event.id);
49560
+ this.settledJobs.delete(event.id);
49561
+ } else if (event.kind === "job-end") {
49562
+ this.liveJobs.delete(event.id);
49563
+ this.settledJobs.add(event.id);
49564
+ } else if (event.kind === "jobs") {
49280
49565
  this.liveJobs.clear();
49281
- for (const id of event.ids) this.liveJobs.add(id);
49566
+ for (const id of event.ids) {
49567
+ if (!this.settledJobs.has(id)) this.liveJobs.add(id);
49568
+ }
49569
+ const listed = new Set(event.ids);
49570
+ for (const id of [...this.settledJobs]) {
49571
+ if (!listed.has(id)) this.settledJobs.delete(id);
49572
+ }
49573
+ } else return;
49574
+ this.syncBackgroundPending();
49575
+ }
49576
+ /**
49577
+ * 살아 있는 잡 목록을 활동축의 백그라운드 대기값으로 접는다.
49578
+ *
49579
+ * 세는 것은 에이전트 작업뿐이다 — 서브에이전트(이름 붙은 팀메이트 포함)와 워크플로우. PTY 어댑터가
49580
+ * hook 목록에서 셸과 MCP 감시 작업을 빼는 것과 같은 계약이며, 이유도 같다: 사람이 기다리는 것은
49581
+ * 자기 일을 대신 하는 에이전트이지 그 에이전트가 남긴 명령이 아니고, 그것까지 세면 긴 백그라운드
49582
+ * 명령 하나가 세션을 유휴 밖에 세워 둔 채 유휴 휴면까지 막는다.
49583
+ *
49584
+ * 처음 보는 종류는 세지 않는다. PTY 쪽은 같은 자리에서 무의견으로 물러나지만 그 축에는 시한이
49585
+ * 있어 무의견이 영원하지 않다 — 이 축에는 시한이 없으므로, 여기서 물러나면 상주하는 미지 잡 하나가
49586
+ * 에이전트 작업이 다 끝난 뒤에도 배지를 영영 켜 둔다. 새 종류를 놓치는 쪽은 이 기능이 없던 상태로
49587
+ * 돌아갈 뿐이고, 그 종류는 화면의 잡 목록에 `other`로 서서 눈에 띈다.
49588
+ *
49589
+ * 턴 경계는 여기서 보지 않는다. 턴이 도는 동안에도 값은 서지만, 읽는 쪽이 작업 중을 앞세우므로
49590
+ * 화면에 나타나는 것은 턴이 닫힌 뒤부터다 — 그 순간이 곧 "남은 것은 백그라운드뿐"인 순간이다.
49591
+ */
49592
+ syncBackgroundPending() {
49593
+ let pending = false;
49594
+ for (const id of this.liveJobs) {
49595
+ const kind = this.jobKinds.get(id);
49596
+ if (kind === "agent" || kind === "workflow") {
49597
+ pending = true;
49598
+ break;
49599
+ }
49282
49600
  }
49601
+ this.seed.reportBackgroundPending(pending);
49283
49602
  }
49284
49603
  /**
49285
49604
  * 턴 하나를 연다. 화면의 스피너와 활동축이 같은 순간에 켜지는 자리다.
@@ -49313,6 +49632,7 @@ var AgentChatSession = class {
49313
49632
  ...end.answer === void 0 ? {} : { answer: end.answer }
49314
49633
  });
49315
49634
  this.abandonAsks("The turn ended before the question was answered.");
49635
+ this.syncBackgroundPending();
49316
49636
  this.seed.reportActivity(false);
49317
49637
  this.liveContextTotal = null;
49318
49638
  const awaiting = this.awaitingTurn;
@@ -49358,6 +49678,8 @@ var AgentChatSession = class {
49358
49678
  }
49359
49679
  for (const id of [...this.liveJobs]) this.push({ kind: "job-end", id, status: "stopped" });
49360
49680
  this.liveJobs.clear();
49681
+ this.settledJobs.clear();
49682
+ this.syncBackgroundPending();
49361
49683
  this.settlingStoppedTurn = false;
49362
49684
  this.closeTurn({ ok: false });
49363
49685
  this.releaseTurnCloseWaiters();
@@ -51047,6 +51369,11 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
51047
51369
  reportAwaiting: (awaiting) => {
51048
51370
  const updated = observability.setTerminalSessionChatAwaiting(node.id, awaiting);
51049
51371
  if (updated) observability.notifySessionUpdated(updated);
51372
+ },
51373
+ reportBackgroundPending: (pending) => {
51374
+ const before = observability.getTerminalSessionInfo(node.id)?.backgroundPending === true;
51375
+ const updated = observability.setTerminalSessionChatBackgroundPending(node.id, pending);
51376
+ if (updated && before !== pending) observability.notifySessionUpdated(updated);
51050
51377
  }
51051
51378
  }
51052
51379
  };