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.
- package/CHANGELOG.md +8 -0
- package/README.md +55 -80
- package/dist/agentconfig.d.ts +11 -36
- package/dist/agentconfig.js +3 -62
- package/dist/approval-policy.d.ts +1 -1
- package/dist/approval-policy.js +1 -1
- package/dist/board.d.ts +3 -19
- package/dist/board.js +4 -42
- package/dist/browser/workspace.js +18 -18
- package/dist/browser-workspace.d.ts +2 -1
- package/dist/builder.d.ts +9 -40
- package/dist/builder.js +50 -257
- package/dist/chat-actions.js +3 -14
- package/dist/chat-controls.d.ts +0 -4
- package/dist/chat-controls.js +0 -1
- package/dist/chat-decide.js +1 -3
- package/dist/chat-delivery.d.ts +0 -2
- package/dist/chat-delivery.js +0 -1
- package/dist/chat-quiet.js +2 -2
- package/dist/chat-task-actions.d.ts +1 -1
- package/dist/chat-task-actions.js +0 -2
- package/dist/claim.d.ts +79 -244
- package/dist/claim.js +688 -1023
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +10 -2
- package/dist/contracts/cli.d.ts +0 -13
- package/dist/contracts/cli.js +0 -2
- package/dist/contracts/console-api.d.ts +5 -10
- package/dist/contracts/console-api.js +4 -10
- package/dist/contracts/flow.d.ts +50 -0
- package/dist/contracts/flow.js +26 -1
- package/dist/contracts/lead-tools.d.ts +0 -13
- package/dist/contracts/lead-tools.js +3 -3
- package/dist/contracts/store-json.d.ts +1 -25
- package/dist/contracts/store-json.js +3 -13
- package/dist/converse.d.ts +1 -11
- package/dist/converse.js +6 -38
- package/dist/coordinator.d.ts +0 -1
- package/dist/coordinator.js +3 -5
- package/dist/demo.js +16 -26
- package/dist/desktop-update-gate.js +0 -1
- package/dist/desktop-update.js +1 -3
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +1 -1
- package/dist/dispose.d.ts +2 -3
- package/dist/dispose.js +17 -92
- package/dist/envelope.js +0 -3
- package/dist/exec.d.ts +0 -64
- package/dist/exec.js +3 -356
- package/dist/exhaustion.d.ts +22 -76
- package/dist/exhaustion.js +14 -140
- package/dist/export.js +4 -4
- package/dist/flow-engine.js +1 -1
- package/dist/flow-schedule.d.ts +154 -0
- package/dist/flow-schedule.js +321 -0
- package/dist/flow-share.js +1 -1
- package/dist/flow-triggers.d.ts +6 -1
- package/dist/flow-triggers.js +65 -10
- package/dist/flows-cli.js +8 -4
- package/dist/flows-ui.js +2 -0
- package/dist/flows.js +1 -1
- package/dist/guides.js +9 -29
- package/dist/invoke.d.ts +1 -24
- package/dist/invoke.js +17 -259
- package/dist/lead-status.js +2 -4
- package/dist/lead-voice.js +1 -1
- package/dist/mate-doors.d.ts +1 -2
- package/dist/mate-doors.js +12 -25
- package/dist/mate-tools.d.ts +2 -2
- package/dist/mate-tools.js +10 -22
- package/dist/modes.d.ts +6 -7
- package/dist/modes.js +7 -13
- package/dist/operate-remote-arguments.js +2 -6
- package/dist/operate-remote.js +6 -7
- package/dist/operate.d.ts +0 -6
- package/dist/operate.js +222 -2247
- package/dist/phase-routing.d.ts +13 -23
- package/dist/phase-routing.js +13 -35
- package/dist/plan-auto.js +2 -2
- package/dist/planner-source.js +2 -1
- package/dist/policy.d.ts +1 -4
- package/dist/policy.js +0 -8
- package/dist/process-recovery-eligibility.js +2 -3
- package/dist/project-delete.js +3 -15
- package/dist/proposal.d.ts +4 -24
- package/dist/proposal.js +3 -88
- package/dist/provider-auth.d.ts +3 -3
- package/dist/provider-auth.js +4 -6
- package/dist/provider.d.ts +11 -25
- package/dist/provider.js +4 -36
- package/dist/push.js +1 -1
- package/dist/recipe-ui.js +6 -6
- package/dist/recipes.d.ts +3 -3
- package/dist/recipes.js +27 -23
- package/dist/run-closeout.d.ts +1 -1
- package/dist/run-closeout.js +1 -4
- package/dist/scope.d.ts +11 -65
- package/dist/scope.js +17 -82
- package/dist/serve.js +15 -24
- package/dist/server/chat.js +7 -30
- package/dist/server/flows.js +11 -10
- package/dist/server/guards.js +0 -1
- package/dist/server/pages-assets.js +1 -1
- package/dist/server/pages.js +0 -2
- package/dist/server/people-tokens.js +0 -4
- package/dist/server/route-table.d.ts +1 -1
- package/dist/server/route-table.js +2 -17
- package/dist/server/settings.js +1 -8
- package/dist/server/shared.d.ts +28 -212
- package/dist/server/shared.js +71 -780
- package/dist/server/task-views.js +9 -76
- package/dist/server/tasks.d.ts +1 -50
- package/dist/server/tasks.js +36 -946
- package/dist/setup-guide.d.ts +2 -2
- package/dist/slack-chat.d.ts +0 -2
- package/dist/slack-chat.js +0 -1
- package/dist/store.d.ts +105 -1340
- package/dist/store.js +706 -4010
- package/dist/surface.js +10 -24
- package/dist/task-composer.d.ts +1 -1
- package/dist/task-composer.js +1 -1
- package/dist/task-control.d.ts +3 -10
- package/dist/task-control.js +2 -7
- package/dist/teammate-desk.js +1 -1
- package/dist/teammates-ui.js +1 -1
- package/dist/telegram-mate.d.ts +0 -3
- package/dist/telegram-mate.js +0 -1
- package/dist/templates.d.ts +11 -7
- package/dist/templates.js +12 -9
- package/dist/toolroll-update.d.ts +10 -0
- package/dist/toolroll-update.js +56 -4
- package/dist/work-index.js +2 -6
- package/dist/workspace-ui.js +1 -1
- package/package.json +1 -1
- package/scripts/recovery-canary.mjs +3 -3
- package/dist/contest.d.ts +0 -318
- package/dist/contest.js +0 -753
- package/dist/held.d.ts +0 -150
- package/dist/held.js +0 -799
- package/dist/liveness.d.ts +0 -30
- package/dist/liveness.js +0 -42
- package/dist/pricing.d.ts +0 -45
- package/dist/pricing.js +0 -77
- package/dist/routine.d.ts +0 -228
- package/dist/routine.js +0 -764
- package/dist/supervisor.mjs +0 -297
package/dist/surface.js
CHANGED
|
@@ -13,7 +13,7 @@ import { FLOWS_DESCRIPTORS } from "./flows-cli.js";
|
|
|
13
13
|
* What the tests DO hold to the code: every root here is a routed verb
|
|
14
14
|
* and every routed verb appears here; every declared flag lives in the
|
|
15
15
|
* parser's global vocabulary with the declared arity; the subcommand
|
|
16
|
-
* inventories for task/publish/config/approver
|
|
16
|
+
* inventories for task/publish/config/approver match the
|
|
17
17
|
* exported action lists their dispatchers consult; keyed mutations
|
|
18
18
|
* declare --key; notableReasons come from DOCUMENTED_REASONS. What they
|
|
19
19
|
* CANNOT prove — that a given flag is read by a given handler — is why
|
|
@@ -243,7 +243,7 @@ const GUIDE_ROWS = [
|
|
|
243
243
|
{ invocation: "task block", synopsis: "make one task wait for another", audience: "agent", agentMayInvoke: true, mutation: "keyed",
|
|
244
244
|
positionals: [{ name: "id", required: true, meaning: "the waiting task" }],
|
|
245
245
|
flags: [jsonFlag, dbFlag, keyFlag, { name: "on", takesValue: true, meaning: "the blocker's id" }],
|
|
246
|
-
notableReasons: ["unknown-task"
|
|
246
|
+
notableReasons: ["unknown-task"] },
|
|
247
247
|
{ invocation: "task unblock", synopsis: "stop waiting", audience: "agent", agentMayInvoke: true, mutation: "keyed",
|
|
248
248
|
positionals: [{ name: "id", required: true, meaning: "the waiting task" }],
|
|
249
249
|
flags: [jsonFlag, dbFlag, keyFlag, { name: "on", takesValue: true, meaning: "the blocker's id" }],
|
|
@@ -251,7 +251,7 @@ const GUIDE_ROWS = [
|
|
|
251
251
|
{ invocation: "task next", synopsis: "move a task to the front of ITS queue (scheduling only; approval still required)", audience: "agent", agentMayInvoke: true, mutation: "keyed",
|
|
252
252
|
positionals: [{ name: "id", required: true, meaning: "the task" }],
|
|
253
253
|
flags: [jsonFlag, dbFlag, keyFlag, { name: "undo", takesValue: false, meaning: "back to filing order" }],
|
|
254
|
-
notableReasons: ["unknown-task", "claimed"
|
|
254
|
+
notableReasons: ["unknown-task", "claimed"] },
|
|
255
255
|
operator("task steer", "one credentialed note read before the task's next attempt — steering speaks with the operator's voice"),
|
|
256
256
|
{ invocation: "task assign", synopsis: "reserve a task for one worker, or return it to the shared queue", audience: "agent", agentMayInvoke: true, mutation: "keyed",
|
|
257
257
|
positionals: [{ name: "id", required: true, meaning: "the task" }],
|
|
@@ -265,9 +265,8 @@ const GUIDE_ROWS = [
|
|
|
265
265
|
{ name: "goal", takesValue: true, meaning: "what success is" },
|
|
266
266
|
{ name: "not", takesValue: true, meaning: "explicitly out of scope" },
|
|
267
267
|
{ name: "touches", takesValue: true, meaning: "comma-separated paths this may change" },
|
|
268
|
-
{ name: "budget-usd", takesValue: true, meaning: "spend ceiling for the build" },
|
|
269
|
-
|
|
270
|
-
notableReasons: ["unknown-task", "contest-open"] },
|
|
268
|
+
{ name: "budget-usd", takesValue: true, meaning: "spend ceiling for the build" }],
|
|
269
|
+
notableReasons: ["unknown-task"] },
|
|
271
270
|
{ invocation: "task plan", synopsis: "ask for a plan document (a planning session drafts; a person approves)", audience: "agent", agentMayInvoke: true, mutation: "keyed",
|
|
272
271
|
positionals: [{ name: "id", required: true, meaning: "the task" }],
|
|
273
272
|
flags: [jsonFlag, dbFlag, keyFlag,
|
|
@@ -300,7 +299,7 @@ const GUIDE_ROWS = [
|
|
|
300
299
|
notableReasons: ["usage", "unauthenticated", "not-found"] },
|
|
301
300
|
operator("task accept", "record an explicit acceptance with limitations; does not change checks or prove deployment"),
|
|
302
301
|
operator("task repair", "inspect or approve an existing historical repair draft; use task revise for new feedback"),
|
|
303
|
-
operator("task route", "which configured agents plan, build and repair this task; phase and
|
|
302
|
+
operator("task route", "which configured agents plan, build and repair this task; phase and size changes require renewed approval"),
|
|
304
303
|
operator("task reopen", "resume external work its tracker closed and has been SEEN open again"),
|
|
305
304
|
operator("task stop", "stop ONE exact live attempt (--run <id>): durable before any process is signalled, answers 'stopping' until its own processes are established gone; work, branch, and evidence preserved; no strike, no retry"),
|
|
306
305
|
operator("run settle", "last resort for a finished run whose process witness can't be proven either way: records your --why in the ledger; refuses while any of the run's processes is alive"),
|
|
@@ -312,7 +311,7 @@ const GUIDE_ROWS = [
|
|
|
312
311
|
{ name: "runner", takesValue: true, meaning: "your registered worker name" },
|
|
313
312
|
{ name: "token", takesValue: true, meaning: "the worker's token" },
|
|
314
313
|
{ name: "ttl", takesValue: true, meaning: "lease seconds" }],
|
|
315
|
-
notableReasons: ["held", "fenced", "unapproved", "reserved", "external", "
|
|
314
|
+
notableReasons: ["held", "fenced", "unapproved", "reserved", "external", "claimed", "not-ready"] },
|
|
316
315
|
{ invocation: "heartbeat", synopsis: "still working; extends the lease", audience: "agent", agentMayInvoke: true, mutation: "identity-idempotent",
|
|
317
316
|
positionals: [{ name: "lease", required: true, meaning: "the lease id" }],
|
|
318
317
|
flags: [jsonFlag, dbFlag], notableReasons: ["not-leased", "fenced"] },
|
|
@@ -338,9 +337,6 @@ const GUIDE_ROWS = [
|
|
|
338
337
|
{ invocation: "cap list", synopsis: "recorded capabilities", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag, repoFlag] },
|
|
339
338
|
{ invocation: "outbox list", synopsis: "queued notifications", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
|
|
340
339
|
{ invocation: "incident list", synopsis: "open incidents", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
|
|
341
|
-
{ invocation: "routine list", synopsis: "scheduled routines", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
|
|
342
|
-
{ invocation: "routine show", synopsis: "one routine in full", audience: "agent", agentMayInvoke: true, mutation: "none",
|
|
343
|
-
positionals: [{ name: "id", required: true, meaning: "the routine" }], flags: [jsonFlag, dbFlag] },
|
|
344
340
|
{ invocation: "config show", synopsis: "phase and spend configuration", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag, repoFlag] },
|
|
345
341
|
{ invocation: "setup show", synopsis: "installation setup", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
|
|
346
342
|
{ invocation: "verify show", synopsis: "the repository's check level, its approved full and quick commands, and a suggested quick command", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag, repoFlag] },
|
|
@@ -350,11 +346,9 @@ const GUIDE_ROWS = [
|
|
|
350
346
|
{ name: "report", takesValue: false, meaning: "record readiness observations under --runner" },
|
|
351
347
|
{ name: "runner", takesValue: true, meaning: "the registered worker name reporting" },
|
|
352
348
|
{ name: "token", takesValue: true, meaning: "that worker's token" }] },
|
|
353
|
-
{ invocation: "template list", synopsis: "
|
|
349
|
+
{ invocation: "template list", synopsis: "work templates: tasks and scheduled flows", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
|
|
354
350
|
{ invocation: "template show", synopsis: "one template", audience: "agent", agentMayInvoke: true, mutation: "none",
|
|
355
351
|
positionals: [{ name: "name", required: true, meaning: "the template" }], flags: [jsonFlag, dbFlag] },
|
|
356
|
-
{ invocation: "contest show", synopsis: "a tournament's state and results", audience: "agent", agentMayInvoke: true, mutation: "none",
|
|
357
|
-
positionals: [{ name: "id", required: true, meaning: "the contest" }], flags: [jsonFlag, dbFlag] },
|
|
358
352
|
{ invocation: "webhook status", synopsis: "connected messaging services", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
|
|
359
353
|
{ invocation: "publish status", synopsis: "publication and merge-grant state", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
|
|
360
354
|
// ---- operator ceremonies and infrastructure (listed, detail-free) ----
|
|
@@ -373,12 +367,6 @@ const GUIDE_ROWS = [
|
|
|
373
367
|
operator("peek", "watch live agents in the terminal — one pane per open run; --tmux opens a window per run"),
|
|
374
368
|
operator("incident resolve", "resolve an incident"),
|
|
375
369
|
operator("decide", "read and ANSWER parked decisions — answering is a person's act"),
|
|
376
|
-
operator("routine add", "file a standing order"),
|
|
377
|
-
operator("routine approve", "approve a standing order"),
|
|
378
|
-
operator("routine refresh", "re-resolve the agents a standing order freezes from today's configuration — approves nothing; approve it again afterwards"),
|
|
379
|
-
operator("routine pause", "pause a standing order"),
|
|
380
|
-
operator("routine resume", "resume a standing order"),
|
|
381
|
-
operator("routine run-now", "run a standing order immediately"),
|
|
382
370
|
operatorRead("keys status", "which provider API keys are stored — never the values"),
|
|
383
371
|
operator("keys set", "store a provider's API key as a private file — piped or --key-file, never on the command line"),
|
|
384
372
|
operator("keys clear", "remove a stored provider key (kept only as a fallback; a subscription-mode provider is unaffected)"),
|
|
@@ -414,7 +402,6 @@ const GUIDE_ROWS = [
|
|
|
414
402
|
operator("intake preview", "preview what intake would file"),
|
|
415
403
|
operator("intake pr-comments", "intake review comments from a pull request"),
|
|
416
404
|
operator("intake clear", "clear intake configuration"),
|
|
417
|
-
operator("contest exclude", "exclude a contestant from a tournament"),
|
|
418
405
|
operator("webhook primary", "choose which service receives alerts"),
|
|
419
406
|
operator("bridge", "the Telegram bridge (pairing, tokens, the follower)"),
|
|
420
407
|
operator("enroll", "grant a repository authority (backends, dispatch) — its own explicit yes"),
|
|
@@ -464,7 +451,7 @@ const REMOTE_NO = [
|
|
|
464
451
|
"memory propose",
|
|
465
452
|
];
|
|
466
453
|
const REMOTE_STEP_UP = [
|
|
467
|
-
"proposals", "decide", "task regate", "task approve", "task accept", "task merge", "run settle", "
|
|
454
|
+
"proposals", "decide", "task regate", "task approve", "task accept", "task merge", "run settle", "knowledge apply", "memory apply",
|
|
468
455
|
"flows card approve", "flows card send-back", "flows script approve",
|
|
469
456
|
"approver list", "approver add", "people list", "people invite", "people projects", "people revoke",
|
|
470
457
|
// A person's own API tokens: a password step-up on the server's machine, or the console. Never with a token.
|
|
@@ -478,9 +465,8 @@ const REMOTE_YES = [
|
|
|
478
465
|
"task state", "task block", "task unblock", "task next", "task steer", "task assign", "task scope", "task plan", "task hold", "task unhold", "task require",
|
|
479
466
|
"task requeue", "task review", "task repair", "task route", "task reopen", "task stop", "task resume",
|
|
480
467
|
"runner list", "coordinator list", "cap list", "cap add", "outbox list", "incident list", "incident resolve",
|
|
481
|
-
"routine list", "routine show", "routine add", "routine refresh", "routine pause", "routine resume", "routine run-now",
|
|
482
468
|
"config show", "verify show", "intake show", "intake run", "intake preview", "intake pr-comments", "template list", "template show",
|
|
483
|
-
"
|
|
469
|
+
"webhook status", "review show", "chat-approval show", "chat-approval off",
|
|
484
470
|
"knowledge search", "knowledge impact", "knowledge refresh",
|
|
485
471
|
"memory search", "memory decisions", "memory show", "memory decide", "memory retire", "memory review", "memory status",
|
|
486
472
|
"flows list", "flows show", "flows export", "flows create", "flows edit", "flows trigger add", "flows trigger pause", "flows trigger resume",
|
package/dist/task-composer.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** UI choices translate into the same digest-bound schedules as the CLI. */
|
|
2
|
-
import { type Schedule } from "./
|
|
2
|
+
import { type Schedule } from "./flow-schedule.js";
|
|
3
3
|
export declare function composerSchedule(fields: URLSearchParams): {
|
|
4
4
|
ok: true;
|
|
5
5
|
schedule: string | null;
|
package/dist/task-composer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** UI choices translate into the same digest-bound schedules as the CLI. */
|
|
2
|
-
import { parseSchedule, scheduleText, validTimezone } from "./
|
|
2
|
+
import { parseSchedule, scheduleText, validTimezone } from "./flow-schedule.js";
|
|
3
3
|
export function composerSchedule(fields) {
|
|
4
4
|
const repeat = fields.get("repeat") ?? "once";
|
|
5
5
|
if (repeat === "once")
|
package/dist/task-control.d.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* the preserved draft with fresh proof.
|
|
17
17
|
*
|
|
18
18
|
* Nothing here is a second engine: the stop rides the existing run, claim,
|
|
19
|
-
* hold, recovery, invocation-gateway
|
|
19
|
+
* hold, recovery, and invocation-gateway machinery. This
|
|
20
20
|
* module only names the door and the fence.
|
|
21
21
|
*/
|
|
22
22
|
import { type DispatchDiagnosis } from "./dispatch.js";
|
|
@@ -60,13 +60,6 @@ export type StopRequest = {
|
|
|
60
60
|
runId: number;
|
|
61
61
|
by: string;
|
|
62
62
|
via: "cli" | "web" | "telegram" | "slack" | "discord" | "teams";
|
|
63
|
-
/** The held-session supervisor in this process, when there is one: a
|
|
64
|
-
* held attempt is fenced through its own handle the moment the request
|
|
65
|
-
* is durable (the lapse interval would take the same road within
|
|
66
|
-
* seconds for a request filed elsewhere). */
|
|
67
|
-
held?: {
|
|
68
|
-
stop(runId: number): Promise<void>;
|
|
69
|
-
} | undefined;
|
|
70
63
|
/** A composing confirmation transaction runs this effect only after it commits. */
|
|
71
64
|
deferSignal?: ((signal: () => void) => void) | undefined;
|
|
72
65
|
};
|
|
@@ -78,13 +71,13 @@ export type StopOutcome = {
|
|
|
78
71
|
taskId: string;
|
|
79
72
|
} | {
|
|
80
73
|
ok: false;
|
|
81
|
-
reason: "no-task" | "no-run" | "wrong-task" | "finished" | "not-live" | "
|
|
74
|
+
reason: "no-task" | "no-run" | "wrong-task" | "finished" | "not-live" | "publication";
|
|
82
75
|
detail: string;
|
|
83
76
|
};
|
|
84
77
|
/**
|
|
85
78
|
* The shared stop door. Records the durable request first (one fenced
|
|
86
79
|
* transaction: exact run, still open, this task's current claim, no
|
|
87
|
-
*
|
|
80
|
+
* admitted publication), THEN signals every child this
|
|
88
81
|
* process holds for the attempt. A worker in another process observes the
|
|
89
82
|
* same row through its stop watch within STOP_WATCH_MS.
|
|
90
83
|
*/
|
package/dist/task-control.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* the preserved draft with fresh proof.
|
|
17
17
|
*
|
|
18
18
|
* Nothing here is a second engine: the stop rides the existing run, claim,
|
|
19
|
-
* hold, recovery, invocation-gateway
|
|
19
|
+
* hold, recovery, and invocation-gateway machinery. This
|
|
20
20
|
* module only names the door and the fence.
|
|
21
21
|
*/
|
|
22
22
|
import { ownedProcessCount, runOwnerTag, terminateOwnedProcesses } from "./exec.js";
|
|
@@ -108,7 +108,7 @@ export async function underStopWatch(store, runId, body, options = {}) {
|
|
|
108
108
|
/**
|
|
109
109
|
* The shared stop door. Records the durable request first (one fenced
|
|
110
110
|
* transaction: exact run, still open, this task's current claim, no
|
|
111
|
-
*
|
|
111
|
+
* admitted publication), THEN signals every child this
|
|
112
112
|
* process holds for the attempt. A worker in another process observes the
|
|
113
113
|
* same row through its stop watch within STOP_WATCH_MS.
|
|
114
114
|
*/
|
|
@@ -123,11 +123,6 @@ export function requestTaskStop(store, request, now) {
|
|
|
123
123
|
let terminated = 0;
|
|
124
124
|
const signal = () => {
|
|
125
125
|
terminated = terminateStoppedAttempt(store, request.runId);
|
|
126
|
-
if (request.held !== undefined && store.heldSessionOf(request.runId) !== null) {
|
|
127
|
-
void request.held.stop(request.runId).catch(() => {
|
|
128
|
-
// The lapse interval retries the same fence; the durable row stands.
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
126
|
};
|
|
132
127
|
if (request.deferSignal)
|
|
133
128
|
request.deferSignal(signal);
|
package/dist/teammate-desk.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { flowCardHref, flowDefinitionOf } from "./flow-engine.js";
|
|
14
14
|
import { flowFromSteps } from "./flows.js";
|
|
15
15
|
import { addFlowTriggerTo, removeFlowTrigger, runScheduleNow, scheduleFromWords, triggerConfigOf } from "./flow-triggers.js";
|
|
16
|
-
import { describeSchedule, parseSchedule } from "./
|
|
16
|
+
import { describeSchedule, parseSchedule } from "./flow-schedule.js";
|
|
17
17
|
import { labelOf, nameOf } from "./teammate-admin.js";
|
|
18
18
|
/** The zone on a desk where the teammate handles what it's asked. */
|
|
19
19
|
export const DESK_ZONE = "handle";
|
package/dist/teammates-ui.js
CHANGED
|
@@ -5,7 +5,7 @@ import { callWords, defaultRule, numberFields, receiptWords } from "./teammate-t
|
|
|
5
5
|
import { MEMORY_CHARS, searchMemories } from "./teammate-memory.js";
|
|
6
6
|
import { deskOf, localZone, routinesOf } from "./teammate-desk.js";
|
|
7
7
|
import { weekOf, weekWords } from "./teammate-week.js";
|
|
8
|
-
import { describeSchedule, parseSchedule } from "./
|
|
8
|
+
import { describeSchedule, parseSchedule } from "./flow-schedule.js";
|
|
9
9
|
/** v94: the Tools section's rule rows; a limit's fields show only while "Up to a limit" is chosen. */
|
|
10
10
|
export const TEAMMATE_CSS = `.teammate-tools .tool-grant{border-top:1px solid var(--so-line);padding-top:12px;margin-top:12px}.teammate-tools .tool-grant:first-of-type{border-top:0;margin-top:0;padding-top:0}.teammate-tools h3{font-size:1rem;margin:0 0 4px}.teammate-tools .tool-rule{display:grid;gap:6px;padding:10px 0;border-bottom:1px solid var(--so-line)}.teammate-tools .tool-rule:last-of-type{border-bottom:0}.teammate-tools .tool-rule .meta{display:block}.teammate-tools select,.teammate-tools input{max-width:100%;box-sizing:border-box}.teammate-tools .tool-limit{display:none;flex-wrap:wrap;align-items:center;gap:6px}.teammate-tools .tool-rule:has(option[value="limit"]:checked) .tool-limit{display:flex}.teammate-tools .tool-rule>select{width:auto;min-width:16em}.teammate-tools .tool-limit select{width:auto}.teammate-tools .tool-limit input{width:7em}.teammate-tools .tool-buttons{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}.teammate-tools button{min-height:44px}.teammate-activity li{overflow-wrap:anywhere}.teammate-memory .memory-tell,.teammate-memory .memory-search{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end;margin:0 0 12px}.teammate-memory .memory-tell textarea{flex:1 1 280px}.teammate-memory .memory-search input{flex:1 1 220px}.teammate-memory button{min-height:44px}.teammate-memories{list-style:none;padding:0;margin:0}.teammate-memories li{padding:10px 0;border-top:1px solid var(--so-line);overflow-wrap:anywhere}.teammate-memories li:first-child{border-top:0}.teammate-memories .meta{display:block;font-size:.85rem}.teammate-memories details.memory-edit{border:0;padding:0;margin:2px 0 0;background:transparent;box-shadow:none}.memory-edit summary{cursor:pointer;min-height:32px;display:inline-flex;align-items:center;font-size:.85rem;font-weight:400;color:var(--so-muted)}.tool-undo{border:0;padding:0;margin:8px 0;background:transparent;box-shadow:none}.tool-undo summary{cursor:pointer;min-height:36px;display:inline-flex;align-items:center;font-size:.9rem}.tool-undo-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin:6px 0}.tool-undo-row select{width:auto}.teammate-overrides,.teammate-undo{padding-left:18px}.teammate-undo li{margin:6px 0}.teammate-undo form{display:inline}.teammate-week h3{font-size:1rem;margin:14px 0 4px}.teammate-week button{min-height:44px}.teammate-routines{list-style:none;padding:0;margin:8px 0}.teammate-routines li{display:grid;gap:4px;padding:10px 0;border-top:1px solid var(--so-line)}.teammate-routines li:first-child{border-top:0}.routine-buttons{display:flex;flex-wrap:wrap;gap:8px}.routine-buttons button,.routine-add button{min-height:44px}.routine-add{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end;margin:8px 0}.routine-add label{display:grid;gap:4px;flex:1 1 200px}.routine-add label:first-of-type{flex:0 1 180px}.teammate-actions,.question-options{display:flex;flex-wrap:wrap;gap:8px;margin:8px 0}.teammate-actions form,.question-options form{margin:0}.memory-edit form{display:grid;gap:8px;margin-top:6px}.memory-buttons{display:flex;flex-wrap:wrap;gap:8px}@media(max-width:600px){.teammate-tools select,.teammate-tools input{font-size:16px}}`;
|
|
11
11
|
const USES = [["free", "Do it"], ["limit", "Do it, up to a limit"], ["ask", "Ask first"], ["never", "Never"]];
|
package/dist/telegram-mate.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type PhoneLink } from "./chat-channel.js";
|
|
2
2
|
export { channelRepos as telegramConversationRepos, resolveChannelMate as resolveTelegramMate, tooLongText, whichTaskText, replyContextFor, NO_PHONE_LINK, NO_TASK_LINK, proposalLink, confirmedLink, proposalPreview, proposalOutcomeText, handoffCardText, confirmedCardText, CHAT_ACTION_PARITY as TELEGRAM_ACTION_PARITY, parityGaps, type PhoneLink, type ResolvedMate, type ParitySupport } from "./chat-channel.js";
|
|
3
|
-
import { type DoorOptions } from "./mate-doors.js";
|
|
4
3
|
import { type MateAsk, type Store, type TelegramBinding } from "./store.js";
|
|
5
4
|
import type { SubscriptionMateRunner } from "./subscription-chat.js";
|
|
6
5
|
import type { TelegramTransport } from "./telegram.js";
|
|
@@ -20,8 +19,6 @@ export type TelegramConversationOptions = {
|
|
|
20
19
|
evidenceRoot: string;
|
|
21
20
|
/** Injected by tests; production invokes the isolated local harness. */
|
|
22
21
|
subscriptionRunner?: SubscriptionMateRunner;
|
|
23
|
-
/** The held-session supervisor in this process, when there is one (a stop fences through it). */
|
|
24
|
-
held?: DoorOptions["held"];
|
|
25
22
|
/**
|
|
26
23
|
* The https origin a phone link may open, read again on EVERY call (a
|
|
27
24
|
* card is linked immediately before it is sent or edited, never from a
|
package/dist/telegram-mate.js
CHANGED
|
@@ -277,7 +277,6 @@ export function applyProposalTap(store, binding, token, message, repos, options,
|
|
|
277
277
|
via: "telegram",
|
|
278
278
|
evidenceRoot: options.evidenceRoot,
|
|
279
279
|
confirm: action.phase === "yes",
|
|
280
|
-
...(options.held === undefined ? {} : { held: options.held }),
|
|
281
280
|
deferSignal: signal => effects.push({ kind: "signal", run: signal }),
|
|
282
281
|
});
|
|
283
282
|
if (!outcome.ok && outcome.reason === "needs-confirm") {
|
package/dist/templates.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The template library (adoption track, step 2) — common
|
|
3
|
-
*
|
|
2
|
+
* The template library (adoption track, step 2) — common work shipped as
|
|
3
|
+
* STATIC DATA that renders into editable, UNAPPROVED drafts.
|
|
4
4
|
*
|
|
5
5
|
* What a template is: a pre-filled form. What it is not: automation with
|
|
6
6
|
* authority. Applying one produces exactly the rows a manual filing
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
* capitals, because `--file` must never read as a yes.
|
|
10
10
|
*
|
|
11
11
|
* The Codex adoption review (finding 10) drew the v1 line: templates may
|
|
12
|
-
* express ONLY what a task or
|
|
12
|
+
* express ONLY what a task proposal or a scheduled flow can carry today. A
|
|
13
|
+
* repeating template (v115: what a routine template was) makes a scheduled
|
|
14
|
+
* flow whose schedule starts paused, and whose every firing waits for
|
|
15
|
+
* approval under the project's rules. The two
|
|
13
16
|
* use cases that imply separate authenticated authority — GitHub issue
|
|
14
17
|
* intake (a grant) and CI babysitting (publish + the repair button) —
|
|
15
18
|
* ship as RECIPES: they display the existing ceremonies, and cannot be
|
|
@@ -35,11 +38,12 @@ export type TaskTemplate = {
|
|
|
35
38
|
/** The fields most installations change — shown in `show` and preview. */
|
|
36
39
|
edit: string[];
|
|
37
40
|
};
|
|
38
|
-
export type
|
|
39
|
-
kind: "
|
|
41
|
+
export type ScheduledTemplate = {
|
|
42
|
+
kind: "scheduled";
|
|
40
43
|
name: string;
|
|
41
44
|
purpose: string;
|
|
42
|
-
|
|
45
|
+
/** The flow's name. */
|
|
46
|
+
title: string;
|
|
43
47
|
goal: string;
|
|
44
48
|
outOfScope: string | null;
|
|
45
49
|
touches: string[];
|
|
@@ -60,6 +64,6 @@ export type RecipeTemplate = {
|
|
|
60
64
|
run: string;
|
|
61
65
|
}[];
|
|
62
66
|
};
|
|
63
|
-
export type Template = TaskTemplate |
|
|
67
|
+
export type Template = TaskTemplate | ScheduledTemplate | RecipeTemplate;
|
|
64
68
|
export declare const TEMPLATES: readonly Template[];
|
|
65
69
|
export declare function templateByName(name: string): Template | null;
|
package/dist/templates.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The template library (adoption track, step 2) — common
|
|
3
|
-
*
|
|
2
|
+
* The template library (adoption track, step 2) — common work shipped as
|
|
3
|
+
* STATIC DATA that renders into editable, UNAPPROVED drafts.
|
|
4
4
|
*
|
|
5
5
|
* What a template is: a pre-filled form. What it is not: automation with
|
|
6
6
|
* authority. Applying one produces exactly the rows a manual filing
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
* capitals, because `--file` must never read as a yes.
|
|
10
10
|
*
|
|
11
11
|
* The Codex adoption review (finding 10) drew the v1 line: templates may
|
|
12
|
-
* express ONLY what a task or
|
|
12
|
+
* express ONLY what a task proposal or a scheduled flow can carry today. A
|
|
13
|
+
* repeating template (v115: what a routine template was) makes a scheduled
|
|
14
|
+
* flow whose schedule starts paused, and whose every firing waits for
|
|
15
|
+
* approval under the project's rules. The two
|
|
13
16
|
* use cases that imply separate authenticated authority — GitHub issue
|
|
14
17
|
* intake (a grant) and CI babysitting (publish + the repair button) —
|
|
15
18
|
* ship as RECIPES: they display the existing ceremonies, and cannot be
|
|
@@ -21,10 +24,10 @@
|
|
|
21
24
|
*/
|
|
22
25
|
export const TEMPLATES = [
|
|
23
26
|
{
|
|
24
|
-
kind: "
|
|
27
|
+
kind: "scheduled",
|
|
25
28
|
name: "nightly-deps",
|
|
26
29
|
purpose: "keep dependencies fresh without surprise majors",
|
|
27
|
-
|
|
30
|
+
title: "Keep dependencies current",
|
|
28
31
|
goal: "Update dependencies conservatively: refresh the lockfile within existing semver ranges, run the full test suite, and summarize what moved in the handoff — plus anything notable you saw (majors now available, security advisories, packages that look abandoned). If the suite fails after the refresh, park with the failure rather than pinning things until it passes.",
|
|
29
32
|
outOfScope: "No major version bumps. No edits to application code beyond what the lockfile refresh itself forces. No changes to CI configuration.",
|
|
30
33
|
touches: [],
|
|
@@ -37,10 +40,10 @@ export const TEMPLATES = [
|
|
|
37
40
|
edit: ["schedule", "goal (name your package manager if it is unusual)", "ceiling (a weekly dollar cap)"],
|
|
38
41
|
},
|
|
39
42
|
{
|
|
40
|
-
kind: "
|
|
43
|
+
kind: "scheduled",
|
|
41
44
|
name: "test-coverage",
|
|
42
45
|
purpose: "grow the test suite one module at a time",
|
|
43
|
-
|
|
46
|
+
title: "Test one overlooked module",
|
|
44
47
|
goal: "Pick ONE module that is under-tested — no tests at all beats thin tests — and write focused tests for its observable behavior: inputs, outputs, and failure paths, not implementation details. One module per firing, then stop. If the module cannot be tested without refactoring it, do not refactor: park with a note naming the obstacle so a human can decide.",
|
|
45
48
|
outOfScope: "No refactoring of the code under test. No snapshot tests. No changes outside the chosen module's test file(s).",
|
|
46
49
|
touches: [],
|
|
@@ -53,10 +56,10 @@ export const TEMPLATES = [
|
|
|
53
56
|
edit: ["schedule", "touches (pin the test directory layout if yours is unusual)"],
|
|
54
57
|
},
|
|
55
58
|
{
|
|
56
|
-
kind: "
|
|
59
|
+
kind: "scheduled",
|
|
57
60
|
name: "docs-drift",
|
|
58
61
|
purpose: "keep the README and docs telling the truth weekly",
|
|
59
|
-
|
|
62
|
+
title: "Keep documentation accurate",
|
|
60
63
|
goal: "Read the README and any docs/ pages against the code as it is today. Fix what has drifted: commands that moved, flags that changed, features that shipped undocumented, claims that stopped being true. Prefer deleting a stale claim over guessing a new one. Summarize every correction in the handoff.",
|
|
61
64
|
outOfScope: "No restructuring or rewriting for style. No new documentation pages. No changes to code.",
|
|
62
65
|
touches: ["README.md", "docs/"],
|
|
@@ -103,6 +103,15 @@ export declare function releaseNotes(changelog: string, version: string): string
|
|
|
103
103
|
* by its directory), and the directory after, so a crash never leaves a torn
|
|
104
104
|
* database, catalog or command. */
|
|
105
105
|
export declare function durableRename(temp: string, target: string): void;
|
|
106
|
+
/** The tables a migration may remove whole (v115's removed features). */
|
|
107
|
+
export declare const RETIRED_TABLES: readonly string[];
|
|
108
|
+
/** A saved routine, as its move into a scheduled flow must account for it. */
|
|
109
|
+
type RoutineDigest = {
|
|
110
|
+
id: number;
|
|
111
|
+
repo: string;
|
|
112
|
+
name: string;
|
|
113
|
+
live: boolean;
|
|
114
|
+
};
|
|
106
115
|
export type TableDigest = {
|
|
107
116
|
name: string;
|
|
108
117
|
columns: string[];
|
|
@@ -117,6 +126,7 @@ export type TableDigest = {
|
|
|
117
126
|
count: number;
|
|
118
127
|
nonNull: Record<string, number>;
|
|
119
128
|
};
|
|
129
|
+
routines?: RoutineDigest[];
|
|
120
130
|
};
|
|
121
131
|
/** Each saved table's columns, row count and last rowid; the append-only ones hashed (deploy-browser's rehearsal check).
|
|
122
132
|
* Bounded: only the ledger and small tables are read row by row. */
|
package/dist/toolroll-update.js
CHANGED
|
@@ -166,7 +166,7 @@ export function runningWorkWords(db) {
|
|
|
166
166
|
const runs = db.prepare("SELECT tr.external_id id, COALESCE(t.title, tr.external_id) title FROM run r JOIN task_ref tr ON tr.id = r.task_ref LEFT JOIN task t ON t.id = tr.external_id WHERE r.outcome IS NULL ORDER BY r.id LIMIT 3").all()
|
|
167
167
|
.map(row => `${String(row["id"])} (${String(row["title"]).slice(0, 60)})`);
|
|
168
168
|
const more = Number(active["runs"] ?? 0) - runs.length;
|
|
169
|
-
const other = Object.entries(active).filter(([key, n]) => key !== "runs" && n > 0).map(([key, n]) => `${n} ${key === "claims" ? "claimed task" : key === "conversations" ? "chat request" : key === "
|
|
169
|
+
const other = Object.entries(active).filter(([key, n]) => key !== "runs" && n > 0).map(([key, n]) => `${n} ${key === "claims" ? "claimed task" : key === "conversations" ? "chat request" : key === "stopping" ? "run still stopping" : key === "codingDeliveries" ? "unconfirmed coding message" : "coding session"}${n === 1 ? "" : "s"}`);
|
|
170
170
|
const parts = [...(runs.length ? [`running ${runs.join(", ")}${more > 0 ? ` and ${more} more` : ""}`] : []), ...other];
|
|
171
171
|
return parts.join("; ");
|
|
172
172
|
}
|
|
@@ -322,7 +322,9 @@ export function durableRename(temp, target) {
|
|
|
322
322
|
* only when every saved value is null, or, with `carried`, when the receiving table gained exactly one receipt under
|
|
323
323
|
* that destination per row holding a value, and as many non-null values per column as the dropped columns held.
|
|
324
324
|
* `receives`: the only rows the table may gain are those receipts. `compacted`: rows may go only into summary rows,
|
|
325
|
-
* whose `sum` grows by exactly as many.
|
|
325
|
+
* whose `sum` grows by exactly as many. `retired`: a removed feature's table, which may go whole. `fromRoutines`: rows
|
|
326
|
+
* kept as they were, and the only rows the table may gain are one per saved routine, moved into a scheduled flow (its
|
|
327
|
+
* flow, or its schedule trigger naming that routine) — on only when the routine was approved and running. */
|
|
326
328
|
const LEGACY_DESTINATION = "legacy:single-destination";
|
|
327
329
|
const HISTORY_RULES = {
|
|
328
330
|
action_ledger: { appendOnly: true }, ledger_seal: { appendOnly: true }, ledger_checkpoint: { appendOnly: true },
|
|
@@ -331,7 +333,16 @@ const HISTORY_RULES = {
|
|
|
331
333
|
// v114: single-destination delivery columns may be dropped only once their values are legacy receipts.
|
|
332
334
|
notification: { dropped: ["attempts", "last_attempt_at", "last_error", "delivered_at", "receipt", "claim_owner", "claim_expires_at"], carried: { into: "notification_delivery", destination: LEGACY_DESTINATION } },
|
|
333
335
|
notification_delivery: { receives: LEGACY_DESTINATION },
|
|
336
|
+
// v115: contests, held sessions, fallback chains and routines are removed; their tables go whole.
|
|
337
|
+
tournament_terms: { retired: true }, contest: { retired: true }, contestant: { retired: true },
|
|
338
|
+
routine: { retired: true }, routine_fire: { retired: true },
|
|
339
|
+
attended_authorization: { retired: true }, session_turn: { retired: true }, held_session: { retired: true },
|
|
340
|
+
fallback_config: { retired: true }, fallback_cycle: { retired: true }, fallback_transition: { retired: true },
|
|
341
|
+
// v115: each routine becomes one scheduled flow and its schedule trigger.
|
|
342
|
+
flow: { fromRoutines: "flow" }, flow_trigger: { fromRoutines: "trigger" },
|
|
334
343
|
};
|
|
344
|
+
/** The tables a migration may remove whole (v115's removed features). */
|
|
345
|
+
export const RETIRED_TABLES = Object.freeze(Object.keys(HISTORY_RULES).filter(name => HISTORY_RULES[name].retired));
|
|
335
346
|
const tableColumns = (db, name) => db.prepare("PRAGMA table_info(" + quote(name) + ")").all().map(r => String(r["name"]));
|
|
336
347
|
const plainValue = (_, v) => v instanceof Uint8Array ? Buffer.from(v).toString("hex") : typeof v === "bigint" ? String(v) : v;
|
|
337
348
|
/** Rows streamed into a sha256, never held together: through a rowid in rowid order, or a WITHOUT ROWID table whole. */
|
|
@@ -371,13 +382,50 @@ export function historySnapshot(db) {
|
|
|
371
382
|
}
|
|
372
383
|
if (rule?.receives)
|
|
373
384
|
t.received = receipts(db, name, rule.receives, columns.filter(column => column !== "notification" && column !== "destination"));
|
|
374
|
-
if (rule?.appendOnly)
|
|
385
|
+
if (rule?.appendOnly || rule?.fromRoutines)
|
|
375
386
|
t.hash = rowsHash(db, name, columns, t.last).hash;
|
|
376
387
|
if (rule?.compacted)
|
|
377
388
|
t.summed = summed(db, rule.compacted);
|
|
389
|
+
if (rule?.fromRoutines)
|
|
390
|
+
t.routines = savedRoutines(db);
|
|
378
391
|
return t;
|
|
379
392
|
});
|
|
380
393
|
}
|
|
394
|
+
/** Each saved routine (none when the table is gone or was never made), and whether it was approved and running. */
|
|
395
|
+
function savedRoutines(db) {
|
|
396
|
+
if (!db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='routine'").get())
|
|
397
|
+
return [];
|
|
398
|
+
const columns = new Set(tableColumns(db, "routine"));
|
|
399
|
+
const live = ["approved_at", "approved_digest", "digest", "paused", "approved_profile_json", "approved_route_json"].every(c => columns.has(c))
|
|
400
|
+
? "(approved_at IS NOT NULL AND approved_digest = digest AND approved_profile_json IS NOT NULL AND approved_route_json IS NOT NULL AND paused = 0)" : "0";
|
|
401
|
+
return db.prepare("SELECT id, repo, name, " + live + " AS live FROM routine ORDER BY id").all()
|
|
402
|
+
.map(r => ({ id: Number(r["id"]), repo: String(r["repo"]), name: String(r["name"]), live: Number(r["live"]) === 1 }));
|
|
403
|
+
}
|
|
404
|
+
/** Whether a table's rows past its saved last rowid are exactly the flows (or schedule triggers) its saved routines became. */
|
|
405
|
+
function movedRoutines(db, t, before) {
|
|
406
|
+
const routines = t.routines ?? [];
|
|
407
|
+
if (HISTORY_RULES[t.name].fromRoutines === "flow") {
|
|
408
|
+
const added = db.prepare("SELECT repo, name, state FROM flow WHERE rowid > ? ORDER BY rowid").all(t.last);
|
|
409
|
+
const key = (repo, name) => JSON.stringify([String(repo), String(name)]);
|
|
410
|
+
return added.length === routines.length && added.every(r => r["state"] === "active")
|
|
411
|
+
&& JSON.stringify(added.map(r => key(r["repo"], r["name"])).sort()) === JSON.stringify(routines.map(r => key(r.repo, r.name)).sort());
|
|
412
|
+
}
|
|
413
|
+
// A trigger names its routine in its order, and belongs to a flow the move added for that same routine.
|
|
414
|
+
const flows = before.find(one => one.name === "flow");
|
|
415
|
+
if (flows === undefined)
|
|
416
|
+
return false;
|
|
417
|
+
const added = db.prepare(`SELECT t.kind, t.state, json_extract(t.config_json, '$.order.routine') AS routine, f.id AS flow, f.repo, f.name FROM flow_trigger t
|
|
418
|
+
LEFT JOIN flow f ON f.id = t.flow AND f.rowid > ? WHERE t.rowid > ? ORDER BY t.rowid`).all(flows.last, t.last);
|
|
419
|
+
const byId = new Map(routines.map(r => [r.id, r]));
|
|
420
|
+
const seen = new Set();
|
|
421
|
+
return added.length === routines.length && added.every(r => {
|
|
422
|
+
const routine = byId.get(Number(r["routine"]));
|
|
423
|
+
if (r["kind"] !== "schedule" || routine === undefined || seen.has(routine.id) || r["flow"] === null)
|
|
424
|
+
return false;
|
|
425
|
+
seen.add(routine.id);
|
|
426
|
+
return r["repo"] === routine.repo && r["name"] === routine.name && (r["state"] === "paused" || (r["state"] === "active" && routine.live));
|
|
427
|
+
});
|
|
428
|
+
}
|
|
381
429
|
/** The tables whose saved history the migration changed beyond what HISTORY_RULES declares. */
|
|
382
430
|
export function changedHistory(db, before) {
|
|
383
431
|
const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all().map(r => String(r["name"])));
|
|
@@ -403,7 +451,7 @@ export function changedHistory(db, before) {
|
|
|
403
451
|
};
|
|
404
452
|
return before.filter(t => {
|
|
405
453
|
if (!tables.has(t.name))
|
|
406
|
-
return true;
|
|
454
|
+
return HISTORY_RULES[t.name]?.retired !== true;
|
|
407
455
|
const rule = HISTORY_RULES[t.name], present = new Set(tableColumns(db, t.name));
|
|
408
456
|
if (t.columns.some(c => !present.has(c) && (!rule?.dropped?.includes(c) || (t.nonNull?.[c] !== 0 && !accounted(rule, t)))))
|
|
409
457
|
return true;
|
|
@@ -412,6 +460,10 @@ export function changedHistory(db, before) {
|
|
|
412
460
|
const after = rowsHash(db, t.name, columns);
|
|
413
461
|
return after.count !== t.count || after.hash !== t.hash;
|
|
414
462
|
}
|
|
463
|
+
if (rule?.fromRoutines) {
|
|
464
|
+
const kept = rowsHash(db, t.name, columns, t.last);
|
|
465
|
+
return kept.count !== t.count || kept.hash !== t.hash || !movedRoutines(db, t, before);
|
|
466
|
+
}
|
|
415
467
|
if (rule?.appendOnly) {
|
|
416
468
|
const after = rowsHash(db, t.name, columns, t.last);
|
|
417
469
|
return after.count !== t.count || after.hash !== t.hash;
|
package/dist/work-index.js
CHANGED
|
@@ -72,8 +72,6 @@ const PROJECTION = `WITH RECURSIVE admitted AS MATERIALIZED (
|
|
|
72
72
|
JOIN run r ON r.id=d.run JOIN admitted a ON a.ref_id=r.task_ref
|
|
73
73
|
WHERE d.state<>'answered' AND d.answered_at IS NULL GROUP BY r.task_ref
|
|
74
74
|
), custody AS MATERIALIZED (
|
|
75
|
-
SELECT r.task_ref FROM held_session h JOIN run r ON r.id=h.run JOIN admitted a ON a.ref_id=r.task_ref WHERE h.ended_at IS NULL
|
|
76
|
-
UNION
|
|
77
75
|
SELECT r.task_ref FROM run r INDEXED BY work_spawned_run JOIN admitted a ON a.ref_id=r.task_ref
|
|
78
76
|
WHERE r.provider_started_at IS NOT NULL AND NOT EXISTS(SELECT 1 FROM run_process p WHERE p.run=r.id) AND NOT EXISTS(SELECT 1 FROM run_process_summary s WHERE s.run=r.id)
|
|
79
77
|
UNION
|
|
@@ -110,8 +108,7 @@ const PROJECTION = `WITH RECURSIVE admitted AS MATERIALIZED (
|
|
|
110
108
|
), workers AS MATERIALIZED (
|
|
111
109
|
SELECT r.name,r.capacity,r.heartbeat_at,j.value repo,
|
|
112
110
|
(SELECT COUNT(*) FROM claim c WHERE c.runner=r.name AND c.released_at IS NULL AND c.expires_at>$now
|
|
113
|
-
AND c.lease_generation=(SELECT MAX(newest.lease_generation) FROM claim newest WHERE newest.task_ref=c.task_ref)
|
|
114
|
-
AND c.lease_id NOT IN (SELECT lease_id FROM held_session WHERE ended_at IS NULL)) occupied
|
|
111
|
+
AND c.lease_generation=(SELECT MAX(newest.lease_generation) FROM claim newest WHERE newest.task_ref=c.task_ref)) occupied
|
|
115
112
|
FROM runner r,json_each(CASE WHEN json_valid(r.repos) THEN r.repos ELSE '[]' END) j WHERE r.retired_at IS NULL
|
|
116
113
|
), facts AS MATERIALIZED (
|
|
117
114
|
SELECT c.*,f.version_count,f.family_updated,f.earlier_active,f.earlier_id,f.family_running,f.question_id,
|
|
@@ -289,8 +286,7 @@ function registerValidators(store) {
|
|
|
289
286
|
* `workspace`: a finished task whose last build's workspace note is still
|
|
290
287
|
* there, which only the store can read (a held or unreadable workspace). */
|
|
291
288
|
function custodyReadings(store) {
|
|
292
|
-
const refs = store.handle.prepare(`SELECT r.task_ref FROM
|
|
293
|
-
UNION SELECT r.task_ref FROM run r WHERE r.provider_started_at IS NOT NULL AND NOT EXISTS(SELECT 1 FROM run_process p WHERE p.run=r.id) AND NOT EXISTS(SELECT 1 FROM run_process_summary s WHERE s.run=r.id)
|
|
289
|
+
const refs = store.handle.prepare(`SELECT r.task_ref FROM run r WHERE r.provider_started_at IS NOT NULL AND NOT EXISTS(SELECT 1 FROM run_process p WHERE p.run=r.id) AND NOT EXISTS(SELECT 1 FROM run_process_summary s WHERE s.run=r.id)
|
|
294
290
|
UNION SELECT r.task_ref FROM run_process p JOIN run r ON r.id=p.run WHERE p.exited_at IS NULL AND p.container_empty_at IS NULL`).all().map(row => Number(row['task_ref']));
|
|
295
291
|
const noted = store.handle.prepare(`SELECT r.task_ref,r.worktree FROM task t JOIN task_ref tr ON tr.external_id=t.id
|
|
296
292
|
JOIN run r ON r.id=(SELECT MAX(id) FROM run WHERE task_ref=tr.id AND role IN ('builder','scout') AND finished_at IS NOT NULL)
|
package/dist/workspace-ui.js
CHANGED
|
@@ -506,7 +506,7 @@ export function workCounts(rows) {
|
|
|
506
506
|
export function compareWorkRows(a, b) {
|
|
507
507
|
return a.rank - b.rank || (a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0);
|
|
508
508
|
}
|
|
509
|
-
const WORK_KEYS = new Set(["inbox", "board", "queue", "work", "done", "activity", "review", "tasks", "runs", "workbench", "
|
|
509
|
+
const WORK_KEYS = new Set(["inbox", "board", "queue", "work", "done", "activity", "review", "tasks", "runs", "workbench", "recipes", "ledger"]);
|
|
510
510
|
const SETTINGS_KEYS = new Set(["fleet", "caps", "people", "mode", "system", "settings"]);
|
|
511
511
|
export function primaryDestinationOf(active) {
|
|
512
512
|
if (active === "chat")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolroll",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.53",
|
|
4
4
|
"description": "A control plane for unattended coding agents — queue tasks, walk away, come back to pull requests. Agents build in leased worktrees, never touch your default branch, and interrupt you only for decisions that need a human.",
|
|
5
5
|
"directories": {
|
|
6
6
|
"doc": "docs"
|
|
@@ -27,7 +27,7 @@ for (let index = 2; index < process.argv.length; index++) {
|
|
|
27
27
|
assert(Number.isSafeInteger(rounds) && rounds > 0 && rounds <= 1000, "rounds must be an integer from 1 to 1000");
|
|
28
28
|
const { openStore } = await import(pathToFileURL(join(root, "dist/store.js")));
|
|
29
29
|
const { register, recoverDead, DEFAULT_LIVENESS_MS } = await import(pathToFileURL(join(root, "dist/runner.js")));
|
|
30
|
-
const { acquire,
|
|
30
|
+
const { acquire, finalize } = await import(pathToFileURL(join(root, "dist/claim.js")));
|
|
31
31
|
const { WorktreePool } = await import(pathToFileURL(join(root, "dist/worktree.js")));
|
|
32
32
|
const { run } = await import(pathToFileURL(join(root, "dist/exec.js")));
|
|
33
33
|
const { HANDOFF_PREFIX } = await import(pathToFileURL(join(root, "dist/evidence.js")));
|
|
@@ -101,7 +101,7 @@ try {
|
|
|
101
101
|
const blocked = await pool.lease(request);
|
|
102
102
|
assert.equal(blocked.ok, false);
|
|
103
103
|
assert.equal(blocked.reason, "in-use");
|
|
104
|
-
assert.equal(
|
|
104
|
+
assert.equal(finalize(store, "old-lease", { kind: "complete", state: "done", now: expired }).ok, false);
|
|
105
105
|
assert.notEqual(store.getTask("work").state, "done");
|
|
106
106
|
writer.kill("SIGKILL");
|
|
107
107
|
await exited;
|
|
@@ -114,7 +114,7 @@ try {
|
|
|
114
114
|
assert.equal(await readFile(join(path, "draft.txt"), "utf8"), draft);
|
|
115
115
|
assert.equal(await git(path, ["rev-parse", "HEAD"]), head);
|
|
116
116
|
assert((await readFile(resumed.reclaimed, "utf8")).includes(draft.trim()));
|
|
117
|
-
assert.equal(
|
|
117
|
+
assert.equal(finalize(store, "new-lease", { kind: "complete", state: "failed", now: expired }).ok, true);
|
|
118
118
|
await pool.release(path, expired);
|
|
119
119
|
cases.push({ round: index + 1, scenario, passed: true, oldCompletion: "fenced", liveWriter: "refused-reuse", draft: "preserved", commit: "preserved" });
|
|
120
120
|
} finally {
|