@sweny-ai/core 0.1.148 → 0.1.150
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/dist/cli/builtin-workflows.d.ts +45 -0
- package/dist/cli/builtin-workflows.js +129 -0
- package/dist/cli/check.d.ts +2 -8
- package/dist/cli/check.js +2 -34
- package/dist/cli/config.js +8 -3
- package/dist/cli/main.js +46 -11
- package/dist/cli/packs.d.ts +9 -5
- package/dist/cli/packs.js +100 -50
- package/dist/executor.js +15 -3
- package/dist/harness/acp-rpc.d.ts +75 -0
- package/dist/harness/acp-rpc.js +204 -0
- package/dist/harness/acp.d.ts +189 -0
- package/dist/harness/acp.js +942 -0
- package/dist/harness/agents.d.ts +5 -1
- package/dist/harness/agents.js +8 -1
- package/dist/harness/auth.d.ts +38 -0
- package/dist/harness/auth.js +93 -0
- package/dist/harness/capabilities.d.ts +30 -0
- package/dist/harness/capabilities.js +41 -0
- package/dist/harness/claude-code.d.ts +8 -0
- package/dist/harness/claude-code.js +8 -1
- package/dist/harness/codex.d.ts +10 -1
- package/dist/harness/codex.js +17 -3
- package/dist/harness/index.d.ts +7 -3
- package/dist/harness/index.js +7 -2
- package/dist/harness/parse.d.ts +12 -0
- package/dist/harness/parse.js +1 -1
- package/dist/harness/policy.js +6 -0
- package/dist/harness/types.d.ts +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/node-policy.js +1 -0
- package/dist/safe-outputs.d.ts +17 -5
- package/dist/safe-outputs.js +85 -11
- package/dist/schema.d.ts +80 -0
- package/dist/schema.js +23 -0
- package/dist/types.d.ts +13 -0
- package/dist/workflows/browser.js +2 -2
- package/dist/workflows/implement.yml +61 -19
- package/dist/workflows/triage.yml +171 -139
- package/package.json +1 -1
package/dist/harness/agents.d.ts
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export declare const SUPPORTED_AGENTS: readonly ["claude", "codex", "pi"];
|
|
8
8
|
export type SupportedAgent = (typeof SUPPORTED_AGENTS)[number];
|
|
9
|
-
|
|
9
|
+
/** `acp:<command>` (#416): any Agent Client Protocol agent, started with `<command>`. */
|
|
10
|
+
export type AcpAgentId = `acp:${string}`;
|
|
11
|
+
/** The command of an `acp:<command>` agent id (`"opencode acp"` for `acp:opencode acp`), or undefined when it is not one or the command is empty. */
|
|
12
|
+
export declare function parseAcpAgent(id: string): string | undefined;
|
|
13
|
+
export declare function isSupportedAgent(id: string): id is SupportedAgent | AcpAgentId;
|
|
10
14
|
/** The error for an agent id with no adapter. */
|
|
11
15
|
export declare function unsupportedAgentError(id: string): string;
|
package/dist/harness/agents.js
CHANGED
|
@@ -5,8 +5,15 @@
|
|
|
5
5
|
* the harness factory share it).
|
|
6
6
|
*/
|
|
7
7
|
export const SUPPORTED_AGENTS = ["claude", "codex", "pi"];
|
|
8
|
+
/** The command of an `acp:<command>` agent id (`"opencode acp"` for `acp:opencode acp`), or undefined when it is not one or the command is empty. */
|
|
9
|
+
export function parseAcpAgent(id) {
|
|
10
|
+
if (!id.startsWith("acp:"))
|
|
11
|
+
return undefined;
|
|
12
|
+
const command = id.slice("acp:".length).trim();
|
|
13
|
+
return command === "" ? undefined : command;
|
|
14
|
+
}
|
|
8
15
|
export function isSupportedAgent(id) {
|
|
9
|
-
return SUPPORTED_AGENTS.includes(id);
|
|
16
|
+
return SUPPORTED_AGENTS.includes(id) || parseAcpAgent(id) !== undefined;
|
|
10
17
|
}
|
|
11
18
|
/** The error for an agent id with no adapter. */
|
|
12
19
|
export function unsupportedAgentError(id) {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent login probes (#339): does the agent a harness drives have a way to
|
|
3
|
+
* authenticate? Each adapter's `preflight()` calls its probe, so `sweny
|
|
4
|
+
* workflow run` and `sweny check` fail before any node runs, with the fix.
|
|
5
|
+
*
|
|
6
|
+
* Presence only. No probe reads a credential's value, and the macOS Keychain
|
|
7
|
+
* probe asks for the entry's attributes, never its secret.
|
|
8
|
+
*/
|
|
9
|
+
export type AuthProbeResult = {
|
|
10
|
+
ok: true;
|
|
11
|
+
via: string;
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
reason: string;
|
|
15
|
+
};
|
|
16
|
+
/** Test seam: adapters take one in their options instead of reading the host. */
|
|
17
|
+
export type AuthProbe = () => AuthProbeResult;
|
|
18
|
+
/** Same shape as the CLI's other "Missing:" lines; names the agent so the fix is unambiguous. */
|
|
19
|
+
export declare const CLAUDE_CODE_AUTH_HINT: string;
|
|
20
|
+
export declare const CODEX_AUTH_HINT = "Missing: OPENAI_API_KEY or CODEX_API_KEY, or a Codex login (`codex login`), for --agent codex";
|
|
21
|
+
/**
|
|
22
|
+
* A local Claude Code login: `~/.claude/.credentials.json` (or under
|
|
23
|
+
* `CLAUDE_CONFIG_DIR`) and, on macOS, the Keychain entry (attributes only).
|
|
24
|
+
*/
|
|
25
|
+
export declare function detectClaudeCodeLogin(opts?: {
|
|
26
|
+
env?: Record<string, string | undefined>;
|
|
27
|
+
home?: string;
|
|
28
|
+
platform?: NodeJS.Platform;
|
|
29
|
+
keychainHasLogin?: () => boolean;
|
|
30
|
+
}): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Claude Code auth, the way the spawned agent will see it: Bedrock or Vertex
|
|
33
|
+
* routing, an env credential that survives `SWENY_AUTH` (`oauth` keeps only
|
|
34
|
+
* `CLAUDE_CODE_OAUTH_TOKEN`), or a stored Claude Code login.
|
|
35
|
+
*/
|
|
36
|
+
export declare function claudeCodeAuth(env?: Record<string, string | undefined>, login?: () => boolean): AuthProbeResult;
|
|
37
|
+
/** Codex auth: an API key or access token in env, or a stored `codex login`. */
|
|
38
|
+
export declare function codexAuth(env?: Record<string, string | undefined>, login?: () => boolean): AuthProbeResult;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent login probes (#339): does the agent a harness drives have a way to
|
|
3
|
+
* authenticate? Each adapter's `preflight()` calls its probe, so `sweny
|
|
4
|
+
* workflow run` and `sweny check` fail before any node runs, with the fix.
|
|
5
|
+
*
|
|
6
|
+
* Presence only. No probe reads a credential's value, and the macOS Keychain
|
|
7
|
+
* probe asks for the entry's attributes, never its secret.
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from "node:fs";
|
|
10
|
+
import * as os from "node:os";
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import { spawnSync } from "node:child_process";
|
|
13
|
+
import { hasCodexLogin } from "../agent-env.js";
|
|
14
|
+
/** Same shape as the CLI's other "Missing:" lines; names the agent so the fix is unambiguous. */
|
|
15
|
+
export const CLAUDE_CODE_AUTH_HINT = "Missing: ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN (from `claude setup-token`), " +
|
|
16
|
+
"or a Claude Code login (run `claude`, then /login), for --agent claude";
|
|
17
|
+
export const CODEX_AUTH_HINT = "Missing: OPENAI_API_KEY or CODEX_API_KEY, or a Codex login (`codex login`), for --agent codex";
|
|
18
|
+
function set(v) {
|
|
19
|
+
return typeof v === "string" && v.trim() !== "";
|
|
20
|
+
}
|
|
21
|
+
function truthy(v) {
|
|
22
|
+
if (!set(v))
|
|
23
|
+
return false;
|
|
24
|
+
const s = v.trim().toLowerCase();
|
|
25
|
+
return s !== "0" && s !== "false" && s !== "no" && s !== "off";
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A local Claude Code login: `~/.claude/.credentials.json` (or under
|
|
29
|
+
* `CLAUDE_CONFIG_DIR`) and, on macOS, the Keychain entry (attributes only).
|
|
30
|
+
*/
|
|
31
|
+
export function detectClaudeCodeLogin(opts = {}) {
|
|
32
|
+
const env = opts.env ?? process.env;
|
|
33
|
+
const home = opts.home ?? os.homedir();
|
|
34
|
+
const platform = opts.platform ?? process.platform;
|
|
35
|
+
const configDir = env.CLAUDE_CONFIG_DIR || path.join(home, ".claude");
|
|
36
|
+
try {
|
|
37
|
+
if (fs.existsSync(path.join(configDir, ".credentials.json")))
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// fall through
|
|
42
|
+
}
|
|
43
|
+
if (platform === "darwin") {
|
|
44
|
+
const probe = opts.keychainHasLogin ??
|
|
45
|
+
(() => {
|
|
46
|
+
const r = spawnSync("security", ["find-generic-password", "-s", "Claude Code-credentials"], {
|
|
47
|
+
stdio: "ignore",
|
|
48
|
+
timeout: 3000,
|
|
49
|
+
});
|
|
50
|
+
return r.status === 0;
|
|
51
|
+
});
|
|
52
|
+
try {
|
|
53
|
+
return probe();
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Claude Code auth, the way the spawned agent will see it: Bedrock or Vertex
|
|
63
|
+
* routing, an env credential that survives `SWENY_AUTH` (`oauth` keeps only
|
|
64
|
+
* `CLAUDE_CODE_OAUTH_TOKEN`), or a stored Claude Code login.
|
|
65
|
+
*/
|
|
66
|
+
export function claudeCodeAuth(env = process.env, login = () => detectClaudeCodeLogin({ env })) {
|
|
67
|
+
if (truthy(env.CLAUDE_CODE_USE_BEDROCK))
|
|
68
|
+
return { ok: true, via: "Amazon Bedrock" };
|
|
69
|
+
if (truthy(env.CLAUDE_CODE_USE_VERTEX))
|
|
70
|
+
return { ok: true, via: "Google Vertex AI" };
|
|
71
|
+
const mode = (env.SWENY_AUTH ?? "").trim().toLowerCase();
|
|
72
|
+
if (set(env.CLAUDE_CODE_OAUTH_TOKEN))
|
|
73
|
+
return { ok: true, via: "CLAUDE_CODE_OAUTH_TOKEN" };
|
|
74
|
+
if (mode !== "oauth") {
|
|
75
|
+
if (set(env.ANTHROPIC_API_KEY))
|
|
76
|
+
return { ok: true, via: "ANTHROPIC_API_KEY" };
|
|
77
|
+
if (set(env.ANTHROPIC_AUTH_TOKEN))
|
|
78
|
+
return { ok: true, via: "ANTHROPIC_AUTH_TOKEN" };
|
|
79
|
+
}
|
|
80
|
+
if (login())
|
|
81
|
+
return { ok: true, via: "Claude Code login" };
|
|
82
|
+
return { ok: false, reason: CLAUDE_CODE_AUTH_HINT };
|
|
83
|
+
}
|
|
84
|
+
/** Codex auth: an API key or access token in env, or a stored `codex login`. */
|
|
85
|
+
export function codexAuth(env = process.env, login = () => hasCodexLogin(env)) {
|
|
86
|
+
for (const k of ["CODEX_API_KEY", "OPENAI_API_KEY", "CODEX_ACCESS_TOKEN"]) {
|
|
87
|
+
if (set(env[k]))
|
|
88
|
+
return { ok: true, via: k };
|
|
89
|
+
}
|
|
90
|
+
if (login())
|
|
91
|
+
return { ok: true, via: "codex login" };
|
|
92
|
+
return { ok: false, reason: CODEX_AUTH_HINT };
|
|
93
|
+
}
|
|
@@ -56,3 +56,33 @@ export declare const CODEX_CAPABILITIES: HarnessCapabilities;
|
|
|
56
56
|
* - cancel rpc: the `abort` command, then SIGTERM and SIGKILL.
|
|
57
57
|
*/
|
|
58
58
|
export declare const PI_CAPABILITIES: HarnessCapabilities;
|
|
59
|
+
/**
|
|
60
|
+
* Any ACP agent (`--agent acp:<command>`, #416), declared from the Agent
|
|
61
|
+
* Client Protocol schema v1.24.1 (agentclientprotocol/agent-client-protocol,
|
|
62
|
+
* `schema/v1/schema.json`, docs under `docs/protocol/v1/`) and probed by the
|
|
63
|
+
* contract suite against a scripted fake agent. ACP is the long-tail adapter:
|
|
64
|
+
* the protocol carries a prompt and a stream of updates, not sweny's opinions.
|
|
65
|
+
* - structuredOutput prompt: `PromptResponse` has only `stopReason`, so there
|
|
66
|
+
* is no schema channel. sweny asks for the JSON in the prompt, parses it,
|
|
67
|
+
* checks it and retries once.
|
|
68
|
+
* - toolTrace full: `tool_call` and `tool_call_update` session updates carry an
|
|
69
|
+
* id, a kind, a status and (optionally) raw input and content.
|
|
70
|
+
* - builtinDeny none: the protocol has no tool allow or deny list. The only
|
|
71
|
+
* hook is `session/request_permission`, which fires only when the agent
|
|
72
|
+
* chooses to ask. sweny answers it by policy (best effort), but cannot
|
|
73
|
+
* promise the agent asks, so nothing is declared.
|
|
74
|
+
* - mcp inject, exclusive none: `session/new` takes `mcpServers`, but whether
|
|
75
|
+
* the agent also loads its own MCP config is agent-defined.
|
|
76
|
+
* - sandbox none: nothing in the protocol. The process wrapper (srt) is the
|
|
77
|
+
* only containment, see sandbox-wrapper.ts.
|
|
78
|
+
* - readOnly none: modes are agent-defined. `policyGate` counts read-only as
|
|
79
|
+
* enforced only with the wrapper's read-only mount.
|
|
80
|
+
* - turnLimit watchdog: no turn limit in the protocol; sweny counts tool calls.
|
|
81
|
+
* - usage costUsd only: `usage_update {used, size, cost?}` is the stable
|
|
82
|
+
* usage. `used` and `size` are context window occupancy, not billed tokens,
|
|
83
|
+
* so they are not mapped; `cost` (cumulative, ISO 4217 currency) is, when it
|
|
84
|
+
* is USD. Per-turn token usage is behind an unstable flag.
|
|
85
|
+
* - cancel rpc: `session/cancel`, then kill.
|
|
86
|
+
* - resume false: sweny starts a fresh session per node.
|
|
87
|
+
*/
|
|
88
|
+
export declare const ACP_CAPABILITIES: HarnessCapabilities;
|
|
@@ -90,3 +90,44 @@ export const PI_CAPABILITIES = {
|
|
|
90
90
|
cancel: "rpc",
|
|
91
91
|
resume: false,
|
|
92
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* Any ACP agent (`--agent acp:<command>`, #416), declared from the Agent
|
|
95
|
+
* Client Protocol schema v1.24.1 (agentclientprotocol/agent-client-protocol,
|
|
96
|
+
* `schema/v1/schema.json`, docs under `docs/protocol/v1/`) and probed by the
|
|
97
|
+
* contract suite against a scripted fake agent. ACP is the long-tail adapter:
|
|
98
|
+
* the protocol carries a prompt and a stream of updates, not sweny's opinions.
|
|
99
|
+
* - structuredOutput prompt: `PromptResponse` has only `stopReason`, so there
|
|
100
|
+
* is no schema channel. sweny asks for the JSON in the prompt, parses it,
|
|
101
|
+
* checks it and retries once.
|
|
102
|
+
* - toolTrace full: `tool_call` and `tool_call_update` session updates carry an
|
|
103
|
+
* id, a kind, a status and (optionally) raw input and content.
|
|
104
|
+
* - builtinDeny none: the protocol has no tool allow or deny list. The only
|
|
105
|
+
* hook is `session/request_permission`, which fires only when the agent
|
|
106
|
+
* chooses to ask. sweny answers it by policy (best effort), but cannot
|
|
107
|
+
* promise the agent asks, so nothing is declared.
|
|
108
|
+
* - mcp inject, exclusive none: `session/new` takes `mcpServers`, but whether
|
|
109
|
+
* the agent also loads its own MCP config is agent-defined.
|
|
110
|
+
* - sandbox none: nothing in the protocol. The process wrapper (srt) is the
|
|
111
|
+
* only containment, see sandbox-wrapper.ts.
|
|
112
|
+
* - readOnly none: modes are agent-defined. `policyGate` counts read-only as
|
|
113
|
+
* enforced only with the wrapper's read-only mount.
|
|
114
|
+
* - turnLimit watchdog: no turn limit in the protocol; sweny counts tool calls.
|
|
115
|
+
* - usage costUsd only: `usage_update {used, size, cost?}` is the stable
|
|
116
|
+
* usage. `used` and `size` are context window occupancy, not billed tokens,
|
|
117
|
+
* so they are not mapped; `cost` (cumulative, ISO 4217 currency) is, when it
|
|
118
|
+
* is USD. Per-turn token usage is behind an unstable flag.
|
|
119
|
+
* - cancel rpc: `session/cancel`, then kill.
|
|
120
|
+
* - resume false: sweny starts a fresh session per node.
|
|
121
|
+
*/
|
|
122
|
+
export const ACP_CAPABILITIES = {
|
|
123
|
+
structuredOutput: "prompt",
|
|
124
|
+
toolTrace: "full",
|
|
125
|
+
builtinDeny: "none",
|
|
126
|
+
mcp: { inject: true, exclusive: "none" },
|
|
127
|
+
sandbox: { fs: false, network: false },
|
|
128
|
+
readOnly: "none",
|
|
129
|
+
turnLimit: "watchdog",
|
|
130
|
+
usage: { tokens: false, costUsd: true, live: false },
|
|
131
|
+
cancel: "rpc",
|
|
132
|
+
resume: false,
|
|
133
|
+
};
|
|
@@ -15,6 +15,7 @@ import { CLAUDE_CODE_CAPABILITIES } from "./capabilities.js";
|
|
|
15
15
|
import { buildEvaluatePrompt } from "./prompts.js";
|
|
16
16
|
import { parseToolResultContent, summarizeToolError } from "./parse.js";
|
|
17
17
|
import { makeAbort } from "./abort.js";
|
|
18
|
+
import { type AuthProbe } from "./auth.js";
|
|
18
19
|
export { buildEvaluatePrompt, CLAUDE_CODE_CAPABILITIES };
|
|
19
20
|
export { parseToolResultContent, summarizeToolError, makeAbort };
|
|
20
21
|
/**
|
|
@@ -117,6 +118,8 @@ export interface ClaudeCodeHarnessOptions {
|
|
|
117
118
|
sandboxAllowedDomains?: string[];
|
|
118
119
|
/** Sandbox preflight probe (test seam). Returns a reason when the sandbox cannot run. */
|
|
119
120
|
sandboxProbe?: () => string | undefined;
|
|
121
|
+
/** Login probe for `preflight()` (test seam). Default: {@link claudeCodeAuth} over `process.env`. */
|
|
122
|
+
authProbe?: AuthProbe;
|
|
120
123
|
/**
|
|
121
124
|
* Serve skill tools through the SwenyToolBridge (a stdio MCP shim over a
|
|
122
125
|
* per-run unix socket, #414) instead of the in-process SDK MCP server.
|
|
@@ -149,6 +152,7 @@ export declare class ClaudeCodeHarness implements Claude, AgentHarness {
|
|
|
149
152
|
private envScope;
|
|
150
153
|
private toolBridge;
|
|
151
154
|
private toolBridgeShim;
|
|
155
|
+
private authProbe;
|
|
152
156
|
constructor(opts?: ClaudeCodeHarnessOptions);
|
|
153
157
|
/**
|
|
154
158
|
* Build env for the Claude Code subprocess (#360). Applies auth precedence
|
|
@@ -162,9 +166,13 @@ export declare class ClaudeCodeHarness implements Claude, AgentHarness {
|
|
|
162
166
|
private buildEnv;
|
|
163
167
|
/** Which harness (and SDK version) produced a result. */
|
|
164
168
|
info(): HarnessInfo;
|
|
169
|
+
/** Checks the agent can authenticate (#339): an env credential, Bedrock/Vertex, or a Claude Code login. */
|
|
165
170
|
preflight(): Promise<{
|
|
166
171
|
ok: true;
|
|
167
172
|
version: string;
|
|
173
|
+
} | {
|
|
174
|
+
ok: false;
|
|
175
|
+
reason: string;
|
|
168
176
|
}>;
|
|
169
177
|
/**
|
|
170
178
|
* Run a node. Runs {@link policyGate} (always `degraded: []` for Claude
|
|
@@ -19,6 +19,7 @@ import { jsonSchemaToZodShape, toolErrorToMcpResult, toolOutputToMcpResult } fro
|
|
|
19
19
|
import { startToolBridge } from "./tool-bridge/server.js";
|
|
20
20
|
import { parseToolResultContent, summarizeToolError, tryParseJSON } from "./parse.js";
|
|
21
21
|
import { makeAbort } from "./abort.js";
|
|
22
|
+
import { claudeCodeAuth } from "./auth.js";
|
|
22
23
|
export { buildEvaluatePrompt, CLAUDE_CODE_CAPABILITIES };
|
|
23
24
|
export { parseToolResultContent, summarizeToolError, makeAbort };
|
|
24
25
|
let cachedSdkVersion;
|
|
@@ -218,6 +219,7 @@ export class ClaudeCodeHarness {
|
|
|
218
219
|
envScope;
|
|
219
220
|
toolBridge;
|
|
220
221
|
toolBridgeShim;
|
|
222
|
+
authProbe;
|
|
221
223
|
constructor(opts = {}) {
|
|
222
224
|
this.model = opts.model;
|
|
223
225
|
this.maxTurns = opts.maxTurns ?? 20;
|
|
@@ -233,6 +235,7 @@ export class ClaudeCodeHarness {
|
|
|
233
235
|
this.sandboxProbe = opts.sandboxProbe;
|
|
234
236
|
this.toolBridge = opts.toolBridge ?? process.env.SWENY_TOOL_BRIDGE === "1";
|
|
235
237
|
this.toolBridgeShim = opts.toolBridgeShim;
|
|
238
|
+
this.authProbe = opts.authProbe ?? (() => claudeCodeAuth(process.env));
|
|
236
239
|
}
|
|
237
240
|
/**
|
|
238
241
|
* Build env for the Claude Code subprocess (#360). Applies auth precedence
|
|
@@ -260,7 +263,11 @@ export class ClaudeCodeHarness {
|
|
|
260
263
|
info() {
|
|
261
264
|
return { id: this.id, version: claudeSdkVersion() };
|
|
262
265
|
}
|
|
266
|
+
/** Checks the agent can authenticate (#339): an env credential, Bedrock/Vertex, or a Claude Code login. */
|
|
263
267
|
async preflight() {
|
|
268
|
+
const auth = this.authProbe();
|
|
269
|
+
if (!auth.ok)
|
|
270
|
+
return { ok: false, reason: auth.reason };
|
|
264
271
|
return { ok: true, version: claudeSdkVersion() };
|
|
265
272
|
}
|
|
266
273
|
/**
|
|
@@ -301,7 +308,7 @@ export class ClaudeCodeHarness {
|
|
|
301
308
|
...req,
|
|
302
309
|
readOnly,
|
|
303
310
|
disallowedTools: disallowedTools.length > 0 ? disallowedTools : undefined,
|
|
304
|
-
strictMcp: readOnly || policy.strict,
|
|
311
|
+
strictMcp: readOnly || policy.strict || policy.exclusiveMcp === true,
|
|
305
312
|
sandboxMode: policy.sandbox,
|
|
306
313
|
agentAccess: { envVars: req.agentAccess?.envVars ?? [], domains: policy.egress },
|
|
307
314
|
});
|
package/dist/harness/codex.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ import type { AgentHarness, HarnessCapabilities, HarnessCompleteRequest, Harness
|
|
|
29
29
|
import { CODEX_CAPABILITIES } from "./capabilities.js";
|
|
30
30
|
import { type HarnessPolicyMode } from "./policy.js";
|
|
31
31
|
import { type SandboxWrapper } from "./sandbox-wrapper.js";
|
|
32
|
+
import { type AuthProbe } from "./auth.js";
|
|
32
33
|
export { CODEX_CAPABILITIES };
|
|
33
34
|
/** Oldest Codex CLI whose flags this adapter uses (`--ignore-user-config`, `--ignore-rules`). */
|
|
34
35
|
export declare const MIN_CODEX_VERSION = "0.159.0";
|
|
@@ -89,6 +90,8 @@ export interface CodexHarnessOptions {
|
|
|
89
90
|
};
|
|
90
91
|
/** Grace period between SIGTERM and SIGKILL when stopping Codex (default: 2000 ms). */
|
|
91
92
|
killGraceMs?: number;
|
|
93
|
+
/** Login probe for `preflight()` (test seam). Default: {@link codexAuth} over `process.env`. */
|
|
94
|
+
authProbe?: AuthProbe;
|
|
92
95
|
}
|
|
93
96
|
/** A TOML value for `-c key=value`, as the official SDK writes it (sdk/typescript/src/exec.ts). */
|
|
94
97
|
export declare function toTomlValue(value: unknown): string;
|
|
@@ -135,9 +138,13 @@ export declare class CodexHarness implements AgentHarness {
|
|
|
135
138
|
private killGraceMs;
|
|
136
139
|
private preflightResult;
|
|
137
140
|
private version;
|
|
141
|
+
private authProbe;
|
|
138
142
|
constructor(opts?: CodexHarnessOptions);
|
|
139
143
|
info(): HarnessInfo;
|
|
140
|
-
/**
|
|
144
|
+
/**
|
|
145
|
+
* Before any node runs (#339): the Codex CLI is installed and new enough,
|
|
146
|
+
* and Codex can authenticate (an API key, an access token, or `codex login`).
|
|
147
|
+
*/
|
|
141
148
|
preflight(): Promise<{
|
|
142
149
|
ok: true;
|
|
143
150
|
version: string;
|
|
@@ -145,6 +152,8 @@ export declare class CodexHarness implements AgentHarness {
|
|
|
145
152
|
ok: false;
|
|
146
153
|
reason: string;
|
|
147
154
|
}>;
|
|
155
|
+
/** `codex --version`, checked against {@link MIN_CODEX_VERSION}. Cached per instance. */
|
|
156
|
+
private checkCli;
|
|
148
157
|
/** Env for the Codex process: scoped like Claude Code's, with Codex's auth vars instead of Anthropic's. */
|
|
149
158
|
private buildEnv;
|
|
150
159
|
/** The node policy after `disallowed_tools` names are translated into Codex classes. */
|
package/dist/harness/codex.js
CHANGED
|
@@ -37,6 +37,7 @@ import { parseToolResultContent, summarizeToolError, tryParseJSON } from "./pars
|
|
|
37
37
|
import { startToolBridge } from "./tool-bridge/server.js";
|
|
38
38
|
import { defaultSandboxWrapper, prepareAgentSpawn, wrappersFrom, } from "./sandbox-wrapper.js";
|
|
39
39
|
import { TOKEN_ENV } from "./tool-bridge/protocol.js";
|
|
40
|
+
import { codexAuth } from "./auth.js";
|
|
40
41
|
export { CODEX_CAPABILITIES };
|
|
41
42
|
/** Oldest Codex CLI whose flags this adapter uses (`--ignore-user-config`, `--ignore-rules`). */
|
|
42
43
|
export const MIN_CODEX_VERSION = "0.159.0";
|
|
@@ -288,6 +289,7 @@ export class CodexHarness {
|
|
|
288
289
|
killGraceMs;
|
|
289
290
|
preflightResult;
|
|
290
291
|
version = "unknown";
|
|
292
|
+
authProbe;
|
|
291
293
|
constructor(opts = {}) {
|
|
292
294
|
this.model = opts.model;
|
|
293
295
|
this.maxTurns = opts.maxTurns ?? 20;
|
|
@@ -305,12 +307,24 @@ export class CodexHarness {
|
|
|
305
307
|
this.codexCommand = opts.codexCommand ?? { command: process.env.SWENY_CODEX_PATH || "codex", args: [] };
|
|
306
308
|
this.toolBridgeShim = opts.toolBridgeShim;
|
|
307
309
|
this.killGraceMs = opts.killGraceMs ?? 2000;
|
|
310
|
+
this.authProbe = opts.authProbe ?? (() => codexAuth(process.env));
|
|
308
311
|
}
|
|
309
312
|
info() {
|
|
310
313
|
return { id: this.id, version: this.version };
|
|
311
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* Before any node runs (#339): the Codex CLI is installed and new enough,
|
|
317
|
+
* and Codex can authenticate (an API key, an access token, or `codex login`).
|
|
318
|
+
*/
|
|
319
|
+
async preflight() {
|
|
320
|
+
const cli = await this.checkCli();
|
|
321
|
+
if (!cli.ok)
|
|
322
|
+
return cli;
|
|
323
|
+
const auth = this.authProbe();
|
|
324
|
+
return auth.ok ? cli : { ok: false, reason: auth.reason };
|
|
325
|
+
}
|
|
312
326
|
/** `codex --version`, checked against {@link MIN_CODEX_VERSION}. Cached per instance. */
|
|
313
|
-
|
|
327
|
+
checkCli() {
|
|
314
328
|
this.preflightResult ??= new Promise((resolve) => {
|
|
315
329
|
const { command, args } = this.codexCommand;
|
|
316
330
|
execFile(command, [...args, "--version"], { timeout: 30_000 }, (err, stdout) => {
|
|
@@ -723,7 +737,7 @@ export class CodexHarness {
|
|
|
723
737
|
};
|
|
724
738
|
if (gate.refuse)
|
|
725
739
|
return refused(gate.refuse);
|
|
726
|
-
const pre = await this.
|
|
740
|
+
const pre = await this.checkCli();
|
|
727
741
|
if (!pre.ok) {
|
|
728
742
|
this.logger.error(pre.reason);
|
|
729
743
|
return tag({ status: "failed", data: { error: pre.reason }, toolCalls: [] });
|
|
@@ -922,7 +936,7 @@ export class CodexHarness {
|
|
|
922
936
|
: `codex.ask: ${why}. Returning empty string.`);
|
|
923
937
|
return null;
|
|
924
938
|
};
|
|
925
|
-
const pre = await this.
|
|
939
|
+
const pre = await this.checkCli();
|
|
926
940
|
if (!pre.ok)
|
|
927
941
|
return failClosed(pre.reason);
|
|
928
942
|
const args = [
|
package/dist/harness/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { ClaudeCodeHarness, type ClaudeCodeHarnessOptions } from "./claude-code.js";
|
|
6
6
|
import { CodexHarness, type CodexHarnessOptions } from "./codex.js";
|
|
7
|
+
import { AcpHarness, type AcpHarnessOptions } from "./acp.js";
|
|
7
8
|
import { PiHarness, type PiHarnessOptions } from "./pi.js";
|
|
8
9
|
export { claudeCompat, asClaude } from "./compat.js";
|
|
9
10
|
export type { AgentHarness, HarnessCapabilities, HarnessCompleteRequest, HarnessId, HarnessInfo, HarnessRunRequest, HarnessRunResult, NodePolicy, PolicyGateResult, PolicyWrappers, ToolClass, } from "./types.js";
|
|
@@ -14,12 +15,14 @@ export { ClaudeCodeHarness, CLAUDE_CODE_CAPABILITIES } from "./claude-code.js";
|
|
|
14
15
|
export type { ClaudeCodeHarnessOptions } from "./claude-code.js";
|
|
15
16
|
export { CodexHarness, CODEX_CAPABILITIES, MIN_CODEX_VERSION } from "./codex.js";
|
|
16
17
|
export type { CodexHarnessOptions } from "./codex.js";
|
|
18
|
+
export { AcpHarness, ACP_CAPABILITIES, ACP_PROTOCOL_VERSION } from "./acp.js";
|
|
19
|
+
export type { AcpHarnessOptions } from "./acp.js";
|
|
17
20
|
export { PiHarness, PI_CAPABILITIES, MIN_PI_VERSION } from "./pi.js";
|
|
18
21
|
export type { PiHarnessOptions } from "./pi.js";
|
|
19
22
|
export { startToolBridge } from "./tool-bridge/server.js";
|
|
20
23
|
export type { ToolBridge, ToolBridgeOptions } from "./tool-bridge/server.js";
|
|
21
|
-
export { SUPPORTED_AGENTS, isSupportedAgent, unsupportedAgentError } from "./agents.js";
|
|
22
|
-
export type { SupportedAgent } from "./agents.js";
|
|
24
|
+
export { SUPPORTED_AGENTS, isSupportedAgent, parseAcpAgent, unsupportedAgentError } from "./agents.js";
|
|
25
|
+
export type { AcpAgentId, SupportedAgent } from "./agents.js";
|
|
23
26
|
/**
|
|
24
27
|
* Build the harness for an agent id. `claude` is the historical `--agent` /
|
|
25
28
|
* `coding-agent-provider` value and means the same as `claude-code`.
|
|
@@ -28,4 +31,5 @@ export type { SupportedAgent } from "./agents.js";
|
|
|
28
31
|
export declare function createHarness(id: "claude" | "claude-code", opts?: ClaudeCodeHarnessOptions): ClaudeCodeHarness;
|
|
29
32
|
export declare function createHarness(id: "codex", opts?: CodexHarnessOptions): CodexHarness;
|
|
30
33
|
export declare function createHarness(id: "pi", opts?: PiHarnessOptions): PiHarness;
|
|
31
|
-
export declare function createHarness(id: string
|
|
34
|
+
export declare function createHarness(id: `acp:${string}`, opts?: AcpHarnessOptions): AcpHarness;
|
|
35
|
+
export declare function createHarness(id: string, opts?: ClaudeCodeHarnessOptions & CodexHarnessOptions & PiHarnessOptions & AcpHarnessOptions): ClaudeCodeHarness | CodexHarness | PiHarness | AcpHarness;
|
package/dist/harness/index.js
CHANGED
|
@@ -4,17 +4,22 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { ClaudeCodeHarness } from "./claude-code.js";
|
|
6
6
|
import { CodexHarness } from "./codex.js";
|
|
7
|
+
import { AcpHarness } from "./acp.js";
|
|
7
8
|
import { PiHarness } from "./pi.js";
|
|
8
|
-
import { unsupportedAgentError } from "./agents.js";
|
|
9
|
+
import { parseAcpAgent, unsupportedAgentError } from "./agents.js";
|
|
9
10
|
export { claudeCompat, asClaude } from "./compat.js";
|
|
10
11
|
export { policyGate, nativeDenyClasses, resolveHarnessPolicy, isToolClass, TOOL_CLASSES } from "./policy.js";
|
|
11
12
|
export { ask, evaluate, buildAskPrompt, buildEvaluatePrompt } from "./prompts.js";
|
|
12
13
|
export { ClaudeCodeHarness, CLAUDE_CODE_CAPABILITIES } from "./claude-code.js";
|
|
13
14
|
export { CodexHarness, CODEX_CAPABILITIES, MIN_CODEX_VERSION } from "./codex.js";
|
|
15
|
+
export { AcpHarness, ACP_CAPABILITIES, ACP_PROTOCOL_VERSION } from "./acp.js";
|
|
14
16
|
export { PiHarness, PI_CAPABILITIES, MIN_PI_VERSION } from "./pi.js";
|
|
15
17
|
export { startToolBridge } from "./tool-bridge/server.js";
|
|
16
|
-
export { SUPPORTED_AGENTS, isSupportedAgent, unsupportedAgentError } from "./agents.js";
|
|
18
|
+
export { SUPPORTED_AGENTS, isSupportedAgent, parseAcpAgent, unsupportedAgentError } from "./agents.js";
|
|
17
19
|
export function createHarness(id, opts = {}) {
|
|
20
|
+
// `acp:<command>` (#416): any ACP agent. An empty command is an error, not a fallback.
|
|
21
|
+
if (id.startsWith("acp:"))
|
|
22
|
+
return new AcpHarness({ ...opts, acpCommand: parseAcpAgent(id) ?? "" });
|
|
18
23
|
switch (id) {
|
|
19
24
|
case "claude":
|
|
20
25
|
case "claude-code":
|
package/dist/harness/parse.d.ts
CHANGED
|
@@ -47,3 +47,15 @@ export declare function summarizeToolError(parsed: unknown): string;
|
|
|
47
47
|
* non-conforming model answer is at least attributable.
|
|
48
48
|
*/
|
|
49
49
|
export declare function tryParseJSON(text: string, outputSchema?: JSONSchema, logger?: Pick<Logger, "warn">, label?: string): Record<string, unknown>;
|
|
50
|
+
/**
|
|
51
|
+
* Lightweight structural check of a parsed object against a JSON Schema.
|
|
52
|
+
*
|
|
53
|
+
* Deliberately shallow: it verifies `required` keys are present and that the
|
|
54
|
+
* top-level `type` and each declared property `type` match. It does NOT do
|
|
55
|
+
* full JSON Schema validation (no `$ref`, `oneOf`, formats, nested arrays).
|
|
56
|
+
* The contract here is "flag an obviously non-conforming object" so a mismatch
|
|
57
|
+
* is logged rather than silently accepted; it is not a validation gate.
|
|
58
|
+
*
|
|
59
|
+
* Returns a list of human-readable problems; empty means no detected mismatch.
|
|
60
|
+
*/
|
|
61
|
+
export declare function schemaMismatches(value: unknown, schema: JSONSchema): string[];
|
package/dist/harness/parse.js
CHANGED
|
@@ -195,7 +195,7 @@ export function tryParseJSON(text, outputSchema, logger, label = "Claude") {
|
|
|
195
195
|
*
|
|
196
196
|
* Returns a list of human-readable problems; empty means no detected mismatch.
|
|
197
197
|
*/
|
|
198
|
-
function schemaMismatches(value, schema) {
|
|
198
|
+
export function schemaMismatches(value, schema) {
|
|
199
199
|
const problems = [];
|
|
200
200
|
const s = schema;
|
|
201
201
|
if (s.type === "object" || s.properties || s.required) {
|
package/dist/harness/policy.js
CHANGED
|
@@ -74,6 +74,12 @@ export function policyGate(caps, policy, wrappers = {}) {
|
|
|
74
74
|
unenforced.push(`disallowed_tools [${policy.nativeDeny.join(", ")}]: harness has no tools by these names`);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
+
// Exclusive MCP (`permissions.strict`): only the servers sweny passes may
|
|
78
|
+
// load, never the user's own config. A harness that cannot exclude them
|
|
79
|
+
// cannot honor it, so strict refuses the node.
|
|
80
|
+
if (policy.exclusiveMcp && caps.mcp.exclusive === "none") {
|
|
81
|
+
unenforced.push("exclusive MCP: harness cannot keep the user's own MCP servers out of the run");
|
|
82
|
+
}
|
|
77
83
|
if (policy.egress.length > 0 && !caps.sandbox.network && !wrappers.egress) {
|
|
78
84
|
unenforced.push("egress allowlist: harness has no network sandbox and no egress wrapper is active");
|
|
79
85
|
}
|
package/dist/harness/types.d.ts
CHANGED
|
@@ -55,6 +55,12 @@ export interface NodePolicy {
|
|
|
55
55
|
egress: string[];
|
|
56
56
|
/** Refuse instead of degrade when an opinion cannot be enforced. */
|
|
57
57
|
strict: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Only the MCP servers sweny passes may load, never the user's own config
|
|
60
|
+
* (`permissions.strict`, #365). A harness whose `mcp.exclusive` is `none`
|
|
61
|
+
* cannot honor it: reported in `degraded`, refused under `strict`.
|
|
62
|
+
*/
|
|
63
|
+
exclusiveMcp?: boolean;
|
|
58
64
|
/**
|
|
59
65
|
* Turn budget for the run. Set by adapters whose `turnLimit` is not native,
|
|
60
66
|
* so the gate reports how the budget is kept (watchdog) or that it is not.
|
package/dist/index.d.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* })
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
|
-
export type { Skill, SkillCategory, SkillDefinition, Tool, ToolContext, ConfigField, JSONSchema, Workflow, Node, Edge, NodeResult, ToolCall, ExecutionEvent, Observer, Claude, Logger, TraceStep, TraceEdge, ExecutionTrace, ExecutionResult, Source, ResolvedSource, SourceKind, SourceResolutionMap, WorkflowInputs, WorkflowInputField, WorkflowInputType, InputValidationError, InputValidationResult, Evaluator, EvaluatorRule, EvalResult, EvaluatorKind, NodeRequires, NodeRetry, NodeToolFilter, OutputMatch, NodeSources, EvalPolicy, RequiresOnFail, NodeOnFail, McpTransport, WorkflowType, SkillHarnessKey, NodeAccess, NodePermissions, NodePermissionsSpec, SafeOutputType, SafeOutputDeclaration, SafeOutputsPolicy, SafeOutputReceipt, AuthorAssociation, } from "./types.js";
|
|
24
|
+
export type { Skill, SkillCategory, SkillDefinition, Tool, ToolContext, ConfigField, JSONSchema, Workflow, Node, Edge, NodeResult, ToolCall, ExecutionEvent, Observer, Claude, Logger, TraceStep, TraceEdge, ExecutionTrace, ExecutionResult, Source, ResolvedSource, SourceKind, SourceResolutionMap, WorkflowInputs, WorkflowInputField, WorkflowInputType, InputValidationError, InputValidationResult, Evaluator, EvaluatorRule, EvalResult, EvaluatorKind, NodeRequires, NodeRetry, NodeToolFilter, OutputMatch, NodeSources, EvalPolicy, RequiresOnFail, NodeOnFail, McpTransport, WorkflowType, SkillHarnessKey, NodeAccess, NodePermissions, NodePermissionsSpec, SafeOutputType, SafeOutputDeclaration, SafeOutputsPolicy, SafeOutputReceipt, SafeOutputPin, AuthorAssociation, } from "./types.js";
|
|
25
25
|
export { WORKFLOW_INPUT_TYPES } from "./types.js";
|
|
26
26
|
export { EVALUATOR_KINDS, EVAL_POLICIES, REQUIRES_ON_FAIL, NODE_ON_FAIL, MCP_TRANSPORTS, SKILL_CATEGORIES, SKILL_HARNESSES, SKILL_ID_PATTERN, SKILL_ID_MAX_LENGTH, isValidSkillId, NODE_ACCESS, TOOL_CLASSES, SAFE_OUTPUT_TYPES, SAFE_OUTPUT_APPLIERS, AUTHOR_ASSOCIATIONS, } from "./types.js";
|
|
27
27
|
export { consoleLogger } from "./types.js";
|
package/dist/node-policy.js
CHANGED
|
@@ -38,5 +38,6 @@ export function buildNodePolicy(opts) {
|
|
|
38
38
|
...(opts.disallowedTools && opts.disallowedTools.length > 0 ? { nativeDeny: opts.disallowedTools } : {}),
|
|
39
39
|
egress: opts.egress ?? [],
|
|
40
40
|
strict: opts.permissions.strict,
|
|
41
|
+
...(opts.permissions.strict ? { exclusiveMcp: true } : {}),
|
|
41
42
|
};
|
|
42
43
|
}
|
package/dist/safe-outputs.d.ts
CHANGED
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
* the write, so this holds on every harness.
|
|
10
10
|
*
|
|
11
11
|
* Order of checks per intent (all deterministic): declared type, workflow
|
|
12
|
-
* ceiling, actor trust, expiry, skill, target pin, shape and size,
|
|
13
|
-
* title prefix, dedupe, node cap, run cap. An optional model screen runs last
|
|
12
|
+
* ceiling, actor trust, expiry, skill, target pin, issue pin, shape and size,
|
|
13
|
+
* labels, title prefix, dedupe, node cap, run cap. An optional model screen runs last
|
|
14
14
|
* over the intents that passed and can only veto them.
|
|
15
15
|
*/
|
|
16
|
-
import type { Logger, SafeOutputDeclaration, SafeOutputReceipt, SafeOutputsPolicy, SafeOutputType, Skill, Tool } from "./types.js";
|
|
16
|
+
import type { Logger, SafeOutputDeclaration, SafeOutputPin, SafeOutputReceipt, SafeOutputsPolicy, SafeOutputType, Skill, Tool } from "./types.js";
|
|
17
17
|
/** Name of the intent-recording tool a node with `outputs` receives. */
|
|
18
18
|
export declare const EMIT_OUTPUT_TOOL = "emit_output";
|
|
19
19
|
/** Longest title the write stage accepts (after the prefix). */
|
|
@@ -43,7 +43,7 @@ export interface SafeOutputIntent {
|
|
|
43
43
|
*/
|
|
44
44
|
export declare function createEmitOutputTool(declarations: SafeOutputDeclaration[], buffer: SafeOutputIntent[], now?: () => number): Tool;
|
|
45
45
|
/** Instruction block that tells the agent how writes work at this node. */
|
|
46
|
-
export declare function safeOutputsInstruction(declarations: SafeOutputDeclaration[]): string;
|
|
46
|
+
export declare function safeOutputsInstruction(declarations: SafeOutputDeclaration[], input?: Record<string, unknown>): string;
|
|
47
47
|
export interface ActorInfo {
|
|
48
48
|
/** GitHub login of whoever triggered the run. */
|
|
49
49
|
login?: string;
|
|
@@ -54,9 +54,19 @@ export interface ActorInfo {
|
|
|
54
54
|
export declare function resolveActor(env: Record<string, string | undefined>, override?: ActorInfo): ActorInfo;
|
|
55
55
|
/** True when no trust list is declared, or the actor matches one. Unknown actor = untrusted. */
|
|
56
56
|
export declare function actorTrusted(policy: SafeOutputsPolicy | undefined, actor: ActorInfo): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* The issue or PR a declaration pins, as a string. `{ input: name }` reads the
|
|
59
|
+
* run input; an absent or empty value is `undefined`, which refuses the write.
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolvePin(pin: SafeOutputPin, input?: Record<string, unknown>): string | undefined;
|
|
57
62
|
/** `30m` → 1_800_000. Undefined for anything the schema would reject. */
|
|
58
63
|
export declare function parseDuration(s: string | undefined): number | undefined;
|
|
59
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* The skill that applies a declaration: its `via`, else the first node skill
|
|
66
|
+
* that can and is configured, else the first node skill that can, else any
|
|
67
|
+
* configured skill that can. A node listing `[linear, github]` therefore files
|
|
68
|
+
* on GitHub when only GitHub has credentials.
|
|
69
|
+
*/
|
|
60
70
|
export declare function resolveOutputSkill(decl: SafeOutputDeclaration, nodeSkills: string[], skills: Map<string, Skill>): string | undefined;
|
|
61
71
|
/** Declarations whose applying skill is not configured (no credentials loaded). */
|
|
62
72
|
export declare function unresolvedOutputs(declarations: SafeOutputDeclaration[], nodeSkills: string[], skills: Map<string, Skill>): SafeOutputType[];
|
|
@@ -85,6 +95,8 @@ export interface ApplySafeOutputsOptions {
|
|
|
85
95
|
staged: boolean;
|
|
86
96
|
state: WriteStageState;
|
|
87
97
|
logger: Logger;
|
|
98
|
+
/** The run input, for `number: { input }` pins. */
|
|
99
|
+
input?: Record<string, unknown>;
|
|
88
100
|
/**
|
|
89
101
|
* Model screen (only called when `policy.screen` is on and at least one write
|
|
90
102
|
* passed every deterministic check). Anything but `ALLOW` is a veto.
|