toolroll 0.9.59 → 0.9.61
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 +26 -0
- package/README.md +8 -6
- package/dist/agent-board.d.ts +208 -0
- package/dist/agent-board.js +454 -0
- package/dist/agent-fence.d.ts +19 -0
- package/dist/agent-fence.js +100 -8
- package/dist/agent-pulse.d.ts +43 -0
- package/dist/agent-pulse.js +214 -0
- package/dist/agent-turns.d.ts +52 -0
- package/dist/agent-turns.js +137 -0
- package/dist/agents-board.d.ts +101 -0
- package/dist/agents-board.js +225 -0
- package/dist/assignment-adapters.d.ts +2 -1
- package/dist/assignment-adapters.js +1 -1
- package/dist/assignment-delivery.d.ts +16 -0
- package/dist/assignment-delivery.js +54 -28
- package/dist/assignment-status.d.ts +14 -5
- package/dist/assignment-status.js +70 -39
- package/dist/assignment.d.ts +10 -2
- package/dist/assignment.js +21 -14
- package/dist/bin.js +11 -2
- package/dist/brief-fence.d.ts +9 -0
- package/dist/brief-fence.js +22 -0
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +29 -25
- package/dist/browser-crew.js +2 -0
- package/dist/browser-workspace.d.ts +79 -1
- package/dist/browser-workspace.js +1 -0
- package/dist/builder.d.ts +8 -0
- package/dist/builder.js +178 -29
- package/dist/chat-channel.js +10 -1
- package/dist/chat-core.d.ts +6 -0
- package/dist/chat-core.js +8 -0
- package/dist/chat-delivery.js +22 -0
- package/dist/chat-messages.js +8 -1
- package/dist/chat-quiet.js +1 -0
- package/dist/chat-voice.d.ts +2 -0
- package/dist/chat-voice.js +2 -0
- package/dist/checkout-cleanup.d.ts +3 -0
- package/dist/checkout-cleanup.js +11 -2
- package/dist/cli-fast.d.ts +8 -0
- package/dist/cli-fast.js +18 -0
- package/dist/cli-help.d.ts +5 -0
- package/dist/cli-help.js +91 -0
- package/dist/cli.d.ts +6 -5
- package/dist/cli.js +17 -96
- package/dist/contracts/console-api.d.ts +3 -1
- package/dist/contracts/console-api.js +5 -1
- package/dist/contracts/lead-tools.d.ts +16 -2
- package/dist/contracts/lead-tools.js +5 -1
- package/dist/design/tokens.css +234 -0
- package/dist/desktop-update.d.ts +1 -1
- package/dist/discord.js +2 -1
- package/dist/evidence-pack.js +1 -1
- package/dist/evidence.d.ts +28 -0
- package/dist/evidence.js +87 -17
- package/dist/exec.d.ts +32 -0
- package/dist/exec.js +229 -10
- package/dist/flow-actions.js +2 -1
- package/dist/guides.js +14 -11
- package/dist/invoke.js +110 -18
- package/dist/landed-cleanup.d.ts +157 -0
- package/dist/landed-cleanup.js +707 -0
- package/dist/lead-cli.js +22 -19
- package/dist/lead-contract.js +1 -1
- package/dist/lead-doors.js +49 -2
- package/dist/lead-follow.d.ts +2 -0
- package/dist/lead-follow.js +138 -88
- package/dist/lead-tools.js +48 -8
- package/dist/live-input.d.ts +68 -0
- package/dist/live-input.js +254 -0
- package/dist/live.d.ts +1 -1
- package/dist/live.js +4 -2
- package/dist/mailbox.js +3 -2
- package/dist/maintenance.d.ts +2 -0
- package/dist/maintenance.js +5 -2
- package/dist/mcp-core.js +1 -1
- package/dist/operate-remote-arguments.js +5 -1
- package/dist/operate-remote.js +1 -1
- package/dist/operate.d.ts +1 -1
- package/dist/operate.js +201 -19
- package/dist/orphan-sweep.d.ts +2 -0
- package/dist/orphan-sweep.js +5 -3
- package/dist/project-delete-ui.d.ts +11 -0
- package/dist/project-delete-ui.js +29 -2
- package/dist/project-tools.d.ts +7 -11
- package/dist/project-tools.js +13 -37
- package/dist/provider.d.ts +73 -8
- package/dist/provider.js +87 -17
- package/dist/result-completion.d.ts +7 -0
- package/dist/result-completion.js +9 -0
- package/dist/runner.d.ts +4 -2
- package/dist/runner.js +3 -3
- package/dist/serve.js +22 -3
- package/dist/server/chrome.d.ts +6 -3
- package/dist/server/chrome.js +8 -4
- package/dist/server/console.css +9 -64
- package/dist/server/live.d.ts +3 -0
- package/dist/server/live.js +35 -2
- package/dist/server/pages.js +59 -35
- package/dist/server/render-chat.js +14 -2
- package/dist/server/render-tasks.d.ts +14 -2
- package/dist/server/render-tasks.js +38 -20
- package/dist/server/route-table.js +4 -1
- package/dist/server/settings.js +2 -1
- package/dist/server/task-views.js +10 -0
- package/dist/server/tasks.js +63 -0
- package/dist/shared-deps.d.ts +9 -4
- package/dist/shared-deps.js +89 -13
- package/dist/slack-socket.d.ts +12 -0
- package/dist/slack-socket.js +29 -0
- package/dist/slack.d.ts +0 -7
- package/dist/slack.js +2 -24
- package/dist/staged-runtimes.d.ts +16 -4
- package/dist/staged-runtimes.js +160 -38
- package/dist/steer-live.d.ts +19 -0
- package/dist/steer-live.js +93 -0
- package/dist/steer-mailbox.d.ts +39 -0
- package/dist/steer-mailbox.js +207 -0
- package/dist/steer-status.d.ts +25 -0
- package/dist/steer-status.js +44 -0
- package/dist/storage-sweep-child.d.ts +1 -0
- package/dist/storage-sweep-child.js +76 -0
- package/dist/storage-sweep-process.d.ts +41 -0
- package/dist/storage-sweep-process.js +187 -0
- package/dist/storage-sweep.d.ts +30 -6
- package/dist/storage-sweep.js +214 -28
- package/dist/storage-ui.d.ts +2 -0
- package/dist/storage-ui.js +16 -4
- package/dist/storage.d.ts +6 -0
- package/dist/storage.js +35 -0
- package/dist/store.d.ts +202 -2
- package/dist/store.js +440 -13
- package/dist/surface.js +5 -3
- package/dist/sweep-gate.d.ts +10 -0
- package/dist/sweep-gate.js +18 -0
- package/dist/sweep-quarantine.d.ts +28 -0
- package/dist/sweep-quarantine.js +153 -0
- package/dist/task-status.d.ts +2 -0
- package/dist/task-status.js +5 -1
- package/dist/team-updates.js +3 -3
- package/dist/test-temp.d.ts +6 -3
- package/dist/test-temp.js +15 -9
- package/dist/toolroll-update.js +15 -2
- package/dist/work-index.d.ts +1 -0
- package/dist/work-index.js +32 -12
- package/dist/work-summary.d.ts +2 -2
- package/dist/workspace-revision.js +3 -1
- package/dist/worktree.d.ts +3 -1
- package/dist/worktree.js +93 -10
- package/package.json +21 -21
- package/scripts/provider-canary.mjs +235 -147
package/dist/project-tools.js
CHANGED
|
@@ -540,7 +540,7 @@ function safeLine(text) {
|
|
|
540
540
|
}
|
|
541
541
|
const toml = (value) => `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
542
542
|
const tomlList = (values) => `[${values.map(toml).join(",")}]`;
|
|
543
|
-
/** Claude: only these servers
|
|
543
|
+
/** Claude: only these servers, written to a private 0600 file removed after the run (the provider adds `--strict-mcp-config`). */
|
|
544
544
|
export function claudeToolArgs(launch) {
|
|
545
545
|
const servers = {};
|
|
546
546
|
for (const tool of launch.tools) {
|
|
@@ -563,49 +563,27 @@ export function claudeToolArgs(launch) {
|
|
|
563
563
|
const file = join(dir, "mcp.json");
|
|
564
564
|
writeFileSync(file, JSON.stringify({ mcpServers: servers }), { mode: 0o600 });
|
|
565
565
|
return {
|
|
566
|
-
argv: ["--
|
|
566
|
+
argv: ["--mcp-config", file],
|
|
567
567
|
// A first `npx` download can take longer than the default start-up wait.
|
|
568
568
|
env: launch.tools.length > 0 ? { MCP_TIMEOUT: "60000" } : {},
|
|
569
569
|
omitEnv: [],
|
|
570
570
|
cleanup: () => rmSync(dir, { recursive: true, force: true }),
|
|
571
571
|
};
|
|
572
572
|
}
|
|
573
|
-
/** The operator's Codex model settings a build keeps when its tools are replaced: how it thinks, never what it can reach. */
|
|
574
|
-
const CARRIED_CODEX_SETTINGS = ["model_reasoning_effort", "model_reasoning_summary", "model_verbosity", "service_tier", "personality"];
|
|
575
|
-
export function carriedCodexSettings(includeModel, codexHome = process.env["CODEX_HOME"] ?? join(homedir(), ".codex")) {
|
|
576
|
-
let text = "";
|
|
577
|
-
try {
|
|
578
|
-
text = readFileSync(join(codexHome, "config.toml"), "utf8");
|
|
579
|
-
}
|
|
580
|
-
catch {
|
|
581
|
-
return [];
|
|
582
|
-
}
|
|
583
|
-
const wanted = includeModel ? ["model", ...CARRIED_CODEX_SETTINGS] : CARRIED_CODEX_SETTINGS;
|
|
584
|
-
const argv = [];
|
|
585
|
-
for (const line of text.split("\n")) {
|
|
586
|
-
if (/^\s*\[/.test(line))
|
|
587
|
-
break;
|
|
588
|
-
const match = /^\s*([a-z_]+)\s*=\s*("[^"\\\n]{0,80}"|[A-Za-z0-9_.-]{1,40})\s*(?:#.*)?$/.exec(line);
|
|
589
|
-
if (match !== null && wanted.includes(match[1]))
|
|
590
|
-
argv.push("-c", `${match[1]}=${match[2]}`);
|
|
591
|
-
}
|
|
592
|
-
return argv;
|
|
593
|
-
}
|
|
594
573
|
/** Where the tool launcher lives beside this module, built. */
|
|
595
574
|
export function toolLauncherPath() {
|
|
596
575
|
return fileURLToPath(new URL("./tool-launcher.js", import.meta.url));
|
|
597
576
|
}
|
|
598
577
|
/**
|
|
599
|
-
* Codex:
|
|
600
|
-
*
|
|
601
|
-
*
|
|
602
|
-
*
|
|
578
|
+
* Codex: each tool is named by `-c` and started through the launcher. The
|
|
579
|
+
* operator's config.toml, rules, apps, plugins and hooks are already off
|
|
580
|
+
* (the provider's CODEX_BUILD_ISOLATION_ARGV), and nothing of config.toml
|
|
581
|
+
* is carried. Codex gives its own environment to the agent's shell whatever
|
|
603
582
|
* its policy says, so no secret rides it: each tool's values sit in the
|
|
604
583
|
* run's private 0600 file and reach only that tool's process.
|
|
605
584
|
*/
|
|
606
|
-
export function codexToolArgs(launch, options) {
|
|
607
|
-
|
|
608
|
-
const argv = ["--ignore-user-config", ...carriedCodexSettings(options.includeModel, options.codexHome), "-c", "apps._default.enabled=false", "-c", "features.plugins=false"];
|
|
585
|
+
export function codexToolArgs(launch, options = {}) {
|
|
586
|
+
const argv = [];
|
|
609
587
|
if (launch.tools.length === 0)
|
|
610
588
|
return { argv, env: {}, omitEnv: [], cleanup: () => undefined };
|
|
611
589
|
const dir = mkdtempSync(join(tmpdir(), "so-tools-"));
|
|
@@ -701,9 +679,8 @@ export function prepareRunTools(store, runId, provider, options) {
|
|
|
701
679
|
const launch = limitedTo(toolLaunchFor(store, runId, options.home), options.readOnly, options.withheld);
|
|
702
680
|
const enabledTools = options.readOnly === undefined ? {} : { enabledTools: options.readOnly };
|
|
703
681
|
const prepared = provider === "claude" ? claudeToolArgs(launch)
|
|
704
|
-
: provider === "codex" ? codexToolArgs(launch,
|
|
705
|
-
:
|
|
706
|
-
: { argv: [...GEMINI_NO_TOOLS_ARGV], env: {}, omitEnv: [], cleanup: () => undefined };
|
|
682
|
+
: provider === "codex" || provider === "openrouter" ? codexToolArgs(launch, enabledTools)
|
|
683
|
+
: { argv: [...GEMINI_NO_TOOLS_ARGV], env: {}, omitEnv: [], cleanup: () => undefined };
|
|
707
684
|
const skipped = provider === "gemini"
|
|
708
685
|
? [...launch.skipped, ...launch.tools.map(one => ({ name: one.spec.name, reason: "Gemini builds can't use project tools yet" }))]
|
|
709
686
|
: launch.skipped;
|
|
@@ -725,13 +702,12 @@ function limitedTo(launch, readOnly, withheld = {}) {
|
|
|
725
702
|
};
|
|
726
703
|
}
|
|
727
704
|
/** When a launch's tools cannot be prepared, it still gets none of anything else's: the same isolation with an empty list. */
|
|
728
|
-
export function noToolsArgs(provider
|
|
729
|
-
const none = { tools: [], skipped: [] };
|
|
705
|
+
export function noToolsArgs(provider) {
|
|
730
706
|
if (provider === "claude")
|
|
731
|
-
return { argv: ["--
|
|
707
|
+
return { argv: ["--mcp-config", '{"mcpServers":{}}'], env: {}, omitEnv: [], cleanup: () => undefined };
|
|
732
708
|
if (provider === "gemini")
|
|
733
709
|
return { argv: [...GEMINI_NO_TOOLS_ARGV], env: {}, omitEnv: [], cleanup: () => undefined };
|
|
734
|
-
return codexToolArgs(
|
|
710
|
+
return codexToolArgs({ tools: [], skipped: [] });
|
|
735
711
|
}
|
|
736
712
|
/** Add one tool (and, for an import, the values it brings) to a project's builds. */
|
|
737
713
|
export function addToolTo(store, repo, input, source, by, now, options = {}) {
|
package/dist/provider.d.ts
CHANGED
|
@@ -64,6 +64,26 @@ export type Invocation = {
|
|
|
64
64
|
* Codex renders it as its own sandbox profile; Claude as deny rules for
|
|
65
65
|
* its file tools (its shell is fenced by the macOS sandbox around it). */
|
|
66
66
|
fence?: readonly string[];
|
|
67
|
+
/** More paths Claude's own file tools refuse, beside the fence: home
|
|
68
|
+
* configuration its CLI still reads itself (so the sandbox around the
|
|
69
|
+
* whole process can't deny them). */
|
|
70
|
+
fileToolFence?: readonly string[];
|
|
71
|
+
/** The task's own folder (its worktree). A Claude build loads no settings
|
|
72
|
+
* files, so this is where the repository's CLAUDE.md is read from. */
|
|
73
|
+
workspace?: string;
|
|
74
|
+
/** A running build's steering folder (steer-mailbox.ts): the absolute path
|
|
75
|
+
* and the worktree-relative name its brief uses. Claude renders a
|
|
76
|
+
* PostToolUse hook that says when a note is waiting; every other provider
|
|
77
|
+
* relies on the brief's own rule to look. */
|
|
78
|
+
steerFolder?: {
|
|
79
|
+
dir: string;
|
|
80
|
+
name: string;
|
|
81
|
+
};
|
|
82
|
+
/** Live input (live-input.ts): the brief arrives as the first stdin message
|
|
83
|
+
* and stdin stays open, so the argv carries `--input-format stream-json`
|
|
84
|
+
* and never the brief (Claude drops a `-p` prompt once stdin carries
|
|
85
|
+
* input). Set by the gateway only, for a transport that streams input. */
|
|
86
|
+
streamInput?: boolean;
|
|
67
87
|
/** Claude's structured-output floor for a non-review phase: the scout's
|
|
68
88
|
* report rides the terminal result event instead of a file plan mode
|
|
69
89
|
* would refuse to write. Ignored by providers without `--json-schema`. */
|
|
@@ -107,6 +127,9 @@ export type ParsedEnvelope = {
|
|
|
107
127
|
tokensIn: number | null;
|
|
108
128
|
tokensOut: number | null;
|
|
109
129
|
costUsd: number | null;
|
|
130
|
+
/** The run spent but its cost could not be read (Claude's main result was too large to keep): the gateway charges
|
|
131
|
+
* the run's dollar cap, or records it unmeasured when none applied. Absent = the cost is what costUsd says. */
|
|
132
|
+
costUnknown?: boolean;
|
|
110
133
|
usageRaw: string | null;
|
|
111
134
|
/**
|
|
112
135
|
* Whether the provider was seen to initialize (codex: thread.started;
|
|
@@ -144,6 +167,9 @@ export type ParsedEnvelope = {
|
|
|
144
167
|
type Adapter = {
|
|
145
168
|
binary: string;
|
|
146
169
|
argv(invocation: Invocation): string[];
|
|
170
|
+
/** Environment the launch needs beside the argv (Claude: read the
|
|
171
|
+
* workspace's CLAUDE.md through `--add-dir`). */
|
|
172
|
+
env?(invocation: Invocation): Record<string, string>;
|
|
147
173
|
/** Prompt input for transports that use stdin instead of a bounded OS argument. */
|
|
148
174
|
stdin?(invocation: Invocation): string | undefined;
|
|
149
175
|
parse(stdout: string): ParsedEnvelope;
|
|
@@ -159,8 +185,47 @@ type Adapter = {
|
|
|
159
185
|
clampTimeout(phase: Phase, requestedMs: number): number;
|
|
160
186
|
};
|
|
161
187
|
export declare const OPENROUTER_ENV_KEY = "OPENROUTER_API_KEY";
|
|
188
|
+
/**
|
|
189
|
+
* A build's own setup (plan, build and repair; never a review, which keeps
|
|
190
|
+
* the isolation above). No settings file loads — not the user's, the
|
|
191
|
+
* repository's or the local one — so none of their hooks, plugins or
|
|
192
|
+
* permissions reach the run; Toolroll's one `--settings` (the fence and the
|
|
193
|
+
* steering hook) is all there is. MCP servers come only from `--mcp-config`:
|
|
194
|
+
* the project's named tools, or an explicit empty list. The repository's
|
|
195
|
+
* CLAUDE.md still loads, through `--add-dir` on the workspace. Sign-in is
|
|
196
|
+
* the CLI's own (keychain or ~/.claude), which no setting source governs.
|
|
197
|
+
* `-p` never shows the workspace trust prompt (Claude Code 2.1.290), so a
|
|
198
|
+
* fresh worktree can't wedge a headless run. Checked against 2.1.290 with
|
|
199
|
+
* planted user, project and local hooks and MCP servers: none loaded.
|
|
200
|
+
*/
|
|
201
|
+
export declare const CLAUDE_BUILD_ISOLATION_ARGV: readonly string[];
|
|
162
202
|
/** Claude's own tools that only make sense with a person to wake them. */
|
|
163
203
|
export declare const HEADLESS_DISALLOWED_TOOLS: readonly string[];
|
|
204
|
+
/**
|
|
205
|
+
* Whether a failed launch was an installed CLI too old for a build's own
|
|
206
|
+
* setup: it names one of those flags as unknown. Then nothing ran, and the
|
|
207
|
+
* run says so plainly instead of reading as a failed sign-in or bad work.
|
|
208
|
+
*/
|
|
209
|
+
export declare function isolationUnsupported(provider: ProviderId, stderr: string): string | null;
|
|
210
|
+
/**
|
|
211
|
+
* Codex's own fail-closed equivalent of `CLAUDE_REVIEW_ISOLATION_ARGV`:
|
|
212
|
+
* read-only sandbox rather than the ordinary `workspace-write` (a reviewer
|
|
213
|
+
* dispatched to codex was, until this, handed a write-capable sandbox —
|
|
214
|
+
* the same class of leak the claude side had), approvals that REFUSE
|
|
215
|
+
* rather than wait on a prompt a headless turn can never answer, and the
|
|
216
|
+
* user's own `~/.codex/config.toml` — which can name arbitrary MCP
|
|
217
|
+
* servers, exactly the surface `--strict-mcp-config` closes for claude —
|
|
218
|
+
* ignored outright.
|
|
219
|
+
*/
|
|
220
|
+
/**
|
|
221
|
+
* A Codex build's own setup, the equivalent of Claude's: the operator's
|
|
222
|
+
* config.toml (its MCP servers, model choices, profiles) and execpolicy
|
|
223
|
+
* rules ignored, ChatGPT apps and plugins off (plugins carry their own MCP
|
|
224
|
+
* servers), and hooks off. Sign-in still comes from CODEX_HOME, which
|
|
225
|
+
* `--ignore-user-config` leaves alone. The project's named tools follow as
|
|
226
|
+
* `-c mcp_servers.*` (project-tools.ts).
|
|
227
|
+
*/
|
|
228
|
+
export declare const CODEX_BUILD_ISOLATION_ARGV: readonly string[];
|
|
164
229
|
/**
|
|
165
230
|
* The diagnostic discipline (Phase 3 B6/C6): the harness's own words are
|
|
166
231
|
* untrusted bytes headed for refusal screens and notifications — controls
|
|
@@ -180,11 +245,10 @@ export declare const ALL_CREDENTIAL_ENV: readonly string[];
|
|
|
180
245
|
export declare function adapterFor(provider: ProviderId): Adapter;
|
|
181
246
|
/**
|
|
182
247
|
* The provider audit: what each harness supports, what we actually pass,
|
|
183
|
-
* and what user-global configuration
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
* instead of hope. `enforced` is a literal false until that day.
|
|
248
|
+
* and what user-global configuration still reaches an unattended run.
|
|
249
|
+
* None of this changes an invocation — it states facts an operator reads
|
|
250
|
+
* on `providers`. Claude and Codex builds now run in their own setup
|
|
251
|
+
* (`enforced`); Gemini's flag is still only reported.
|
|
188
252
|
*/
|
|
189
253
|
export type ProviderAudit = {
|
|
190
254
|
/** How output reaches us — and therefore which signals can exist at all. */
|
|
@@ -209,13 +273,14 @@ export type ProviderAudit = {
|
|
|
209
273
|
*/
|
|
210
274
|
terminalContract: "required" | "none";
|
|
211
275
|
isolation: {
|
|
212
|
-
/** The harness's hermetic flag, if it has one.
|
|
276
|
+
/** The harness's hermetic flag, if it has one. */
|
|
213
277
|
flag: string | null;
|
|
214
278
|
/** Whether that flag preserves resume. false = documented conflict. */
|
|
215
279
|
resumeSafe: boolean | null;
|
|
216
|
-
|
|
280
|
+
/** Whether every build, fresh or resumed, passes it. */
|
|
281
|
+
enforced: boolean;
|
|
217
282
|
};
|
|
218
|
-
/**
|
|
283
|
+
/** What still reaches a build: its own sources and the user surfaces left. */
|
|
219
284
|
configSurface: readonly string[];
|
|
220
285
|
};
|
|
221
286
|
/** Read-only facts about a provider — safe anywhere, spawns nothing. */
|
package/dist/provider.js
CHANGED
|
@@ -19,8 +19,9 @@
|
|
|
19
19
|
* shell the model itself launches (Codex provider review, Q5).
|
|
20
20
|
*/
|
|
21
21
|
import { claudeFenceSettings, codexFenceArgv } from "./agent-fence.js";
|
|
22
|
+
import { claudeSteerHooks } from "./steer-mailbox.js";
|
|
22
23
|
import {} from "./exec.js";
|
|
23
|
-
import { runStreamJsonl, runClaudeStreamJsonl, runGeminiStreamJsonl } from "./exec.js";
|
|
24
|
+
import { runStreamJsonl, runClaudeStreamJsonl, runGeminiStreamJsonl, CLAUDE_COST_UNKNOWN } from "./exec.js";
|
|
24
25
|
import { scanForSecrets } from "./evidence.js";
|
|
25
26
|
import { FINDINGS_MODEL_SCHEMA } from "./contracts/review-findings.js";
|
|
26
27
|
import { REVIEW_OUTPUT_LIMITS } from "./structured-output.js";
|
|
@@ -166,11 +167,26 @@ const CLAUDE_REVIEW_JSON_SCHEMA = {
|
|
|
166
167
|
required: ["version"],
|
|
167
168
|
additionalProperties: false,
|
|
168
169
|
};
|
|
170
|
+
/**
|
|
171
|
+
* A build's own setup (plan, build and repair; never a review, which keeps
|
|
172
|
+
* the isolation above). No settings file loads — not the user's, the
|
|
173
|
+
* repository's or the local one — so none of their hooks, plugins or
|
|
174
|
+
* permissions reach the run; Toolroll's one `--settings` (the fence and the
|
|
175
|
+
* steering hook) is all there is. MCP servers come only from `--mcp-config`:
|
|
176
|
+
* the project's named tools, or an explicit empty list. The repository's
|
|
177
|
+
* CLAUDE.md still loads, through `--add-dir` on the workspace. Sign-in is
|
|
178
|
+
* the CLI's own (keychain or ~/.claude), which no setting source governs.
|
|
179
|
+
* `-p` never shows the workspace trust prompt (Claude Code 2.1.290), so a
|
|
180
|
+
* fresh worktree can't wedge a headless run. Checked against 2.1.290 with
|
|
181
|
+
* planted user, project and local hooks and MCP servers: none loaded.
|
|
182
|
+
*/
|
|
183
|
+
export const CLAUDE_BUILD_ISOLATION_ARGV = ["--setting-sources", "", "--strict-mcp-config"];
|
|
184
|
+
const EMPTY_MCP_CONFIG = '{"mcpServers":{}}';
|
|
169
185
|
/** Claude's own tools that only make sense with a person to wake them. */
|
|
170
186
|
export const HEADLESS_DISALLOWED_TOOLS = ["ScheduleWakeup", "CronCreate", "Monitor"];
|
|
171
187
|
const claudeArgv = (invocation) => [
|
|
172
188
|
"-p",
|
|
173
|
-
invocation.brief,
|
|
189
|
+
...(invocation.streamInput === true ? ["--input-format", "stream-json"] : [invocation.brief]),
|
|
174
190
|
...(invocation.resumeSession === null ? [] : ["--resume", invocation.resumeSession]),
|
|
175
191
|
// stream-json (arc 1): the terminal result event IS the old buffered
|
|
176
192
|
// envelope, now arriving as one line of many — the streaming transport
|
|
@@ -196,12 +212,47 @@ const claudeArgv = (invocation) => [
|
|
|
196
212
|
// turn that a -p process never gets. Denied at the harness too.
|
|
197
213
|
"--disallowedTools", HEADLESS_DISALLOWED_TOOLS.join(","),
|
|
198
214
|
...(invocation.allowedTools === undefined || invocation.allowedTools.length === 0 ? [] : ["--allowedTools", invocation.allowedTools.join(",")]),
|
|
215
|
+
...CLAUDE_BUILD_ISOLATION_ARGV,
|
|
216
|
+
...(invocation.workspace === undefined ? [] : ["--add-dir", invocation.workspace]),
|
|
199
217
|
...(invocation.toolArgv ?? []),
|
|
200
218
|
...(invocation.extraMcpServers === undefined ? [] : ["--mcp-config", JSON.stringify({ mcpServers: invocation.extraMcpServers })]),
|
|
201
|
-
|
|
219
|
+
// Strict with nothing named still names its empty list.
|
|
220
|
+
...((invocation.toolArgv ?? []).includes("--mcp-config") || invocation.extraMcpServers !== undefined ? [] : ["--mcp-config", EMPTY_MCP_CONFIG]),
|
|
221
|
+
...claudeSettingsArgv(invocation),
|
|
202
222
|
...(invocation.jsonSchema === undefined ? [] : ["--json-schema", JSON.stringify(invocation.jsonSchema)])
|
|
203
223
|
]),
|
|
204
224
|
];
|
|
225
|
+
/** The build's one --settings: no personal memory, plus the fence and steering doorbell. */
|
|
226
|
+
function claudeSettingsArgv(invocation) {
|
|
227
|
+
const denied = [...(invocation.fence ?? []), ...(invocation.fileToolFence ?? [])];
|
|
228
|
+
return ["--settings", JSON.stringify({
|
|
229
|
+
autoMemoryEnabled: false,
|
|
230
|
+
...(denied.length > 0 ? JSON.parse(claudeFenceSettings(denied)) : {}),
|
|
231
|
+
...(invocation.steerFolder === undefined ? {} : { hooks: claudeSteerHooks(invocation.steerFolder.dir, invocation.steerFolder.name) }),
|
|
232
|
+
})];
|
|
233
|
+
}
|
|
234
|
+
/** With no settings sources, only `--add-dir` folders' CLAUDE.md load, and only with this set. */
|
|
235
|
+
const claudeEnv = (invocation) => invocation.phase === "review" || invocation.workspace === undefined ? {} : { CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD: "1" };
|
|
236
|
+
/** The flags a build's own setup depends on, by provider. */
|
|
237
|
+
const ISOLATION_FLAGS = {
|
|
238
|
+
claude: ["--setting-sources", "--strict-mcp-config", "--settings", "--add-dir", "--mcp-config"],
|
|
239
|
+
codex: ["--ignore-user-config", "--ignore-rules"],
|
|
240
|
+
openrouter: ["--ignore-user-config", "--ignore-rules"],
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Whether a failed launch was an installed CLI too old for a build's own
|
|
244
|
+
* setup: it names one of those flags as unknown. Then nothing ran, and the
|
|
245
|
+
* run says so plainly instead of reading as a failed sign-in or bad work.
|
|
246
|
+
*/
|
|
247
|
+
export function isolationUnsupported(provider, stderr) {
|
|
248
|
+
const flags = ISOLATION_FLAGS[provider] ?? [];
|
|
249
|
+
const said = stderr.slice(0, 8192);
|
|
250
|
+
const flag = flags.find(one => new RegExp(`(unknown|unexpected|unrecognized) (option|argument)[^\\n]{0,8}${one.replace(/-/g, "\\-")}\\b`, "i").test(said));
|
|
251
|
+
if (flag === undefined)
|
|
252
|
+
return null;
|
|
253
|
+
const name = provider === "claude" ? "Claude Code" : "Codex";
|
|
254
|
+
return `The installed ${name} doesn't support ${flag}, which builds need to run without your own hooks, plugins and MCP servers. Update ${name} and try again.`;
|
|
255
|
+
}
|
|
205
256
|
/**
|
|
206
257
|
* The turn's own words, preferring the schema-validated structured field
|
|
207
258
|
* (present only under `--json-schema`) over the plain result string — the
|
|
@@ -256,6 +307,7 @@ function claudeEnvelopeOf(result, sessionFromInit, initObserved, initSessionConf
|
|
|
256
307
|
tokensIn: typeof input === "number" && input >= 0 ? input : null,
|
|
257
308
|
tokensOut: typeof output === "number" && output >= 0 ? output : null,
|
|
258
309
|
costUsd: typeof cost === "number" && cost >= 0 ? cost : null,
|
|
310
|
+
...(result !== null && result[CLAUDE_COST_UNKNOWN] === true ? { costUnknown: true } : {}),
|
|
259
311
|
usageRaw: result?.usage === undefined ? null : JSON.stringify(result.usage).slice(0, USAGE_JSON_CAP),
|
|
260
312
|
initObserved,
|
|
261
313
|
// Structural, never prose (finding 15): consumed means the primary
|
|
@@ -365,6 +417,21 @@ const codexSandboxArgv = (mode, resuming) => resuming ? ["-c", `sandbox_mode="${
|
|
|
365
417
|
* servers, exactly the surface `--strict-mcp-config` closes for claude —
|
|
366
418
|
* ignored outright.
|
|
367
419
|
*/
|
|
420
|
+
/**
|
|
421
|
+
* A Codex build's own setup, the equivalent of Claude's: the operator's
|
|
422
|
+
* config.toml (its MCP servers, model choices, profiles) and execpolicy
|
|
423
|
+
* rules ignored, ChatGPT apps and plugins off (plugins carry their own MCP
|
|
424
|
+
* servers), and hooks off. Sign-in still comes from CODEX_HOME, which
|
|
425
|
+
* `--ignore-user-config` leaves alone. The project's named tools follow as
|
|
426
|
+
* `-c mcp_servers.*` (project-tools.ts).
|
|
427
|
+
*/
|
|
428
|
+
export const CODEX_BUILD_ISOLATION_ARGV = [
|
|
429
|
+
"--ignore-user-config",
|
|
430
|
+
"--ignore-rules",
|
|
431
|
+
"-c", "apps._default.enabled=false",
|
|
432
|
+
"-c", "features.plugins=false",
|
|
433
|
+
"-c", "features.hooks=false",
|
|
434
|
+
];
|
|
368
435
|
const CODEX_REVIEW_ISOLATION_ARGV = (resuming) => [
|
|
369
436
|
"--ignore-user-config",
|
|
370
437
|
...codexSandboxArgv("read-only", resuming),
|
|
@@ -395,6 +462,7 @@ const codexArgv = (extra) => (invocation) => {
|
|
|
395
462
|
...(invocation.resumeSession === null ? [] : ["resume", invocation.resumeSession]),
|
|
396
463
|
"--json",
|
|
397
464
|
"--skip-git-repo-check",
|
|
465
|
+
...(invocation.phase === "review" ? [] : CODEX_BUILD_ISOLATION_ARGV),
|
|
398
466
|
...(invocation.phase === "review"
|
|
399
467
|
? CODEX_REVIEW_ISOLATION_ARGV(resuming)
|
|
400
468
|
// The agent fence: Codex's own sandbox with Toolroll's secrets
|
|
@@ -714,6 +782,7 @@ const ADAPTERS = {
|
|
|
714
782
|
claude: {
|
|
715
783
|
binary: "claude",
|
|
716
784
|
argv: claudeArgv,
|
|
785
|
+
env: claudeEnv,
|
|
717
786
|
parse: claudeParse,
|
|
718
787
|
defaultRunner: runClaudeStreamJsonl,
|
|
719
788
|
extraOmitEnv: foreignCredentialEnv("claude"),
|
|
@@ -777,13 +846,14 @@ const AUDITS = {
|
|
|
777
846
|
initSignal: "system-init",
|
|
778
847
|
sessionIdentity: "announced",
|
|
779
848
|
terminalContract: "none",
|
|
780
|
-
//
|
|
781
|
-
//
|
|
782
|
-
|
|
783
|
-
isolation: { flag: "--restricted", resumeSafe: true, enforced: false },
|
|
849
|
+
// Builds: no settings sources and strict MCP, fresh and resumed alike
|
|
850
|
+
// (CLAUDE_BUILD_ISOLATION_ARGV). Reviews keep --restricted --safe-mode.
|
|
851
|
+
isolation: { flag: CLAUDE_BUILD_ISOLATION_ARGV.filter(one => one !== "").join(" "), resumeSafe: true, enforced: true },
|
|
784
852
|
configSurface: [
|
|
785
|
-
"
|
|
786
|
-
"
|
|
853
|
+
"Toolroll's own --settings (the fence and steering hook)",
|
|
854
|
+
"the project's named tools (Settings → Tools)",
|
|
855
|
+
"repository CLAUDE.md",
|
|
856
|
+
"organisation-managed settings",
|
|
787
857
|
],
|
|
788
858
|
},
|
|
789
859
|
codex: {
|
|
@@ -792,10 +862,11 @@ const AUDITS = {
|
|
|
792
862
|
initSignal: "thread.started",
|
|
793
863
|
sessionIdentity: "announced",
|
|
794
864
|
terminalContract: "none",
|
|
795
|
-
//
|
|
796
|
-
//
|
|
797
|
-
|
|
798
|
-
|
|
865
|
+
// Builds ignore config.toml and execpolicy rules, with apps, plugins and
|
|
866
|
+
// hooks off (CODEX_BUILD_ISOLATION_ARGV); sign-in stays in CODEX_HOME.
|
|
867
|
+
// Never --ephemeral: repair resumes sessions.
|
|
868
|
+
isolation: { flag: "--ignore-user-config --ignore-rules", resumeSafe: true, enforced: true },
|
|
869
|
+
configSurface: ["the project's named tools (Settings → Tools)", "~/.codex/AGENTS.md and repository AGENTS.md", "sign-in from CODEX_HOME"],
|
|
799
870
|
},
|
|
800
871
|
openrouter: {
|
|
801
872
|
transport: "streaming-jsonl",
|
|
@@ -803,10 +874,9 @@ const AUDITS = {
|
|
|
803
874
|
initSignal: "thread.started",
|
|
804
875
|
sessionIdentity: "announced",
|
|
805
876
|
terminalContract: "none",
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
configSurface: ["~/.codex/config.toml (model_provider pinned per invocation)", "repository AGENTS.md"],
|
|
877
|
+
// Codex's build setup, with the model provider pinned by constant -c overrides.
|
|
878
|
+
isolation: { flag: "--ignore-user-config --ignore-rules", resumeSafe: true, enforced: true },
|
|
879
|
+
configSurface: ["the project's named tools (Settings → Tools)", "~/.codex/AGENTS.md and repository AGENTS.md", `${OPENROUTER_ENV_KEY}`],
|
|
810
880
|
},
|
|
811
881
|
gemini: {
|
|
812
882
|
transport: "streaming-jsonl",
|
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
import type { Store } from "./store.js";
|
|
7
7
|
/** The ledger action a person's (or lead's) completion of a result writes. */
|
|
8
8
|
export declare const COMPLETION_ACTION = "assignment handoff checked";
|
|
9
|
+
/** The actor of an automatic completion: the result's work already landed (landed-cleanup.ts). Its ledger detail is
|
|
10
|
+
* the plain-English reason ("its pull request merged"), the one place every surface reads it from. */
|
|
11
|
+
export declare const LANDED_ACTOR = "system:landed";
|
|
12
|
+
/** Who may complete a result, as a completion's ledger actor: a person, a lead, or Toolroll when the work landed. */
|
|
13
|
+
export declare const COMPLETER_ACTOR: RegExp;
|
|
14
|
+
/** Why Toolroll marked a result complete, from its completion's actor and detail; null for a person's completion. */
|
|
15
|
+
export declare function landedReasonOf(actor: string | null | undefined, detail: string | null | undefined): string | null;
|
|
9
16
|
/** Whether a task's family is marked complete. Completion is recorded on the
|
|
10
17
|
* family's root against its current version's latest result, so the root, a
|
|
11
18
|
* superseded revision and the current version all read Complete together. */
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { readVerifiedArtifact } from "./evidence.js";
|
|
2
2
|
/** The ledger action a person's (or lead's) completion of a result writes. */
|
|
3
3
|
export const COMPLETION_ACTION = "assignment handoff checked";
|
|
4
|
+
/** The actor of an automatic completion: the result's work already landed (landed-cleanup.ts). Its ledger detail is
|
|
5
|
+
* the plain-English reason ("its pull request merged"), the one place every surface reads it from. */
|
|
6
|
+
export const LANDED_ACTOR = "system:landed";
|
|
7
|
+
/** Who may complete a result, as a completion's ledger actor: a person, a lead, or Toolroll when the work landed. */
|
|
8
|
+
export const COMPLETER_ACTOR = /^(?:(?:operator|coordinator|lead):.+|system:landed)$/;
|
|
9
|
+
/** Why Toolroll marked a result complete, from its completion's actor and detail; null for a person's completion. */
|
|
10
|
+
export function landedReasonOf(actor, detail) {
|
|
11
|
+
return actor === LANDED_ACTOR ? detail?.trim() || "its work is already in main" : null;
|
|
12
|
+
}
|
|
4
13
|
/** Whether a task's family is marked complete. Completion is recorded on the
|
|
5
14
|
* family's root against its current version's latest result, so the root, a
|
|
6
15
|
* superseded revision and the current version all read Complete together. */
|
package/dist/runner.d.ts
CHANGED
|
@@ -121,8 +121,10 @@ export declare function removeRunnerRepoAuthed(store: Store, args: {
|
|
|
121
121
|
export declare const RUNNER_NAME_MAX = 60;
|
|
122
122
|
/** The most tasks one worker may be set to run at once (the console's register form says the same). */
|
|
123
123
|
export declare const RUNNER_CAPACITY_MAX = 64;
|
|
124
|
-
/** A capacity as typed
|
|
125
|
-
export declare function parseRunnerCapacity(value: string
|
|
124
|
+
/** A capacity as typed; changing an existing worker may use zero to drain it. */
|
|
125
|
+
export declare function parseRunnerCapacity(value: string, { allowZero }?: {
|
|
126
|
+
allowZero?: boolean | undefined;
|
|
127
|
+
}): number | null;
|
|
126
128
|
export declare function validRunnerName(name: string): boolean;
|
|
127
129
|
/** A generated base name: lowercase [a-z0-9-], collapsed, trimmed. The
|
|
128
130
|
* suffix budget (finding 24) is the CALLER's arithmetic — truncate the
|
package/dist/runner.js
CHANGED
|
@@ -165,10 +165,10 @@ export function removeRunnerRepoAuthed(store, args, now) {
|
|
|
165
165
|
export const RUNNER_NAME_MAX = 60;
|
|
166
166
|
/** The most tasks one worker may be set to run at once (the console's register form says the same). */
|
|
167
167
|
export const RUNNER_CAPACITY_MAX = 64;
|
|
168
|
-
/** A capacity as typed
|
|
169
|
-
export function parseRunnerCapacity(value) {
|
|
168
|
+
/** A capacity as typed; changing an existing worker may use zero to drain it. */
|
|
169
|
+
export function parseRunnerCapacity(value, { allowZero = false } = {}) {
|
|
170
170
|
const capacity = Number(value.trim());
|
|
171
|
-
return value.trim() !== "" && Number.isInteger(capacity) && capacity >= 1 && capacity <= RUNNER_CAPACITY_MAX ? capacity : null;
|
|
171
|
+
return value.trim() !== "" && Number.isInteger(capacity) && capacity >= (allowZero ? 0 : 1) && capacity <= RUNNER_CAPACITY_MAX ? capacity : null;
|
|
172
172
|
}
|
|
173
173
|
export function validRunnerName(name) {
|
|
174
174
|
if (name === "" || name.length > RUNNER_NAME_MAX)
|
package/dist/serve.js
CHANGED
|
@@ -7,14 +7,15 @@ import { CodingWorkspace } from './coding-workspace.js';
|
|
|
7
7
|
import { CONSOLE_FORMS, readForm } from "./contracts/console-api.js";
|
|
8
8
|
import { createFlowRooms, flowFingerprint } from "./flow-live.js";
|
|
9
9
|
import {} from "./google-mail.js";
|
|
10
|
-
import { configureLeadFollow, runLeadFollowPass } from './lead-follow.js';
|
|
10
|
+
import { configureLeadFollow, recordLeadFollowFailure, runLeadFollowPass } from './lead-follow.js';
|
|
11
11
|
import { leadNameOf } from "./lead-identity.js";
|
|
12
12
|
import { createLiveBus, followWorkspace } from "./live-bus.js";
|
|
13
13
|
import { startMaintenance } from './maintenance.js';
|
|
14
14
|
import { connectionsOf, localConnectOf, oneClickOf } from "./mcp-connect.js";
|
|
15
15
|
import { MOBILE_VIEWPORT_SCRIPT } from "./mobile-viewport.js";
|
|
16
16
|
import { projectAuthority } from "./project-access.js";
|
|
17
|
-
import { discoverTools, projectToolsOf, secretsSetFor, TOOL_CATALOG } from "./project-tools.js";
|
|
17
|
+
import { discoverTools, projectToolsOf, secretsSetFor, TOOL_CATALOG, toolStanding } from "./project-tools.js";
|
|
18
|
+
import { toolRefusal } from "./policy.js";
|
|
18
19
|
import { isEdgeAddress, refuseEdge } from "./server/edge-refusal.js";
|
|
19
20
|
import { announcePasswordBearer, isPasswordBearer, PASSWORD_BEARER_REFUSED, passwordBearerAccepted } from "./password-bearer.js";
|
|
20
21
|
import { createSharedGuards } from "./server/guards.js";
|
|
@@ -390,7 +391,24 @@ export function createDecisionServer(options) {
|
|
|
390
391
|
repo, name: projectName(repo), holdings: projectHoldings(store, repo), running: projectRunning(store, repo, clock()),
|
|
391
392
|
canDelete: who.via === "cookie" && store.isInstanceOperator(who.name),
|
|
392
393
|
builds: projectBuildsOf(repo, who),
|
|
394
|
+
access: projectAccessOf(repo),
|
|
393
395
|
});
|
|
396
|
+
/** Settings → Project → Build access: the project's named tools and whether each loads for its builds. */
|
|
397
|
+
const projectAccessOf = (repo) => {
|
|
398
|
+
try {
|
|
399
|
+
const policy = store.orgPolicy();
|
|
400
|
+
return { tools: projectToolsOf(store, repo).map(tool => {
|
|
401
|
+
const refused = toolRefusal(policy, tool.name);
|
|
402
|
+
const set = secretsSetFor(repo, tool.spec, toolHome);
|
|
403
|
+
// As a launch decides (toolLaunchFor): a tool the organisation doesn't allow, or missing a secret, is left out.
|
|
404
|
+
return refused !== null ? { name: tool.name, loads: false, words: refused }
|
|
405
|
+
: { name: tool.name, loads: tool.spec.secrets.every(one => one.optional || set.includes(one.name)), words: toolStanding(tool, set).words };
|
|
406
|
+
}) };
|
|
407
|
+
}
|
|
408
|
+
catch {
|
|
409
|
+
return { problem: "Couldn't read this project's tools right now." };
|
|
410
|
+
}
|
|
411
|
+
};
|
|
394
412
|
/** Settings → Project → Builds at once: the saved number, its workers' capacity, and what builds now. */
|
|
395
413
|
const projectBuildsOf = (repo, who) => {
|
|
396
414
|
const capacities = store.listRunners().filter(one => one.retiredAt === null && one.repos.includes(repo)).map(one => one.capacity);
|
|
@@ -1570,7 +1588,8 @@ export function createDecisionServer(options) {
|
|
|
1570
1588
|
run: () => runLeadFollowPass({ store, repos: managedRepos, evidenceRoot, clock,
|
|
1571
1589
|
provider: () => { const enabled = chatEnablement(); return enabled.ok ? { config: enabled.config, key: enabled.key } : null; },
|
|
1572
1590
|
fetcher: chatFetcher, ...(options.subscriptionChatRunner ? { subscriptionRunner: options.subscriptionChatRunner } : {}) }),
|
|
1573
|
-
|
|
1591
|
+
// Pending delivery is durable and is read again on the next pass; a failure is said where follow status is read.
|
|
1592
|
+
onError: error => recordLeadFollowFailure(store, error, clock()),
|
|
1574
1593
|
});
|
|
1575
1594
|
});
|
|
1576
1595
|
// Keep native process custody until shutdown has verified agent/tool exit.
|
package/dist/server/chrome.d.ts
CHANGED
|
@@ -42,9 +42,12 @@ export declare const DEMO_BANNER_SHORT = "Demo: sample projects. Nothing calls a
|
|
|
42
42
|
* inset into it. Semantic names only; every surface (server pages, the
|
|
43
43
|
* React workspace, the pre-script fallback) reads these.
|
|
44
44
|
*
|
|
45
|
-
* The
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
* The palette above, in light, dark and pinned themes, is Signal's generated
|
|
46
|
+
* tokens.css (src/design, built by youi from the design-language repo); the
|
|
47
|
+
* console's own rules follow in console.css, shipped beside this module.
|
|
48
|
+
* Their bytes are the served sheet's (WORKSPACE_STYLE hashes them). The React
|
|
49
|
+
* workspace's stylesheet loads after this one and never redefines a token, so
|
|
50
|
+
* a person's accent (accentHead) still wins over the generated default. */
|
|
48
51
|
export declare const STYLE: string;
|
|
49
52
|
/** Appearance: a three-way segmented switch, one tap per choice. */
|
|
50
53
|
export declare const THEME_CONTROLS_CSS = ".task-repo select{width:100%;min-height:2.75rem;font-size:1rem}.task-repo-add{margin:.35rem .1rem .5rem}.task-repo-add a{display:inline-flex;align-items:center;min-height:2.25rem}details.result-request-open.result-request-form>summary{border:0;background:transparent;padding:.5rem 0;min-height:2.75rem;font-weight:600;display:list-item;list-style:revert}details.result-request-open.result-request-form>summary::-webkit-details-marker{display:revert}form.js-autosave button[type=submit]{display:none}.provider-row{border-bottom:1px solid var(--so-line);padding:.35rem 0}.provider-row:first-of-type{border-top:1px solid var(--so-line)}.provider-head{display:flex;align-items:center;gap:.75rem;margin:.4rem 0 0}.provider-status{display:inline-flex;align-items:center;gap:.4rem;color:var(--so-muted);font-size:.875rem}.provider-status i{width:.5rem;height:.5rem;border-radius:50%;background:var(--so-muted)}.provider-status--ok i{background:var(--so-success)}.provider-status--warn i{background:var(--so-attention)}.provider-status--off i{background:transparent;border:1.5px solid var(--so-muted)}details.provider-manage>summary{cursor:pointer;color:var(--so-accent-text);font-size:.875rem;min-height:2.5rem;display:list-item;padding-block:.5rem}.card.props .row{display:grid;gap:.1rem;margin:0 0 .75rem}.card.props .row>.meta{display:block;font-size:.75rem}.card.props .row>.meta::first-letter{text-transform:uppercase}.card.props .row>.mono{font-family:var(--font-sans);font-size:.875rem}.card.props .row>.mono .seal{font-family:var(--font-mono);font-size:.8125rem}details.evidence-files{margin:1rem 0}details.evidence-files>summary{cursor:pointer;min-height:2.75rem;display:list-item;padding-block:.7rem;font-weight:600}details.evidence-files ul{list-style:none;margin:0;padding:0}details.evidence-files li{display:flex;justify-content:space-between;gap:1rem;padding:.5rem 0;border-bottom:1px solid var(--so-line)}.result-action .result-feedback-link{display:inline-flex;align-items:center;min-height:2.5rem;padding:.5rem 1rem;border:1px solid var(--so-input-line);border-radius:.5rem;background:var(--so-paper);color:var(--so-ink);font-weight:600;text-decoration:none}@media(hover:hover) and (pointer:fine){.result-action .result-feedback-link:hover{background:var(--so-raised)}}.so-sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.verdict{margin:.5rem 0 .75rem}.verdict-chips{display:flex;flex-wrap:wrap;gap:.4rem;list-style:none;padding:0;margin:0}.verdict-chip{display:inline-flex;align-items:center;gap:.3rem;min-height:1.75rem;padding:.2rem .65rem;border-radius:999px;font-size:.8125rem;font-weight:600;background:var(--so-neutral-soft);color:var(--so-neutral-ink)}.verdict-chip svg{width:.9rem;height:.9rem}.verdict-chip--success{background:var(--so-success-soft);color:var(--so-success)}.verdict-chip--danger{background:var(--so-danger-soft);color:var(--so-danger)}.verdict-chip--warning{background:var(--so-warning-soft);color:var(--so-warning)}.verdict-chip--info{background:var(--so-info-soft);color:var(--so-info)}.verdict-by{margin:.4rem 0 0}details.result-request-open{margin:.5rem 0}details.result-request-open>summary{display:inline-flex;align-items:center;min-height:2.5rem;padding:.5rem 1rem;border:1px solid var(--so-input-line);border-radius:.5rem;background:var(--so-paper);color:var(--so-ink);font-weight:600;cursor:pointer;list-style:none}details.result-request-open>summary::-webkit-details-marker{display:none}details.result-request-open[open]>summary{margin-bottom:.75rem}.settings-tiles{display:grid;gap:1.25rem;margin:0 0 2rem}.settings-tiles h2{margin:0 0 .5rem;font-size:.875rem;font-weight:600;color:var(--so-muted)}.settings-tiles section>div{display:grid;grid-template-columns:repeat(auto-fill,minmax(8.5rem,1fr));gap:.5rem}.settings-tiles a>span{display:grid;gap:.1rem;min-width:0}.settings-tiles .provider-status{font-weight:400;font-size:.8125rem}.settings-tiles a{display:flex;align-items:center;gap:.6rem;min-height:3rem;padding:.65rem .8rem;border:1px solid var(--so-line);border-radius:.625rem;background:var(--so-paper);color:var(--so-ink);text-decoration:none;font-weight:550;font-size:.875rem}@media(hover:hover) and (pointer:fine){.settings-tiles a:hover{border-color:var(--so-input-line);background:var(--so-raised)}}.settings-tiles a>svg{width:1.1rem;height:1.1rem;flex-shrink:0;color:var(--so-accent-text)}details.settings-more{margin:.25rem 0 1.25rem}details.settings-more>summary{cursor:pointer;min-height:2.75rem;display:list-item;padding-block:.7rem;font-weight:550}details.settings-more>summary .meta{font-weight:400;margin-left:.35rem}.settings-changed{margin-top:-.25rem}.appearance{margin:0 0 28px}.appearance h2{margin:0 0 10px}.theme-switch{display:inline-flex;flex-wrap:nowrap;max-width:100%;gap:4px;padding:4px;margin:0;border:1px solid var(--so-line);border-radius:10px;background:var(--so-raised)}.theme-switch .theme-choice,.so-native-region .theme-switch .theme-choice{flex:1 1 0;width:auto;white-space:nowrap;min-height:40px;padding:8px 16px;border:0;border-radius:7px;background:transparent;color:var(--so-muted);font:inherit;font-weight:550;box-shadow:none;cursor:pointer}@media(hover:hover) and (pointer:fine){.theme-switch .theme-choice:hover{color:var(--so-ink)}}.theme-switch .theme-choice[aria-pressed=\"true\"]{background:var(--so-paper);color:var(--so-ink);box-shadow:0 1px 2px rgb(0 0 0 / .1)}.appearance .meta{margin:8px 0 0}@media(max-width:600px){.theme-switch .theme-choice{min-height:44px}}.update-notes{white-space:pre-wrap;overflow-wrap:anywhere;font:inherit;max-height:18rem;overflow:auto}";
|
package/dist/server/chrome.js
CHANGED
|
@@ -127,10 +127,14 @@ export const DEMO_BANNER_SHORT = "Demo: sample projects. Nothing calls a model o
|
|
|
127
127
|
* inset into it. Semantic names only; every surface (server pages, the
|
|
128
128
|
* React workspace, the pre-script fallback) reads these.
|
|
129
129
|
*
|
|
130
|
-
* The
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
|
|
130
|
+
* The palette above, in light, dark and pinned themes, is Signal's generated
|
|
131
|
+
* tokens.css (src/design, built by youi from the design-language repo); the
|
|
132
|
+
* console's own rules follow in console.css, shipped beside this module.
|
|
133
|
+
* Their bytes are the served sheet's (WORKSPACE_STYLE hashes them). The React
|
|
134
|
+
* workspace's stylesheet loads after this one and never redefines a token, so
|
|
135
|
+
* a person's accent (accentHead) still wins over the generated default. */
|
|
136
|
+
export const STYLE = readFileSync(join(import.meta.dirname, "..", "design", "tokens.css"), "utf8") +
|
|
137
|
+
readFileSync(join(import.meta.dirname, "console.css"), "utf8");
|
|
134
138
|
/** Appearance: a three-way segmented switch, one tap per choice. */
|
|
135
139
|
export const THEME_CONTROLS_CSS = `.task-repo select{width:100%;min-height:2.75rem;font-size:1rem}.task-repo-add{margin:.35rem .1rem .5rem}.task-repo-add a{display:inline-flex;align-items:center;min-height:2.25rem}details.result-request-open.result-request-form>summary{border:0;background:transparent;padding:.5rem 0;min-height:2.75rem;font-weight:600;display:list-item;list-style:revert}details.result-request-open.result-request-form>summary::-webkit-details-marker{display:revert}form.js-autosave button[type=submit]{display:none}.provider-row{border-bottom:1px solid var(--so-line);padding:.35rem 0}.provider-row:first-of-type{border-top:1px solid var(--so-line)}.provider-head{display:flex;align-items:center;gap:.75rem;margin:.4rem 0 0}.provider-status{display:inline-flex;align-items:center;gap:.4rem;color:var(--so-muted);font-size:.875rem}.provider-status i{width:.5rem;height:.5rem;border-radius:50%;background:var(--so-muted)}.provider-status--ok i{background:var(--so-success)}.provider-status--warn i{background:var(--so-attention)}.provider-status--off i{background:transparent;border:1.5px solid var(--so-muted)}details.provider-manage>summary{cursor:pointer;color:var(--so-accent-text);font-size:.875rem;min-height:2.5rem;display:list-item;padding-block:.5rem}.card.props .row{display:grid;gap:.1rem;margin:0 0 .75rem}.card.props .row>.meta{display:block;font-size:.75rem}.card.props .row>.meta::first-letter{text-transform:uppercase}.card.props .row>.mono{font-family:var(--font-sans);font-size:.875rem}.card.props .row>.mono .seal{font-family:var(--font-mono);font-size:.8125rem}details.evidence-files{margin:1rem 0}details.evidence-files>summary{cursor:pointer;min-height:2.75rem;display:list-item;padding-block:.7rem;font-weight:600}details.evidence-files ul{list-style:none;margin:0;padding:0}details.evidence-files li{display:flex;justify-content:space-between;gap:1rem;padding:.5rem 0;border-bottom:1px solid var(--so-line)}.result-action .result-feedback-link{display:inline-flex;align-items:center;min-height:2.5rem;padding:.5rem 1rem;border:1px solid var(--so-input-line);border-radius:.5rem;background:var(--so-paper);color:var(--so-ink);font-weight:600;text-decoration:none}@media(hover:hover) and (pointer:fine){.result-action .result-feedback-link:hover{background:var(--so-raised)}}.so-sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.verdict{margin:.5rem 0 .75rem}.verdict-chips{display:flex;flex-wrap:wrap;gap:.4rem;list-style:none;padding:0;margin:0}.verdict-chip{display:inline-flex;align-items:center;gap:.3rem;min-height:1.75rem;padding:.2rem .65rem;border-radius:999px;font-size:.8125rem;font-weight:600;background:var(--so-neutral-soft);color:var(--so-neutral-ink)}.verdict-chip svg{width:.9rem;height:.9rem}.verdict-chip--success{background:var(--so-success-soft);color:var(--so-success)}.verdict-chip--danger{background:var(--so-danger-soft);color:var(--so-danger)}.verdict-chip--warning{background:var(--so-warning-soft);color:var(--so-warning)}.verdict-chip--info{background:var(--so-info-soft);color:var(--so-info)}.verdict-by{margin:.4rem 0 0}details.result-request-open{margin:.5rem 0}details.result-request-open>summary{display:inline-flex;align-items:center;min-height:2.5rem;padding:.5rem 1rem;border:1px solid var(--so-input-line);border-radius:.5rem;background:var(--so-paper);color:var(--so-ink);font-weight:600;cursor:pointer;list-style:none}details.result-request-open>summary::-webkit-details-marker{display:none}details.result-request-open[open]>summary{margin-bottom:.75rem}.settings-tiles{display:grid;gap:1.25rem;margin:0 0 2rem}.settings-tiles h2{margin:0 0 .5rem;font-size:.875rem;font-weight:600;color:var(--so-muted)}.settings-tiles section>div{display:grid;grid-template-columns:repeat(auto-fill,minmax(8.5rem,1fr));gap:.5rem}.settings-tiles a>span{display:grid;gap:.1rem;min-width:0}.settings-tiles .provider-status{font-weight:400;font-size:.8125rem}.settings-tiles a{display:flex;align-items:center;gap:.6rem;min-height:3rem;padding:.65rem .8rem;border:1px solid var(--so-line);border-radius:.625rem;background:var(--so-paper);color:var(--so-ink);text-decoration:none;font-weight:550;font-size:.875rem}@media(hover:hover) and (pointer:fine){.settings-tiles a:hover{border-color:var(--so-input-line);background:var(--so-raised)}}.settings-tiles a>svg{width:1.1rem;height:1.1rem;flex-shrink:0;color:var(--so-accent-text)}details.settings-more{margin:.25rem 0 1.25rem}details.settings-more>summary{cursor:pointer;min-height:2.75rem;display:list-item;padding-block:.7rem;font-weight:550}details.settings-more>summary .meta{font-weight:400;margin-left:.35rem}.settings-changed{margin-top:-.25rem}.appearance{margin:0 0 28px}.appearance h2{margin:0 0 10px}.theme-switch{display:inline-flex;flex-wrap:nowrap;max-width:100%;gap:4px;padding:4px;margin:0;border:1px solid var(--so-line);border-radius:10px;background:var(--so-raised)}.theme-switch .theme-choice,.so-native-region .theme-switch .theme-choice{flex:1 1 0;width:auto;white-space:nowrap;min-height:40px;padding:8px 16px;border:0;border-radius:7px;background:transparent;color:var(--so-muted);font:inherit;font-weight:550;box-shadow:none;cursor:pointer}@media(hover:hover) and (pointer:fine){.theme-switch .theme-choice:hover{color:var(--so-ink)}}.theme-switch .theme-choice[aria-pressed="true"]{background:var(--so-paper);color:var(--so-ink);box-shadow:0 1px 2px rgb(0 0 0 / .1)}.appearance .meta{margin:8px 0 0}@media(max-width:600px){.theme-switch .theme-choice{min-height:44px}}.update-notes{white-space:pre-wrap;overflow-wrap:anywhere;font:inherit;max-height:18rem;overflow:auto}`;
|
|
136
140
|
/** The page CSS this module writes itself (not the imported modules'), for the stylesheet contract tests. */
|