@stigmer/runner 3.12.2 → 3.12.4
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/.build-fingerprint +1 -1
- package/dist/activities/call-http.js +12 -0
- package/dist/activities/call-http.js.map +1 -1
- package/dist/activities/call-llm.d.ts +18 -0
- package/dist/activities/call-llm.js +56 -2
- package/dist/activities/call-llm.js.map +1 -1
- package/dist/activities/execute-cursor/agent-session-cache.d.ts +72 -0
- package/dist/activities/execute-cursor/agent-session-cache.js +186 -0
- package/dist/activities/execute-cursor/agent-session-cache.js.map +1 -0
- package/dist/activities/execute-cursor/index.js +61 -28
- package/dist/activities/execute-cursor/index.js.map +1 -1
- package/dist/activities/execute-cursor/service-tier.d.ts +5 -15
- package/dist/activities/execute-cursor/service-tier.js +5 -21
- package/dist/activities/execute-cursor/service-tier.js.map +1 -1
- package/dist/activities/execute-cursor/skill-resolver.d.ts +15 -0
- package/dist/activities/execute-cursor/skill-resolver.js +45 -7
- package/dist/activities/execute-cursor/skill-resolver.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +9 -1
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +13 -2
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -1
- package/dist/activities/execute-deep-agent/setup.js +14 -0
- package/dist/activities/execute-deep-agent/setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/shell-env.d.ts +8 -5
- package/dist/activities/execute-deep-agent/shell-env.js +10 -7
- package/dist/activities/execute-deep-agent/shell-env.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +5 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.js +9 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
- package/dist/client/stigmer-client.d.ts +9 -1
- package/dist/client/stigmer-client.js +10 -0
- package/dist/client/stigmer-client.js.map +1 -1
- package/dist/config.js +10 -5
- package/dist/config.js.map +1 -1
- package/dist/encryption/config.js +7 -2
- package/dist/encryption/config.js.map +1 -1
- package/dist/main.js +12 -6
- package/dist/main.js.map +1 -1
- package/dist/middleware/index.d.ts +6 -5
- package/dist/middleware/index.js +8 -5
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/tool-intent.d.ts +57 -0
- package/dist/middleware/tool-intent.js +152 -0
- package/dist/middleware/tool-intent.js.map +1 -0
- package/dist/payload-codecs.js +2 -1
- package/dist/payload-codecs.js.map +1 -1
- package/dist/runner-manager.js +20 -7
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner.js +19 -6
- package/dist/runner.js.map +1 -1
- package/dist/shared/fingerprint-secret.d.ts +3 -2
- package/dist/shared/fingerprint-secret.js +5 -3
- package/dist/shared/fingerprint-secret.js.map +1 -1
- package/dist/shared/llm-backend.js +8 -1
- package/dist/shared/llm-backend.js.map +1 -1
- package/dist/shared/model-client.d.ts +15 -0
- package/dist/shared/model-client.js +57 -13
- package/dist/shared/model-client.js.map +1 -1
- package/dist/shared/registry-endpoint.d.ts +5 -0
- package/dist/shared/registry-endpoint.js +7 -1
- package/dist/shared/registry-endpoint.js.map +1 -1
- package/dist/shared/runner-credential-keys.d.ts +26 -1
- package/dist/shared/runner-credential-keys.js +34 -1
- package/dist/shared/runner-credential-keys.js.map +1 -1
- package/dist/shared/runner-credential-store.d.ts +77 -0
- package/dist/shared/runner-credential-store.js +111 -0
- package/dist/shared/runner-credential-store.js.map +1 -0
- package/dist/shared/service-tier.d.ts +55 -0
- package/dist/shared/service-tier.js +67 -0
- package/dist/shared/service-tier.js.map +1 -0
- package/dist/shared/skill-writer.js +2 -2
- package/dist/shared/skill-writer.js.map +1 -1
- package/dist/shared/zip-extract.d.ts +10 -3
- package/dist/shared/zip-extract.js +10 -3
- package/dist/shared/zip-extract.js.map +1 -1
- package/dist/workflow-engine/tasks/call-function.d.ts +14 -0
- package/dist/workflow-engine/tasks/call-function.js +49 -5
- package/dist/workflow-engine/tasks/call-function.js.map +1 -1
- package/dist/workflow-engine/types.d.ts +6 -0
- package/dist/workflow-engine/types.js.map +1 -1
- package/dist/workflows/engine-core.js +36 -8
- package/dist/workflows/engine-core.js.map +1 -1
- package/package.json +2 -2
- package/src/activities/__tests__/call-http.test.ts +36 -0
- package/src/activities/__tests__/call-llm.test.ts +77 -0
- package/src/activities/call-http.ts +17 -0
- package/src/activities/call-llm.ts +78 -2
- package/src/activities/execute-cursor/__tests__/agent-session-cache.test.ts +220 -0
- package/src/activities/execute-cursor/__tests__/service-tier.test.ts +1 -1
- package/src/activities/execute-cursor/__tests__/skill-resolver.test.ts +104 -1
- package/src/activities/execute-cursor/agent-session-cache.ts +229 -0
- package/src/activities/execute-cursor/index.ts +66 -20
- package/src/activities/execute-cursor/service-tier.ts +5 -29
- package/src/activities/execute-cursor/skill-resolver.ts +52 -8
- package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +13 -2
- package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +21 -20
- package/src/activities/execute-deep-agent/setup.ts +15 -0
- package/src/activities/execute-deep-agent/shell-env.ts +10 -7
- package/src/activities/execute-deep-agent/subagent-wiring.ts +10 -1
- package/src/client/stigmer-client.ts +12 -1
- package/src/config.ts +10 -5
- package/src/encryption/config.ts +8 -2
- package/src/main.ts +16 -6
- package/src/middleware/__tests__/tool-intent.test.ts +266 -0
- package/src/middleware/index.ts +9 -5
- package/src/middleware/tool-intent.ts +174 -0
- package/src/payload-codecs.ts +2 -1
- package/src/runner-manager.ts +29 -6
- package/src/runner.ts +25 -6
- package/src/shared/__tests__/model-client.test.ts +99 -0
- package/src/shared/__tests__/runner-credential-store.test.ts +155 -0
- package/src/shared/__tests__/zip-extract.test.ts +46 -11
- package/src/shared/fingerprint-secret.ts +5 -3
- package/src/shared/llm-backend.ts +7 -1
- package/src/shared/model-client.ts +76 -13
- package/src/shared/registry-endpoint.ts +9 -1
- package/src/shared/runner-credential-keys.ts +36 -1
- package/src/shared/runner-credential-store.ts +115 -0
- package/src/shared/service-tier.ts +78 -0
- package/src/shared/skill-writer.ts +2 -2
- package/src/shared/zip-extract.ts +14 -7
- package/src/workflow-engine/__tests__/golden-execution.test.ts +20 -1
- package/src/workflow-engine/__tests__/tasks/call-function.test.ts +105 -0
- package/src/workflow-engine/tasks/call-function.ts +74 -13
- package/src/workflow-engine/types.ts +6 -0
- package/src/workflows/engine-core.ts +39 -8
package/dist/.build-fingerprint
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"hash":"
|
|
1
|
+
{"hash":"30fa0c9a11a788e3","builtAt":"2026-08-14T07:34:03.879Z","fileCount":297}
|
|
@@ -72,11 +72,23 @@ export async function callHttpAction(config, runtimeEnv) {
|
|
|
72
72
|
if (resolved.redirect === "false" || resolved.redirect === false) {
|
|
73
73
|
fetchOptions.redirect = "manual";
|
|
74
74
|
}
|
|
75
|
+
// Honor the author's timeout_seconds (#686). Non-retryable by design:
|
|
76
|
+
// the workflow author declared the budget, so a breach is a task failure
|
|
77
|
+
// they can catch with try/catch — Temporal re-running the same slow call
|
|
78
|
+
// up to 5 times would multiply the wait without changing the outcome.
|
|
79
|
+
const timeoutMs = resolved.timeout_seconds ? resolved.timeout_seconds * 1000 : undefined;
|
|
80
|
+
if (timeoutMs !== undefined) {
|
|
81
|
+
fetchOptions.signal = AbortSignal.timeout(timeoutMs);
|
|
82
|
+
}
|
|
75
83
|
let response;
|
|
76
84
|
try {
|
|
77
85
|
response = await fetch(uri, fetchOptions);
|
|
78
86
|
}
|
|
79
87
|
catch (err) {
|
|
88
|
+
if (timeoutMs !== undefined && err instanceof Error &&
|
|
89
|
+
(err.name === "TimeoutError" || err.name === "AbortError")) {
|
|
90
|
+
throw ApplicationFailure.nonRetryable(`HTTP ${method} ${uri} timed out after ${resolved.timeout_seconds}s (task timeout_seconds)`, "HTTP_CALL_TIMEOUT", { timeoutSeconds: resolved.timeout_seconds });
|
|
91
|
+
}
|
|
80
92
|
throw new Error(`HTTP ${method} ${uri} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
81
93
|
}
|
|
82
94
|
const statusCode = response.status;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"call-http.js","sourceRoot":"","sources":["../../src/activities/call-http.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"call-http.js","sourceRoot":"","sources":["../../src/activities/call-http.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,7 +23,25 @@ export interface LlmCallConfig {
|
|
|
23
23
|
readonly response_schema?: Record<string, unknown>;
|
|
24
24
|
readonly temperature?: number;
|
|
25
25
|
readonly max_tokens?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Author-declared call budget in seconds (proto: LlmCallTaskConfig.timeout,
|
|
28
|
+
* 1-600). Bounds the provider request via buildChatModel's timeout seam;
|
|
29
|
+
* a breach fails non-retryably with LLM_TIMEOUT (#686). The engine widens
|
|
30
|
+
* the activity's startToClose to fit values above the default 5m.
|
|
31
|
+
*/
|
|
26
32
|
readonly timeout?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Schema-validation policy (proto: LlmCallTaskConfig.on_invalid). The
|
|
35
|
+
* retry/fallback ORCHESTRATION lives in the workflow engine
|
|
36
|
+
* (call-function.ts, mirroring call-agent.ts); this activity only reads
|
|
37
|
+
* it to pick the failure channel: soft policies get a `parse_error`
|
|
38
|
+
* result the engine can act on, the default throws LLM_SCHEMA_VALIDATION.
|
|
39
|
+
*/
|
|
40
|
+
readonly on_invalid?: string;
|
|
41
|
+
/** Engine-owned (see on_invalid); accepted here so config passes through. */
|
|
42
|
+
readonly max_retries?: number;
|
|
43
|
+
/** Engine-owned (see on_invalid); accepted here so config passes through. */
|
|
44
|
+
readonly fallback_task?: string;
|
|
27
45
|
}
|
|
28
46
|
export interface LlmCallResult {
|
|
29
47
|
readonly input_tokens: number;
|
|
@@ -23,6 +23,7 @@ import { inferProvider, stripProviderPrefix, } from "../shared/llm-proxy.js";
|
|
|
23
23
|
import { computeLlmCostMicros, ensureLoaded as ensurePricingLoaded } from "../shared/model-pricing.js";
|
|
24
24
|
import { resolveToApiModelId } from "../shared/model-registry.js";
|
|
25
25
|
import { buildChatModel } from "../shared/model-client.js";
|
|
26
|
+
import { getRunnerSecret } from "../shared/runner-credential-store.js";
|
|
26
27
|
import { checkDirectCredentials } from "../shared/llm-backend.js";
|
|
27
28
|
import { classifyModelCallError } from "../shared/model-error.js";
|
|
28
29
|
/**
|
|
@@ -92,6 +93,18 @@ function classifyAndThrowLlmError(err, modelId, provider, proxyMode) {
|
|
|
92
93
|
const providerLabel = provider === "anthropic" ? "Anthropic" : "OpenAI";
|
|
93
94
|
throw ApplicationFailure.nonRetryable(`LLM call failed for model "${modelId}" (${providerLabel}): ${rawMessage}`, "LLM_UNKNOWN_ERROR");
|
|
94
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Matches the request-timeout errors the provider SDKs raise when
|
|
98
|
+
* buildChatModel's timeout bound fires (OpenAI: APIConnectionTimeoutError
|
|
99
|
+
* "Request timed out."; Anthropic mirrors the shape). Only consulted when
|
|
100
|
+
* the task declared an explicit `timeout`, so the loose message match
|
|
101
|
+
* cannot reclassify errors on unbudgeted calls.
|
|
102
|
+
*/
|
|
103
|
+
function isRequestTimeoutError(err) {
|
|
104
|
+
if (!(err instanceof Error))
|
|
105
|
+
return false;
|
|
106
|
+
return err.name === "APIConnectionTimeoutError" || /timed?\s*out/i.test(err.message);
|
|
107
|
+
}
|
|
95
108
|
export async function callLlmAction(config, runtimeEnv, executionId) {
|
|
96
109
|
if (!config.model) {
|
|
97
110
|
throw new Error("LLM call requires 'model' in config");
|
|
@@ -105,7 +118,10 @@ export async function callLlmAction(config, runtimeEnv, executionId) {
|
|
|
105
118
|
const callStart = Date.now();
|
|
106
119
|
await ensurePricingLoaded().catch(() => { });
|
|
107
120
|
const proxyEndpoint = process.env.STIGMER_PROXY_ENDPOINT;
|
|
108
|
-
|
|
121
|
+
// Per-call store read: rotation (manager updateToken / static renewal)
|
|
122
|
+
// must be visible to in-flight workers, exactly like the env read it
|
|
123
|
+
// replaced (#508).
|
|
124
|
+
const stigmerToken = getRunnerSecret("STIGMER_TOKEN");
|
|
109
125
|
const proxyActive = !!(proxyEndpoint && stigmerToken);
|
|
110
126
|
console.log(`[call-llm] model=${modelId} provider=${provider} proxy=${proxyActive} ` +
|
|
111
127
|
`structured=${!!config.response_schema} execution=${executionId}`);
|
|
@@ -123,6 +139,12 @@ export async function callLlmAction(config, runtimeEnv, executionId) {
|
|
|
123
139
|
// Anthropic requires an explicit maxTokens; preserve the 4096 default here
|
|
124
140
|
// (buildChatModel intentionally imposes none). resolvedModel is already an
|
|
125
141
|
// API id, so buildChatModel's resolve step is a no-op for it.
|
|
142
|
+
//
|
|
143
|
+
// maxRetries: 0 — Temporal owns retries for this activity (callProxy
|
|
144
|
+
// retries up to 5x). LangChain's default retry loop underneath would
|
|
145
|
+
// multiply that, and it also blind-retries schema-validation failures:
|
|
146
|
+
// before this was pinned to 0, one non-conforming structured response
|
|
147
|
+
// burned ~7 identical model calls before surfacing (#686).
|
|
126
148
|
const { model } = await buildChatModel({
|
|
127
149
|
modelName: resolvedModel,
|
|
128
150
|
proxyEndpoint: proxyActive ? proxyEndpoint : undefined,
|
|
@@ -130,12 +152,22 @@ export async function callLlmAction(config, runtimeEnv, executionId) {
|
|
|
130
152
|
headerScope: { workflowExecutionId: executionId },
|
|
131
153
|
temperature: config.temperature,
|
|
132
154
|
maxTokens: provider === "anthropic" ? (config.max_tokens ?? 4096) : config.max_tokens,
|
|
155
|
+
// The author's per-call budget rides the same seam as the operator's
|
|
156
|
+
// STIGMER_LLM_REQUEST_TIMEOUT_MS (#468); an explicit value wins there.
|
|
157
|
+
timeoutMs: config.timeout ? config.timeout * 1000 : undefined,
|
|
158
|
+
maxRetries: 0,
|
|
133
159
|
});
|
|
134
160
|
const messages = [];
|
|
135
161
|
if (config.system_prompt) {
|
|
136
162
|
messages.push(new SystemMessage(config.system_prompt));
|
|
137
163
|
}
|
|
138
164
|
messages.push(new HumanMessage(config.prompt));
|
|
165
|
+
// Soft schema-failure channel: when the engine will orchestrate
|
|
166
|
+
// ON_INVALID_RETRY / ON_INVALID_FALLBACK, a validation miss is a signal
|
|
167
|
+
// (parse_error result), not a failure — the engine re-prompts or
|
|
168
|
+
// branches. ON_INVALID_FAIL (and unset) keeps the throwing contract.
|
|
169
|
+
const softSchemaFailure = config.on_invalid === "ON_INVALID_RETRY" ||
|
|
170
|
+
config.on_invalid === "ON_INVALID_FALLBACK";
|
|
139
171
|
let result;
|
|
140
172
|
try {
|
|
141
173
|
if (config.response_schema) {
|
|
@@ -163,7 +195,29 @@ export async function callLlmAction(config, runtimeEnv, executionId) {
|
|
|
163
195
|
}
|
|
164
196
|
}
|
|
165
197
|
catch (err) {
|
|
166
|
-
|
|
198
|
+
if (softSchemaFailure && err instanceof z.ZodError) {
|
|
199
|
+
// Usage is unrecoverable here — the structured runnable throws
|
|
200
|
+
// before exposing the raw response (same loss as the throwing path).
|
|
201
|
+
result = {
|
|
202
|
+
input_tokens: 0,
|
|
203
|
+
output_tokens: 0,
|
|
204
|
+
result: undefined,
|
|
205
|
+
model: modelId,
|
|
206
|
+
provider,
|
|
207
|
+
parse_error: err.errors
|
|
208
|
+
.map((e) => `${e.path.join(".")}: ${e.message}`)
|
|
209
|
+
.join("; "),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
else if (config.timeout && isRequestTimeoutError(err)) {
|
|
213
|
+
// The author declared this budget in the task config; breaching it is
|
|
214
|
+
// a task failure they can catch, not a transient to retry — Temporal
|
|
215
|
+
// re-running the same over-budget call 5x would multiply the wait.
|
|
216
|
+
throw ApplicationFailure.nonRetryable(`LLM call for model "${modelId}" timed out after ${config.timeout}s (task timeout)`, "LLM_TIMEOUT", { timeoutSeconds: config.timeout });
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
classifyAndThrowLlmError(err, modelId, provider, proxyActive);
|
|
220
|
+
}
|
|
167
221
|
}
|
|
168
222
|
const costMicros = computeLlmCostMicros(config.model, result.input_tokens, result.output_tokens);
|
|
169
223
|
const enrichedResult = costMicros > 0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"call-llm.js","sourceRoot":"","sources":["../../src/activities/call-llm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAGvE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAElE,OAAO,EACL,aAAa,EACb,mBAAmB,GAEpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,oBAAoB,EAAE,YAAY,IAAI,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACvG,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"call-llm.js","sourceRoot":"","sources":["../../src/activities/call-llm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-keyed Cursor agent cache — keeps the SDK executor (and its stdio
|
|
3
|
+
* MCP server processes) alive across turns of the SAME session (#215).
|
|
4
|
+
*
|
|
5
|
+
* Why this exists: the SDK's local executor cache is refcounted and keyed
|
|
6
|
+
* by the full acquisition config (workingDirectory, hashed apiKey,
|
|
7
|
+
* settingSources, mcpServers, customSubagents). `agent.close()` releases
|
|
8
|
+
* the agent's lease; at refcount zero the executor is DISPOSED and every
|
|
9
|
+
* stdio MCP server is killed. The activity used to close on every terminal
|
|
10
|
+
* path, so each turn re-acquired the executor and re-spawned every MCP
|
|
11
|
+
* server inside `agent.send()` — a measured 2.2–3.2s per-turn tax
|
|
12
|
+
* (`turn_first_event`'s `send_returned` segment).
|
|
13
|
+
*
|
|
14
|
+
* Ownership model — exclusive checkout, explicit lifetime:
|
|
15
|
+
* - A finishing turn PARKS its healthy agent here (`cacheSessionAgent`);
|
|
16
|
+
* the next activity for the session CHECKS IT OUT (`takeCachedAgent`),
|
|
17
|
+
* removing it from the cache, so two concurrent activities can never
|
|
18
|
+
* share one Agent handle — the loser of the race resolves its own.
|
|
19
|
+
* - The cached agent is reused only when the acquisition FINGERPRINT
|
|
20
|
+
* matches. Any config drift (rotated credential, edited MCP servers,
|
|
21
|
+
* model change, different workspace) closes the parked agent and forces
|
|
22
|
+
* a fresh resolve — correctness by construction: a reused executor would
|
|
23
|
+
* otherwise keep serving the OLD config.
|
|
24
|
+
* - Failure paths never park: a suspect agent is closed where it failed
|
|
25
|
+
* (the pre-existing close sites), and `evictSessionAgent` clears any
|
|
26
|
+
* parked entry when a session's state is replaced out from under it.
|
|
27
|
+
* - Idle TTL + LRU cap bound memory on multi-session hosts (the desktop
|
|
28
|
+
* runner-manager hosts many sessions per process; a cloud sandbox is
|
|
29
|
+
* session-pinned and holds at most one entry). Worker shutdown closes
|
|
30
|
+
* everything (`closeAllCachedAgents`).
|
|
31
|
+
*/
|
|
32
|
+
/** The slice of SDKAgent this cache needs — close() releases the executor lease. */
|
|
33
|
+
export interface CacheableAgent {
|
|
34
|
+
readonly agentId: string;
|
|
35
|
+
close(): void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Fingerprint of everything that determines whether a parked agent can
|
|
39
|
+
* serve the next turn as-is: the SDK executor cache key inputs PLUS the
|
|
40
|
+
* per-agent options resume() re-supplies (model selection, sub-agents).
|
|
41
|
+
* The API key contributes only as a hash — the fingerprint must never be
|
|
42
|
+
* a secret-bearing value (it appears in no logs, but defense in depth).
|
|
43
|
+
*/
|
|
44
|
+
export declare function computeAgentFingerprint(createOptions: Record<string, unknown>): string;
|
|
45
|
+
/**
|
|
46
|
+
* Exclusive checkout: returns the parked agent for the session and removes
|
|
47
|
+
* it from the cache, or undefined when there is nothing reusable.
|
|
48
|
+
*
|
|
49
|
+
* A parked agent is reusable only when BOTH hold:
|
|
50
|
+
* - `fingerprint` matches (config identical to what the agent was built with);
|
|
51
|
+
* - `expectedAgentId`, when non-empty, matches the parked agent (the
|
|
52
|
+
* session's harnessStateId is the source of truth — a recovery in another
|
|
53
|
+
* activity may have replaced the agent since this one was parked).
|
|
54
|
+
*
|
|
55
|
+
* A mismatch on either closes the parked agent: it can never serve this
|
|
56
|
+
* session again, and holding it would only pin dead MCP processes.
|
|
57
|
+
*/
|
|
58
|
+
export declare function takeCachedAgent(sessionId: string, fingerprint: string, expectedAgentId: string): CacheableAgent | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Parks a healthy agent for the session's next turn. Replaces (and closes)
|
|
61
|
+
* any agent already parked for the session; evicts the oldest entry when
|
|
62
|
+
* the cap is reached.
|
|
63
|
+
*/
|
|
64
|
+
export declare function cacheSessionAgent(sessionId: string, agent: CacheableAgent, fingerprint: string): void;
|
|
65
|
+
/** Closes and forgets the parked agent for a session, if any. */
|
|
66
|
+
export declare function evictSessionAgent(sessionId: string): void;
|
|
67
|
+
/** Worker shutdown: release every parked lease so executors dispose cleanly. */
|
|
68
|
+
export declare function closeAllCachedAgents(): void;
|
|
69
|
+
/** Test seam. */
|
|
70
|
+
export declare function _resetAgentSessionCacheForTests(): void;
|
|
71
|
+
/** Test seam. */
|
|
72
|
+
export declare function _parkedAgentCountForTests(): number;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-keyed Cursor agent cache — keeps the SDK executor (and its stdio
|
|
3
|
+
* MCP server processes) alive across turns of the SAME session (#215).
|
|
4
|
+
*
|
|
5
|
+
* Why this exists: the SDK's local executor cache is refcounted and keyed
|
|
6
|
+
* by the full acquisition config (workingDirectory, hashed apiKey,
|
|
7
|
+
* settingSources, mcpServers, customSubagents). `agent.close()` releases
|
|
8
|
+
* the agent's lease; at refcount zero the executor is DISPOSED and every
|
|
9
|
+
* stdio MCP server is killed. The activity used to close on every terminal
|
|
10
|
+
* path, so each turn re-acquired the executor and re-spawned every MCP
|
|
11
|
+
* server inside `agent.send()` — a measured 2.2–3.2s per-turn tax
|
|
12
|
+
* (`turn_first_event`'s `send_returned` segment).
|
|
13
|
+
*
|
|
14
|
+
* Ownership model — exclusive checkout, explicit lifetime:
|
|
15
|
+
* - A finishing turn PARKS its healthy agent here (`cacheSessionAgent`);
|
|
16
|
+
* the next activity for the session CHECKS IT OUT (`takeCachedAgent`),
|
|
17
|
+
* removing it from the cache, so two concurrent activities can never
|
|
18
|
+
* share one Agent handle — the loser of the race resolves its own.
|
|
19
|
+
* - The cached agent is reused only when the acquisition FINGERPRINT
|
|
20
|
+
* matches. Any config drift (rotated credential, edited MCP servers,
|
|
21
|
+
* model change, different workspace) closes the parked agent and forces
|
|
22
|
+
* a fresh resolve — correctness by construction: a reused executor would
|
|
23
|
+
* otherwise keep serving the OLD config.
|
|
24
|
+
* - Failure paths never park: a suspect agent is closed where it failed
|
|
25
|
+
* (the pre-existing close sites), and `evictSessionAgent` clears any
|
|
26
|
+
* parked entry when a session's state is replaced out from under it.
|
|
27
|
+
* - Idle TTL + LRU cap bound memory on multi-session hosts (the desktop
|
|
28
|
+
* runner-manager hosts many sessions per process; a cloud sandbox is
|
|
29
|
+
* session-pinned and holds at most one entry). Worker shutdown closes
|
|
30
|
+
* everything (`closeAllCachedAgents`).
|
|
31
|
+
*/
|
|
32
|
+
import { createHash } from "node:crypto";
|
|
33
|
+
/**
|
|
34
|
+
* Default idle lifetime for a parked agent. Long enough to cover human
|
|
35
|
+
* think-time between turns and approval round-trips; short enough that an
|
|
36
|
+
* abandoned session does not pin MCP subprocesses for hours.
|
|
37
|
+
*/
|
|
38
|
+
const DEFAULT_IDLE_TTL_MS = 30 * 60 * 1000;
|
|
39
|
+
/**
|
|
40
|
+
* Ceiling on concurrently parked agents (each holds an executor + its MCP
|
|
41
|
+
* subprocesses). Cloud sandboxes never approach it (one session per pod);
|
|
42
|
+
* it protects long-lived desktop runner-managers.
|
|
43
|
+
*/
|
|
44
|
+
const MAX_PARKED_AGENTS = 32;
|
|
45
|
+
function resolveIdleTtlMs() {
|
|
46
|
+
const parsed = Number.parseInt(process.env.STIGMER_CURSOR_AGENT_CACHE_TTL_MS ?? "", 10);
|
|
47
|
+
return parsed > 0 ? parsed : DEFAULT_IDLE_TTL_MS;
|
|
48
|
+
}
|
|
49
|
+
const parkedAgents = new Map();
|
|
50
|
+
function closeQuietly(agent) {
|
|
51
|
+
try {
|
|
52
|
+
agent.close();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
/* best effort — the lease release is advisory on an already-dead agent */
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Fingerprint of everything that determines whether a parked agent can
|
|
60
|
+
* serve the next turn as-is: the SDK executor cache key inputs PLUS the
|
|
61
|
+
* per-agent options resume() re-supplies (model selection, sub-agents).
|
|
62
|
+
* The API key contributes only as a hash — the fingerprint must never be
|
|
63
|
+
* a secret-bearing value (it appears in no logs, but defense in depth).
|
|
64
|
+
*/
|
|
65
|
+
export function computeAgentFingerprint(createOptions) {
|
|
66
|
+
const { apiKey, ...rest } = createOptions;
|
|
67
|
+
const material = {
|
|
68
|
+
...rest,
|
|
69
|
+
apiKeyHash: typeof apiKey === "string" && apiKey.length > 0
|
|
70
|
+
? createHash("sha256").update(apiKey).digest("hex")
|
|
71
|
+
: undefined,
|
|
72
|
+
};
|
|
73
|
+
return createHash("sha256").update(stableStringify(material)).digest("hex");
|
|
74
|
+
}
|
|
75
|
+
/** Deterministic JSON: object keys sorted recursively (the SDK's own idiom). */
|
|
76
|
+
function stableStringify(value) {
|
|
77
|
+
return JSON.stringify(sortKeys(value));
|
|
78
|
+
}
|
|
79
|
+
function sortKeys(value) {
|
|
80
|
+
if (Array.isArray(value))
|
|
81
|
+
return value.map(sortKeys);
|
|
82
|
+
if (value !== null && typeof value === "object" && value.constructor === Object) {
|
|
83
|
+
return Object.fromEntries(Object.keys(value)
|
|
84
|
+
.sort()
|
|
85
|
+
.map((k) => [k, sortKeys(value[k])]));
|
|
86
|
+
}
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Exclusive checkout: returns the parked agent for the session and removes
|
|
91
|
+
* it from the cache, or undefined when there is nothing reusable.
|
|
92
|
+
*
|
|
93
|
+
* A parked agent is reusable only when BOTH hold:
|
|
94
|
+
* - `fingerprint` matches (config identical to what the agent was built with);
|
|
95
|
+
* - `expectedAgentId`, when non-empty, matches the parked agent (the
|
|
96
|
+
* session's harnessStateId is the source of truth — a recovery in another
|
|
97
|
+
* activity may have replaced the agent since this one was parked).
|
|
98
|
+
*
|
|
99
|
+
* A mismatch on either closes the parked agent: it can never serve this
|
|
100
|
+
* session again, and holding it would only pin dead MCP processes.
|
|
101
|
+
*/
|
|
102
|
+
export function takeCachedAgent(sessionId, fingerprint, expectedAgentId) {
|
|
103
|
+
const entry = parkedAgents.get(sessionId);
|
|
104
|
+
if (!entry)
|
|
105
|
+
return undefined;
|
|
106
|
+
parkedAgents.delete(sessionId);
|
|
107
|
+
clearTimeout(entry.evictTimer);
|
|
108
|
+
const agentMatches = expectedAgentId === "" || entry.agent.agentId === expectedAgentId;
|
|
109
|
+
if (entry.fingerprint !== fingerprint || !agentMatches) {
|
|
110
|
+
console.log(`agent-session-cache: parked agent for session=${sessionId} not reusable ` +
|
|
111
|
+
`(fingerprintMatch=${entry.fingerprint === fingerprint}, agentIdMatch=${agentMatches}) — closing`);
|
|
112
|
+
closeQuietly(entry.agent);
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
return entry.agent;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Parks a healthy agent for the session's next turn. Replaces (and closes)
|
|
119
|
+
* any agent already parked for the session; evicts the oldest entry when
|
|
120
|
+
* the cap is reached.
|
|
121
|
+
*/
|
|
122
|
+
export function cacheSessionAgent(sessionId, agent, fingerprint) {
|
|
123
|
+
if (!sessionId) {
|
|
124
|
+
// No stable key to reuse by — release the lease as before the cache.
|
|
125
|
+
closeQuietly(agent);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const displaced = parkedAgents.get(sessionId);
|
|
129
|
+
if (displaced) {
|
|
130
|
+
clearTimeout(displaced.evictTimer);
|
|
131
|
+
closeQuietly(displaced.agent);
|
|
132
|
+
}
|
|
133
|
+
if (parkedAgents.size >= MAX_PARKED_AGENTS) {
|
|
134
|
+
let oldestKey;
|
|
135
|
+
let oldestAt = Infinity;
|
|
136
|
+
for (const [key, entry] of parkedAgents) {
|
|
137
|
+
if (entry.parkedAt < oldestAt) {
|
|
138
|
+
oldestAt = entry.parkedAt;
|
|
139
|
+
oldestKey = key;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (oldestKey !== undefined)
|
|
143
|
+
evictSessionAgent(oldestKey);
|
|
144
|
+
}
|
|
145
|
+
const ttlMs = resolveIdleTtlMs();
|
|
146
|
+
const evictTimer = setTimeout(() => evictSessionAgent(sessionId), ttlMs);
|
|
147
|
+
// Never keep the process alive just to evict an idle agent.
|
|
148
|
+
evictTimer.unref?.();
|
|
149
|
+
parkedAgents.set(sessionId, {
|
|
150
|
+
agent,
|
|
151
|
+
fingerprint,
|
|
152
|
+
evictTimer,
|
|
153
|
+
parkedAt: Date.now(),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/** Closes and forgets the parked agent for a session, if any. */
|
|
157
|
+
export function evictSessionAgent(sessionId) {
|
|
158
|
+
const entry = parkedAgents.get(sessionId);
|
|
159
|
+
if (!entry)
|
|
160
|
+
return;
|
|
161
|
+
parkedAgents.delete(sessionId);
|
|
162
|
+
clearTimeout(entry.evictTimer);
|
|
163
|
+
closeQuietly(entry.agent);
|
|
164
|
+
console.log(`agent-session-cache: evicted parked agent for session=${sessionId}`);
|
|
165
|
+
}
|
|
166
|
+
/** Worker shutdown: release every parked lease so executors dispose cleanly. */
|
|
167
|
+
export function closeAllCachedAgents() {
|
|
168
|
+
for (const [sessionId, entry] of parkedAgents) {
|
|
169
|
+
clearTimeout(entry.evictTimer);
|
|
170
|
+
closeQuietly(entry.agent);
|
|
171
|
+
console.log(`agent-session-cache: closed parked agent for session=${sessionId} (shutdown)`);
|
|
172
|
+
}
|
|
173
|
+
parkedAgents.clear();
|
|
174
|
+
}
|
|
175
|
+
/** Test seam. */
|
|
176
|
+
export function _resetAgentSessionCacheForTests() {
|
|
177
|
+
for (const entry of parkedAgents.values()) {
|
|
178
|
+
clearTimeout(entry.evictTimer);
|
|
179
|
+
}
|
|
180
|
+
parkedAgents.clear();
|
|
181
|
+
}
|
|
182
|
+
/** Test seam. */
|
|
183
|
+
export function _parkedAgentCountForTests() {
|
|
184
|
+
return parkedAgents.size;
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=agent-session-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-session-cache.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/agent-session-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,6 +34,7 @@ import { ExecutionPhase, FileChangeSetStatus, InteractionMode, MessageType, Appr
|
|
|
34
34
|
import { StigmerClient } from "../../client/stigmer-client.js";
|
|
35
35
|
import { describeExecutionError } from "../../shared/model-error.js";
|
|
36
36
|
import { resolveAgentWithTransportRecovery } from "./session-lifecycle.js";
|
|
37
|
+
import { cacheSessionAgent, computeAgentFingerprint, takeCachedAgent } from "./agent-session-cache.js";
|
|
37
38
|
import { CursorMode } from "@stigmer/protos/ai/stigmer/agentic/session/v1/enum_pb";
|
|
38
39
|
import { determineCursorMode, isCloudMode } from "./cursor-mode.js";
|
|
39
40
|
import { MessageAccumulator, cancelInProgressSubAgentProtos, collapseRedundantToolCallTwins } from "./message-translator.js";
|
|
@@ -91,7 +92,8 @@ import { statusProtoWriter } from "../../shared/execution-status-writer.js";
|
|
|
91
92
|
import { setInterceptorExecutionId, runWithExecutionContext } from "./fetch-interceptor.js";
|
|
92
93
|
import { closeProxySessions } from "./http2-interceptor.js";
|
|
93
94
|
import { resolveModelId, ensureLoaded as ensurePricingLoaded } from "./model-pricing.js";
|
|
94
|
-
import { resolveEffectiveServiceTier
|
|
95
|
+
import { resolveEffectiveServiceTier } from "../../shared/service-tier.js";
|
|
96
|
+
import { resolveServiceTierParams } from "./service-tier.js";
|
|
95
97
|
import { UsageAccumulator } from "./usage-accumulator.js";
|
|
96
98
|
import { StreamingUsageSummarySchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/usage_pb";
|
|
97
99
|
import { activityStarted, activityFinished } from "../../idle-watchdog.js";
|
|
@@ -882,20 +884,47 @@ turnSeq) {
|
|
|
882
884
|
// is the largest user-visible setup segment; this split keeps its
|
|
883
885
|
// historical meaning — the SDK call was already 98%+ of it).
|
|
884
886
|
setupTiming.mark("prepare_agent");
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
887
|
+
// Phase 8a: Reuse the previous turn's agent when this session parked one
|
|
888
|
+
// (#215). A checkout hit skips Agent.resume() AND — the real win — keeps
|
|
889
|
+
// the SDK executor lease alive, so agent.send() below re-acquires the
|
|
890
|
+
// warm executor instead of re-spawning every stdio MCP server (the
|
|
891
|
+
// measured 2.2–3.2s `send_returned` tax). The fingerprint covers the
|
|
892
|
+
// full acquisition config, so any drift (rotated credential, edited MCP
|
|
893
|
+
// servers, model change) falls through to a fresh resolve.
|
|
894
|
+
const agentFingerprint = computeAgentFingerprint(createOptions);
|
|
895
|
+
const parkedAgent = takeCachedAgent(sessionId, agentFingerprint, threadId ?? "");
|
|
896
|
+
let resolution;
|
|
897
|
+
if (parkedAgent) {
|
|
898
|
+
console.log(`ExecuteCursor reusing parked session agent: execution=${executionId}, ` +
|
|
899
|
+
`session=${sessionId}, agentId=${parkedAgent.agentId}`);
|
|
900
|
+
resolution = {
|
|
901
|
+
agent: parkedAgent,
|
|
902
|
+
agentId: parkedAgent.agentId,
|
|
903
|
+
isNew: false,
|
|
904
|
+
resumed: true,
|
|
905
|
+
mode: agentMode,
|
|
906
|
+
// The parked handle IS the live conversation — every consumer of
|
|
907
|
+
// "resumed_successfully" (prompt selection, poisoned-handle
|
|
908
|
+
// recovery eligibility) wants exactly those semantics.
|
|
909
|
+
reason: "resumed_successfully",
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
else {
|
|
913
|
+
resolution = await resolveAgentWithTransportRecovery({
|
|
914
|
+
harnessStateId: threadId,
|
|
915
|
+
createOptions,
|
|
916
|
+
mode: agentMode,
|
|
917
|
+
timeoutMs: config.agentResolveTimeoutMs,
|
|
918
|
+
buildTimeoutMessage: (finalAttempt) => `Cursor agent ${threadId ? "resume" : "create"} timed out after ${resolveTimeoutSeconds}s ` +
|
|
919
|
+
`(${config.proxyEndpoint ? `via proxy ${config.proxyEndpoint}` : "direct Cursor API connection"}). ` +
|
|
920
|
+
`The transport connection is likely dead. ` +
|
|
921
|
+
(finalAttempt
|
|
922
|
+
? `An automatic retry on a fresh transport connection also timed out. ` +
|
|
923
|
+
`Retry the message later; if this persists, check proxy and network health.`
|
|
924
|
+
: `Resetting the transport and retrying automatically.`),
|
|
925
|
+
resetTransport: closeProxySessions,
|
|
926
|
+
});
|
|
927
|
+
}
|
|
899
928
|
console.log(`ExecuteCursor agent resolved: execution=${executionId}, ` +
|
|
900
929
|
`reason=${resolution.reason}, mode=${resolution.mode}, ` +
|
|
901
930
|
`agentId=${resolution.agentId}, resumed=${resolution.resumed}` +
|
|
@@ -1234,10 +1263,9 @@ turnSeq) {
|
|
|
1234
1263
|
timestamp: utcTimestamp(),
|
|
1235
1264
|
}));
|
|
1236
1265
|
await persist(status);
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
catch { /* best effort */ }
|
|
1266
|
+
// Clean terminal: the conversation continues on the next message,
|
|
1267
|
+
// so park the healthy agent for that turn (#215).
|
|
1268
|
+
cacheSessionAgent(sessionId ?? "", resolution.agent, agentFingerprint);
|
|
1241
1269
|
console.warn(`ExecuteCursor terminated (cost cap): execution=${executionId}, ` +
|
|
1242
1270
|
`estimatedCostUsd=${estimated.toFixed(4)}, maxCostUsd=${maxCostUsd.toFixed(2)}`);
|
|
1243
1271
|
return { kind: "return" };
|
|
@@ -1296,10 +1324,8 @@ turnSeq) {
|
|
|
1296
1324
|
timestamp: utcTimestamp(),
|
|
1297
1325
|
}));
|
|
1298
1326
|
await persist(status);
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
}
|
|
1302
|
-
catch { /* best effort */ }
|
|
1327
|
+
// Clean terminal — park for the session's next turn (#215).
|
|
1328
|
+
cacheSessionAgent(sessionId ?? "", resolution.agent, agentFingerprint);
|
|
1303
1329
|
console.log(`ExecuteCursor completed (platform stop): execution=${executionId}`);
|
|
1304
1330
|
return { kind: "return" };
|
|
1305
1331
|
}
|
|
@@ -1343,6 +1369,12 @@ turnSeq) {
|
|
|
1343
1369
|
const enterApprovalPause = async (boundary) => {
|
|
1344
1370
|
status.phase = ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL;
|
|
1345
1371
|
await persist(status);
|
|
1372
|
+
// The approval-resume reinvocation is the cache's best case: park the
|
|
1373
|
+
// agent so the resumed turn skips the full executor rebuild (#215).
|
|
1374
|
+
// (This path previously dropped the handle without close() — the
|
|
1375
|
+
// lease leaked; parking makes the lifetime explicit.) An absent
|
|
1376
|
+
// sessionId falls back to "" — the cache closes the lease immediately.
|
|
1377
|
+
cacheSessionAgent(sessionId ?? "", resolution.agent, agentFingerprint);
|
|
1346
1378
|
console.log(`ExecuteCursor returning WAITING_FOR_APPROVAL: ${boundary.deniedToolCallCount} gated tool(s), ` +
|
|
1347
1379
|
`${boundary.capturedChangeCount} file card(s) pending`);
|
|
1348
1380
|
return slimStatus(status);
|
|
@@ -1755,11 +1787,12 @@ turnSeq) {
|
|
|
1755
1787
|
console.log(`ExecuteCursor completed: execution=${executionId}, phase=${ExecutionPhase[status.phase]}, ` +
|
|
1756
1788
|
`hasStructuredOutput=${structuredOutput !== undefined}` +
|
|
1757
1789
|
(status.error ? `, error=${status.error}` : ""));
|
|
1758
|
-
//
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1790
|
+
// Park the agent (with its executor lease) for the session's next turn
|
|
1791
|
+
// instead of closing it — the idle TTL / shutdown hooks in
|
|
1792
|
+
// agent-session-cache own the eventual release, so cache buildup across
|
|
1793
|
+
// sessions stays bounded while turns of ONE session stop paying the
|
|
1794
|
+
// executor + MCP re-spawn tax (#215).
|
|
1795
|
+
cacheSessionAgent(sessionId ?? "", resolution.agent, agentFingerprint);
|
|
1763
1796
|
const slim = slimStatus(status);
|
|
1764
1797
|
if (finalText !== undefined) {
|
|
1765
1798
|
slim.final_text = finalText;
|