visionclaw 0.1.219-dev.codex-issue-2805-worker-runtime.3 → 0.1.219-dev.codex-2941-task-board-on-main.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 (144) hide show
  1. package/dist/agent/browser-launcher.d.ts +19 -1
  2. package/dist/agent/browser-launcher.d.ts.map +1 -1
  3. package/dist/agent/browser-launcher.js +40 -0
  4. package/dist/agent/browser-launcher.js.map +1 -1
  5. package/dist/agent/command-handlers.js +2 -2
  6. package/dist/agent/command-handlers.js.map +1 -1
  7. package/dist/agent/engines/claude/session.d.ts +6 -0
  8. package/dist/agent/engines/claude/session.d.ts.map +1 -1
  9. package/dist/agent/engines/claude/session.js +34 -2
  10. package/dist/agent/engines/claude/session.js.map +1 -1
  11. package/dist/agent/engines/pi/tools.d.ts.map +1 -1
  12. package/dist/agent/engines/pi/tools.js +2 -1
  13. package/dist/agent/engines/pi/tools.js.map +1 -1
  14. package/dist/agent/fund-league-hold.d.ts.map +1 -1
  15. package/dist/agent/fund-league-hold.js +5 -1
  16. package/dist/agent/fund-league-hold.js.map +1 -1
  17. package/dist/agent/interrupt-handler.d.ts.map +1 -1
  18. package/dist/agent/interrupt-handler.js +4 -1
  19. package/dist/agent/interrupt-handler.js.map +1 -1
  20. package/dist/agent/lifecycle-trace.d.ts +3 -2
  21. package/dist/agent/lifecycle-trace.d.ts.map +1 -1
  22. package/dist/agent/lifecycle-trace.js +4 -9
  23. package/dist/agent/lifecycle-trace.js.map +1 -1
  24. package/dist/agent/lifecycle-transitions.d.ts +42 -0
  25. package/dist/agent/lifecycle-transitions.d.ts.map +1 -1
  26. package/dist/agent/lifecycle-transitions.js +62 -6
  27. package/dist/agent/lifecycle-transitions.js.map +1 -1
  28. package/dist/agent/loop.d.ts.map +1 -1
  29. package/dist/agent/loop.js +99 -27
  30. package/dist/agent/loop.js.map +1 -1
  31. package/dist/agent/public-activity.d.ts +1 -1
  32. package/dist/agent/public-activity.d.ts.map +1 -1
  33. package/dist/agent/public-activity.js.map +1 -1
  34. package/dist/agent/runtime-context.d.ts +2 -0
  35. package/dist/agent/runtime-context.d.ts.map +1 -1
  36. package/dist/agent/runtime-context.js +4 -0
  37. package/dist/agent/runtime-context.js.map +1 -1
  38. package/dist/agent/service-gate.d.ts.map +1 -1
  39. package/dist/agent/service-gate.js +4 -1
  40. package/dist/agent/service-gate.js.map +1 -1
  41. package/dist/agent/state.d.ts +14 -0
  42. package/dist/agent/state.d.ts.map +1 -1
  43. package/dist/agent/state.js +13 -5
  44. package/dist/agent/state.js.map +1 -1
  45. package/dist/agent/status.d.ts.map +1 -1
  46. package/dist/agent/status.js +4 -1
  47. package/dist/agent/status.js.map +1 -1
  48. package/dist/agent/system-prompt.d.ts.map +1 -1
  49. package/dist/agent/system-prompt.js +11 -1
  50. package/dist/agent/system-prompt.js.map +1 -1
  51. package/dist/agent/tool-call-watchdog.d.ts +7 -0
  52. package/dist/agent/tool-call-watchdog.d.ts.map +1 -1
  53. package/dist/agent/tool-call-watchdog.js +32 -2
  54. package/dist/agent/tool-call-watchdog.js.map +1 -1
  55. package/dist/agent/wake-cycle-runner.d.ts +11 -0
  56. package/dist/agent/wake-cycle-runner.d.ts.map +1 -1
  57. package/dist/agent/wake-cycle-runner.js +42 -9
  58. package/dist/agent/wake-cycle-runner.js.map +1 -1
  59. package/dist/channels/interface.d.ts +18 -0
  60. package/dist/channels/interface.d.ts.map +1 -1
  61. package/dist/channels/interface.js.map +1 -1
  62. package/dist/channels/qq-status.d.ts.map +1 -1
  63. package/dist/channels/qq-status.js +1 -0
  64. package/dist/channels/qq-status.js.map +1 -1
  65. package/dist/channels/telegram-status.d.ts +22 -4
  66. package/dist/channels/telegram-status.d.ts.map +1 -1
  67. package/dist/channels/telegram-status.js +216 -39
  68. package/dist/channels/telegram-status.js.map +1 -1
  69. package/dist/channels/telegram-task-board.d.ts +79 -0
  70. package/dist/channels/telegram-task-board.d.ts.map +1 -0
  71. package/dist/channels/telegram-task-board.js +384 -0
  72. package/dist/channels/telegram-task-board.js.map +1 -0
  73. package/dist/channels/telegram.d.ts +6 -1
  74. package/dist/channels/telegram.d.ts.map +1 -1
  75. package/dist/channels/telegram.js +31 -2
  76. package/dist/channels/telegram.js.map +1 -1
  77. package/dist/i18n/messages.d.ts +248 -0
  78. package/dist/i18n/messages.d.ts.map +1 -1
  79. package/dist/i18n/messages.js +79 -13
  80. package/dist/i18n/messages.js.map +1 -1
  81. package/dist/obs/tunnel.d.ts +9 -1
  82. package/dist/obs/tunnel.d.ts.map +1 -1
  83. package/dist/obs/tunnel.js +121 -54
  84. package/dist/obs/tunnel.js.map +1 -1
  85. package/dist/stt-client.d.ts +10 -0
  86. package/dist/stt-client.d.ts.map +1 -1
  87. package/dist/stt-client.js +64 -31
  88. package/dist/stt-client.js.map +1 -1
  89. package/dist/threads/foreground-task.d.ts +10 -2
  90. package/dist/threads/foreground-task.d.ts.map +1 -1
  91. package/dist/threads/foreground-task.js +18 -3
  92. package/dist/threads/foreground-task.js.map +1 -1
  93. package/dist/threads/foreman-host.d.ts +13 -1
  94. package/dist/threads/foreman-host.d.ts.map +1 -1
  95. package/dist/threads/foreman-host.js +79 -7
  96. package/dist/threads/foreman-host.js.map +1 -1
  97. package/dist/threads/foreman-protocol.d.ts +2 -0
  98. package/dist/threads/foreman-protocol.d.ts.map +1 -1
  99. package/dist/threads/foreman-protocol.js +3 -1
  100. package/dist/threads/foreman-protocol.js.map +1 -1
  101. package/dist/threads/process-group.js +7 -2
  102. package/dist/threads/process-group.js.map +1 -1
  103. package/dist/threads/relay-adapter.d.ts +2 -0
  104. package/dist/threads/relay-adapter.d.ts.map +1 -1
  105. package/dist/threads/relay-adapter.js +6 -0
  106. package/dist/threads/relay-adapter.js.map +1 -1
  107. package/dist/threads/task-settlement.d.ts +17 -0
  108. package/dist/threads/task-settlement.d.ts.map +1 -0
  109. package/dist/threads/task-settlement.js +34 -0
  110. package/dist/threads/task-settlement.js.map +1 -0
  111. package/dist/threads/thread-activity-controller.d.ts +21 -0
  112. package/dist/threads/thread-activity-controller.d.ts.map +1 -1
  113. package/dist/threads/thread-activity-controller.js +111 -3
  114. package/dist/threads/thread-activity-controller.js.map +1 -1
  115. package/dist/tools/browser-health.d.ts +2 -0
  116. package/dist/tools/browser-health.d.ts.map +1 -0
  117. package/dist/tools/browser-health.js +18 -0
  118. package/dist/tools/browser-health.js.map +1 -0
  119. package/dist/tools/browser-set-engine.js +1 -1
  120. package/dist/tools/browser-set-engine.js.map +1 -1
  121. package/dist/tools/delegation.d.ts +7 -0
  122. package/dist/tools/delegation.d.ts.map +1 -1
  123. package/dist/tools/delegation.js +30 -3
  124. package/dist/tools/delegation.js.map +1 -1
  125. package/dist/tools/fund-league.d.ts +4 -0
  126. package/dist/tools/fund-league.d.ts.map +1 -1
  127. package/dist/tools/fund-league.js +1 -1
  128. package/dist/tools/fund-league.js.map +1 -1
  129. package/dist/tools/index.d.ts.map +1 -1
  130. package/dist/tools/index.js +2 -0
  131. package/dist/tools/index.js.map +1 -1
  132. package/dist/tools/upgrade.d.ts +2 -1
  133. package/dist/tools/upgrade.d.ts.map +1 -1
  134. package/dist/tools/upgrade.js +26 -8
  135. package/dist/tools/upgrade.js.map +1 -1
  136. package/dist/utils/restart.d.ts +6 -0
  137. package/dist/utils/restart.d.ts.map +1 -1
  138. package/dist/utils/restart.js +7 -1
  139. package/dist/utils/restart.js.map +1 -1
  140. package/dist/utils/version-check.d.ts.map +1 -1
  141. package/dist/utils/version-check.js +19 -9
  142. package/dist/utils/version-check.js.map +1 -1
  143. package/dist-agent/bundle.cjs +2 -2
  144. package/package.json +1 -1
@@ -39,6 +39,7 @@ export type ForegroundPhase = "Idle" | "Running" | "Recovering" | "Stopping";
39
39
  * only after that release.
40
40
  */
41
41
  export declare function endForegroundWake(input: {
42
+ readonly state?: LifecyclePublication;
42
43
  readonly from: Exclude<ForegroundPhase, "Idle" | "Stopping">;
43
44
  readonly cleanup: () => void;
44
45
  readonly release: () => void;
@@ -79,13 +80,54 @@ export declare function nextRecoveryPhase(input: {
79
80
  readonly bucket: RecoveryBucket;
80
81
  readonly spent: number;
81
82
  }): "Recovering" | "Stopping";
83
+ /**
84
+ * The published lifecycle plus whether the host entry is held without an
85
+ * engine receipt. `entryHeld` is not a sixth state: Gated still requires the
86
+ * receipt, and a closed entry without one stays Idle but cannot take work.
87
+ */
88
+ export interface PublishedLifecycle {
89
+ readonly lifecycle: LifecycleState;
90
+ readonly entryHeld: boolean;
91
+ }
92
+ /** What a caller may store. Scheduling and the dashboard read it back. */
93
+ export interface LifecyclePublication {
94
+ lifecycle?: LifecycleState;
95
+ entryHeld?: boolean;
96
+ }
97
+ /**
98
+ * Move the published lifecycle. The transition table is checked against the
99
+ * state already stored, so a caller cannot skip Stopping or enter Gated from
100
+ * a running wake. Staying in the same state is not an edge.
101
+ *
102
+ * `barrierConfirmed` distinguishes Gated from a closed entry. Without the
103
+ * receipt the entry is held and the published state stays Idle.
104
+ */
105
+ export declare function publishLifecycle(target: LifecyclePublication, next: LifecycleState, barrierConfirmed?: boolean): PublishedLifecycle;
106
+ /**
107
+ * Whether the thread loop may start new foreground work.
108
+ *
109
+ * Only a published Idle admits work, and Idle is not enough when the entry
110
+ * is held. Claude never confirms the barrier, so its service gate and
111
+ * fund-league hold stay refused until the entry reopens.
112
+ */
113
+ export declare function canAdmitForegroundWork(state: LifecyclePublication): boolean;
114
+ /**
115
+ * A mid-turn interrupt belongs to the wake that is already Running. Stopping,
116
+ * Recovering, and a closed entry do not drain queued chat into that turn.
117
+ */
118
+ export declare function canDrainRunningTurn(state: LifecyclePublication & {
119
+ busy?: boolean;
120
+ gated?: boolean;
121
+ }): boolean;
82
122
  /**
83
123
  * How a host entry may close. A foreground run must stop before the entry is
84
124
  * marked closed. An idle loop may close directly. Neither path confirms the
85
125
  * engine barrier: that still needs an engine receipt, which this does not have.
86
126
  */
87
127
  export declare function closeHostEntry(input: {
128
+ readonly state: LifecyclePublication;
88
129
  readonly busy: boolean;
130
+ readonly barrierConfirmed?: boolean;
89
131
  readonly requestStop: () => void;
90
132
  readonly markClosed: () => void;
91
133
  }): "Idle" | "Stopping";
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-transitions.d.ts","sourceRoot":"","sources":["../../src/agent/lifecycle-transitions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,oBAAoB,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,SAAS,cAAc,EAAE,CAAC,CAM7F,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,+BAA+B,EAAE,SAAS,CAAC,SAAS,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC,EAMjG,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAExF;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,CAGxF;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,UAAU,CAAC;AAE7E;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,eAAe,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;CACrD,GAAG,eAAe,CAQlB;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB,2HASnB,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAS3E,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAE/E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,GAAG,YAAY,GAAG,UAAU,CAU5B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,IAAI,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,IAAI,CAAC;CACjC,GAAG,MAAM,GAAG,UAAU,CAWtB"}
1
+ {"version":3,"file":"lifecycle-transitions.d.ts","sourceRoot":"","sources":["../../src/agent/lifecycle-transitions.ts"],"names":[],"mappings":"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"}
@@ -43,10 +43,19 @@ export function assertLifecycleTransition(from, to) {
43
43
  * only after that release.
44
44
  */
45
45
  export function endForegroundWake(input) {
46
- assertLifecycleTransition(input.from, "Stopping");
46
+ // A wake the loop already returned to Idle has nothing left to stop.
47
+ // Stopping is only entered from the run that still holds the slot.
48
+ const phase = input.state?.lifecycle;
49
+ if ((phase === "Running" || phase === "Recovering") && input.state)
50
+ publishLifecycle(input.state, "Stopping");
51
+ else if (!input.state)
52
+ assertLifecycleTransition(input.from, "Stopping");
47
53
  input.onPhase?.("Stopping");
48
54
  input.cleanup();
49
- assertLifecycleTransition("Stopping", "Idle");
55
+ if (input.state?.lifecycle === "Stopping")
56
+ publishLifecycle(input.state, "Idle");
57
+ else if (!input.state)
58
+ assertLifecycleTransition("Stopping", "Idle");
50
59
  input.release();
51
60
  input.onPhase?.("Idle");
52
61
  return "Idle";
@@ -112,20 +121,67 @@ export function nextRecoveryPhase(input) {
112
121
  assertLifecycleTransition("Recovering", "Stopping");
113
122
  return "Stopping";
114
123
  }
124
+ /**
125
+ * Move the published lifecycle. The transition table is checked against the
126
+ * state already stored, so a caller cannot skip Stopping or enter Gated from
127
+ * a running wake. Staying in the same state is not an edge.
128
+ *
129
+ * `barrierConfirmed` distinguishes Gated from a closed entry. Without the
130
+ * receipt the entry is held and the published state stays Idle.
131
+ */
132
+ export function publishLifecycle(target, next, barrierConfirmed = false) {
133
+ // Gated without a receipt is not Gated. The legal edge is still checked,
134
+ // then the published state stays Idle with the entry held.
135
+ const published = next === "Gated" && !barrierConfirmed ? "Idle" : next;
136
+ const current = target.lifecycle ?? "Idle";
137
+ if (current !== next)
138
+ assertLifecycleTransition(current, next);
139
+ if (current !== published)
140
+ assertLifecycleTransition(current, published);
141
+ target.lifecycle = published;
142
+ target.entryHeld = next === "Gated" && !barrierConfirmed;
143
+ return { lifecycle: target.lifecycle, entryHeld: target.entryHeld };
144
+ }
145
+ /**
146
+ * Whether the thread loop may start new foreground work.
147
+ *
148
+ * Only a published Idle admits work, and Idle is not enough when the entry
149
+ * is held. Claude never confirms the barrier, so its service gate and
150
+ * fund-league hold stay refused until the entry reopens.
151
+ */
152
+ export function canAdmitForegroundWork(state) {
153
+ return (state.lifecycle ?? "Idle") === "Idle" && !state.entryHeld;
154
+ }
155
+ /**
156
+ * A mid-turn interrupt belongs to the wake that is already Running. Stopping,
157
+ * Recovering, and a closed entry do not drain queued chat into that turn.
158
+ */
159
+ export function canDrainRunningTurn(state) {
160
+ // A state that was never published still answers with the slot it holds.
161
+ // Once published, only Running with an open entry drains a mid-turn batch.
162
+ if (state.lifecycle === undefined)
163
+ return state.busy === true && state.gated !== true;
164
+ return state.lifecycle === "Running" && !state.entryHeld;
165
+ }
115
166
  /**
116
167
  * How a host entry may close. A foreground run must stop before the entry is
117
168
  * marked closed. An idle loop may close directly. Neither path confirms the
118
169
  * engine barrier: that still needs an engine receipt, which this does not have.
119
170
  */
120
171
  export function closeHostEntry(input) {
121
- if (input.busy) {
122
- assertLifecycleTransition("Running", "Stopping");
172
+ const barrierConfirmed = input.barrierConfirmed === true;
173
+ // `busy` still says a run holds the slot after Stopping has been published.
174
+ // Stopping again would be an illegal edge, so only Running or Recovering
175
+ // takes that step. Idle closes directly.
176
+ const running = input.state.lifecycle === "Running" || input.state.lifecycle === "Recovering";
177
+ if (input.busy && running) {
178
+ publishLifecycle(input.state, "Stopping");
123
179
  input.requestStop();
124
- assertLifecycleTransition("Stopping", "Gated");
180
+ publishLifecycle(input.state, "Gated", barrierConfirmed);
125
181
  input.markClosed();
126
182
  return "Stopping";
127
183
  }
128
- assertLifecycleTransition("Idle", "Gated");
184
+ publishLifecycle(input.state, "Gated", barrierConfirmed);
129
185
  input.markClosed();
130
186
  return "Idle";
131
187
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-transitions.js","sourceRoot":"","sources":["../../src/agent/lifecycle-transitions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lifecycle-transitions.js","sourceRoot":"","sources":["../../src/agent/lifecycle-transitions.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,EAA+D,KAAK,gBAAgB,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAG1I,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAsBpD,OAAO,EAAkB,KAAK,UAAU,EAA6G,MAAM,YAAY,CAAC;AA4BxK,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC9B,MAAM,sBAAsB,CAAC;AAK9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAYtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAmChE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAS3C;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,gBAAgB,EACxB,OAAO,CAAC,EAAE;IACR,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B;+DAC2D;IAC3D,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IACpD,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IACpD,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,uBAAuB,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;IACxE,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,UAAU,EAAE,UAAU,CAAC;QAAC,cAAc,EAAE,cAAc,CAAC;QAAC,YAAY,EAAE,YAAY,CAAC;QAAC,WAAW,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3I;;;;OAIG;IACH,UAAU,CAAC,EAAE;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,YAAY,CAAC;QACpB,yEAAyE;QACzE,SAAS,CAAC,EAAE,eAAe,CAAC;KAC7B,CAAC;CACH,GACA,OAAO,CAAC,IAAI,CAAC,CA8lDf"}
1
+ {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,EAA+D,KAAK,gBAAgB,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAG1I,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAuBpD,OAAO,EAAkB,KAAK,UAAU,EAA6G,MAAM,YAAY,CAAC;AA4BxK,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC9B,MAAM,sBAAsB,CAAC;AAK9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAYtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAqChE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAS3C;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,gBAAgB,EACxB,OAAO,CAAC,EAAE;IACR,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B;+DAC2D;IAC3D,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IACpD,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IACpD,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,uBAAuB,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;IACxE,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,UAAU,EAAE,UAAU,CAAC;QAAC,cAAc,EAAE,cAAc,CAAC;QAAC,YAAY,EAAE,YAAY,CAAC;QAAC,WAAW,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3I;;;;OAIG;IACH,UAAU,CAAC,EAAE;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,YAAY,CAAC;QACpB,yEAAyE;QACzE,SAAS,CAAC,EAAE,eAAe,CAAC;KAC7B,CAAC;CACH,GACA,OAAO,CAAC,IAAI,CAAC,CA0pDf"}
@@ -13,9 +13,9 @@
13
13
  * - ChannelManager: messaging channel adapters
14
14
  */
15
15
  import { createDefaultExternalContextRegistry, ExternalContextRegistry } from "./external-context/registry.js";
16
- import { createHash, randomUUID } from "node:crypto";
17
- import { beginLifecycleRun, createLifecycleRunId, deriveShadowLifecycleState, endLifecycleRun, traceLifecycle, traceShadowLifecycle } from "./lifecycle-trace.js";
18
- import { closeHostEntry, endForegroundWake } from "./lifecycle-transitions.js";
16
+ import { randomUUID } from "node:crypto";
17
+ import { beginLifecycleRun, createLifecycleRunId, endLifecycleRun, traceLifecycle, traceShadowLifecycle } from "./lifecycle-trace.js";
18
+ import { canAdmitForegroundWork, closeHostEntry, endForegroundWake, publishLifecycle } from "./lifecycle-transitions.js";
19
19
  import { isProactiveWakeEnabled, isTunnelEnabled, isWorkspaceEnabled } from "../config/types.js";
20
20
  import { DEFAULT_THREAD_ID, normalizeChannelName, resolveThreadId } from "../channels/interface.js";
21
21
  import { buildSystemPrompt } from "./system-prompt.js";
@@ -31,6 +31,7 @@ import { loadOwnerConfig, consumePendingUpgrade, consumePendingRestart, writePen
31
31
  import { syncSystemTimezone } from "../utils/timezone.js";
32
32
  import { logger } from "../logger.js";
33
33
  import { cleanOldScreenshots } from "../tools/screenshot.js";
34
+ import { wakeDispatchId } from "../tools/delegation.js";
34
35
  import { getEffectiveEngine, maybeStartApiProxy } from "./model-provider.js";
35
36
  import { EngineFallbackManager, setActiveEngineFallback } from "./engine-fallback.js";
36
37
  import { cleanupOldTranscripts } from "./session-trimmer.js";
@@ -91,6 +92,8 @@ import { retireLegacyServices } from "../service/retired-services.js";
91
92
  import { backfillMissingThreadApplications, migrateLegacyThreadApplications, threadApplicationBackfillHealthEvent } from "../threads/application-migration.js";
92
93
  import { currentApplicationContext, inheritThreadApplication, threadApplication } from "../threads/application-context.js";
93
94
  import { ForemanHost } from "../threads/foreman-host.js";
95
+ import { onTaskSettled, reportTaskSettled, settledTaskState } from "../threads/task-settlement.js";
96
+ import { bindRuntimeContext } from "./runtime-context.js";
94
97
  import { ForemanStore } from "../threads/foreman-store.js";
95
98
  import { canDelegateTasks, foremanEventsOf, getForemanClient, installForemanClient, isForemanRuntime, supportsTaskRuntime } from "../threads/foreman-protocol.js";
96
99
  import { ForegroundTaskTracker, installForegroundTaskTracker } from "../threads/foreground-task.js";
@@ -190,6 +193,22 @@ export async function startAgentLoop(config, options) {
190
193
  channelManager.publishThreadActivity("default", activity, activity.workerInstanceId, deliveryTarget);
191
194
  },
192
195
  });
196
+ // The progress card waits for handed-off Tasks; it ends when they end,
197
+ // whether or not their result wakes this conversation.
198
+ const finishBackgroundCard = (taskId, state) => {
199
+ if (workerMode)
200
+ workerMode.relay.finishBackgroundActivity(taskId, state);
201
+ else
202
+ mainActivityController?.finishBackgroundTask(taskId, state);
203
+ };
204
+ const stopSettlementListener = onTaskSettled(bindRuntimeContext((threadId, taskId, state) => {
205
+ if (threadId === executionContext.threadId)
206
+ finishBackgroundCard(taskId, state);
207
+ }));
208
+ registerShutdownCallback(() => {
209
+ stopSettlementListener();
210
+ return Promise.resolve();
211
+ });
193
212
  const finAppCapabilityState = {
194
213
  runtimeReady: false,
195
214
  active: null,
@@ -236,6 +255,13 @@ export async function startAgentLoop(config, options) {
236
255
  ?? Promise.resolve({ ok: false, text: "Business reads are unavailable" }),
237
256
  });
238
257
  supervisor.onExecutionEvent((event, caller) => {
258
+ // The board and the progress card are a view: never let them break delivery.
259
+ try {
260
+ foremanHost?.publishTaskState(event, caller);
261
+ }
262
+ catch (error) {
263
+ logger.warn(`Background task state was not published: ${error instanceof Error ? error.message : String(error)}`);
264
+ }
239
265
  if (caller.kind !== "tui")
240
266
  return;
241
267
  channelManager.getAdapter("tui")?.publishExecutionEvent(event);
@@ -272,6 +298,13 @@ export async function startAgentLoop(config, options) {
272
298
  const owner = resolveOwnerDefaultTarget(config, loadOwnerConfig());
273
299
  return owner ? { channel: owner.channel, to: owner.to } : null;
274
300
  },
301
+ publishTask: (target, task) => {
302
+ const adapter = channelManager.getAdapter(target.channel);
303
+ // The board repairs rows whose final update was lost from the ledger.
304
+ adapter?.setBackgroundTaskLookup?.((taskId) => activeForemanHost?.taskView(taskId) ?? null);
305
+ adapter?.publishBackgroundTask?.(target, task);
306
+ },
307
+ settleTask: reportTaskSettled,
275
308
  locale: () => resolveLocale(loadOwnerConfig().language),
276
309
  });
277
310
  const host = foremanHost;
@@ -508,7 +541,11 @@ export async function startAgentLoop(config, options) {
508
541
  activityTracker,
509
542
  busy: false,
510
543
  stopRequested: false,
544
+ lifecycle: "Idle",
545
+ entryHeld: false,
511
546
  requestStop: () => {
547
+ // Stopping is published by the caller that decided to end the wake.
548
+ // This only records the request and asks the engine to stop.
512
549
  agentState.stopRequested = true;
513
550
  requestSessionStop();
514
551
  },
@@ -760,11 +797,11 @@ export async function startAgentLoop(config, options) {
760
797
  wakeMainLoop();
761
798
  return;
762
799
  }
763
- if (agentState.busy) {
800
+ if (!canAdmitForegroundWork(agentState)) {
764
801
  // Correlated internal requests are durable queued work, not chat
765
802
  // interrupts. They must own a fresh turn for precise lifecycle and
766
- // cancellation semantics.
767
- if (typeof msg.meta?.tuiRequestId === "string")
803
+ // cancellation semantics. A closed entry is not a turn to interrupt.
804
+ if (typeof msg.meta?.tuiRequestId === "string" || agentState.entryHeld || agentState.lifecycle === "Gated")
768
805
  return;
769
806
  interruptHandler.schedule();
770
807
  }
@@ -916,7 +953,7 @@ export async function startAgentLoop(config, options) {
916
953
  logger.system("[nightly-maintenance] Dreaming needs the thread supervisor (multiThread); skipped on this profile");
917
954
  nightlyMaintenance = new NightlyMaintenance({
918
955
  tryAcquire: () => {
919
- if (agentState.busy || agentState.learning || agentState.compacting || agentState.gated
956
+ if (!canAdmitForegroundWork(agentState) || agentState.learning || agentState.compacting
920
957
  || !messageQueue.isEmpty() || readFundLeagueHold()
921
958
  || shouldEnforceServiceGate(heartbeat.lastServiceStatus))
922
959
  return null;
@@ -930,6 +967,7 @@ export async function startAgentLoop(config, options) {
930
967
  // Maintenance holds the slot the way a foreground wake does. Release
931
968
  // it through Stopping so workers resume only after the slot is free.
932
969
  endForegroundWake({
970
+ state: agentState,
933
971
  from: "Running",
934
972
  cleanup: () => { agentState.learning = false; },
935
973
  release: () => {
@@ -1014,7 +1052,9 @@ export async function startAgentLoop(config, options) {
1014
1052
  // held stops first; an idle loop closes the entry directly. Neither
1015
1053
  // confirms an engine barrier.
1016
1054
  closeHostEntry({
1055
+ state: agentState,
1017
1056
  busy: agentState.busy,
1057
+ barrierConfirmed: session.executionBarrierConfirmed(),
1018
1058
  requestStop: () => { agentState.requestStop(); },
1019
1059
  markClosed: () => {
1020
1060
  executionOwner.setBusy(false, "league_hold");
@@ -1030,8 +1070,12 @@ export async function startAgentLoop(config, options) {
1030
1070
  // A task handed off in an earlier wake no longer blocks new foreground work.
1031
1071
  foregroundTasks?.beginWake();
1032
1072
  executionOwner.setBusy(false, "wake_reset");
1033
- agentState.gated = false;
1034
1073
  agentState.stopRequested = false;
1074
+ // The hold check above already continued. Reaching here means the entry
1075
+ // may open. A held entry returns through Idle, which is the only way out.
1076
+ if (agentState.lifecycle !== "Idle" || agentState.entryHeld)
1077
+ publishLifecycle(agentState, "Idle");
1078
+ agentState.gated = false;
1035
1079
  agentState.activeTuiRequestId = undefined;
1036
1080
  agentState.hasRepliedInCycle = false;
1037
1081
  agentState.notifiedUserThisWake = false;
@@ -1207,6 +1251,14 @@ export async function startAgentLoop(config, options) {
1207
1251
  if (!workerMode && shouldEnforceServiceGate(heartbeat.lastServiceStatus)) {
1208
1252
  await runServiceGate(config, channelManager, heartbeat, messageQueue);
1209
1253
  }
1254
+ // The five states admit a wake only from Idle, and a closed entry that
1255
+ // still lacks an engine receipt is not Idle enough. Sleep again instead of
1256
+ // taking the slot. Claude never confirms the barrier, so this is what
1257
+ // keeps a closed service gate or fund-league hold from starting work.
1258
+ if (!canAdmitForegroundWork(agentState)) {
1259
+ logger.system(`Foreground wake refused: ${agentState.lifecycle}${agentState.entryHeld ? " (entry held)" : ""}`);
1260
+ continue;
1261
+ }
1210
1262
  // The slot is taken only after the gate. A closed entry therefore does not
1211
1263
  // overlap a foreground run, which would be Running → Gated.
1212
1264
  executionOwner.setBusy(true, "wake", lifecycleRunId);
@@ -1218,19 +1270,12 @@ export async function startAgentLoop(config, options) {
1218
1270
  const hasMessages = !messageQueue.isEmpty();
1219
1271
  const trigger = hasMessages ? "message" : "heartbeat";
1220
1272
  beginLifecycleRun(lifecycleRunId);
1221
- const observeShadow = (phase, recovering = false) => {
1273
+ const observeShadow = (phase) => {
1222
1274
  traceShadowLifecycle({
1223
1275
  runId: lifecycleRunId,
1224
1276
  phase,
1225
- state: deriveShadowLifecycleState({
1226
- gated: agentState.gated,
1227
- stopRequested: agentState.stopRequested,
1228
- busy: agentState.busy,
1229
- recovering,
1230
- // Pi confirms this with `agent_settled`. An abort is not a receipt,
1231
- // so an engine that cannot prove it stopped stays unconfirmed.
1232
- barrierConfirmed: session.executionBarrierConfirmed(),
1233
- }),
1277
+ state: agentState.lifecycle ?? "Idle",
1278
+ entryHeld: agentState.entryHeld,
1234
1279
  legacy: {
1235
1280
  busy: agentState.busy,
1236
1281
  gated: agentState.gated,
@@ -1245,9 +1290,11 @@ export async function startAgentLoop(config, options) {
1245
1290
  // A worker thread is the same loop: its busy flag lives on the
1246
1291
  // supervisor, and it is cleared only after this cleanup.
1247
1292
  endForegroundWake({
1248
- // A failed compaction is still this same foreground run. Phase 3 is
1249
- // what moves that path into Recovering before the wake ends.
1250
- from: "Running",
1293
+ state: agentState,
1294
+ // A failed compaction is still this same foreground run. Recovering
1295
+ // publishes itself before the wake ends, and Stopping is legal from
1296
+ // either Running or Recovering.
1297
+ from: agentState.lifecycle === "Recovering" ? "Recovering" : "Running",
1251
1298
  onPhase: (phase) => {
1252
1299
  if (phase === "Stopping")
1253
1300
  observeShadow("stopping");
@@ -1382,7 +1429,8 @@ export async function startAgentLoop(config, options) {
1382
1429
  // long). Rotate with our bounded summarizer before the turn starts
1383
1430
  // so this wake runs on a session that fits. The summary and the
1384
1431
  // continuation hint are seeded inline; no extra wake is queued.
1385
- observeShadow("compaction_failed", true);
1432
+ publishLifecycle(agentState, "Recovering");
1433
+ observeShadow("compaction_failed");
1386
1434
  traceLifecycle({
1387
1435
  runId: lifecycleRunId,
1388
1436
  stage: "compaction",
@@ -1486,7 +1534,7 @@ export async function startAgentLoop(config, options) {
1486
1534
  outcome: "started",
1487
1535
  });
1488
1536
  let releaseConversationTurn;
1489
- agentState.currentDispatchId = createHash("sha256").update(JSON.stringify(messages.map((message) => message.id))).digest("hex");
1537
+ agentState.currentDispatchId = wakeDispatchId(messages.map((message) => message.id), lifecycleRunId);
1490
1538
  try {
1491
1539
  if (!workerExecution) {
1492
1540
  releaseConversationTurn = await (options?.acquireConversationTurn?.(options.signal)
@@ -1521,6 +1569,10 @@ export async function startAgentLoop(config, options) {
1521
1569
  // Foreman: the background-task updates this turn handled, read before
1522
1570
  // their claims are released (mid-turn interrupts included).
1523
1571
  const handledForemanEvents = failoverRestartPending ? [] : foremanEventsOf(messageQueue.currentCycleClaims());
1572
+ // Every Task the host accepted this wake (one response can delegate
1573
+ // several, or a batch): the status card waits for all of them.
1574
+ const handedOffIds = [...(foregroundTasks?.handedOffTaskIds() ?? [])];
1575
+ const waitsForBackground = result.finished === true && result.terminalState === "completed" && handedOffIds.length > 0;
1524
1576
  // A completed foreground wake acknowledges its claims. Accepted background
1525
1577
  // work already belongs to the canonical Task ledger.
1526
1578
  if (!failoverRestartPending)
@@ -1536,11 +1588,31 @@ export async function startAgentLoop(config, options) {
1536
1588
  void getForemanClient()?.request({ type: "turn_ended", events: handledForemanEvents });
1537
1589
  }
1538
1590
  }
1539
- if (workerMode) {
1540
- workerMode.relay.finishActivityRun(result.terminalState);
1591
+ if (waitsForBackground) {
1592
+ for (const taskId of handedOffIds) {
1593
+ if (workerMode)
1594
+ workerMode.relay.markActivityWaitingForBackground(taskId);
1595
+ else
1596
+ mainActivityController?.markWaitingForBackground(taskId);
1597
+ }
1598
+ // A quick Task can end before this wake does; its notice came earlier.
1599
+ for (const taskId of handedOffIds) {
1600
+ const settled = settledTaskState(executionContext.threadId, taskId);
1601
+ if (settled)
1602
+ finishBackgroundCard(taskId, settled);
1603
+ }
1541
1604
  }
1542
- else {
1543
- mainActivityController?.finishRun(result.terminalState);
1605
+ for (const event of handledForemanEvents) {
1606
+ if (event.kind !== "result")
1607
+ continue;
1608
+ const state = event.status === "succeeded" ? "completed" : "failed";
1609
+ finishBackgroundCard(event.taskId, state);
1610
+ }
1611
+ if (!waitsForBackground) {
1612
+ if (workerMode)
1613
+ workerMode.relay.finishActivityRun(result.terminalState);
1614
+ else
1615
+ mainActivityController?.finishRun(result.terminalState);
1544
1616
  }
1545
1617
  tuiTerminalOutcome = resolveTuiTerminalOutcome(result.terminalState);
1546
1618
  }