@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.cjs
CHANGED
|
@@ -913,6 +913,32 @@ var init_mcp = __esm({
|
|
|
913
913
|
});
|
|
914
914
|
|
|
915
915
|
// src/agent/claude-sdk.ts
|
|
916
|
+
function isStandardMcpServerConfig(config) {
|
|
917
|
+
if (!config || typeof config !== "object") return false;
|
|
918
|
+
const candidate = config;
|
|
919
|
+
if (typeof candidate.command === "string") return true;
|
|
920
|
+
if (typeof candidate.url === "string") return true;
|
|
921
|
+
if (candidate.type === "stdio" || candidate.type === "http" || candidate.type === "sse") return true;
|
|
922
|
+
return false;
|
|
923
|
+
}
|
|
924
|
+
function hasCustomMcpServers(mcpServers) {
|
|
925
|
+
if (!mcpServers) return false;
|
|
926
|
+
return Object.values(mcpServers).some((config) => !isStandardMcpServerConfig(config));
|
|
927
|
+
}
|
|
928
|
+
async function* toStreamingPrompt(prompt) {
|
|
929
|
+
yield {
|
|
930
|
+
type: "user",
|
|
931
|
+
message: {
|
|
932
|
+
role: "user",
|
|
933
|
+
content: prompt
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
function normalizePromptForSdk(prompt, mcpServers) {
|
|
938
|
+
if (typeof prompt !== "string") return prompt;
|
|
939
|
+
if (!hasCustomMcpServers(mcpServers)) return prompt;
|
|
940
|
+
return toStreamingPrompt(prompt);
|
|
941
|
+
}
|
|
916
942
|
function convertClaudeMessage(claudeMessage, sessionId) {
|
|
917
943
|
if (claudeMessage.type !== "assistant" || !claudeMessage.message) {
|
|
918
944
|
return null;
|
|
@@ -956,16 +982,17 @@ function normalizeMcpServers(mcpServers) {
|
|
|
956
982
|
if (!mcpServers) return mcpServers;
|
|
957
983
|
return Object.fromEntries(
|
|
958
984
|
Object.entries(mcpServers).map(([name, config]) => {
|
|
959
|
-
|
|
985
|
+
const configWithAuth = config;
|
|
986
|
+
if (!configWithAuth.auth) {
|
|
960
987
|
return [name, config];
|
|
961
988
|
}
|
|
962
|
-
const authHeaders = mcpAuthToHeaders(
|
|
963
|
-
const { auth: _auth, ...rest } =
|
|
989
|
+
const authHeaders = mcpAuthToHeaders(configWithAuth.auth);
|
|
990
|
+
const { auth: _auth, ...rest } = configWithAuth;
|
|
964
991
|
return [name, {
|
|
965
992
|
...rest,
|
|
966
993
|
headers: {
|
|
967
994
|
...authHeaders,
|
|
968
|
-
...
|
|
995
|
+
...configWithAuth.headers
|
|
969
996
|
// Explicit headers take precedence
|
|
970
997
|
}
|
|
971
998
|
}];
|
|
@@ -992,8 +1019,22 @@ var init_claude_sdk = __esm({
|
|
|
992
1019
|
async *query(prompt, options = {}) {
|
|
993
1020
|
const model = options.model ?? this.defaultModel;
|
|
994
1021
|
if (await this.checkSdkAvailable()) {
|
|
995
|
-
|
|
1022
|
+
const normalizedOptions = { ...options, model };
|
|
1023
|
+
if (normalizedOptions.agents) {
|
|
1024
|
+
const allowedTools = normalizedOptions.allowedTools ?? [];
|
|
1025
|
+
if (!allowedTools.includes("Task")) {
|
|
1026
|
+
normalizedOptions.allowedTools = [...allowedTools, "Task"];
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
yield* this.executeRealQuery(prompt, normalizedOptions);
|
|
996
1030
|
} else {
|
|
1031
|
+
if (typeof prompt !== "string") {
|
|
1032
|
+
yield {
|
|
1033
|
+
type: "error",
|
|
1034
|
+
error: "Streaming prompts require the real Claude Agent SDK to be installed."
|
|
1035
|
+
};
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
997
1038
|
yield* this.executeSimulatedQuery(prompt, { ...options, model });
|
|
998
1039
|
}
|
|
999
1040
|
}
|
|
@@ -1007,8 +1048,9 @@ var init_claude_sdk = __esm({
|
|
|
1007
1048
|
}
|
|
1008
1049
|
try {
|
|
1009
1050
|
const { query } = await import('@anthropic-ai/claude-agent-sdk');
|
|
1051
|
+
const normalizedMcpServers = normalizeMcpServers(options.mcpServers);
|
|
1010
1052
|
const queryOptions = {
|
|
1011
|
-
prompt,
|
|
1053
|
+
prompt: normalizePromptForSdk(prompt, options.mcpServers),
|
|
1012
1054
|
options: {
|
|
1013
1055
|
model: options.model,
|
|
1014
1056
|
allowedTools: options.allowedTools,
|
|
@@ -1017,10 +1059,12 @@ var init_claude_sdk = __esm({
|
|
|
1017
1059
|
maxTurns: options.maxTurns,
|
|
1018
1060
|
resume: options.resume,
|
|
1019
1061
|
forkSession: options.forkSession,
|
|
1020
|
-
mcpServers:
|
|
1062
|
+
mcpServers: normalizedMcpServers,
|
|
1021
1063
|
agents: options.agents,
|
|
1022
1064
|
hooks: options.hooks,
|
|
1065
|
+
plugins: options.plugins,
|
|
1023
1066
|
settingSources: options.settingSources,
|
|
1067
|
+
outputFormat: options.outputFormat,
|
|
1024
1068
|
// Enable streaming partial messages for real-time text deltas
|
|
1025
1069
|
includePartialMessages: true,
|
|
1026
1070
|
// Environment variables for the SDK
|
|
@@ -1043,7 +1087,8 @@ var init_claude_sdk = __esm({
|
|
|
1043
1087
|
if (message.type === "system" && message.subtype === "init") {
|
|
1044
1088
|
yield {
|
|
1045
1089
|
type: "session_init",
|
|
1046
|
-
sessionId: message.session_id
|
|
1090
|
+
sessionId: message.session_id,
|
|
1091
|
+
slashCommands: message.slash_commands
|
|
1047
1092
|
};
|
|
1048
1093
|
continue;
|
|
1049
1094
|
}
|
|
@@ -1094,12 +1139,17 @@ var init_claude_sdk = __esm({
|
|
|
1094
1139
|
}
|
|
1095
1140
|
}
|
|
1096
1141
|
if (message.type === "result") {
|
|
1142
|
+
const usage = message.usage;
|
|
1143
|
+
const hasUsageTokens = !!usage && (usage.input_tokens !== void 0 || usage.output_tokens !== void 0);
|
|
1144
|
+
const totalTokens = hasUsageTokens ? (usage?.input_tokens ?? 0) + (usage?.output_tokens ?? 0) : message.tokens ? message.tokens.input + message.tokens.output : void 0;
|
|
1145
|
+
const totalCost = usage?.total_cost_usd ?? message.cost ?? message.total_cost_usd;
|
|
1097
1146
|
yield {
|
|
1098
1147
|
type: "complete",
|
|
1099
1148
|
sessionId: message.session_id,
|
|
1100
1149
|
result: message.result,
|
|
1101
|
-
|
|
1102
|
-
|
|
1150
|
+
structured_output: message.structured_output,
|
|
1151
|
+
totalCost,
|
|
1152
|
+
totalTokens
|
|
1103
1153
|
};
|
|
1104
1154
|
}
|
|
1105
1155
|
}
|
|
@@ -1107,6 +1157,13 @@ var init_claude_sdk = __esm({
|
|
|
1107
1157
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1108
1158
|
if (errorMessage.includes("Cannot find module") || errorMessage.includes("MODULE_NOT_FOUND")) {
|
|
1109
1159
|
console.warn("Claude Agent SDK not installed, using simulation mode");
|
|
1160
|
+
if (typeof prompt !== "string") {
|
|
1161
|
+
yield {
|
|
1162
|
+
type: "error",
|
|
1163
|
+
error: "Streaming prompts require the real Claude Agent SDK to be installed."
|
|
1164
|
+
};
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1110
1167
|
yield* this.executeSimulatedQuery(prompt, options);
|
|
1111
1168
|
} else {
|
|
1112
1169
|
yield {
|
|
@@ -1284,7 +1341,11 @@ var init_claude_sdk = __esm({
|
|
|
1284
1341
|
this.sessionId = msg.session_id;
|
|
1285
1342
|
}
|
|
1286
1343
|
if (msg.type === "system" && msg.subtype === "init") {
|
|
1287
|
-
yield {
|
|
1344
|
+
yield {
|
|
1345
|
+
type: "session_init",
|
|
1346
|
+
sessionId: msg.session_id,
|
|
1347
|
+
slashCommands: msg.slash_commands
|
|
1348
|
+
};
|
|
1288
1349
|
} else if (msg.type === "assistant" && msg.message) {
|
|
1289
1350
|
for (const block of msg.message.content) {
|
|
1290
1351
|
if (block.type === "text") {
|
|
@@ -1712,6 +1773,13 @@ function createGeminiBackendExecutor(options) {
|
|
|
1712
1773
|
defaultModel: options.model ?? exports.DEFAULT_MODELS.gemini
|
|
1713
1774
|
});
|
|
1714
1775
|
return async function* (prompt, queryOptions) {
|
|
1776
|
+
if (typeof prompt !== "string") {
|
|
1777
|
+
yield {
|
|
1778
|
+
type: "error",
|
|
1779
|
+
error: "Gemini backend does not support streaming prompt inputs."
|
|
1780
|
+
};
|
|
1781
|
+
return;
|
|
1782
|
+
}
|
|
1715
1783
|
const geminiOptions = mapClaudeOptionsToGemini(queryOptions);
|
|
1716
1784
|
if (queryOptions.signal) {
|
|
1717
1785
|
geminiOptions.signal = queryOptions.signal;
|
|
@@ -1876,6 +1944,11 @@ var init_sandbox_logger = __esm({
|
|
|
1876
1944
|
};
|
|
1877
1945
|
}
|
|
1878
1946
|
});
|
|
1947
|
+
function getClaudeSdkOverrides(config) {
|
|
1948
|
+
const raw = config?.claudeSdkOptions;
|
|
1949
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
1950
|
+
return raw;
|
|
1951
|
+
}
|
|
1879
1952
|
exports.AgentHarness = void 0;
|
|
1880
1953
|
var init_harness = __esm({
|
|
1881
1954
|
"src/agent/harness.ts"() {
|
|
@@ -2132,6 +2205,7 @@ var init_harness = __esm({
|
|
|
2132
2205
|
yield sessionStartEvent;
|
|
2133
2206
|
yield* yieldQueuedLogs();
|
|
2134
2207
|
const assistantContent = [];
|
|
2208
|
+
let structuredOutput;
|
|
2135
2209
|
let wasAborted = false;
|
|
2136
2210
|
try {
|
|
2137
2211
|
logger3.info("execution", "Starting Claude Agent SDK query");
|
|
@@ -2222,6 +2296,10 @@ var init_harness = __esm({
|
|
|
2222
2296
|
};
|
|
2223
2297
|
writeEvent?.(toolResultEvent);
|
|
2224
2298
|
yield toolResultEvent;
|
|
2299
|
+
} else if (event.type === "complete") {
|
|
2300
|
+
if (event.structured_output !== void 0) {
|
|
2301
|
+
structuredOutput = event.structured_output;
|
|
2302
|
+
}
|
|
2225
2303
|
}
|
|
2226
2304
|
}
|
|
2227
2305
|
if (wasAborted || controller.signal.aborted) {
|
|
@@ -2273,7 +2351,8 @@ var init_harness = __esm({
|
|
|
2273
2351
|
[
|
|
2274
2352
|
{
|
|
2275
2353
|
role: "assistant",
|
|
2276
|
-
content: assistantContent
|
|
2354
|
+
content: assistantContent,
|
|
2355
|
+
...structuredOutput !== void 0 ? { metadata: { structured_output: structuredOutput } } : {}
|
|
2277
2356
|
}
|
|
2278
2357
|
]
|
|
2279
2358
|
);
|
|
@@ -2394,11 +2473,15 @@ var init_harness = __esm({
|
|
|
2394
2473
|
async *executeAgentQuery(session, prompt, options, signal, _logger) {
|
|
2395
2474
|
const sessionEnvVars = session.metadata?.envVars;
|
|
2396
2475
|
const sessionStartupScript = session.metadata?.startupScript;
|
|
2476
|
+
const sdkOverrides = getClaudeSdkOverrides(this.config.config);
|
|
2397
2477
|
const mergedEnvVars = {
|
|
2398
2478
|
MAX_THINKING_TOKENS: "1024",
|
|
2399
2479
|
...this.config.envVars,
|
|
2400
2480
|
...sessionEnvVars
|
|
2401
2481
|
};
|
|
2482
|
+
if (sdkOverrides?.enableFileCheckpointing) {
|
|
2483
|
+
mergedEnvVars.CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING = "1";
|
|
2484
|
+
}
|
|
2402
2485
|
const hasEnvVars = Object.keys(mergedEnvVars).length > 0;
|
|
2403
2486
|
const startupScript = sessionStartupScript ?? this.config.startupScript;
|
|
2404
2487
|
const queryOptions = {
|
|
@@ -2407,7 +2490,9 @@ var init_harness = __esm({
|
|
|
2407
2490
|
disallowedTools: this.config.disallowedTools,
|
|
2408
2491
|
permissionMode: this.config.permissionMode,
|
|
2409
2492
|
maxTurns: this.config.maxTurns,
|
|
2493
|
+
outputFormat: options.outputFormat,
|
|
2410
2494
|
mcpServers: this.config.mcpServers,
|
|
2495
|
+
settingSources: this.config.settingSources ?? ["project"],
|
|
2411
2496
|
// Pass the harness session ID for sandbox caching
|
|
2412
2497
|
harnessSessionId: session.id,
|
|
2413
2498
|
// Pass environment and startup configuration
|
|
@@ -2416,6 +2501,17 @@ var init_harness = __esm({
|
|
|
2416
2501
|
// Pass config file URL for cloud-hosted .claude directory (downloaded in sandbox)
|
|
2417
2502
|
...this.config.configFileUrl && { configFileUrl: this.config.configFileUrl }
|
|
2418
2503
|
};
|
|
2504
|
+
if (sdkOverrides) {
|
|
2505
|
+
if (sdkOverrides.hooks) queryOptions.hooks = sdkOverrides.hooks;
|
|
2506
|
+
if (typeof sdkOverrides.enableFileCheckpointing === "boolean") {
|
|
2507
|
+
queryOptions.enableFileCheckpointing = sdkOverrides.enableFileCheckpointing;
|
|
2508
|
+
if (sdkOverrides.enableFileCheckpointing && !sdkOverrides.extraArgs) {
|
|
2509
|
+
queryOptions.extraArgs = { "replay-user-messages": null };
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
if (sdkOverrides.extraArgs) queryOptions.extraArgs = sdkOverrides.extraArgs;
|
|
2513
|
+
if (sdkOverrides.permissionMode) queryOptions.permissionMode = sdkOverrides.permissionMode;
|
|
2514
|
+
}
|
|
2419
2515
|
const sessionApiKey = this.sessionApiKeys.get(session.id);
|
|
2420
2516
|
if (sessionApiKey) {
|
|
2421
2517
|
queryOptions.apiKey = sessionApiKey;
|
|
@@ -2451,7 +2547,9 @@ ${prompt}`;
|
|
|
2451
2547
|
const skillsDir = this.sessionSkillDirs.get(session.id);
|
|
2452
2548
|
if (skillsDir) {
|
|
2453
2549
|
queryOptions.cwd = skillsDir;
|
|
2454
|
-
queryOptions.settingSources
|
|
2550
|
+
if (queryOptions.settingSources === void 0) {
|
|
2551
|
+
queryOptions.settingSources = ["project"];
|
|
2552
|
+
}
|
|
2455
2553
|
if (queryOptions.allowedTools && !queryOptions.allowedTools.includes("Skill")) {
|
|
2456
2554
|
queryOptions.allowedTools = [...queryOptions.allowedTools, "Skill"];
|
|
2457
2555
|
}
|
|
@@ -2480,6 +2578,7 @@ ${prompt}`;
|
|
|
2480
2578
|
await this.sessionManager.updateSession(session.id, {
|
|
2481
2579
|
sdkSessionId: event.sessionId
|
|
2482
2580
|
});
|
|
2581
|
+
yield { type: "session_init", sessionId: event.sessionId, slashCommands: event.slashCommands };
|
|
2483
2582
|
} else if (event.type === "text_delta" && event.delta) {
|
|
2484
2583
|
yield { type: "text_delta", delta: event.delta };
|
|
2485
2584
|
} else if (event.type === "thinking_delta" && event.delta) {
|
|
@@ -2500,6 +2599,11 @@ ${prompt}`;
|
|
|
2500
2599
|
content: event.content,
|
|
2501
2600
|
isError: event.isError
|
|
2502
2601
|
};
|
|
2602
|
+
} else if (event.type === "complete") {
|
|
2603
|
+
yield {
|
|
2604
|
+
type: "complete",
|
|
2605
|
+
structured_output: event.structured_output
|
|
2606
|
+
};
|
|
2503
2607
|
} else if (event.type === "error") {
|
|
2504
2608
|
throw new Error(event.error ?? "Unknown error from Claude SDK");
|
|
2505
2609
|
}
|
|
@@ -4641,6 +4745,12 @@ echo "[warmup] Warmup complete!"
|
|
|
4641
4745
|
startPromise = null;
|
|
4642
4746
|
/** Registered warmup specs by tag (e.g. agentId -> spec) */
|
|
4643
4747
|
warmupSpecs = /* @__PURE__ */ new Map();
|
|
4748
|
+
/** Tags currently being warmed (prevents duplicate warming of same spec) */
|
|
4749
|
+
warmingTags = /* @__PURE__ */ new Set();
|
|
4750
|
+
/** Max warmup specs to keep (LRU eviction above this) */
|
|
4751
|
+
static MAX_SPECS = 10;
|
|
4752
|
+
/** Timeout for spec setup in ms (prevents hanging S3 pulls / install.sh) */
|
|
4753
|
+
static SPEC_SETUP_TIMEOUT_MS = 12e4;
|
|
4644
4754
|
/** Consecutive warmup failure count (reset on success) */
|
|
4645
4755
|
consecutiveFailures = 0;
|
|
4646
4756
|
/** Timestamp of last warmup attempt — used for backoff */
|
|
@@ -4653,7 +4763,7 @@ echo "[warmup] Warmup complete!"
|
|
|
4653
4763
|
this.config = {
|
|
4654
4764
|
minPoolSize: config.minPoolSize ?? parseInt(process.env.SANDBOX_POOL_MIN_SIZE ?? "2"),
|
|
4655
4765
|
maxPoolSize: config.maxPoolSize ?? parseInt(process.env.SANDBOX_POOL_MAX_SIZE ?? "5"),
|
|
4656
|
-
sandboxTimeout: config.sandboxTimeout ?? parseInt(process.env.SANDBOX_TIMEOUT ?? "
|
|
4766
|
+
sandboxTimeout: config.sandboxTimeout ?? parseInt(process.env.SANDBOX_TIMEOUT ?? "300"),
|
|
4657
4767
|
expiryThresholdMs: config.expiryThresholdMs ?? parseInt(process.env.SANDBOX_EXPIRY_THRESHOLD_MS ?? "120000"),
|
|
4658
4768
|
maintenanceIntervalMs: config.maintenanceIntervalMs ?? parseInt(process.env.SANDBOX_POOL_MAINTENANCE_MS ?? "30000"),
|
|
4659
4769
|
runtime: config.runtime ?? "node22",
|
|
@@ -4859,18 +4969,28 @@ echo "[warmup] Warmup complete!"
|
|
|
4859
4969
|
}
|
|
4860
4970
|
/**
|
|
4861
4971
|
* Register a warmup spec so the pool can pre-warm agent-specific sandboxes.
|
|
4862
|
-
* If a spec with the same tag already exists,
|
|
4972
|
+
* If a spec with the same tag and configHash already exists, only updates priority (skip #7).
|
|
4973
|
+
* Evicts lowest-priority specs when exceeding MAX_SPECS (fix #2).
|
|
4863
4974
|
* Triggers replenishment to warm a sandbox for this spec.
|
|
4864
4975
|
*/
|
|
4865
4976
|
registerWarmupSpec(spec) {
|
|
4866
|
-
const
|
|
4977
|
+
const existing = this.warmupSpecs.get(spec.tag);
|
|
4978
|
+
if (existing && spec.configHash && existing.configHash === spec.configHash) {
|
|
4979
|
+
existing.priority = spec.priority;
|
|
4980
|
+
return;
|
|
4981
|
+
}
|
|
4982
|
+
const isNew = !existing;
|
|
4867
4983
|
this.warmupSpecs.set(spec.tag, spec);
|
|
4868
|
-
|
|
4984
|
+
if (this.warmupSpecs.size > _SandboxPool.MAX_SPECS) {
|
|
4985
|
+
this.evictLowestPrioritySpecs();
|
|
4986
|
+
}
|
|
4987
|
+
console.log(`[POOL] ${isNew ? "Registered" : "Updated"} warmup spec: ${spec.tag} (priority=${spec.priority}, specs=${this.warmupSpecs.size})`);
|
|
4869
4988
|
this.emitMetric("spec_registered", {
|
|
4870
4989
|
tag: spec.tag,
|
|
4871
4990
|
priority: spec.priority,
|
|
4872
4991
|
isNew,
|
|
4873
|
-
totalSpecs: this.warmupSpecs.size
|
|
4992
|
+
totalSpecs: this.warmupSpecs.size,
|
|
4993
|
+
configHash: spec.configHash
|
|
4874
4994
|
});
|
|
4875
4995
|
if (this.running) {
|
|
4876
4996
|
this.triggerReplenishment();
|
|
@@ -4997,20 +5117,41 @@ echo "[warmup] Warmup complete!"
|
|
|
4997
5117
|
}
|
|
4998
5118
|
let warmupTag;
|
|
4999
5119
|
let agentSetupComplete = false;
|
|
5120
|
+
let warmupInstallRan = false;
|
|
5121
|
+
let warmupStartupRan = false;
|
|
5000
5122
|
if (spec) {
|
|
5123
|
+
this.warmingTags.add(spec.tag);
|
|
5001
5124
|
console.log(`[POOL] Running spec setup for tag=${spec.tag} on sandbox ${sandbox.sandboxId}...`);
|
|
5002
5125
|
this.emitMetric("spec_setup_started", { tag: spec.tag, sandboxId: sandbox.sandboxId });
|
|
5003
5126
|
const specStartTime = Date.now();
|
|
5004
5127
|
try {
|
|
5005
|
-
await
|
|
5128
|
+
const setupResult = await new Promise((resolve3, reject) => {
|
|
5129
|
+
const timer = setTimeout(
|
|
5130
|
+
() => reject(new Error(`Spec setup timed out after ${_SandboxPool.SPEC_SETUP_TIMEOUT_MS / 1e3}s`)),
|
|
5131
|
+
_SandboxPool.SPEC_SETUP_TIMEOUT_MS
|
|
5132
|
+
);
|
|
5133
|
+
spec.setup(sandbox).then((result) => {
|
|
5134
|
+
clearTimeout(timer);
|
|
5135
|
+
resolve3(result);
|
|
5136
|
+
}).catch((error) => {
|
|
5137
|
+
clearTimeout(timer);
|
|
5138
|
+
reject(error);
|
|
5139
|
+
});
|
|
5140
|
+
});
|
|
5006
5141
|
warmupTag = spec.tag;
|
|
5007
5142
|
agentSetupComplete = true;
|
|
5143
|
+
if (setupResult && typeof setupResult === "object") {
|
|
5144
|
+
warmupInstallRan = setupResult.installRan === true;
|
|
5145
|
+
warmupStartupRan = setupResult.startupRan === true;
|
|
5146
|
+
}
|
|
5008
5147
|
const specDuration = Date.now() - specStartTime;
|
|
5009
|
-
console.log(`[POOL] Spec setup completed for tag=${spec.tag} on sandbox ${sandbox.sandboxId} (${specDuration}ms)`);
|
|
5148
|
+
console.log(`[POOL] Spec setup completed for tag=${spec.tag} on sandbox ${sandbox.sandboxId} (${specDuration}ms, install=${warmupInstallRan}, startup=${warmupStartupRan})`);
|
|
5010
5149
|
this.emitMetric("spec_setup_completed", {
|
|
5011
5150
|
tag: spec.tag,
|
|
5012
5151
|
sandboxId: sandbox.sandboxId,
|
|
5013
|
-
durationMs: specDuration
|
|
5152
|
+
durationMs: specDuration,
|
|
5153
|
+
warmupInstallRan,
|
|
5154
|
+
warmupStartupRan
|
|
5014
5155
|
});
|
|
5015
5156
|
} catch (specError) {
|
|
5016
5157
|
const specDuration = Date.now() - specStartTime;
|
|
@@ -5024,6 +5165,8 @@ echo "[warmup] Warmup complete!"
|
|
|
5024
5165
|
durationMs: specDuration,
|
|
5025
5166
|
error: specErrorMessage
|
|
5026
5167
|
});
|
|
5168
|
+
} finally {
|
|
5169
|
+
this.warmingTags.delete(spec.tag);
|
|
5027
5170
|
}
|
|
5028
5171
|
}
|
|
5029
5172
|
const warmupTime = Date.now() - startTime;
|
|
@@ -5037,7 +5180,9 @@ echo "[warmup] Warmup complete!"
|
|
|
5037
5180
|
eligible: true,
|
|
5038
5181
|
lastHeartbeat: now,
|
|
5039
5182
|
warmupTag,
|
|
5040
|
-
agentSetupComplete
|
|
5183
|
+
agentSetupComplete,
|
|
5184
|
+
warmupInstallRan,
|
|
5185
|
+
warmupStartupRan
|
|
5041
5186
|
};
|
|
5042
5187
|
const tagInfo = warmupTag ? ` [tag=${warmupTag}]` : "";
|
|
5043
5188
|
console.log(`[POOL] Warmup completed for ${sandbox.sandboxId} (took ${warmupTime}ms)${useTarball ? " [tarball]" : ""}${tagInfo}`);
|
|
@@ -5239,15 +5384,20 @@ echo "[warmup] Warmup complete!"
|
|
|
5239
5384
|
}
|
|
5240
5385
|
/**
|
|
5241
5386
|
* Decide which specs to apply to new sandboxes during replenishment.
|
|
5242
|
-
* Strategy:
|
|
5387
|
+
* Strategy:
|
|
5388
|
+
* - Always reserve at least 1 slot for generic (fix #3)
|
|
5389
|
+
* - Cover uncovered specs first (highest priority), skipping in-flight tags (fix #4)
|
|
5390
|
+
* - Fill remaining as generic
|
|
5243
5391
|
* Returns an array of length `needed`, where each element is a spec or undefined (generic).
|
|
5244
5392
|
*/
|
|
5245
5393
|
selectSpecsForReplenishment(needed) {
|
|
5246
|
-
if (this.warmupSpecs.size === 0) {
|
|
5394
|
+
if (this.warmupSpecs.size === 0 || needed === 0) {
|
|
5247
5395
|
return new Array(needed).fill(void 0);
|
|
5248
5396
|
}
|
|
5397
|
+
const maxTaggedSlots = Math.max(0, needed - 1);
|
|
5249
5398
|
const uncoveredSpecs = [];
|
|
5250
5399
|
for (const spec of this.warmupSpecs.values()) {
|
|
5400
|
+
if (this.warmingTags.has(spec.tag)) continue;
|
|
5251
5401
|
let hasCoverage = false;
|
|
5252
5402
|
for (const pooled of this.pool.values()) {
|
|
5253
5403
|
if (pooled.warmupTag === spec.tag && pooled.eligible && !pooled.assignedTo) {
|
|
@@ -5262,7 +5412,7 @@ echo "[warmup] Warmup complete!"
|
|
|
5262
5412
|
uncoveredSpecs.sort((a, b) => b.priority - a.priority);
|
|
5263
5413
|
const assignments = [];
|
|
5264
5414
|
for (const spec of uncoveredSpecs) {
|
|
5265
|
-
if (assignments.length >=
|
|
5415
|
+
if (assignments.length >= maxTaggedSlots) break;
|
|
5266
5416
|
assignments.push(spec);
|
|
5267
5417
|
}
|
|
5268
5418
|
while (assignments.length < needed) {
|
|
@@ -5270,6 +5420,27 @@ echo "[warmup] Warmup complete!"
|
|
|
5270
5420
|
}
|
|
5271
5421
|
return assignments;
|
|
5272
5422
|
}
|
|
5423
|
+
/**
|
|
5424
|
+
* Evict lowest-priority specs when over MAX_SPECS capacity (fix #2).
|
|
5425
|
+
*/
|
|
5426
|
+
evictLowestPrioritySpecs() {
|
|
5427
|
+
while (this.warmupSpecs.size > _SandboxPool.MAX_SPECS) {
|
|
5428
|
+
let lowestTag;
|
|
5429
|
+
let lowestPriority = Infinity;
|
|
5430
|
+
for (const [tag, spec] of this.warmupSpecs.entries()) {
|
|
5431
|
+
if (spec.priority < lowestPriority) {
|
|
5432
|
+
lowestPriority = spec.priority;
|
|
5433
|
+
lowestTag = tag;
|
|
5434
|
+
}
|
|
5435
|
+
}
|
|
5436
|
+
if (lowestTag) {
|
|
5437
|
+
this.warmupSpecs.delete(lowestTag);
|
|
5438
|
+
console.log(`[POOL] Evicted warmup spec: ${lowestTag} (priority=${lowestPriority}, specs=${this.warmupSpecs.size})`);
|
|
5439
|
+
} else {
|
|
5440
|
+
break;
|
|
5441
|
+
}
|
|
5442
|
+
}
|
|
5443
|
+
}
|
|
5273
5444
|
/**
|
|
5274
5445
|
* Destroy a sandbox and clean up
|
|
5275
5446
|
*/
|
|
@@ -5589,6 +5760,8 @@ async function getOrCreateSandbox(options) {
|
|
|
5589
5760
|
const pooled = await pool.acquire(sessionId, preferTag);
|
|
5590
5761
|
const tagInfo = pooled.warmupTag ? ` [tag=${pooled.warmupTag}, agentSetup=${pooled.agentSetupComplete}]` : "";
|
|
5591
5762
|
console.log(`[SANDBOX] Acquired pre-warmed sandbox: ${pooled.sandboxId}${tagInfo}`);
|
|
5763
|
+
const installDone = pooled.warmupInstallRan === true;
|
|
5764
|
+
const startupDone = pooled.warmupStartupRan === true;
|
|
5592
5765
|
const agentSetupDone = pooled.agentSetupComplete === true;
|
|
5593
5766
|
const now2 = Date.now();
|
|
5594
5767
|
const entry2 = {
|
|
@@ -5597,24 +5770,26 @@ async function getOrCreateSandbox(options) {
|
|
|
5597
5770
|
createdAt: pooled.createdAt,
|
|
5598
5771
|
lastUsedAt: now2,
|
|
5599
5772
|
sdkInstalled: pooled.sdkInstalled,
|
|
5600
|
-
startupScriptRan:
|
|
5601
|
-
installScriptRan:
|
|
5773
|
+
startupScriptRan: startupDone,
|
|
5774
|
+
installScriptRan: installDone
|
|
5602
5775
|
};
|
|
5603
5776
|
sandboxCache.set(sessionId, entry2);
|
|
5604
5777
|
return {
|
|
5605
5778
|
sandbox: pooled.sandbox,
|
|
5606
5779
|
sandboxId: pooled.sandboxId,
|
|
5607
5780
|
sdkInstalled: pooled.sdkInstalled,
|
|
5608
|
-
startupScriptRan:
|
|
5781
|
+
startupScriptRan: startupDone,
|
|
5609
5782
|
startupScriptHash: void 0,
|
|
5610
|
-
installScriptRan:
|
|
5783
|
+
installScriptRan: installDone,
|
|
5611
5784
|
installScriptHash: void 0,
|
|
5612
5785
|
isNew: false,
|
|
5613
5786
|
// Not new - came from pool
|
|
5614
5787
|
configFileUrl: void 0,
|
|
5615
5788
|
configInstalledAt: agentSetupDone ? now2 : void 0,
|
|
5616
5789
|
warmupTag: pooled.warmupTag,
|
|
5617
|
-
agentSetupComplete: pooled.agentSetupComplete
|
|
5790
|
+
agentSetupComplete: pooled.agentSetupComplete,
|
|
5791
|
+
warmupInstallRan: pooled.warmupInstallRan,
|
|
5792
|
+
warmupStartupRan: pooled.warmupStartupRan
|
|
5618
5793
|
};
|
|
5619
5794
|
} catch (error) {
|
|
5620
5795
|
console.warn(
|
|
@@ -5959,6 +6134,13 @@ function createVercelSandboxExecutor(apiKey) {
|
|
|
5959
6134
|
};
|
|
5960
6135
|
}
|
|
5961
6136
|
async function* executeInSandbox(prompt, apiKey, options) {
|
|
6137
|
+
if (typeof prompt !== "string") {
|
|
6138
|
+
yield {
|
|
6139
|
+
type: "error",
|
|
6140
|
+
error: "Vercel sandbox executor does not support streaming prompt inputs."
|
|
6141
|
+
};
|
|
6142
|
+
return;
|
|
6143
|
+
}
|
|
5962
6144
|
const sessionId = options.harnessSessionId || `temp-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
5963
6145
|
try {
|
|
5964
6146
|
const { sandbox, sdkInstalled, startupScriptRan, startupScriptHash: cachedScriptHash, configFileUrl: cachedConfigUrl } = await getOrCreateSandbox({
|
|
@@ -6100,9 +6282,13 @@ async function* executeInSandbox(prompt, apiKey, options) {
|
|
|
6100
6282
|
permissionMode: options.permissionMode || "bypassPermissions",
|
|
6101
6283
|
includePartialMessages: true
|
|
6102
6284
|
};
|
|
6285
|
+
if (options.settingSources !== void 0) {
|
|
6286
|
+
sdkOptions.settingSources = options.settingSources;
|
|
6287
|
+
} else {
|
|
6288
|
+
sdkOptions.settingSources = ["project"];
|
|
6289
|
+
}
|
|
6103
6290
|
const hasConfig = options.configFileUrl || cachedConfigUrl;
|
|
6104
6291
|
if (hasConfig) {
|
|
6105
|
-
sdkOptions.settingSources = ["project"];
|
|
6106
6292
|
if (options.allowedTools && options.allowedTools.length > 0) {
|
|
6107
6293
|
const allowedTools = [...options.allowedTools];
|
|
6108
6294
|
if (!allowedTools.includes("Skill")) {
|
|
@@ -6138,12 +6324,47 @@ async function* executeInSandbox(prompt, apiKey, options) {
|
|
|
6138
6324
|
if (options.resume) {
|
|
6139
6325
|
sdkOptions.resume = options.resume;
|
|
6140
6326
|
}
|
|
6327
|
+
try {
|
|
6328
|
+
const pluginFindResult = await sandbox.runCommand({
|
|
6329
|
+
cmd: "bash",
|
|
6330
|
+
args: [
|
|
6331
|
+
"-c",
|
|
6332
|
+
[
|
|
6333
|
+
'for base in ".claude/plugins" "$HOME/.claude/plugins"; do',
|
|
6334
|
+
' if [ -d "$base" ]; then',
|
|
6335
|
+
' find "$base" -type f -path "*/.claude-plugin/plugin.json" -print',
|
|
6336
|
+
" fi",
|
|
6337
|
+
"done | sed 's#/.claude-plugin/plugin.json$##'"
|
|
6338
|
+
].join("\n")
|
|
6339
|
+
]
|
|
6340
|
+
});
|
|
6341
|
+
const rawPluginRoots = (await pluginFindResult.stdout()).trim();
|
|
6342
|
+
const pluginRoots = rawPluginRoots ? rawPluginRoots.split("\n").map((line) => line.trim()).filter(Boolean) : [];
|
|
6343
|
+
const uniquePluginRoots = Array.from(new Set(pluginRoots));
|
|
6344
|
+
if (uniquePluginRoots.length > 0) {
|
|
6345
|
+
sdkOptions.plugins = uniquePluginRoots.map((path15) => ({ type: "local", path: path15 }));
|
|
6346
|
+
console.log("[SANDBOX] Plugins detected:", uniquePluginRoots);
|
|
6347
|
+
}
|
|
6348
|
+
} catch (pluginError) {
|
|
6349
|
+
console.warn("[SANDBOX] Failed to detect plugins:", pluginError);
|
|
6350
|
+
}
|
|
6141
6351
|
const agentScript = `
|
|
6142
6352
|
const { query } = require('@anthropic-ai/claude-agent-sdk');
|
|
6353
|
+
const fs = require('fs');
|
|
6143
6354
|
|
|
6144
6355
|
const prompt = ${JSON.stringify(prompt)};
|
|
6145
6356
|
const options = ${JSON.stringify(sdkOptions)};
|
|
6146
6357
|
|
|
6358
|
+
// Enable subagents if .claude/agents exists (requires Task tool)
|
|
6359
|
+
if (fs.existsSync('.claude/agents')) {
|
|
6360
|
+
if (!Array.isArray(options.allowedTools)) {
|
|
6361
|
+
options.allowedTools = [];
|
|
6362
|
+
}
|
|
6363
|
+
if (!options.allowedTools.includes('Task')) {
|
|
6364
|
+
options.allowedTools.push('Task');
|
|
6365
|
+
}
|
|
6366
|
+
}
|
|
6367
|
+
|
|
6147
6368
|
let queryCompleted = false;
|
|
6148
6369
|
|
|
6149
6370
|
async function run() {
|
|
@@ -6240,7 +6461,11 @@ SCRIPT_EOF`]
|
|
|
6240
6461
|
});
|
|
6241
6462
|
}
|
|
6242
6463
|
if (event.type === "system" && event.subtype === "init") {
|
|
6243
|
-
events.push({
|
|
6464
|
+
events.push({
|
|
6465
|
+
type: "session_init",
|
|
6466
|
+
sessionId: event.session_id || "",
|
|
6467
|
+
slashCommands: event.slash_commands
|
|
6468
|
+
});
|
|
6244
6469
|
} else if (event.type === "stream_event" && event.event) {
|
|
6245
6470
|
const streamEvent = event.event;
|
|
6246
6471
|
if (streamEvent.type === "content_block_delta") {
|
|
@@ -6275,12 +6500,16 @@ SCRIPT_EOF`]
|
|
|
6275
6500
|
}
|
|
6276
6501
|
}
|
|
6277
6502
|
} else if (event.type === "result") {
|
|
6503
|
+
const usage = event.usage;
|
|
6504
|
+
const hasUsageTokens = !!usage && (usage.input_tokens !== void 0 || usage.output_tokens !== void 0);
|
|
6505
|
+
const totalTokens = hasUsageTokens ? (usage?.input_tokens ?? 0) + (usage?.output_tokens ?? 0) : event.total_tokens ?? (event.tokens ? event.tokens.input + event.tokens.output : void 0);
|
|
6506
|
+
const totalCost = usage?.total_cost_usd ?? event.total_cost_usd ?? event.cost ?? event.total_cost;
|
|
6278
6507
|
events.push({
|
|
6279
6508
|
type: "complete",
|
|
6280
6509
|
sessionId: event.session_id,
|
|
6281
6510
|
result: event.result,
|
|
6282
|
-
totalCost
|
|
6283
|
-
totalTokens
|
|
6511
|
+
totalCost,
|
|
6512
|
+
totalTokens
|
|
6284
6513
|
});
|
|
6285
6514
|
}
|
|
6286
6515
|
return events;
|
|
@@ -12859,6 +13088,7 @@ __export(schemas_exports, {
|
|
|
12859
13088
|
RunAgentRequestSchema: () => RunAgentRequestSchema,
|
|
12860
13089
|
SendMessageRequestSchema: () => SendMessageRequestSchema,
|
|
12861
13090
|
SessionEndEventSchema: () => SessionEndEventSchema,
|
|
13091
|
+
SessionInitEventSchema: () => SessionInitEventSchema,
|
|
12862
13092
|
SessionSchema: () => SessionSchema,
|
|
12863
13093
|
SessionStartEventSchema: () => SessionStartEventSchema,
|
|
12864
13094
|
SessionStatusSchema: () => SessionStatusSchema,
|
|
@@ -12878,7 +13108,7 @@ __export(schemas_exports, {
|
|
|
12878
13108
|
TurnCompleteEventSchema: () => TurnCompleteEventSchema,
|
|
12879
13109
|
UpdateAgentRequestSchema: () => UpdateAgentRequestSchema
|
|
12880
13110
|
});
|
|
12881
|
-
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;
|
|
13111
|
+
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;
|
|
12882
13112
|
var init_schemas = __esm({
|
|
12883
13113
|
"src/server/openapi/schemas.ts"() {
|
|
12884
13114
|
init_dist3();
|
|
@@ -13104,6 +13334,15 @@ var init_schemas = __esm({
|
|
|
13104
13334
|
sessionId: zod.z.string().openapi({ description: "Session ID" }),
|
|
13105
13335
|
claudeSessionId: zod.z.string().openapi({ description: "Claude SDK session ID" })
|
|
13106
13336
|
}).openapi("SessionStartEvent");
|
|
13337
|
+
SessionInitEventSchema = zod.z.object({
|
|
13338
|
+
type: zod.z.literal("session_init"),
|
|
13339
|
+
sessionId: zod.z.string().openapi({ description: "Claude SDK session ID" }),
|
|
13340
|
+
slashCommands: zod.z.array(zod.z.object({
|
|
13341
|
+
name: zod.z.string().openapi({ description: "Slash command name" }),
|
|
13342
|
+
description: zod.z.string().optional().openapi({ description: "Slash command description" }),
|
|
13343
|
+
prompt: zod.z.string().optional().openapi({ description: "Slash command prompt" })
|
|
13344
|
+
})).optional().openapi({ description: "Slash commands advertised by the SDK" })
|
|
13345
|
+
}).openapi("SessionInitEvent");
|
|
13107
13346
|
TextDeltaEventSchema = zod.z.object({
|
|
13108
13347
|
type: zod.z.literal("text_delta"),
|
|
13109
13348
|
delta: zod.z.string().openapi({ description: "Text chunk", example: "Hello" })
|
|
@@ -13430,6 +13669,7 @@ var init_sessions2 = __esm({
|
|
|
13430
13669
|
The stream emits the following event types:
|
|
13431
13670
|
|
|
13432
13671
|
- \`session_start\` - Session started, includes sessionId and sdkSessionId
|
|
13672
|
+
- \`session_init\` - Claude SDK session initialized (includes slash commands)
|
|
13433
13673
|
- \`text_delta\` - Text chunk being generated
|
|
13434
13674
|
- \`thinking_delta\` - Thinking/reasoning text chunk
|
|
13435
13675
|
- \`message\` - Complete message saved to storage
|
|
@@ -13878,6 +14118,7 @@ This is a convenience endpoint that combines session creation and message sendin
|
|
|
13878
14118
|
The stream emits the following event types:
|
|
13879
14119
|
|
|
13880
14120
|
- \`session_start\` - Session started
|
|
14121
|
+
- \`session_init\` - Claude SDK session initialized (includes slash commands)
|
|
13881
14122
|
- \`text_delta\` - Text chunk being generated
|
|
13882
14123
|
- \`thinking_delta\` - Thinking/reasoning text chunk
|
|
13883
14124
|
- \`message\` - Complete message saved
|