@nyte-ai/plugin 0.0.21 → 0.0.24
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/THIRD-PARTY-NOTICES.md +2 -38
- package/dist/chunks/{billing-2v3g7rd9.js → bash-description-2v3g7rd9.js} +1 -1
- package/dist/chunks/{billing-3j77ss7y.js → bash-description-3j77ss7y.js} +1 -1
- package/dist/chunks/{billing-bv5zr32b.js → bash-description-dnvgk30y.js} +2 -2
- package/dist/chunks/{billing-hg64tqf1.js → bash-description-hg64tqf1.js} +1 -1
- package/dist/chunks/{billing-wqjabjnc.js → bash-description-necnb2k7.js} +18 -23
- package/dist/chunks/{billing-mvc33pv6.js → bash-description-qnwggxq3.js} +1 -1
- package/dist/chunks/{billing-svrhh5a5.js → bash-description-svrhh5a5.js} +1 -1
- package/dist/chunks/{billing-wbe8w0te.js → bash-description-wbe8w0te.js} +1 -1
- package/dist/chunks/{billing-wmcbg6fw.js → bash-description-wmcbg6fw.js} +1 -1
- package/dist/chunks/{billing-sxc70w72.js → bash-description-yyd9jznf.js} +1 -1
- package/dist/chunks/{billing-kn3d30b4.js → bash-description-zz15raxk.js} +3 -3
- package/dist/src/acp/connection.js +3 -3
- package/dist/src/acp/index.js +6 -6
- package/dist/src/acp/registry.js +2 -2
- package/dist/src/acp/thread.d.ts +1 -1
- package/dist/src/acp/thread.js +2 -2
- package/dist/src/acp/transcript.d.ts +1 -1
- package/dist/src/acp/transcript.js +2 -2
- package/dist/src/acp/translate.d.ts +1 -1
- package/dist/src/acp/translate.js +1 -1
- package/dist/src/approvals/approvals.js +1 -1
- package/dist/src/approvals/schema.js +1 -1
- package/dist/src/canvas/canvas-compile.js +3 -3
- package/dist/src/canvas/canvas-libraries.js +1 -1
- package/dist/src/canvas/canvas-preview.js +3 -3
- package/dist/src/canvas/canvas-protocol.d.ts +1 -1
- package/dist/src/canvas/canvas-protocol.js +2 -2
- package/dist/src/canvas/canvas.js +5 -5
- package/dist/src/canvas/schema.js +3 -3
- package/dist/src/claude-code-sdk/index.js +6 -6
- package/dist/src/claude-code-sdk/session.js +2 -2
- package/dist/src/claude-code-sdk/transcript.d.ts +3 -1
- package/dist/src/claude-code-sdk/transcript.js +2 -2
- package/dist/src/claude-code-sdk/translate.js +1 -1
- package/dist/src/find-on-path.js +1 -1
- package/dist/src/question/client/questions-tui.js +1 -1
- package/dist/src/question/client/questions.js +1 -1
- package/dist/src/question/question.js +2 -2
- package/dist/src/question/schema.js +1 -1
- package/package.json +19 -23
- package/dist/chunks/billing-0y7dfkp2.js +0 -146
- package/dist/chunks/billing-3scqmkzb.js +0 -559
- package/dist/chunks/billing-8rjk35gr.js +0 -444
- package/dist/chunks/billing-9qc0eqh4.js +0 -174
- package/dist/chunks/billing-a9vqn66p.js +0 -351
- package/dist/chunks/billing-cy1rg619.js +0 -302
- package/dist/chunks/billing-ejx2hv52.js +0 -323
- package/dist/chunks/billing-j643d12p.js +0 -197
- package/dist/chunks/billing-s4rrspsk.js +0 -7
- package/dist/chunks/billing-t1qrxe16.js +0 -41
- package/dist/chunks/billing-tzr3cebf.js +0 -20
- package/dist/examples/claude-code/LICENSE +0 -21
- package/dist/examples/claude-code/billing.d.ts +0 -7
- package/dist/examples/claude-code/billing.js +0 -11
- package/dist/examples/claude-code/claude-cli.d.ts +0 -35
- package/dist/examples/claude-code/claude-cli.js +0 -15
- package/dist/examples/claude-code/env.d.ts +0 -1
- package/dist/examples/claude-code/env.js +0 -6
- package/dist/examples/claude-code/index.d.ts +0 -24
- package/dist/examples/claude-code/index.js +0 -149
- package/dist/examples/claude-code/inert-mcp.d.ts +0 -1
- package/dist/examples/claude-code/inert-mcp.js +0 -6
- package/dist/examples/claude-code/model-catalog.d.ts +0 -27
- package/dist/examples/claude-code/model-catalog.js +0 -12
- package/dist/examples/claude-code/process.d.ts +0 -22
- package/dist/examples/claude-code/process.js +0 -25
- package/dist/examples/claude-code/replay.d.ts +0 -4
- package/dist/examples/claude-code/replay.js +0 -11
- package/dist/examples/claude-code/stream.d.ts +0 -3
- package/dist/examples/claude-code/stream.js +0 -16
- package/dist/examples/claude-code/transcript.d.ts +0 -83
- package/dist/examples/claude-code/transcript.js +0 -23
- package/dist/examples/claude-code/turn-relay.d.ts +0 -48
- package/dist/examples/claude-code/turn-relay.js +0 -9
- package/dist/examples/claude-code/wire.d.ts +0 -372
- package/dist/examples/claude-code/wire.js +0 -18
- /package/dist/chunks/{billing-4vz9pxyx.js → bash-description-4vz9pxyx.js} +0 -0
- /package/dist/chunks/{billing-6z2kgtyt.js → bash-description-6z2kgtyt.js} +0 -0
- /package/dist/chunks/{billing-9nn3yp18.js → bash-description-9nn3yp18.js} +0 -0
- /package/dist/chunks/{billing-h13r0byk.js → bash-description-h13r0byk.js} +0 -0
- /package/dist/chunks/{billing-ye3wk5r3.js → bash-description-ye3wk5r3.js} +0 -0
- /package/dist/chunks/{billing-zfpm2rt0.js → bash-description-zfpm2rt0.js} +0 -0
|
@@ -1,559 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
usageCreditsAllowed2,
|
|
3
|
-
usageCreditsChargeError2
|
|
4
|
-
} from "./billing-tzr3cebf.js";
|
|
5
|
-
import {
|
|
6
|
-
INERT_MCP_SOURCE2
|
|
7
|
-
} from "./billing-t1qrxe16.js";
|
|
8
|
-
import {
|
|
9
|
-
matchesRecord2,
|
|
10
|
-
parseOutputEvent2
|
|
11
|
-
} from "./billing-0y7dfkp2.js";
|
|
12
|
-
import {
|
|
13
|
-
configuredCommand2,
|
|
14
|
-
readyClaudeCommand2,
|
|
15
|
-
spawnCommand2,
|
|
16
|
-
childExit2,
|
|
17
|
-
killProcessTree2,
|
|
18
|
-
processEnvironment2
|
|
19
|
-
} from "./billing-cy1rg619.js";
|
|
20
|
-
import {
|
|
21
|
-
deriveInitialUserPrefix2,
|
|
22
|
-
deriveInitialSystemMessages2,
|
|
23
|
-
rewriteInitialRequest2
|
|
24
|
-
} from "./billing-j643d12p.js";
|
|
25
|
-
import {
|
|
26
|
-
TurnRelay2
|
|
27
|
-
} from "./billing-a9vqn66p.js";
|
|
28
|
-
import {
|
|
29
|
-
TOOL_PREFIX2
|
|
30
|
-
} from "./billing-ejx2hv52.js";
|
|
31
|
-
|
|
32
|
-
// examples/claude-code/claude-cli.ts
|
|
33
|
-
import { createHash } from "node:crypto";
|
|
34
|
-
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
35
|
-
import { tmpdir } from "node:os";
|
|
36
|
-
import { join, resolve as resolvePath } from "node:path";
|
|
37
|
-
import { createInterface } from "node:readline";
|
|
38
|
-
import { registerSessionResourceCleanup } from "@nyte-ai/ai";
|
|
39
|
-
class AsyncQueue {
|
|
40
|
-
values = [];
|
|
41
|
-
waiters = [];
|
|
42
|
-
ended = false;
|
|
43
|
-
failure;
|
|
44
|
-
push(value) {
|
|
45
|
-
const waiter = this.waiters.shift();
|
|
46
|
-
if (waiter)
|
|
47
|
-
waiter({ value, done: false });
|
|
48
|
-
else
|
|
49
|
-
this.values.push(value);
|
|
50
|
-
}
|
|
51
|
-
end(error) {
|
|
52
|
-
this.ended = true;
|
|
53
|
-
this.failure = error;
|
|
54
|
-
for (const waiter of this.waiters.splice(0))
|
|
55
|
-
waiter({ value: undefined, done: true });
|
|
56
|
-
}
|
|
57
|
-
async next(signal, timeoutMs) {
|
|
58
|
-
if (this.values.length)
|
|
59
|
-
return this.values.shift();
|
|
60
|
-
if (this.ended) {
|
|
61
|
-
if (this.failure)
|
|
62
|
-
throw this.failure;
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
signal?.throwIfAborted();
|
|
66
|
-
return new Promise((resolve, reject) => {
|
|
67
|
-
let settled = false;
|
|
68
|
-
const finish = (fn) => {
|
|
69
|
-
if (settled)
|
|
70
|
-
return;
|
|
71
|
-
settled = true;
|
|
72
|
-
clearTimeout(timer);
|
|
73
|
-
signal?.removeEventListener("abort", onAbort);
|
|
74
|
-
const index = this.waiters.findIndex((pending) => pending === waiter);
|
|
75
|
-
if (index >= 0)
|
|
76
|
-
this.waiters.splice(index, 1);
|
|
77
|
-
fn();
|
|
78
|
-
};
|
|
79
|
-
const waiter = (item) => finish(() => {
|
|
80
|
-
if (item.done && this.failure)
|
|
81
|
-
reject(this.failure);
|
|
82
|
-
else
|
|
83
|
-
resolve(item.done ? undefined : item.value);
|
|
84
|
-
});
|
|
85
|
-
const onAbort = () => finish(() => reject(signal?.reason ?? new Error("Claude Code request aborted")));
|
|
86
|
-
const timer = setTimeout(() => finish(() => reject(new Error("Claude Code stream timed out"))), timeoutMs);
|
|
87
|
-
this.waiters.push(waiter);
|
|
88
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
async function writeFrame(child, frame) {
|
|
93
|
-
const input = child.stdin;
|
|
94
|
-
if (!input?.writable)
|
|
95
|
-
throw new Error("Claude Code stdin closed during history replay");
|
|
96
|
-
if (!input.write(`${JSON.stringify(frame)}
|
|
97
|
-
`)) {
|
|
98
|
-
await new Promise((resolve, reject) => {
|
|
99
|
-
input.once("drain", resolve);
|
|
100
|
-
input.once("error", reject);
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
function nativeErrorText(event) {
|
|
105
|
-
const messageError = event.message.error;
|
|
106
|
-
if (!event.error && !messageError)
|
|
107
|
-
return;
|
|
108
|
-
return event.message.content.filter((block) => block.type === "text").map((block) => block.text).join(`
|
|
109
|
-
`);
|
|
110
|
-
}
|
|
111
|
-
var CLAUDE_PROCESS_MAX_AGE_MS = 5 * 60 * 1000;
|
|
112
|
-
var MAX_CACHED_CLAUDE_PROCESSES = 2;
|
|
113
|
-
function canonicalJson(value) {
|
|
114
|
-
if (value === undefined)
|
|
115
|
-
return "null";
|
|
116
|
-
if (value === null || typeof value !== "object")
|
|
117
|
-
return JSON.stringify(value);
|
|
118
|
-
if (Array.isArray(value))
|
|
119
|
-
return `[${value.map(canonicalJson).join(",")}]`;
|
|
120
|
-
if (!matchesRecord2(value))
|
|
121
|
-
throw new Error("Expected a JSON value");
|
|
122
|
-
return `{${Object.entries(value).filter(([, child]) => child !== undefined).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, child]) => `${JSON.stringify(key)}:${canonicalJson(child)}`).join(",")}}`;
|
|
123
|
-
}
|
|
124
|
-
function digest(value) {
|
|
125
|
-
return createHash("sha256").update(canonicalJson(value)).digest("hex");
|
|
126
|
-
}
|
|
127
|
-
function frameHistoryHash(frames) {
|
|
128
|
-
return digest(frames.map((frame) => ({
|
|
129
|
-
type: frame.type,
|
|
130
|
-
message: frame.message
|
|
131
|
-
})));
|
|
132
|
-
}
|
|
133
|
-
function launchPlan(input) {
|
|
134
|
-
const timeoutMs = input.timeoutMs ?? 180000;
|
|
135
|
-
const env = processEnvironment2(input.env, input.extraBody);
|
|
136
|
-
return {
|
|
137
|
-
env,
|
|
138
|
-
timeoutMs,
|
|
139
|
-
fingerprint: digest({
|
|
140
|
-
command: configuredCommand2(env),
|
|
141
|
-
cwd: resolvePath(input.cwd ?? process.cwd()),
|
|
142
|
-
env,
|
|
143
|
-
extraBody: input.extraBody,
|
|
144
|
-
manifest: input.manifest,
|
|
145
|
-
model: input.model,
|
|
146
|
-
systemPrompt: input.systemPrompt,
|
|
147
|
-
timeoutMs,
|
|
148
|
-
upstreamUrl: input.upstreamUrl ?? "https://api.anthropic.com"
|
|
149
|
-
})
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
class ClaudeProcess {
|
|
154
|
-
busy = false;
|
|
155
|
-
createdAt = Date.now();
|
|
156
|
-
lastUsed = this.createdAt;
|
|
157
|
-
tipHash;
|
|
158
|
-
expiryTimer;
|
|
159
|
-
closed = false;
|
|
160
|
-
stderr = "";
|
|
161
|
-
fingerprint;
|
|
162
|
-
root;
|
|
163
|
-
child;
|
|
164
|
-
relay;
|
|
165
|
-
queue;
|
|
166
|
-
readTask;
|
|
167
|
-
exitOutcome;
|
|
168
|
-
timeoutMs;
|
|
169
|
-
constructor(fingerprint, root, child, relay, queue, readTask, exitOutcome, timeoutMs) {
|
|
170
|
-
this.fingerprint = fingerprint;
|
|
171
|
-
this.root = root;
|
|
172
|
-
this.child = child;
|
|
173
|
-
this.relay = relay;
|
|
174
|
-
this.queue = queue;
|
|
175
|
-
this.readTask = readTask;
|
|
176
|
-
this.exitOutcome = exitOutcome;
|
|
177
|
-
this.timeoutMs = timeoutMs;
|
|
178
|
-
child.stderr?.setEncoding("utf8");
|
|
179
|
-
child.stderr?.on("data", (chunk) => this.stderr = (this.stderr + chunk).slice(-65536));
|
|
180
|
-
}
|
|
181
|
-
static async create(input, plan) {
|
|
182
|
-
input.signal?.throwIfAborted();
|
|
183
|
-
const command = await readyClaudeCommand2({ ...process.env, ...input.env }, input.signal);
|
|
184
|
-
const root = await mkdtemp(join(tmpdir(), "nyte-claude-code-"));
|
|
185
|
-
const toolsPath = join(root, "tools.json");
|
|
186
|
-
const settingsPath = join(root, "settings.json");
|
|
187
|
-
const systemPath = join(root, "system.md");
|
|
188
|
-
const inertMcpPath = join(root, "inert-mcp.mjs");
|
|
189
|
-
let child;
|
|
190
|
-
let relay;
|
|
191
|
-
try {
|
|
192
|
-
await Promise.all([
|
|
193
|
-
writeFile(inertMcpPath, INERT_MCP_SOURCE2, "utf8"),
|
|
194
|
-
writeFile(toolsPath, JSON.stringify(input.manifest), "utf8"),
|
|
195
|
-
writeFile(settingsPath, JSON.stringify({ env: { CLAUDE_CODE_EXTRA_BODY: JSON.stringify(input.extraBody) } }), "utf8"),
|
|
196
|
-
writeFile(systemPath, input.systemPrompt, "utf8")
|
|
197
|
-
]);
|
|
198
|
-
relay = new TurnRelay2(input.upstreamUrl ?? "https://api.anthropic.com", plan.timeoutMs);
|
|
199
|
-
const env = { ...plan.env, ANTHROPIC_BASE_URL: await relay.start() };
|
|
200
|
-
const mcp = {
|
|
201
|
-
mcpServers: {
|
|
202
|
-
nyte: {
|
|
203
|
-
command: process.versions.bun === undefined ? process.execPath : "node",
|
|
204
|
-
args: [inertMcpPath, toolsPath],
|
|
205
|
-
env: { ELECTRON_RUN_AS_NODE: "1" }
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
const args = [
|
|
210
|
-
"-p",
|
|
211
|
-
"--model",
|
|
212
|
-
input.model,
|
|
213
|
-
"--input-format",
|
|
214
|
-
"stream-json",
|
|
215
|
-
"--output-format",
|
|
216
|
-
"stream-json",
|
|
217
|
-
"--verbose",
|
|
218
|
-
"--include-partial-messages",
|
|
219
|
-
"--tools",
|
|
220
|
-
"",
|
|
221
|
-
"--system-prompt-file",
|
|
222
|
-
systemPath,
|
|
223
|
-
"--settings",
|
|
224
|
-
settingsPath,
|
|
225
|
-
"--setting-sources",
|
|
226
|
-
"",
|
|
227
|
-
"--strict-mcp-config",
|
|
228
|
-
"--disable-slash-commands",
|
|
229
|
-
"--max-turns",
|
|
230
|
-
"1",
|
|
231
|
-
"--permission-mode",
|
|
232
|
-
"dontAsk",
|
|
233
|
-
"--no-session-persistence",
|
|
234
|
-
"--mcp-config",
|
|
235
|
-
JSON.stringify(mcp)
|
|
236
|
-
];
|
|
237
|
-
child = spawnCommand2(command, args, {
|
|
238
|
-
cwd: input.cwd ?? process.cwd(),
|
|
239
|
-
env,
|
|
240
|
-
detached: process.platform !== "win32",
|
|
241
|
-
windowsHide: true,
|
|
242
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
243
|
-
});
|
|
244
|
-
const queue = new AsyncQueue;
|
|
245
|
-
if (child.stdout === null)
|
|
246
|
-
throw new Error("Claude Code stdout is unavailable");
|
|
247
|
-
const reader = createInterface({ input: child.stdout, crlfDelay: Infinity });
|
|
248
|
-
const readTask = (async () => {
|
|
249
|
-
try {
|
|
250
|
-
for await (const line of reader) {
|
|
251
|
-
if (!line.trim())
|
|
252
|
-
continue;
|
|
253
|
-
try {
|
|
254
|
-
const event = parseOutputEvent2(line);
|
|
255
|
-
if (event !== undefined)
|
|
256
|
-
queue.push(event);
|
|
257
|
-
} catch (error) {
|
|
258
|
-
throw new Error(`Invalid Claude Code stream-json output: ${line.slice(0, 300)}`, {
|
|
259
|
-
cause: error
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
queue.end();
|
|
264
|
-
} catch (error) {
|
|
265
|
-
queue.end(error instanceof Error ? error : new Error(String(error)));
|
|
266
|
-
}
|
|
267
|
-
})();
|
|
268
|
-
const exitOutcome = childExit2(child).then((code) => ({ code }), (error) => ({ error: error instanceof Error ? error : new Error(String(error)) }));
|
|
269
|
-
return new ClaudeProcess(plan.fingerprint, root, child, relay, queue, readTask, exitOutcome, plan.timeoutMs);
|
|
270
|
-
} catch (error) {
|
|
271
|
-
relay?.abort();
|
|
272
|
-
if (child && child.exitCode === null)
|
|
273
|
-
killProcessTree2(child);
|
|
274
|
-
await relay?.close();
|
|
275
|
-
await rm(root, { recursive: true, force: true });
|
|
276
|
-
throw error;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
isAlive() {
|
|
280
|
-
return !this.closed && this.child.exitCode === null && !!this.child.stdin?.writable;
|
|
281
|
-
}
|
|
282
|
-
isExpired(now = Date.now()) {
|
|
283
|
-
return now - this.createdAt >= CLAUDE_PROCESS_MAX_AGE_MS;
|
|
284
|
-
}
|
|
285
|
-
canContinue(input) {
|
|
286
|
-
if (!this.tipHash || input.frames.length === 0)
|
|
287
|
-
return false;
|
|
288
|
-
return frameHistoryHash(input.frames.slice(0, -1)) === this.tipHash;
|
|
289
|
-
}
|
|
290
|
-
clearExpiryTimer() {
|
|
291
|
-
if (this.expiryTimer)
|
|
292
|
-
clearTimeout(this.expiryTimer);
|
|
293
|
-
this.expiryTimer = undefined;
|
|
294
|
-
}
|
|
295
|
-
armExpiryTimer(expire) {
|
|
296
|
-
this.clearExpiryTimer();
|
|
297
|
-
this.lastUsed = Date.now();
|
|
298
|
-
const remainingLifetime = CLAUDE_PROCESS_MAX_AGE_MS - (this.lastUsed - this.createdAt);
|
|
299
|
-
this.expiryTimer = setTimeout(expire, Math.max(0, remainingLifetime));
|
|
300
|
-
this.expiryTimer.unref?.();
|
|
301
|
-
}
|
|
302
|
-
commit(input, message) {
|
|
303
|
-
if (message.content.some((block) => block.type === "tool_use")) {
|
|
304
|
-
this.tipHash = undefined;
|
|
305
|
-
return false;
|
|
306
|
-
}
|
|
307
|
-
this.tipHash = frameHistoryHash([
|
|
308
|
-
...input.frames,
|
|
309
|
-
{ type: "assistant", message: { role: "assistant", content: message.content } }
|
|
310
|
-
]);
|
|
311
|
-
return true;
|
|
312
|
-
}
|
|
313
|
-
async run(input, continuation) {
|
|
314
|
-
if (!this.isAlive())
|
|
315
|
-
throw new Error("Claude Code process is no longer available");
|
|
316
|
-
input.signal?.throwIfAborted();
|
|
317
|
-
let sawMessageStop = false;
|
|
318
|
-
const deliver = (event) => {
|
|
319
|
-
if (event.type === "message_stop")
|
|
320
|
-
sawMessageStop = true;
|
|
321
|
-
input.onEvent?.(event);
|
|
322
|
-
};
|
|
323
|
-
const canonicalInitialUser = input.frames.find((frame) => frame.type === "user")?.message.content ?? [];
|
|
324
|
-
this.relay.beginTurn(deliver, input.initialUserPrefix || input.initialSystemMessages || input.nativeSystemInjections ? (body) => rewriteInitialRequest2(body, input.initialUserPrefix, canonicalInitialUser, input.initialSystemMessages, input.nativeSystemInjections) : undefined, input.betas);
|
|
325
|
-
const abort = () => {
|
|
326
|
-
this.relay.abort();
|
|
327
|
-
this.closeNow();
|
|
328
|
-
};
|
|
329
|
-
input.signal?.addEventListener("abort", abort, { once: true });
|
|
330
|
-
const assistants = [];
|
|
331
|
-
let result;
|
|
332
|
-
let nativeError;
|
|
333
|
-
let rateLimit;
|
|
334
|
-
try {
|
|
335
|
-
const frames = continuation ? input.frames.slice(-1) : input.frames;
|
|
336
|
-
for (const frame of frames) {
|
|
337
|
-
await writeFrame(this.child, frame);
|
|
338
|
-
if (frame.shouldQuery === false) {
|
|
339
|
-
for (;; ) {
|
|
340
|
-
const event = await this.queue.next(input.signal, this.timeoutMs);
|
|
341
|
-
if (!event)
|
|
342
|
-
throw await this.exitError("Claude Code exited before acknowledging history replay");
|
|
343
|
-
if (event.type === "result") {
|
|
344
|
-
if (event.num_turns !== 0 || event.is_error) {
|
|
345
|
-
throw new Error("Claude Code rejected zero-turn history replay");
|
|
346
|
-
}
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
for (;; ) {
|
|
353
|
-
const event = await this.queue.next(input.signal, this.timeoutMs);
|
|
354
|
-
if (!event)
|
|
355
|
-
throw await this.exitError("Claude Code exited before completing its response");
|
|
356
|
-
if (event.type === "assistant") {
|
|
357
|
-
const error = nativeErrorText(event);
|
|
358
|
-
if (error !== undefined)
|
|
359
|
-
nativeError = error;
|
|
360
|
-
else
|
|
361
|
-
assistants.push(event.message);
|
|
362
|
-
} else if (event.type === "result") {
|
|
363
|
-
result = event;
|
|
364
|
-
break;
|
|
365
|
-
} else if (event.type === "rate_limit_event") {
|
|
366
|
-
if (event.rate_limit_info && typeof event.rate_limit_info === "object")
|
|
367
|
-
rateLimit = event.rate_limit_info;
|
|
368
|
-
} else if (event.type === "stream_event" && !this.relay.used) {
|
|
369
|
-
deliver(event.event);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
await this.relay.settled();
|
|
373
|
-
input.signal?.throwIfAborted();
|
|
374
|
-
let message;
|
|
375
|
-
if (this.relay.used) {
|
|
376
|
-
if (this.relay.status !== 200 || !this.relay.capture.complete || !this.relay.capture.message) {
|
|
377
|
-
let detail = `status ${this.relay.status ?? "unknown"}, capture ${this.relay.capture.complete ? "complete" : "incomplete"}`;
|
|
378
|
-
if (this.relay.failure)
|
|
379
|
-
detail += `, relay failure ${this.relay.failure}`;
|
|
380
|
-
if (this.relay.denied)
|
|
381
|
-
detail += `, native retries denied ${this.relay.denied}`;
|
|
382
|
-
const upstreamError = this.relay.errorText();
|
|
383
|
-
if (upstreamError)
|
|
384
|
-
detail += `, upstream said: ${upstreamError.slice(0, 500)}`;
|
|
385
|
-
throw new Error(`Incomplete Claude Code upstream response (${detail})`);
|
|
386
|
-
}
|
|
387
|
-
message = this.relay.capture.message;
|
|
388
|
-
sawMessageStop = true;
|
|
389
|
-
} else {
|
|
390
|
-
message = assistants.at(-1);
|
|
391
|
-
}
|
|
392
|
-
if (!message || !sawMessageStop || !result) {
|
|
393
|
-
throw new Error(`Incomplete Claude Code response: assistant, message_stop and one result required${this.stderr ? ` (${this.stderr.trim().slice(-500)})` : ""}`);
|
|
394
|
-
}
|
|
395
|
-
const nativeFailureHandled = this.relay.denied > 0 || message.stop_reason === "refusal";
|
|
396
|
-
if (nativeError && !nativeFailureHandled)
|
|
397
|
-
throw new Error(`Claude Code API error: ${nativeError}`);
|
|
398
|
-
const hasTools = message.content.some((block) => block.type === "tool_use");
|
|
399
|
-
const maxTurnsBoundary = hasTools && result.subtype === "error_max_turns";
|
|
400
|
-
if (!maxTurnsBoundary && !nativeFailureHandled && (result.is_error || result.subtype !== "success")) {
|
|
401
|
-
throw new Error(`Claude Code request failed: ${result.subtype ?? "unknown result"}`);
|
|
402
|
-
}
|
|
403
|
-
for (const block of message.content) {
|
|
404
|
-
if (block.type !== "tool_use")
|
|
405
|
-
continue;
|
|
406
|
-
if (!block.name.startsWith(TOOL_PREFIX2) || !input.toolNames.has(block.name.slice(TOOL_PREFIX2.length))) {
|
|
407
|
-
throw new Error("Claude Code returned a tool outside Nyte's current tool inventory");
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
const usage = message.usage ?? result.usage;
|
|
411
|
-
if (!usage || typeof usage.input_tokens !== "number" || typeof usage.output_tokens !== "number") {
|
|
412
|
-
throw new Error("Claude Code response did not include complete token usage");
|
|
413
|
-
}
|
|
414
|
-
if (rateLimit?.isUsingOverage === true && !usageCreditsAllowed2({ ...process.env, ...input.env })) {
|
|
415
|
-
throw usageCreditsChargeError2(rateLimit);
|
|
416
|
-
}
|
|
417
|
-
return {
|
|
418
|
-
message,
|
|
419
|
-
usage,
|
|
420
|
-
relay: {
|
|
421
|
-
upstreamRequests: Number(this.relay.used),
|
|
422
|
-
blockedRequests: this.relay.denied,
|
|
423
|
-
requestId: this.relay.requestId
|
|
424
|
-
},
|
|
425
|
-
initialUserPrefix: input.initialUserPrefix ?? deriveInitialUserPrefix2(this.relay.originalRequestBody, canonicalInitialUser),
|
|
426
|
-
initialSystemMessages: input.initialSystemMessages ?? deriveInitialSystemMessages2(this.relay.originalRequestBody),
|
|
427
|
-
rateLimit
|
|
428
|
-
};
|
|
429
|
-
} finally {
|
|
430
|
-
input.signal?.removeEventListener("abort", abort);
|
|
431
|
-
this.relay.finishTurn();
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
async close() {
|
|
435
|
-
this.closeNow();
|
|
436
|
-
await Promise.allSettled([this.readTask, this.exitOutcome, this.relay.close()]);
|
|
437
|
-
await rm(this.root, { recursive: true, force: true });
|
|
438
|
-
}
|
|
439
|
-
closeNow() {
|
|
440
|
-
if (this.closed)
|
|
441
|
-
return;
|
|
442
|
-
this.closed = true;
|
|
443
|
-
this.clearExpiryTimer();
|
|
444
|
-
this.relay.abort();
|
|
445
|
-
if (this.child.exitCode === null)
|
|
446
|
-
killProcessTree2(this.child);
|
|
447
|
-
}
|
|
448
|
-
async exitError(fallback) {
|
|
449
|
-
const outcome = await this.exitOutcome;
|
|
450
|
-
if (outcome.error)
|
|
451
|
-
return outcome.error;
|
|
452
|
-
const detail = this.stderr.trim().slice(-500);
|
|
453
|
-
return new Error(`${fallback} (exit ${outcome.code ?? "unknown"}${detail ? `: ${detail}` : ""})`);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
var processCache = new Map;
|
|
457
|
-
var managerTail = Promise.resolve();
|
|
458
|
-
async function withManagerLock(operation) {
|
|
459
|
-
const previous = managerTail;
|
|
460
|
-
const pending = Promise.withResolvers();
|
|
461
|
-
managerTail = pending.promise;
|
|
462
|
-
await previous;
|
|
463
|
-
try {
|
|
464
|
-
return await operation();
|
|
465
|
-
} finally {
|
|
466
|
-
pending.resolve();
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
async function expireProcess(sessionId, worker) {
|
|
470
|
-
const expired = await withManagerLock(() => {
|
|
471
|
-
if (processCache.get(sessionId) !== worker || worker.busy || !worker.isExpired())
|
|
472
|
-
return false;
|
|
473
|
-
processCache.delete(sessionId);
|
|
474
|
-
return true;
|
|
475
|
-
});
|
|
476
|
-
if (expired)
|
|
477
|
-
await worker.close();
|
|
478
|
-
}
|
|
479
|
-
async function discardProcess(acquired) {
|
|
480
|
-
const { sessionId, worker } = acquired;
|
|
481
|
-
if (sessionId && processCache.get(sessionId) === worker) {
|
|
482
|
-
await withManagerLock(() => {
|
|
483
|
-
if (processCache.get(sessionId) === worker)
|
|
484
|
-
processCache.delete(sessionId);
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
await worker.close();
|
|
488
|
-
}
|
|
489
|
-
async function acquireProcess(input, plan) {
|
|
490
|
-
return withManagerLock(async () => {
|
|
491
|
-
const sessionId = input.sessionId;
|
|
492
|
-
const cached = sessionId ? processCache.get(sessionId) : undefined;
|
|
493
|
-
if (sessionId && cached && !cached.busy) {
|
|
494
|
-
cached.clearExpiryTimer();
|
|
495
|
-
if (cached.isAlive() && !cached.isExpired() && cached.fingerprint === plan.fingerprint && cached.canContinue(input)) {
|
|
496
|
-
cached.busy = true;
|
|
497
|
-
return { worker: cached, retained: true, continuation: true, sessionId };
|
|
498
|
-
}
|
|
499
|
-
processCache.delete(sessionId);
|
|
500
|
-
await cached.close();
|
|
501
|
-
}
|
|
502
|
-
const retain = !!sessionId && !cached?.busy;
|
|
503
|
-
if (retain) {
|
|
504
|
-
while (processCache.size >= MAX_CACHED_CLAUDE_PROCESSES) {
|
|
505
|
-
const victim = [...processCache.entries()].filter(([, worker]) => !worker.busy).toSorted((left, right) => left[1].lastUsed - right[1].lastUsed)[0];
|
|
506
|
-
if (!victim)
|
|
507
|
-
break;
|
|
508
|
-
processCache.delete(victim[0]);
|
|
509
|
-
await victim[1].close();
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
const worker = await ClaudeProcess.create(input, plan);
|
|
513
|
-
worker.busy = true;
|
|
514
|
-
const retained = retain && processCache.size < MAX_CACHED_CLAUDE_PROCESSES;
|
|
515
|
-
if (retained && sessionId)
|
|
516
|
-
processCache.set(sessionId, worker);
|
|
517
|
-
return { worker, retained, continuation: false, sessionId };
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
async function closeClaudeProcesses(sessionId) {
|
|
521
|
-
const workers = await withManagerLock(() => {
|
|
522
|
-
if (sessionId) {
|
|
523
|
-
const worker = processCache.get(sessionId);
|
|
524
|
-
processCache.delete(sessionId);
|
|
525
|
-
return worker ? [worker] : [];
|
|
526
|
-
}
|
|
527
|
-
const all = [...processCache.values()];
|
|
528
|
-
processCache.clear();
|
|
529
|
-
return all;
|
|
530
|
-
});
|
|
531
|
-
await Promise.allSettled(workers.map((worker) => worker.close()));
|
|
532
|
-
}
|
|
533
|
-
registerSessionResourceCleanup((sessionId) => {
|
|
534
|
-
closeClaudeProcesses(sessionId);
|
|
535
|
-
});
|
|
536
|
-
async function runClaudeRequest2(input) {
|
|
537
|
-
input.signal?.throwIfAborted();
|
|
538
|
-
const plan = launchPlan(input);
|
|
539
|
-
const acquired = await acquireProcess(input, plan);
|
|
540
|
-
try {
|
|
541
|
-
const output = await acquired.worker.run(input, acquired.continuation);
|
|
542
|
-
const stable = acquired.worker.commit(input, output.message);
|
|
543
|
-
if (stable && acquired.retained && acquired.sessionId && processCache.get(acquired.sessionId) === acquired.worker) {
|
|
544
|
-
acquired.worker.busy = false;
|
|
545
|
-
const sessionId = acquired.sessionId;
|
|
546
|
-
acquired.worker.armExpiryTimer(() => {
|
|
547
|
-
expireProcess(sessionId, acquired.worker);
|
|
548
|
-
});
|
|
549
|
-
} else {
|
|
550
|
-
await discardProcess(acquired);
|
|
551
|
-
}
|
|
552
|
-
return output;
|
|
553
|
-
} catch (error) {
|
|
554
|
-
await discardProcess(acquired);
|
|
555
|
-
throw error;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
export { runClaudeRequest2 };
|