toolroll 0.9.62 → 0.9.65
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 +80 -0
- package/README.md +66 -8
- 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 +203 -10
- package/dist/agent-fence.js +623 -41
- 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 +36 -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/browser/THIRD_PARTY_NOTICES.txt +48 -0
- package/dist/browser/terminal.css +37 -0
- package/dist/browser/terminal.js +117 -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 +263 -1
- package/dist/browser-workspace.js +60 -0
- package/dist/builder.d.ts +6 -1
- package/dist/builder.js +34 -13
- package/dist/chat-actions.d.ts +13 -1
- package/dist/chat-actions.js +199 -17
- 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/check-levels.js +3 -0
- 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 +20 -1
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +5 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +24 -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/cli.d.ts +8 -0
- package/dist/contracts/cli.js +3 -1
- package/dist/contracts/console-api.d.ts +30 -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/integration-metadata.d.ts +2 -1
- package/dist/contracts/integration-metadata.js +2 -1
- 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/discord-chat.js +4 -1
- package/dist/dispatch.js +7 -4
- package/dist/doctor.d.ts +53 -0
- package/dist/doctor.js +427 -0
- 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/evidence-pack.d.ts +1 -1
- package/dist/evidence-pack.js +2 -2
- package/dist/exec.d.ts +81 -0
- package/dist/exec.js +368 -9
- package/dist/export-ui.js +2 -2
- package/dist/flow-code.d.ts +2 -2
- package/dist/flow-code.js +4 -15
- package/dist/flow-engine.js +35 -11
- 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 +84 -11
- package/dist/flow-triggers.js +554 -87
- package/dist/flow-unattended-words.d.ts +28 -0
- package/dist/flow-unattended-words.js +50 -0
- package/dist/flow-unattended.d.ts +227 -0
- package/dist/flow-unattended.js +776 -0
- package/dist/flows-cli.d.ts +28 -1
- package/dist/flows-cli.js +63 -12
- package/dist/flows-ui.d.ts +2 -3
- package/dist/flows-ui.js +56 -5
- package/dist/flows.d.ts +4 -0
- package/dist/flows.js +9 -1
- package/dist/google-mail.d.ts +1 -0
- package/dist/google-mail.js +61 -18
- package/dist/html.d.ts +9 -0
- package/dist/html.js +11 -0
- package/dist/integrations-ui.d.ts +2 -1
- package/dist/integrations-ui.js +6 -3
- package/dist/integrations.d.ts +17 -3
- package/dist/integrations.js +38 -18
- package/dist/invoke.d.ts +17 -0
- package/dist/invoke.js +510 -182
- package/dist/keys.d.ts +18 -14
- package/dist/keys.js +5 -0
- package/dist/landed-cleanup.d.ts +3 -2
- package/dist/landed-cleanup.js +12 -5
- 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 +163 -4
- 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 +258 -0
- package/dist/live-terminal.js +649 -0
- package/dist/live.d.ts +38 -27
- package/dist/live.js +142 -124
- package/dist/mcp-connect.d.ts +8 -7
- package/dist/mcp-connect.js +49 -51
- 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/oauth-run-token.d.ts +51 -0
- package/dist/oauth-run-token.js +127 -0
- package/dist/operate.d.ts +2 -0
- package/dist/operate.js +282 -18
- 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/plan-auto.d.ts +11 -0
- package/dist/plan-auto.js +62 -11
- 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/process-recovery-eligibility.d.ts +1 -1
- package/dist/project-access.d.ts +3 -0
- package/dist/project-access.js +3 -0
- package/dist/project-delete-ui.d.ts +12 -0
- package/dist/project-delete-ui.js +41 -9
- package/dist/project-delete.d.ts +3 -1
- package/dist/project-delete.js +12 -6
- package/dist/project-file.d.ts +3 -0
- package/dist/project-file.js +16 -0
- package/dist/project-memory.d.ts +3 -1
- package/dist/project-memory.js +57 -25
- package/dist/project-tools.d.ts +103 -27
- package/dist/project-tools.js +399 -100
- 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-home.d.ts +22 -0
- package/dist/provider-home.js +99 -0
- package/dist/provider.d.ts +42 -5
- package/dist/provider.js +97 -19
- 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.d.ts +3 -0
- package/dist/release.js +14 -8
- package/dist/remote-main.d.ts +4 -0
- package/dist/remote-main.js +3 -3
- package/dist/request-budget-ui.js +2 -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 +171 -6
- package/dist/scope.js +251 -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 +258 -43
- package/dist/server/chat.d.ts +1 -1
- package/dist/server/chat.js +122 -31
- package/dist/server/chrome.d.ts +6 -1
- package/dist/server/chrome.js +18 -7
- package/dist/server/console.css +52 -38
- package/dist/server/flows.js +65 -13
- 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 +4 -1
- package/dist/server/render-pages.js +5 -4
- 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 +110 -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 +49 -5
- package/dist/server/runtime.d.ts +34 -3
- package/dist/server/session.d.ts +25 -1
- package/dist/server/session.js +18 -4
- package/dist/server/settings.js +132 -13
- 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 +320 -74
- 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-sweep-child.js +3 -1
- package/dist/storage-sweep-process.d.ts +12 -5
- package/dist/storage-sweep-process.js +23 -6
- 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 +466 -19
- package/dist/store.js +1185 -63
- package/dist/subagent-desk.js +1 -1
- package/dist/subagent-tools.d.ts +1 -1
- package/dist/summary.js +3 -0
- package/dist/surface.js +6 -4
- 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 +14 -0
- package/dist/teardown-git.js +119 -0
- 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 +39 -0
- package/dist/tool-broker.js +370 -0
- package/dist/tool-launcher.js +5 -4
- package/dist/tool-secret-store.d.ts +170 -0
- package/dist/tool-secret-store.js +834 -0
- package/dist/toolroll-update-ui.js +2 -2
- package/dist/toolroll-update.js +14 -3
- package/dist/tools-ui.d.ts +7 -1
- package/dist/tools-ui.js +19 -10
- 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 +21 -5
- package/dist/workspace-revision.js +3 -1
- package/dist/worktree.d.ts +10 -4
- package/dist/worktree.js +70 -29
- package/package.json +13 -2
- package/scripts/provider-canary.mjs +210 -31
package/dist/provider-auth.d.ts
CHANGED
|
@@ -14,16 +14,17 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { Store } from "./store.js";
|
|
16
16
|
import { type ProviderId } from "./provider.js";
|
|
17
|
+
import type { AuthMode } from "./keys.js";
|
|
17
18
|
/** Every sign-in message's dedupe key starts here: `signin:<kind>:<provider>:<ordinal>:<person>`.
|
|
18
19
|
* Not a lifecycle (`life:`) key: those are progress chatter, and an undelivered
|
|
19
20
|
* one stays out of the attention list — this one needs a person. */
|
|
20
21
|
export declare const SIGN_IN_KEY_PREFIX = "signin:";
|
|
21
|
-
export declare const PROVIDER_AUTH_SCHEMA = "\nCREATE TABLE IF NOT EXISTS provider_auth_pause (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n provider TEXT NOT NULL,\n ordinal INTEGER NOT NULL,\n auth_mode TEXT NOT NULL DEFAULT 'subscription' CHECK (auth_mode IN ('subscription', 'api-key')),\n opened_at TEXT NOT NULL,\n first_run INTEGER,\n task_ref INTEGER,\n runs INTEGER NOT NULL DEFAULT 1,\n -- 1 once a sign-in probe during this pause said the provider is signed out:\n -- only a probe that then sees it signed in again may lift the pause.\n probe_saw_out INTEGER NOT NULL DEFAULT 0,\n lifted_at TEXT,\n lifted_by TEXT,\n lifted_how TEXT CHECK (lifted_how IS NULL OR lifted_how IN ('run', 'probe', 'person')),\n resumed INTEGER,\n -- The last task let through as a trial while paused: a sign-in check can\n -- say \"logged in\" for a session that no longer works, so a real run decides.\n last_trial_at TEXT,\n UNIQUE (provider, ordinal)\n);\nCREATE UNIQUE INDEX IF NOT EXISTS provider_auth_pause_open ON provider_auth_pause (provider) WHERE lifted_at IS NULL;\n";
|
|
22
|
+
export declare const PROVIDER_AUTH_SCHEMA = "\nCREATE TABLE IF NOT EXISTS provider_auth_pause (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n provider TEXT NOT NULL,\n ordinal INTEGER NOT NULL,\n auth_mode TEXT NOT NULL DEFAULT 'subscription' CHECK (auth_mode IN ('subscription', 'api-key', 'agent-login')),\n opened_at TEXT NOT NULL,\n first_run INTEGER,\n task_ref INTEGER,\n runs INTEGER NOT NULL DEFAULT 1,\n -- 1 once a sign-in probe during this pause said the provider is signed out:\n -- only a probe that then sees it signed in again may lift the pause.\n probe_saw_out INTEGER NOT NULL DEFAULT 0,\n lifted_at TEXT,\n lifted_by TEXT,\n lifted_how TEXT CHECK (lifted_how IS NULL OR lifted_how IN ('run', 'probe', 'person')),\n resumed INTEGER,\n -- The last task let through as a trial while paused: a sign-in check can\n -- say \"logged in\" for a session that no longer works, so a real run decides.\n last_trial_at TEXT,\n UNIQUE (provider, ordinal)\n);\nCREATE UNIQUE INDEX IF NOT EXISTS provider_auth_pause_open ON provider_auth_pause (provider) WHERE lifted_at IS NULL;\n";
|
|
22
23
|
export type AuthPause = {
|
|
23
24
|
id: number;
|
|
24
25
|
provider: ProviderId;
|
|
25
26
|
ordinal: number;
|
|
26
|
-
authMode:
|
|
27
|
+
authMode: AuthMode;
|
|
27
28
|
openedAt: string;
|
|
28
29
|
firstRun: number | null;
|
|
29
30
|
taskRef: number | null;
|
|
@@ -63,7 +64,7 @@ export declare function openAuthPauses(store: Store): AuthPause[];
|
|
|
63
64
|
*/
|
|
64
65
|
export declare function pauseForAuth(store: Store, args: {
|
|
65
66
|
provider: string;
|
|
66
|
-
authMode:
|
|
67
|
+
authMode: AuthMode | null;
|
|
67
68
|
runId: number;
|
|
68
69
|
taskRef: number;
|
|
69
70
|
now: Date;
|
package/dist/provider-auth.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PROVIDER_IDS } from "./provider.js";
|
|
2
|
+
import { withAcp } from "./acp-agents.js";
|
|
2
3
|
/** Every sign-in message's dedupe key starts here: `signin:<kind>:<provider>:<ordinal>:<person>`.
|
|
3
4
|
* Not a lifecycle (`life:`) key: those are progress chatter, and an undelivered
|
|
4
5
|
* one stays out of the attention list — this one needs a person. */
|
|
@@ -8,7 +9,7 @@ CREATE TABLE IF NOT EXISTS provider_auth_pause (
|
|
|
8
9
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
9
10
|
provider TEXT NOT NULL,
|
|
10
11
|
ordinal INTEGER NOT NULL,
|
|
11
|
-
auth_mode TEXT NOT NULL DEFAULT 'subscription' CHECK (auth_mode IN ('subscription', 'api-key')),
|
|
12
|
+
auth_mode TEXT NOT NULL DEFAULT 'subscription' CHECK (auth_mode IN ('subscription', 'api-key', 'agent-login')),
|
|
12
13
|
opened_at TEXT NOT NULL,
|
|
13
14
|
first_run INTEGER,
|
|
14
15
|
task_ref INTEGER,
|
|
@@ -27,8 +28,8 @@ CREATE TABLE IF NOT EXISTS provider_auth_pause (
|
|
|
27
28
|
);
|
|
28
29
|
CREATE UNIQUE INDEX IF NOT EXISTS provider_auth_pause_open ON provider_auth_pause (provider) WHERE lifted_at IS NULL;
|
|
29
30
|
`;
|
|
30
|
-
const NAMES = { claude: "Claude", codex: "Codex", openrouter: "OpenRouter", gemini: "Gemini" };
|
|
31
|
-
const SIGN_IN = { claude: "claude /login", codex: "codex login", gemini: "gemini", openrouter: "toolroll keys set openrouter" };
|
|
31
|
+
const NAMES = withAcp({ claude: "Claude", codex: "Codex", openrouter: "OpenRouter", gemini: "Gemini" }, agent => agent.name);
|
|
32
|
+
const SIGN_IN = withAcp({ claude: "claude /login", codex: "codex login", gemini: "gemini", openrouter: "toolroll keys set openrouter" }, agent => agent.signIn);
|
|
32
33
|
export function providerName(provider) {
|
|
33
34
|
return PROVIDER_IDS.includes(provider) ? NAMES[provider] : provider;
|
|
34
35
|
}
|
|
@@ -57,7 +58,7 @@ function readPause(row) {
|
|
|
57
58
|
id: Number(row["id"]),
|
|
58
59
|
provider: String(row["provider"]),
|
|
59
60
|
ordinal: Number(row["ordinal"]),
|
|
60
|
-
authMode: row["auth_mode"] === "api-key" ? "api-key" : "subscription",
|
|
61
|
+
authMode: row["auth_mode"] === "api-key" ? "api-key" : row["auth_mode"] === "agent-login" ? "agent-login" : "subscription",
|
|
61
62
|
openedAt: String(row["opened_at"]),
|
|
62
63
|
firstRun: row["first_run"] == null ? null : Number(row["first_run"]),
|
|
63
64
|
taskRef: row["task_ref"] == null ? null : Number(row["task_ref"]),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { run, type ExecResult } from "./exec.js";
|
|
2
2
|
import { type ProviderId } from "./provider.js";
|
|
3
|
+
import { type KeyedProviderId } from "./acp-agents.js";
|
|
3
4
|
import { type AuthMode, type KeyVerdict } from "./keys.js";
|
|
4
5
|
export type ProviderConnection = {
|
|
5
6
|
state: "connected" | "signed-out" | "not-installed" | "unverified" | "key-present" | "key-works" | "key-refused" | "missing-key";
|
|
@@ -18,5 +19,5 @@ export declare function createConnectionChecker(options?: {
|
|
|
18
19
|
home?: string;
|
|
19
20
|
clock?: () => Date;
|
|
20
21
|
env?: NodeJS.ProcessEnv;
|
|
21
|
-
verify?: (provider:
|
|
22
|
+
verify?: (provider: KeyedProviderId, key: string) => Promise<KeyVerdict>;
|
|
22
23
|
}): ConnectionChecker;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { run } from "./exec.js";
|
|
4
4
|
import { ALL_CREDENTIAL_ENV, inspectionOf } from "./provider.js";
|
|
5
|
+
import { isAcpProvider } from "./acp-agents.js";
|
|
5
6
|
import { keyStatus, PROVIDER_KEY_ENV, readAuthMode, readProviderKey, verifyProviderKey } from "./keys.js";
|
|
6
7
|
export function signInFacts(provider, result, now = new Date()) {
|
|
7
8
|
const base = { mode: "subscription", checkedAt: now.toISOString() };
|
|
@@ -46,6 +47,10 @@ export function createConnectionChecker(options = {}) {
|
|
|
46
47
|
const cache = new Map();
|
|
47
48
|
const pending = new Map();
|
|
48
49
|
return async (provider, fresh = false) => {
|
|
50
|
+
// An ACP agent is never run to ask how it is signed in (nothing executes it outside the gateway): what is known
|
|
51
|
+
// of it is its conformance check, read elsewhere. Here it is simply unverified.
|
|
52
|
+
if (isAcpProvider(provider))
|
|
53
|
+
return { state: "unverified", mode: "agent-login", checkedAt: clock().toISOString() };
|
|
49
54
|
const mode = readAuthMode(provider, home);
|
|
50
55
|
const keyFacts = keyStatus(provider, home);
|
|
51
56
|
const ambient = !!(options.env ?? process.env)[PROVIDER_KEY_ENV[provider]];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** The operator's own choices a no-model build keeps. */
|
|
2
|
+
export type ProviderDefaults = {
|
|
3
|
+
model?: string;
|
|
4
|
+
alwaysThinking?: boolean;
|
|
5
|
+
effort?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function claudeConfigDir(env?: Readonly<Record<string, string | undefined>>, home?: string): string;
|
|
8
|
+
export declare function codexHomeDir(env?: Readonly<Record<string, string | undefined>>, home?: string): string;
|
|
9
|
+
/** Claude's user settings: its `model` and `alwaysThinkingEnabled`, when valid. */
|
|
10
|
+
export declare function claudeDefaults(configDir: string): ProviderDefaults;
|
|
11
|
+
/** Codex's config.toml: its top-level `model` and `model_reasoning_effort`, or the selected profile's. */
|
|
12
|
+
export declare function codexDefaults(codexHome: string): ProviderDefaults;
|
|
13
|
+
/**
|
|
14
|
+
* One Codex launch's CODEX_HOME: a private folder linking the operator's
|
|
15
|
+
* sign-in and sessions. A missing `auth.json` stays missing (an API-key
|
|
16
|
+
* build needs none); a missing `sessions/` is made where Codex would make
|
|
17
|
+
* it, so a resumed build finds the thread its first launch wrote.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isolatedCodexHome(source: string, root?: string): {
|
|
20
|
+
dir: string;
|
|
21
|
+
cleanup: () => void;
|
|
22
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a build keeps of the provider's own home, and nothing more.
|
|
3
|
+
*
|
|
4
|
+
* A build loads none of the operator's Claude settings or Codex config
|
|
5
|
+
* (provider.ts), so two things from them are carried explicitly:
|
|
6
|
+
*
|
|
7
|
+
* - A build routed with no model runs as the operator's own CLI would: the
|
|
8
|
+
* model they chose (Claude's settings.json `model`, Codex's config.toml
|
|
9
|
+
* `model`), Claude's `alwaysThinkingEnabled` and Codex's reasoning effort.
|
|
10
|
+
* Nothing else of either file (hooks, plugins, MCP servers) is read.
|
|
11
|
+
* - A Codex build gets its own CODEX_HOME for the launch: links to the
|
|
12
|
+
* operator's `auth.json` and `sessions/` (so it signs in, and a repair
|
|
13
|
+
* resumes the same thread), and no skills, AGENTS.md or anything else from
|
|
14
|
+
* theirs. Links, never copies: a refreshed sign-in lands in the real file.
|
|
15
|
+
* The folder is fenced from the agent's commands and removed after the run.
|
|
16
|
+
*/
|
|
17
|
+
import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync } from "node:fs";
|
|
18
|
+
import { homedir, tmpdir } from "node:os";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { validModelId } from "./provider.js";
|
|
21
|
+
const EFFORT = /^[a-z]{1,16}$/;
|
|
22
|
+
export function claudeConfigDir(env = process.env, home = homedir()) {
|
|
23
|
+
return env["CLAUDE_CONFIG_DIR"] || join(home, ".claude");
|
|
24
|
+
}
|
|
25
|
+
export function codexHomeDir(env = process.env, home = homedir()) {
|
|
26
|
+
return env["CODEX_HOME"] || join(home, ".codex");
|
|
27
|
+
}
|
|
28
|
+
/** Claude's user settings: its `model` and `alwaysThinkingEnabled`, when valid. */
|
|
29
|
+
export function claudeDefaults(configDir) {
|
|
30
|
+
let settings;
|
|
31
|
+
try {
|
|
32
|
+
settings = JSON.parse(readFileSync(join(configDir, "settings.json"), "utf8"));
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
if (settings === null || typeof settings !== "object" || Array.isArray(settings))
|
|
38
|
+
return {};
|
|
39
|
+
const { model, alwaysThinkingEnabled } = settings;
|
|
40
|
+
return {
|
|
41
|
+
...(typeof model === "string" && model !== "" && validModelId(model) ? { model } : {}),
|
|
42
|
+
...(typeof alwaysThinkingEnabled === "boolean" ? { alwaysThinking: alwaysThinkingEnabled } : {}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Codex's config.toml: its top-level `model` and `model_reasoning_effort`, or the selected profile's. */
|
|
46
|
+
export function codexDefaults(codexHome) {
|
|
47
|
+
let text;
|
|
48
|
+
try {
|
|
49
|
+
text = readFileSync(join(codexHome, "config.toml"), "utf8");
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
const tables = new Map([["", {}]]);
|
|
55
|
+
let table = "";
|
|
56
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
57
|
+
const line = raw.trim();
|
|
58
|
+
const header = /^\[\s*([^\]]+?)\s*\]$/.exec(line);
|
|
59
|
+
if (header !== null) {
|
|
60
|
+
table = header[1].replace(/"/g, "");
|
|
61
|
+
if (!tables.has(table))
|
|
62
|
+
tables.set(table, {});
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const pair = /^([A-Za-z0-9_]+)\s*=\s*"([^"\\]*)"\s*(?:#.*)?$/.exec(line);
|
|
66
|
+
if (pair !== null)
|
|
67
|
+
tables.get(table)[pair[1]] = pair[2];
|
|
68
|
+
}
|
|
69
|
+
const top = tables.get("");
|
|
70
|
+
const profile = top["profile"] === undefined ? {} : tables.get(`profiles.${top["profile"]}`) ?? {};
|
|
71
|
+
const model = profile["model"] ?? top["model"];
|
|
72
|
+
const effort = profile["model_reasoning_effort"] ?? top["model_reasoning_effort"];
|
|
73
|
+
return {
|
|
74
|
+
...(model !== undefined && model !== "" && validModelId(model) ? { model } : {}),
|
|
75
|
+
...(effort !== undefined && EFFORT.test(effort) ? { effort } : {}),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* One Codex launch's CODEX_HOME: a private folder linking the operator's
|
|
80
|
+
* sign-in and sessions. A missing `auth.json` stays missing (an API-key
|
|
81
|
+
* build needs none); a missing `sessions/` is made where Codex would make
|
|
82
|
+
* it, so a resumed build finds the thread its first launch wrote.
|
|
83
|
+
*/
|
|
84
|
+
export function isolatedCodexHome(source, root = tmpdir()) {
|
|
85
|
+
const dir = mkdtempSync(join(root, "so-codex-"));
|
|
86
|
+
try {
|
|
87
|
+
chmodSync(dir, 0o700);
|
|
88
|
+
if (existsSync(join(source, "auth.json")))
|
|
89
|
+
symlinkSync(join(source, "auth.json"), join(dir, "auth.json"));
|
|
90
|
+
mkdirSync(join(source, "sessions"), { recursive: true, mode: 0o700 });
|
|
91
|
+
symlinkSync(join(source, "sessions"), join(dir, "sessions"), "dir");
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
rmSync(dir, { recursive: true, force: true });
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
// rm removes the links themselves, never what they point to.
|
|
98
|
+
return { dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
|
|
99
|
+
}
|
package/dist/provider.d.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* TOML-quoted deterministically, and the API key excluded from every
|
|
19
19
|
* shell the model itself launches (Codex provider review, Q5).
|
|
20
20
|
*/
|
|
21
|
+
import { type FenceGrants } from "./agent-fence.js";
|
|
21
22
|
import { type ExecResult, type RunOptions } from "./exec.js";
|
|
22
23
|
import { MODEL_ID, type AgentSpec, type ProviderId } from "./contracts/provider.js";
|
|
23
24
|
export type { AgentSpec, ProviderId } from "./contracts/provider.js";
|
|
@@ -64,6 +65,17 @@ export type Invocation = {
|
|
|
64
65
|
* Codex renders it as its own sandbox profile; Claude as deny rules for
|
|
65
66
|
* its file tools (its shell is fenced by the macOS sandbox around it). */
|
|
66
67
|
fence?: readonly string[];
|
|
68
|
+
/** What the fence gives back (agent-fence.ts): Codex renders read-only
|
|
69
|
+
* paths as "read", Claude's file tools never edit them. */
|
|
70
|
+
fenceGrants?: FenceGrants;
|
|
71
|
+
/** The operator's own model choices, for a build routed with no model
|
|
72
|
+
* (provider-home.ts): the model, Claude's always-on thinking and Codex's
|
|
73
|
+
* reasoning effort. Never rendered for a review or an explicit model. */
|
|
74
|
+
inherited?: {
|
|
75
|
+
model?: string;
|
|
76
|
+
alwaysThinking?: boolean;
|
|
77
|
+
effort?: string;
|
|
78
|
+
};
|
|
67
79
|
/** More paths Claude's own file tools refuse, beside the fence: home
|
|
68
80
|
* configuration its CLI still reads itself (so the sandbox around the
|
|
69
81
|
* whole process can't deny them). */
|
|
@@ -163,6 +175,29 @@ export type ParsedEnvelope = {
|
|
|
163
175
|
* failure terminal seen.
|
|
164
176
|
*/
|
|
165
177
|
structuralTerminal: import("./exhaustion.js").StructuralTerminal | null;
|
|
178
|
+
/** What an ACP agent itself reported it used — kept on the run, never priced. Absent for every other dialect. */
|
|
179
|
+
agentReportedUsage?: string | null;
|
|
180
|
+
/** An ACP conversation's facts the conformance check reads (acp-client.ts AcpEnvelope): nothing else does. */
|
|
181
|
+
acp?: {
|
|
182
|
+
offered: string[];
|
|
183
|
+
exit: {
|
|
184
|
+
how: "eof" | "term" | "kill" | "self" | "error";
|
|
185
|
+
leftover: boolean;
|
|
186
|
+
timedOut: boolean;
|
|
187
|
+
} | null;
|
|
188
|
+
agent: {
|
|
189
|
+
name: string;
|
|
190
|
+
version: string;
|
|
191
|
+
} | null;
|
|
192
|
+
stop: string | null;
|
|
193
|
+
/** The conversation broke the protocol (acpParse's protocolError), or null. */
|
|
194
|
+
protocolError: string | null;
|
|
195
|
+
/** The plane's isolation probes for a check's first step (invoke.ts), when it ran them. */
|
|
196
|
+
isolation?: {
|
|
197
|
+
fileDenied: boolean;
|
|
198
|
+
envClear: boolean;
|
|
199
|
+
};
|
|
200
|
+
};
|
|
166
201
|
};
|
|
167
202
|
type Adapter = {
|
|
168
203
|
binary: string;
|
|
@@ -199,6 +234,7 @@ export declare const OPENROUTER_ENV_KEY = "OPENROUTER_API_KEY";
|
|
|
199
234
|
* planted user, project and local hooks and MCP servers: none loaded.
|
|
200
235
|
*/
|
|
201
236
|
export declare const CLAUDE_BUILD_ISOLATION_ARGV: readonly string[];
|
|
237
|
+
export declare function claudePluginEnv(env: Readonly<Record<string, string | undefined>>): string[];
|
|
202
238
|
/** Claude's own tools that only make sense with a person to wake them. */
|
|
203
239
|
export declare const HEADLESS_DISALLOWED_TOOLS: readonly string[];
|
|
204
240
|
/**
|
|
@@ -221,9 +257,10 @@ export declare function isolationUnsupported(provider: ProviderId, stderr: strin
|
|
|
221
257
|
* A Codex build's own setup, the equivalent of Claude's: the operator's
|
|
222
258
|
* config.toml (its MCP servers, model choices, profiles) and execpolicy
|
|
223
259
|
* rules ignored, ChatGPT apps and plugins off (plugins carry their own MCP
|
|
224
|
-
* servers), and hooks off.
|
|
225
|
-
*
|
|
226
|
-
*
|
|
260
|
+
* servers), and hooks off. The gateway gives each launch its own CODEX_HOME
|
|
261
|
+
* linking only the operator's sign-in and sessions (provider-home.ts), so no
|
|
262
|
+
* skills or AGENTS.md of theirs load either. The project's named tools follow
|
|
263
|
+
* as `-c mcp_servers.*` (project-tools.ts).
|
|
227
264
|
*/
|
|
228
265
|
export declare const CODEX_BUILD_ISOLATION_ARGV: readonly string[];
|
|
229
266
|
/**
|
|
@@ -252,13 +289,13 @@ export declare function adapterFor(provider: ProviderId): Adapter;
|
|
|
252
289
|
*/
|
|
253
290
|
export type ProviderAudit = {
|
|
254
291
|
/** How output reaches us — and therefore which signals can exist at all. */
|
|
255
|
-
transport: "buffered-json" | "streaming-jsonl";
|
|
292
|
+
transport: "buffered-json" | "streaming-jsonl" | "acp-jsonrpc";
|
|
256
293
|
/** Whether a later invocation can resume this provider's session. */
|
|
257
294
|
resume: "native" | "none";
|
|
258
295
|
/** The event whose absence on a failed run means "never initialized".
|
|
259
296
|
* CAPABILITY metadata, not a per-run observation (arc 1 finding 16):
|
|
260
297
|
* whether a given run actually saw it lives in ParsedEnvelope.initObserved. */
|
|
261
|
-
initSignal: "thread.started" | "system-init" | "init-event" | "none";
|
|
298
|
+
initSignal: "thread.started" | "system-init" | "init-event" | "acp-session" | "none";
|
|
262
299
|
/** How session identity is established: "announced" = read back from the
|
|
263
300
|
* harness's own stream; "minted" = the PLANE chooses the id pre-spawn
|
|
264
301
|
* (gemini --session-id) and the envelope must echo it (Phase 3 A5). */
|
package/dist/provider.js
CHANGED
|
@@ -18,13 +18,16 @@
|
|
|
18
18
|
* TOML-quoted deterministically, and the API key excluded from every
|
|
19
19
|
* shell the model itself launches (Codex provider review, Q5).
|
|
20
20
|
*/
|
|
21
|
+
import { INTERPRETER_INJECTION_ENV } from "./acp-attest.js";
|
|
21
22
|
import { claudeFenceSettings, codexFenceArgv } from "./agent-fence.js";
|
|
22
23
|
import { claudeSteerHooks } from "./steer-mailbox.js";
|
|
23
24
|
import {} from "./exec.js";
|
|
24
|
-
import { runStreamJsonl, runClaudeStreamJsonl, runGeminiStreamJsonl, CLAUDE_COST_UNKNOWN } from "./exec.js";
|
|
25
|
+
import { runStreamJsonl, runClaudeStreamJsonl, runGeminiStreamJsonl, runAcpSession, CLAUDE_COST_UNKNOWN } from "./exec.js";
|
|
25
26
|
import { scanForSecrets } from "./evidence.js";
|
|
26
27
|
import { FINDINGS_MODEL_SCHEMA } from "./contracts/review-findings.js";
|
|
27
28
|
import { REVIEW_OUTPUT_LIMITS } from "./structured-output.js";
|
|
29
|
+
import { ACP_AGENTS, isAcpProvider, withAcp } from "./acp-agents.js";
|
|
30
|
+
import { acpParse } from "./acp-client.js";
|
|
28
31
|
import { agentSpecSchema, MODEL_ID, modelIdSchema, PROVIDER_ID_VALUES, providerIdSchema } from "./contracts/provider.js";
|
|
29
32
|
export const PROVIDER_IDS = PROVIDER_ID_VALUES;
|
|
30
33
|
export function isProviderId(value) {
|
|
@@ -181,6 +184,16 @@ const CLAUDE_REVIEW_JSON_SCHEMA = {
|
|
|
181
184
|
* planted user, project and local hooks and MCP servers: none loaded.
|
|
182
185
|
*/
|
|
183
186
|
export const CLAUDE_BUILD_ISOLATION_ARGV = ["--setting-sources", "", "--strict-mcp-config"];
|
|
187
|
+
/**
|
|
188
|
+
* Plugins also ride the environment: a plugin named in CLAUDE_CODE_PLUGIN_DIRS
|
|
189
|
+
* loads its hooks whatever `--setting-sources` says (Claude Code 2.1.290,
|
|
190
|
+
* the provider canary's plugin sentinel). A build's launch sheds every
|
|
191
|
+
* plugin variable of the environment it would inherit.
|
|
192
|
+
*/
|
|
193
|
+
const CLAUDE_PLUGIN_ENV = /^CLAUDE_(CODE_\w*PLUGIN|AI_PLUGINS)\w*$/;
|
|
194
|
+
export function claudePluginEnv(env) {
|
|
195
|
+
return Object.keys(env).filter(name => CLAUDE_PLUGIN_ENV.test(name));
|
|
196
|
+
}
|
|
184
197
|
const EMPTY_MCP_CONFIG = '{"mcpServers":{}}';
|
|
185
198
|
/** Claude's own tools that only make sense with a person to wake them. */
|
|
186
199
|
export const HEADLESS_DISALLOWED_TOOLS = ["ScheduleWakeup", "CronCreate", "Monitor"];
|
|
@@ -203,7 +216,7 @@ const claudeArgv = (invocation) => [
|
|
|
203
216
|
: invocation.skipPermissions
|
|
204
217
|
? ["--dangerously-skip-permissions"]
|
|
205
218
|
: ["--permission-mode", invocation.permissionMode]),
|
|
206
|
-
...(
|
|
219
|
+
...modelArgv("--model", invocation),
|
|
207
220
|
...(invocation.maxBudgetUsd === undefined ? [] : ["--max-budget-usd", String(invocation.maxBudgetUsd)]),
|
|
208
221
|
...(invocation.phase === "review"
|
|
209
222
|
? [...CLAUDE_REVIEW_ISOLATION_ARGV, "--json-schema", JSON.stringify(CLAUDE_REVIEW_JSON_SCHEMA)]
|
|
@@ -222,12 +235,22 @@ const claudeArgv = (invocation) => [
|
|
|
222
235
|
...(invocation.jsonSchema === undefined ? [] : ["--json-schema", JSON.stringify(invocation.jsonSchema)])
|
|
223
236
|
]),
|
|
224
237
|
];
|
|
238
|
+
/** The route's model, or for a build routed with none, the operator's own (never for a review). */
|
|
239
|
+
function modelArgv(flag, invocation) {
|
|
240
|
+
const model = invocation.model ?? (invocation.phase === "review" ? undefined : invocation.inherited?.model);
|
|
241
|
+
return model === undefined ? [] : [flag, model];
|
|
242
|
+
}
|
|
243
|
+
/** What else a no-model build keeps of the operator's setup. */
|
|
244
|
+
const inheritedFrom = (invocation) => invocation.model === null && invocation.phase !== "review" ? invocation.inherited ?? {} : {};
|
|
225
245
|
/** The build's one --settings: no personal memory, plus the fence and steering doorbell. */
|
|
226
246
|
function claudeSettingsArgv(invocation) {
|
|
227
247
|
const denied = [...(invocation.fence ?? []), ...(invocation.fileToolFence ?? [])];
|
|
248
|
+
const readOnly = invocation.fenceGrants?.readOnly ?? [];
|
|
249
|
+
const thinking = inheritedFrom(invocation).alwaysThinking;
|
|
228
250
|
return ["--settings", JSON.stringify({
|
|
229
251
|
autoMemoryEnabled: false,
|
|
230
|
-
...(
|
|
252
|
+
...(thinking === undefined ? {} : { alwaysThinkingEnabled: thinking }),
|
|
253
|
+
...(denied.length > 0 || readOnly.length > 0 ? JSON.parse(claudeFenceSettings(denied, readOnly)) : {}),
|
|
231
254
|
...(invocation.steerFolder === undefined ? {} : { hooks: claudeSteerHooks(invocation.steerFolder.dir, invocation.steerFolder.name) }),
|
|
232
255
|
})];
|
|
233
256
|
}
|
|
@@ -421,9 +444,10 @@ const codexSandboxArgv = (mode, resuming) => resuming ? ["-c", `sandbox_mode="${
|
|
|
421
444
|
* A Codex build's own setup, the equivalent of Claude's: the operator's
|
|
422
445
|
* config.toml (its MCP servers, model choices, profiles) and execpolicy
|
|
423
446
|
* rules ignored, ChatGPT apps and plugins off (plugins carry their own MCP
|
|
424
|
-
* servers), and hooks off.
|
|
425
|
-
*
|
|
426
|
-
*
|
|
447
|
+
* servers), and hooks off. The gateway gives each launch its own CODEX_HOME
|
|
448
|
+
* linking only the operator's sign-in and sessions (provider-home.ts), so no
|
|
449
|
+
* skills or AGENTS.md of theirs load either. The project's named tools follow
|
|
450
|
+
* as `-c mcp_servers.*` (project-tools.ts).
|
|
427
451
|
*/
|
|
428
452
|
export const CODEX_BUILD_ISOLATION_ARGV = [
|
|
429
453
|
"--ignore-user-config",
|
|
@@ -469,11 +493,12 @@ const codexArgv = (extra) => (invocation) => {
|
|
|
469
493
|
// denied. Full access becomes that sandbox widened to write anywhere
|
|
470
494
|
// with network, instead of no sandbox (which fenced nothing).
|
|
471
495
|
: invocation.fence !== undefined && invocation.fence.length > 0
|
|
472
|
-
? codexFenceArgv(invocation.fence, invocation.skipPermissions)
|
|
496
|
+
? codexFenceArgv(invocation.fence, invocation.skipPermissions, invocation.fenceGrants)
|
|
473
497
|
: invocation.skipPermissions
|
|
474
498
|
? ["--dangerously-bypass-approvals-and-sandbox"]
|
|
475
499
|
: codexSandboxArgv("workspace-write", resuming)),
|
|
476
|
-
...(
|
|
500
|
+
...modelArgv("-m", invocation),
|
|
501
|
+
...(inheritedFrom(invocation).effort === undefined ? [] : ["-c", `model_reasoning_effort=${toml(inheritedFrom(invocation).effort)}`]),
|
|
477
502
|
...extra,
|
|
478
503
|
// After `extra`: the tools' shell_environment_policy must be the last word (it keeps OpenRouter's key out too).
|
|
479
504
|
...(invocation.phase === "review" ? [] : invocation.toolArgv ?? []),
|
|
@@ -765,12 +790,13 @@ export function safeDiagnostic(text) {
|
|
|
765
790
|
* provider gets its OWN keys and nobody else's: S4 (live spike) confirmed
|
|
766
791
|
* what inheritance makes true by construction — an agent's shell reads its
|
|
767
792
|
* process env, so a foreign key in that env is a foreign key disclosed. */
|
|
768
|
-
const CREDENTIAL_ENV = {
|
|
793
|
+
const CREDENTIAL_ENV = withAcp({
|
|
769
794
|
claude: ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN"],
|
|
770
795
|
codex: ["OPENAI_API_KEY", "CODEX_API_KEY"],
|
|
771
796
|
openrouter: [OPENROUTER_ENV_KEY],
|
|
772
797
|
gemini: ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
|
773
|
-
|
|
798
|
+
// An ACP agent signs in its own way; Toolroll holds no key for it, so it sheds every key above.
|
|
799
|
+
}, () => []);
|
|
774
800
|
const foreignCredentialEnv = (own) => Object.entries(CREDENTIAL_ENV)
|
|
775
801
|
.filter(([provider]) => provider !== own)
|
|
776
802
|
.flatMap(([, keys]) => [...keys]);
|
|
@@ -778,7 +804,40 @@ const foreignCredentialEnv = (own) => Object.entries(CREDENTIAL_ENV)
|
|
|
778
804
|
* since a feature check needs no key at all (Codex gemini verify,
|
|
779
805
|
* finding 2). Agent spawns keep their own; probes keep none. */
|
|
780
806
|
export const ALL_CREDENTIAL_ENV = Object.values(CREDENTIAL_ENV).flat();
|
|
781
|
-
|
|
807
|
+
/**
|
|
808
|
+
* An ACP agent's retained record (acp-client.ts) read into the gateway's
|
|
809
|
+
* envelope. Its own usage report is never priced: tokens and cost stay null.
|
|
810
|
+
*/
|
|
811
|
+
function acpEnvelope(stdout) {
|
|
812
|
+
const read = acpParse(stdout);
|
|
813
|
+
return {
|
|
814
|
+
sessionId: read.sessionId,
|
|
815
|
+
protocolError: read.protocolError,
|
|
816
|
+
finalMessage: read.finalMessage,
|
|
817
|
+
tokensIn: null,
|
|
818
|
+
tokensOut: null,
|
|
819
|
+
costUsd: null,
|
|
820
|
+
usageRaw: null,
|
|
821
|
+
initObserved: read.initObserved,
|
|
822
|
+
promptConsumed: read.promptConsumed,
|
|
823
|
+
diagnostic: read.diagnostic === null ? null : safeDiagnostic(read.diagnostic),
|
|
824
|
+
structuralTerminal: read.terminal === null ? null : { failed: true, text: read.terminal.text === null ? null : safeDiagnostic(read.terminal.text), code: read.terminal.code.slice(0, 128) },
|
|
825
|
+
agentReportedUsage: read.reportedUsage,
|
|
826
|
+
acp: { offered: read.offered, exit: read.exit, agent: read.agent, stop: read.stop, protocolError: read.protocolError },
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
function acpAdapter(provider) {
|
|
830
|
+
return {
|
|
831
|
+
binary: ACP_AGENTS[provider].binary,
|
|
832
|
+
argv: () => [...ACP_AGENTS[provider].argv],
|
|
833
|
+
parse: acpEnvelope,
|
|
834
|
+
defaultRunner: runAcpSession,
|
|
835
|
+
// Every key Toolroll knows, and whatever would make the pinned interpreter load code beside the checked script.
|
|
836
|
+
extraOmitEnv: [...foreignCredentialEnv(provider), ...INTERPRETER_INJECTION_ENV],
|
|
837
|
+
clampTimeout: (_phase, requested) => requested,
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
const ADAPTERS = withAcp({
|
|
782
841
|
claude: {
|
|
783
842
|
binary: "claude",
|
|
784
843
|
argv: claudeArgv,
|
|
@@ -830,7 +889,7 @@ const ADAPTERS = {
|
|
|
830
889
|
extraOmitEnv: foreignCredentialEnv("gemini"),
|
|
831
890
|
clampTimeout: (phase, requested) => Math.min(requested, GEMINI_TIMEOUT_CAP_MS[phase]),
|
|
832
891
|
},
|
|
833
|
-
};
|
|
892
|
+
}, (_agent, provider) => acpAdapter(provider));
|
|
834
893
|
/**
|
|
835
894
|
* The spawning surface. Imported by invoke.ts and NOWHERE else — the
|
|
836
895
|
* architecture test reads imports, not string literals, because provider
|
|
@@ -839,7 +898,7 @@ const ADAPTERS = {
|
|
|
839
898
|
export function adapterFor(provider) {
|
|
840
899
|
return ADAPTERS[provider];
|
|
841
900
|
}
|
|
842
|
-
const AUDITS = {
|
|
901
|
+
const AUDITS = withAcp({
|
|
843
902
|
claude: {
|
|
844
903
|
transport: "streaming-jsonl",
|
|
845
904
|
resume: "native",
|
|
@@ -851,6 +910,7 @@ const AUDITS = {
|
|
|
851
910
|
isolation: { flag: CLAUDE_BUILD_ISOLATION_ARGV.filter(one => one !== "").join(" "), resumeSafe: true, enforced: true },
|
|
852
911
|
configSurface: [
|
|
853
912
|
"Toolroll's own --settings (the fence and steering hook)",
|
|
913
|
+
"your model and alwaysThinkingEnabled, when the route names no model",
|
|
854
914
|
"the project's named tools (Settings → Tools)",
|
|
855
915
|
"repository CLAUDE.md",
|
|
856
916
|
"organisation-managed settings",
|
|
@@ -863,10 +923,11 @@ const AUDITS = {
|
|
|
863
923
|
sessionIdentity: "announced",
|
|
864
924
|
terminalContract: "none",
|
|
865
925
|
// Builds ignore config.toml and execpolicy rules, with apps, plugins and
|
|
866
|
-
// hooks off (CODEX_BUILD_ISOLATION_ARGV)
|
|
926
|
+
// hooks off (CODEX_BUILD_ISOLATION_ARGV), in a per-launch CODEX_HOME that
|
|
927
|
+
// links only the sign-in and sessions (provider-home.ts).
|
|
867
928
|
// Never --ephemeral: repair resumes sessions.
|
|
868
929
|
isolation: { flag: "--ignore-user-config --ignore-rules", resumeSafe: true, enforced: true },
|
|
869
|
-
configSurface: ["the project's named tools (Settings → Tools)", "
|
|
930
|
+
configSurface: ["the project's named tools (Settings → Tools)", "repository AGENTS.md", "sign-in and sessions linked from CODEX_HOME (no skills or AGENTS.md)", "your model and reasoning effort, when the route names no model"],
|
|
870
931
|
},
|
|
871
932
|
openrouter: {
|
|
872
933
|
transport: "streaming-jsonl",
|
|
@@ -876,7 +937,7 @@ const AUDITS = {
|
|
|
876
937
|
terminalContract: "none",
|
|
877
938
|
// Codex's build setup, with the model provider pinned by constant -c overrides.
|
|
878
939
|
isolation: { flag: "--ignore-user-config --ignore-rules", resumeSafe: true, enforced: true },
|
|
879
|
-
configSurface: ["the project's named tools (Settings → Tools)", "
|
|
940
|
+
configSurface: ["the project's named tools (Settings → Tools)", "repository AGENTS.md", `${OPENROUTER_ENV_KEY}`],
|
|
880
941
|
},
|
|
881
942
|
gemini: {
|
|
882
943
|
transport: "streaming-jsonl",
|
|
@@ -899,8 +960,17 @@ const AUDITS = {
|
|
|
899
960
|
"trusted-folder store (S2, live at 0.57.0: headless REFUSES untrusted directories; the plane grants --skip-trust per-invocation to its own leased worktrees)",
|
|
900
961
|
],
|
|
901
962
|
},
|
|
902
|
-
}
|
|
903
|
-
|
|
963
|
+
}, agent => ({
|
|
964
|
+
// One ACP conversation per build: the prompt's answer is the only success (acp-client.ts), so exit 0 alone is never believed.
|
|
965
|
+
transport: "acp-jsonrpc",
|
|
966
|
+
resume: "none",
|
|
967
|
+
initSignal: "acp-session",
|
|
968
|
+
sessionIdentity: "announced",
|
|
969
|
+
terminalContract: "required",
|
|
970
|
+
isolation: { flag: agent.argv.filter(one => one.startsWith("--") && one !== "--acp").join(" ") || null, resumeSafe: null, enforced: agent.argv.some(one => one.startsWith("--") && one !== "--acp") },
|
|
971
|
+
configSurface: [...agent.configSurface],
|
|
972
|
+
}));
|
|
973
|
+
export const MONEY_CAPABILITIES = withAcp({
|
|
904
974
|
claude: {
|
|
905
975
|
incrementalUsage: true,
|
|
906
976
|
nativeDollarCapFlag: "--max-budget-usd",
|
|
@@ -923,7 +993,12 @@ export const MONEY_CAPABILITIES = {
|
|
|
923
993
|
// numbers cover that invocation only (conformance fixture j).
|
|
924
994
|
usageSemantics: "per-invocation",
|
|
925
995
|
},
|
|
926
|
-
}
|
|
996
|
+
}, () => ({
|
|
997
|
+
// ACP carries no dollars Toolroll trusts: an agent's own usage report is kept apart, unpriced.
|
|
998
|
+
incrementalUsage: false,
|
|
999
|
+
nativeDollarCapFlag: null,
|
|
1000
|
+
usageSemantics: "per-invocation",
|
|
1001
|
+
}));
|
|
927
1002
|
/**
|
|
928
1003
|
* The fail-closed budget-flag probe (finding 24's amendment): resolve
|
|
929
1004
|
* the EXACT executable that will spawn, read its version, and prove the
|
|
@@ -973,6 +1048,9 @@ export function validateSpec(spec) {
|
|
|
973
1048
|
if (spec.provider === "gemini" && spec.model === null) {
|
|
974
1049
|
return { ok: false, problem: "model: gemini needs an explicit model — the harness default drifts with its releases" };
|
|
975
1050
|
}
|
|
1051
|
+
if (isAcpProvider(spec.provider) && spec.model === null) {
|
|
1052
|
+
return { ok: false, problem: `model: ${ACP_AGENTS[spec.provider].name} needs an explicit model — one it offered when it was checked` };
|
|
1053
|
+
}
|
|
976
1054
|
return { ok: true };
|
|
977
1055
|
}
|
|
978
1056
|
/**
|
package/dist/pull-request-ui.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** Settings → Projects → Pull requests: one project's pull-request setup, in plain words. Off shows what setup
|
|
2
2
|
* found (or what to fix) and one Turn on; on shows where pull requests go and how they merge. Every change takes
|
|
3
3
|
* the password; turning on restates the exact repository and branch that were checked. */
|
|
4
|
-
import { html, postForm } from "./html.js";
|
|
4
|
+
import { html, postForm, reauth } from "./html.js";
|
|
5
5
|
const METHOD_WORDS = { squash: "Squash and merge", merge: "Merge commit", rebase: "Rebase and merge" };
|
|
6
6
|
export function pullRequestSettingsHtml(view) {
|
|
7
7
|
const post = (act, body, hidden = {}) => postForm("/settings/pull-requests", body, { attrs: { class: "card pr-settings" }, hidden: { repo: view.repo, act, ...hidden } });
|
|
8
|
-
const password = html `<label>Password<input type="password" name="password" autocomplete="current-password" required></label
|
|
8
|
+
const password = html `${reauth(html `<label>Password<input type="password" name="password" autocomplete="current-password" required></label>`)}`;
|
|
9
9
|
const notes = [
|
|
10
10
|
view.said === null ? "" : html `<p class="notice" role="status">${view.said}</p>`,
|
|
11
11
|
view.problem === null ? "" : html `<p class="problem" role="alert">${view.problem}</p>`,
|
package/dist/push.js
CHANGED
|
@@ -259,7 +259,7 @@ export async function pushPass(store, options) {
|
|
|
259
259
|
// Renew and re-fence immediately before the network (finding 12).
|
|
260
260
|
if (!store.renewPushClaim(pair.id, owner, pair.claimGeneration, PUSH_CLAIM_TTL_MS, clock()))
|
|
261
261
|
continue;
|
|
262
|
-
const fenced = store.pushSendFence(pair.id, owner, pair.claimGeneration);
|
|
262
|
+
const fenced = store.pushSendFence(pair.id, owner, pair.claimGeneration, clock());
|
|
263
263
|
if (fenced === null)
|
|
264
264
|
continue; // resolved, retired, rotated, or lost — nothing to send
|
|
265
265
|
const subscription = fenced.subscriptionRow;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type AttachSeams } from "./console-instance.js";
|
|
2
|
+
export type QuickStartSeams = {
|
|
3
|
+
databaseFile: string;
|
|
4
|
+
cwd: string;
|
|
5
|
+
env: Record<string, string | undefined>;
|
|
6
|
+
build: string;
|
|
7
|
+
/** The repository's top folder for a directory, or null outside one. */
|
|
8
|
+
findRepo: (cwd: string) => Promise<string | null>;
|
|
9
|
+
/** Whether this installation already admitted the repository. */
|
|
10
|
+
enrolled: (repo: string) => Promise<boolean>;
|
|
11
|
+
/** The ordinary admission (`repos add`): its registry lock and ceilings, from this CLI's own database. */
|
|
12
|
+
admit: (repo: string) => Promise<number>;
|
|
13
|
+
/** `up`, for this repository. */
|
|
14
|
+
startUp: () => Promise<number>;
|
|
15
|
+
openBrowser: (url: string) => void | Promise<void>;
|
|
16
|
+
terminal: boolean;
|
|
17
|
+
attach?: AttachSeams;
|
|
18
|
+
/** Tests: the agent check (default: the environment and the process's ancestors). */
|
|
19
|
+
underAgent?: boolean;
|
|
20
|
+
};
|
|
21
|
+
/** Whether argv is a bare `toolroll` (nothing, or only --json). */
|
|
22
|
+
export declare const isBareInvocation: (argv: readonly string[]) => boolean;
|
|
23
|
+
/** null: not this command (outside a repository) — the caller scans as before. */
|
|
24
|
+
export declare function runQuickStart(argv: readonly string[], write: (line: string) => void, seams: QuickStartSeams): Promise<number | null>;
|