@sweny-ai/core 0.1.132 → 0.1.134

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.
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Claude Client - Headless Claude Code backend
3
+ *
4
+ * Uses the @anthropic-ai/claude-agent-sdk to run headless Claude Code
5
+ * as the LLM backend. Tools are exposed via an in-process MCP server
6
+ * that Claude Code calls during execution.
7
+ *
8
+ * This is the ONLY supported LLM backend. The whole point of sweny
9
+ * is to use headless Claude Code - never the raw Anthropic API.
10
+ */
11
+ import type { Claude, ToolContext, Logger, McpServerConfig } from "../types.js";
12
+ import { type SandboxMode } from "../agent-env.js";
13
+ import type { AgentHarness, HarnessCapabilities, HarnessCompleteRequest, HarnessInfo, HarnessRunRequest, HarnessRunResult } from "./types.js";
14
+ import { CLAUDE_CODE_CAPABILITIES } from "./capabilities.js";
15
+ import { buildEvaluatePrompt } from "./prompts.js";
16
+ export { buildEvaluatePrompt, CLAUDE_CODE_CAPABILITIES };
17
+ /**
18
+ * Built-in tools disallowed for the route-evaluation (`evaluate`) and
19
+ * reflection/judge (`ask`) calls.
20
+ *
21
+ * These are pure classification calls over prior-node data that can be
22
+ * attacker-influenceable (a prior node summarizes an untrusted issue body,
23
+ * PR diff, log line, etc.). They must never be able to mutate the workspace
24
+ * or shell out, regardless of any node's own `disallowed_tools` policy.
25
+ * `maxTurns: 1` already bounds them; this removes the powerful built-ins
26
+ * from the model's context entirely as structural defense-in-depth.
27
+ */
28
+ export declare const CLASSIFICATION_DISALLOWED_TOOLS: readonly ["Bash", "Write", "Edit", "MultiEdit", "NotebookEdit", "WebFetch", "WebSearch"];
29
+ /**
30
+ * Built-in tools disallowed for a node run with `readOnly: true` (dry run,
31
+ * #380): everything that can edit the workspace, shell out (and so reach
32
+ * `gh`, `curl`, `git push`), spawn a subagent that could, or fetch an
33
+ * arbitrary URL (a GET can still trigger a webhook or exfiltrate data).
34
+ * Read, Grep, Glob and WebSearch stay available for analysis.
35
+ */
36
+ export declare const READ_ONLY_DISALLOWED_TOOLS: readonly ["Bash", "Write", "Edit", "MultiEdit", "NotebookEdit", "Task", "Agent", "WebFetch"];
37
+ /** How sweny resolves which credentials reach the Claude Code subprocess. */
38
+ export type SwenyAuthMode = "auto" | "api-key" | "oauth";
39
+ export interface ResolveAuthEnvOpts {
40
+ /** Explicit override; when absent, read from `env.SWENY_AUTH`. Test seam. */
41
+ mode?: SwenyAuthMode;
42
+ /** Logger for the debug line + invalid-value warning. */
43
+ logger?: Pick<Logger, "debug" | "warn">;
44
+ }
45
+ /**
46
+ * Decide which auth credentials survive into the Claude Code subprocess env.
47
+ *
48
+ * sweny does not select the winning credential (the spawned agent and any
49
+ * on-disk `~/.claude/.credentials.json` do that). This function only controls
50
+ * what is *present* in the env we hand the agent.
51
+ *
52
+ * Modes (`SWENY_AUTH`, default `auto`):
53
+ * - `auto` - today's protective behavior: when an OAuth token is present,
54
+ * strip `ANTHROPIC_API_KEY` so a stray `.env` key cannot
55
+ * silently bill a metered API account. `ANTHROPIC_AUTH_TOKEN`
56
+ * (a bearer token, not a console key) is never touched.
57
+ * - `api-key` - user explicitly authenticates a gateway with a key/bearer:
58
+ * preserve both even when an OAuth token is also present.
59
+ * - `oauth` - user explicitly wants subscription/OAuth: strip both
60
+ * `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` so a leftover
61
+ * credential cannot win at the agent. Fails closed.
62
+ *
63
+ * Base-URL presence is deliberately NOT a signal here: pass-through proxies
64
+ * (corporate egress, observability) set `ANTHROPIC_BASE_URL` while still
65
+ * billing the real key, so inferring "use the key" from a base URL would
66
+ * re-introduce a surprise-billing path. Intent must be explicit.
67
+ *
68
+ * An unrecognized `SWENY_AUTH` value falls back to `auto` with a warning
69
+ * (never throws - an env typo must not crash a CI run).
70
+ *
71
+ * Pure: returns a copy, never mutates the input.
72
+ */
73
+ export declare function resolveAuthEnv(env: Record<string, string>, opts?: ResolveAuthEnvOpts): Record<string, string>;
74
+ export interface ClaudeCodeHarnessOptions {
75
+ /** Model override */
76
+ model?: string;
77
+ /** Max turns for tool use loop (default: 20) */
78
+ maxTurns?: number;
79
+ /** Working directory for Claude Code (default: process.cwd()) */
80
+ cwd?: string;
81
+ /** Logger */
82
+ logger?: Logger;
83
+ /** Default tool context for standalone usage (not via executor) */
84
+ defaultContext?: ToolContext;
85
+ /** Catalog defaults, overridden by per-run skill servers and explicit mcpServers. */
86
+ defaultMcpServers?: Record<string, McpServerConfig>;
87
+ /** Explicit external MCP servers, overriding defaults and per-run skill servers. */
88
+ mcpServers?: Record<string, McpServerConfig>;
89
+ /**
90
+ * Extra env var names passed to the agent subprocess on top of the
91
+ * allowlist (see agent-env.ts). `"*"` inherits the full environment.
92
+ * Default: `SWENY_ENV_PASSTHROUGH` (comma-separated).
93
+ */
94
+ envPassthrough?: string[];
95
+ /**
96
+ * Scope the agent env to the allowlist. Default: `SWENY_ENV_SCOPE`, else on
97
+ * when CI is truthy and off locally (full env).
98
+ */
99
+ envScope?: boolean;
100
+ /**
101
+ * SDK sandbox for agent commands: `auto` (sandbox when the host supports
102
+ * it, else warn and run unsandboxed), `strict` (sandbox or fail), `off`.
103
+ * Default: `SWENY_SANDBOX`, else `auto` when CI is truthy, `off` otherwise.
104
+ */
105
+ sandbox?: SandboxMode;
106
+ /** Extra hosts sandboxed commands may reach. Default: `SWENY_SANDBOX_ALLOWED_DOMAINS`. */
107
+ sandboxAllowedDomains?: string[];
108
+ /** Sandbox preflight probe (test seam). Returns a reason when the sandbox cannot run. */
109
+ sandboxProbe?: () => string | undefined;
110
+ }
111
+ /**
112
+ * Wire an optional timeout + caller signal onto a single AbortController.
113
+ *
114
+ * Returns undefined when neither a timeout nor a signal is supplied so the
115
+ * default code path (no abortController, no timer) is byte-for-byte unchanged.
116
+ *
117
+ * `reason()` reports whether the abort came from the timeout timer or an
118
+ * external signal, so callers can log a distinct timeout message.
119
+ */
120
+ export declare function makeAbort(timeoutMs?: number, signal?: AbortSignal): {
121
+ controller: AbortController;
122
+ clear: () => void;
123
+ reason: () => "timeout" | "signal" | undefined;
124
+ } | undefined;
125
+ export declare class ClaudeCodeHarness implements Claude, AgentHarness {
126
+ readonly id: "claude-code";
127
+ readonly capabilities: HarnessCapabilities;
128
+ readonly defaultJudgeModel = "claude-haiku-4-5";
129
+ private model;
130
+ private maxTurns;
131
+ private cwd;
132
+ readonly logger: Logger;
133
+ private defaultContext;
134
+ private mcpServers;
135
+ private defaultMcpServers;
136
+ private envPassthrough;
137
+ private sandboxMode;
138
+ private sandboxAllowedDomains;
139
+ private sandboxProbe;
140
+ private sandboxWarned;
141
+ private envScope;
142
+ constructor(opts?: ClaudeCodeHarnessOptions);
143
+ /**
144
+ * Build env for the Claude Code subprocess (#360). Applies auth precedence
145
+ * via {@link resolveAuthEnv} on the full process env (it reads
146
+ * `SWENY_AUTH`). When env scoping is on (default in CI, see
147
+ * {@link resolveEnvScope}) the result is narrowed to the allowlist plus
148
+ * `extraVars` (the node's declared skill env vars) and the operator
149
+ * passthrough list, and the withheld names (never values) are reported once
150
+ * per process ({@link reportWithheldEnv}). When off, the full env passes through as before.
151
+ */
152
+ private buildEnv;
153
+ /** Which harness (and SDK version) produced a result. */
154
+ info(): HarnessInfo;
155
+ preflight(): Promise<{
156
+ ok: true;
157
+ version: string;
158
+ }>;
159
+ /**
160
+ * Run a node. The query itself is unchanged by the harness seam; this only
161
+ * runs {@link policyGate} (always `degraded: []` for Claude Code) and tags
162
+ * the result with the harness id and version.
163
+ */
164
+ run(req: HarnessRunRequest): Promise<HarnessRunResult>;
165
+ private runQuery;
166
+ /**
167
+ * Single completion: no tools, no MCP, one turn. Shared by core `ask()`
168
+ * (retry reflection, judge scoring) and `evaluate()` (route choice); the
169
+ * query options are the ones `ask` and `evaluate` used before the seam.
170
+ * Returns null when the query failed (SDK error, timeout, non-success
171
+ * subtype); callers fail closed.
172
+ */
173
+ complete(req: HarnessCompleteRequest): Promise<string | null>;
174
+ /** Route evaluation. Delegates to core {@link coreEvaluate} over {@link complete}. */
175
+ evaluate(opts: {
176
+ question: string;
177
+ context: Record<string, unknown>;
178
+ choices: {
179
+ id: string;
180
+ description: string;
181
+ }[];
182
+ /** Abort the query after this many ms. Default: no timeout (back-compat). */
183
+ timeoutMs?: number;
184
+ /** Caller-supplied abort signal. Aborting it interrupts the query. */
185
+ signal?: AbortSignal;
186
+ }): Promise<string | null>;
187
+ /** Free-text completion. Delegates to core {@link coreAsk} over {@link complete}. */
188
+ ask(opts: {
189
+ instruction: string;
190
+ context: Record<string, unknown>;
191
+ model?: string;
192
+ /** Abort the query after this many ms. Default: no timeout (back-compat). */
193
+ timeoutMs?: number;
194
+ /** Caller-supplied abort signal. Aborting it interrupts the query. */
195
+ signal?: AbortSignal;
196
+ }): Promise<string>;
197
+ }
198
+ /** @deprecated Use {@link ClaudeCodeHarnessOptions}. */
199
+ export type ClaudeClientOptions = ClaudeCodeHarnessOptions;
200
+ /** @deprecated Use {@link ClaudeCodeHarness} (or `createHarness("claude-code")`). */
201
+ export declare const ClaudeClient: typeof ClaudeCodeHarness;
202
+ /** @deprecated Use {@link ClaudeCodeHarness}. */
203
+ export type ClaudeClient = ClaudeCodeHarness;
204
+ /**
205
+ * Best-effort recovery of typed output from a tool_result's `content`.
206
+ *
207
+ * The MCP protocol sends tool results as string content. Our in-process
208
+ * wrapper JSON-stringifies structured output before returning; external
209
+ * MCP servers generally do the same for JSON payloads. If the string
210
+ * looks like a JSON object or array and parses, return the parsed value
211
+ * so verify's output-path walks work against typed data.
212
+ *
213
+ * Raw strings are preserved verbatim. JSON-primitive strings (e.g. the
214
+ * literal four characters `"42"`) are intentionally NOT parsed - we
215
+ * cannot distinguish a tool that returned the number 42 (wrapper sends
216
+ * `"42"`) from a tool that returned the string "42" (wrapper also sends
217
+ * `"42"`). Preserving the string is safer than guessing.
218
+ */
219
+ export declare function parseToolResultContent(content: unknown): unknown;
220
+ /**
221
+ * Produce a short, single-line description of a tool-error payload suitable
222
+ * for the CI log stream. The full parsed value stays on the ToolCall for
223
+ * verify and downstream tooling - this is only for inline observability.
224
+ *
225
+ * Collapses newlines, trims whitespace, and caps to 300 chars so a huge
226
+ * API response body doesn't flood the log.
227
+ */
228
+ export declare function summarizeToolError(parsed: unknown): string;