copperhead 0.5.0 → 0.7.0

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.
Files changed (74) hide show
  1. package/README.md +34 -1
  2. package/dist/agent/loop.js +130 -15
  3. package/dist/agent/loop.js.map +1 -1
  4. package/dist/agent/prompts.js +2 -1
  5. package/dist/agent/prompts.js.map +1 -1
  6. package/dist/agent/providers/claude-code.js +466 -0
  7. package/dist/agent/providers/claude-code.js.map +1 -0
  8. package/dist/agent/providers/openai.js +30 -10
  9. package/dist/agent/providers/openai.js.map +1 -1
  10. package/dist/agent/recovery.js +148 -0
  11. package/dist/agent/recovery.js.map +1 -0
  12. package/dist/agent/render.js +17 -2
  13. package/dist/agent/render.js.map +1 -1
  14. package/dist/agent/response-cache.js +81 -0
  15. package/dist/agent/response-cache.js.map +1 -0
  16. package/dist/agent/tools.js +61 -4
  17. package/dist/agent/tools.js.map +1 -1
  18. package/dist/agent/transcript.js.map +1 -1
  19. package/dist/cli.js +47 -2
  20. package/dist/cli.js.map +1 -1
  21. package/dist/commands/create.js +486 -35
  22. package/dist/commands/create.js.map +1 -1
  23. package/dist/commands/export.js +90 -0
  24. package/dist/commands/export.js.map +1 -0
  25. package/dist/config.js +33 -6
  26. package/dist/config.js.map +1 -1
  27. package/dist/kicad/bom-export.js +240 -0
  28. package/dist/kicad/bom-export.js.map +1 -0
  29. package/dist/kicad/bootstrap.js +166 -0
  30. package/dist/kicad/bootstrap.js.map +1 -0
  31. package/dist/kicad/fab.js +94 -0
  32. package/dist/kicad/fab.js.map +1 -0
  33. package/dist/kicad/spice.js +306 -0
  34. package/dist/kicad/spice.js.map +1 -0
  35. package/dist/kicad/symlib.js +228 -0
  36. package/dist/kicad/symlib.js.map +1 -0
  37. package/dist/memory/bom-table.js +232 -0
  38. package/dist/memory/bom-table.js.map +1 -0
  39. package/dist/memory/drift.js +33 -27
  40. package/dist/memory/drift.js.map +1 -1
  41. package/dist/util/git.js +37 -1
  42. package/dist/util/git.js.map +1 -1
  43. package/dist/util/preflight.js +37 -0
  44. package/dist/util/preflight.js.map +1 -1
  45. package/dist/util/retry.js +23 -0
  46. package/dist/util/retry.js.map +1 -1
  47. package/dist/util/tmp.js +119 -0
  48. package/dist/util/tmp.js.map +1 -0
  49. package/package.json +6 -2
  50. package/src/agent/loop.ts +148 -15
  51. package/src/agent/prompts.ts +2 -1
  52. package/src/agent/providers/claude-code.ts +550 -0
  53. package/src/agent/providers/openai.ts +33 -16
  54. package/src/agent/recovery.ts +162 -0
  55. package/src/agent/render.ts +28 -1
  56. package/src/agent/response-cache.ts +80 -0
  57. package/src/agent/tools.ts +62 -4
  58. package/src/agent/transcript.ts +1 -0
  59. package/src/agent/types.ts +18 -0
  60. package/src/cli.ts +52 -2
  61. package/src/commands/create.ts +543 -38
  62. package/src/commands/export.ts +117 -0
  63. package/src/config.ts +54 -6
  64. package/src/kicad/bom-export.ts +321 -0
  65. package/src/kicad/bootstrap.ts +181 -0
  66. package/src/kicad/fab.ts +121 -0
  67. package/src/kicad/spice.ts +399 -0
  68. package/src/kicad/symlib.ts +248 -0
  69. package/src/memory/bom-table.ts +249 -0
  70. package/src/memory/drift.ts +42 -32
  71. package/src/util/git.ts +37 -1
  72. package/src/util/preflight.ts +44 -0
  73. package/src/util/retry.ts +29 -0
  74. package/src/util/tmp.ts +113 -0
@@ -0,0 +1,466 @@
1
+ import { mkdtemp, rm, utimes } from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ /** Explicit deny list, belt-and-suspenders on top of the empty `tools` allowlist.
5
+ * `'*'` is the documented wildcard; the named entries stay for clarity and in
6
+ * case a given SDK build does not honor the wildcard. */
7
+ const DISALLOWED_BUILTINS = [
8
+ '*',
9
+ 'Bash',
10
+ 'Edit',
11
+ 'MultiEdit',
12
+ 'Write',
13
+ 'Read',
14
+ 'Glob',
15
+ 'Grep',
16
+ 'NotebookEdit',
17
+ 'WebFetch',
18
+ 'WebSearch',
19
+ 'Task',
20
+ 'TodoWrite',
21
+ ];
22
+ export class ClaudeCodeProvider {
23
+ model;
24
+ injectedQuery;
25
+ importSdk;
26
+ sessionResume;
27
+ name = 'claude-code';
28
+ callSeq = 0;
29
+ cwdPromise;
30
+ /** In-flight query aborters, so close() (called by the turn watchdog on a
31
+ * hung turn) can tear down the live subprocess, not just delete its cwd. */
32
+ inFlight = new Set();
33
+ /** Session-resume state (1.1). `sessionId` is the last session the SDK reported;
34
+ * `sentCount` is how many `messages` we have already handed it, so a resumed
35
+ * turn sends only the delta. Unused unless `sessionResume` is on. */
36
+ sessionId;
37
+ sentCount = 0;
38
+ constructor(model, injectedQuery, importSdk = (specifier) => import(specifier),
39
+ /**
40
+ * Opt-in: resume one SDK session across turns and send only new messages,
41
+ * instead of flattening and re-sending the entire conversation every turn
42
+ * (1.1). Cuts the ~quadratic history re-send that dominates long-stage cost.
43
+ * OFF by default and deliberately mutually exclusive with the response cache:
44
+ * the cache replays turns the resumed session never saw, so mixing them would
45
+ * desync the session. `makeProvider` enables it only when the cache is off.
46
+ */
47
+ sessionResume = false) {
48
+ this.model = model;
49
+ this.injectedQuery = injectedQuery;
50
+ this.importSdk = importSdk;
51
+ this.sessionResume = sessionResume;
52
+ }
53
+ // `opts.maxTokens` is intentionally ignored: the Agent SDK drives the Claude
54
+ // Code subprocess and exposes no per-call max-tokens knob. `opts.onStream` is
55
+ // honored: this provider streams, so it reports cumulative streamed-text length
56
+ // as blocks arrive, which the loop turns into a liveness heartbeat (5.1).
57
+ async chat(messages, tools, opts = {}) {
58
+ const query = await this.resolveQuery();
59
+ const system = messages
60
+ .filter((m) => m.role === 'system')
61
+ .map((m) => m.content)
62
+ .join('\n\n');
63
+ const systemPrompt = [system, renderToolProtocol(tools)].filter(Boolean).join('\n\n');
64
+ // Session-resume mode (1.1): once the SDK has given us a session id, resume it
65
+ // and send only the messages added since our last turn — the subprocess still
66
+ // holds the earlier conversation, so re-sending it would just re-bill it. The
67
+ // first turn (no session id yet) sends the full flattened history as usual.
68
+ const resume = this.sessionResume ? this.sessionId : undefined;
69
+ const prompt = resume ? renderDelta(messages, this.sentCount) : renderConversation(messages);
70
+ const catalog = new Set(tools.map((t) => t.name));
71
+ const cwd = await this.ensureCwd();
72
+ let text = null;
73
+ let inputTokens = 0;
74
+ let outputTokens = 0;
75
+ // One aborter per turn: close() aborts it to kill a hung subprocess.
76
+ const aborter = new AbortController();
77
+ this.inFlight.add(aborter);
78
+ try {
79
+ for await (const msg of query({
80
+ prompt,
81
+ options: {
82
+ systemPrompt,
83
+ ...(this.model ? { model: this.model } : {}),
84
+ abortController: aborter,
85
+ // Layered "the SDK executes nothing" defense (D1/D5):
86
+ // 1. `tools: []` disables ALL built-in tools (Agent SDK 0.3.x docs:
87
+ // "[] (empty array) - Disable all built-in tools").
88
+ // 2. `disallowedTools` denies by name, with a wildcard, as a backstop.
89
+ // 3. `canUseTool` denies every tool BEFORE it runs — the permission
90
+ // analog to Codex's read-only sandbox — so even an unrecognized
91
+ // future tool cannot execute.
92
+ // 4. The tool_use tripwire below fails the run loudly if one is
93
+ // emitted anyway. Any single layer failing is caught by the next.
94
+ tools: [],
95
+ ...(resume ? { resume } : {}),
96
+ disallowedTools: DISALLOWED_BUILTINS,
97
+ canUseTool: async (toolName) => ({
98
+ behavior: 'deny',
99
+ message: `copperhead claude-code is reasoning-only; the SDK must not execute tools (blocked ${toolName}).`,
100
+ interrupt: true,
101
+ }),
102
+ cwd,
103
+ // The SDK's `env` REPLACES the subprocess environment entirely, so
104
+ // inherit process.env and strip the billed API keys: a claude-code run
105
+ // must use the saved login and never silently a paid ANTHROPIC_API_KEY
106
+ // / OPENAI_API_KEY, even when one is also set (D2).
107
+ env: { ...process.env, ANTHROPIC_API_KEY: undefined, OPENAI_API_KEY: undefined },
108
+ maxTurns: 1,
109
+ },
110
+ })) {
111
+ if (msg.type === 'assistant') {
112
+ for (const block of msg.message?.content ?? []) {
113
+ if (block.type === 'text' && block.text) {
114
+ text = (text ?? '') + block.text;
115
+ // Report progress so the loop's heartbeat shows this turn is alive
116
+ // and streaming, not hung, during a multi-minute large-output turn.
117
+ opts.onStream?.(text.length);
118
+ }
119
+ else if (block.type === 'tool_use') {
120
+ // Load-bearing invariant (D1): the SDK must execute nothing, so it
121
+ // must never emit a tool_use block. If it does, `tools: []` was not
122
+ // honored — fail loudly rather than let an edit bypass copperhead's
123
+ // snapshot / verify / commit gates.
124
+ throw new Error('claude-code: the Agent SDK emitted a tool_use block, but its tools are ' +
125
+ 'disabled — the reasoning-only invariant was violated (SDK option drift?). ' +
126
+ 'Refusing to continue.');
127
+ }
128
+ }
129
+ }
130
+ else if (msg.type === 'result') {
131
+ if (typeof msg.usage?.input_tokens === 'number')
132
+ inputTokens = msg.usage.input_tokens;
133
+ if (typeof msg.usage?.output_tokens === 'number')
134
+ outputTokens = msg.usage.output_tokens;
135
+ }
136
+ // The session id can arrive on any message (init/system/result); keep the
137
+ // latest so the next turn can resume it (1.1). No-op unless resume is on.
138
+ if (this.sessionResume && typeof msg.session_id === 'string')
139
+ this.sessionId = msg.session_id;
140
+ }
141
+ }
142
+ catch (err) {
143
+ // Auth failures get an actionable message (non-retryable); everything else
144
+ // — crucially a 429 — is re-thrown untouched so its status survives for
145
+ // withRetry/isRateLimit (D4). We never fall back to a keyed provider: the
146
+ // distinct `name` makes otherProvider() return null for us.
147
+ if (isAuthError(err))
148
+ throw new Error(authHint(err.message));
149
+ throw err;
150
+ }
151
+ finally {
152
+ this.inFlight.delete(aborter);
153
+ }
154
+ // Only advance the high-water mark on a turn that completed: a thrown turn
155
+ // (rate limit, timeout) is retried, and must re-send the same delta so no
156
+ // message is lost from the resumed session (1.1).
157
+ if (this.sessionResume)
158
+ this.sentCount = messages.length;
159
+ const parsed = parseToolCalls(text, () => `cc-${++this.callSeq}`, catalog);
160
+ return {
161
+ text: parsed.text,
162
+ toolCalls: parsed.toolCalls,
163
+ usage: { inputTokens, outputTokens },
164
+ nudge: parsed.nudge,
165
+ };
166
+ }
167
+ /** Tear down in-flight work and remove the scratch cwd. Called by the turn
168
+ * watchdog on a hung turn (via withTimeout's onTimeout) AND once per run in a
169
+ * finally. Aborting first kills the `claude` subprocess a hung turn spawned —
170
+ * without it the process is orphaned and keeps writing to its temp cwd, which
171
+ * (with KiCad local history) was a source of the disk-fill halt (2.2/4.1, I8).
172
+ * A leftover empty dir in the OS tmpdir is harmless; the startup sweep reclaims
173
+ * any that a hard SIGKILL bypassed this cleanup for. */
174
+ async close() {
175
+ for (const aborter of this.inFlight) {
176
+ try {
177
+ aborter.abort();
178
+ }
179
+ catch {
180
+ // best effort: a controller that already settled throws nothing useful
181
+ }
182
+ }
183
+ this.inFlight.clear();
184
+ const pending = this.cwdPromise;
185
+ this.cwdPromise = undefined;
186
+ if (!pending)
187
+ return;
188
+ try {
189
+ await rm(await pending, { recursive: true, force: true });
190
+ }
191
+ catch {
192
+ // best effort: a leftover empty dir in the OS tmpdir is harmless
193
+ }
194
+ }
195
+ /** One isolated scratch cwd per provider instance, created once and reused
196
+ * across turns so a long run does not leak a temp dir per turn. Even with
197
+ * tools disabled this guarantees the SDK has no path into the repo (D5). */
198
+ async ensureCwd() {
199
+ if (!this.cwdPromise)
200
+ this.cwdPromise = mkdtemp(path.join(os.tmpdir(), 'copperhead-cc-'));
201
+ const cwd = await this.cwdPromise;
202
+ // Keep this reused scratch dir's mtime fresh on every turn. It is the only
203
+ // long-lived temp dir a run holds (kicad-cli dirs are per-call), so a
204
+ // multi-hour run would otherwise leave it with a stale mtime and a concurrent
205
+ // run's startup sweep (sweepStaleTempDirs, age-gated) could delete it out from
206
+ // under the live process (F4). Best-effort: a touch failure is harmless.
207
+ const now = new Date();
208
+ await utimes(cwd, now, now).catch(() => { });
209
+ return cwd;
210
+ }
211
+ async resolveQuery() {
212
+ if (this.injectedQuery)
213
+ return this.injectedQuery;
214
+ let mod;
215
+ try {
216
+ // importSdk defaults to a non-literal `import()`, so tsc never resolves the
217
+ // optional dependency at build time and it may legitimately be absent (D3).
218
+ mod = (await this.importSdk('@anthropic-ai/claude-agent-sdk'));
219
+ }
220
+ catch (err) {
221
+ // Only a genuinely-absent module gets the "install it" message; a present
222
+ // but broken install surfaces its real error rather than being mislabeled.
223
+ const code = err.code;
224
+ if (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') {
225
+ throw new Error('the claude-code provider needs the optional dependency @anthropic-ai/claude-agent-sdk; ' +
226
+ 'install it with `npm i @anthropic-ai/claude-agent-sdk`');
227
+ }
228
+ throw err;
229
+ }
230
+ const query = mod.query ?? mod.default?.query;
231
+ if (!query) {
232
+ throw new Error('@anthropic-ai/claude-agent-sdk did not export `query`; the installed version may be incompatible');
233
+ }
234
+ return query;
235
+ }
236
+ }
237
+ function renderToolProtocol(tools) {
238
+ if (!tools.length)
239
+ return '';
240
+ const lines = [
241
+ '# Tool protocol',
242
+ '',
243
+ 'You are the reasoning half of a tool-driven workflow; you cannot run anything yourself.',
244
+ 'To take an action, reply with EXACTLY ONE JSON object and nothing else, wrapped in a',
245
+ '```json fenced code block:',
246
+ '',
247
+ '```json',
248
+ '{"tool": "<tool_name>", "args": { ... }}',
249
+ '```',
250
+ '',
251
+ 'Use only the tools listed below, with `args` matching the tool\'s JSON Schema. If you have',
252
+ 'no tool to call and only want to say something, reply with plain prose and no JSON block.',
253
+ '',
254
+ '## Available tools',
255
+ ];
256
+ for (const t of tools) {
257
+ lines.push('', `### ${t.name}`, t.description, `Parameters (JSON Schema): ${JSON.stringify(t.parameters)}`);
258
+ }
259
+ return lines.join('\n');
260
+ }
261
+ /**
262
+ * The prompt for a *resumed* turn (1.1): only the messages added since the last
263
+ * turn we sent, and only the ones the resumed session does not already hold. The
264
+ * subprocess already has every prior turn plus its own assistant replies, so we
265
+ * send just the new user nudges and tool results — that delta is what advances
266
+ * the conversation. Falls back to the full render (via the caller) when there is
267
+ * no session yet.
268
+ */
269
+ function renderDelta(messages, from) {
270
+ const idToName = new Map();
271
+ for (const m of messages) {
272
+ if (m.role === 'assistant')
273
+ for (const call of m.toolCalls ?? [])
274
+ idToName.set(call.id, call.name);
275
+ }
276
+ const parts = [];
277
+ for (const m of messages.slice(Math.max(0, from))) {
278
+ if (m.role === 'user') {
279
+ parts.push(`[user]\n${m.content}`);
280
+ }
281
+ else if (m.role === 'tool') {
282
+ const name = idToName.get(m.toolCallId) ?? m.toolCallId;
283
+ parts.push(`[result of ${name}]\n${m.content}`);
284
+ }
285
+ // assistant/system messages are already in the resumed session — skip them.
286
+ }
287
+ return parts.join('\n\n');
288
+ }
289
+ function renderConversation(messages) {
290
+ const idToName = new Map();
291
+ const parts = [];
292
+ for (const m of messages) {
293
+ if (m.role === 'system')
294
+ continue;
295
+ if (m.role === 'user') {
296
+ parts.push(`[user]\n${m.content}`);
297
+ }
298
+ else if (m.role === 'assistant') {
299
+ if (m.content)
300
+ parts.push(`[assistant]\n${m.content}`);
301
+ for (const call of m.toolCalls ?? []) {
302
+ idToName.set(call.id, call.name);
303
+ parts.push(`[assistant tool call]\n\`\`\`json\n${JSON.stringify({ tool: call.name, args: call.args })}\n\`\`\``);
304
+ }
305
+ }
306
+ else {
307
+ const name = idToName.get(m.toolCallId) ?? m.toolCallId;
308
+ parts.push(`[result of ${name}]\n${m.content}`);
309
+ }
310
+ }
311
+ return parts.join('\n\n');
312
+ }
313
+ /**
314
+ * Detect a malformed-but-intended tool call in a turn that dispatched none
315
+ * (#I10). The signature is machine-recognizable: the text contains
316
+ * `"tool":"<name>"` naming a tool in the current catalog, yet nothing parsed.
317
+ * That is the exact case where the tolerant extractor's silence misleads the
318
+ * model — the JSON was near-miss malformed (a brace short, or the outer object
319
+ * split so only an inner `{args}` with no `tool` key balanced), not the tool
320
+ * being broken. Returns a one-line steer to re-emit it, or undefined when the
321
+ * absence of a call is genuine (plain prose, no tool named).
322
+ */
323
+ function detectMalformedCall(text, catalog) {
324
+ const re = /"tool"\s*:\s*"([^"]+)"/g;
325
+ let m;
326
+ while ((m = re.exec(text)) !== null) {
327
+ const name = m[1];
328
+ if (catalog.has(name)) {
329
+ return (`A tool call for "${name}" looks malformed — it named the tool but did not parse as ` +
330
+ 'valid JSON (likely unbalanced braces or a missing closing brace), so no call ran. ' +
331
+ 'Re-emit it as exactly one complete JSON object: {"tool": "...", "args": { ... }}.');
332
+ }
333
+ }
334
+ return undefined;
335
+ }
336
+ /**
337
+ * Extract tool-call JSON from the model's reply. Tolerant by design (D1):
338
+ * unparseable output is returned as plain text with no tool calls rather than
339
+ * throwing, so a non-conforming turn degrades to the loop's stall/nudge path.
340
+ * A parsed block only counts as a tool call when its name is in the current
341
+ * turn's catalog (`availableTools(ctx)`): a hallucinated or locked tool name is
342
+ * left as prose so the loop nudges, rather than dispatching a bogus call.
343
+ */
344
+ function parseToolCalls(text, nextId, catalog) {
345
+ if (!text)
346
+ return { text: null, toolCalls: [] };
347
+ const toolCalls = [];
348
+ const matched = [];
349
+ // Extract tool calls by scanning for complete JSON objects, NOT by matching
350
+ // ``` fences. A tool call's `content`/`args` can hold a full markdown doc that
351
+ // itself contains ``` code fences; a fence regex truncates the JSON at the
352
+ // first inner fence, JSON.parse fails, and the call is silently dropped (the
353
+ // model then assumes it wrote a file it never did). The brace scan is
354
+ // string-aware, so braces and backticks inside JSON string values are ignored.
355
+ let searchFrom = 0;
356
+ while (searchFrom < text.length) {
357
+ const braceAt = text.indexOf('{', searchFrom);
358
+ if (braceAt < 0)
359
+ break;
360
+ const span = scanJsonObject(text, braceAt);
361
+ if (!span) {
362
+ // Unbalanced '{' (stray brace in prose): retry from the next candidate so
363
+ // one bad brace can't hide a well-formed call later in the reply.
364
+ searchFrom = braceAt + 1;
365
+ continue;
366
+ }
367
+ const call = toToolCall(text.slice(span.start, span.end), nextId, catalog);
368
+ if (call) {
369
+ toolCalls.push(call);
370
+ matched.push([span.start, span.end]);
371
+ }
372
+ searchFrom = span.end;
373
+ }
374
+ if (!toolCalls.length) {
375
+ // No call dispatched — but did the model clearly *intend* one? A fenced
376
+ // ```json block that names a catalog tool yet produced zero calls is a
377
+ // malformed near-miss (unbalanced braces, a missing `}`, or an inner object
378
+ // with no `tool` key). Silently dropping it gives the model no signal, so it
379
+ // misreads "no result" as "this tool is broken" and can bake that false
380
+ // conclusion into a committed summary (#I10). Surface a nudge instead.
381
+ return { text: text.trim() ? text : null, toolCalls, nudge: detectMalformedCall(text, catalog) };
382
+ }
383
+ // Prose is whatever survives once the tool-call objects (and any now-empty
384
+ // ```json fences around them) are removed.
385
+ let prose = '';
386
+ let cursor = 0;
387
+ for (const [start, end] of matched) {
388
+ prose += text.slice(cursor, start);
389
+ cursor = end;
390
+ }
391
+ prose += text.slice(cursor);
392
+ prose = prose.replace(/```(?:json)?\s*```/gi, '').replace(/```(?:json)?\s*$/gi, '').trim();
393
+ return { text: prose.length ? prose : null, toolCalls };
394
+ }
395
+ /**
396
+ * Find the first complete, brace-balanced JSON object at or after `from`,
397
+ * respecting JSON string quoting/escaping so braces or backticks inside string
398
+ * values do not end the scan. Returns its `[start, end)` bounds or null.
399
+ */
400
+ function scanJsonObject(text, from) {
401
+ const start = text.indexOf('{', from);
402
+ if (start < 0)
403
+ return null;
404
+ let depth = 0;
405
+ let inStr = false;
406
+ let esc = false;
407
+ for (let i = start; i < text.length; i++) {
408
+ const ch = text[i];
409
+ if (inStr) {
410
+ if (esc)
411
+ esc = false;
412
+ else if (ch === '\\')
413
+ esc = true;
414
+ else if (ch === '"')
415
+ inStr = false;
416
+ continue;
417
+ }
418
+ if (ch === '"')
419
+ inStr = true;
420
+ else if (ch === '{')
421
+ depth++;
422
+ else if (ch === '}' && --depth === 0)
423
+ return { start, end: i + 1 };
424
+ }
425
+ return null;
426
+ }
427
+ function toToolCall(raw, nextId, catalog) {
428
+ if (!raw)
429
+ return null;
430
+ let obj;
431
+ try {
432
+ obj = JSON.parse(raw.trim());
433
+ }
434
+ catch {
435
+ return null;
436
+ }
437
+ if (!obj || typeof obj !== 'object')
438
+ return null;
439
+ const rec = obj;
440
+ if (typeof rec.tool !== 'string')
441
+ return null;
442
+ // Only accept names the turn actually advertised. An empty catalog means the
443
+ // turn offered no tools, so nothing parses as a call.
444
+ if (!catalog.has(rec.tool))
445
+ return null;
446
+ const args = rec.args && typeof rec.args === 'object' ? rec.args : {};
447
+ return { id: nextId(), name: rec.tool, args };
448
+ }
449
+ function isAuthError(err) {
450
+ const status = err?.status
451
+ ?? err?.statusCode;
452
+ // A present status is authoritative: only 401/403 are auth failures, so a 429
453
+ // (or anything else) is NOT treated as auth and is re-thrown untouched — its
454
+ // status must survive for withRetry/isRateLimit even if the message mentions
455
+ // "oauth token". Only when there is no status do we fall back to a narrow
456
+ // message heuristic.
457
+ if (typeof status === 'number')
458
+ return status === 401 || status === 403;
459
+ const m = (err?.message ?? '').toLowerCase();
460
+ return /unauthenticat|unauthoriz|not logged in|please log in|invalid api key|oauth token|setup-token/.test(m);
461
+ }
462
+ function authHint(detail) {
463
+ return ('claude-code is not authenticated: log in to Claude Code, or run `claude setup-token` and ' +
464
+ `set CLAUDE_CODE_OAUTH_TOKEN. copperhead never reads your credential itself (original error: ${detail})`);
465
+ }
466
+ //# sourceMappingURL=claude-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../../src/agent/providers/claude-code.ts"],"names":[],"mappings":"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"}
@@ -26,11 +26,7 @@ export class OpenAIProvider {
26
26
  content: m.content,
27
27
  ...(m.toolCalls?.length
28
28
  ? {
29
- tool_calls: m.toolCalls.map((t) => ({
30
- id: t.id,
31
- type: 'function',
32
- function: { name: t.name, arguments: JSON.stringify(t.args) },
33
- })),
29
+ tool_calls: m.toolCalls.map(serializeToolCall),
34
30
  }
35
31
  : {}),
36
32
  };
@@ -48,11 +44,10 @@ export class OpenAIProvider {
48
44
  : {}),
49
45
  });
50
46
  const choice = res.choices[0];
51
- const toolCalls = (choice?.message.tool_calls ?? []).map((t) => ({
52
- id: t.id,
53
- name: t.function.name,
54
- args: safeParse(t.function.arguments),
55
- }));
47
+ // Capture any non-standard properties returned by the API (e.g. Gemini thought
48
+ // signatures) so they can be echoed back on subsequent turns. Dropping them
49
+ // causes reasoning-model backends to reject the follow-up request with 400.
50
+ const toolCalls = (choice?.message.tool_calls ?? []).map(parseToolCall);
56
51
  return {
57
52
  text: choice?.message.content ?? null,
58
53
  toolCalls,
@@ -71,4 +66,29 @@ function safeParse(s) {
71
66
  return { _raw: s };
72
67
  }
73
68
  }
69
+ export function serializeToolCall(t) {
70
+ return {
71
+ id: t.id,
72
+ type: 'function',
73
+ function: { name: t.name, arguments: JSON.stringify(t.args) },
74
+ // Preserve vendor-specific tool-call fields (e.g. Gemini thought signatures).
75
+ // Dropping them makes the next turn's request 400.
76
+ ...(t.extra || {}),
77
+ };
78
+ }
79
+ export function parseToolCall(t) {
80
+ const extra = {};
81
+ for (const [k, v] of Object.entries(t)) {
82
+ if (k !== 'id' && k !== 'type' && k !== 'function') {
83
+ extra[k] = v;
84
+ }
85
+ }
86
+ const fn = t.function;
87
+ return {
88
+ id: t.id,
89
+ name: fn.name,
90
+ args: safeParse(fn.arguments),
91
+ ...(Object.keys(extra).length ? { extra } : {}),
92
+ };
93
+ }
74
94
  //# sourceMappingURL=openai.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../src/agent/providers/openai.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../src/agent/providers/openai.ts"],"names":[],"mappings":"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"}