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
package/dist/exec.js
CHANGED
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
import { linuxFenceAvailable, linuxFenced, macosFenceAvailable, macosFenced } from "./agent-fence.js";
|
|
10
10
|
import { execFile, spawn } from "node:child_process";
|
|
11
11
|
import { realpathSync } from "node:fs";
|
|
12
|
-
import {
|
|
13
|
-
import { observeProcessTree, stopProcessTree, readProcessObservationFailure } from "./process-tree.js";
|
|
12
|
+
import { observeProcessTree, stopProcessTree } from "./process-tree.js";
|
|
14
13
|
import { jsonlDiscriminants } from "./jsonl-discriminants.js";
|
|
15
14
|
import { createContainer, currentContainment } from "./containment.js";
|
|
16
15
|
import { claudeLimitsOf, noteLimits } from "./provider-limits.js";
|
|
@@ -63,8 +62,8 @@ function rejectedChild(error, child) {
|
|
|
63
62
|
return error instanceof SpawnCustodyFailure ? error.child : child;
|
|
64
63
|
}
|
|
65
64
|
/**
|
|
66
|
-
* The ONE spawn road for containable children (buffered
|
|
67
|
-
*
|
|
65
|
+
* The ONE spawn road for containable children (buffered and streaming
|
|
66
|
+
* transports): consult the pinned policy, make this spawn's OS
|
|
68
67
|
* object, spawn the target INSIDE it, and register the object beside the
|
|
69
68
|
* handle. Under observed containment this is exactly the old spawn. A
|
|
70
69
|
* required policy that cannot be met throws ContainmentRefusal here —
|
|
@@ -274,15 +273,6 @@ export function terminateOwnedProcesses(owner) {
|
|
|
274
273
|
}
|
|
275
274
|
return terminated;
|
|
276
275
|
}
|
|
277
|
-
/**
|
|
278
|
-
* Live held-session SUPERVISORS (v6 W7). Not in liveProviders: SIGKILLing a
|
|
279
|
-
* supervisor's group would orphan the agent living in its own fresh group.
|
|
280
|
-
* The sweep's contract here is SIGTERM first — the supervisor's handler
|
|
281
|
-
* fences its child (EOF → grace → group SIGKILL) — and SIGKILL only on a
|
|
282
|
-
* repeat sweep, which is the one road that can orphan an agent and is why
|
|
283
|
-
* it is the escalation, never the opener.
|
|
284
|
-
*/
|
|
285
|
-
const heldSupervisors = new Map();
|
|
286
276
|
/** SIGKILL the child's whole process group; fall back to the child alone. */
|
|
287
277
|
function killGroup(child) {
|
|
288
278
|
const container = containers.get(child);
|
|
@@ -346,28 +336,6 @@ export function terminateLiveProviders() {
|
|
|
346
336
|
killGroup(child);
|
|
347
337
|
terminated += 1;
|
|
348
338
|
}
|
|
349
|
-
// Held supervisors take the graceful road first (v6 W7): SIGTERM makes
|
|
350
|
-
// the supervisor fence its own agent race-free through the handle only
|
|
351
|
-
// it holds. A second sweep escalates to SIGKILL — the documented
|
|
352
|
-
// residual hole, reachable only by double-escalation, with the startup
|
|
353
|
-
// orphan fence paging for anything that lingers.
|
|
354
|
-
for (const [supervisor, stage] of heldSupervisors) {
|
|
355
|
-
try {
|
|
356
|
-
if (stage === "fresh") {
|
|
357
|
-
supervisor.kill("SIGTERM");
|
|
358
|
-
heldSupervisors.set(supervisor, "termed");
|
|
359
|
-
}
|
|
360
|
-
else {
|
|
361
|
-
// With a native OS object the escalation reaches the agent too:
|
|
362
|
-
// the object's kill is the one road that cannot orphan it.
|
|
363
|
-
killGroup(supervisor);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
catch {
|
|
367
|
-
// Already gone.
|
|
368
|
-
}
|
|
369
|
-
terminated += 1;
|
|
370
|
-
}
|
|
371
339
|
return terminated;
|
|
372
340
|
}
|
|
373
341
|
/** The one place a child's environment is decided (audit IV-5). */
|
|
@@ -1555,324 +1523,3 @@ export function runClaudeStreamJsonl(file, args, options = {}) {
|
|
|
1555
1523
|
child.on("close", code => finish(code));
|
|
1556
1524
|
});
|
|
1557
1525
|
}
|
|
1558
|
-
// ---- the held session transport (Parity II Phase 2, spec v2 S0a + v6 W9) ----
|
|
1559
|
-
/**
|
|
1560
|
-
* Where the supervisor script lives: a plain-.mjs sibling of this module in
|
|
1561
|
-
* BOTH layouts (src/ under tsx, dist/ after build — postbuild copies it).
|
|
1562
|
-
*/
|
|
1563
|
-
export function supervisorPath() {
|
|
1564
|
-
return fileURLToPath(new URL("./supervisor.mjs", import.meta.url));
|
|
1565
|
-
}
|
|
1566
|
-
/**
|
|
1567
|
-
* Darwin's sun_path is 104 bytes including the terminator; Linux allows a
|
|
1568
|
-
* hair more. Refusing at spawn beats failing inside bind() with a raw
|
|
1569
|
-
* ENAMETOOLONG after custody was already recorded.
|
|
1570
|
-
*/
|
|
1571
|
-
export const HELD_SOCKET_PATH_LIMIT = 103;
|
|
1572
|
-
export function heldSocketPathProblem(path) {
|
|
1573
|
-
const bytes = Buffer.byteLength(path, "utf8");
|
|
1574
|
-
if (bytes > HELD_SOCKET_PATH_LIMIT) {
|
|
1575
|
-
return `the control socket path is ${bytes} bytes — the platform limit is ${HELD_SOCKET_PATH_LIMIT}: ${path}`;
|
|
1576
|
-
}
|
|
1577
|
-
return null;
|
|
1578
|
-
}
|
|
1579
|
-
/**
|
|
1580
|
-
* Hold one claude process open across stream-json turns, under a
|
|
1581
|
-
* supervisor whose parenthood makes the eventual kill provable. Returns
|
|
1582
|
-
* once the supervisor's control frame settles the spawn either way.
|
|
1583
|
-
*
|
|
1584
|
-
* No timers live here: the per-turn wall deadline, liveness lapse, and
|
|
1585
|
-
* absolute expiry are the coordinator's exact timers (v2 S1d) — this
|
|
1586
|
-
* transport only moves bytes and counts the protocol's own marks.
|
|
1587
|
-
*/
|
|
1588
|
-
export function startClaudeHeldSession(file, args, options) {
|
|
1589
|
-
const pathProblem = heldSocketPathProblem(options.socketPath);
|
|
1590
|
-
if (pathProblem !== null) {
|
|
1591
|
-
return Promise.resolve({ ok: false, reason: "socket-path", message: pathProblem });
|
|
1592
|
-
}
|
|
1593
|
-
const childEnv = resolveChildEnv(options) ?? { ...process.env };
|
|
1594
|
-
const events = options.events ?? {};
|
|
1595
|
-
return new Promise(resolveStart => {
|
|
1596
|
-
let supervisor;
|
|
1597
|
-
let container = null;
|
|
1598
|
-
let attachFailure = null;
|
|
1599
|
-
try {
|
|
1600
|
-
// The supervisor spawns INSIDE the OS object, so the agent it holds
|
|
1601
|
-
// and every tool the agent launches are members too: one kill reaches
|
|
1602
|
-
// the whole hold, and the object's empty state proves the hold over.
|
|
1603
|
-
const spawned = spawnContained(process.execPath, [supervisorPath(), file, ...args], {
|
|
1604
|
-
cwd: options.cwd,
|
|
1605
|
-
env: {
|
|
1606
|
-
...childEnv,
|
|
1607
|
-
SO_HELD_SOCKET: options.socketPath,
|
|
1608
|
-
SO_HELD_COOKIE: options.cookie,
|
|
1609
|
-
SO_HELD_GRACE_MS: String(options.graceMs ?? 10_000),
|
|
1610
|
-
SO_HELD_DIAGNOSTIC_FD: currentContainment().mode === "observed" ? "3" : "",
|
|
1611
|
-
},
|
|
1612
|
-
stdio: currentContainment().mode === "observed" ? ["pipe", "pipe", "pipe", "pipe"] : ["pipe", "pipe", "pipe"],
|
|
1613
|
-
detached: false,
|
|
1614
|
-
}, options.owner ?? "held", {
|
|
1615
|
-
beforeSpawn: options.beforeSpawn,
|
|
1616
|
-
onSpawn: options.onSpawn,
|
|
1617
|
-
onSpawnFailed: options.onSpawnFailed,
|
|
1618
|
-
onContainer: options.onContainer,
|
|
1619
|
-
onContainerEmpty: options.onContainerEmpty,
|
|
1620
|
-
onUnknown: options.onUnknown,
|
|
1621
|
-
fence: options.fence,
|
|
1622
|
-
});
|
|
1623
|
-
supervisor = spawned.child;
|
|
1624
|
-
container = spawned.container;
|
|
1625
|
-
void spawned.attached.then(outcome => { if (!outcome.ok)
|
|
1626
|
-
attachFailure = outcome.detail; });
|
|
1627
|
-
if (container === null)
|
|
1628
|
-
observeProcessTree(supervisor, options);
|
|
1629
|
-
}
|
|
1630
|
-
catch (error) {
|
|
1631
|
-
const left = rejectedChild(error, supervisor);
|
|
1632
|
-
const refused = () => resolveStart({ ok: false, reason: "spawn-failed", message: String(error instanceof SpawnCustodyFailure ? error.reason : error) });
|
|
1633
|
-
if (left === undefined)
|
|
1634
|
-
refused();
|
|
1635
|
-
else {
|
|
1636
|
-
left.once("close", refused);
|
|
1637
|
-
left.on("error", () => { });
|
|
1638
|
-
killGroup(left);
|
|
1639
|
-
}
|
|
1640
|
-
return;
|
|
1641
|
-
}
|
|
1642
|
-
// This pipe belongs to the supervisor, not the agent's relayed stream.
|
|
1643
|
-
// Native containment uses its existing attachment descriptors instead.
|
|
1644
|
-
if (container === null) {
|
|
1645
|
-
const diagnostics = supervisor.stdio[3];
|
|
1646
|
-
let buffer = "", reportedFault = false;
|
|
1647
|
-
const diagnosticFault = () => {
|
|
1648
|
-
if (reportedFault)
|
|
1649
|
-
return;
|
|
1650
|
-
reportedFault = true;
|
|
1651
|
-
// This channel carries diagnostics only. Its failure cannot invent
|
|
1652
|
-
// lost child identity; genuine supervisor uncertainty still exits126.
|
|
1653
|
-
try {
|
|
1654
|
-
options.onObservationFailure?.({ phase: "supervisor", operation: "diagnostic-read", code: "SUPERVISOR_DIAGNOSTIC", rootPid: supervisor.pid ?? null, at: new Date().toISOString(), identityUnknown: false });
|
|
1655
|
-
}
|
|
1656
|
-
catch { }
|
|
1657
|
-
};
|
|
1658
|
-
if (diagnostics !== null && diagnostics !== undefined && "on" in diagnostics) {
|
|
1659
|
-
diagnostics.on("data", (chunk) => {
|
|
1660
|
-
buffer += chunk.toString("utf8");
|
|
1661
|
-
if (buffer.length > 16_384) {
|
|
1662
|
-
buffer = "";
|
|
1663
|
-
diagnosticFault();
|
|
1664
|
-
return;
|
|
1665
|
-
}
|
|
1666
|
-
let cut;
|
|
1667
|
-
while ((cut = buffer.indexOf("\n")) !== -1) {
|
|
1668
|
-
const line = buffer.slice(0, cut);
|
|
1669
|
-
buffer = buffer.slice(cut + 1);
|
|
1670
|
-
let failure = null;
|
|
1671
|
-
try {
|
|
1672
|
-
failure = readProcessObservationFailure(JSON.parse(line));
|
|
1673
|
-
}
|
|
1674
|
-
catch { }
|
|
1675
|
-
if (failure === null) {
|
|
1676
|
-
diagnosticFault();
|
|
1677
|
-
continue;
|
|
1678
|
-
}
|
|
1679
|
-
try {
|
|
1680
|
-
options.onObservationFailure?.(failure);
|
|
1681
|
-
}
|
|
1682
|
-
catch { /* No diagnostic can certify exit or clear an existing witness. */ }
|
|
1683
|
-
}
|
|
1684
|
-
});
|
|
1685
|
-
diagnostics.on("error", diagnosticFault);
|
|
1686
|
-
diagnostics.on("end", () => { if (buffer !== "")
|
|
1687
|
-
diagnosticFault(); });
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
heldSupervisors.set(supervisor, "fresh");
|
|
1691
|
-
let settledStart = false;
|
|
1692
|
-
let agentPgid = null;
|
|
1693
|
-
let initSeq = 0;
|
|
1694
|
-
let resultSeq = 0;
|
|
1695
|
-
let sessionSeen = false;
|
|
1696
|
-
let partial = "";
|
|
1697
|
-
let exitResolve = () => { };
|
|
1698
|
-
const exited = new Promise(pass => {
|
|
1699
|
-
exitResolve = pass;
|
|
1700
|
-
});
|
|
1701
|
-
const readyTimer = setTimeout(() => {
|
|
1702
|
-
if (settledStart)
|
|
1703
|
-
return;
|
|
1704
|
-
settledStart = true;
|
|
1705
|
-
try {
|
|
1706
|
-
killGroup(supervisor);
|
|
1707
|
-
}
|
|
1708
|
-
catch {
|
|
1709
|
-
// Already gone.
|
|
1710
|
-
}
|
|
1711
|
-
resolveStart({ ok: false, reason: "ready-timeout", message: "the supervisor never sent its control frame" });
|
|
1712
|
-
}, options.readyTimeoutMs ?? 10_000);
|
|
1713
|
-
const handle = {
|
|
1714
|
-
supervisorPid: null,
|
|
1715
|
-
agentPgid: null,
|
|
1716
|
-
writeTurn(json) {
|
|
1717
|
-
const stdin = supervisor.stdin;
|
|
1718
|
-
if (stdin === null || stdin.destroyed || !stdin.writable)
|
|
1719
|
-
return false;
|
|
1720
|
-
try {
|
|
1721
|
-
stdin.write(json.endsWith("\n") ? json : `${json}\n`);
|
|
1722
|
-
return true;
|
|
1723
|
-
}
|
|
1724
|
-
catch {
|
|
1725
|
-
return false;
|
|
1726
|
-
}
|
|
1727
|
-
},
|
|
1728
|
-
endInput() {
|
|
1729
|
-
try {
|
|
1730
|
-
supervisor.stdin?.end();
|
|
1731
|
-
}
|
|
1732
|
-
catch {
|
|
1733
|
-
// Already closed.
|
|
1734
|
-
}
|
|
1735
|
-
},
|
|
1736
|
-
terminate() {
|
|
1737
|
-
try {
|
|
1738
|
-
supervisor.kill("SIGTERM");
|
|
1739
|
-
heldSupervisors.set(supervisor, "termed");
|
|
1740
|
-
}
|
|
1741
|
-
catch {
|
|
1742
|
-
// Already gone.
|
|
1743
|
-
}
|
|
1744
|
-
},
|
|
1745
|
-
killHard() {
|
|
1746
|
-
try {
|
|
1747
|
-
killGroup(supervisor);
|
|
1748
|
-
}
|
|
1749
|
-
catch {
|
|
1750
|
-
// Already gone.
|
|
1751
|
-
}
|
|
1752
|
-
},
|
|
1753
|
-
exited,
|
|
1754
|
-
};
|
|
1755
|
-
const keep = (line) => {
|
|
1756
|
-
if (line.length > JSONL_LINE_CAP)
|
|
1757
|
-
return;
|
|
1758
|
-
let event;
|
|
1759
|
-
try {
|
|
1760
|
-
event = JSON.parse(line);
|
|
1761
|
-
}
|
|
1762
|
-
catch {
|
|
1763
|
-
return;
|
|
1764
|
-
}
|
|
1765
|
-
if (event === null || typeof event !== "object")
|
|
1766
|
-
return;
|
|
1767
|
-
// The control frame settles the start, exactly once, before any
|
|
1768
|
-
// agent byte — the two-hop handshake (v6 W9).
|
|
1769
|
-
if (!settledStart && typeof event["so_supervisor"] === "string") {
|
|
1770
|
-
settledStart = true;
|
|
1771
|
-
clearTimeout(readyTimer);
|
|
1772
|
-
if (event["so_supervisor"] === "ready") {
|
|
1773
|
-
agentPgid = typeof event["agentPgid"] === "number" ? event["agentPgid"] : null;
|
|
1774
|
-
handle.agentPgid = agentPgid;
|
|
1775
|
-
handle.supervisorPid =
|
|
1776
|
-
typeof event["supervisorPid"] === "number" ? event["supervisorPid"] : (supervisor.pid ?? null);
|
|
1777
|
-
resolveStart({ ok: true, handle });
|
|
1778
|
-
}
|
|
1779
|
-
else {
|
|
1780
|
-
const message = typeof event["message"] === "string" ? event["message"] : "spawn failed";
|
|
1781
|
-
resolveStart({ ok: false, reason: "spawn-failed", message });
|
|
1782
|
-
}
|
|
1783
|
-
return;
|
|
1784
|
-
}
|
|
1785
|
-
if (typeof event["so_supervisor"] === "string")
|
|
1786
|
-
return; // never two frames; drop strays
|
|
1787
|
-
const type = String(event["type"] ?? "");
|
|
1788
|
-
if (type === "system" && String(event["subtype"] ?? "") === "init") {
|
|
1789
|
-
initSeq += 1;
|
|
1790
|
-
if (!sessionSeen && events.onSessionId !== undefined) {
|
|
1791
|
-
const id = transportSessionId(event["session_id"]);
|
|
1792
|
-
if (id !== null) {
|
|
1793
|
-
sessionSeen = true;
|
|
1794
|
-
try {
|
|
1795
|
-
events.onSessionId(id);
|
|
1796
|
-
}
|
|
1797
|
-
catch {
|
|
1798
|
-
// A registry that cannot be written must not kill the turn.
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
}
|
|
1802
|
-
try {
|
|
1803
|
-
events.onTurnInit?.(initSeq);
|
|
1804
|
-
}
|
|
1805
|
-
catch {
|
|
1806
|
-
// Observational.
|
|
1807
|
-
}
|
|
1808
|
-
}
|
|
1809
|
-
else if (type === "rate_limit_event") {
|
|
1810
|
-
noteLimits(claudeLimitsOf(event));
|
|
1811
|
-
}
|
|
1812
|
-
else if (type === "result" && primaryResultOrigin(event)) {
|
|
1813
|
-
resultSeq += 1;
|
|
1814
|
-
try {
|
|
1815
|
-
events.onTurnResult?.(resultSeq, event);
|
|
1816
|
-
}
|
|
1817
|
-
catch {
|
|
1818
|
-
// Observational.
|
|
1819
|
-
}
|
|
1820
|
-
}
|
|
1821
|
-
try {
|
|
1822
|
-
events.onStreamEvent?.(event);
|
|
1823
|
-
}
|
|
1824
|
-
catch {
|
|
1825
|
-
// Observational only, arc-1 rule unchanged.
|
|
1826
|
-
}
|
|
1827
|
-
};
|
|
1828
|
-
supervisor.stdout?.setEncoding("utf8");
|
|
1829
|
-
supervisor.stdout?.on("data", (chunk) => {
|
|
1830
|
-
partial += chunk;
|
|
1831
|
-
let cut = partial.indexOf("\n");
|
|
1832
|
-
while (cut !== -1) {
|
|
1833
|
-
keep(partial.slice(0, cut));
|
|
1834
|
-
partial = partial.slice(cut + 1);
|
|
1835
|
-
cut = partial.indexOf("\n");
|
|
1836
|
-
}
|
|
1837
|
-
if (partial.length > JSONL_LINE_CAP * 2)
|
|
1838
|
-
partial = partial.slice(-JSONL_LINE_CAP);
|
|
1839
|
-
});
|
|
1840
|
-
supervisor.on("error", error => {
|
|
1841
|
-
heldSupervisors.delete(supervisor);
|
|
1842
|
-
if (!settledStart) {
|
|
1843
|
-
settledStart = true;
|
|
1844
|
-
clearTimeout(readyTimer);
|
|
1845
|
-
resolveStart({ ok: false, reason: "spawn-failed", message: String(error) });
|
|
1846
|
-
}
|
|
1847
|
-
exitResolve({ code: null });
|
|
1848
|
-
});
|
|
1849
|
-
supervisor.on("close", code => {
|
|
1850
|
-
heldSupervisors.delete(supervisor);
|
|
1851
|
-
if (partial.trim() !== "")
|
|
1852
|
-
keep(partial);
|
|
1853
|
-
// The POSIX supervisor could not prove its observed tree drained.
|
|
1854
|
-
// Persist uncertainty before any onExit handler can release custody.
|
|
1855
|
-
if (container === null && code === CONTAINMENT_REFUSED_CODE) {
|
|
1856
|
-
try {
|
|
1857
|
-
options.onUnknown?.();
|
|
1858
|
-
}
|
|
1859
|
-
catch { /* the observer must not prevent settlement */ }
|
|
1860
|
-
}
|
|
1861
|
-
const settlement = container === null ? Promise.resolve(true) : settleContainer(container, options);
|
|
1862
|
-
void settlement.then(empty => {
|
|
1863
|
-
if (!settledStart) {
|
|
1864
|
-
settledStart = true;
|
|
1865
|
-
clearTimeout(readyTimer);
|
|
1866
|
-
resolveStart({ ok: false, reason: "spawn-failed", message: attachFailure ?? "the supervisor exited before its control frame" });
|
|
1867
|
-
}
|
|
1868
|
-
try {
|
|
1869
|
-
events.onExit?.({ code: empty ? code : CONTAINMENT_REFUSED_CODE });
|
|
1870
|
-
}
|
|
1871
|
-
catch {
|
|
1872
|
-
// Observational.
|
|
1873
|
-
}
|
|
1874
|
-
exitResolve({ code: empty ? code : CONTAINMENT_REFUSED_CODE });
|
|
1875
|
-
});
|
|
1876
|
-
});
|
|
1877
|
-
});
|
|
1878
|
-
}
|
package/dist/exhaustion.d.ts
CHANGED
|
@@ -1,93 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* How a finished attempt ended, classified where the evidence still exists
|
|
3
|
+
* (the provider adapter's structural terminal record), never in disposal.
|
|
4
|
+
* The one class that changes what happens next is `auth-expired`: a sign-in
|
|
5
|
+
* or API key that no longer works pauses dispatch for that provider until
|
|
6
|
+
* someone signs in, instead of burning attempts. Everything else is the
|
|
7
|
+
* ordinary failure road.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* no fixture for a (provider, version) can therefore NEVER return an
|
|
12
|
-
* eligible class — so it can never authorize a paid fallback. A
|
|
13
|
-
* successful run cannot establish the exhausted shape; only real
|
|
14
|
-
* exhaustion can, and the plane cannot manufacture it.
|
|
15
|
-
*
|
|
16
|
-
* Precedence (C5), applied by the classifier:
|
|
17
|
-
* - A present STRUCTURAL FAILURE terminal blocks success ingestion even
|
|
18
|
-
* on exit 0 (that is the parser's job; this module reads the record).
|
|
19
|
-
* - Only an ALLOWLISTED, version+auth-specific pattern yields an ELIGIBLE
|
|
20
|
-
* class. Exit code, bare rate-limit prose, timeout, notFound, stderr,
|
|
21
|
-
* and zero tokens NEVER independently establish exhaustion.
|
|
22
|
-
* - Everything unmatched is `unknown` — the ordinary failure road.
|
|
23
|
-
* - A sign-in that no longer works (`auth-expired`) is recognized FIRST,
|
|
24
|
-
* for every provider and version: an expired or revoked login is not a
|
|
25
|
-
* property of a CLI version, and retrying it only burns attempts. It is
|
|
26
|
-
* never fallback-eligible — a lapsed login must not reach a paid key.
|
|
9
|
+
* `usage-exhausted`, `credits-depleted` and `transient-throttle` are only
|
|
10
|
+
* ever read from older runs' records (they were stamped by a removed
|
|
11
|
+
* feature); this build never produces them.
|
|
27
12
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"usage-exhausted"
|
|
32
|
-
/** An API-key account's paid credits/quota are gone — FALLBACK-ELIGIBLE. */
|
|
33
|
-
| "credits-depleted"
|
|
34
|
-
/** Temporary throttling NOT tied to the usage cap — backoff, NEVER paid fallback. */
|
|
35
|
-
| "transient-throttle"
|
|
36
|
-
/** The provider's sign-in or API key no longer works — NO retry, NO
|
|
37
|
-
* fallback: dispatch for that provider pauses until someone signs in. */
|
|
13
|
+
export type TerminalClass = "usage-exhausted" | "credits-depleted" | "transient-throttle"
|
|
14
|
+
/** The provider's sign-in or API key no longer works — no retry:
|
|
15
|
+
* dispatch for that provider pauses until someone signs in. */
|
|
38
16
|
| "auth-expired"
|
|
39
|
-
/** A definite
|
|
17
|
+
/** A definite structural failure terminal (ordinary failure). */
|
|
40
18
|
| "not-exhausted"
|
|
41
|
-
/** No structural signal
|
|
19
|
+
/** No structural signal — the ordinary failure/strike road. */
|
|
42
20
|
| "unknown";
|
|
43
|
-
/** The two classes that may authorize a fallback advance. */
|
|
44
|
-
export declare const FALLBACK_ELIGIBLE: ReadonlySet<TerminalClass>;
|
|
45
|
-
export declare function isFallbackEligible(cls: TerminalClass): boolean;
|
|
46
21
|
/**
|
|
47
|
-
* The structural terminal a provider's parser retained
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
22
|
+
* The structural terminal a provider's parser retained. `failed` marks a
|
|
23
|
+
* structural FAILURE terminal (codex turn.failed, a gemini error terminal):
|
|
24
|
+
* its presence blocks success ingestion regardless of exit code. `text` is
|
|
25
|
+
* the bounded terminal message; `code` its machine code if the harness
|
|
26
|
+
* typed one.
|
|
52
27
|
*/
|
|
53
28
|
export type StructuralTerminal = {
|
|
54
29
|
failed: boolean;
|
|
55
30
|
text: string | null;
|
|
56
31
|
code: string | null;
|
|
57
32
|
};
|
|
58
|
-
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* build severs any in-flight fallback authority. */
|
|
62
|
-
export declare function hasRecognizer(provider: ProviderId, version: string | null): boolean;
|
|
63
|
-
/**
|
|
64
|
-
* The EXACT C8 capability check (Codex E2/E3 review, finding 8): whether
|
|
65
|
-
* this build can recognize the specific eligible class an AUTH MODE would
|
|
66
|
-
* produce — a subscription's `usage-exhausted` needs a nonempty
|
|
67
|
-
* eligibleUsage set; an api-key account's `credits-depleted` needs a
|
|
68
|
-
* nonempty eligibleCredits set. `hasRecognizer` (any-eligible) is too coarse
|
|
69
|
-
* at the authority point: a version fixtured for usage only must NOT
|
|
70
|
-
* authorize a credits-depleted advance.
|
|
71
|
-
*/
|
|
72
|
-
export declare function recognizesEligible(provider: ProviderId, version: string | null, authMode: "subscription" | "api-key"): boolean;
|
|
73
|
-
/** The eligible class an auth mode can legitimately carry — the C8
|
|
74
|
-
* consistency invariant: `usage-exhausted` iff subscription,
|
|
75
|
-
* `credits-depleted` iff api-key. A stamp that violates this pairing is a
|
|
76
|
-
* bad/overwritten record and must never advance. */
|
|
77
|
-
export declare function classMatchesAuthMode(cls: TerminalClass, authMode: "subscription" | "api-key"): boolean;
|
|
78
|
-
/**
|
|
79
|
-
* Classify a finished attempt. Fail closed: no recognizer for this exact
|
|
80
|
-
* (provider, version) => never an eligible class. `authMode` decides which
|
|
81
|
-
* eligible class a match yields — a subscription that hits its cap is
|
|
82
|
-
* `usage-exhausted`; an api-key account out of credits is
|
|
83
|
-
* `credits-depleted`. A structural failure with no eligible/transient
|
|
84
|
-
* match is `not-exhausted`; no structural failure and no match is
|
|
85
|
-
* `unknown`.
|
|
86
|
-
*/
|
|
33
|
+
/** Classify a finished attempt: a sign-in that no longer works first, for
|
|
34
|
+
* any provider or version; then a structural failure terminal is
|
|
35
|
+
* `not-exhausted`, and anything else `unknown`. */
|
|
87
36
|
export declare function classifyTerminal(args: {
|
|
88
|
-
provider: ProviderId;
|
|
89
|
-
version: string | null;
|
|
90
|
-
authMode: "subscription" | "api-key";
|
|
91
37
|
terminal: StructuralTerminal | null;
|
|
92
38
|
/** What a run that failed within seconds, having produced nothing, said
|
|
93
39
|
* on its way out (stderr and unstructured stdout) — read ONLY for the
|