@ash-cloud/ash-ai 0.1.19 → 0.1.20
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/index.cjs +276 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +529 -420
- package/dist/index.d.ts +529 -420
- package/dist/index.js +276 -35
- package/dist/index.js.map +1 -1
- package/dist/playground/components/NormalizedMessageList.d.ts.map +1 -1
- package/dist/playground/contexts/ThemeContext.d.ts +12 -8
- package/dist/playground/contexts/ThemeContext.d.ts.map +1 -1
- package/dist/playground/index.d.ts +1 -1
- package/dist/playground/index.d.ts.map +1 -1
- package/dist/playground.js +1004 -969
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -885,6 +885,32 @@ var init_mcp = __esm({
|
|
|
885
885
|
});
|
|
886
886
|
|
|
887
887
|
// src/agent/claude-sdk.ts
|
|
888
|
+
function isStandardMcpServerConfig(config) {
|
|
889
|
+
if (!config || typeof config !== "object") return false;
|
|
890
|
+
const candidate = config;
|
|
891
|
+
if (typeof candidate.command === "string") return true;
|
|
892
|
+
if (typeof candidate.url === "string") return true;
|
|
893
|
+
if (candidate.type === "stdio" || candidate.type === "http" || candidate.type === "sse") return true;
|
|
894
|
+
return false;
|
|
895
|
+
}
|
|
896
|
+
function hasCustomMcpServers(mcpServers) {
|
|
897
|
+
if (!mcpServers) return false;
|
|
898
|
+
return Object.values(mcpServers).some((config) => !isStandardMcpServerConfig(config));
|
|
899
|
+
}
|
|
900
|
+
async function* toStreamingPrompt(prompt) {
|
|
901
|
+
yield {
|
|
902
|
+
type: "user",
|
|
903
|
+
message: {
|
|
904
|
+
role: "user",
|
|
905
|
+
content: prompt
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
function normalizePromptForSdk(prompt, mcpServers) {
|
|
910
|
+
if (typeof prompt !== "string") return prompt;
|
|
911
|
+
if (!hasCustomMcpServers(mcpServers)) return prompt;
|
|
912
|
+
return toStreamingPrompt(prompt);
|
|
913
|
+
}
|
|
888
914
|
function convertClaudeMessage(claudeMessage, sessionId) {
|
|
889
915
|
if (claudeMessage.type !== "assistant" || !claudeMessage.message) {
|
|
890
916
|
return null;
|
|
@@ -928,16 +954,17 @@ function normalizeMcpServers(mcpServers) {
|
|
|
928
954
|
if (!mcpServers) return mcpServers;
|
|
929
955
|
return Object.fromEntries(
|
|
930
956
|
Object.entries(mcpServers).map(([name, config]) => {
|
|
931
|
-
|
|
957
|
+
const configWithAuth = config;
|
|
958
|
+
if (!configWithAuth.auth) {
|
|
932
959
|
return [name, config];
|
|
933
960
|
}
|
|
934
|
-
const authHeaders = mcpAuthToHeaders(
|
|
935
|
-
const { auth: _auth, ...rest } =
|
|
961
|
+
const authHeaders = mcpAuthToHeaders(configWithAuth.auth);
|
|
962
|
+
const { auth: _auth, ...rest } = configWithAuth;
|
|
936
963
|
return [name, {
|
|
937
964
|
...rest,
|
|
938
965
|
headers: {
|
|
939
966
|
...authHeaders,
|
|
940
|
-
...
|
|
967
|
+
...configWithAuth.headers
|
|
941
968
|
// Explicit headers take precedence
|
|
942
969
|
}
|
|
943
970
|
}];
|
|
@@ -964,8 +991,22 @@ var init_claude_sdk = __esm({
|
|
|
964
991
|
async *query(prompt, options = {}) {
|
|
965
992
|
const model = options.model ?? this.defaultModel;
|
|
966
993
|
if (await this.checkSdkAvailable()) {
|
|
967
|
-
|
|
994
|
+
const normalizedOptions = { ...options, model };
|
|
995
|
+
if (normalizedOptions.agents) {
|
|
996
|
+
const allowedTools = normalizedOptions.allowedTools ?? [];
|
|
997
|
+
if (!allowedTools.includes("Task")) {
|
|
998
|
+
normalizedOptions.allowedTools = [...allowedTools, "Task"];
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
yield* this.executeRealQuery(prompt, normalizedOptions);
|
|
968
1002
|
} else {
|
|
1003
|
+
if (typeof prompt !== "string") {
|
|
1004
|
+
yield {
|
|
1005
|
+
type: "error",
|
|
1006
|
+
error: "Streaming prompts require the real Claude Agent SDK to be installed."
|
|
1007
|
+
};
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
969
1010
|
yield* this.executeSimulatedQuery(prompt, { ...options, model });
|
|
970
1011
|
}
|
|
971
1012
|
}
|
|
@@ -979,8 +1020,9 @@ var init_claude_sdk = __esm({
|
|
|
979
1020
|
}
|
|
980
1021
|
try {
|
|
981
1022
|
const { query } = await import('@anthropic-ai/claude-agent-sdk');
|
|
1023
|
+
const normalizedMcpServers = normalizeMcpServers(options.mcpServers);
|
|
982
1024
|
const queryOptions = {
|
|
983
|
-
prompt,
|
|
1025
|
+
prompt: normalizePromptForSdk(prompt, options.mcpServers),
|
|
984
1026
|
options: {
|
|
985
1027
|
model: options.model,
|
|
986
1028
|
allowedTools: options.allowedTools,
|
|
@@ -989,10 +1031,12 @@ var init_claude_sdk = __esm({
|
|
|
989
1031
|
maxTurns: options.maxTurns,
|
|
990
1032
|
resume: options.resume,
|
|
991
1033
|
forkSession: options.forkSession,
|
|
992
|
-
mcpServers:
|
|
1034
|
+
mcpServers: normalizedMcpServers,
|
|
993
1035
|
agents: options.agents,
|
|
994
1036
|
hooks: options.hooks,
|
|
1037
|
+
plugins: options.plugins,
|
|
995
1038
|
settingSources: options.settingSources,
|
|
1039
|
+
outputFormat: options.outputFormat,
|
|
996
1040
|
// Enable streaming partial messages for real-time text deltas
|
|
997
1041
|
includePartialMessages: true,
|
|
998
1042
|
// Environment variables for the SDK
|
|
@@ -1015,7 +1059,8 @@ var init_claude_sdk = __esm({
|
|
|
1015
1059
|
if (message.type === "system" && message.subtype === "init") {
|
|
1016
1060
|
yield {
|
|
1017
1061
|
type: "session_init",
|
|
1018
|
-
sessionId: message.session_id
|
|
1062
|
+
sessionId: message.session_id,
|
|
1063
|
+
slashCommands: message.slash_commands
|
|
1019
1064
|
};
|
|
1020
1065
|
continue;
|
|
1021
1066
|
}
|
|
@@ -1066,12 +1111,17 @@ var init_claude_sdk = __esm({
|
|
|
1066
1111
|
}
|
|
1067
1112
|
}
|
|
1068
1113
|
if (message.type === "result") {
|
|
1114
|
+
const usage = message.usage;
|
|
1115
|
+
const hasUsageTokens = !!usage && (usage.input_tokens !== void 0 || usage.output_tokens !== void 0);
|
|
1116
|
+
const totalTokens = hasUsageTokens ? (usage?.input_tokens ?? 0) + (usage?.output_tokens ?? 0) : message.tokens ? message.tokens.input + message.tokens.output : void 0;
|
|
1117
|
+
const totalCost = usage?.total_cost_usd ?? message.cost ?? message.total_cost_usd;
|
|
1069
1118
|
yield {
|
|
1070
1119
|
type: "complete",
|
|
1071
1120
|
sessionId: message.session_id,
|
|
1072
1121
|
result: message.result,
|
|
1073
|
-
|
|
1074
|
-
|
|
1122
|
+
structured_output: message.structured_output,
|
|
1123
|
+
totalCost,
|
|
1124
|
+
totalTokens
|
|
1075
1125
|
};
|
|
1076
1126
|
}
|
|
1077
1127
|
}
|
|
@@ -1079,6 +1129,13 @@ var init_claude_sdk = __esm({
|
|
|
1079
1129
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1080
1130
|
if (errorMessage.includes("Cannot find module") || errorMessage.includes("MODULE_NOT_FOUND")) {
|
|
1081
1131
|
console.warn("Claude Agent SDK not installed, using simulation mode");
|
|
1132
|
+
if (typeof prompt !== "string") {
|
|
1133
|
+
yield {
|
|
1134
|
+
type: "error",
|
|
1135
|
+
error: "Streaming prompts require the real Claude Agent SDK to be installed."
|
|
1136
|
+
};
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1082
1139
|
yield* this.executeSimulatedQuery(prompt, options);
|
|
1083
1140
|
} else {
|
|
1084
1141
|
yield {
|
|
@@ -1256,7 +1313,11 @@ var init_claude_sdk = __esm({
|
|
|
1256
1313
|
this.sessionId = msg.session_id;
|
|
1257
1314
|
}
|
|
1258
1315
|
if (msg.type === "system" && msg.subtype === "init") {
|
|
1259
|
-
yield {
|
|
1316
|
+
yield {
|
|
1317
|
+
type: "session_init",
|
|
1318
|
+
sessionId: msg.session_id,
|
|
1319
|
+
slashCommands: msg.slash_commands
|
|
1320
|
+
};
|
|
1260
1321
|
} else if (msg.type === "assistant" && msg.message) {
|
|
1261
1322
|
for (const block of msg.message.content) {
|
|
1262
1323
|
if (block.type === "text") {
|
|
@@ -1684,6 +1745,13 @@ function createGeminiBackendExecutor(options) {
|
|
|
1684
1745
|
defaultModel: options.model ?? DEFAULT_MODELS.gemini
|
|
1685
1746
|
});
|
|
1686
1747
|
return async function* (prompt, queryOptions) {
|
|
1748
|
+
if (typeof prompt !== "string") {
|
|
1749
|
+
yield {
|
|
1750
|
+
type: "error",
|
|
1751
|
+
error: "Gemini backend does not support streaming prompt inputs."
|
|
1752
|
+
};
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1687
1755
|
const geminiOptions = mapClaudeOptionsToGemini(queryOptions);
|
|
1688
1756
|
if (queryOptions.signal) {
|
|
1689
1757
|
geminiOptions.signal = queryOptions.signal;
|
|
@@ -1848,6 +1916,11 @@ var init_sandbox_logger = __esm({
|
|
|
1848
1916
|
};
|
|
1849
1917
|
}
|
|
1850
1918
|
});
|
|
1919
|
+
function getClaudeSdkOverrides(config) {
|
|
1920
|
+
const raw = config?.claudeSdkOptions;
|
|
1921
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
1922
|
+
return raw;
|
|
1923
|
+
}
|
|
1851
1924
|
var AgentHarness;
|
|
1852
1925
|
var init_harness = __esm({
|
|
1853
1926
|
"src/agent/harness.ts"() {
|
|
@@ -2104,6 +2177,7 @@ var init_harness = __esm({
|
|
|
2104
2177
|
yield sessionStartEvent;
|
|
2105
2178
|
yield* yieldQueuedLogs();
|
|
2106
2179
|
const assistantContent = [];
|
|
2180
|
+
let structuredOutput;
|
|
2107
2181
|
let wasAborted = false;
|
|
2108
2182
|
try {
|
|
2109
2183
|
logger3.info("execution", "Starting Claude Agent SDK query");
|
|
@@ -2194,6 +2268,10 @@ var init_harness = __esm({
|
|
|
2194
2268
|
};
|
|
2195
2269
|
writeEvent?.(toolResultEvent);
|
|
2196
2270
|
yield toolResultEvent;
|
|
2271
|
+
} else if (event.type === "complete") {
|
|
2272
|
+
if (event.structured_output !== void 0) {
|
|
2273
|
+
structuredOutput = event.structured_output;
|
|
2274
|
+
}
|
|
2197
2275
|
}
|
|
2198
2276
|
}
|
|
2199
2277
|
if (wasAborted || controller.signal.aborted) {
|
|
@@ -2245,7 +2323,8 @@ var init_harness = __esm({
|
|
|
2245
2323
|
[
|
|
2246
2324
|
{
|
|
2247
2325
|
role: "assistant",
|
|
2248
|
-
content: assistantContent
|
|
2326
|
+
content: assistantContent,
|
|
2327
|
+
...structuredOutput !== void 0 ? { metadata: { structured_output: structuredOutput } } : {}
|
|
2249
2328
|
}
|
|
2250
2329
|
]
|
|
2251
2330
|
);
|
|
@@ -2366,11 +2445,15 @@ var init_harness = __esm({
|
|
|
2366
2445
|
async *executeAgentQuery(session, prompt, options, signal, _logger) {
|
|
2367
2446
|
const sessionEnvVars = session.metadata?.envVars;
|
|
2368
2447
|
const sessionStartupScript = session.metadata?.startupScript;
|
|
2448
|
+
const sdkOverrides = getClaudeSdkOverrides(this.config.config);
|
|
2369
2449
|
const mergedEnvVars = {
|
|
2370
2450
|
MAX_THINKING_TOKENS: "1024",
|
|
2371
2451
|
...this.config.envVars,
|
|
2372
2452
|
...sessionEnvVars
|
|
2373
2453
|
};
|
|
2454
|
+
if (sdkOverrides?.enableFileCheckpointing) {
|
|
2455
|
+
mergedEnvVars.CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING = "1";
|
|
2456
|
+
}
|
|
2374
2457
|
const hasEnvVars = Object.keys(mergedEnvVars).length > 0;
|
|
2375
2458
|
const startupScript = sessionStartupScript ?? this.config.startupScript;
|
|
2376
2459
|
const queryOptions = {
|
|
@@ -2379,7 +2462,9 @@ var init_harness = __esm({
|
|
|
2379
2462
|
disallowedTools: this.config.disallowedTools,
|
|
2380
2463
|
permissionMode: this.config.permissionMode,
|
|
2381
2464
|
maxTurns: this.config.maxTurns,
|
|
2465
|
+
outputFormat: options.outputFormat,
|
|
2382
2466
|
mcpServers: this.config.mcpServers,
|
|
2467
|
+
settingSources: this.config.settingSources ?? ["project"],
|
|
2383
2468
|
// Pass the harness session ID for sandbox caching
|
|
2384
2469
|
harnessSessionId: session.id,
|
|
2385
2470
|
// Pass environment and startup configuration
|
|
@@ -2388,6 +2473,17 @@ var init_harness = __esm({
|
|
|
2388
2473
|
// Pass config file URL for cloud-hosted .claude directory (downloaded in sandbox)
|
|
2389
2474
|
...this.config.configFileUrl && { configFileUrl: this.config.configFileUrl }
|
|
2390
2475
|
};
|
|
2476
|
+
if (sdkOverrides) {
|
|
2477
|
+
if (sdkOverrides.hooks) queryOptions.hooks = sdkOverrides.hooks;
|
|
2478
|
+
if (typeof sdkOverrides.enableFileCheckpointing === "boolean") {
|
|
2479
|
+
queryOptions.enableFileCheckpointing = sdkOverrides.enableFileCheckpointing;
|
|
2480
|
+
if (sdkOverrides.enableFileCheckpointing && !sdkOverrides.extraArgs) {
|
|
2481
|
+
queryOptions.extraArgs = { "replay-user-messages": null };
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
if (sdkOverrides.extraArgs) queryOptions.extraArgs = sdkOverrides.extraArgs;
|
|
2485
|
+
if (sdkOverrides.permissionMode) queryOptions.permissionMode = sdkOverrides.permissionMode;
|
|
2486
|
+
}
|
|
2391
2487
|
const sessionApiKey = this.sessionApiKeys.get(session.id);
|
|
2392
2488
|
if (sessionApiKey) {
|
|
2393
2489
|
queryOptions.apiKey = sessionApiKey;
|
|
@@ -2423,7 +2519,9 @@ ${prompt}`;
|
|
|
2423
2519
|
const skillsDir = this.sessionSkillDirs.get(session.id);
|
|
2424
2520
|
if (skillsDir) {
|
|
2425
2521
|
queryOptions.cwd = skillsDir;
|
|
2426
|
-
queryOptions.settingSources
|
|
2522
|
+
if (queryOptions.settingSources === void 0) {
|
|
2523
|
+
queryOptions.settingSources = ["project"];
|
|
2524
|
+
}
|
|
2427
2525
|
if (queryOptions.allowedTools && !queryOptions.allowedTools.includes("Skill")) {
|
|
2428
2526
|
queryOptions.allowedTools = [...queryOptions.allowedTools, "Skill"];
|
|
2429
2527
|
}
|
|
@@ -2452,6 +2550,7 @@ ${prompt}`;
|
|
|
2452
2550
|
await this.sessionManager.updateSession(session.id, {
|
|
2453
2551
|
sdkSessionId: event.sessionId
|
|
2454
2552
|
});
|
|
2553
|
+
yield { type: "session_init", sessionId: event.sessionId, slashCommands: event.slashCommands };
|
|
2455
2554
|
} else if (event.type === "text_delta" && event.delta) {
|
|
2456
2555
|
yield { type: "text_delta", delta: event.delta };
|
|
2457
2556
|
} else if (event.type === "thinking_delta" && event.delta) {
|
|
@@ -2472,6 +2571,11 @@ ${prompt}`;
|
|
|
2472
2571
|
content: event.content,
|
|
2473
2572
|
isError: event.isError
|
|
2474
2573
|
};
|
|
2574
|
+
} else if (event.type === "complete") {
|
|
2575
|
+
yield {
|
|
2576
|
+
type: "complete",
|
|
2577
|
+
structured_output: event.structured_output
|
|
2578
|
+
};
|
|
2475
2579
|
} else if (event.type === "error") {
|
|
2476
2580
|
throw new Error(event.error ?? "Unknown error from Claude SDK");
|
|
2477
2581
|
}
|
|
@@ -4613,6 +4717,12 @@ echo "[warmup] Warmup complete!"
|
|
|
4613
4717
|
startPromise = null;
|
|
4614
4718
|
/** Registered warmup specs by tag (e.g. agentId -> spec) */
|
|
4615
4719
|
warmupSpecs = /* @__PURE__ */ new Map();
|
|
4720
|
+
/** Tags currently being warmed (prevents duplicate warming of same spec) */
|
|
4721
|
+
warmingTags = /* @__PURE__ */ new Set();
|
|
4722
|
+
/** Max warmup specs to keep (LRU eviction above this) */
|
|
4723
|
+
static MAX_SPECS = 10;
|
|
4724
|
+
/** Timeout for spec setup in ms (prevents hanging S3 pulls / install.sh) */
|
|
4725
|
+
static SPEC_SETUP_TIMEOUT_MS = 12e4;
|
|
4616
4726
|
/** Consecutive warmup failure count (reset on success) */
|
|
4617
4727
|
consecutiveFailures = 0;
|
|
4618
4728
|
/** Timestamp of last warmup attempt — used for backoff */
|
|
@@ -4625,7 +4735,7 @@ echo "[warmup] Warmup complete!"
|
|
|
4625
4735
|
this.config = {
|
|
4626
4736
|
minPoolSize: config.minPoolSize ?? parseInt(process.env.SANDBOX_POOL_MIN_SIZE ?? "2"),
|
|
4627
4737
|
maxPoolSize: config.maxPoolSize ?? parseInt(process.env.SANDBOX_POOL_MAX_SIZE ?? "5"),
|
|
4628
|
-
sandboxTimeout: config.sandboxTimeout ?? parseInt(process.env.SANDBOX_TIMEOUT ?? "
|
|
4738
|
+
sandboxTimeout: config.sandboxTimeout ?? parseInt(process.env.SANDBOX_TIMEOUT ?? "300"),
|
|
4629
4739
|
expiryThresholdMs: config.expiryThresholdMs ?? parseInt(process.env.SANDBOX_EXPIRY_THRESHOLD_MS ?? "120000"),
|
|
4630
4740
|
maintenanceIntervalMs: config.maintenanceIntervalMs ?? parseInt(process.env.SANDBOX_POOL_MAINTENANCE_MS ?? "30000"),
|
|
4631
4741
|
runtime: config.runtime ?? "node22",
|
|
@@ -4831,18 +4941,28 @@ echo "[warmup] Warmup complete!"
|
|
|
4831
4941
|
}
|
|
4832
4942
|
/**
|
|
4833
4943
|
* Register a warmup spec so the pool can pre-warm agent-specific sandboxes.
|
|
4834
|
-
* If a spec with the same tag already exists,
|
|
4944
|
+
* If a spec with the same tag and configHash already exists, only updates priority (skip #7).
|
|
4945
|
+
* Evicts lowest-priority specs when exceeding MAX_SPECS (fix #2).
|
|
4835
4946
|
* Triggers replenishment to warm a sandbox for this spec.
|
|
4836
4947
|
*/
|
|
4837
4948
|
registerWarmupSpec(spec) {
|
|
4838
|
-
const
|
|
4949
|
+
const existing = this.warmupSpecs.get(spec.tag);
|
|
4950
|
+
if (existing && spec.configHash && existing.configHash === spec.configHash) {
|
|
4951
|
+
existing.priority = spec.priority;
|
|
4952
|
+
return;
|
|
4953
|
+
}
|
|
4954
|
+
const isNew = !existing;
|
|
4839
4955
|
this.warmupSpecs.set(spec.tag, spec);
|
|
4840
|
-
|
|
4956
|
+
if (this.warmupSpecs.size > _SandboxPool.MAX_SPECS) {
|
|
4957
|
+
this.evictLowestPrioritySpecs();
|
|
4958
|
+
}
|
|
4959
|
+
console.log(`[POOL] ${isNew ? "Registered" : "Updated"} warmup spec: ${spec.tag} (priority=${spec.priority}, specs=${this.warmupSpecs.size})`);
|
|
4841
4960
|
this.emitMetric("spec_registered", {
|
|
4842
4961
|
tag: spec.tag,
|
|
4843
4962
|
priority: spec.priority,
|
|
4844
4963
|
isNew,
|
|
4845
|
-
totalSpecs: this.warmupSpecs.size
|
|
4964
|
+
totalSpecs: this.warmupSpecs.size,
|
|
4965
|
+
configHash: spec.configHash
|
|
4846
4966
|
});
|
|
4847
4967
|
if (this.running) {
|
|
4848
4968
|
this.triggerReplenishment();
|
|
@@ -4969,20 +5089,41 @@ echo "[warmup] Warmup complete!"
|
|
|
4969
5089
|
}
|
|
4970
5090
|
let warmupTag;
|
|
4971
5091
|
let agentSetupComplete = false;
|
|
5092
|
+
let warmupInstallRan = false;
|
|
5093
|
+
let warmupStartupRan = false;
|
|
4972
5094
|
if (spec) {
|
|
5095
|
+
this.warmingTags.add(spec.tag);
|
|
4973
5096
|
console.log(`[POOL] Running spec setup for tag=${spec.tag} on sandbox ${sandbox.sandboxId}...`);
|
|
4974
5097
|
this.emitMetric("spec_setup_started", { tag: spec.tag, sandboxId: sandbox.sandboxId });
|
|
4975
5098
|
const specStartTime = Date.now();
|
|
4976
5099
|
try {
|
|
4977
|
-
await
|
|
5100
|
+
const setupResult = await new Promise((resolve3, reject) => {
|
|
5101
|
+
const timer = setTimeout(
|
|
5102
|
+
() => reject(new Error(`Spec setup timed out after ${_SandboxPool.SPEC_SETUP_TIMEOUT_MS / 1e3}s`)),
|
|
5103
|
+
_SandboxPool.SPEC_SETUP_TIMEOUT_MS
|
|
5104
|
+
);
|
|
5105
|
+
spec.setup(sandbox).then((result) => {
|
|
5106
|
+
clearTimeout(timer);
|
|
5107
|
+
resolve3(result);
|
|
5108
|
+
}).catch((error) => {
|
|
5109
|
+
clearTimeout(timer);
|
|
5110
|
+
reject(error);
|
|
5111
|
+
});
|
|
5112
|
+
});
|
|
4978
5113
|
warmupTag = spec.tag;
|
|
4979
5114
|
agentSetupComplete = true;
|
|
5115
|
+
if (setupResult && typeof setupResult === "object") {
|
|
5116
|
+
warmupInstallRan = setupResult.installRan === true;
|
|
5117
|
+
warmupStartupRan = setupResult.startupRan === true;
|
|
5118
|
+
}
|
|
4980
5119
|
const specDuration = Date.now() - specStartTime;
|
|
4981
|
-
console.log(`[POOL] Spec setup completed for tag=${spec.tag} on sandbox ${sandbox.sandboxId} (${specDuration}ms)`);
|
|
5120
|
+
console.log(`[POOL] Spec setup completed for tag=${spec.tag} on sandbox ${sandbox.sandboxId} (${specDuration}ms, install=${warmupInstallRan}, startup=${warmupStartupRan})`);
|
|
4982
5121
|
this.emitMetric("spec_setup_completed", {
|
|
4983
5122
|
tag: spec.tag,
|
|
4984
5123
|
sandboxId: sandbox.sandboxId,
|
|
4985
|
-
durationMs: specDuration
|
|
5124
|
+
durationMs: specDuration,
|
|
5125
|
+
warmupInstallRan,
|
|
5126
|
+
warmupStartupRan
|
|
4986
5127
|
});
|
|
4987
5128
|
} catch (specError) {
|
|
4988
5129
|
const specDuration = Date.now() - specStartTime;
|
|
@@ -4996,6 +5137,8 @@ echo "[warmup] Warmup complete!"
|
|
|
4996
5137
|
durationMs: specDuration,
|
|
4997
5138
|
error: specErrorMessage
|
|
4998
5139
|
});
|
|
5140
|
+
} finally {
|
|
5141
|
+
this.warmingTags.delete(spec.tag);
|
|
4999
5142
|
}
|
|
5000
5143
|
}
|
|
5001
5144
|
const warmupTime = Date.now() - startTime;
|
|
@@ -5009,7 +5152,9 @@ echo "[warmup] Warmup complete!"
|
|
|
5009
5152
|
eligible: true,
|
|
5010
5153
|
lastHeartbeat: now,
|
|
5011
5154
|
warmupTag,
|
|
5012
|
-
agentSetupComplete
|
|
5155
|
+
agentSetupComplete,
|
|
5156
|
+
warmupInstallRan,
|
|
5157
|
+
warmupStartupRan
|
|
5013
5158
|
};
|
|
5014
5159
|
const tagInfo = warmupTag ? ` [tag=${warmupTag}]` : "";
|
|
5015
5160
|
console.log(`[POOL] Warmup completed for ${sandbox.sandboxId} (took ${warmupTime}ms)${useTarball ? " [tarball]" : ""}${tagInfo}`);
|
|
@@ -5211,15 +5356,20 @@ echo "[warmup] Warmup complete!"
|
|
|
5211
5356
|
}
|
|
5212
5357
|
/**
|
|
5213
5358
|
* Decide which specs to apply to new sandboxes during replenishment.
|
|
5214
|
-
* Strategy:
|
|
5359
|
+
* Strategy:
|
|
5360
|
+
* - Always reserve at least 1 slot for generic (fix #3)
|
|
5361
|
+
* - Cover uncovered specs first (highest priority), skipping in-flight tags (fix #4)
|
|
5362
|
+
* - Fill remaining as generic
|
|
5215
5363
|
* Returns an array of length `needed`, where each element is a spec or undefined (generic).
|
|
5216
5364
|
*/
|
|
5217
5365
|
selectSpecsForReplenishment(needed) {
|
|
5218
|
-
if (this.warmupSpecs.size === 0) {
|
|
5366
|
+
if (this.warmupSpecs.size === 0 || needed === 0) {
|
|
5219
5367
|
return new Array(needed).fill(void 0);
|
|
5220
5368
|
}
|
|
5369
|
+
const maxTaggedSlots = Math.max(0, needed - 1);
|
|
5221
5370
|
const uncoveredSpecs = [];
|
|
5222
5371
|
for (const spec of this.warmupSpecs.values()) {
|
|
5372
|
+
if (this.warmingTags.has(spec.tag)) continue;
|
|
5223
5373
|
let hasCoverage = false;
|
|
5224
5374
|
for (const pooled of this.pool.values()) {
|
|
5225
5375
|
if (pooled.warmupTag === spec.tag && pooled.eligible && !pooled.assignedTo) {
|
|
@@ -5234,7 +5384,7 @@ echo "[warmup] Warmup complete!"
|
|
|
5234
5384
|
uncoveredSpecs.sort((a, b) => b.priority - a.priority);
|
|
5235
5385
|
const assignments = [];
|
|
5236
5386
|
for (const spec of uncoveredSpecs) {
|
|
5237
|
-
if (assignments.length >=
|
|
5387
|
+
if (assignments.length >= maxTaggedSlots) break;
|
|
5238
5388
|
assignments.push(spec);
|
|
5239
5389
|
}
|
|
5240
5390
|
while (assignments.length < needed) {
|
|
@@ -5242,6 +5392,27 @@ echo "[warmup] Warmup complete!"
|
|
|
5242
5392
|
}
|
|
5243
5393
|
return assignments;
|
|
5244
5394
|
}
|
|
5395
|
+
/**
|
|
5396
|
+
* Evict lowest-priority specs when over MAX_SPECS capacity (fix #2).
|
|
5397
|
+
*/
|
|
5398
|
+
evictLowestPrioritySpecs() {
|
|
5399
|
+
while (this.warmupSpecs.size > _SandboxPool.MAX_SPECS) {
|
|
5400
|
+
let lowestTag;
|
|
5401
|
+
let lowestPriority = Infinity;
|
|
5402
|
+
for (const [tag, spec] of this.warmupSpecs.entries()) {
|
|
5403
|
+
if (spec.priority < lowestPriority) {
|
|
5404
|
+
lowestPriority = spec.priority;
|
|
5405
|
+
lowestTag = tag;
|
|
5406
|
+
}
|
|
5407
|
+
}
|
|
5408
|
+
if (lowestTag) {
|
|
5409
|
+
this.warmupSpecs.delete(lowestTag);
|
|
5410
|
+
console.log(`[POOL] Evicted warmup spec: ${lowestTag} (priority=${lowestPriority}, specs=${this.warmupSpecs.size})`);
|
|
5411
|
+
} else {
|
|
5412
|
+
break;
|
|
5413
|
+
}
|
|
5414
|
+
}
|
|
5415
|
+
}
|
|
5245
5416
|
/**
|
|
5246
5417
|
* Destroy a sandbox and clean up
|
|
5247
5418
|
*/
|
|
@@ -5561,6 +5732,8 @@ async function getOrCreateSandbox(options) {
|
|
|
5561
5732
|
const pooled = await pool.acquire(sessionId, preferTag);
|
|
5562
5733
|
const tagInfo = pooled.warmupTag ? ` [tag=${pooled.warmupTag}, agentSetup=${pooled.agentSetupComplete}]` : "";
|
|
5563
5734
|
console.log(`[SANDBOX] Acquired pre-warmed sandbox: ${pooled.sandboxId}${tagInfo}`);
|
|
5735
|
+
const installDone = pooled.warmupInstallRan === true;
|
|
5736
|
+
const startupDone = pooled.warmupStartupRan === true;
|
|
5564
5737
|
const agentSetupDone = pooled.agentSetupComplete === true;
|
|
5565
5738
|
const now2 = Date.now();
|
|
5566
5739
|
const entry2 = {
|
|
@@ -5569,24 +5742,26 @@ async function getOrCreateSandbox(options) {
|
|
|
5569
5742
|
createdAt: pooled.createdAt,
|
|
5570
5743
|
lastUsedAt: now2,
|
|
5571
5744
|
sdkInstalled: pooled.sdkInstalled,
|
|
5572
|
-
startupScriptRan:
|
|
5573
|
-
installScriptRan:
|
|
5745
|
+
startupScriptRan: startupDone,
|
|
5746
|
+
installScriptRan: installDone
|
|
5574
5747
|
};
|
|
5575
5748
|
sandboxCache.set(sessionId, entry2);
|
|
5576
5749
|
return {
|
|
5577
5750
|
sandbox: pooled.sandbox,
|
|
5578
5751
|
sandboxId: pooled.sandboxId,
|
|
5579
5752
|
sdkInstalled: pooled.sdkInstalled,
|
|
5580
|
-
startupScriptRan:
|
|
5753
|
+
startupScriptRan: startupDone,
|
|
5581
5754
|
startupScriptHash: void 0,
|
|
5582
|
-
installScriptRan:
|
|
5755
|
+
installScriptRan: installDone,
|
|
5583
5756
|
installScriptHash: void 0,
|
|
5584
5757
|
isNew: false,
|
|
5585
5758
|
// Not new - came from pool
|
|
5586
5759
|
configFileUrl: void 0,
|
|
5587
5760
|
configInstalledAt: agentSetupDone ? now2 : void 0,
|
|
5588
5761
|
warmupTag: pooled.warmupTag,
|
|
5589
|
-
agentSetupComplete: pooled.agentSetupComplete
|
|
5762
|
+
agentSetupComplete: pooled.agentSetupComplete,
|
|
5763
|
+
warmupInstallRan: pooled.warmupInstallRan,
|
|
5764
|
+
warmupStartupRan: pooled.warmupStartupRan
|
|
5590
5765
|
};
|
|
5591
5766
|
} catch (error) {
|
|
5592
5767
|
console.warn(
|
|
@@ -5931,6 +6106,13 @@ function createVercelSandboxExecutor(apiKey) {
|
|
|
5931
6106
|
};
|
|
5932
6107
|
}
|
|
5933
6108
|
async function* executeInSandbox(prompt, apiKey, options) {
|
|
6109
|
+
if (typeof prompt !== "string") {
|
|
6110
|
+
yield {
|
|
6111
|
+
type: "error",
|
|
6112
|
+
error: "Vercel sandbox executor does not support streaming prompt inputs."
|
|
6113
|
+
};
|
|
6114
|
+
return;
|
|
6115
|
+
}
|
|
5934
6116
|
const sessionId = options.harnessSessionId || `temp-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
5935
6117
|
try {
|
|
5936
6118
|
const { sandbox, sdkInstalled, startupScriptRan, startupScriptHash: cachedScriptHash, configFileUrl: cachedConfigUrl } = await getOrCreateSandbox({
|
|
@@ -6072,9 +6254,13 @@ async function* executeInSandbox(prompt, apiKey, options) {
|
|
|
6072
6254
|
permissionMode: options.permissionMode || "bypassPermissions",
|
|
6073
6255
|
includePartialMessages: true
|
|
6074
6256
|
};
|
|
6257
|
+
if (options.settingSources !== void 0) {
|
|
6258
|
+
sdkOptions.settingSources = options.settingSources;
|
|
6259
|
+
} else {
|
|
6260
|
+
sdkOptions.settingSources = ["project"];
|
|
6261
|
+
}
|
|
6075
6262
|
const hasConfig = options.configFileUrl || cachedConfigUrl;
|
|
6076
6263
|
if (hasConfig) {
|
|
6077
|
-
sdkOptions.settingSources = ["project"];
|
|
6078
6264
|
if (options.allowedTools && options.allowedTools.length > 0) {
|
|
6079
6265
|
const allowedTools = [...options.allowedTools];
|
|
6080
6266
|
if (!allowedTools.includes("Skill")) {
|
|
@@ -6110,12 +6296,47 @@ async function* executeInSandbox(prompt, apiKey, options) {
|
|
|
6110
6296
|
if (options.resume) {
|
|
6111
6297
|
sdkOptions.resume = options.resume;
|
|
6112
6298
|
}
|
|
6299
|
+
try {
|
|
6300
|
+
const pluginFindResult = await sandbox.runCommand({
|
|
6301
|
+
cmd: "bash",
|
|
6302
|
+
args: [
|
|
6303
|
+
"-c",
|
|
6304
|
+
[
|
|
6305
|
+
'for base in ".claude/plugins" "$HOME/.claude/plugins"; do',
|
|
6306
|
+
' if [ -d "$base" ]; then',
|
|
6307
|
+
' find "$base" -type f -path "*/.claude-plugin/plugin.json" -print',
|
|
6308
|
+
" fi",
|
|
6309
|
+
"done | sed 's#/.claude-plugin/plugin.json$##'"
|
|
6310
|
+
].join("\n")
|
|
6311
|
+
]
|
|
6312
|
+
});
|
|
6313
|
+
const rawPluginRoots = (await pluginFindResult.stdout()).trim();
|
|
6314
|
+
const pluginRoots = rawPluginRoots ? rawPluginRoots.split("\n").map((line) => line.trim()).filter(Boolean) : [];
|
|
6315
|
+
const uniquePluginRoots = Array.from(new Set(pluginRoots));
|
|
6316
|
+
if (uniquePluginRoots.length > 0) {
|
|
6317
|
+
sdkOptions.plugins = uniquePluginRoots.map((path15) => ({ type: "local", path: path15 }));
|
|
6318
|
+
console.log("[SANDBOX] Plugins detected:", uniquePluginRoots);
|
|
6319
|
+
}
|
|
6320
|
+
} catch (pluginError) {
|
|
6321
|
+
console.warn("[SANDBOX] Failed to detect plugins:", pluginError);
|
|
6322
|
+
}
|
|
6113
6323
|
const agentScript = `
|
|
6114
6324
|
const { query } = require('@anthropic-ai/claude-agent-sdk');
|
|
6325
|
+
const fs = require('fs');
|
|
6115
6326
|
|
|
6116
6327
|
const prompt = ${JSON.stringify(prompt)};
|
|
6117
6328
|
const options = ${JSON.stringify(sdkOptions)};
|
|
6118
6329
|
|
|
6330
|
+
// Enable subagents if .claude/agents exists (requires Task tool)
|
|
6331
|
+
if (fs.existsSync('.claude/agents')) {
|
|
6332
|
+
if (!Array.isArray(options.allowedTools)) {
|
|
6333
|
+
options.allowedTools = [];
|
|
6334
|
+
}
|
|
6335
|
+
if (!options.allowedTools.includes('Task')) {
|
|
6336
|
+
options.allowedTools.push('Task');
|
|
6337
|
+
}
|
|
6338
|
+
}
|
|
6339
|
+
|
|
6119
6340
|
let queryCompleted = false;
|
|
6120
6341
|
|
|
6121
6342
|
async function run() {
|
|
@@ -6212,7 +6433,11 @@ SCRIPT_EOF`]
|
|
|
6212
6433
|
});
|
|
6213
6434
|
}
|
|
6214
6435
|
if (event.type === "system" && event.subtype === "init") {
|
|
6215
|
-
events.push({
|
|
6436
|
+
events.push({
|
|
6437
|
+
type: "session_init",
|
|
6438
|
+
sessionId: event.session_id || "",
|
|
6439
|
+
slashCommands: event.slash_commands
|
|
6440
|
+
});
|
|
6216
6441
|
} else if (event.type === "stream_event" && event.event) {
|
|
6217
6442
|
const streamEvent = event.event;
|
|
6218
6443
|
if (streamEvent.type === "content_block_delta") {
|
|
@@ -6247,12 +6472,16 @@ SCRIPT_EOF`]
|
|
|
6247
6472
|
}
|
|
6248
6473
|
}
|
|
6249
6474
|
} else if (event.type === "result") {
|
|
6475
|
+
const usage = event.usage;
|
|
6476
|
+
const hasUsageTokens = !!usage && (usage.input_tokens !== void 0 || usage.output_tokens !== void 0);
|
|
6477
|
+
const totalTokens = hasUsageTokens ? (usage?.input_tokens ?? 0) + (usage?.output_tokens ?? 0) : event.total_tokens ?? (event.tokens ? event.tokens.input + event.tokens.output : void 0);
|
|
6478
|
+
const totalCost = usage?.total_cost_usd ?? event.total_cost_usd ?? event.cost ?? event.total_cost;
|
|
6250
6479
|
events.push({
|
|
6251
6480
|
type: "complete",
|
|
6252
6481
|
sessionId: event.session_id,
|
|
6253
6482
|
result: event.result,
|
|
6254
|
-
totalCost
|
|
6255
|
-
totalTokens
|
|
6483
|
+
totalCost,
|
|
6484
|
+
totalTokens
|
|
6256
6485
|
});
|
|
6257
6486
|
}
|
|
6258
6487
|
return events;
|
|
@@ -12831,6 +13060,7 @@ __export(schemas_exports, {
|
|
|
12831
13060
|
RunAgentRequestSchema: () => RunAgentRequestSchema,
|
|
12832
13061
|
SendMessageRequestSchema: () => SendMessageRequestSchema,
|
|
12833
13062
|
SessionEndEventSchema: () => SessionEndEventSchema,
|
|
13063
|
+
SessionInitEventSchema: () => SessionInitEventSchema,
|
|
12834
13064
|
SessionSchema: () => SessionSchema,
|
|
12835
13065
|
SessionStartEventSchema: () => SessionStartEventSchema,
|
|
12836
13066
|
SessionStatusSchema: () => SessionStatusSchema,
|
|
@@ -12850,7 +13080,7 @@ __export(schemas_exports, {
|
|
|
12850
13080
|
TurnCompleteEventSchema: () => TurnCompleteEventSchema,
|
|
12851
13081
|
UpdateAgentRequestSchema: () => UpdateAgentRequestSchema
|
|
12852
13082
|
});
|
|
12853
|
-
var ErrorResponseSchema, SuccessResponseSchema, PaginationQuerySchema, OrderQuerySchema, TextContentSchema, ToolUseContentSchema, ToolResultContentSchema, ImageContentSchema, FileContentSchema, MessageContentSchema, MessageSchema, PaginatedMessagesSchema, SessionStatusSchema, SessionSchema, PaginatedSessionsSchema, CreateSessionRequestSchema, SendMessageRequestSchema, ResumeSessionRequestSchema, ListSessionsQuerySchema, AgentStatusSchema, PermissionModeSchema, McpServerConfigSchema, StoredAgentSchema, SimpleAgentSchema, PaginatedAgentsSchema, CreateAgentRequestSchema, UpdateAgentRequestSchema, RunAgentRequestSchema, ListAgentsQuerySchema, GitHubSkillSourceSchema, LocalSkillSourceSchema, SkillSourceSchema, BrowseSkillsRequestSchema, ReadSkillFileRequestSchema, FileEntrySchema, BrowseSkillsResponseSchema, SkillFileContentSchema, ReadSkillFileResponseSchema, SessionStartEventSchema, TextDeltaEventSchema, ThinkingDeltaEventSchema, MessageEventSchema, ToolUseEventSchema, ToolResultEventSchema, SessionEndEventSchema, TurnCompleteEventSchema, StreamErrorEventSchema, HealthResponseSchema;
|
|
13083
|
+
var ErrorResponseSchema, SuccessResponseSchema, PaginationQuerySchema, OrderQuerySchema, TextContentSchema, ToolUseContentSchema, ToolResultContentSchema, ImageContentSchema, FileContentSchema, MessageContentSchema, MessageSchema, PaginatedMessagesSchema, SessionStatusSchema, SessionSchema, PaginatedSessionsSchema, CreateSessionRequestSchema, SendMessageRequestSchema, ResumeSessionRequestSchema, ListSessionsQuerySchema, AgentStatusSchema, PermissionModeSchema, McpServerConfigSchema, StoredAgentSchema, SimpleAgentSchema, PaginatedAgentsSchema, CreateAgentRequestSchema, UpdateAgentRequestSchema, RunAgentRequestSchema, ListAgentsQuerySchema, GitHubSkillSourceSchema, LocalSkillSourceSchema, SkillSourceSchema, BrowseSkillsRequestSchema, ReadSkillFileRequestSchema, FileEntrySchema, BrowseSkillsResponseSchema, SkillFileContentSchema, ReadSkillFileResponseSchema, SessionStartEventSchema, SessionInitEventSchema, TextDeltaEventSchema, ThinkingDeltaEventSchema, MessageEventSchema, ToolUseEventSchema, ToolResultEventSchema, SessionEndEventSchema, TurnCompleteEventSchema, StreamErrorEventSchema, HealthResponseSchema;
|
|
12854
13084
|
var init_schemas = __esm({
|
|
12855
13085
|
"src/server/openapi/schemas.ts"() {
|
|
12856
13086
|
init_dist3();
|
|
@@ -13076,6 +13306,15 @@ var init_schemas = __esm({
|
|
|
13076
13306
|
sessionId: z.string().openapi({ description: "Session ID" }),
|
|
13077
13307
|
claudeSessionId: z.string().openapi({ description: "Claude SDK session ID" })
|
|
13078
13308
|
}).openapi("SessionStartEvent");
|
|
13309
|
+
SessionInitEventSchema = z.object({
|
|
13310
|
+
type: z.literal("session_init"),
|
|
13311
|
+
sessionId: z.string().openapi({ description: "Claude SDK session ID" }),
|
|
13312
|
+
slashCommands: z.array(z.object({
|
|
13313
|
+
name: z.string().openapi({ description: "Slash command name" }),
|
|
13314
|
+
description: z.string().optional().openapi({ description: "Slash command description" }),
|
|
13315
|
+
prompt: z.string().optional().openapi({ description: "Slash command prompt" })
|
|
13316
|
+
})).optional().openapi({ description: "Slash commands advertised by the SDK" })
|
|
13317
|
+
}).openapi("SessionInitEvent");
|
|
13079
13318
|
TextDeltaEventSchema = z.object({
|
|
13080
13319
|
type: z.literal("text_delta"),
|
|
13081
13320
|
delta: z.string().openapi({ description: "Text chunk", example: "Hello" })
|
|
@@ -13402,6 +13641,7 @@ var init_sessions2 = __esm({
|
|
|
13402
13641
|
The stream emits the following event types:
|
|
13403
13642
|
|
|
13404
13643
|
- \`session_start\` - Session started, includes sessionId and sdkSessionId
|
|
13644
|
+
- \`session_init\` - Claude SDK session initialized (includes slash commands)
|
|
13405
13645
|
- \`text_delta\` - Text chunk being generated
|
|
13406
13646
|
- \`thinking_delta\` - Thinking/reasoning text chunk
|
|
13407
13647
|
- \`message\` - Complete message saved to storage
|
|
@@ -13850,6 +14090,7 @@ This is a convenience endpoint that combines session creation and message sendin
|
|
|
13850
14090
|
The stream emits the following event types:
|
|
13851
14091
|
|
|
13852
14092
|
- \`session_start\` - Session started
|
|
14093
|
+
- \`session_init\` - Claude SDK session initialized (includes slash commands)
|
|
13853
14094
|
- \`text_delta\` - Text chunk being generated
|
|
13854
14095
|
- \`thinking_delta\` - Thinking/reasoning text chunk
|
|
13855
14096
|
- \`message\` - Complete message saved
|