toolroll 0.9.52 → 0.9.53

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 (146) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +55 -80
  3. package/dist/agentconfig.d.ts +11 -36
  4. package/dist/agentconfig.js +3 -62
  5. package/dist/approval-policy.d.ts +1 -1
  6. package/dist/approval-policy.js +1 -1
  7. package/dist/board.d.ts +3 -19
  8. package/dist/board.js +4 -42
  9. package/dist/browser/workspace.js +18 -18
  10. package/dist/browser-workspace.d.ts +2 -1
  11. package/dist/builder.d.ts +9 -40
  12. package/dist/builder.js +50 -257
  13. package/dist/chat-actions.js +3 -14
  14. package/dist/chat-controls.d.ts +0 -4
  15. package/dist/chat-controls.js +0 -1
  16. package/dist/chat-decide.js +1 -3
  17. package/dist/chat-delivery.d.ts +0 -2
  18. package/dist/chat-delivery.js +0 -1
  19. package/dist/chat-quiet.js +2 -2
  20. package/dist/chat-task-actions.d.ts +1 -1
  21. package/dist/chat-task-actions.js +0 -2
  22. package/dist/claim.d.ts +79 -244
  23. package/dist/claim.js +688 -1023
  24. package/dist/cli.d.ts +2 -0
  25. package/dist/cli.js +10 -2
  26. package/dist/contracts/cli.d.ts +0 -13
  27. package/dist/contracts/cli.js +0 -2
  28. package/dist/contracts/console-api.d.ts +5 -10
  29. package/dist/contracts/console-api.js +4 -10
  30. package/dist/contracts/flow.d.ts +50 -0
  31. package/dist/contracts/flow.js +26 -1
  32. package/dist/contracts/lead-tools.d.ts +0 -13
  33. package/dist/contracts/lead-tools.js +3 -3
  34. package/dist/contracts/store-json.d.ts +1 -25
  35. package/dist/contracts/store-json.js +3 -13
  36. package/dist/converse.d.ts +1 -11
  37. package/dist/converse.js +6 -38
  38. package/dist/coordinator.d.ts +0 -1
  39. package/dist/coordinator.js +3 -5
  40. package/dist/demo.js +16 -26
  41. package/dist/desktop-update-gate.js +0 -1
  42. package/dist/desktop-update.js +1 -3
  43. package/dist/dispatch.d.ts +1 -1
  44. package/dist/dispatch.js +1 -1
  45. package/dist/dispose.d.ts +2 -3
  46. package/dist/dispose.js +17 -92
  47. package/dist/envelope.js +0 -3
  48. package/dist/exec.d.ts +0 -64
  49. package/dist/exec.js +3 -356
  50. package/dist/exhaustion.d.ts +22 -76
  51. package/dist/exhaustion.js +14 -140
  52. package/dist/export.js +4 -4
  53. package/dist/flow-engine.js +1 -1
  54. package/dist/flow-schedule.d.ts +154 -0
  55. package/dist/flow-schedule.js +321 -0
  56. package/dist/flow-share.js +1 -1
  57. package/dist/flow-triggers.d.ts +6 -1
  58. package/dist/flow-triggers.js +65 -10
  59. package/dist/flows-cli.js +8 -4
  60. package/dist/flows-ui.js +2 -0
  61. package/dist/flows.js +1 -1
  62. package/dist/guides.js +9 -29
  63. package/dist/invoke.d.ts +1 -24
  64. package/dist/invoke.js +17 -259
  65. package/dist/lead-status.js +2 -4
  66. package/dist/lead-voice.js +1 -1
  67. package/dist/mate-doors.d.ts +1 -2
  68. package/dist/mate-doors.js +12 -25
  69. package/dist/mate-tools.d.ts +2 -2
  70. package/dist/mate-tools.js +10 -22
  71. package/dist/modes.d.ts +6 -7
  72. package/dist/modes.js +7 -13
  73. package/dist/operate-remote-arguments.js +2 -6
  74. package/dist/operate-remote.js +6 -7
  75. package/dist/operate.d.ts +0 -6
  76. package/dist/operate.js +222 -2247
  77. package/dist/phase-routing.d.ts +13 -23
  78. package/dist/phase-routing.js +13 -35
  79. package/dist/plan-auto.js +2 -2
  80. package/dist/planner-source.js +2 -1
  81. package/dist/policy.d.ts +1 -4
  82. package/dist/policy.js +0 -8
  83. package/dist/process-recovery-eligibility.js +2 -3
  84. package/dist/project-delete.js +3 -15
  85. package/dist/proposal.d.ts +4 -24
  86. package/dist/proposal.js +3 -88
  87. package/dist/provider-auth.d.ts +3 -3
  88. package/dist/provider-auth.js +4 -6
  89. package/dist/provider.d.ts +11 -25
  90. package/dist/provider.js +4 -36
  91. package/dist/push.js +1 -1
  92. package/dist/recipe-ui.js +6 -6
  93. package/dist/recipes.d.ts +3 -3
  94. package/dist/recipes.js +27 -23
  95. package/dist/run-closeout.d.ts +1 -1
  96. package/dist/run-closeout.js +1 -4
  97. package/dist/scope.d.ts +11 -65
  98. package/dist/scope.js +17 -82
  99. package/dist/serve.js +15 -24
  100. package/dist/server/chat.js +7 -30
  101. package/dist/server/flows.js +11 -10
  102. package/dist/server/guards.js +0 -1
  103. package/dist/server/pages-assets.js +1 -1
  104. package/dist/server/pages.js +0 -2
  105. package/dist/server/people-tokens.js +0 -4
  106. package/dist/server/route-table.d.ts +1 -1
  107. package/dist/server/route-table.js +2 -17
  108. package/dist/server/settings.js +1 -8
  109. package/dist/server/shared.d.ts +28 -212
  110. package/dist/server/shared.js +71 -780
  111. package/dist/server/task-views.js +9 -76
  112. package/dist/server/tasks.d.ts +1 -50
  113. package/dist/server/tasks.js +36 -946
  114. package/dist/setup-guide.d.ts +2 -2
  115. package/dist/slack-chat.d.ts +0 -2
  116. package/dist/slack-chat.js +0 -1
  117. package/dist/store.d.ts +105 -1340
  118. package/dist/store.js +706 -4010
  119. package/dist/surface.js +10 -24
  120. package/dist/task-composer.d.ts +1 -1
  121. package/dist/task-composer.js +1 -1
  122. package/dist/task-control.d.ts +3 -10
  123. package/dist/task-control.js +2 -7
  124. package/dist/teammate-desk.js +1 -1
  125. package/dist/teammates-ui.js +1 -1
  126. package/dist/telegram-mate.d.ts +0 -3
  127. package/dist/telegram-mate.js +0 -1
  128. package/dist/templates.d.ts +11 -7
  129. package/dist/templates.js +12 -9
  130. package/dist/toolroll-update.d.ts +10 -0
  131. package/dist/toolroll-update.js +56 -4
  132. package/dist/work-index.js +2 -6
  133. package/dist/workspace-ui.js +1 -1
  134. package/package.json +1 -1
  135. package/scripts/recovery-canary.mjs +3 -3
  136. package/dist/contest.d.ts +0 -318
  137. package/dist/contest.js +0 -753
  138. package/dist/held.d.ts +0 -150
  139. package/dist/held.js +0 -799
  140. package/dist/liveness.d.ts +0 -30
  141. package/dist/liveness.js +0 -42
  142. package/dist/pricing.d.ts +0 -45
  143. package/dist/pricing.js +0 -77
  144. package/dist/routine.d.ts +0 -228
  145. package/dist/routine.js +0 -764
  146. package/dist/supervisor.mjs +0 -297
@@ -1,297 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * The held-session supervisor (Parity II Phase 2, spec v5 P4 / v6 W7+W9).
4
- *
5
- * A minimal, zero-dependency parent for one held agent process. Its whole
6
- * value is PARENTHOOD: while this process lives and has not reaped its
7
- * child, POSIX guarantees the child's PID cannot be reused — so a kill
8
- * ordered through this process can never hit an innocent stranger, which
9
- * no PID-plus-timestamp guess can promise. Everything else here is a dumb
10
- * byte pump.
11
- *
12
- * Invocation (by exec.ts only):
13
- * node supervisor.mjs <agent-binary> <agent-args...>
14
- * env: SO_HELD_SOCKET — unix socket path for the control channel
15
- * SO_HELD_COOKIE — 128-bit hex cookie; every socket verb must carry it
16
- * SO_HELD_GRACE_MS — EOF→SIGKILL grace (default 10s)
17
- * SO_HELD_DIAGNOSTIC_FD — private observer diagnostic pipe (optional)
18
- *
19
- * Protocol, in order:
20
- * 1. The control socket is created FIRST (mode 0600), so a fencer can
21
- * always reach a supervisor that got as far as spawning.
22
- * 2. The agent spawns DETACHED into its own fresh process group.
23
- * 3. stdout emits exactly ONE control frame before any relayed agent
24
- * byte: {"so_supervisor":"ready","agentPgid":N,"supervisorPid":N}
25
- * or {"so_supervisor":"spawn-failed","message":...}. Everything after
26
- * the ready frame is the agent's stream, byte for byte.
27
- * 4. stdin bytes relay to the agent verbatim. stdin EOF (the owner ended
28
- * the hold, or died — the pipe broke) starts the AUTONOMOUS FENCE:
29
- * EOF to the agent, grace, then SIGKILL of its whole group. SIGTERM
30
- * and SIGHUP take the same road (the hard-stop sweep's contract).
31
- * 5. Socket verbs, one JSON line per connection, cookie-checked:
32
- * {"cookie":..,"verb":"status"} → {"ok":true,"alive":bool,...}
33
- * {"cookie":..,"verb":"kill"} → group SIGKILL via the held handle,
34
- * answered only after the group is PROVEN gone (ESRCH poll, 5s bound).
35
- * 6. The supervisor exits when the agent has exited and the relay has
36
- * flushed; the socket file is unlinked on the way out.
37
- */
38
-
39
- // The source supervisor is also exercised directly by the TypeScript test
40
- // runner; its shared helper comes from the normal production build there.
41
- const { stopProcessTree, observeProcessTree, sampleProcessTree } = await import(new URL("./process-tree.js", import.meta.url).href).catch(error => {
42
- if (error.code !== "ERR_MODULE_NOT_FOUND") throw error;
43
- return import(new URL("../dist/process-tree.js", import.meta.url).href);
44
- });
45
- import { spawn } from "node:child_process";
46
- import { createServer } from "node:net";
47
- import { chmodSync, unlinkSync, writeSync } from "node:fs";
48
-
49
- const socketPath = process.env["SO_HELD_SOCKET"] ?? "";
50
- const cookie = process.env["SO_HELD_COOKIE"] ?? "";
51
- const graceMs = Math.max(1000, Number(process.env["SO_HELD_GRACE_MS"] ?? 10_000) || 10_000);
52
- const [agentFile, ...agentArgs] = process.argv.slice(2);
53
-
54
- const frame = payload => {
55
- process.stdout.write(`${JSON.stringify(payload)}\n`);
56
- };
57
-
58
- if (socketPath === "" || cookie === "" || agentFile === undefined) {
59
- frame({ so_supervisor: "spawn-failed", message: "missing SO_HELD_SOCKET, SO_HELD_COOKIE, or agent argv" });
60
- process.exit(2);
61
- }
62
-
63
- let exited = false;
64
- let exitCode = null;
65
- let fencing = false;
66
- let server = null;
67
- let killsInFlight = 0;
68
- const descendantPids = new Set();
69
- let treeUnknown = false;
70
- let exitWanted = false;
71
- let drain = null;
72
- let relayClosed = false;
73
- let relayDeadline = null;
74
-
75
- /** The exit road, deferred while a kill verb's reply is still owed: the
76
- * fencer must HEAR that the group is proven gone before the process that
77
- * proved it disappears. */
78
- const maybeExit = () => {
79
- if (!exitWanted || killsInFlight > 0) return;
80
- cleanup();
81
- for (const connection of pending) connection.destroy();
82
- process.exit(exitCode ?? 1);
83
- };
84
-
85
- const cleanup = () => {
86
- try {
87
- server?.close();
88
- } catch {
89
- // Already closed.
90
- }
91
- try {
92
- unlinkSync(socketPath);
93
- } catch {
94
- // Never existed, or already gone.
95
- }
96
- };
97
-
98
- /** True while ANY member of the agent's group survives. */
99
- const groupAlive = pgid => {
100
- try {
101
- process.kill(-pgid, 0);
102
- return true;
103
- } catch (error) {
104
- return error.code !== "ESRCH";
105
- }
106
- };
107
- const descendantsAlive = () => treeUnknown || [...descendantPids].some(pid => {
108
- try { process.kill(pid, 0); return true; } catch (error) { return error.code !== "ESRCH"; }
109
- });
110
- const holdGone = pgid => relayClosed && !groupAlive(pgid) && !descendantsAlive();
111
-
112
- /** All exit roads share one drain. In particular, child.close must not
113
- * outrun an EOF fence that is still waiting for a detached tool to die. */
114
- const killGroupSettled = pgid => drain ??= Promise.resolve().then(async () => {
115
- if (child !== undefined && !exited) stopProcessTree(child);
116
- try {
117
- process.kill(-pgid, "SIGKILL");
118
- } catch {
119
- // Group already gone — proving below still runs.
120
- }
121
- const deadline = Date.now() + 5_000;
122
- while (Date.now() < deadline) {
123
- if (holdGone(pgid)) return true;
124
- await new Promise(pass => setTimeout(pass, 50));
125
- }
126
- return holdGone(pgid);
127
- });
128
-
129
- // The control socket comes up BEFORE the agent spawns.
130
- const pending = new Set();
131
- server = createServer(connection => {
132
- pending.add(connection);
133
- connection.on("close", () => pending.delete(connection));
134
- let buffered = "";
135
- connection.on("data", chunk => {
136
- buffered += String(chunk);
137
- const cut = buffered.indexOf("\n");
138
- if (cut === -1) {
139
- if (buffered.length > 4096) connection.destroy();
140
- return;
141
- }
142
- const line = buffered.slice(0, cut);
143
- buffered = "";
144
- let order = null;
145
- try {
146
- order = JSON.parse(line);
147
- } catch {
148
- connection.destroy();
149
- return;
150
- }
151
- if (order === null || typeof order !== "object" || order.cookie !== cookie) {
152
- // Wrong cookie: one refusal, no detail, connection over.
153
- connection.end(`${JSON.stringify({ ok: false })}\n`);
154
- return;
155
- }
156
- if (order.verb === "status") {
157
- const pgid = child?.pid;
158
- const group = pgid !== undefined && groupAlive(pgid);
159
- connection.end(
160
- `${JSON.stringify({ ok: true, alive: !exited || !relayClosed || group || descendantsAlive(), groupAlive: group, agentPgid: pgid ?? null, exitCode })}\n`,
161
- );
162
- return;
163
- }
164
- if (order.verb === "kill") {
165
- const pgid = child?.pid;
166
- if (pgid === undefined) {
167
- connection.end(`${JSON.stringify({ ok: true, killed: false, settled: false, exitCode })}\n`);
168
- return;
169
- }
170
- const killed = !exited;
171
- killsInFlight += 1;
172
- killGroupSettled(pgid).then(settled => {
173
- // The reply counts as owed until the write side has FLUSHED —
174
- // exiting on the mere call would let process death eat the FIN.
175
- let settledReply = false;
176
- const done = () => {
177
- if (settledReply) return;
178
- settledReply = true;
179
- killsInFlight -= 1;
180
- maybeExit();
181
- };
182
- try {
183
- connection.on("finish", done);
184
- connection.on("close", done);
185
- connection.end(`${JSON.stringify({ ok: true, killed, settled, exitCode })}\n`);
186
- setTimeout(done, 2_000).unref();
187
- } catch {
188
- done();
189
- }
190
- });
191
- return;
192
- }
193
- connection.end(`${JSON.stringify({ ok: false })}\n`);
194
- });
195
- });
196
-
197
- let child;
198
- server.on("error", error => {
199
- frame({ so_supervisor: "spawn-failed", message: `control socket: ${String(error)}` });
200
- process.exit(1);
201
- });
202
- server.listen(socketPath, () => {
203
- try {
204
- chmodSync(socketPath, 0o600);
205
- } catch {
206
- // Best effort; the parent directory is already private.
207
- }
208
-
209
- child = spawn(agentFile, agentArgs, {
210
- detached: true,
211
- stdio: ["pipe", "pipe", "pipe"],
212
- });
213
-
214
- child.on("error", error => {
215
- frame({ so_supervisor: "spawn-failed", message: String(error) });
216
- cleanup();
217
- process.exit(1);
218
- });
219
-
220
- observeProcessTree(child, {
221
- onDescendant: pid => descendantPids.add(pid),
222
- onObservationFailure: failure => {
223
- // A dedicated pipe, never the untrusted agent's stdout/stderr. The
224
- // agent gets exactly three stdio descriptors and cannot inherit it.
225
- if (process.env["SO_HELD_DIAGNOSTIC_FD"] === "3") writeSync(3, `${JSON.stringify(failure)}\n`);
226
- },
227
- onUnknown: () => { treeUnknown = true; },
228
- });
229
- child.on("spawn", () => {
230
- frame({ so_supervisor: "ready", agentPgid: child.pid, supervisorPid: process.pid });
231
- // Relay AFTER the frame: agent bytes buffered meanwhile flow next.
232
- child.stdout.pipe(process.stdout, { end: false });
233
- child.stderr.pipe(process.stderr, { end: false });
234
- process.stdin.pipe(child.stdin, { end: false });
235
- process.stdin.on("end", () => fence());
236
- process.stdin.on("error", () => fence());
237
- });
238
-
239
- child.on("exit", (code, signal) => {
240
- exited = true;
241
- exitCode = code ?? (signal === null ? 1 : 143);
242
- // A tool may inherit stdout/stderr and keep `close` from ever firing.
243
- // Start cleanup at root exit, while retaining the relay until flushed.
244
- if (child.pid !== undefined) void killGroupSettled(child.pid);
245
- relayDeadline = setTimeout(() => {
246
- if (relayClosed) return;
247
- // Open pipes after the drain bound may belong to an unobserved,
248
- // detached writer. Do not call the hold successfully completed.
249
- treeUnknown = true;
250
- exitCode = 126;
251
- child.stdout.destroy();
252
- child.stderr.destroy();
253
- }, 5_000);
254
- });
255
-
256
- child.on("close", code => {
257
- relayClosed = true;
258
- clearTimeout(relayDeadline);
259
- // stdio flushed and the process reaped. The GROUP may still have
260
- // members — a tool the agent launched can outlive its leader (round-6
261
- // finding 4) — so the whole group is drained and PROVEN gone before
262
- // custody releases; the whole-tree invariant is the group, never the
263
- // one pid.
264
- exitCode = exitCode ?? code ?? 1;
265
- const pgid = child.pid;
266
- const drained = pgid !== undefined ? killGroupSettled(pgid) : Promise.resolve(false);
267
- drained.then(settled => {
268
- // Match exec.ts's containment refusal. A clean root exit is not a
269
- // clean hold when descendants remain or observation failed.
270
- if (!settled || treeUnknown) exitCode = 126;
271
- exitWanted = true;
272
- maybeExit();
273
- });
274
- });
275
- });
276
-
277
- /** EOF → grace → group SIGKILL. Idempotent; every exit road converges here. */
278
- const fence = () => {
279
- if (fencing || exited || child === undefined) return;
280
- fencing = true;
281
- // Preserve witnesses before a cooperative root can exit and orphan its
282
- // tools between the observer's periodic snapshots.
283
- sampleProcessTree(child);
284
- try {
285
- child.stdin.end();
286
- } catch {
287
- // Already closed.
288
- }
289
- const timer = setTimeout(() => {
290
- if (!exited && child.pid !== undefined) void killGroupSettled(child.pid);
291
- }, graceMs);
292
- timer.unref();
293
- };
294
-
295
- process.on("SIGTERM", () => fence());
296
- process.on("SIGHUP", () => fence());
297
- process.on("SIGINT", () => fence());