toolroll 0.9.51 → 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 +20 -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/backup.d.ts +8 -0
- package/dist/backup.js +10 -1
- package/dist/board.d.ts +3 -19
- package/dist/board.js +4 -42
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +18 -18
- package/dist/browser-workspace.d.ts +3 -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/database-reclaim.d.ts +4 -0
- package/dist/database-reclaim.js +13 -0
- package/dist/demo.d.ts +1 -1
- package/dist/demo.js +17 -27
- package/dist/desktop-update-gate.js +0 -1
- package/dist/desktop-update.d.ts +1 -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/live-bus.d.ts +3 -3
- package/dist/live-bus.js +3 -3
- 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/monitoring.js +32 -8
- package/dist/notification-delivery.d.ts +19 -0
- package/dist/notification-delivery.js +37 -0
- 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 +283 -2378
- 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/process-recovery-settlement.js +1 -0
- 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/retention.js +6 -5
- 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 +16 -25
- 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 +4 -9
- package/dist/server/shared.d.ts +29 -212
- package/dist/server/shared.js +73 -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 +142 -1382
- package/dist/store.js +900 -4083
- package/dist/surface.js +10 -25
- 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 +24 -3
- package/dist/toolroll-update.js +159 -16
- package/dist/webhooks.d.ts +10 -22
- package/dist/webhooks.js +43 -81
- package/dist/work-index.js +3 -7
- package/dist/workspace-revision.d.ts +28 -1
- package/dist/workspace-revision.js +276 -49
- package/dist/workspace-ui.js +1 -1
- package/package.json +1 -3
- 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
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Database } from "./store.js";
|
|
2
|
+
/** Reclaim whole free pages on databases created without auto_vacuum. Call only after committing writes.
|
|
3
|
+
* A migration needs at least 1 MiB to justify the I/O; retention reclaims whenever more than 25% is free. */
|
|
4
|
+
export declare function reclaimDatabase(db: Database, reason: "migration" | "retention"): boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Reclaim whole free pages on databases created without auto_vacuum. Call only after committing writes.
|
|
2
|
+
* A migration needs at least 1 MiB to justify the I/O; retention reclaims whenever more than 25% is free. */
|
|
3
|
+
export function reclaimDatabase(db, reason) {
|
|
4
|
+
const free = Number(db.prepare("PRAGMA freelist_count").get()["freelist_count"]);
|
|
5
|
+
const pages = Number(db.prepare("PRAGMA page_count").get()["page_count"]);
|
|
6
|
+
const size = Number(db.prepare("PRAGMA page_size").get()["page_size"]);
|
|
7
|
+
if (reason === "migration" ? free * size < 1024 * 1024 : free * 4 <= pages)
|
|
8
|
+
return false;
|
|
9
|
+
db.exec("VACUUM");
|
|
10
|
+
// VACUUM writes through WAL too: checkpoint so the main file (and its backups) shrinks now.
|
|
11
|
+
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
12
|
+
return true;
|
|
13
|
+
}
|
package/dist/demo.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - The database is stamped `demo` BEFORE any row exists — an append-only
|
|
9
9
|
* installation fact with no unset API. Every spending or external-effect
|
|
10
10
|
* command (tick, watch, build, publish, reconcile, daemon, bridge,
|
|
11
|
-
*
|
|
11
|
+
* intake) fails closed on the stamp, so a kept sandbox
|
|
12
12
|
* can never be mistaken for real work by a worker pointed at it later.
|
|
13
13
|
* The console banner is decoration; the fence is enforcement.
|
|
14
14
|
* - The seeded history is SYNTHETIC and does not pretend otherwise: runs
|
package/dist/demo.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - The database is stamped `demo` BEFORE any row exists — an append-only
|
|
9
9
|
* installation fact with no unset API. Every spending or external-effect
|
|
10
10
|
* command (tick, watch, build, publish, reconcile, daemon, bridge,
|
|
11
|
-
*
|
|
11
|
+
* intake) fails closed on the stamp, so a kept sandbox
|
|
12
12
|
* can never be mistaken for real work by a worker pointed at it later.
|
|
13
13
|
* The console banner is decoration; the fence is enforcement.
|
|
14
14
|
* - The seeded history is SYNTHETIC and does not pretend otherwise: runs
|
|
@@ -33,8 +33,8 @@ import { addApprover, propose, approve } from "./scope.js";
|
|
|
33
33
|
import { legOf, routeDigestOf } from "./phase-routing.js";
|
|
34
34
|
import { acquire, release } from "./claim.js";
|
|
35
35
|
import { register } from "./runner.js";
|
|
36
|
-
import {
|
|
37
|
-
import { fileTaskProposal
|
|
36
|
+
import { createScheduledFlow } from "./flow-schedule.js";
|
|
37
|
+
import { fileTaskProposal } from "./proposal.js";
|
|
38
38
|
import { storeEvidence, budgetedStatJson, imageDimensions } from "./evidence.js";
|
|
39
39
|
import { parseProof, adjudicate } from "./proof.js";
|
|
40
40
|
import { sealVerificationReceipt } from "./verification-evidence.js";
|
|
@@ -58,7 +58,7 @@ const DEMO_PROFILE = {
|
|
|
58
58
|
function presentedRoute(store, taskRef, role, spend = { provider: "claude", model: null }) {
|
|
59
59
|
// A task with no scope presents the bare word `legacy` for the exact
|
|
60
60
|
// pair it spends as (atomic authority closure) — nothing opens unstamped.
|
|
61
|
-
const authority = store.routeAuthorityFor(taskRef, role) ?? store.routeAuthorityFor(taskRef, role,
|
|
61
|
+
const authority = store.routeAuthorityFor(taskRef, role) ?? store.routeAuthorityFor(taskRef, role, spend);
|
|
62
62
|
if (authority === null || !authority.ok)
|
|
63
63
|
throw new Error(`demo seed: ${authority === null ? "no task" : authority.problem}`);
|
|
64
64
|
return { route: authority.stamp };
|
|
@@ -419,11 +419,11 @@ export function seedDemo(store, repos, evidenceRoot, now) {
|
|
|
419
419
|
// --- needs-you: an approval waiting -----------------------------------
|
|
420
420
|
const planned = task("rotate-log-format", "Rotate the request-log format to JSON lines", repos.web, "Switch the request logger to JSON lines so the collector stops parsing free text. Keep the human console formatter for local dev. Migrate the two dashboards that grep the old format.");
|
|
421
421
|
const plannedRef = store.refFor("built-in", planned).id;
|
|
422
|
-
// An explainable route (v47): the operator
|
|
422
|
+
// An explainable route (v47): the operator sized the change large and
|
|
423
423
|
// overrode the reviewer, so the ceremony shows a recommended leg, a
|
|
424
424
|
// strong-tier leg, and an overridden leg side by side, with the runner's
|
|
425
425
|
// readiness per provider.
|
|
426
|
-
store.editTaskRoute(plannedRef, { by: "demo", authenticate: () => ({ ok: true }),
|
|
426
|
+
store.editTaskRoute(plannedRef, { by: "demo", authenticate: () => ({ ok: true }), size: { size: "large", risky: false } }, hoursAgo(3.5));
|
|
427
427
|
store.editTaskRoute(plannedRef, { by: "demo", authenticate: () => ({ ok: true }), override: { phase: "review", provider: "claude", model: "opus" } }, hoursAgo(3.2));
|
|
428
428
|
store.setPlanState(plannedRef, "drafted");
|
|
429
429
|
const plannerRun = store.startRun({
|
|
@@ -948,28 +948,18 @@ export function seedDemo(store, repos, evidenceRoot, now) {
|
|
|
948
948
|
store.cancelTask("design-tokens", hoursAgo(10), "the token package was superseded");
|
|
949
949
|
const held = task("migrate-billing", "Migrate billing exports to the new vendor", repos.api);
|
|
950
950
|
store.hold(store.refFor("built-in", held).id, "waiting on the vendor sandbox account", null, hoursAgo(12));
|
|
951
|
-
// --- a
|
|
952
|
-
const
|
|
953
|
-
name: "nightly-deps",
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
],
|
|
961
|
-
requirements: [],
|
|
962
|
-
schedule: "daily:03:30",
|
|
963
|
-
costCeilingUsd: null,
|
|
964
|
-
filedVia: "demo",
|
|
951
|
+
// --- a scheduled flow, waiting for someone to turn its schedule on -----
|
|
952
|
+
const nightly = createScheduledFlow(store, {
|
|
953
|
+
repo: realpathSync(repos.api), name: "Nightly dependency refresh", stem: "nightly-deps", schedule: "daily:03:30", by: "demo",
|
|
954
|
+
terms: {
|
|
955
|
+
goal: "Refresh the lockfile within existing ranges, run the suite, summarize anything notable.",
|
|
956
|
+
outOfScope: "No major version bumps.",
|
|
957
|
+
touches: [], requirements: [], budgetPerRunMicrousd: null, costCeilingUsd: null,
|
|
958
|
+
acceptance: [{ id: "c1", statement: "The full test suite passes against the refreshed lockfile.", how: null, evidence: ["check"] }],
|
|
959
|
+
},
|
|
965
960
|
}, hoursAgo(70));
|
|
966
|
-
if (!
|
|
967
|
-
throw new Error(`seed
|
|
968
|
-
approveRoutine(store, routine.id, "demo", hoursAgo(69), routine.digest, token);
|
|
969
|
-
// Two nightly slots since approval: one fires, the second records its
|
|
970
|
-
// single-flight skip honestly (the first instance is still open).
|
|
971
|
-
fireRoutine(store, routine.id, hoursAgo(45));
|
|
972
|
-
fireRoutine(store, routine.id, hoursAgo(21));
|
|
961
|
+
if (!nightly.ok)
|
|
962
|
+
throw new Error(`seed scheduled flow: ${nightly.message}`);
|
|
973
963
|
// --- the outer loop: an opened PR with observed checks ----------------
|
|
974
964
|
const pub = store.createPublicationIntent({
|
|
975
965
|
run: doneRun,
|
|
@@ -61,7 +61,6 @@ export function activeUpdateWork(db) {
|
|
|
61
61
|
runs: count("SELECT count(*) n FROM run WHERE outcome IS NULL"),
|
|
62
62
|
claims: count("SELECT count(*) n FROM claim WHERE released_at IS NULL"),
|
|
63
63
|
conversations: count("SELECT count(*) n FROM mate_turn WHERE state IN ('queued','running')") + count("SELECT count(*) n FROM chat_turn WHERE state IN ('queued','running')"),
|
|
64
|
-
sessions: count("SELECT count(*) n FROM held_session WHERE ended_at IS NULL"),
|
|
65
64
|
stopping: count("SELECT count(*) n FROM run_stop WHERE settled_at IS NULL"),
|
|
66
65
|
...activeCodingUpdateWork(db),
|
|
67
66
|
};
|
package/dist/desktop-update.d.ts
CHANGED
|
@@ -94,13 +94,13 @@ export declare function prepareDesktopUpdate(stateDir: string, installed: string
|
|
|
94
94
|
export declare function sqliteLock(file: string, busyTimeoutMs?: number): DatabaseSync | null;
|
|
95
95
|
export declare const installationLock: (j: UpdateJournal) => DatabaseSync | null;
|
|
96
96
|
export declare function desktopUpdateStatus(stateDir: string): {
|
|
97
|
-
error?: never;
|
|
98
97
|
active: boolean;
|
|
99
98
|
phase: string;
|
|
100
99
|
detail: string;
|
|
101
100
|
running: boolean;
|
|
102
101
|
canResume: boolean;
|
|
103
102
|
canCancel: boolean;
|
|
103
|
+
error?: never;
|
|
104
104
|
automaticRecovery?: never;
|
|
105
105
|
recoveryAttempts?: never;
|
|
106
106
|
recoveryRepairs?: never;
|
package/dist/desktop-update.js
CHANGED
|
@@ -295,8 +295,6 @@ export function plainLingering(fallbackRun, problem) {
|
|
|
295
295
|
return { run, on: `Run #${run} finished, but Toolroll can't check whether its folder is still in use`, action: null };
|
|
296
296
|
if (/may still be running|owned subprocess/.test(problem))
|
|
297
297
|
return { run, on: `Run #${run} finished, but ${pid ? `its process ${pid}` : "one of its processes"} is still running`, action: null };
|
|
298
|
-
if (/held supervisor/.test(problem))
|
|
299
|
-
return { run, on: `Run #${run} is still shutting down`, action: null };
|
|
300
298
|
if (/still open/.test(problem))
|
|
301
299
|
return { run, on: `Run #${run} is still running`, action: null };
|
|
302
300
|
return { run, on: `Run #${run} hasn't finished shutting down`, action: null };
|
|
@@ -844,7 +842,7 @@ export async function runDesktopUpdate(stateDir, hooks = {}) {
|
|
|
844
842
|
if (j.waiting && now().getTime() - Date.parse(j.waiting.since) >= LINGERING_LIMIT_MS)
|
|
845
843
|
stuck = lingering;
|
|
846
844
|
else
|
|
847
|
-
save(j, "draining", lingering ? `New work is paused. Waiting for run #${lingering.run}: ${lingeringWords(lingering)} No process is being killed.` : `Waiting for current work: ${active.runs} runs, ${active.claims} leases, ${active.conversations} chat requests, ${active.
|
|
845
|
+
save(j, "draining", lingering ? `New work is paused. Waiting for run #${lingering.run}: ${lingeringWords(lingering)} No process is being killed.` : `Waiting for current work: ${active.runs} runs, ${active.claims} leases, ${active.conversations} chat requests, ${active.stopping} shutdowns, ${active.coding} coding sessions, ${active.codingDeliveries} unconfirmed messages. Nothing is being cancelled.`);
|
|
848
846
|
}
|
|
849
847
|
finally {
|
|
850
848
|
checking.close();
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -90,7 +90,7 @@ export declare function taskReadinessBlocker(store: Store, taskRef: number, now:
|
|
|
90
90
|
* mode still stand. */
|
|
91
91
|
export declare function scopeApprovedForDispatch(store: Store, taskRef: number, now: Date): boolean;
|
|
92
92
|
/** The phase the ordinary dispatch loop would choose before it resolves an
|
|
93
|
-
* agent.
|
|
93
|
+
* agent. */
|
|
94
94
|
export declare function dispatchRoleFor(store: Store, taskRef: number, now: Date): "builder" | "planner" | "scout";
|
|
95
95
|
/** The first requirement this task fails, in words, or null. */
|
|
96
96
|
export declare function missingCapability(store: Store, taskRef: number, dispatchRepo: string | null, now: Date): string | null;
|
package/dist/dispatch.js
CHANGED
|
@@ -85,7 +85,7 @@ export function scopeApprovedForDispatch(store, taskRef, now) {
|
|
|
85
85
|
return row !== undefined;
|
|
86
86
|
}
|
|
87
87
|
/** The phase the ordinary dispatch loop would choose before it resolves an
|
|
88
|
-
* agent.
|
|
88
|
+
* agent. */
|
|
89
89
|
export function dispatchRoleFor(store, taskRef, now) {
|
|
90
90
|
const ref = store.refForId(taskRef);
|
|
91
91
|
const approved = scopeApprovedForDispatch(store, taskRef, now);
|
package/dist/dispose.d.ts
CHANGED
|
@@ -6,10 +6,9 @@ import { type ProofVerdict } from "./proof.js";
|
|
|
6
6
|
* Which road is disposing. 'tick' = the unattended loop: full task
|
|
7
7
|
* completion, strikes, quota, publication. 'standalone' = the one-off
|
|
8
8
|
* `build` command: run records only — no task state, no strikes, no
|
|
9
|
-
* publication (its historical shape).
|
|
10
|
-
* an attended session is still the task's real attempt.
|
|
9
|
+
* publication (its historical shape).
|
|
11
10
|
*/
|
|
12
|
-
export type DisposePolicy = "tick" | "standalone"
|
|
11
|
+
export type DisposePolicy = "tick" | "standalone";
|
|
13
12
|
export type DisposeContext = {
|
|
14
13
|
store: Store;
|
|
15
14
|
policy: DisposePolicy;
|
package/dist/dispose.js
CHANGED
|
@@ -5,16 +5,15 @@ import { originalTaskBase, focusedTestCommandSupported } from "./observations.js
|
|
|
5
5
|
* operations that actually END an attempt — sealing parks, accepting
|
|
6
6
|
* completions behind the completion fence, recording failures with their
|
|
7
7
|
* strikes and holds, opening publication intents — extracted from tick's
|
|
8
|
-
* and the standalone build command's finalizers into ONE place
|
|
9
|
-
*
|
|
10
|
-
* every other road uses. The two historical roads differ deliberately
|
|
8
|
+
* and the standalone build command's finalizers into ONE place. The two
|
|
9
|
+
* historical roads differ deliberately
|
|
11
10
|
* (the standalone command completes no task, strikes nothing, publishes
|
|
12
11
|
* nothing), and the policy record says so explicitly instead of forking
|
|
13
12
|
* the logic: behavior on both is byte-identical to what the callers
|
|
14
13
|
* inlined before.
|
|
15
14
|
*/
|
|
16
15
|
import { createHash } from "node:crypto";
|
|
17
|
-
import {
|
|
16
|
+
import { interruptIfStopped, release, finalize, } from "./claim.js";
|
|
18
17
|
import { bodyHashOf, publicationBody } from "./publish.js";
|
|
19
18
|
import { modeTermsFromJson } from "./modes.js";
|
|
20
19
|
import { evidenceRoot, readVerifiedArtifact, writeEvidenceFile } from "./evidence.js";
|
|
@@ -101,8 +100,8 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
101
100
|
if (result.ok && result.parked !== undefined) {
|
|
102
101
|
if (leaseId === undefined)
|
|
103
102
|
return { kind: "park-fenced" };
|
|
104
|
-
const sealed =
|
|
105
|
-
|
|
103
|
+
const sealed = finalize(store, leaseId, {
|
|
104
|
+
kind: "park",
|
|
106
105
|
runId,
|
|
107
106
|
taskId,
|
|
108
107
|
decision: result.parked.decision,
|
|
@@ -118,57 +117,6 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
118
117
|
return { kind: "parked", decisionId: sealed.decisionId };
|
|
119
118
|
}
|
|
120
119
|
if (result.ok) {
|
|
121
|
-
if (policy === "continuation") {
|
|
122
|
-
// The taskless success (v4 Q7 + v5 P5): run finished, claim released,
|
|
123
|
-
// publication intent under the SAME completion latch the ordinary
|
|
124
|
-
// road holds — and the parent task untouched in state, strikes,
|
|
125
|
-
// holds, and derived stats. All one transaction.
|
|
126
|
-
const sealed = store.transact(() => {
|
|
127
|
-
const latchOpen = store.mirrorAllowsCompletion(taskId);
|
|
128
|
-
store.finishRun(runId, {
|
|
129
|
-
outcome: result.noChange === true ? "no-change" : "built",
|
|
130
|
-
...(result.noChange === true ? { reason: "handoff" } : {}),
|
|
131
|
-
committed: result.committed,
|
|
132
|
-
now: clock(),
|
|
133
|
-
});
|
|
134
|
-
if (leaseId !== undefined)
|
|
135
|
-
release(store, leaseId, clock());
|
|
136
|
-
if (!latchOpen)
|
|
137
|
-
return { disowned: true };
|
|
138
|
-
if (result.noChange !== true && result.committed) {
|
|
139
|
-
const grant = store.publicationGrantFor(repo);
|
|
140
|
-
const headSha = store.getRun(runId)?.headRevision ?? null;
|
|
141
|
-
if (grant !== null &&
|
|
142
|
-
grant.publishOn !== "complete" &&
|
|
143
|
-
headSha !== null &&
|
|
144
|
-
headWithin(branch, grant.headPrefix) &&
|
|
145
|
-
(grant.selector === "all" || origin === "ours")) {
|
|
146
|
-
const intentId = store.createPublicationIntent({
|
|
147
|
-
run: runId,
|
|
148
|
-
taskRef,
|
|
149
|
-
githubRepo: grant.githubRepo,
|
|
150
|
-
remote: grant.remote,
|
|
151
|
-
base: grant.base,
|
|
152
|
-
head: branch,
|
|
153
|
-
headSha,
|
|
154
|
-
bodyHash: "",
|
|
155
|
-
draft: grant.draft,
|
|
156
|
-
}, clock());
|
|
157
|
-
const publication = store.publicationForRun(runId);
|
|
158
|
-
if (publication !== null) {
|
|
159
|
-
store.handle
|
|
160
|
-
.prepare("UPDATE publication SET body_hash = ? WHERE id = ?")
|
|
161
|
-
.run(bodyHashOf(publicationBody(store, publication)), intentId);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return { disowned: false };
|
|
166
|
-
});
|
|
167
|
-
store.clearQuota(runner, provider, model ?? "");
|
|
168
|
-
if (sealed.disowned)
|
|
169
|
-
return { kind: "disowned" };
|
|
170
|
-
return { kind: "built", committed: result.committed, noChange: result.noChange === true };
|
|
171
|
-
}
|
|
172
120
|
if (policy === "standalone") {
|
|
173
121
|
store.finishRun(runId, {
|
|
174
122
|
outcome: result.noChange === true ? "no-change" : "built",
|
|
@@ -184,7 +132,7 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
184
132
|
// around the fenced release, the run's outcome, and the publication
|
|
185
133
|
// intent, so "done" and "this must reach a PR" cannot come apart.
|
|
186
134
|
const sealed = store.transact(() => {
|
|
187
|
-
const fence =
|
|
135
|
+
const fence = finalize(store, leaseId, { kind: "complete", state: "done", now: clock() });
|
|
188
136
|
if (!fence.ok)
|
|
189
137
|
return fence;
|
|
190
138
|
// The disowned arm (external dispatch, v4 §24): the tracker closed
|
|
@@ -264,14 +212,14 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
264
212
|
// THE ALREADY-SEALED ENDINGS (adaptive execution plans). Every other arm
|
|
265
213
|
// below ends the attempt itself — releases the claim, writes the run's
|
|
266
214
|
// outcome, counts what it costs. These two arrive with all of that
|
|
267
|
-
// already done: `
|
|
215
|
+
// already done: `finalize` (a `revision` ending) released the lease, appended the
|
|
268
216
|
// ledger row, placed the hold when one was owed, finished the run, and
|
|
269
217
|
// paged, all inside ONE fenced transaction — the planner road's shape,
|
|
270
218
|
// where claim.ts owns the ending and dispose only reports it.
|
|
271
219
|
//
|
|
272
220
|
// So this branch exists to do NOTHING, deliberately, and it is placed
|
|
273
|
-
// above the policy arms so that every road — tick
|
|
274
|
-
//
|
|
221
|
+
// above the policy arms so that every road — tick and standalone —
|
|
222
|
+
// skips them alike.
|
|
275
223
|
//
|
|
276
224
|
// Falling through instead would be wrong twice over. `release()` would
|
|
277
225
|
// survive it (an already-released lease whose `released_by` is
|
|
@@ -284,20 +232,10 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
284
232
|
if (result.reason === "plan-revised" || result.reason === "plan-revision-blocked") {
|
|
285
233
|
return { kind: "skipped", reason: result.reason };
|
|
286
234
|
}
|
|
287
|
-
if (policy === "continuation") {
|
|
288
|
-
// The taskless failure (v4 Q7): the run says what happened, the claim
|
|
289
|
-
// releases — NO strikes, NO holds, NO done→failed demotion; three
|
|
290
|
-
// failed continuations still leave the parent exactly as it finished.
|
|
291
|
-
const outcome = result.reason === "fenced" ? "refused" : "failed";
|
|
292
|
-
store.finishRun(runId, { outcome, reason: result.reason, now: clock() });
|
|
293
|
-
if (leaseId !== undefined)
|
|
294
|
-
release(store, leaseId, clock());
|
|
295
|
-
return { kind: "recorded", outcome };
|
|
296
|
-
}
|
|
297
235
|
if (policy === "standalone") {
|
|
298
236
|
if (result.reason === "malformed-decision" && leaseId !== undefined) {
|
|
299
|
-
const sealed =
|
|
300
|
-
|
|
237
|
+
const sealed = finalize(store, leaseId, {
|
|
238
|
+
kind: "malformed",
|
|
301
239
|
runId,
|
|
302
240
|
taskId,
|
|
303
241
|
problems: result.problems ?? [],
|
|
@@ -309,19 +247,6 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
309
247
|
store.finishRun(runId, { outcome, reason: result.reason, now: clock() });
|
|
310
248
|
return { kind: "recorded", outcome };
|
|
311
249
|
}
|
|
312
|
-
// The attended refusal family (v28 sweep): typed, no strike, release and
|
|
313
|
-
// move on — the pre-claim gates make every one a rare race, and the
|
|
314
|
-
// invariant arm they used to fall through is for BUGS, not races.
|
|
315
|
-
if (result.reason === "attended-only" ||
|
|
316
|
-
result.reason === "attended-held" ||
|
|
317
|
-
result.reason === "stale-authorization" ||
|
|
318
|
-
result.reason === "session-cap" ||
|
|
319
|
-
result.reason === "run-held") {
|
|
320
|
-
if (leaseId !== undefined)
|
|
321
|
-
release(store, leaseId, clock());
|
|
322
|
-
store.finishRun(runId, { outcome: "refused", reason: result.reason, now: clock() });
|
|
323
|
-
return { kind: "skipped", reason: result.reason };
|
|
324
|
-
}
|
|
325
250
|
if (result.reason === "unapproved" || result.reason === "scope-changed") {
|
|
326
251
|
// Approval drifted between the prefilter and the builder's own gate.
|
|
327
252
|
if (leaseId !== undefined)
|
|
@@ -337,8 +262,8 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
337
262
|
if (result.reason === "malformed-decision") {
|
|
338
263
|
if (leaseId === undefined)
|
|
339
264
|
return { kind: "malformed", sealed: false };
|
|
340
|
-
const sealed =
|
|
341
|
-
|
|
265
|
+
const sealed = finalize(store, leaseId, {
|
|
266
|
+
kind: "malformed",
|
|
342
267
|
runId,
|
|
343
268
|
taskId,
|
|
344
269
|
problems: result.problems ?? [],
|
|
@@ -349,7 +274,7 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
349
274
|
// A stop no operator asked for is the SERVICE stopping (the fence above
|
|
350
275
|
// already settled every task stop): hand the attempt back, never strike it.
|
|
351
276
|
if (result.reason === "stopped" && policy === "tick" && leaseId !== undefined && store.getRun(runId)?.role === "builder") {
|
|
352
|
-
const handed =
|
|
277
|
+
const handed = finalize(store, leaseId, { kind: "hand-back", runId, taskId, message: result.message, now: clock() });
|
|
353
278
|
return { kind: "handed-back", requeued: handed.requeued };
|
|
354
279
|
}
|
|
355
280
|
if (TICK_FAILURE_REASONS.has(result.reason)) {
|
|
@@ -376,8 +301,8 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
376
301
|
store.finishRun(runId, { outcome: "failed", reason: result.reason, now: clock() });
|
|
377
302
|
return { kind: "failed", failureClass, disposition: null, strikes: null, sealed: false };
|
|
378
303
|
}
|
|
379
|
-
const sealed =
|
|
380
|
-
|
|
304
|
+
const sealed = finalize(store, leaseId, {
|
|
305
|
+
kind: "failure",
|
|
381
306
|
runId,
|
|
382
307
|
taskId,
|
|
383
308
|
failureClass,
|
|
@@ -531,7 +456,7 @@ export function regateTask(store, taskId, now, approval) {
|
|
|
531
456
|
return store.transact(() => {
|
|
532
457
|
const proposed = propose(store, {
|
|
533
458
|
taskId, goal: scope.goal, outOfScope: scope.outOfScope, touches: scope.touches, budgetMicrousd: scope.budgetMicrousd,
|
|
534
|
-
acceptance: scope.acceptance, qualityMode: scope.qualityMode ?? "default",
|
|
459
|
+
acceptance: scope.acceptance, qualityMode: scope.qualityMode ?? "default",
|
|
535
460
|
candidate: head, now,
|
|
536
461
|
});
|
|
537
462
|
let sealed = false;
|
package/dist/envelope.js
CHANGED
|
@@ -55,12 +55,9 @@ export const DOCUMENTED_REASONS = [
|
|
|
55
55
|
"unapproved", // no person has agreed to the scope yet
|
|
56
56
|
"reserved", // the task belongs to another worker's queue
|
|
57
57
|
"external", // a tracker mirror is not dispatchable (see detail)
|
|
58
|
-
"contest-open", // a tournament is running; a person picks
|
|
59
|
-
"all-lanes-raceable", // every compared agent could hold a real budget — race them instead
|
|
60
58
|
"provider-unattested", // the provider binary is outside its attested range
|
|
61
59
|
"daily-runs", // the active mode's daily run cap is spent
|
|
62
60
|
"daily-dollars", // the active mode's daily measured-spend rail is reached
|
|
63
|
-
"steering-pending", // a steering note is waiting; contests refuse
|
|
64
61
|
"not-ready", // blocked, held, or otherwise not dispatchable
|
|
65
62
|
"not-leased", // the lease does not exist or already ended
|
|
66
63
|
"not-yours", // held or reserved by someone else
|
package/dist/exec.d.ts
CHANGED
|
@@ -226,67 +226,3 @@ export declare function runStreamJsonl(file: string, args: readonly string[], op
|
|
|
226
226
|
*/
|
|
227
227
|
export declare function runGeminiStreamJsonl(file: string, args: readonly string[], options?: RunOptions): Promise<ExecResult>;
|
|
228
228
|
export declare function runClaudeStreamJsonl(file: string, args: readonly string[], options?: RunOptions): Promise<ExecResult>;
|
|
229
|
-
/**
|
|
230
|
-
* Where the supervisor script lives: a plain-.mjs sibling of this module in
|
|
231
|
-
* BOTH layouts (src/ under tsx, dist/ after build — postbuild copies it).
|
|
232
|
-
*/
|
|
233
|
-
export declare function supervisorPath(): string;
|
|
234
|
-
/**
|
|
235
|
-
* Darwin's sun_path is 104 bytes including the terminator; Linux allows a
|
|
236
|
-
* hair more. Refusing at spawn beats failing inside bind() with a raw
|
|
237
|
-
* ENAMETOOLONG after custody was already recorded.
|
|
238
|
-
*/
|
|
239
|
-
export declare const HELD_SOCKET_PATH_LIMIT = 103;
|
|
240
|
-
export declare function heldSocketPathProblem(path: string): string | null;
|
|
241
|
-
export type HeldSessionEvents = {
|
|
242
|
-
/** The nth system/init observed (1-based) — acceptance of the nth written turn. */
|
|
243
|
-
onTurnInit?: (seq: number) => void;
|
|
244
|
-
/** The nth primary-origin result (1-based) — settlement material for the nth turn. */
|
|
245
|
-
onTurnResult?: (seq: number, event: Record<string, unknown>) => void;
|
|
246
|
-
/** Every parsed stream event, arc-1 isolation rules unchanged. */
|
|
247
|
-
onStreamEvent?: (event: Record<string, unknown>) => void;
|
|
248
|
-
onSessionId?: (id: string) => void;
|
|
249
|
-
/** The supervisor exited: the hold is over, however it ended. */
|
|
250
|
-
onExit?: (info: {
|
|
251
|
-
code: number | null;
|
|
252
|
-
}) => void;
|
|
253
|
-
};
|
|
254
|
-
export type HeldSessionHandle = {
|
|
255
|
-
supervisorPid: number | null;
|
|
256
|
-
agentPgid: number | null;
|
|
257
|
-
/** One turn as a stream-json line. False = the pipe is gone; nothing was written. */
|
|
258
|
-
writeTurn(json: string): boolean;
|
|
259
|
-
/** The EOF road: the supervisor fences (EOF → grace → group kill) on its own. */
|
|
260
|
-
endInput(): void;
|
|
261
|
-
/** SIGTERM the supervisor — the same fence, for expiry/revocation/lapse. */
|
|
262
|
-
terminate(): void;
|
|
263
|
-
/** Last resort only: SIGKILL the supervisor itself (can orphan the agent). */
|
|
264
|
-
killHard(): void;
|
|
265
|
-
exited: Promise<{
|
|
266
|
-
code: number | null;
|
|
267
|
-
}>;
|
|
268
|
-
};
|
|
269
|
-
export type HeldSessionStart = {
|
|
270
|
-
ok: true;
|
|
271
|
-
handle: HeldSessionHandle;
|
|
272
|
-
} | {
|
|
273
|
-
ok: false;
|
|
274
|
-
reason: "socket-path" | "spawn-failed" | "ready-timeout";
|
|
275
|
-
message: string;
|
|
276
|
-
};
|
|
277
|
-
/**
|
|
278
|
-
* Hold one claude process open across stream-json turns, under a
|
|
279
|
-
* supervisor whose parenthood makes the eventual kill provable. Returns
|
|
280
|
-
* once the supervisor's control frame settles the spawn either way.
|
|
281
|
-
*
|
|
282
|
-
* No timers live here: the per-turn wall deadline, liveness lapse, and
|
|
283
|
-
* absolute expiry are the coordinator's exact timers (v2 S1d) — this
|
|
284
|
-
* transport only moves bytes and counts the protocol's own marks.
|
|
285
|
-
*/
|
|
286
|
-
export declare function startClaudeHeldSession(file: string, args: readonly string[], options: RunOptions & {
|
|
287
|
-
socketPath: string;
|
|
288
|
-
cookie: string;
|
|
289
|
-
graceMs?: number;
|
|
290
|
-
events?: HeldSessionEvents;
|
|
291
|
-
readyTimeoutMs?: number;
|
|
292
|
-
}): Promise<HeldSessionStart>;
|