toolroll 0.9.63 → 0.9.67
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 +108 -0
- package/README.md +33 -5
- package/dist/acp-agents.d.ts +43 -0
- package/dist/acp-agents.js +62 -0
- package/dist/acp-attest.d.ts +44 -0
- package/dist/acp-attest.js +269 -0
- package/dist/acp-check.d.ts +28 -0
- package/dist/acp-check.js +410 -0
- package/dist/acp-client.d.ts +163 -0
- package/dist/acp-client.js +738 -0
- package/dist/acp-inspect.d.ts +23 -0
- package/dist/acp-inspect.js +109 -0
- package/dist/agent-board.d.ts +19 -6
- package/dist/agent-board.js +30 -9
- package/dist/agent-fence.d.ts +117 -2
- package/dist/agent-fence.js +439 -12
- package/dist/agent-pulse.js +9 -2
- package/dist/agent-state-words.d.ts +19 -0
- package/dist/agent-state-words.js +20 -0
- package/dist/agent-state.d.ts +65 -0
- package/dist/agent-state.js +229 -0
- package/dist/agentconfig.js +39 -13
- package/dist/agents-board.d.ts +34 -2
- package/dist/agents-board.js +110 -1
- package/dist/approval-rules-ui.js +2 -2
- package/dist/assignment-delivery.d.ts +14 -0
- package/dist/assignment-delivery.js +13 -0
- package/dist/assignment.js +13 -0
- package/dist/attest.js +7 -0
- package/dist/backup-ui.js +2 -2
- package/dist/batch-checks.js +3 -2
- package/dist/browser/THIRD_PARTY_NOTICES.txt +94 -0
- package/dist/browser/terminal.css +37 -0
- package/dist/browser/terminal.js +228 -0
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +42 -38
- package/dist/browser-shell.js +6 -3
- package/dist/browser-workspace.d.ts +235 -1
- package/dist/browser-workspace.js +60 -0
- package/dist/builder.d.ts +10 -1
- package/dist/builder.js +87 -24
- package/dist/chat-actions.d.ts +13 -1
- package/dist/chat-actions.js +197 -15
- package/dist/chat-ask.d.ts +4 -0
- package/dist/chat-ask.js +14 -0
- package/dist/chat-channel.js +20 -0
- package/dist/chat-continuity.js +5 -4
- package/dist/chat-decide.d.ts +0 -14
- package/dist/chat-decide.js +4 -1
- package/dist/chat-delivery.d.ts +9 -0
- package/dist/chat-delivery.js +45 -6
- package/dist/chat-messages.d.ts +8 -0
- package/dist/chat-messages.js +36 -4
- package/dist/check-levels-ui.js +2 -2
- package/dist/checkout-proof.d.ts +28 -0
- package/dist/checkout-proof.js +62 -0
- package/dist/claim.d.ts +12 -0
- package/dist/claim.js +19 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +4 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +21 -2
- package/dist/coding-ui.js +6 -4
- package/dist/console-instance.d.ts +124 -0
- package/dist/console-instance.js +337 -0
- package/dist/contracts/chat-actions.d.ts +16 -0
- package/dist/contracts/chat-actions.js +4 -0
- package/dist/contracts/chat-content.d.ts +19 -0
- package/dist/contracts/chat-content.js +6 -0
- package/dist/contracts/console-api.d.ts +13 -4
- package/dist/contracts/console-api.js +13 -4
- package/dist/contracts/flow-card.d.ts +4 -0
- package/dist/contracts/flow-card.js +3 -0
- package/dist/contracts/flow.d.ts +46 -0
- package/dist/contracts/flow.js +11 -2
- package/dist/contracts/lead-tools.d.ts +92 -0
- package/dist/contracts/lead-tools.js +13 -0
- package/dist/contracts/provider.d.ts +11 -1
- package/dist/contracts/provider.js +1 -1
- package/dist/contracts/route.d.ts +76 -1
- package/dist/contracts/route.js +1 -1
- package/dist/contracts/store-json.d.ts +2 -0
- package/dist/control-setup.d.ts +1 -1
- package/dist/control-ui.js +2 -2
- package/dist/converse.d.ts +16 -0
- package/dist/converse.js +32 -4
- package/dist/demo-chat.js +4 -4
- package/dist/demo.js +2 -2
- package/dist/desktop-update-gate.js +6 -0
- package/dist/desktop-update.d.ts +1 -1
- package/dist/discord-chat.js +4 -1
- package/dist/doctor.js +1 -1
- package/dist/egress-bridge.d.ts +17 -0
- package/dist/egress-bridge.js +111 -0
- package/dist/egress.d.ts +119 -0
- package/dist/egress.js +423 -0
- package/dist/exec.d.ts +78 -0
- package/dist/exec.js +367 -9
- package/dist/export-ui.js +2 -2
- package/dist/flow-engine.js +24 -5
- package/dist/flow-gallery-ui.js +2 -2
- package/dist/flow-gallery.js +27 -8
- package/dist/flow-share.js +16 -5
- package/dist/flow-triggers.d.ts +77 -8
- package/dist/flow-triggers.js +517 -70
- package/dist/flows-cli.js +8 -2
- package/dist/flows-ui.d.ts +0 -2
- package/dist/flows-ui.js +52 -4
- package/dist/flows.d.ts +4 -0
- package/dist/flows.js +9 -1
- package/dist/html.d.ts +9 -0
- package/dist/html.js +11 -0
- package/dist/invoke.d.ts +35 -0
- package/dist/invoke.js +347 -28
- package/dist/keys.d.ts +18 -14
- package/dist/keys.js +5 -0
- package/dist/lead-capabilities.js +3 -2
- package/dist/lead-contract.js +2 -0
- package/dist/lead-doors.d.ts +2 -0
- package/dist/lead-doors.js +7 -0
- package/dist/lead-follow.d.ts +2 -0
- package/dist/lead-follow.js +58 -2
- package/dist/lead-progress.js +1 -0
- package/dist/lead-queue-runtime.d.ts +46 -0
- package/dist/lead-queue-runtime.js +191 -0
- package/dist/lead-queue.d.ts +104 -0
- package/dist/lead-queue.js +187 -0
- package/dist/lead-sweep.d.ts +67 -0
- package/dist/lead-sweep.js +99 -0
- package/dist/lead-tools.d.ts +22 -0
- package/dist/lead-tools.js +152 -1
- package/dist/lead.d.ts +13 -5
- package/dist/lead.js +89 -29
- package/dist/limits-ui.d.ts +8 -1
- package/dist/limits-ui.js +26 -0
- package/dist/live-bus.d.ts +8 -0
- package/dist/live-bus.js +40 -4
- package/dist/live-detail.d.ts +27 -0
- package/dist/live-detail.js +57 -0
- package/dist/live-notes.d.ts +20 -0
- package/dist/live-notes.js +47 -0
- package/dist/live-records.d.ts +140 -0
- package/dist/live-records.js +935 -0
- package/dist/live-terminal.d.ts +269 -0
- package/dist/live-terminal.js +686 -0
- package/dist/live.d.ts +38 -27
- package/dist/live.js +142 -124
- package/dist/mcp-oauth.d.ts +6 -3
- package/dist/mcp-oauth.js +8 -4
- package/dist/memory-pass.js +2 -2
- package/dist/mobile-viewport.d.ts +5 -1
- package/dist/mobile-viewport.js +18 -2
- package/dist/model-catalog.js +4 -0
- package/dist/monitoring-ui.js +2 -2
- package/dist/needs-you.d.ts +1 -1
- package/dist/needs-you.js +11 -4
- package/dist/operate.d.ts +4 -0
- package/dist/operate.js +335 -19
- package/dist/passwordless.d.ts +23 -0
- package/dist/passwordless.js +26 -0
- package/dist/peek-cli.d.ts +12 -4
- package/dist/peek-cli.js +19 -11
- package/dist/phase-routing.js +10 -3
- package/dist/planner-source.d.ts +3 -0
- package/dist/planner-source.js +14 -0
- package/dist/planner.d.ts +2 -1
- package/dist/planner.js +7 -1
- package/dist/policy-ui.js +2 -2
- package/dist/policy.d.ts +1 -20
- package/dist/policy.js +29 -2
- package/dist/project-delete-ui.d.ts +18 -0
- package/dist/project-delete-ui.js +60 -9
- package/dist/project-delete.js +2 -2
- package/dist/project-memory.d.ts +3 -1
- package/dist/project-memory.js +57 -25
- package/dist/project-tools.d.ts +5 -3
- package/dist/project-tools.js +12 -9
- package/dist/provider-auth.d.ts +4 -3
- package/dist/provider-auth.js +5 -4
- package/dist/provider-connection.d.ts +2 -1
- package/dist/provider-connection.js +5 -0
- package/dist/provider.d.ts +25 -2
- package/dist/provider.js +63 -9
- package/dist/pull-request-ui.js +2 -2
- package/dist/push.js +1 -1
- package/dist/quick-start.d.ts +24 -0
- package/dist/quick-start.js +127 -0
- package/dist/release-adapters.d.ts +6 -0
- package/dist/release-adapters.js +70 -3
- package/dist/release.d.ts +10 -1
- package/dist/release.js +34 -15
- package/dist/request-budget-ui.js +2 -2
- package/dist/result-follow-ups.js +3 -2
- package/dist/result-review.d.ts +1 -1
- package/dist/result-review.js +9 -3
- package/dist/retention-ui.js +2 -2
- package/dist/retention.js +10 -1
- package/dist/run-efficiency.d.ts +73 -0
- package/dist/run-efficiency.js +228 -0
- package/dist/runner.d.ts +5 -1
- package/dist/runner.js +22 -1
- package/dist/scope.d.ts +183 -5
- package/dist/scope.js +256 -31
- package/dist/scout-net.d.ts +4 -1
- package/dist/scout-net.js +37 -12
- package/dist/scout.d.ts +20 -6
- package/dist/scout.js +27 -3
- package/dist/serve.js +248 -40
- package/dist/server/chat.d.ts +1 -1
- package/dist/server/chat.js +122 -31
- package/dist/server/chrome.d.ts +16 -2
- package/dist/server/chrome.js +63 -11
- package/dist/server/console.css +107 -42
- package/dist/server/flows.js +38 -8
- package/dist/server/guards.d.ts +1 -0
- package/dist/server/guards.js +23 -9
- package/dist/server/http.d.ts +7 -0
- package/dist/server/http.js +3 -2
- package/dist/server/live.d.ts +34 -1
- package/dist/server/live.js +39 -17
- package/dist/server/pages-assets.js +5 -0
- package/dist/server/pages.js +90 -13
- package/dist/server/people-tokens.d.ts +3 -0
- package/dist/server/people-tokens.js +55 -17
- package/dist/server/remote-hooks.js +12 -6
- package/dist/server/remote.d.ts +2 -1
- package/dist/server/remote.js +14 -5
- package/dist/server/render-chat.d.ts +12 -3
- package/dist/server/render-chat.js +47 -16
- package/dist/server/render-pages.d.ts +32 -4
- package/dist/server/render-pages.js +83 -13
- package/dist/server/render-people.d.ts +3 -2
- package/dist/server/render-people.js +3 -2
- package/dist/server/render-results.d.ts +18 -2
- package/dist/server/render-results.js +37 -28
- package/dist/server/render-settings.js +6 -5
- package/dist/server/render-tasks.d.ts +35 -10
- package/dist/server/render-tasks.js +111 -27
- package/dist/server/request-context.d.ts +2 -0
- package/dist/server/request-context.js +2 -1
- package/dist/server/route-table.d.ts +3 -0
- package/dist/server/route-table.js +47 -5
- package/dist/server/runtime.d.ts +33 -2
- package/dist/server/session.d.ts +25 -1
- package/dist/server/session.js +18 -4
- package/dist/server/settings.js +142 -10
- package/dist/server/sweeps.d.ts +4 -0
- package/dist/server/sweeps.js +58 -0
- package/dist/server/task-views.d.ts +2 -1
- package/dist/server/task-views.js +96 -6
- package/dist/server/tasks.js +327 -78
- package/dist/server/terminals.d.ts +31 -0
- package/dist/server/terminals.js +365 -0
- package/dist/setup-guide.js +3 -2
- package/dist/slack-chat.js +4 -1
- package/dist/spend-ui.js +3 -3
- package/dist/spend.js +2 -1
- package/dist/steer-capabilities.d.ts +32 -0
- package/dist/steer-capabilities.js +60 -0
- package/dist/steer-controls.d.ts +14 -0
- package/dist/steer-controls.js +36 -0
- package/dist/steer-status.js +2 -2
- package/dist/storage-ui.js +4 -4
- package/dist/store-baseline.d.ts +1 -1
- package/dist/store-baseline.js +1 -1
- package/dist/store.d.ts +440 -15
- package/dist/store.js +1072 -52
- package/dist/subagent-desk.js +1 -1
- package/dist/summary.js +3 -0
- package/dist/task-source.d.ts +58 -0
- package/dist/task-source.js +93 -0
- package/dist/team-leads.js +1 -1
- package/dist/teams-chat.js +4 -1
- package/dist/teardown-git.d.ts +22 -0
- package/dist/teardown-git.js +136 -2
- package/dist/telegram-lead.d.ts +3 -0
- package/dist/telegram-lead.js +44 -7
- package/dist/telegram.d.ts +11 -2
- package/dist/telegram.js +51 -17
- package/dist/terminal-catalog.d.ts +11 -0
- package/dist/terminal-catalog.js +129 -0
- package/dist/text-limits.d.ts +2 -0
- package/dist/text-limits.js +2 -0
- package/dist/tool-broker.d.ts +14 -0
- package/dist/tool-broker.js +67 -2
- package/dist/toolroll-update-ui.js +2 -2
- package/dist/toolroll-update.js +13 -3
- package/dist/tools-ui.js +2 -2
- package/dist/unfenced-test-support.d.ts +2 -0
- package/dist/unfenced-test-support.js +11 -0
- package/dist/webhook-limits.d.ts +23 -0
- package/dist/webhook-limits.js +30 -0
- package/dist/webhook-readers.d.ts +56 -0
- package/dist/webhook-readers.js +138 -0
- package/dist/webhook-signing.d.ts +27 -0
- package/dist/webhook-signing.js +119 -0
- package/dist/work-index.d.ts +29 -0
- package/dist/work-index.js +16 -2
- package/dist/workspace-revision.js +3 -1
- package/dist/writes-mode.d.ts +9 -0
- package/dist/writes-mode.js +8 -0
- package/package.json +15 -2
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type AcpProviderId } from "./acp-agents.js";
|
|
2
|
+
export type AcpInspection = {
|
|
3
|
+
found: false;
|
|
4
|
+
binary: string;
|
|
5
|
+
} | {
|
|
6
|
+
found: true;
|
|
7
|
+
binary: string;
|
|
8
|
+
/** The PATH entry that matched, and where it really is. */
|
|
9
|
+
path: string;
|
|
10
|
+
realpath: string;
|
|
11
|
+
/** A native executable (Mach-O/ELF), or a script with a shebang line. */
|
|
12
|
+
kind: "native" | "script" | "unknown";
|
|
13
|
+
/** For a script: the nearest package.json's name and version, read, never run. */
|
|
14
|
+
package: {
|
|
15
|
+
name: string;
|
|
16
|
+
version: string;
|
|
17
|
+
} | null;
|
|
18
|
+
size: number;
|
|
19
|
+
};
|
|
20
|
+
/** Find an agent's binary on PATH, and describe it without running it. */
|
|
21
|
+
export declare function inspectAcpAgent(provider: AcpProviderId, env?: Readonly<Record<string, string | undefined>>): AcpInspection;
|
|
22
|
+
/** The words a screen says for an inspection: "installed at …, version …" or "not installed". */
|
|
23
|
+
export declare function inspectionWords(inspection: AcpInspection): string;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What can be known about an ACP agent's binary without running it
|
|
3
|
+
* (docs/plans/acp-adapter.md § Identity): nothing outside the one spawning
|
|
4
|
+
* gateway executes an ACP agent — not readiness, not the providers screen,
|
|
5
|
+
* not a version probe. So readiness and the screens look, and never run:
|
|
6
|
+
* PATH lookup, realpath, lstat, and for a script launcher a bounded read of
|
|
7
|
+
* the nearest package.json for its name and version.
|
|
8
|
+
*
|
|
9
|
+
* None of this is evidence an agent may run. That is its conformance check
|
|
10
|
+
* (attestation), which this only helps describe.
|
|
11
|
+
*/
|
|
12
|
+
import { closeSync, constants, fstatSync, lstatSync, openSync, readSync, realpathSync, statSync } from "node:fs";
|
|
13
|
+
import { delimiter, dirname, isAbsolute, join } from "node:path";
|
|
14
|
+
import { ACP_AGENTS } from "./acp-agents.js";
|
|
15
|
+
/** The most bytes read from a launcher (its first line) or a package.json. */
|
|
16
|
+
const READ_CAP = 64 * 1024;
|
|
17
|
+
/** How many folders up from the launcher a package.json is looked for. */
|
|
18
|
+
const PACKAGE_DEPTH = 6;
|
|
19
|
+
/** Read at most `cap` bytes from the start of a regular file, without following a final symlink. */
|
|
20
|
+
function readHead(file, cap) {
|
|
21
|
+
let fd = null;
|
|
22
|
+
try {
|
|
23
|
+
fd = openSync(file, constants.O_RDONLY | constants.O_NOFOLLOW | (constants.O_NONBLOCK ?? 0));
|
|
24
|
+
if (!fstatSync(fd).isFile())
|
|
25
|
+
return null;
|
|
26
|
+
const buffer = Buffer.alloc(cap);
|
|
27
|
+
const read = readSync(fd, buffer, 0, cap, 0);
|
|
28
|
+
return buffer.subarray(0, read);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
if (fd !== null)
|
|
35
|
+
closeSync(fd);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function kindOf(head) {
|
|
39
|
+
if (head === null || head.length < 4)
|
|
40
|
+
return "unknown";
|
|
41
|
+
if (head[0] === 0x23 && head[1] === 0x21)
|
|
42
|
+
return "script"; // "#!"
|
|
43
|
+
const magic = head.readUInt32BE(0);
|
|
44
|
+
// ELF, Mach-O (both byte orders, 32/64-bit) and fat binaries.
|
|
45
|
+
if (magic === 0x7f454c46 || [0xfeedface, 0xfeedfacf, 0xcefaedfe, 0xcffaedfe, 0xcafebabe, 0xbebafeca].includes(magic))
|
|
46
|
+
return "native";
|
|
47
|
+
return "unknown";
|
|
48
|
+
}
|
|
49
|
+
/** The nearest package.json above a script, by bounded read; its name and plain version, or null. */
|
|
50
|
+
function packageOf(realpath) {
|
|
51
|
+
let folder = dirname(realpath);
|
|
52
|
+
for (let depth = 0; depth < PACKAGE_DEPTH; depth += 1) {
|
|
53
|
+
const head = readHead(join(folder, "package.json"), READ_CAP);
|
|
54
|
+
if (head !== null) {
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(head.toString("utf8"));
|
|
57
|
+
const name = parsed["name"];
|
|
58
|
+
const version = parsed["version"];
|
|
59
|
+
if (typeof name === "string" && typeof version === "string" && name.length <= 214 && /^\d+\.\d+\.\d+$/.test(version))
|
|
60
|
+
return { name, version };
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// A package.json past the cap or not JSON says nothing.
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const up = dirname(folder);
|
|
68
|
+
if (up === folder)
|
|
69
|
+
break;
|
|
70
|
+
folder = up;
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
/** Find an agent's binary on PATH, and describe it without running it. */
|
|
75
|
+
export function inspectAcpAgent(provider, env = process.env) {
|
|
76
|
+
const binary = ACP_AGENTS[provider].binary;
|
|
77
|
+
for (const folder of (env["PATH"] ?? "").split(delimiter)) {
|
|
78
|
+
if (folder === "" || !isAbsolute(folder))
|
|
79
|
+
continue;
|
|
80
|
+
const path = join(folder, binary);
|
|
81
|
+
try {
|
|
82
|
+
lstatSync(path);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
let realpath;
|
|
88
|
+
try {
|
|
89
|
+
realpath = realpathSync(path);
|
|
90
|
+
const stat = statSync(realpath);
|
|
91
|
+
if (!stat.isFile() || (stat.mode & 0o111) === 0)
|
|
92
|
+
continue;
|
|
93
|
+
const head = readHead(realpath, 4096);
|
|
94
|
+
const kind = kindOf(head);
|
|
95
|
+
return { found: true, binary, path, realpath, kind, package: kind === "script" ? packageOf(realpath) : null, size: stat.size };
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return { found: false, binary };
|
|
102
|
+
}
|
|
103
|
+
/** The words a screen says for an inspection: "installed at …, version …" or "not installed". */
|
|
104
|
+
export function inspectionWords(inspection) {
|
|
105
|
+
if (!inspection.found)
|
|
106
|
+
return `\`${inspection.binary}\` is not installed on this computer's PATH`;
|
|
107
|
+
const version = inspection.package === null ? "" : ` (${inspection.package.name} ${inspection.package.version})`;
|
|
108
|
+
return `installed at ${inspection.realpath}${version}`;
|
|
109
|
+
}
|
package/dist/agent-board.d.ts
CHANGED
|
@@ -20,8 +20,6 @@ export declare const QUESTIONS_PER_ATTEMPT = 10;
|
|
|
20
20
|
export declare const QUESTION_PAGE_EVERY_MS: number;
|
|
21
21
|
/** The board's one note on a run whose agent asked past the caps (set once). */
|
|
22
22
|
export declare const QUESTIONS_OVERFLOW_NOTE = "The agent asked more questions than the board takes; see its transcript.";
|
|
23
|
-
/** Notification kinds held while the person watches the run, and re-checked when the watch lapses. */
|
|
24
|
-
export declare const WATCHED_KINDS: ReadonlySet<string>;
|
|
25
23
|
export declare const AGENT_BOARD_SCHEMA = "\nCREATE TABLE IF NOT EXISTS agent_turn (\n id INTEGER PRIMARY KEY,\n run INTEGER NOT NULL REFERENCES run(id) ON DELETE CASCADE,\n attempt_key TEXT NOT NULL,\n state TEXT NOT NULL CHECK (state IN ('busy','idle','crashed')),\n at TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS agent_turn_run ON agent_turn (run, id);\nCREATE TABLE IF NOT EXISTS agent_now (\n task_ref INTEGER PRIMARY KEY REFERENCES task_ref(id) ON DELETE CASCADE,\n run INTEGER NOT NULL REFERENCES run(id) ON DELETE CASCADE,\n attempt_key TEXT NOT NULL,\n state TEXT NOT NULL CHECK (state IN ('busy','idle','crashed')),\n state_at TEXT NOT NULL,\n transcript_bytes INTEGER NOT NULL DEFAULT 0,\n transcript_at TEXT NOT NULL,\n stuck_at TEXT,\n said TEXT CHECK (said IN ('working','blocked','done')),\n said_note TEXT,\n said_at TEXT,\n status_offset INTEGER NOT NULL DEFAULT 0,\n diff_files INTEGER,\n diff_at TEXT\n);\nCREATE TABLE IF NOT EXISTS agent_question (\n id INTEGER PRIMARY KEY,\n run INTEGER NOT NULL REFERENCES run(id) ON DELETE CASCADE,\n task_ref INTEGER NOT NULL REFERENCES task_ref(id) ON DELETE CASCADE,\n attempt_key TEXT NOT NULL,\n key TEXT NOT NULL,\n question TEXT NOT NULL,\n asked_at TEXT NOT NULL,\n closed_at TEXT,\n closed_how TEXT CHECK (closed_how IN ('answered','ended')),\n answer_note INTEGER REFERENCES task_steer(id),\n answered_by TEXT,\n UNIQUE (run, key)\n);\nCREATE INDEX IF NOT EXISTS agent_question_open ON agent_question (task_ref) WHERE closed_at IS NULL;\nCREATE TABLE IF NOT EXISTS agent_seen (\n account TEXT NOT NULL,\n run INTEGER NOT NULL REFERENCES run(id) ON DELETE CASCADE,\n seen_at TEXT NOT NULL,\n PRIMARY KEY (account, run)\n);\nCREATE TABLE IF NOT EXISTS agent_view (\n view TEXT PRIMARY KEY,\n account TEXT NOT NULL,\n run INTEGER NOT NULL,\n expires_at TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS agent_view_run ON agent_view (run, account);\nCREATE INDEX IF NOT EXISTS agent_board_open ON run (task_ref, id) WHERE role = 'builder' AND outcome IS NULL;\nCREATE INDEX IF NOT EXISTS agent_board_recent ON run (finished_at, task_ref, id) WHERE role = 'builder' AND outcome IS NOT NULL;\nCREATE INDEX IF NOT EXISTS agent_board_repair ON run (parent_run, id) WHERE role = 'repair';\n";
|
|
26
24
|
/** Added to agent_now after the table first shipped (store.ts addColumn): the oldest open tool call's kind and start. */
|
|
27
25
|
export declare const AGENT_NOW_COLUMNS: readonly [string, string][];
|
|
@@ -194,15 +192,30 @@ export declare function releaseAgentView(store: Store, view: string, at: Date):
|
|
|
194
192
|
export declare function watchingAgentUntil(store: Store, account: string, run: number, at: Date): string | null;
|
|
195
193
|
/** Someone has this exact run open right now. */
|
|
196
194
|
export declare function watchingAgent(store: Store, account: string, run: number, at: Date): boolean;
|
|
195
|
+
export declare const neverHeld: (kind: string) => boolean;
|
|
197
196
|
/**
|
|
198
|
-
* A
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
197
|
+
* A notification's subject (cockpit §3, R11): the run it is about — its own, or for a ping about a task the task's
|
|
198
|
+
* live build — so any ping, not only the agent's own kinds, can be held while its person watches that agent. Null:
|
|
199
|
+
* about nothing a person watches. Its version is the notification's own identity (its dedupe key), so a newer fact
|
|
200
|
+
* about the same subject is a new ping.
|
|
201
|
+
*/
|
|
202
|
+
export declare function attentionSubjectOf(store: Store, row: {
|
|
203
|
+
kind: string;
|
|
204
|
+
run: number | null;
|
|
205
|
+
taskRef?: number | null;
|
|
206
|
+
}): {
|
|
207
|
+
run: number;
|
|
208
|
+
} | null;
|
|
209
|
+
/**
|
|
210
|
+
* A watched ping (cockpit §3, R11), decided when its delivery is claimed AND again at every channel's final send
|
|
211
|
+
* fence, after the permanent rules in store.pingAllowed: held until the watch lapses while this person has its
|
|
212
|
+
* subject's agent open; then skipped when it was news about a finished run they have since seen; otherwise sent.
|
|
213
|
+
* A ping about nothing anyone watches is sent. (A delivery already handed to the network can't be recalled.)
|
|
202
214
|
*/
|
|
203
215
|
export declare function watchedPing(store: Store, row: {
|
|
204
216
|
kind: string;
|
|
205
217
|
run: number | null;
|
|
218
|
+
taskRef?: number | null;
|
|
206
219
|
}, account: string, now: Date): {
|
|
207
220
|
hold: string;
|
|
208
221
|
} | "skip" | "send";
|
package/dist/agent-board.js
CHANGED
|
@@ -46,8 +46,6 @@ export const QUESTIONS_PER_ATTEMPT = 10;
|
|
|
46
46
|
export const QUESTION_PAGE_EVERY_MS = 5 * 60_000;
|
|
47
47
|
/** The board's one note on a run whose agent asked past the caps (set once). */
|
|
48
48
|
export const QUESTIONS_OVERFLOW_NOTE = "The agent asked more questions than the board takes; see its transcript.";
|
|
49
|
-
/** Notification kinds held while the person watches the run, and re-checked when the watch lapses. */
|
|
50
|
-
export const WATCHED_KINDS = new Set(["agent-question", "run-finished", "build-failed"]);
|
|
51
49
|
export const AGENT_BOARD_SCHEMA = `
|
|
52
50
|
CREATE TABLE IF NOT EXISTS agent_turn (
|
|
53
51
|
id INTEGER PRIMARY KEY,
|
|
@@ -435,19 +433,42 @@ export function watchingAgentUntil(store, account, run, at) {
|
|
|
435
433
|
export function watchingAgent(store, account, run, at) {
|
|
436
434
|
return watchingAgentUntil(store, account, run, at) !== null;
|
|
437
435
|
}
|
|
436
|
+
/** Kinds no watch ever holds: a security alert and the evening digest reach people as they are (store.pingAllowed).
|
|
437
|
+
* Nothing held may delay them either: a channel's ordering never keeps one behind a held ping (neverHeld). */
|
|
438
|
+
const NEVER_HELD = new Set(["secret-detected", "security-release", "evening-digest"]);
|
|
439
|
+
export const neverHeld = (kind) => NEVER_HELD.has(kind);
|
|
440
|
+
/** Kinds that are news only until the finished run is seen: a ping about one already opened is not sent at all. */
|
|
441
|
+
const SEEN_SETTLES = new Set(["run-finished", "build-failed"]);
|
|
438
442
|
/**
|
|
439
|
-
* A
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
443
|
+
* A notification's subject (cockpit §3, R11): the run it is about — its own, or for a ping about a task the task's
|
|
444
|
+
* live build — so any ping, not only the agent's own kinds, can be held while its person watches that agent. Null:
|
|
445
|
+
* about nothing a person watches. Its version is the notification's own identity (its dedupe key), so a newer fact
|
|
446
|
+
* about the same subject is a new ping.
|
|
447
|
+
*/
|
|
448
|
+
export function attentionSubjectOf(store, row) {
|
|
449
|
+
if (NEVER_HELD.has(row.kind))
|
|
450
|
+
return null;
|
|
451
|
+
if (row.run !== null)
|
|
452
|
+
return { run: row.run };
|
|
453
|
+
if (row.taskRef === undefined || row.taskRef === null)
|
|
454
|
+
return null;
|
|
455
|
+
const live = store.handle.prepare("SELECT id FROM run WHERE task_ref = ? AND role = 'builder' AND outcome IS NULL ORDER BY id DESC LIMIT 1").get(row.taskRef);
|
|
456
|
+
return live === undefined ? null : { run: Number(live["id"]) };
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* A watched ping (cockpit §3, R11), decided when its delivery is claimed AND again at every channel's final send
|
|
460
|
+
* fence, after the permanent rules in store.pingAllowed: held until the watch lapses while this person has its
|
|
461
|
+
* subject's agent open; then skipped when it was news about a finished run they have since seen; otherwise sent.
|
|
462
|
+
* A ping about nothing anyone watches is sent. (A delivery already handed to the network can't be recalled.)
|
|
443
463
|
*/
|
|
444
464
|
export function watchedPing(store, row, account, now) {
|
|
445
|
-
|
|
465
|
+
const subject = attentionSubjectOf(store, row);
|
|
466
|
+
if (subject === null)
|
|
446
467
|
return "send";
|
|
447
|
-
const until = watchingAgentUntil(store, account,
|
|
468
|
+
const until = watchingAgentUntil(store, account, subject.run, now);
|
|
448
469
|
if (until !== null)
|
|
449
470
|
return { hold: until };
|
|
450
|
-
if (row.kind ===
|
|
471
|
+
if (!SEEN_SETTLES.has(row.kind) || row.run === null)
|
|
451
472
|
return "send";
|
|
452
473
|
const seen = store.handle.prepare("SELECT 1 AS hit FROM agent_seen WHERE account = ? AND run = ?").get(account, boardRunOf(store, row.run));
|
|
453
474
|
return seen === undefined ? "send" : "skip";
|
package/dist/agent-fence.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { LoopbackMode } from "./egress.js";
|
|
2
|
+
/** Beside the database: where Linux builds' network bridges are made (its name reads as secret wherever it is). */
|
|
3
|
+
export declare const BRIDGE_DIR = "bridge-secrets";
|
|
1
4
|
/** How a launch was fenced, as recorded on the run and said on the task. */
|
|
2
5
|
export type FenceMethod = "codex-profile" | "macos-sandbox" | "linux-bubblewrap" | "claude-rules" | "none";
|
|
3
6
|
/** Only the plane's recorded repository may supply an own-Git exemption. */
|
|
@@ -55,8 +58,9 @@ export type RunFolders = {
|
|
|
55
58
|
root: string;
|
|
56
59
|
own: string | null;
|
|
57
60
|
};
|
|
58
|
-
/** The prefixes of every launch's private folders: its tools' config
|
|
59
|
-
|
|
61
|
+
/** The prefixes of every launch's private folders: its tools' config, a Codex build's own home, and a Linux build's
|
|
62
|
+
* network bridge (egress-bridge.ts). */
|
|
63
|
+
export declare const RUN_FOLDER_PREFIXES: readonly ["so-tools-", "so-codex-", "so-net-", "so-cmd-"];
|
|
60
64
|
export declare function runFolders(own: string | null, root?: string): RunFolders;
|
|
61
65
|
/**
|
|
62
66
|
* What a fence gives back beside its denied paths. Each is narrow and none
|
|
@@ -75,7 +79,94 @@ export type FenceGrants = {
|
|
|
75
79
|
lookup: string[];
|
|
76
80
|
runFolders?: RunFolders;
|
|
77
81
|
pool?: CheckoutPool;
|
|
82
|
+
network?: NetworkFence;
|
|
83
|
+
writes?: WriteAllowlist;
|
|
78
84
|
};
|
|
85
|
+
export { isWritesMode, WRITES_DEFAULT, type WritesMode } from "./writes-mode.js";
|
|
86
|
+
/**
|
|
87
|
+
* The only places a `workspace` build writes: `subpaths`, each a folder (or file) writable whole, and `prefixes`, a file
|
|
88
|
+
* with the temporary names it is atomically replaced through, or a folder named by a pattern (a regex on macOS;
|
|
89
|
+
* elsewhere the path alone, when it exists). Everything else is read-only to the build; its fence's denies and read-only grants still hold inside these.
|
|
90
|
+
*/
|
|
91
|
+
export type WriteAllowlist = {
|
|
92
|
+
subpaths: string[];
|
|
93
|
+
prefixes: WritePrefix[];
|
|
94
|
+
};
|
|
95
|
+
/** A file or folder named by `path` followed by what `suffix` (a regular expression, anchored at its end) allows. */
|
|
96
|
+
export type WritePrefix = {
|
|
97
|
+
path: string;
|
|
98
|
+
suffix: string;
|
|
99
|
+
};
|
|
100
|
+
/** Claude's transcript folder for a working folder: its path with every character but a letter or digit made `-`
|
|
101
|
+
* (Claude Code's own naming; a name over 200 characters gets a suffix, so it is granted by prefix). */
|
|
102
|
+
export declare function claudeProjectFolder(home: string, cwd: string): {
|
|
103
|
+
path: string;
|
|
104
|
+
byPrefix: boolean;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* A folder a write grant may name: made when absent, then only as the real folder it is — never a link, nor reached
|
|
108
|
+
* through one (its real path is its parent's real path and its own name). A build can rewrite what it may write, so a
|
|
109
|
+
* folder it was given could otherwise come back as a link to anywhere and be granted, followed, on its next launch.
|
|
110
|
+
* Null when it isn't such a folder: nothing is granted, and the write it was for fails.
|
|
111
|
+
*/
|
|
112
|
+
export declare function grantFolder(path: string, create?: boolean): string | null;
|
|
113
|
+
/**
|
|
114
|
+
* What a `workspace` build writes and what it loses (arc 2): its checkout, its checkout's Git data (less the shared
|
|
115
|
+
* repository's config, hooks and info, which Git runs or reads as commands), its own launch folder (its temp is there),
|
|
116
|
+
* its project's cache folder, the terminal devices, and only the provider state its CLI must write (measured: Claude's
|
|
117
|
+
* transcript folder for this checkout; Gemini's history, temp and project list; an ACP agent's own folders). The
|
|
118
|
+
* folders named here are made first, so the provider never has to make them where it can't. `deny` is what this
|
|
119
|
+
* provider must not even read (Claude's live sessions' keys).
|
|
120
|
+
*/
|
|
121
|
+
export declare function writeAllowlist(options: {
|
|
122
|
+
provider: string;
|
|
123
|
+
home: string;
|
|
124
|
+
worktree: string;
|
|
125
|
+
binding: WorktreeBinding | null;
|
|
126
|
+
launchFolder: string | null;
|
|
127
|
+
cache: string | null;
|
|
128
|
+
}): {
|
|
129
|
+
writes: WriteAllowlist;
|
|
130
|
+
readOnly: string[];
|
|
131
|
+
deny: string[];
|
|
132
|
+
};
|
|
133
|
+
/** Where a project's builds keep their package caches: one folder per project (one trust domain), never used by the
|
|
134
|
+
* plane's own tools, below the user's cache folder. */
|
|
135
|
+
export declare function buildCacheFolder(repo: string, home?: string, platform?: NodeJS.Platform, env?: Readonly<Record<string, string | undefined>>): string;
|
|
136
|
+
/** The environment that points a build's package managers at its project's cache folder, not the user's own. */
|
|
137
|
+
export declare function buildCacheEnv(folder: string): Record<string, string>;
|
|
138
|
+
/**
|
|
139
|
+
* A launch's network confinement (egress.ts, docs/plans/agent-baseline-hardening.md arc 1). `open`: the network as it
|
|
140
|
+
* always was. `allowlist`: nothing leaves the machine but through the run's own proxy (`proxyPort`); the run's tool
|
|
141
|
+
* broker (`brokerPorts`) stays reachable; on macOS this Mac's other local services are reachable only under
|
|
142
|
+
* `loopback: "any"` (all of them: Seatbelt can't refuse one local port once it admits the rest); unix sockets only
|
|
143
|
+
* under `socketRoots` (the run's own temp folder and checkout). On Linux the agent gets a network of its own and
|
|
144
|
+
* reaches the proxy and broker through `bridge` (a folder of unix sockets the plane serves, and the forwarder that
|
|
145
|
+
* carries them onto the namespace's own loopback).
|
|
146
|
+
*/
|
|
147
|
+
export type NetworkFence = {
|
|
148
|
+
mode: "open";
|
|
149
|
+
} | {
|
|
150
|
+
mode: "allowlist";
|
|
151
|
+
proxyPort: number;
|
|
152
|
+
brokerPorts: number[];
|
|
153
|
+
loopback: LoopbackMode;
|
|
154
|
+
socketRoots: string[];
|
|
155
|
+
bridge?: {
|
|
156
|
+
folder: string;
|
|
157
|
+
forwarder: string;
|
|
158
|
+
node: string;
|
|
159
|
+
};
|
|
160
|
+
/** Linux: the run's own folders a private /tmp gives back (its checkout, its launch folder), when they are there. */
|
|
161
|
+
keep?: string[];
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Services a sandboxed process could ask to act outside the sandbox for it (macOS): open a URL in a browser (Launch
|
|
165
|
+
* Services), script another application (Apple Events), fetch in the background (nsurlsessiond), look a name up
|
|
166
|
+
* (the DNS service: a lookup is itself a message out), or read and write the clipboard. Denied when the network is
|
|
167
|
+
* confined; the run's proxy resolves names for it.
|
|
168
|
+
*/
|
|
169
|
+
export declare const DELEGATION_SERVICES: readonly string[];
|
|
79
170
|
export declare const NO_GRANTS: FenceGrants;
|
|
80
171
|
/**
|
|
81
172
|
* The plane's checkout pools, denied whole: every project's worktrees, and any made there after launch. `agentFence`
|
|
@@ -187,7 +278,17 @@ export declare function ownedAncestors(paths: readonly string[], roots?: readonl
|
|
|
187
278
|
* checkout given back right after, so every later deny still holds inside it.
|
|
188
279
|
*/
|
|
189
280
|
export declare function macosFenceProfile(fence: readonly string[], grants?: FenceGrants): string;
|
|
281
|
+
/**
|
|
282
|
+
* The network half of the macOS fence. Every outbound connection is denied, then loopback is given back as the launch
|
|
283
|
+
* allows: every local port under `loopback: "any"`, else the run's proxy and broker alone. (Seatbelt admits a port an
|
|
284
|
+
* allow names whatever a deny after it says, so a local port can't be refused once all are admitted: the choice is all
|
|
285
|
+
* or none, plus the run's own.) Filters name the remote end only: a `local ip` filter would admit any outbound
|
|
286
|
+
* connection.
|
|
287
|
+
*/
|
|
288
|
+
export declare function macosNetworkRules(network: NetworkFence | undefined): string[];
|
|
190
289
|
export declare const SANDBOX_EXEC = "/usr/bin/sandbox-exec";
|
|
290
|
+
/** Where macOS's resolver (mDNSResponder) answers: every name lookup on a Mac is a message to this socket. */
|
|
291
|
+
export declare const MACOS_DNS_SOCKET = "/private/var/run/mDNSResponder";
|
|
191
292
|
/**
|
|
192
293
|
* Whether this machine can run an agent inside the macOS fence (checked once). Not from inside another
|
|
193
294
|
* sandbox: a Toolroll a fenced process starts (the end-to-end journeys a flow's check zone runs) can't nest
|
|
@@ -213,3 +314,17 @@ export declare function linuxFenced(file: string, args: readonly string[], fence
|
|
|
213
314
|
file: string;
|
|
214
315
|
args: string[];
|
|
215
316
|
};
|
|
317
|
+
/** The host's own sockets a process in a mounted filesystem could still talk to (D-Bus, the display, container
|
|
318
|
+
* engines), wherever they exist here. */
|
|
319
|
+
export declare function linuxHostSockets(uid?: number | undefined, home?: string, tmp?: string, followLinks?: boolean, env?: Readonly<Record<string, string | undefined>>): string[];
|
|
320
|
+
/**
|
|
321
|
+
* What each agent's fence gives it, by permission mode: the one table README's "What an agent can reach" shows
|
|
322
|
+
* (fenceSummaryMarkdown; a test holds them equal).
|
|
323
|
+
*/
|
|
324
|
+
export declare const FENCE_SUMMARY: readonly {
|
|
325
|
+
agent: string;
|
|
326
|
+
auto: string;
|
|
327
|
+
full: string;
|
|
328
|
+
}[];
|
|
329
|
+
/** FENCE_SUMMARY as README's markdown table. */
|
|
330
|
+
export declare function fenceSummaryMarkdown(): string;
|