nolo-cli 0.34.0-alpha.3 → 0.36.0-alpha.1

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 (66) hide show
  1. package/{_executeModel-TMUXL7J5.js → _executeModel-2S6HNTBF.js} +15 -14
  2. package/{agentDeleteCommand-KA2IQAPG.js → agentDeleteCommand-EJ6RRIGV.js} +3 -3
  3. package/{agentEmailCommands-UIYBGCZW.js → agentEmailCommands-44XJPIUG.js} +15 -14
  4. package/{agentListCommands-L6JNUOLH.js → agentListCommands-HSWVSDLI.js} +3 -3
  5. package/{agentMachineCommands-5CQW5NZR.js → agentMachineCommands-VMWDSASB.js} +4 -4
  6. package/{agentRecordCommands-VUDJQIZH.js → agentRecordCommands-NLRF3ZCV.js} +15 -14
  7. package/{agentRunCommand-EJWYF3BL.js → agentRunCommand-RUCLOMIL.js} +16 -15
  8. package/{agentRuntimeLocal-XXBMDMBU.js → agentRuntimeLocal-GTNYV7LH.js} +3 -3
  9. package/{agentSlice-HTIEYKIU.js → agentSlice-LAILHBMM.js} +1 -1
  10. package/{appDeleteCommands-OUBL6VHB.js → appDeleteCommands-YDXSR75I.js} +3 -3
  11. package/{appDeployCommands-BZSS2MX4.js → appDeployCommands-4X5SN3EF.js} +3 -3
  12. package/{appDeployStatusCommands-P2ZQOY5X.js → appDeployStatusCommands-TK3JKBHH.js} +3 -3
  13. package/{appGetCommands-WCFWQZZM.js → appGetCommands-ZKQHHJYB.js} +3 -3
  14. package/{appListCommands-FVJ3QNL7.js → appListCommands-JRDOEEGV.js} +3 -3
  15. package/{chatQueueReduxAdapter-ZP2ZS63U.js → chatQueueReduxAdapter-DQWFZYNL.js} +2 -2
  16. package/{chunk-P2I7ELDC.js → chunk-2KO5QQFV.js} +1 -1
  17. package/{chunk-6HIGYUBQ.js → chunk-6XIE5ZGI.js} +1 -1
  18. package/{chunk-RKRXAL3V.js → chunk-7MWPUOKI.js} +5 -5
  19. package/chunk-AETZ7AD6.js +274 -0
  20. package/{chunk-IEEEWIVO.js → chunk-ANM6AIWX.js} +3 -3
  21. package/{chunk-PILJX4DH.js → chunk-ARFXKWJH.js} +8 -8
  22. package/{chunk-TLBZMLLJ.js → chunk-CVXDBLYH.js} +3 -3
  23. package/{chunk-QI3CHZUT.js → chunk-D72M3G32.js} +2 -2
  24. package/{chunk-XMFDQYWA.js → chunk-EJAL5LSG.js} +1 -1
  25. package/{chunk-AE5GVFMG.js → chunk-FXZBSLFK.js} +5 -5
  26. package/{chunk-WBZE7C6S.js → chunk-HRSIJO5A.js} +1 -1
  27. package/{chunk-5GQ6XCJY.js → chunk-JK7UM3LT.js} +11 -11
  28. package/{chunk-EY7V7REW.js → chunk-KHCLMKZZ.js} +6 -280
  29. package/{chunk-Z7COG62J.js → chunk-KR74XLMV.js} +2 -2
  30. package/{chunk-I7VDDN45.js → chunk-LBZHTQTD.js} +397 -14
  31. package/{chunk-ILZY5B4U.js → chunk-MUG4FG5J.js} +11 -11
  32. package/{chunk-Y5W623XU.js → chunk-NT3QRCVS.js} +3 -3
  33. package/chunk-OFZPJ3NT.js +114 -0
  34. package/{chunk-AA2YUJNV.js → chunk-P66M7LSQ.js} +4 -0
  35. package/{chunk-5LDKQUOT.js → chunk-QIMPDPEE.js} +1 -1
  36. package/{chunk-M3JQWYBC.js → chunk-T2BZICLK.js} +3 -1
  37. package/chunk-T5HJEUQN.js +956 -0
  38. package/{chunk-FT4XBCK4.js → chunk-YNAGAUJT.js} +7 -7
  39. package/{clipboardy-PRUHGPGC.js → clipboardy-BX4NMYHE.js} +7 -5
  40. package/{codingSkills-6F4EQFCM.js → codingSkills-H6N5EKSX.js} +3 -2
  41. package/{createAgentAutomationAction-TGUXUZCY.js → createAgentAutomationAction-767ZITQJ.js} +3 -3
  42. package/{createDialogAction-FD5AEOUF.js → createDialogAction-H75NDXYH.js} +6 -5
  43. package/{createPageAction-NPC3LYNK.js → createPageAction-KYJYZPLC.js} +5 -4
  44. package/{dialogCommands-ZF2RSNCG.js → dialogCommands-VYH2BY5H.js} +3 -3
  45. package/{dialogSlice-6BINHWQT.js → dialogSlice-YJV3AVFM.js} +2 -2
  46. package/{docCreateCommands-4VQ5MOHP.js → docCreateCommands-JS7A3OHQ.js} +6 -5
  47. package/{docDeleteCommands-M7WLNVPB.js → docDeleteCommands-HG5HNKZR.js} +6 -5
  48. package/{docReadCommands-6LU6P2WT.js → docReadCommands-BH4WN6HH.js} +6 -5
  49. package/{docStore-SIUFLABN.js → docStore-JOODSWDW.js} +4 -4
  50. package/{docUpdateCommands-S2LX4ENL.js → docUpdateCommands-TAXC7LHJ.js} +6 -5
  51. package/{handleSendMessageAction-QVG5YNZY.js → handleSendMessageAction-DGL6H4E4.js} +2 -2
  52. package/index.js +59 -46
  53. package/{machineCommands-OSEBN7BZ.js → machineCommands-4J5NN3JH.js} +15 -14
  54. package/{machineWsRunDispatch-NTJQCIIF.js → machineWsRunDispatch-KBPZKHM2.js} +15 -14
  55. package/package.json +1 -1
  56. package/{readlineWorkspace-JPME5S7A.js → readlineWorkspace-EP2W65SI.js} +35 -27
  57. package/{runAgentClientLoop-SOKEXCUF.js → runAgentClientLoop-B3TTKXIX.js} +12 -11
  58. package/{skillDiscovery-GPUDHDTZ.js → skillDiscovery-COX56OBO.js} +2 -1
  59. package/{spaceCommands-Z5JLMFFJ.js → spaceCommands-Y33JLAKF.js} +3 -3
  60. package/{spaceSlice-Z3BGCLGX.js → spaceSlice-ZKUZSVYD.js} +3 -3
  61. package/{streamAgentChatTurn-INF5RGK4.js → streamAgentChatTurn-PLTB7RMV.js} +14 -13
  62. package/{tools-ZELMSJZ3.js → tools-KQXDW66A.js} +9 -8
  63. package/{updateCommands-MNDUIKN7.js → updateCommands-IYKLF3JX.js} +3 -1
  64. package/{updateDialogTitleAction-EDUCIB7G.js → updateDialogTitleAction-OTYEQJAK.js} +3 -3
  65. package/chunk-2BWU4ORW.js +0 -2574
  66. package/chunk-VFVCYIRQ.js +0 -407
@@ -27,7 +27,7 @@ import {
27
27
  hasAssistantVisibleOutput,
28
28
  resolveEmptyAssistantFallbackMessage,
29
29
  resolveEmptyAssistantOutcome
30
- } from "./chunk-AA2YUJNV.js";
30
+ } from "./chunk-P66M7LSQ.js";
31
31
  import {
32
32
  createThinkParserState,
33
33
  createToolCallAccumulator,
@@ -42,7 +42,7 @@ import {
42
42
  requiresBareImageUrl,
43
43
  resolveToolGuidedSections,
44
44
  toBareImageUrlMessages
45
- } from "./chunk-M3JQWYBC.js";
45
+ } from "./chunk-T2BZICLK.js";
46
46
  import {
47
47
  convertMessagesToResponsesInput,
48
48
  extractReasoningFromResponseOutput,
@@ -722,6 +722,191 @@ async function maybeAutoCompactLocalHistory(args) {
722
722
  }
723
723
  }
724
724
 
725
+ // packages/agent-runtime/progressGuard.ts
726
+ var DEFAULT_MAX_CONSECUTIVE_IDENTICAL_ROUNDS = 5;
727
+ var DEFAULT_MAX_CONSECUTIVE_STAGNANT_TOOL_ROUNDS = 8;
728
+ function sortJsonKeys(obj) {
729
+ if (obj === null || typeof obj !== "object") return obj;
730
+ if (Array.isArray(obj)) return obj.map(sortJsonKeys);
731
+ const sorted = {};
732
+ for (const key of Object.keys(obj).sort()) {
733
+ sorted[key] = sortJsonKeys(obj[key]);
734
+ }
735
+ return sorted;
736
+ }
737
+ function normalizeToolArguments(rawArgs) {
738
+ if (typeof rawArgs !== "string") {
739
+ try {
740
+ return JSON.stringify(sortJsonKeys(rawArgs ?? ""));
741
+ } catch {
742
+ return String(rawArgs ?? "");
743
+ }
744
+ }
745
+ const trimmed = rawArgs.trim();
746
+ if (!trimmed) return "";
747
+ try {
748
+ const parsed = JSON.parse(trimmed);
749
+ return JSON.stringify(sortJsonKeys(parsed));
750
+ } catch {
751
+ return trimmed;
752
+ }
753
+ }
754
+ function extractNormalizedContentText(content) {
755
+ if (typeof content === "string") return content.trim();
756
+ if (!Array.isArray(content)) return "";
757
+ return content.map((part) => {
758
+ if (part?.type === "text") return String(part.text ?? "").trim();
759
+ if (part?.type === "image_url") return `[img:${part.image_url?.url ?? ""}]`;
760
+ return "";
761
+ }).filter(Boolean).join("\n");
762
+ }
763
+ function buildToolCallsSignature(toolCalls) {
764
+ if (!toolCalls || toolCalls.length === 0) return "";
765
+ return toolCalls.map(
766
+ (tc) => `${tc.function?.name ?? ""}:${normalizeToolArguments(tc.function?.arguments)}`
767
+ ).join(";;");
768
+ }
769
+ function buildAssistantActionFingerprint(result) {
770
+ const content = extractNormalizedContentText(result.content);
771
+ const reasoning = typeof result.reasoning_content === "string" ? result.reasoning_content.trim() : "";
772
+ let toolCallsSig = buildToolCallsSignature(result.tool_calls);
773
+ let hasToolCalls = Array.isArray(result.tool_calls) && result.tool_calls.length > 0;
774
+ if (!toolCallsSig && Array.isArray(result.output) && result.output.length > 0) {
775
+ const blocksToolCalls = result.output.filter(
776
+ (b) => b.type === "toolCall"
777
+ ).map((b) => b.toolCall);
778
+ if (blocksToolCalls.length > 0) {
779
+ toolCallsSig = buildToolCallsSignature(blocksToolCalls);
780
+ hasToolCalls = true;
781
+ }
782
+ }
783
+ const hasNonEmptyText = content.length > 0 || reasoning.length > 0;
784
+ const fingerprint = `${content}##${reasoning}##${toolCallsSig}`;
785
+ return {
786
+ fingerprint,
787
+ hasNonEmptyText,
788
+ hasToolCalls,
789
+ toolCallsSignature: toolCallsSig
790
+ };
791
+ }
792
+ function buildToolResultsSignature(toolExecutions) {
793
+ if (toolExecutions.length === 0) return "";
794
+ return toolExecutions.map((exec) => {
795
+ const contentStr = typeof exec.content === "string" ? exec.content.trim() : "";
796
+ const metaStr = exec.metadata ? JSON.stringify(sortJsonKeys(exec.metadata)) : "";
797
+ return `${exec.toolName}:${contentStr}:${metaStr}`;
798
+ }).join(";;");
799
+ }
800
+ function resolveProgressGuardConfig(options, env = typeof process !== "undefined" ? process.env : {}) {
801
+ const envDisabled = env.NOLO_LOOP_PROGRESS_GUARD_DISABLED === "true" || env.NOLO_LOOP_PROGRESS_GUARD_DISABLED === "1";
802
+ const disabled = options?.disabled ?? envDisabled ?? false;
803
+ const parsePositiveInt = (val, fallback) => {
804
+ if (typeof val === "number" && Number.isFinite(val) && val >= 1) {
805
+ return Math.floor(val);
806
+ }
807
+ if (typeof val === "string") {
808
+ const parsed = parseInt(val, 10);
809
+ if (Number.isFinite(parsed) && parsed >= 1) {
810
+ return parsed;
811
+ }
812
+ }
813
+ return fallback;
814
+ };
815
+ const maxConsecutiveIdenticalRounds = parsePositiveInt(
816
+ options?.maxConsecutiveIdenticalRounds ?? env.NOLO_LOOP_MAX_IDENTICAL_ROUNDS,
817
+ DEFAULT_MAX_CONSECUTIVE_IDENTICAL_ROUNDS
818
+ );
819
+ const maxConsecutiveStagnantToolRounds = parsePositiveInt(
820
+ options?.maxConsecutiveStagnantToolRounds ?? env.NOLO_LOOP_MAX_STAGNANT_TOOL_ROUNDS,
821
+ DEFAULT_MAX_CONSECUTIVE_STAGNANT_TOOL_ROUNDS
822
+ );
823
+ return {
824
+ disabled,
825
+ maxConsecutiveIdenticalRounds,
826
+ maxConsecutiveStagnantToolRounds
827
+ };
828
+ }
829
+ var LocalLoopProgressGuard = class {
830
+ config;
831
+ lastAssistantFingerprint = null;
832
+ lastAssistantHasNonEmptyText = false;
833
+ consecutiveIdenticalCount = 0;
834
+ lastToolCallsSignature = null;
835
+ lastToolResultsSignature = null;
836
+ consecutiveStagnantToolCount = 0;
837
+ constructor(config, env) {
838
+ this.config = resolveProgressGuardConfig(config, env);
839
+ }
840
+ /**
841
+ * 在 LLM 返回 assistant 响应后调用。
842
+ * 仅在纯文本/无工具调用且实质文本相同达到阈值时直接熔断;
843
+ * 若存在工具调用,则不在此处熔断,留待工具执行后结合工具结果综合判定。
844
+ */
845
+ observeAssistantResponse(result) {
846
+ if (this.config.disabled) return { action: "continue" };
847
+ const { fingerprint, hasNonEmptyText, hasToolCalls } = buildAssistantActionFingerprint(result);
848
+ if (this.lastAssistantFingerprint !== null && this.lastAssistantFingerprint === fingerprint) {
849
+ this.consecutiveIdenticalCount += 1;
850
+ if (!hasToolCalls && hasNonEmptyText && this.consecutiveIdenticalCount >= this.config.maxConsecutiveIdenticalRounds) {
851
+ return {
852
+ action: "stall",
853
+ reason: "repetition_loop",
854
+ detail: `Consecutive identical text-only assistant response for ${this.consecutiveIdenticalCount} rounds.`,
855
+ consecutiveRounds: this.consecutiveIdenticalCount
856
+ };
857
+ }
858
+ } else {
859
+ this.lastAssistantFingerprint = fingerprint;
860
+ this.lastAssistantHasNonEmptyText = hasNonEmptyText;
861
+ this.consecutiveIdenticalCount = 1;
862
+ }
863
+ return { action: "continue" };
864
+ }
865
+ /**
866
+ * 在本轮所有工具执行完毕后调用,检查是否陷入停滞死循环。
867
+ * 只有当工具返回内容与元数据完全无变化时才计入无进展 streak;
868
+ * 一旦工具结果发生变化(如合法轮询状态推进),立即重置 identical streak,确保绝不误杀正常长任务。
869
+ */
870
+ observeToolExecution(toolCalls, toolResults) {
871
+ if (this.config.disabled) return { action: "continue" };
872
+ if (!toolCalls || toolCalls.length === 0) return { action: "continue" };
873
+ const toolCallsSig = buildToolCallsSignature(toolCalls);
874
+ const toolResultsSig = buildToolResultsSignature(toolResults);
875
+ const toolCallsUnchanged = this.lastToolCallsSignature !== null && this.lastToolCallsSignature === toolCallsSig;
876
+ const toolResultsUnchanged = this.lastToolResultsSignature !== null && this.lastToolResultsSignature === toolResultsSig;
877
+ if (toolCallsUnchanged && toolResultsUnchanged) {
878
+ this.consecutiveStagnantToolCount += 1;
879
+ if (this.consecutiveIdenticalCount >= this.config.maxConsecutiveIdenticalRounds) {
880
+ return {
881
+ action: "stall",
882
+ reason: "repetition_loop",
883
+ detail: `Consecutive identical assistant response with unchanged tool results for ${this.consecutiveIdenticalCount} rounds.`,
884
+ consecutiveRounds: this.consecutiveIdenticalCount
885
+ };
886
+ }
887
+ if (this.consecutiveStagnantToolCount >= this.config.maxConsecutiveStagnantToolRounds) {
888
+ return {
889
+ action: "stall",
890
+ reason: "stagnant_tool_calls",
891
+ detail: `Consecutive stagnant tool calls with unchanged results for ${this.consecutiveStagnantToolCount} rounds.`,
892
+ consecutiveRounds: this.consecutiveStagnantToolCount
893
+ };
894
+ }
895
+ } else {
896
+ this.lastToolCallsSignature = toolCallsSig;
897
+ this.lastToolResultsSignature = toolResultsSig;
898
+ this.consecutiveStagnantToolCount = 1;
899
+ if (!toolResultsUnchanged) {
900
+ this.consecutiveIdenticalCount = 1;
901
+ }
902
+ }
903
+ return { action: "continue" };
904
+ }
905
+ };
906
+ function createLocalLoopProgressGuard(config, env) {
907
+ return new LocalLoopProgressGuard(config, env);
908
+ }
909
+
725
910
  // packages/agent-runtime/localLoop.ts
726
911
  var LOCAL_AGENT_CONFIG_MISSING_CODE = "LOCAL_AGENT_CONFIG_MISSING";
727
912
  function formatToolExecutionError(args) {
@@ -1490,6 +1675,7 @@ async function runLocalAgentTurn(input) {
1490
1675
  let reasoningEmptyRepairCount = 0;
1491
1676
  let skipFinalAppend = false;
1492
1677
  let partialContent = "";
1678
+ const progressGuard = createLocalLoopProgressGuard(input.progressGuardConfig);
1493
1679
  try {
1494
1680
  const provider = await resolveProviderOnce();
1495
1681
  while (true) {
@@ -1549,6 +1735,23 @@ async function runLocalAgentTurn(input) {
1549
1735
  ...result.provider || agentConfig.provider ? { provider: result.provider || agentConfig.provider } : {}
1550
1736
  });
1551
1737
  }
1738
+ const assistantGuardVerdict = progressGuard.observeAssistantResponse(result);
1739
+ if (assistantGuardVerdict.action === "stall") {
1740
+ emitLoopEvent(input, {
1741
+ kind: "loop-stalled",
1742
+ round,
1743
+ reason: assistantGuardVerdict.reason,
1744
+ detail: assistantGuardVerdict.detail,
1745
+ consecutiveRounds: assistantGuardVerdict.consecutiveRounds,
1746
+ atMs: Date.now()
1747
+ });
1748
+ result = {
1749
+ ...result,
1750
+ content: resolveEmptyAssistantFallbackMessage(assistantGuardVerdict.reason),
1751
+ emptyAssistantFallbackReason: assistantGuardVerdict.reason
1752
+ };
1753
+ break;
1754
+ }
1552
1755
  const toolCalls = result.tool_calls ?? [];
1553
1756
  const rawToolCallsCount = (result.tool_calls?.length ?? 0) || (Array.isArray(result.raw_tool_calls) ? result.raw_tool_calls.length : 0);
1554
1757
  if (toolCalls.length === 0 && rawToolCallsCount === 0) {
@@ -1618,6 +1821,7 @@ ${reasoningTailLog}
1618
1821
  ...result.reasoning_content ? { reasoning_content: result.reasoning_content } : {},
1619
1822
  tool_calls: toolCalls
1620
1823
  });
1824
+ const executedToolResults = [];
1621
1825
  for (const toolCall of toolCalls) {
1622
1826
  throwIfAborted(input);
1623
1827
  const toolName = toolCall.function.name;
@@ -1730,6 +1934,11 @@ ${reasoningTailLog}
1730
1934
  }
1731
1935
  };
1732
1936
  }
1937
+ executedToolResults.push({
1938
+ toolName,
1939
+ content: toolResult.content,
1940
+ metadata: toolResult.metadata
1941
+ });
1733
1942
  messages.push({
1734
1943
  role: "tool",
1735
1944
  content: formatToolMessageContent({
@@ -1742,6 +1951,26 @@ ${reasoningTailLog}
1742
1951
  ...toolResult.metadata ? { tool_result_metadata: toolResult.metadata } : {}
1743
1952
  });
1744
1953
  }
1954
+ const toolExecutionGuardVerdict = progressGuard.observeToolExecution(
1955
+ toolCalls,
1956
+ executedToolResults
1957
+ );
1958
+ if (toolExecutionGuardVerdict.action === "stall") {
1959
+ emitLoopEvent(input, {
1960
+ kind: "loop-stalled",
1961
+ round,
1962
+ reason: toolExecutionGuardVerdict.reason,
1963
+ detail: toolExecutionGuardVerdict.detail,
1964
+ consecutiveRounds: toolExecutionGuardVerdict.consecutiveRounds,
1965
+ atMs: Date.now()
1966
+ });
1967
+ result = {
1968
+ ...result,
1969
+ content: resolveEmptyAssistantFallbackMessage(toolExecutionGuardVerdict.reason),
1970
+ emptyAssistantFallbackReason: toolExecutionGuardVerdict.reason
1971
+ };
1972
+ break;
1973
+ }
1745
1974
  round += 1;
1746
1975
  }
1747
1976
  } catch (error) {
@@ -3379,27 +3608,116 @@ function spawnToWebStreams(options) {
3379
3608
  };
3380
3609
  }
3381
3610
 
3611
+ // packages/agent-runtime/processTask.ts
3612
+ var ProcessTaskEventLog = class {
3613
+ events = /* @__PURE__ */ new Map();
3614
+ /** Append one event; assigns the next monotonic seq for the task. */
3615
+ append(input) {
3616
+ const list = this.events.get(input.taskId) ?? [];
3617
+ const lastSeq = list.length > 0 ? list[list.length - 1].seq : 0;
3618
+ const event = Object.freeze({
3619
+ taskId: input.taskId,
3620
+ seq: lastSeq + 1,
3621
+ type: input.type,
3622
+ pid: input.pid,
3623
+ createdAt: Date.now(),
3624
+ ...input.exitCode !== void 0 ? { exitCode: input.exitCode } : {}
3625
+ });
3626
+ list.push(event);
3627
+ this.events.set(input.taskId, list);
3628
+ return { ...event };
3629
+ }
3630
+ /** All events of the task with `seq > cursor` (default 0 = from the start). */
3631
+ read(taskId, cursor = 0) {
3632
+ return (this.events.get(taskId) ?? []).filter((event) => event.seq > cursor).map((event) => ({ ...event }));
3633
+ }
3634
+ latestSeq(taskId) {
3635
+ const list = this.events.get(taskId);
3636
+ return list && list.length > 0 ? list[list.length - 1].seq : 0;
3637
+ }
3638
+ clear() {
3639
+ this.events.clear();
3640
+ }
3641
+ };
3642
+
3382
3643
  // packages/agent-runtime/processRegistry.ts
3644
+ var taskIdCounter = 0;
3645
+ function mintTaskId(pid) {
3646
+ taskIdCounter += 1;
3647
+ return `ptask-${pid}-${Date.now().toString(36)}-${taskIdCounter.toString(36)}`;
3648
+ }
3383
3649
  var ProcessRegistry = class {
3384
3650
  processes = /* @__PURE__ */ new Map();
3651
+ byTaskId = /* @__PURE__ */ new Map();
3652
+ eventLog = new ProcessTaskEventLog();
3653
+ /**
3654
+ * Pre-register an envelope right after a successful spawn and emit the
3655
+ * `started` event. Returns the stored record (with its stable taskId).
3656
+ */
3385
3657
  add(proc) {
3386
- this.processes.set(proc.pid, {
3658
+ const record = {
3659
+ taskId: proc.taskId ?? mintTaskId(proc.pid),
3387
3660
  pid: proc.pid,
3388
3661
  pgid: proc.pgid,
3389
3662
  command: proc.command,
3390
3663
  label: proc.label,
3391
3664
  startedAt: Date.now(),
3392
3665
  status: "running",
3393
- persist: proc.persist ?? false
3394
- });
3666
+ persist: proc.persist ?? false,
3667
+ promoted: false,
3668
+ transient: proc.transient ?? false
3669
+ };
3670
+ this.processes.set(proc.pid, record);
3671
+ this.byTaskId.set(record.taskId, proc.pid);
3672
+ this.eventLog.append({ taskId: record.taskId, pid: proc.pid, type: "started" });
3673
+ return { ...record };
3395
3674
  }
3396
3675
  list() {
3397
3676
  return Array.from(this.processes.values()).map((proc) => ({ ...proc }));
3398
3677
  }
3678
+ /**
3679
+ * User-visible view: only real background tasks — entries registered by
3680
+ * launchProcess (background from birth) plus envelopes promoted to
3681
+ * background by timeout detach. Transient foreground grace-period envelopes
3682
+ * (workspaceShell pre-registration, not yet promoted) are excluded so the
3683
+ * status line, /procs and /stop keep behaving exactly as before Phase 0.
3684
+ * Audit/internal consumers that need the full truth use list() instead.
3685
+ */
3686
+ listBackground() {
3687
+ return Array.from(this.processes.values()).filter((proc) => !proc.transient).map((proc) => ({ ...proc }));
3688
+ }
3399
3689
  get(pid) {
3400
3690
  const item = this.processes.get(pid);
3401
3691
  return item ? { ...item } : void 0;
3402
3692
  }
3693
+ getTaskId(pid) {
3694
+ return this.processes.get(pid)?.taskId;
3695
+ }
3696
+ getByTaskId(taskId) {
3697
+ const pid = this.byTaskId.get(taskId);
3698
+ return pid === void 0 ? void 0 : this.get(pid);
3699
+ }
3700
+ /** Append-only lifecycle events of a task with `seq > cursor` (ascending). */
3701
+ getTaskEvents(taskId, cursor = 0) {
3702
+ return this.eventLog.read(taskId, cursor);
3703
+ }
3704
+ latestTaskSeq(taskId) {
3705
+ return this.eventLog.latestSeq(taskId);
3706
+ }
3707
+ /**
3708
+ * Timeout-detach promotion: mark the pre-registered envelope as a background
3709
+ * task. Same record, same taskId, no re-execution, no second registration.
3710
+ */
3711
+ promote(pid) {
3712
+ const item = this.processes.get(pid);
3713
+ if (!item) return void 0;
3714
+ if (!item.promoted) {
3715
+ item.promoted = true;
3716
+ item.transient = false;
3717
+ this.eventLog.append({ taskId: item.taskId, pid, type: "promoted" });
3718
+ }
3719
+ return { ...item };
3720
+ }
3403
3721
  kill(pid, signal = "SIGTERM") {
3404
3722
  const item = this.processes.get(pid);
3405
3723
  if (!item) return false;
@@ -3409,18 +3727,21 @@ var ProcessRegistry = class {
3409
3727
  } catch {
3410
3728
  }
3411
3729
  item.status = "stopped";
3730
+ this.eventLog.append({ taskId: item.taskId, pid, type: "killed" });
3412
3731
  return true;
3413
3732
  }
3414
3733
  return false;
3415
3734
  }
3416
3735
  stopAll(signal = "SIGTERM", opts) {
3417
3736
  for (const item of this.processes.values()) {
3737
+ if (opts?.backgroundOnly && item.transient) continue;
3418
3738
  if (item.status === "running" && (opts?.includePersist || !item.persist)) {
3419
3739
  try {
3420
3740
  process.kill(-item.pgid, signal);
3421
3741
  } catch {
3422
3742
  }
3423
3743
  item.status = "stopped";
3744
+ this.eventLog.append({ taskId: item.taskId, pid: item.pid, type: "killed" });
3424
3745
  }
3425
3746
  }
3426
3747
  }
@@ -3429,10 +3750,40 @@ var ProcessRegistry = class {
3429
3750
  if (item && item.status === "running") {
3430
3751
  item.exitCode = exitCode;
3431
3752
  item.status = exitCode === 0 ? "exited" : "failed";
3753
+ this.eventLog.append({
3754
+ taskId: item.taskId,
3755
+ pid,
3756
+ type: "exited",
3757
+ exitCode
3758
+ });
3432
3759
  }
3433
3760
  }
3761
+ /**
3762
+ * Grace-period completion: the command ended before detach promotion, so the
3763
+ * envelope is transient by design — record the final event, then drop the
3764
+ * envelope entirely ("不留痕是结果"). The event stream keeps the audit trail.
3765
+ * No-op for already-promoted envelopes (their lifecycle is owned by
3766
+ * markExited after detach).
3767
+ */
3768
+ completeTransient(pid, exitCode) {
3769
+ const item = this.processes.get(pid);
3770
+ if (!item || item.promoted) return;
3771
+ this.eventLog.append({ taskId: item.taskId, pid, type: "exited", exitCode });
3772
+ this.processes.delete(pid);
3773
+ this.byTaskId.delete(item.taskId);
3774
+ }
3775
+ /** Drop an envelope without emitting events (spawn/registration failure paths). */
3776
+ remove(pid) {
3777
+ const item = this.processes.get(pid);
3778
+ if (!item) return false;
3779
+ this.processes.delete(pid);
3780
+ this.byTaskId.delete(item.taskId);
3781
+ return true;
3782
+ }
3434
3783
  clear() {
3435
3784
  this.processes.clear();
3785
+ this.byTaskId.clear();
3786
+ this.eventLog.clear();
3436
3787
  }
3437
3788
  };
3438
3789
  var registry = null;
@@ -3780,6 +4131,21 @@ async function runWorkspaceCommand(args) {
3780
4131
  proc.stdin.write(args.stdin);
3781
4132
  proc.stdin.end();
3782
4133
  }
4134
+ const label = deriveLabel(args.command.join(" "));
4135
+ const registry2 = getProcessRegistry();
4136
+ if (typeof proc.pid === "number") {
4137
+ registry2.add({
4138
+ pid: proc.pid,
4139
+ pgid: proc.pid,
4140
+ command: args.command.join(" "),
4141
+ label,
4142
+ // Foreground grace-period envelope: not a user-visible background task
4143
+ // until promote() flips the marker on timeout detach (see
4144
+ // listBackground). Keeps the status line / /procs / /stop semantics
4145
+ // identical to pre-Phase-0.
4146
+ transient: true
4147
+ });
4148
+ }
3783
4149
  const exitPromise = waitForNodeProcessExit(proc);
3784
4150
  const cleanupChildOnHostSignal = (signal) => {
3785
4151
  if (typeof proc.pid === "number") {
@@ -3856,6 +4222,9 @@ async function runWorkspaceCommand(args) {
3856
4222
  if (abortListener && args.abortSignal) {
3857
4223
  args.abortSignal.removeEventListener("abort", abortListener);
3858
4224
  }
4225
+ if (typeof proc.pid === "number") {
4226
+ registry2.remove(proc.pid);
4227
+ }
3859
4228
  if (isSpawnFailureError(error)) {
3860
4229
  return spawnFailedCommandResult(error, args.outputLimit);
3861
4230
  }
@@ -3885,10 +4254,10 @@ async function runWorkspaceCommand(args) {
3885
4254
  exitPromise.catch(() => {
3886
4255
  });
3887
4256
  const pid = typeof proc.pid === "number" ? proc.pid : 0;
3888
- const label = deriveLabel(args.command.join(" "));
3889
4257
  const pgid = pid;
3890
- const registry2 = getProcessRegistry();
3891
- registry2.add({ pid, pgid, command: args.command.join(" "), label });
4258
+ const promoted = registry2.promote(pid);
4259
+ const envelope = promoted ?? registry2.add({ pid, pgid, command: args.command.join(" "), label });
4260
+ const taskId = envelope.taskId;
3892
4261
  proc.on("close", (code) => {
3893
4262
  detachSignalCleanup();
3894
4263
  registry2.markExited(pid, code ?? 1);
@@ -3904,10 +4273,12 @@ Use listProcesses to inspect or stop it.`,
3904
4273
  detached: true,
3905
4274
  pid,
3906
4275
  label,
4276
+ taskId,
3907
4277
  content: JSON.stringify({
3908
4278
  detached: true,
3909
4279
  pid,
3910
4280
  label,
4281
+ taskId,
3911
4282
  status: "running",
3912
4283
  ...immediate ? { reason: "long-running-command" } : {}
3913
4284
  })
@@ -3918,6 +4289,9 @@ Use listProcesses to inspect or stop it.`,
3918
4289
  stderrPromise
3919
4290
  ]);
3920
4291
  const exitCode = aborted ? 130 : timedOut ? 124 : Number(raceWinner);
4292
+ if (typeof proc.pid === "number") {
4293
+ registry2.completeTransient(proc.pid, exitCode);
4294
+ }
3921
4295
  const stderr = aborted ? `${rawStderr.trim() ? `${rawStderr.trim()}
3922
4296
  ` : ""}command aborted by signal
3923
4297
  ` : timedOut ? `${rawStderr.trim() ? `${rawStderr.trim()}
@@ -4054,7 +4428,15 @@ var execShellCapability = {
4054
4428
  exitCode: result.exitCode,
4055
4429
  timedOut: result.timedOut,
4056
4430
  ...result.aborted ? { aborted: true } : {},
4057
- ...result.detached ? { detached: true, pid: result.pid, label: result.label, status: "running" } : {},
4431
+ ...result.detached ? {
4432
+ detached: true,
4433
+ pid: result.pid,
4434
+ label: result.label,
4435
+ // Phase 0: stable taskId from the pre-registered Execution
4436
+ // Envelope (additive; existing detached fields untouched).
4437
+ taskId: result.taskId,
4438
+ status: "running"
4439
+ } : {},
4058
4440
  ...normalized.activity ? { activity: normalized.activity } : {}
4059
4441
  }
4060
4442
  };
@@ -5666,7 +6048,7 @@ async function launchProcessTool(args) {
5666
6048
  }
5667
6049
  const pgid = detached ? pid : pid;
5668
6050
  const registry2 = getProcessRegistry();
5669
- registry2.add({ pid, pgid, command, label, persist });
6051
+ const envelope = registry2.add({ pid, pgid, command, label, persist });
5670
6052
  const cleanupChildOnHostSignal = (signal) => {
5671
6053
  try {
5672
6054
  if (detached) {
@@ -5691,7 +6073,7 @@ async function launchProcessTool(args) {
5691
6073
  registry2.markExited(pid, code ?? 1);
5692
6074
  });
5693
6075
  const activity = extractActivity(parsed);
5694
- const resultData = { pid, label, status: "running" };
6076
+ const resultData = { pid, label, taskId: envelope.taskId, status: "running" };
5695
6077
  return {
5696
6078
  content: JSON.stringify(resultData),
5697
6079
  metadata: {
@@ -5699,6 +6081,7 @@ async function launchProcessTool(args) {
5699
6081
  pid,
5700
6082
  label,
5701
6083
  status: "running",
6084
+ taskId: envelope.taskId,
5702
6085
  processLaunch: resultData,
5703
6086
  ...activity ? { activity } : {}
5704
6087
  }
@@ -5903,10 +6286,10 @@ function buildLoadSkillExecutor(args) {
5903
6286
  metadata: { loadSkill: true, resolved: false }
5904
6287
  };
5905
6288
  }
5906
- const { resolveSkillByName } = await import("./skillDiscovery-GPUDHDTZ.js");
6289
+ const { resolveSkillByName } = await import("./skillDiscovery-COX56OBO.js");
5907
6290
  const resolved = resolveSkillByName(args.cwd, name);
5908
6291
  if (!resolved) {
5909
- const { resolveCodingBuiltinSlug, buildCodingSkillContentBySlug } = await import("./codingSkills-6F4EQFCM.js");
6292
+ const { resolveCodingBuiltinSlug, buildCodingSkillContentBySlug } = await import("./codingSkills-H6N5EKSX.js");
5910
6293
  const builtinSlug = resolveCodingBuiltinSlug(name);
5911
6294
  if (builtinSlug) {
5912
6295
  const body = buildCodingSkillContentBySlug(builtinSlug);
@@ -5941,7 +6324,7 @@ ${body}`,
5941
6324
  };
5942
6325
  }
5943
6326
  async function formatUnknownSkillMessage(cwd, requestedName) {
5944
- const { discoverSkills } = await import("./skillDiscovery-GPUDHDTZ.js");
6327
+ const { discoverSkills } = await import("./skillDiscovery-COX56OBO.js");
5945
6328
  let available = [];
5946
6329
  try {
5947
6330
  available = discoverSkills(cwd).map((s) => s.name);
@@ -20,7 +20,7 @@ import {
20
20
  parseAgentRunEvent,
21
21
  t,
22
22
  toolLabel
23
- } from "./chunk-RKRXAL3V.js";
23
+ } from "./chunk-7MWPUOKI.js";
24
24
  import {
25
25
  getReadableCliDb
26
26
  } from "./chunk-UNHKVD2N.js";
@@ -51,7 +51,7 @@ import {
51
51
  } from "./chunk-LTRXGFMS.js";
52
52
  import {
53
53
  formatAgentListCard
54
- } from "./chunk-5GQ6XCJY.js";
54
+ } from "./chunk-JK7UM3LT.js";
55
55
  import {
56
56
  isGatewayHttpStatus
57
57
  } from "./chunk-RQSOAMPJ.js";
@@ -70,28 +70,28 @@ import {
70
70
  chooseMemoryOwners,
71
71
  loadMemoryCandidatesFromDb,
72
72
  resolveAgentMemoryPolicy
73
- } from "./chunk-Y5W623XU.js";
73
+ } from "./chunk-NT3QRCVS.js";
74
74
  import {
75
75
  API_REPORTED_COST_MULTIPLIER,
76
76
  normalizeUsage,
77
77
  parseUiAskChoiceContent
78
- } from "./chunk-PILJX4DH.js";
78
+ } from "./chunk-ARFXKWJH.js";
79
79
  import {
80
80
  getDefaultCliLocalRuntimeDb
81
81
  } from "./chunk-N4XRDM26.js";
82
82
  import {
83
83
  buildSkillDiscoveryContextLayer
84
- } from "./chunk-2BWU4ORW.js";
84
+ } from "./chunk-OFZPJ3NT.js";
85
85
  import {
86
86
  parseExternalSkillMarkdown,
87
87
  parseSkillDocProtocol
88
- } from "./chunk-EY7V7REW.js";
88
+ } from "./chunk-AETZ7AD6.js";
89
89
  import {
90
90
  LOCAL_AGENT_CONFIG_MISSING_CODE,
91
91
  LOCAL_TURN_ABORTED_CODE,
92
92
  readActionGate,
93
93
  readCommandActionGatePayload
94
- } from "./chunk-I7VDDN45.js";
94
+ } from "./chunk-LBZHTQTD.js";
95
95
  import {
96
96
  resolveAgentRuntimeConfigFromRecord
97
97
  } from "./chunk-64KIQZA2.js";
@@ -5359,7 +5359,7 @@ function findCollapsedPasteSpanAt(buffer, pos, opts = {}) {
5359
5359
  // packages/cli/client/agentRun.ts
5360
5360
  import { ulid } from "ulid";
5361
5361
  async function loadRunLocalAgentTurn() {
5362
- const { runLocalAgentTurn } = await import("./agentRuntimeLocal-XXBMDMBU.js");
5362
+ const { runLocalAgentTurn } = await import("./agentRuntimeLocal-GTNYV7LH.js");
5363
5363
  return runLocalAgentTurn;
5364
5364
  }
5365
5365
  async function resolveCurrentMachineId(options) {
@@ -7149,9 +7149,9 @@ async function runAgentRunCommand(args, deps) {
7149
7149
  }
7150
7150
  }
7151
7151
  if (typeof childRunId === "string" && childRunId.length > 0) {
7152
- const isTruncated = result.emptyAssistantFallbackReason === "length_truncated" || result.emptyAssistantFallbackReason === "stream_truncated";
7153
- const truncationNote = isTruncated ? { note: `empty assistant output: ${result.emptyAssistantFallbackReason}` } : {};
7154
- const finalStatus = isTruncated || result.exitCode !== 0 ? "failed" : "done";
7152
+ const isStalledOrTruncated = result.emptyAssistantFallbackReason === "length_truncated" || result.emptyAssistantFallbackReason === "stream_truncated" || result.emptyAssistantFallbackReason === "repetition_loop" || result.emptyAssistantFallbackReason === "stagnant_tool_calls";
7153
+ const truncationNote = isStalledOrTruncated ? { note: `empty assistant output: ${result.emptyAssistantFallbackReason}` } : {};
7154
+ const finalStatus = isStalledOrTruncated || result.exitCode !== 0 ? "failed" : "done";
7155
7155
  await (deps.finalizeRunRecord ?? finalizeRunRecord)(
7156
7156
  childRunId,
7157
7157
  {
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  addContentToSpace
3
- } from "./chunk-QI3CHZUT.js";
3
+ } from "./chunk-D72M3G32.js";
4
4
  import {
5
5
  prepareAndPersistMessage,
6
6
  uiAskChoiceFunc,
7
7
  uiAskChoiceFunctionSchema
8
- } from "./chunk-PILJX4DH.js";
8
+ } from "./chunk-ARFXKWJH.js";
9
9
  import {
10
10
  require_date_fns
11
11
  } from "./chunk-M76ECQRD.js";
@@ -14,7 +14,7 @@ import {
14
14
  } from "./chunk-IIOHYLIG.js";
15
15
  import {
16
16
  buildSkillDocMarkdown
17
- } from "./chunk-EY7V7REW.js";
17
+ } from "./chunk-AETZ7AD6.js";
18
18
  import {
19
19
  PLATFORM_HOSTED_DEEPSEEK_FLASH_MODEL,
20
20
  PLATFORM_HOSTED_DEEPSEEK_FLASH_PEAK_PRICE,