nolo-cli 0.38.0-alpha.3 → 0.47.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/{_executeModel-SWONBJ7Z.js → _executeModel-5PG6CUWD.js} +41 -42
  2. package/{action-KMBZ3LCZ.js → action-FLLC2SIX.js} +7 -7
  3. package/{addContentAction-KQ44UT2U.js → addContentAction-SVSPUUK4.js} +6 -6
  4. package/{addDialogAgentAction-ILSDCMYL.js → addDialogAgentAction-W3JGU4F7.js} +5 -5
  5. package/{agentDeleteCommand-SMUJYI6N.js → agentDeleteCommand-SHSPQZHI.js} +25 -26
  6. package/{agentEmailCommands-YWCGZ4RO.js → agentEmailCommands-LQKO5GRW.js} +54 -55
  7. package/{agentListCommands-ULFV2XJQ.js → agentListCommands-5QTNEYMQ.js} +31 -29
  8. package/{agentMachineCommands-7YZ2TIYZ.js → agentMachineCommands-P5K454CN.js} +24 -25
  9. package/{agentPullCommand-TSWAJPS2.js → agentPullCommand-M552CYSI.js} +2 -2
  10. package/{agentRecordCommands-EVOSSHJR.js → agentRecordCommands-Z4QYCNP4.js} +50 -51
  11. package/{agentRunCommand-PO55WUIY.js → agentRunCommand-HV3Y34B5.js} +52 -53
  12. package/{agentRunControl-7MUPSYDQ.js → agentRunControl-LHXOP4IU.js} +43 -1
  13. package/{agentRuntimeLocal-Q6D3G6G7.js → agentRuntimeLocal-ZKXYFLXI.js} +20 -21
  14. package/{agentSlice-6TRUUOSX.js → agentSlice-NKFLP3RI.js} +6 -6
  15. package/{agentSuperviseCommands-MKWW3ICX.js → agentSuperviseCommands-IMRZ56RC.js} +2 -2
  16. package/{appDeleteCommands-JDBSRLTX.js → appDeleteCommands-XYGAZIJX.js} +24 -25
  17. package/{appDeployCommands-Q5H2NBKQ.js → appDeployCommands-7T5CABRB.js} +24 -25
  18. package/{appDeployStatusCommands-PSYBUIZT.js → appDeployStatusCommands-KTFQMRPZ.js} +24 -25
  19. package/{appGetCommands-IX64IUW6.js → appGetCommands-HOX2ULSV.js} +24 -25
  20. package/{appListCommands-BJZFX65H.js → appListCommands-3SLCEHND.js} +24 -25
  21. package/{authCommand-B2XRHNJH.js → authCommand-OY2E2VQK.js} +12 -12
  22. package/{authCommands-GWABTFMR.js → authCommands-SBQVTD6V.js} +5 -5
  23. package/{chatQueueReduxAdapter-N7Z5MUPU.js → chatQueueReduxAdapter-WMMBQK3Y.js} +20 -20
  24. package/chunk-25J3BB35.js +6041 -0
  25. package/{chunk-U6QQLXWI.js → chunk-2X5S4IBZ.js} +1 -1
  26. package/{chunk-MJWX4JQO.js → chunk-3EEK7T3D.js} +1 -1
  27. package/{chunk-UNHKVD2N.js → chunk-43CXXVJD.js} +1 -1
  28. package/{chunk-WRP5TTM6.js → chunk-4MXGXSYN.js} +83 -3
  29. package/{chunk-7N7XKGEQ.js → chunk-4ZNDBDWG.js} +6 -6
  30. package/{chunk-XVVNNJAA.js → chunk-5BPZLBFB.js} +20 -51
  31. package/{chunk-MWGCZ4CS.js → chunk-5PPFYHU2.js} +3 -3
  32. package/{chunk-W445DPVT.js → chunk-5QRCPERU.js} +1 -1
  33. package/{chunk-GKHV4UH5.js → chunk-5UOBIU5G.js} +31 -3
  34. package/{chunk-BMB563Y6.js → chunk-6BYAXNH5.js} +3 -3
  35. package/{chunk-SMPAUSX5.js → chunk-6FGXGFAH.js} +2 -2
  36. package/{chunk-46VPXYIV.js → chunk-6LYUN76O.js} +5 -5
  37. package/{chunk-FTM4KUOZ.js → chunk-7XTC3KRG.js} +1 -1
  38. package/{chunk-IMHO5LC6.js → chunk-7XZ5GXSN.js} +1 -1
  39. package/{chunk-E2OQ5N2F.js → chunk-AEXP2UZT.js} +341 -16
  40. package/{chunk-MZZZYUZS.js → chunk-AV2CELZZ.js} +37 -12
  41. package/{chunk-JI2TIPLP.js → chunk-AYXSUBVC.js} +1 -1
  42. package/{chunk-J6WH4PYZ.js → chunk-CIF4X6ZM.js} +1 -1
  43. package/{chunk-GZRMWK5R.js → chunk-DQLYEGP7.js} +2 -2
  44. package/{chunk-GUYMR3JL.js → chunk-E2QH2AYX.js} +69 -3
  45. package/{chunk-B6H5XCT2.js → chunk-EMEUU5IY.js} +3 -3
  46. package/{chunk-X6EN3KJX.js → chunk-ENYDQKOB.js} +319 -71
  47. package/{chunk-RCFIB45Q.js → chunk-EVBFYPDC.js} +2 -2
  48. package/{chunk-PV734VZX.js → chunk-EXLOROHG.js} +1 -1
  49. package/{chunk-WRN7L5CS.js → chunk-FA4SY4F5.js} +1 -1
  50. package/{chunk-4WVNHL3V.js → chunk-FAWK6DBF.js} +157 -51
  51. package/{chunk-IFS5CUEH.js → chunk-FJHLA6SX.js} +1 -1
  52. package/{chunk-T2BZICLK.js → chunk-G46DB6KG.js} +49 -6
  53. package/{chunk-FGLKRJDI.js → chunk-GK74OOFT.js} +2 -2
  54. package/{chunk-LM6IYQ3F.js → chunk-GLKOYSRU.js} +8 -1
  55. package/{chunk-JIADXJYU.js → chunk-H2G6JXTR.js} +1 -1
  56. package/{chunk-IKGHZMK2.js → chunk-H2JQB6B6.js} +12 -12
  57. package/{chunk-MGIHZ5CJ.js → chunk-HAPCUPEO.js} +2 -2
  58. package/{chunk-LD6Y6RJY.js → chunk-HGDOIDQ4.js} +2 -2
  59. package/{chunk-NZLQ7B5P.js → chunk-HOM6NILS.js} +3 -3
  60. package/{chunk-IZNJDUJ4.js → chunk-I4JISVWK.js} +1 -1
  61. package/{chunk-N4XRDM26.js → chunk-IA3A5373.js} +2 -2
  62. package/{chunk-Y5MMUI3L.js → chunk-IWUPQUWA.js} +2 -2
  63. package/{chunk-L7SMDIL6.js → chunk-JEGPB5H2.js} +3 -3
  64. package/{chunk-GEPHJJW4.js → chunk-L7NG2M6H.js} +1 -1
  65. package/{chunk-XQES6LXQ.js → chunk-LFLWZX76.js} +2 -2
  66. package/{chunk-IIOHYLIG.js → chunk-LGUQHYAL.js} +1 -1
  67. package/{chunk-4AWH4LQA.js → chunk-LYGNQHUW.js} +3 -1
  68. package/{db-3H3JECGY.js → chunk-M5PVFLD4.js} +11 -158
  69. package/{chunk-WN5AJG5I.js → chunk-MKZRKDQM.js} +1 -1
  70. package/{chunk-VOXNBPLB.js → chunk-MUIJYGYW.js} +649 -41
  71. package/{chunk-CYAC5WXX.js → chunk-MWT2BBCH.js} +213 -45
  72. package/{chunk-V5GA74HI.js → chunk-MZXZJVVD.js} +20 -39
  73. package/{chunk-ZBB3MZR3.js → chunk-OLNC7AWK.js} +7 -4
  74. package/{chunk-J5NMOM35.js → chunk-OYNDYVSE.js} +1 -1
  75. package/{chunk-C6ELZQ7H.js → chunk-OZVGA4SY.js} +1 -1
  76. package/{chunk-K4KIFRIN.js → chunk-Q24S4NDF.js} +1 -1
  77. package/{chunk-NRROOY22.js → chunk-Q6DMA2OM.js} +271 -130
  78. package/{chunk-2WKUUY2E.js → chunk-SVEUG45B.js} +5 -3
  79. package/{chunk-IZCHPD7T.js → chunk-TNPCQNGQ.js} +11 -6
  80. package/{chunk-4DMPISV6.js → chunk-URBGEE42.js} +2 -2
  81. package/{chunk-2KO5QQFV.js → chunk-V2IPBG5U.js} +1 -1
  82. package/{chunk-B6YYACBT.js → chunk-WPFNAEIW.js} +1 -1
  83. package/{chunk-23KKIMD5.js → chunk-WVGEBWA6.js} +4 -4
  84. package/{chunk-KU6CDTZ5.js → chunk-XMI3APSU.js} +61 -49
  85. package/{chunk-64KIQZA2.js → chunk-XO3G35OV.js} +1 -1
  86. package/{chunk-UWGWUOVN.js → chunk-YNOGNUBC.js} +108 -47
  87. package/{chunk-XT73C4VH.js → chunk-Z5NW3O7V.js} +1 -1
  88. package/{codingSkills-H6N5EKSX.js → codingSkills-BYQI6EPT.js} +2 -2
  89. package/{createAgentAutomationAction-U57ZMPMS.js → createAgentAutomationAction-ENDNA3Q4.js} +21 -21
  90. package/{createDialogAction-MAJLBNK4.js → createDialogAction-2KNX6S2D.js} +23 -23
  91. package/{createPageAction-4EX7HU6G.js → createPageAction-SNBCYPK6.js} +21 -21
  92. package/db-VQSAOWAO.js +162 -0
  93. package/{dbSlice-PIQGICNM.js → dbSlice-XWUM4QOF.js} +5 -5
  94. package/deleteFile-NPZ64DFN.js +1 -1
  95. package/{dialogCommands-4MHZXEIS.js → dialogCommands-6E5P35DG.js} +28 -26
  96. package/{dialogSlice-AL3UJLOJ.js → dialogSlice-RTPOD6EW.js} +20 -20
  97. package/{docCreateCommands-GDYFEK67.js → docCreateCommands-JWY25UFB.js} +23 -24
  98. package/{docDeleteCommands-WIE5CXED.js → docDeleteCommands-BPJOCSX4.js} +24 -25
  99. package/{docReadCommands-LTOOPJVC.js → docReadCommands-BSEZCYTQ.js} +23 -24
  100. package/{docStore-35B5VAFF.js → docStore-TYHVLU7G.js} +22 -22
  101. package/{docUpdateCommands-TZGAGXSG.js → docUpdateCommands-S2IE2VV4.js} +23 -24
  102. package/{fetchMessages-CUEFN4GQ.js → fetchMessages-KZQVBXKY.js} +3 -3
  103. package/{getPricing-P3E2WRIH.js → getPricing-X3HXP6VQ.js} +3 -3
  104. package/{handleSendMessageAction-PXX2R5JS.js → handleSendMessageAction-QYXDQXGH.js} +21 -21
  105. package/index.js +69 -69
  106. package/levelLazyShim-GZBKODGI.js +7 -0
  107. package/{localDialogRead-P7CHFMIC.js → localDialogRead-NQ33HBSI.js} +2 -2
  108. package/{localRuntimeAuthority-S77B5QVB.js → localRuntimeAuthority-KQDAE64E.js} +4 -4
  109. package/{localRuntimeDb-N3H3HQ7G.js → localRuntimeDb-PF7CFKDL.js} +2 -2
  110. package/{machineCommands-VCPPY23X.js → machineCommands-QCE4KLGR.js} +48 -49
  111. package/{machineWsRunDispatch-A4C727GL.js → machineWsRunDispatch-FBRMGU5D.js} +48 -49
  112. package/{models-L7S2EKPM.js → models-7LWATQFH.js} +2 -2
  113. package/package.json +1 -1
  114. package/{read-6C2XD3YZ.js → read-DQQ75VI3.js} +2 -2
  115. package/{readDocTool-Y7AB365O.js → readDocTool-HCQQFAZD.js} +1 -1
  116. package/{readlineWorkspace-BIBIM6DG.js → readlineWorkspace-2TACWKMN.js} +164 -78
  117. package/{removeDialogAgentAction-KG5WMJRZ.js → removeDialogAgentAction-DNONRKST.js} +7 -7
  118. package/{runAgentClientLoop-RKDXMIZW.js → runAgentClientLoop-GBLDVBT5.js} +39 -39
  119. package/{runtimeDoctorCommands-HT4O2PXL.js → runtimeDoctorCommands-JKTTN723.js} +4 -4
  120. package/serverStoreFactory-YMXDF4NI.js +20 -0
  121. package/{setDialogExtraReferencesAction-O2NLXMSM.js → setDialogExtraReferencesAction-ERHUPT3F.js} +6 -6
  122. package/{setPrimaryDialogAgentAction-FYYUY7PB.js → setPrimaryDialogAgentAction-6C2HYNEC.js} +7 -7
  123. package/{settingSlice-GPLI6E3M.js → settingSlice-YVRJFKQX.js} +6 -6
  124. package/{spaceCommands-4FMO3SNP.js → spaceCommands-XOLPJMRK.js} +23 -24
  125. package/{spaceSlice-2W4GJ2OE.js → spaceSlice-OQPAGQDP.js} +21 -21
  126. package/{streamAgentChatTurn-FS52LDQP.js → streamAgentChatTurn-SN5XJJ7U.js} +91 -49
  127. package/{switchDialogAgentAction-GH5P6Q3Z.js → switchDialogAgentAction-HP57XYFP.js} +5 -5
  128. package/{tools-3J5CKEE7.js → tools-Y65Y3CI4.js} +32 -32
  129. package/{tuiSummaryLlmCaller-SHRB4RWM.js → tuiSummaryLlmCaller-NBTWT57U.js} +25 -14
  130. package/{updateDialogTitleAction-5MGPA3OO.js → updateDialogTitleAction-OAOP7CCE.js} +24 -23
  131. package/chunk-4APLX2RT.js +0 -1514
  132. package/chunk-P66M7LSQ.js +0 -58
  133. package/{chunk-6WD3OOJS.js → chunk-UV6VISQJ.js} +0 -0
  134. package/{tableCommands-PQUJ55AS.js → tableCommands-P2TXJ3CF.js} +1 -1
@@ -7,7 +7,7 @@ import {
7
7
  providerHttpFailure,
8
8
  readSseFrames,
9
9
  throwIfChatCompletionStreamFailed
10
- } from "./chunk-MZZZYUZS.js";
10
+ } from "./chunk-AV2CELZZ.js";
11
11
  import {
12
12
  FRESH_TOOL_OUTPUT_MAX_CHARS,
13
13
  MAX_HISTORICAL_TOOL_CONTENT_CHARS,
@@ -17,17 +17,10 @@ import {
17
17
  resolveHistoricalToolContentCap,
18
18
  resolveToolOutputProfile,
19
19
  stripImagePartsFromMessages
20
- } from "./chunk-64KIQZA2.js";
20
+ } from "./chunk-XO3G35OV.js";
21
21
  import {
22
22
  sanitizeForOutbound
23
23
  } from "./chunk-T27IEBNQ.js";
24
- import {
25
- EMPTY_ASSISTANT_REPAIR_PROMPT,
26
- formatLengthTruncatedReasoningTail,
27
- hasAssistantVisibleOutput,
28
- resolveEmptyAssistantFallbackMessage,
29
- resolveEmptyAssistantOutcome
30
- } from "./chunk-P66M7LSQ.js";
31
24
  import {
32
25
  createThinkParserState,
33
26
  createToolCallAccumulator,
@@ -39,10 +32,11 @@ import {
39
32
  buildCurrentTimeBlock,
40
33
  buildIdentityBlock,
41
34
  buildRuntimeGuidanceBlocks,
35
+ normalizeChatCompletionsBodyForProvider,
42
36
  requiresBareImageUrl,
43
37
  resolveToolGuidedSections,
44
38
  toBareImageUrlMessages
45
- } from "./chunk-T2BZICLK.js";
39
+ } from "./chunk-G46DB6KG.js";
46
40
  import {
47
41
  convertMessagesToResponsesInput,
48
42
  extractReasoningFromResponseOutput,
@@ -52,11 +46,11 @@ import {
52
46
  shouldStripReasoningContentForOutbound,
53
47
  toOpenAiCompatibleMessages,
54
48
  toResponsesTools
55
- } from "./chunk-BMB563Y6.js";
49
+ } from "./chunk-6BYAXNH5.js";
56
50
  import {
57
51
  buildProviderAuthHeaders,
58
52
  buildProviderExecutionPlan
59
- } from "./chunk-GKHV4UH5.js";
53
+ } from "./chunk-5UOBIU5G.js";
60
54
  import {
61
55
  NOLO_WORKSPACE_TOOL_NAMES,
62
56
  buildNoloWorkspaceCommandArgs,
@@ -64,13 +58,18 @@ import {
64
58
  noloStringArg,
65
59
  parseNoloWorkspaceToolArguments,
66
60
  resolveNoloDialogInput
67
- } from "./chunk-IZCHPD7T.js";
61
+ } from "./chunk-TNPCQNGQ.js";
68
62
  import {
69
63
  buildDialogFallbackTitleFromUserInput
70
64
  } from "./chunk-2MBTWINN.js";
71
65
  import {
72
- getModelContextWindow
73
- } from "./chunk-WRP5TTM6.js";
66
+ EMPTY_ASSISTANT_REPAIR_PROMPT,
67
+ getModelContextWindow,
68
+ hasAssistantVisibleOutput,
69
+ resolveEmptyAssistantFallbackMessage,
70
+ resolveEmptyAssistantOutcome,
71
+ resolveTruncatedReasoningTailLog
72
+ } from "./chunk-4MXGXSYN.js";
74
73
  import {
75
74
  COMPACTION_SUMMARY_SCHEMA_VERSION,
76
75
  COMPACTION_SUMMARY_SYSTEM_PROMPT,
@@ -84,7 +83,7 @@ import {
84
83
  formatMessagesForSummaryWithTruncation,
85
84
  planCompression,
86
85
  planContextUsage
87
- } from "./chunk-FGLKRJDI.js";
86
+ } from "./chunk-GK74OOFT.js";
88
87
  import {
89
88
  resolveNoloStateDir
90
89
  } from "./chunk-FD36SCCD.js";
@@ -107,13 +106,13 @@ import {
107
106
  import {
108
107
  dialogMessageKey,
109
108
  dialogMessageRange
110
- } from "./chunk-2WKUUY2E.js";
111
- import {
112
- asTrimmedNonEmptyStringArray
113
- } from "./chunk-I42VC5KR.js";
109
+ } from "./chunk-SVEUG45B.js";
114
110
  import {
115
111
  asOptionalFiniteNumber
116
112
  } from "./chunk-WWC7I6TA.js";
113
+ import {
114
+ asTrimmedNonEmptyStringArray
115
+ } from "./chunk-I42VC5KR.js";
117
116
  import {
118
117
  asTrimmedLowercaseString
119
118
  } from "./chunk-QSJ3HFEF.js";
@@ -447,6 +446,15 @@ function spillToolOutput(options) {
447
446
  hash
448
447
  };
449
448
  }
449
+ function formatToolOverflowMarker(args) {
450
+ const { spillRef, totalChars, totalLines, omittedChars } = args;
451
+ const omittedNote = omittedChars !== void 0 && omittedChars > 0 ? ` omitted: ${omittedChars} chars;` : "";
452
+ return `
453
+
454
+ [TOOL-OVERFLOW: full output spilled to ${spillRef} (total: ${totalChars} chars, ${totalLines} lines;${omittedNote} inspect via readFile or grep)]
455
+
456
+ `;
457
+ }
450
458
 
451
459
  // packages/agent-runtime/localAutoCompaction.ts
452
460
  import { createHash as createHash2 } from "node:crypto";
@@ -1770,22 +1778,32 @@ async function runLocalAgentTurn(input) {
1770
1778
  if (!!result.reasoning_content) reasoningEmptyRepairCount += 1;
1771
1779
  continue;
1772
1780
  }
1781
+ if (outcome.kind === "ok_with_warning") {
1782
+ result = {
1783
+ ...result,
1784
+ errorMessage: resolveEmptyAssistantFallbackMessage(outcome.reason),
1785
+ emptyAssistantFallbackReason: outcome.reason,
1786
+ emptyAssistantRepairUsed
1787
+ };
1788
+ break;
1789
+ }
1773
1790
  if (outcome.kind === "fallback") {
1774
- if (outcome.reason === "length_truncated" && result.reasoning_content) {
1775
- const reasoningTailLog = formatLengthTruncatedReasoningTail(result.reasoning_content);
1776
- if (reasoningTailLog) {
1777
- console.warn(`
1791
+ const reasoningTailLog = resolveTruncatedReasoningTailLog(outcome.reason, result.reasoning_content);
1792
+ if (reasoningTailLog) {
1793
+ console.warn(`
1778
1794
  ${reasoningTailLog}
1779
1795
  `);
1780
- }
1781
1796
  }
1797
+ const fallbackMessage = resolveEmptyAssistantFallbackMessage(outcome.reason);
1782
1798
  result = {
1783
1799
  ...result,
1784
- content: resolveEmptyAssistantFallbackMessage(outcome.reason),
1800
+ content: fallbackMessage,
1785
1801
  // 只打标记不抛错:交互侧仍照常显示诊断文案(零行为变化)。
1786
1802
  // 上层(后台 run 编排者)据 emptyAssistantFallbackReason 判断
1787
1803
  // 是否把本轮结算为 failed。见 LocalAgentTurnResult 注释。
1788
- emptyAssistantFallbackReason: outcome.reason
1804
+ errorMessage: fallbackMessage,
1805
+ emptyAssistantFallbackReason: outcome.reason,
1806
+ emptyAssistantRepairUsed
1789
1807
  };
1790
1808
  break;
1791
1809
  }
@@ -2009,12 +2027,17 @@ ${reasoningTailLog}
2009
2027
  }
2010
2028
  result = result;
2011
2029
  if (!skipFinalAppend) {
2030
+ const truncatedReasoningTailLog = resolveTruncatedReasoningTailLog(
2031
+ result.emptyAssistantFallbackReason,
2032
+ result.reasoning_content
2033
+ );
2012
2034
  messages.push({
2013
2035
  role: "assistant",
2014
2036
  content: result.content,
2015
2037
  // 与中间轮(:561)一致带上 reasoning_content,让 saveTurn 持久化思维链,
2016
2038
  // 空轮/异常排查时能回看模型实际想了什么。
2017
- ...result.reasoning_content ? { reasoning_content: result.reasoning_content } : {}
2039
+ ...result.reasoning_content ? { reasoning_content: result.reasoning_content } : {},
2040
+ ...truncatedReasoningTailLog ? { thinkContent: truncatedReasoningTailLog } : {}
2018
2041
  });
2019
2042
  }
2020
2043
  const turnMessages = applyPersistedTurnInput(
@@ -2105,7 +2128,12 @@ function buildResponsesRequestBody(body, model) {
2105
2128
  messages: void 0,
2106
2129
  max_tokens: void 0,
2107
2130
  reasoning_effort: void 0,
2108
- tool_choice: void 0
2131
+ tool_choice: void 0,
2132
+ // stream_options 是 chat.completions 专属参数;客户端按 chat 线构建的 body
2133
+ // 携带它(旧版本 runtime 或 wire 分流窗口期)经此处转 Responses 上游时必须
2134
+ // 剥离,否则 OpenAI 直接 400 Unknown parameter: 'stream_options.include_usage'。
2135
+ // Responses 线天然在流末返回 usage,无需 include_usage。
2136
+ stream_options: void 0
2109
2137
  };
2110
2138
  }
2111
2139
 
@@ -2276,9 +2304,13 @@ function buildOpenAiCompatibleChatCompletionRequest(args) {
2276
2304
  stream: args.stream ?? false,
2277
2305
  ...args.providerConfig.requestOptions,
2278
2306
  ...args.tools && args.tools.length > 0 ? { tools: args.tools } : {},
2279
- ...!isResponses && args.stream ? getUsageRequestOptions(args.providerConfig.provider, { api: "chat-completions" }) : {}
2307
+ ...!isResponses && args.stream ? getUsageRequestOptions(args.providerConfig.usageProvider ?? args.providerConfig.provider, { api: "chat-completions" }) : {}
2280
2308
  };
2281
- const body = isResponses ? buildResponsesRequestBody(rawBody, args.providerConfig.model) : rawBody;
2309
+ const body = isResponses ? buildResponsesRequestBody(rawBody, args.providerConfig.model) : normalizeChatCompletionsBodyForProvider({
2310
+ body: rawBody,
2311
+ provider: args.providerConfig.provider,
2312
+ model: args.providerConfig.model
2313
+ });
2282
2314
  const endpoint = args.providerConfig.endpoint;
2283
2315
  const headers = {
2284
2316
  "Content-Type": "application/json",
@@ -2342,6 +2374,11 @@ function parseOpenAiCompatibleChatCompletionResponse(args) {
2342
2374
  }
2343
2375
  function processOpenAiCompatibleSseEvent(event, state) {
2344
2376
  for (const line of event.split("\n")) {
2377
+ const trimmed = line.trim();
2378
+ if (trimmed.startsWith("data:") && trimmed.slice(5).trim() === "[DONE]") {
2379
+ state.sawDone = true;
2380
+ continue;
2381
+ }
2345
2382
  const parsed = parseSseDataLineJson(line);
2346
2383
  if (parsed == null) continue;
2347
2384
  applyChatCompletionDelta(parsed, state);
@@ -2356,7 +2393,8 @@ async function readOpenAiCompatibleSseCompletion(args) {
2356
2393
  thinkState: createThinkParserState(),
2357
2394
  toolCallTextState: createToolCallTextParserState(),
2358
2395
  onTextDelta: args.onTextDelta,
2359
- onReasoningDelta: args.onReasoningDelta
2396
+ onReasoningDelta: args.onReasoningDelta,
2397
+ sawDone: false
2360
2398
  };
2361
2399
  for await (const frame of readSseFrames(args.response)) {
2362
2400
  processOpenAiCompatibleSseEvent(frame, state);
@@ -2364,11 +2402,13 @@ async function readOpenAiCompatibleSseCompletion(args) {
2364
2402
  flushChatCompletionStream(state);
2365
2403
  throwIfChatCompletionStreamFailed(state);
2366
2404
  const tool_calls = finalizeAccumulatedToolCalls(state.accumulatedToolCalls);
2405
+ const streamComplete = state.sawDone || !!state.usage || !!state.finishReason;
2367
2406
  return {
2368
2407
  content: state.content,
2369
2408
  ...state.reasoning ? { reasoning_content: state.reasoning } : {},
2370
2409
  ...tool_calls.length > 0 ? { tool_calls } : {},
2371
- ...state.usage ? { usage: state.usage, stream_complete: true } : {},
2410
+ ...state.usage ? { usage: state.usage } : {},
2411
+ ...streamComplete ? { stream_complete: true } : {},
2372
2412
  ...state.finishReason ? { finish_reason: state.finishReason } : {}
2373
2413
  };
2374
2414
  }
@@ -2493,7 +2533,8 @@ async function resolveOpenAiCompatibleProviderConfig(args) {
2493
2533
  apiKey: plan.apiKey,
2494
2534
  ...plan.apiKeyHeader ? { apiKeyHeader: plan.apiKeyHeader } : {},
2495
2535
  provider: plan.provider,
2496
- requestOptions: plan.requestOptions
2536
+ requestOptions: plan.requestOptions,
2537
+ ...plan.usageProvider ? { usageProvider: plan.usageProvider } : {}
2497
2538
  };
2498
2539
  }
2499
2540
 
@@ -2753,6 +2794,9 @@ function buildDialogMessageWriteOps(args) {
2753
2794
  // 持久化思维链(reasoning):空轮/异常排查的关键证据,
2754
2795
  // 回读由 dialogMessageRecordToAgentRuntimeMessage 还原。
2755
2796
  ...typeof message.reasoning_content === "string" ? { reasoning_content: message.reasoning_content } : {},
2797
+ // web 思考折叠可读的截断 reasoning 尾部(带 marker)。仅在截断
2798
+ // 兜底/告警轮由 localLoop 附加,正常轮无此字段。
2799
+ ...typeof message.thinkContent === "string" ? { thinkContent: message.thinkContent } : {},
2756
2800
  ...message.role === "user" ? { userId: args.userId } : {},
2757
2801
  ...message.role === "assistant" ? {
2758
2802
  agentKey: args.input.agentKey,
@@ -2767,6 +2811,20 @@ function buildDialogMessageWriteOps(args) {
2767
2811
  };
2768
2812
  });
2769
2813
  }
2814
+ function buildEmptyAssistantWoundFields(result, existingDialog) {
2815
+ const reason = result.emptyAssistantFallbackReason;
2816
+ if (reason) {
2817
+ return {
2818
+ ...result.errorMessage ? { errorMessage: result.errorMessage } : {},
2819
+ fallbackReason: reason,
2820
+ ...typeof result.emptyAssistantRepairUsed === "boolean" ? { repairUsed: result.emptyAssistantRepairUsed } : {}
2821
+ };
2822
+ }
2823
+ if (existingDialog?.fallbackReason || existingDialog?.errorMessage) {
2824
+ return { errorMessage: null, fallbackReason: null, repairUsed: null };
2825
+ }
2826
+ return {};
2827
+ }
2770
2828
  function buildRuntimeCheckpointField(args) {
2771
2829
  if (args.runtimeToolSurface) {
2772
2830
  return {
@@ -2806,6 +2864,10 @@ function buildAgentRuntimeDialogWritePlan(args) {
2806
2864
  const titleChanged = typeof args.titleOverride === "string" && args.titleOverride.trim().length > 0;
2807
2865
  const titleUpdatedAtIso = titleChanged ? nowIso : existingTitleUpdatedAtMs > 0 ? new Date(existingTitleUpdatedAtMs).toISOString() : args.existingDialog?.createdAt ?? nowIso;
2808
2866
  const { availableToolNames: _deprecatedAvailableToolNames, ...cleanExistingCheckpoint } = asRecordOrEmpty(args.existingDialog?.runtimeCheckpoint);
2867
+ const woundFields = buildEmptyAssistantWoundFields(
2868
+ args.input.result,
2869
+ args.existingDialog
2870
+ );
2809
2871
  const dialogRecord = {
2810
2872
  ...asRecordOrEmpty(args.existingDialog),
2811
2873
  id: dialogId,
@@ -2817,6 +2879,7 @@ function buildAgentRuntimeDialogWritePlan(args) {
2817
2879
  titleSource: pickedTitle.titleSource,
2818
2880
  titleUpdatedAt: titleUpdatedAtIso,
2819
2881
  status: args.input.result.error === true ? "failed" : "done",
2882
+ ...woundFields,
2820
2883
  triggerType: `${args.runtimeHost}-local`,
2821
2884
  executionMode: args.existingDialog?.executionMode ?? "foreground",
2822
2885
  createdAt: args.existingDialog?.createdAt ?? nowIso,
@@ -3294,6 +3357,13 @@ var DEFAULT_LOCAL_TOOLS = /* @__PURE__ */ new Set([
3294
3357
  "execShell",
3295
3358
  "launchProcess",
3296
3359
  "listProcesses",
3360
+ // ProcessTask follow-up tools (Phase 1). Same tier as listProcesses: they
3361
+ // observe/stop tasks the agent itself already spawned, so their blast radius
3362
+ // is strictly narrower than launchProcess (which is default-enabled).
3363
+ "taskWait",
3364
+ "taskLogs",
3365
+ "taskStop",
3366
+ "tasks",
3297
3367
  // Scheduling tools: forking/observing sub-agents is a declared capability
3298
3368
  // of capable agents, and the blast radius is strictly narrower than
3299
3369
  // execShell (already default-enabled). Requiring an env allowlist for these
@@ -3609,10 +3679,44 @@ function spawnToWebStreams(options) {
3609
3679
  }
3610
3680
 
3611
3681
  // packages/agent-runtime/processTask.ts
3682
+ var PROCESS_TASK_TERMINAL_STATUSES = [
3683
+ "stopped",
3684
+ "exited",
3685
+ "failed"
3686
+ ];
3687
+ function isProcessTaskTerminalStatus(status) {
3688
+ return typeof status === "string" && PROCESS_TASK_TERMINAL_STATUSES.includes(status);
3689
+ }
3690
+ function reportedTaskStatus(source, exitCode) {
3691
+ if (source === "killed") {
3692
+ return "stopped";
3693
+ }
3694
+ if (source === "exited") {
3695
+ return exitCode !== void 0 && exitCode !== 0 ? "failed" : "exited";
3696
+ }
3697
+ if (source === "started" || source === "promoted") {
3698
+ return "running";
3699
+ }
3700
+ return source;
3701
+ }
3702
+ var DEFAULT_TASK_EVENT_RETENTION_MS = 5 * 60 * 1e3;
3703
+ var DEFAULT_MAX_TASK_EVENTS_TASKS = 200;
3612
3704
  var ProcessTaskEventLog = class {
3613
3705
  events = /* @__PURE__ */ new Map();
3706
+ terminalTasks = /* @__PURE__ */ new Map();
3707
+ retentionMs;
3708
+ maxTasks;
3709
+ now;
3710
+ evictedCount = 0;
3711
+ activeOverflowCount = 0;
3712
+ constructor(options) {
3713
+ this.retentionMs = options?.retentionMs ?? DEFAULT_TASK_EVENT_RETENTION_MS;
3714
+ this.maxTasks = options?.maxTasks ?? DEFAULT_MAX_TASK_EVENTS_TASKS;
3715
+ this.now = options?.now ?? Date.now;
3716
+ }
3614
3717
  /** Append one event; assigns the next monotonic seq for the task. */
3615
3718
  append(input) {
3719
+ const currentTime = this.now();
3616
3720
  const list = this.events.get(input.taskId) ?? [];
3617
3721
  const lastSeq = list.length > 0 ? list[list.length - 1].seq : 0;
3618
3722
  const event = Object.freeze({
@@ -3620,23 +3724,79 @@ var ProcessTaskEventLog = class {
3620
3724
  seq: lastSeq + 1,
3621
3725
  type: input.type,
3622
3726
  pid: input.pid,
3623
- createdAt: Date.now(),
3727
+ createdAt: currentTime,
3624
3728
  ...input.exitCode !== void 0 ? { exitCode: input.exitCode } : {}
3625
3729
  });
3626
3730
  list.push(event);
3627
3731
  this.events.set(input.taskId, list);
3732
+ if (input.type === "exited" || input.type === "killed") {
3733
+ this.terminalTasks.set(input.taskId, currentTime);
3734
+ }
3735
+ this.evict(currentTime);
3628
3736
  return { ...event };
3629
3737
  }
3630
- /** All events of the task with `seq > cursor` (default 0 = from the start). */
3738
+ /**
3739
+ * All events of the task with `seq > cursor` (default 0 = from the start).
3740
+ *
3741
+ * NOTE for cursor consumers (Phase 1 taskWait/taskLogs): an empty array is
3742
+ * ambiguous — it means either "no new events yet" or "the whole task was
3743
+ * evicted" (retention is per-taskId, never partial, so a cursor can never
3744
+ * land in a hole). Do NOT treat empty as "still running and idle": pair the
3745
+ * read with `hasTask(taskId)` to tell the two apart, otherwise a wait loop
3746
+ * can hang forever on an evicted task. Eviction only ever happens after a
3747
+ * terminal event plus the retention delay, so a live task is always readable.
3748
+ */
3631
3749
  read(taskId, cursor = 0) {
3632
3750
  return (this.events.get(taskId) ?? []).filter((event) => event.seq > cursor).map((event) => ({ ...event }));
3633
3751
  }
3752
+ /**
3753
+ * Whether the task's event trail is still retained. Lets a cursor consumer
3754
+ * disambiguate the empty result of `read()` (see its note).
3755
+ */
3756
+ hasTask(taskId) {
3757
+ return this.events.has(taskId);
3758
+ }
3634
3759
  latestSeq(taskId) {
3635
3760
  const list = this.events.get(taskId);
3636
3761
  return list && list.length > 0 ? list[list.length - 1].seq : 0;
3637
3762
  }
3763
+ getStats() {
3764
+ return {
3765
+ taskCount: this.events.size,
3766
+ terminalTaskCount: this.terminalTasks.size,
3767
+ activeTaskCount: this.events.size - this.terminalTasks.size,
3768
+ evictedCount: this.evictedCount,
3769
+ activeOverflowCount: this.activeOverflowCount
3770
+ };
3771
+ }
3638
3772
  clear() {
3639
3773
  this.events.clear();
3774
+ this.terminalTasks.clear();
3775
+ this.evictedCount = 0;
3776
+ this.activeOverflowCount = 0;
3777
+ }
3778
+ evict(currentTime) {
3779
+ for (const [taskId, terminalTime] of this.terminalTasks.entries()) {
3780
+ if (currentTime - terminalTime >= this.retentionMs) {
3781
+ this.events.delete(taskId);
3782
+ this.terminalTasks.delete(taskId);
3783
+ this.evictedCount += 1;
3784
+ }
3785
+ }
3786
+ if (this.events.size > this.maxTasks) {
3787
+ const sortedTerminal = Array.from(this.terminalTasks.entries()).sort((a, b) => a[1] - b[1]);
3788
+ for (const [taskId] of sortedTerminal) {
3789
+ if (this.events.size <= this.maxTasks) {
3790
+ break;
3791
+ }
3792
+ this.events.delete(taskId);
3793
+ this.terminalTasks.delete(taskId);
3794
+ this.evictedCount += 1;
3795
+ }
3796
+ if (this.events.size > this.maxTasks) {
3797
+ this.activeOverflowCount += 1;
3798
+ }
3799
+ }
3640
3800
  }
3641
3801
  };
3642
3802
 
@@ -3649,7 +3809,10 @@ function mintTaskId(pid) {
3649
3809
  var ProcessRegistry = class {
3650
3810
  processes = /* @__PURE__ */ new Map();
3651
3811
  byTaskId = /* @__PURE__ */ new Map();
3652
- eventLog = new ProcessTaskEventLog();
3812
+ eventLog;
3813
+ constructor(eventLogOptions) {
3814
+ this.eventLog = new ProcessTaskEventLog(eventLogOptions);
3815
+ }
3653
3816
  /**
3654
3817
  * Pre-register an envelope right after a successful spawn and emit the
3655
3818
  * `started` event. Returns the stored record (with its stable taskId).
@@ -3704,6 +3867,15 @@ var ProcessRegistry = class {
3704
3867
  latestTaskSeq(taskId) {
3705
3868
  return this.eventLog.latestSeq(taskId);
3706
3869
  }
3870
+ /**
3871
+ * Whether the task's event trail is still retained. Cursor consumers MUST
3872
+ * pair this with getTaskEvents(): an empty read means either "no new events"
3873
+ * or "the whole trail was evicted" (§1.1), and only this call tells them
3874
+ * apart — without it a wait loop hangs forever on an evicted task.
3875
+ */
3876
+ hasTaskEvents(taskId) {
3877
+ return this.eventLog.hasTask(taskId);
3878
+ }
3707
3879
  /**
3708
3880
  * Timeout-detach promotion: mark the pre-registered envelope as a background
3709
3881
  * task. Same record, same taskId, no re-execution, no second registration.
@@ -3718,9 +3890,19 @@ var ProcessRegistry = class {
3718
3890
  }
3719
3891
  return { ...item };
3720
3892
  }
3893
+ /**
3894
+ * Terminate a background process group and update its status to "stopped".
3895
+ *
3896
+ * Invariant (§1.3): Transient foreground envelopes (workspaceShell grace
3897
+ * period before timeout detach) are NOT stoppable through this entry point;
3898
+ * their lifecycle belongs to the foreground runner (abort/timeout). Calling
3899
+ * kill() on a transient entry does not signal the process, does not emit a
3900
+ * "killed" event, and returns false.
3901
+ */
3721
3902
  kill(pid, signal = "SIGTERM") {
3722
3903
  const item = this.processes.get(pid);
3723
3904
  if (!item) return false;
3905
+ if (item.transient) return false;
3724
3906
  if (item.status === "running") {
3725
3907
  try {
3726
3908
  process.kill(-item.pgid, signal);
@@ -4313,6 +4495,242 @@ ${stderr.trim()}` : "",
4313
4495
  };
4314
4496
  }
4315
4497
 
4498
+ // packages/agent-runtime/processTaskTools.ts
4499
+ var TASK_WAIT_DEFAULT_TIMEOUT_MS = 6e4;
4500
+ var TASK_WAIT_MAX_TIMEOUT_MS = 5 * 6e4;
4501
+ var TASK_WAIT_POLL_INTERVAL_MS = 250;
4502
+ var TASK_STOP_GRACE_MS = 2e3;
4503
+ var TERMINAL_EVENT_TYPES = /* @__PURE__ */ new Set(["exited", "killed"]);
4504
+ function defaultSleep(ms) {
4505
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
4506
+ }
4507
+ function defaultSignalSender(pid, signal) {
4508
+ process.kill(pid, signal);
4509
+ }
4510
+ function statusOf(record) {
4511
+ return reportedTaskStatus(record.status, record.exitCode);
4512
+ }
4513
+ function inspectTask(registry2, taskId) {
4514
+ const record = registry2.getByTaskId(taskId);
4515
+ const hasEvents = registry2.hasTaskEvents(taskId);
4516
+ return { record, hasEvents, known: hasEvents || record !== void 0 };
4517
+ }
4518
+ function notFoundDetail(taskId) {
4519
+ return `No task ${taskId} is known: it was never started, or both its envelope and its event trail have been dropped. Call tasks to list current handles.`;
4520
+ }
4521
+ function evictedDetail(taskId) {
4522
+ return `Event trail of ${taskId} is gone (retention window elapsed). No further events will appear; do not wait again.`;
4523
+ }
4524
+ function lastTerminalEvent(events) {
4525
+ for (let i = events.length - 1; i >= 0; i -= 1) {
4526
+ const event = events[i];
4527
+ if (TERMINAL_EVENT_TYPES.has(event.type)) return event;
4528
+ }
4529
+ return void 0;
4530
+ }
4531
+ function resolveTaskWaitTimeoutMs(raw) {
4532
+ const value = typeof raw === "number" ? raw : typeof raw === "string" && raw.trim() !== "" ? Number(raw) : Number.NaN;
4533
+ if (!Number.isFinite(value)) {
4534
+ return { timeoutMs: TASK_WAIT_DEFAULT_TIMEOUT_MS, clamped: false };
4535
+ }
4536
+ if (value <= 0) return { timeoutMs: 0, clamped: value < 0 };
4537
+ if (value > TASK_WAIT_MAX_TIMEOUT_MS) {
4538
+ return { timeoutMs: TASK_WAIT_MAX_TIMEOUT_MS, clamped: true };
4539
+ }
4540
+ return { timeoutMs: value, clamped: false };
4541
+ }
4542
+ async function runTaskWait(args) {
4543
+ const { registry: registry2, taskId } = args;
4544
+ const now = args.now ?? Date.now;
4545
+ const sleep = args.sleep ?? defaultSleep;
4546
+ const pollIntervalMs = Math.max(1, args.pollIntervalMs ?? TASK_WAIT_POLL_INTERVAL_MS);
4547
+ const { timeoutMs, clamped } = resolveTaskWaitTimeoutMs(args.timeoutMs);
4548
+ const initial = inspectTask(registry2, taskId);
4549
+ if (!initial.known) {
4550
+ return { outcome: "not-found", taskId, detail: notFoundDetail(taskId) };
4551
+ }
4552
+ const startedAt = now();
4553
+ const deadline = startedAt + timeoutMs;
4554
+ const maxPolls = Math.ceil(timeoutMs / pollIntervalMs) + 2;
4555
+ for (let poll = 0; poll < maxPolls; poll += 1) {
4556
+ const events = registry2.getTaskEvents(taskId, 0);
4557
+ if (!registry2.hasTaskEvents(taskId)) {
4558
+ const record = registry2.getByTaskId(taskId);
4559
+ return {
4560
+ outcome: "evicted",
4561
+ taskId,
4562
+ cursor: 0,
4563
+ events: [],
4564
+ ...record ? { status: statusOf(record) } : {},
4565
+ detail: evictedDetail(taskId)
4566
+ };
4567
+ }
4568
+ const terminal = lastTerminalEvent(events);
4569
+ if (terminal) {
4570
+ return {
4571
+ outcome: "terminal",
4572
+ taskId,
4573
+ status: reportedTaskStatus(terminal.type, terminal.exitCode),
4574
+ ...terminal.exitCode !== void 0 ? { exitCode: terminal.exitCode } : {},
4575
+ cursor: registry2.latestTaskSeq(taskId),
4576
+ events,
4577
+ waitedMs: Math.max(0, now() - startedAt)
4578
+ };
4579
+ }
4580
+ if (now() >= deadline) {
4581
+ return {
4582
+ outcome: "timeout",
4583
+ taskId,
4584
+ status: "running",
4585
+ cursor: registry2.latestTaskSeq(taskId),
4586
+ events,
4587
+ waitedMs: Math.max(0, now() - startedAt),
4588
+ timeoutMs,
4589
+ ...clamped ? { timeoutClamped: true } : {}
4590
+ };
4591
+ }
4592
+ await sleep(Math.min(pollIntervalMs, Math.max(1, deadline - now())));
4593
+ }
4594
+ return {
4595
+ outcome: "timeout",
4596
+ taskId,
4597
+ status: "running",
4598
+ cursor: registry2.latestTaskSeq(taskId),
4599
+ events: registry2.getTaskEvents(taskId, 0),
4600
+ waitedMs: Math.max(0, now() - startedAt),
4601
+ timeoutMs,
4602
+ ...clamped ? { timeoutClamped: true } : {}
4603
+ };
4604
+ }
4605
+ function runTaskLogs(args) {
4606
+ const { registry: registry2, taskId } = args;
4607
+ const cursorRaw = typeof args.cursor === "number" ? args.cursor : typeof args.cursor === "string" && args.cursor.trim() !== "" ? Number(args.cursor) : Number.NaN;
4608
+ const cursor = Number.isFinite(cursorRaw) && cursorRaw > 0 ? Math.floor(cursorRaw) : 0;
4609
+ const { record, hasEvents, known } = inspectTask(registry2, taskId);
4610
+ if (!known) {
4611
+ return { outcome: "not-found", taskId, detail: notFoundDetail(taskId) };
4612
+ }
4613
+ if (!hasEvents) {
4614
+ return {
4615
+ outcome: "evicted",
4616
+ taskId,
4617
+ cursor: 0,
4618
+ events: [],
4619
+ ...record ? { status: statusOf(record) } : {},
4620
+ detail: evictedDetail(taskId)
4621
+ };
4622
+ }
4623
+ const events = registry2.getTaskEvents(taskId, cursor);
4624
+ const allEvents = registry2.getTaskEvents(taskId, 0);
4625
+ const terminal = lastTerminalEvent(allEvents);
4626
+ const status = terminal ? reportedTaskStatus(terminal.type, terminal.exitCode) : record ? statusOf(record) : "running";
4627
+ return {
4628
+ outcome: "ok",
4629
+ taskId,
4630
+ cursor: registry2.latestTaskSeq(taskId),
4631
+ events,
4632
+ status,
4633
+ terminal: isProcessTaskTerminalStatus(status)
4634
+ };
4635
+ }
4636
+ function formatTaskLogsContent(result, options) {
4637
+ const full = JSON.stringify(result);
4638
+ const limit = options?.outputLimit && options.outputLimit > 0 ? options.outputLimit : void 0;
4639
+ const effectiveLimit = limit ?? 2e4;
4640
+ if (full.length <= effectiveLimit) return { content: full };
4641
+ try {
4642
+ const spill = spillToolOutput({
4643
+ content: full,
4644
+ toolName: "taskLogs",
4645
+ workspaceRoot: options?.workspaceRoot
4646
+ });
4647
+ const omitted = result.outcome === "ok" ? result.events.length : 0;
4648
+ const head = JSON.stringify({
4649
+ ...result,
4650
+ events: [],
4651
+ eventsOmitted: omitted,
4652
+ logRef: spill.displayPath
4653
+ });
4654
+ return {
4655
+ content: `${head}${formatToolOverflowMarker({
4656
+ spillRef: spill.displayPath,
4657
+ totalChars: spill.totalChars,
4658
+ totalLines: spill.totalLines,
4659
+ omittedChars: Math.max(0, full.length - head.length),
4660
+ toolName: "taskLogs"
4661
+ })}`.trimEnd(),
4662
+ logRef: spill.displayPath
4663
+ };
4664
+ } catch {
4665
+ return { content: truncateToolOutput(full, effectiveLimit) };
4666
+ }
4667
+ }
4668
+ function isProcessGroupAlive(pgid, kill = defaultSignalSender) {
4669
+ try {
4670
+ kill(-pgid, 0);
4671
+ return true;
4672
+ } catch (error) {
4673
+ return error.code === "EPERM";
4674
+ }
4675
+ }
4676
+ async function runTaskStop(args) {
4677
+ const { registry: registry2, taskId } = args;
4678
+ const sleep = args.sleep ?? defaultSleep;
4679
+ const kill = args.kill ?? defaultSignalSender;
4680
+ const graceMs = Math.max(0, args.graceMs ?? TASK_STOP_GRACE_MS);
4681
+ const record = registry2.getByTaskId(taskId);
4682
+ if (!record) {
4683
+ return { outcome: "not-found", taskId, detail: notFoundDetail(taskId) };
4684
+ }
4685
+ const termed = registry2.kill(record.pid, "SIGTERM");
4686
+ if (!termed) {
4687
+ const reason = record.transient ? "transient-foreground" : record.status !== "running" ? "already-terminal" : "kill-refused";
4688
+ const detail = reason === "transient-foreground" ? `${taskId} is a foreground command still inside its grace window, not a background task; its lifecycle belongs to the running execShell call.` : reason === "already-terminal" ? `${taskId} is already ${statusOf(record)}; nothing to stop.` : `Registry refused to stop ${taskId}.`;
4689
+ return {
4690
+ outcome: "not-stoppable",
4691
+ taskId,
4692
+ pid: record.pid,
4693
+ status: statusOf(record),
4694
+ reason,
4695
+ detail
4696
+ };
4697
+ }
4698
+ if (graceMs > 0) await sleep(graceMs);
4699
+ if (!isProcessGroupAlive(record.pgid, kill)) {
4700
+ return {
4701
+ outcome: "stopped",
4702
+ taskId,
4703
+ pid: record.pid,
4704
+ status: reportedTaskStatus("killed"),
4705
+ signal: "SIGTERM",
4706
+ escalated: false
4707
+ };
4708
+ }
4709
+ try {
4710
+ kill(-record.pgid, "SIGKILL");
4711
+ } catch {
4712
+ }
4713
+ return {
4714
+ outcome: "stopped",
4715
+ taskId,
4716
+ pid: record.pid,
4717
+ status: reportedTaskStatus("killed"),
4718
+ signal: "SIGKILL",
4719
+ escalated: true
4720
+ };
4721
+ }
4722
+ function runTasks(args) {
4723
+ const tasks = args.registry.listBackground().map((record) => ({
4724
+ taskId: record.taskId,
4725
+ pid: record.pid,
4726
+ label: record.label,
4727
+ status: statusOf(record),
4728
+ startedAt: record.startedAt,
4729
+ persist: record.persist
4730
+ }));
4731
+ return { tasks, count: tasks.length };
4732
+ }
4733
+
4316
4734
  // packages/agent-runtime/capabilities/execShellCapability.ts
4317
4735
  function buildExecShellToolDefinition(toolName = "execShell") {
4318
4736
  return {
@@ -4452,9 +4870,21 @@ var WORKSPACE_TOOL_NAMES = [
4452
4870
  "captureVisualState",
4453
4871
  "execShell",
4454
4872
  "launchProcess",
4455
- "listProcesses"
4873
+ "listProcesses",
4874
+ "taskWait",
4875
+ "taskLogs",
4876
+ "taskStop",
4877
+ "tasks"
4878
+ ];
4879
+ var SHELL_TOOL_NAMES = [
4880
+ "execShell",
4881
+ "launchProcess",
4882
+ "listProcesses",
4883
+ "taskWait",
4884
+ "taskLogs",
4885
+ "taskStop",
4886
+ "tasks"
4456
4887
  ];
4457
- var SHELL_TOOL_NAMES = ["execShell", "launchProcess", "listProcesses"];
4458
4888
  var WORKSPACE_TOOL_NAME_SET = new Set(WORKSPACE_TOOL_NAMES);
4459
4889
  function buildWorkspacePathProperty() {
4460
4890
  return {
@@ -4678,6 +5108,87 @@ function buildListProcessesTool() {
4678
5108
  }
4679
5109
  };
4680
5110
  }
5111
+ function buildTaskWaitTool() {
5112
+ return {
5113
+ type: "function",
5114
+ function: {
5115
+ name: "taskWait",
5116
+ description: 'Block until a background task (from launchProcess, or an execShell command that detached) reaches a terminal state, or until timeoutMs elapses. Returns {outcome, status, exitCode?, cursor, events}: outcome "terminal" with status exited|failed|stopped when it finished, outcome "timeout" with status "running" when it is still going (call again to keep waiting), outcome "not-found" for an unknown taskId, or outcome "evicted" when the event trail aged out (never wait again in that case). `cursor` is the highest event seq covered by this response \u2014 pass it to taskLogs to read only newer events.',
5117
+ parameters: {
5118
+ type: "object",
5119
+ properties: {
5120
+ taskId: {
5121
+ type: "string",
5122
+ minLength: 1,
5123
+ description: "Task id returned by launchProcess or by a detached execShell result."
5124
+ },
5125
+ timeoutMs: {
5126
+ type: "number",
5127
+ description: "Wait budget in milliseconds. Defaults to 60000; values above 300000 are clamped to 300000."
5128
+ }
5129
+ },
5130
+ required: ["taskId"]
5131
+ }
5132
+ }
5133
+ };
5134
+ }
5135
+ function buildTaskLogsTool() {
5136
+ return {
5137
+ type: "function",
5138
+ function: {
5139
+ name: "taskLogs",
5140
+ description: "Read a background task's lifecycle events incrementally. Returns {outcome, status, cursor, events} where events are the started|promoted|exited|killed records with seq greater than the cursor you passed, and the returned `cursor` is the new high-water mark to pass next time (omit cursor to read from the beginning; repeating the same cursor is idempotent). Oversized output is spilled to a file and reported as `logRef`, which you can open with readFile.",
5141
+ parameters: {
5142
+ type: "object",
5143
+ properties: {
5144
+ taskId: {
5145
+ type: "string",
5146
+ minLength: 1,
5147
+ description: "Task id returned by launchProcess or by a detached execShell result."
5148
+ },
5149
+ cursor: {
5150
+ type: "number",
5151
+ description: "Last event seq you already consumed; only events with a higher seq are returned. Defaults to 0 (all retained events)."
5152
+ }
5153
+ },
5154
+ required: ["taskId"]
5155
+ }
5156
+ }
5157
+ };
5158
+ }
5159
+ function buildTaskStopTool() {
5160
+ return {
5161
+ type: "function",
5162
+ function: {
5163
+ name: "taskStop",
5164
+ description: 'Stop a background task: SIGTERM to its process group, then SIGKILL if it is still alive after a short grace window. Returns {outcome, status, signal, escalated}: outcome "stopped" with status "stopped" on success, or outcome "not-stoppable" (already finished, or a foreground command that is not a background task) or "not-found". Never reports success without signalling.',
5165
+ parameters: {
5166
+ type: "object",
5167
+ properties: {
5168
+ taskId: {
5169
+ type: "string",
5170
+ minLength: 1,
5171
+ description: "Task id returned by launchProcess or by a detached execShell result."
5172
+ }
5173
+ },
5174
+ required: ["taskId"]
5175
+ }
5176
+ }
5177
+ };
5178
+ }
5179
+ function buildTasksTool() {
5180
+ return {
5181
+ type: "function",
5182
+ function: {
5183
+ name: "tasks",
5184
+ description: "List background tasks you can still wait on, read logs from, or stop. Returns {count, tasks: {taskId, pid, label, status, startedAt, persist}[]}. Use it to recover a taskId you lost track of; foreground commands still running inside execShell are not listed.",
5185
+ parameters: {
5186
+ type: "object",
5187
+ properties: {}
5188
+ }
5189
+ }
5190
+ };
5191
+ }
4681
5192
  function buildWorkspaceToolDefinition(toolName) {
4682
5193
  if (toolName === "readFile") {
4683
5194
  return buildReadWorkspaceFileTool();
@@ -4695,6 +5206,10 @@ function buildWorkspaceToolDefinition(toolName) {
4695
5206
  if (toolName === "execShell") return buildExecShellTool(toolName);
4696
5207
  if (toolName === "launchProcess") return buildLaunchProcessTool();
4697
5208
  if (toolName === "listProcesses") return buildListProcessesTool();
5209
+ if (toolName === "taskWait") return buildTaskWaitTool();
5210
+ if (toolName === "taskLogs") return buildTaskLogsTool();
5211
+ if (toolName === "taskStop") return buildTaskStopTool();
5212
+ if (toolName === "tasks") return buildTasksTool();
4698
5213
  return null;
4699
5214
  }
4700
5215
  function filterDeclaredWorkspaceToolNames(args) {
@@ -6097,6 +6612,91 @@ async function listProcessesTool(args) {
6097
6612
  }
6098
6613
  };
6099
6614
  }
6615
+ function requireTaskId(parsed) {
6616
+ return readTrimmedString2(parsed.taskId);
6617
+ }
6618
+ function missingTaskIdResult(toolName) {
6619
+ return {
6620
+ content: JSON.stringify({
6621
+ outcome: "invalid-arguments",
6622
+ detail: `${toolName} requires a taskId (see tasks for current handles).`
6623
+ }),
6624
+ metadata: { status: "invalid-arguments", tool: toolName }
6625
+ };
6626
+ }
6627
+ async function taskWaitTool(args) {
6628
+ const parsed = parseWorkspaceToolArguments(args.call.arguments);
6629
+ const taskId = requireTaskId(parsed);
6630
+ if (!taskId) return missingTaskIdResult(args.call.name);
6631
+ const result = await runTaskWait({
6632
+ taskId,
6633
+ timeoutMs: parsed.timeoutMs,
6634
+ registry: getProcessRegistry(),
6635
+ ...args.pollIntervalMs !== void 0 ? { pollIntervalMs: args.pollIntervalMs } : {}
6636
+ });
6637
+ return {
6638
+ content: JSON.stringify(result),
6639
+ metadata: {
6640
+ taskId,
6641
+ outcome: result.outcome,
6642
+ ..."status" in result && result.status ? { status: result.status } : {},
6643
+ ..."cursor" in result ? { cursor: result.cursor } : {},
6644
+ ..."exitCode" in result && result.exitCode !== void 0 ? { exitCode: result.exitCode } : {}
6645
+ }
6646
+ };
6647
+ }
6648
+ async function taskLogsTool(args) {
6649
+ const parsed = parseWorkspaceToolArguments(args.call.arguments);
6650
+ const taskId = requireTaskId(parsed);
6651
+ if (!taskId) return missingTaskIdResult(args.call.name);
6652
+ const result = runTaskLogs({
6653
+ taskId,
6654
+ cursor: parsed.cursor,
6655
+ registry: getProcessRegistry()
6656
+ });
6657
+ const rendered = formatTaskLogsContent(result, {
6658
+ ...args.commandOutputLimit !== void 0 ? { outputLimit: args.commandOutputLimit } : {},
6659
+ workspaceRoot: args.workspaceRoot
6660
+ });
6661
+ return {
6662
+ content: rendered.content,
6663
+ metadata: {
6664
+ taskId,
6665
+ outcome: result.outcome,
6666
+ ..."status" in result && result.status ? { status: result.status } : {},
6667
+ ..."cursor" in result ? { cursor: result.cursor } : {},
6668
+ ..."events" in result ? { eventCount: result.events.length } : {},
6669
+ ...rendered.logRef ? { logRef: rendered.logRef } : {}
6670
+ }
6671
+ };
6672
+ }
6673
+ async function taskStopTool(args) {
6674
+ const parsed = parseWorkspaceToolArguments(args.call.arguments);
6675
+ const taskId = requireTaskId(parsed);
6676
+ if (!taskId) return missingTaskIdResult(args.call.name);
6677
+ const result = await runTaskStop({
6678
+ taskId,
6679
+ registry: getProcessRegistry(),
6680
+ ...args.graceMs !== void 0 ? { graceMs: args.graceMs } : {}
6681
+ });
6682
+ return {
6683
+ content: JSON.stringify(result),
6684
+ metadata: {
6685
+ taskId,
6686
+ outcome: result.outcome,
6687
+ ..."status" in result ? { status: result.status } : {},
6688
+ ..."pid" in result ? { pid: result.pid } : {},
6689
+ ..."signal" in result ? { signal: result.signal } : {}
6690
+ }
6691
+ };
6692
+ }
6693
+ async function tasksTool(_args) {
6694
+ const result = runTasks({ registry: getProcessRegistry() });
6695
+ return {
6696
+ content: JSON.stringify(result),
6697
+ metadata: { count: result.count, tasks: result.tasks }
6698
+ };
6699
+ }
6100
6700
  function createLocalWorkspaceToolExecutors(args) {
6101
6701
  const fileAccess = args.confirmExternalFileAccess ? { confirmExternalFileAccess: args.confirmExternalFileAccess } : {};
6102
6702
  const readFileLedger = /* @__PURE__ */ new Map();
@@ -6151,7 +6751,15 @@ function createLocalWorkspaceToolExecutors(args) {
6151
6751
  }),
6152
6752
  listProcesses: (call) => listProcessesTool({
6153
6753
  call
6154
- })
6754
+ }),
6755
+ taskWait: (call) => taskWaitTool({ call }),
6756
+ taskLogs: (call) => taskLogsTool({
6757
+ call,
6758
+ workspaceRoot: args.workspaceRoot,
6759
+ ...args.commandOutputLimit !== void 0 ? { commandOutputLimit: args.commandOutputLimit } : {}
6760
+ }),
6761
+ taskStop: (call) => taskStopTool({ call }),
6762
+ tasks: (call) => tasksTool({ call })
6155
6763
  };
6156
6764
  }
6157
6765
 
@@ -6289,7 +6897,7 @@ function buildLoadSkillExecutor(args) {
6289
6897
  const { resolveSkillByName } = await import("./skillDiscovery-COX56OBO.js");
6290
6898
  const resolved = resolveSkillByName(args.cwd, name);
6291
6899
  if (!resolved) {
6292
- const { resolveCodingBuiltinSlug, buildCodingSkillContentBySlug } = await import("./codingSkills-H6N5EKSX.js");
6900
+ const { resolveCodingBuiltinSlug, buildCodingSkillContentBySlug } = await import("./codingSkills-BYQI6EPT.js");
6293
6901
  const builtinSlug = resolveCodingBuiltinSlug(name);
6294
6902
  if (builtinSlug) {
6295
6903
  const body = buildCodingSkillContentBySlug(builtinSlug);