@stigmer/runner 3.9.0 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/dist/.build-fingerprint +1 -1
- package/dist/activities/call-llm.js +9 -10
- package/dist/activities/call-llm.js.map +1 -1
- package/dist/activities/classify-tool-approvals.d.ts +2 -1
- package/dist/activities/classify-tool-approvals.js +28 -2
- package/dist/activities/classify-tool-approvals.js.map +1 -1
- package/dist/activities/discover-mcp-server.d.ts +32 -0
- package/dist/activities/discover-mcp-server.js +162 -27
- package/dist/activities/discover-mcp-server.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.d.ts +8 -0
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js +1 -1
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js.map +1 -1
- package/dist/activities/execute-cursor/approval-state.d.ts +28 -2
- package/dist/activities/execute-cursor/approval-state.js +7 -1
- package/dist/activities/execute-cursor/approval-state.js.map +1 -1
- package/dist/activities/execute-cursor/attachment-resolver.d.ts +14 -0
- package/dist/activities/execute-cursor/attachment-resolver.js +25 -4
- package/dist/activities/execute-cursor/attachment-resolver.js.map +1 -1
- package/dist/activities/execute-cursor/blueprint-resolver.d.ts +1 -9
- package/dist/activities/execute-cursor/blueprint-resolver.js +6 -22
- package/dist/activities/execute-cursor/blueprint-resolver.js.map +1 -1
- package/dist/activities/execute-cursor/env-resolver.js +3 -1
- package/dist/activities/execute-cursor/env-resolver.js.map +1 -1
- package/dist/activities/execute-cursor/error-classifier.d.ts +40 -3
- package/dist/activities/execute-cursor/error-classifier.js +81 -3
- package/dist/activities/execute-cursor/error-classifier.js.map +1 -1
- package/dist/activities/execute-cursor/extract-structured-output.d.ts +29 -0
- package/dist/activities/execute-cursor/extract-structured-output.js +58 -0
- package/dist/activities/execute-cursor/extract-structured-output.js.map +1 -0
- package/dist/activities/execute-cursor/hook-script.d.ts +14 -3
- package/dist/activities/execute-cursor/hook-script.js +72 -10
- package/dist/activities/execute-cursor/hook-script.js.map +1 -1
- package/dist/activities/execute-cursor/index.d.ts +5 -1
- package/dist/activities/execute-cursor/index.js +59 -58
- package/dist/activities/execute-cursor/index.js.map +1 -1
- package/dist/activities/execute-cursor/mcp-resolver.d.ts +24 -1
- package/dist/activities/execute-cursor/mcp-resolver.js +5 -2
- package/dist/activities/execute-cursor/mcp-resolver.js.map +1 -1
- package/dist/activities/execute-cursor/prompt-builder.d.ts +18 -4
- package/dist/activities/execute-cursor/prompt-builder.js +12 -7
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- package/dist/activities/execute-cursor/turn-stream.js +4 -1
- package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
- package/dist/activities/execute-deep-agent/attachment-injector.d.ts +18 -1
- package/dist/activities/execute-deep-agent/attachment-injector.js +68 -23
- package/dist/activities/execute-deep-agent/attachment-injector.js.map +1 -1
- package/dist/activities/execute-deep-agent/environment.js +3 -1
- package/dist/activities/execute-deep-agent/environment.js.map +1 -1
- package/dist/activities/execute-deep-agent/index.js +15 -0
- package/dist/activities/execute-deep-agent/index.js.map +1 -1
- package/dist/activities/execute-deep-agent/prompt-builder.d.ts +7 -7
- package/dist/activities/execute-deep-agent/prompt-builder.js +8 -2
- package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/setup.d.ts +10 -0
- package/dist/activities/execute-deep-agent/setup.js +65 -26
- package/dist/activities/execute-deep-agent/setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +18 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.js +8 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +11 -4
- package/dist/activities/execute-deep-agent/subagent-wiring.js +13 -4
- package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
- package/dist/activities/hydrate-workflow-execution.js +3 -1
- package/dist/activities/hydrate-workflow-execution.js.map +1 -1
- package/dist/activities/workflow-event-activities.d.ts +28 -10
- package/dist/activities/workflow-event-activities.js +87 -58
- package/dist/activities/workflow-event-activities.js.map +1 -1
- package/dist/claimcheck/payload-codec.js +21 -1
- package/dist/claimcheck/payload-codec.js.map +1 -1
- package/dist/client/stigmer-client.d.ts +9 -4
- package/dist/client/stigmer-client.js +28 -15
- package/dist/client/stigmer-client.js.map +1 -1
- package/dist/encryption/config.d.ts +32 -0
- package/dist/encryption/config.js +68 -0
- package/dist/encryption/config.js.map +1 -0
- package/dist/encryption/index.d.ts +3 -0
- package/dist/encryption/index.js +3 -0
- package/dist/encryption/index.js.map +1 -0
- package/dist/encryption/payload-codec.d.ts +41 -0
- package/dist/encryption/payload-codec.js +130 -0
- package/dist/encryption/payload-codec.js.map +1 -0
- package/dist/payload-codecs.d.ts +16 -0
- package/dist/payload-codecs.js +38 -0
- package/dist/payload-codecs.js.map +1 -0
- package/dist/preflight.d.ts +31 -0
- package/dist/preflight.js +43 -0
- package/dist/preflight.js.map +1 -1
- package/dist/runner-manager.js +5 -15
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner.js +5 -16
- package/dist/runner.js.map +1 -1
- package/dist/shared/approval-policy.d.ts +9 -3
- package/dist/shared/approval-policy.js +15 -6
- package/dist/shared/approval-policy.js.map +1 -1
- package/dist/shared/attachment-naming.d.ts +53 -0
- package/dist/shared/attachment-naming.js +59 -0
- package/dist/shared/attachment-naming.js.map +1 -0
- package/dist/shared/attachment-vision.d.ts +43 -2
- package/dist/shared/attachment-vision.js +72 -6
- package/dist/shared/attachment-vision.js.map +1 -1
- package/dist/shared/caller-identity.d.ts +23 -2
- package/dist/shared/caller-identity.js +36 -5
- package/dist/shared/caller-identity.js.map +1 -1
- package/dist/shared/channel-attachment.js +1 -0
- package/dist/shared/channel-attachment.js.map +1 -1
- package/dist/shared/checkpointer/http-saver.d.ts +42 -1
- package/dist/shared/checkpointer/http-saver.js +96 -8
- package/dist/shared/checkpointer/http-saver.js.map +1 -1
- package/dist/shared/conversation-attachment.js +1 -0
- package/dist/shared/conversation-attachment.js.map +1 -1
- package/dist/shared/datastore-attachment.d.ts +50 -7
- package/dist/shared/datastore-attachment.js +93 -11
- package/dist/shared/datastore-attachment.js.map +1 -1
- package/dist/shared/http-retry.d.ts +43 -0
- package/dist/shared/http-retry.js +50 -0
- package/dist/shared/http-retry.js.map +1 -0
- package/dist/shared/llm-backend.d.ts +275 -0
- package/dist/shared/llm-backend.js +425 -0
- package/dist/shared/llm-backend.js.map +1 -0
- package/dist/shared/llm-proxy.d.ts +8 -0
- package/dist/shared/llm-proxy.js +15 -0
- package/dist/shared/llm-proxy.js.map +1 -1
- package/dist/shared/mcp-enabled-tools.d.ts +57 -0
- package/dist/shared/mcp-enabled-tools.js +86 -0
- package/dist/shared/mcp-enabled-tools.js.map +1 -0
- package/dist/shared/mcp-manager.d.ts +17 -2
- package/dist/shared/mcp-manager.js +37 -25
- package/dist/shared/mcp-manager.js.map +1 -1
- package/dist/shared/mcp-resolver.d.ts +39 -2
- package/dist/shared/mcp-resolver.js +38 -2
- package/dist/shared/mcp-resolver.js.map +1 -1
- package/dist/shared/model-client.d.ts +12 -5
- package/dist/shared/model-client.js +138 -18
- package/dist/shared/model-client.js.map +1 -1
- package/dist/shared/model-error.js +198 -5
- package/dist/shared/model-error.js.map +1 -1
- package/dist/shared/model-registry.d.ts +20 -2
- package/dist/shared/model-registry.js +37 -2
- package/dist/shared/model-registry.js.map +1 -1
- package/dist/shared/plan-mode-permissions.d.ts +26 -0
- package/dist/shared/plan-mode-permissions.js +28 -0
- package/dist/shared/plan-mode-permissions.js.map +1 -0
- package/dist/worker.d.ts +2 -1
- package/dist/worker.js +2 -4
- package/dist/worker.js.map +1 -1
- package/dist/workflow-engine/types.d.ts +18 -0
- package/dist/workflow-engine/types.js.map +1 -1
- package/dist/workflows/call-agent-orchestrator.d.ts +9 -0
- package/dist/workflows/call-agent-orchestrator.js +1 -0
- package/dist/workflows/call-agent-orchestrator.js.map +1 -1
- package/dist/workflows/connect-mcp-server.js +7 -0
- package/dist/workflows/connect-mcp-server.js.map +1 -1
- package/dist/workflows/engine-core.js +23 -2
- package/dist/workflows/engine-core.js.map +1 -1
- package/dist/workflows/execute-from-execution.d.ts +1 -1
- package/dist/workflows/execute-from-execution.js +11 -1
- package/dist/workflows/execute-from-execution.js.map +1 -1
- package/package.json +8 -2
- package/src/__tests__/claimcheck-codec.test.ts +36 -0
- package/src/__tests__/encryption-codec.test.ts +234 -0
- package/src/__tests__/fixtures/encrypted-payload-fixture.json +15 -0
- package/src/__tests__/history-encryption-e2e.test.ts +243 -0
- package/src/__tests__/preflight.test.ts +50 -2
- package/src/activities/__tests__/call-llm.test.ts +75 -0
- package/src/activities/__tests__/classify-tool-approvals.test.ts +117 -1
- package/src/activities/__tests__/discover-mcp-server.hang.test.ts +103 -0
- package/src/activities/__tests__/discover-mcp-server.test.ts +203 -0
- package/src/activities/__tests__/workflow-event-activities.test.ts +107 -8
- package/src/activities/call-llm.ts +9 -16
- package/src/activities/classify-tool-approvals.ts +34 -4
- package/src/activities/discover-mcp-server.ts +190 -32
- package/src/activities/execute-cursor/__test-utils__/cursor-hook-harness.ts +9 -0
- package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +14 -0
- package/src/activities/execute-cursor/__tests__/attachment-resolver.test.ts +92 -0
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +40 -14
- package/src/activities/execute-cursor/__tests__/error-classifier-extraction.test.ts +208 -0
- package/src/activities/execute-cursor/__tests__/extract-structured-output.test.ts +120 -0
- package/src/activities/execute-cursor/__tests__/hook-script.test.ts +93 -0
- package/src/activities/execute-cursor/__tests__/mcp-resolver.test.ts +125 -0
- package/src/activities/execute-cursor/__tests__/prompt-builder-delegation.test.ts +1 -1
- package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +13 -0
- package/src/activities/execute-cursor/approval-state.ts +30 -1
- package/src/activities/execute-cursor/attachment-resolver.ts +38 -3
- package/src/activities/execute-cursor/blueprint-resolver.ts +7 -27
- package/src/activities/execute-cursor/env-resolver.ts +3 -1
- package/src/activities/execute-cursor/error-classifier.ts +91 -4
- package/src/activities/execute-cursor/extract-structured-output.ts +72 -0
- package/src/activities/execute-cursor/hook-script.ts +74 -10
- package/src/activities/execute-cursor/index.ts +63 -72
- package/src/activities/execute-cursor/mcp-resolver.ts +36 -2
- package/src/activities/execute-cursor/prompt-builder.ts +34 -9
- package/src/activities/execute-cursor/turn-stream.ts +5 -2
- package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +132 -8
- package/src/activities/execute-deep-agent/__tests__/datastore-degradation.test.ts +104 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-reject.test.ts +2 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +1 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +1 -0
- package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +34 -5
- package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +1 -0
- package/src/activities/execute-deep-agent/__tests__/subagent-plan-mode-permissions.test.ts +173 -0
- package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +12 -7
- package/src/activities/execute-deep-agent/attachment-injector.ts +94 -30
- package/src/activities/execute-deep-agent/environment.ts +3 -1
- package/src/activities/execute-deep-agent/index.ts +20 -0
- package/src/activities/execute-deep-agent/prompt-builder.ts +20 -10
- package/src/activities/execute-deep-agent/setup.ts +86 -31
- package/src/activities/execute-deep-agent/subagent-transformer.ts +23 -1
- package/src/activities/execute-deep-agent/subagent-wiring.ts +14 -4
- package/src/activities/hydrate-workflow-execution.ts +3 -1
- package/src/activities/workflow-event-activities.ts +96 -69
- package/src/claimcheck/payload-codec.ts +33 -1
- package/src/client/__tests__/stigmer-client.test.ts +8 -8
- package/src/client/stigmer-client.ts +32 -18
- package/src/encryption/config.ts +91 -0
- package/src/encryption/index.ts +3 -0
- package/src/encryption/payload-codec.ts +152 -0
- package/src/payload-codecs.ts +56 -0
- package/src/preflight.ts +45 -0
- package/src/runner-manager.ts +6 -24
- package/src/runner.ts +6 -25
- package/src/shared/__tests__/approval-policy.test.ts +82 -39
- package/src/shared/__tests__/attachment-naming.test.ts +159 -0
- package/src/shared/__tests__/attachment-vision.test.ts +97 -0
- package/src/shared/__tests__/bedrock-adapter.test.ts +213 -0
- package/src/shared/__tests__/bedrock-seam.test.ts +390 -0
- package/src/shared/__tests__/caller-identity.test.ts +25 -0
- package/src/shared/__tests__/channel-attachment.test.ts +1 -1
- package/src/shared/__tests__/connect-backfill.test.ts +1 -0
- package/src/shared/__tests__/conversation-attachment.test.ts +1 -1
- package/src/shared/__tests__/datastore-attachment.test.ts +129 -1
- package/src/shared/__tests__/foundry-adapter.test.ts +276 -0
- package/src/shared/__tests__/foundry-seam.test.ts +482 -0
- package/src/shared/__tests__/http-retry.test.ts +67 -0
- package/src/shared/__tests__/llm-backend.test.ts +616 -0
- package/src/shared/__tests__/mcp-enabled-tools.test.ts +86 -0
- package/src/shared/__tests__/mcp-manager.test.ts +170 -2
- package/src/shared/__tests__/mcp-resolver.test.ts +146 -3
- package/src/shared/__tests__/model-client.test.ts +154 -0
- package/src/shared/__tests__/model-error.test.ts +289 -1
- package/src/shared/__tests__/model-registry.test.ts +71 -0
- package/src/shared/__tests__/synthesized-attachment.test.ts +1 -0
- package/src/shared/__tests__/vertex-adapter.test.ts +169 -0
- package/src/shared/__tests__/vertex-seam.test.ts +295 -0
- package/src/shared/approval-policy.ts +14 -7
- package/src/shared/attachment-naming.ts +78 -0
- package/src/shared/attachment-vision.ts +92 -9
- package/src/shared/caller-identity.ts +40 -5
- package/src/shared/channel-attachment.ts +1 -0
- package/src/shared/checkpointer/__tests__/http-saver.test.ts +196 -1
- package/src/shared/checkpointer/http-saver.ts +117 -9
- package/src/shared/conversation-attachment.ts +1 -0
- package/src/shared/datastore-attachment.ts +106 -11
- package/src/shared/http-retry.ts +50 -0
- package/src/shared/llm-backend.ts +544 -0
- package/src/shared/llm-proxy.ts +15 -0
- package/src/shared/mcp-enabled-tools.ts +105 -0
- package/src/shared/mcp-manager.ts +43 -26
- package/src/shared/mcp-resolver.ts +73 -2
- package/src/shared/model-client.ts +161 -19
- package/src/shared/model-error.ts +222 -4
- package/src/shared/model-registry.ts +50 -2
- package/src/shared/plan-mode-permissions.ts +30 -0
- package/src/worker.ts +4 -5
- package/src/workflow-engine/types.ts +18 -0
- package/src/workflows/__tests__/execute-serverless-workflow.test.ts +68 -2
- package/src/workflows/call-agent-orchestrator.ts +10 -0
- package/src/workflows/connect-mcp-server.ts +7 -0
- package/src/workflows/engine-core.ts +23 -2
- package/src/workflows/execute-from-execution.ts +12 -2
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier-2 structured-output extraction for the Cursor harness.
|
|
3
|
+
*
|
|
4
|
+
* When tier-1 text extraction (shared/extract-json.ts) cannot find JSON in
|
|
5
|
+
* the agent's free-text response, this tier asks an economy-tier LLM to
|
|
6
|
+
* extract it via withStructuredOutput (function-calling), which guarantees
|
|
7
|
+
* schema-conformant output through the API's tool-use mechanism.
|
|
8
|
+
*
|
|
9
|
+
* Lives in its own module (rather than inside execute-cursor/index.ts) so
|
|
10
|
+
* the LangChain construction path stays out of the Cursor activity's module
|
|
11
|
+
* graph until a run actually needs tier 2 — index.ts imports this module
|
|
12
|
+
* lazily at the call site, mirroring its tier-1 import, which is what
|
|
13
|
+
* bundle-slim's deferred evaluation preserves.
|
|
14
|
+
*/
|
|
15
|
+
import type { Config } from "../../config.js";
|
|
16
|
+
/**
|
|
17
|
+
* Extract structured data from an agent's free-text response using an
|
|
18
|
+
* economy-tier LLM with withStructuredOutput (function-calling).
|
|
19
|
+
*
|
|
20
|
+
* Construction (registry-id resolution, provider inference, proxy wiring) is
|
|
21
|
+
* delegated to the shared buildChatModel so the economy model's registry id
|
|
22
|
+
* is always resolved to a provider API id before the call.
|
|
23
|
+
*
|
|
24
|
+
* Throws when no LLM is reachable (no proxy and no credential path for the
|
|
25
|
+
* extraction model's provider) — the caller treats any throw here as "tier 2
|
|
26
|
+
* unavailable", logs it, and returns the agent's text without structured
|
|
27
|
+
* output, so the failure mode is a diagnosable log line, never a lost run.
|
|
28
|
+
*/
|
|
29
|
+
export declare function extractStructuredOutput(agentResponse: string, schema: Record<string, unknown>, config: Config, primaryModel: string): Promise<unknown | null>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier-2 structured-output extraction for the Cursor harness.
|
|
3
|
+
*
|
|
4
|
+
* When tier-1 text extraction (shared/extract-json.ts) cannot find JSON in
|
|
5
|
+
* the agent's free-text response, this tier asks an economy-tier LLM to
|
|
6
|
+
* extract it via withStructuredOutput (function-calling), which guarantees
|
|
7
|
+
* schema-conformant output through the API's tool-use mechanism.
|
|
8
|
+
*
|
|
9
|
+
* Lives in its own module (rather than inside execute-cursor/index.ts) so
|
|
10
|
+
* the LangChain construction path stays out of the Cursor activity's module
|
|
11
|
+
* graph until a run actually needs tier 2 — index.ts imports this module
|
|
12
|
+
* lazily at the call site, mirroring its tier-1 import, which is what
|
|
13
|
+
* bundle-slim's deferred evaluation preserves.
|
|
14
|
+
*/
|
|
15
|
+
import { getEconomyModel } from "../../shared/model-registry.js";
|
|
16
|
+
import { buildChatModel } from "../../shared/model-client.js";
|
|
17
|
+
import { checkDirectCredentials } from "../../shared/llm-backend.js";
|
|
18
|
+
import { tryInferProvider } from "../../shared/llm-proxy.js";
|
|
19
|
+
import { jsonSchemaToZod } from "../../shared/json-schema-to-zod.js";
|
|
20
|
+
/**
|
|
21
|
+
* Extract structured data from an agent's free-text response using an
|
|
22
|
+
* economy-tier LLM with withStructuredOutput (function-calling).
|
|
23
|
+
*
|
|
24
|
+
* Construction (registry-id resolution, provider inference, proxy wiring) is
|
|
25
|
+
* delegated to the shared buildChatModel so the economy model's registry id
|
|
26
|
+
* is always resolved to a provider API id before the call.
|
|
27
|
+
*
|
|
28
|
+
* Throws when no LLM is reachable (no proxy and no credential path for the
|
|
29
|
+
* extraction model's provider) — the caller treats any throw here as "tier 2
|
|
30
|
+
* unavailable", logs it, and returns the agent's text without structured
|
|
31
|
+
* output, so the failure mode is a diagnosable log line, never a lost run.
|
|
32
|
+
*/
|
|
33
|
+
export async function extractStructuredOutput(agentResponse, schema, config, primaryModel) {
|
|
34
|
+
const extractionModel = await getEconomyModel(primaryModel);
|
|
35
|
+
const proxyEndpoint = config.proxyEndpoint ?? undefined;
|
|
36
|
+
if (!proxyEndpoint) {
|
|
37
|
+
const provider = tryInferProvider(extractionModel);
|
|
38
|
+
const missing = provider === null ? null : checkDirectCredentials(provider);
|
|
39
|
+
if (missing !== null) {
|
|
40
|
+
throw new Error(`Structured-output extraction needs the ${provider} model ` +
|
|
41
|
+
`'${extractionModel}' but has no credential path. ${missing}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const { model: llm } = await buildChatModel({
|
|
45
|
+
modelName: extractionModel,
|
|
46
|
+
proxyEndpoint,
|
|
47
|
+
stigmerToken: config.stigmerToken ?? undefined,
|
|
48
|
+
maxTokens: 4096,
|
|
49
|
+
});
|
|
50
|
+
const zodSchema = jsonSchemaToZod(schema);
|
|
51
|
+
const structured = llm.withStructuredOutput(zodSchema);
|
|
52
|
+
const result = await structured.invoke([
|
|
53
|
+
{ role: "system", content: "Extract the structured data from the agent's response. Return only the data that matches the schema." },
|
|
54
|
+
{ role: "user", content: agentResponse },
|
|
55
|
+
]);
|
|
56
|
+
return result ?? null;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=extract-structured-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-structured-output.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/extract-structured-output.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAErE;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,aAAqB,EACrB,MAA+B,EAC/B,MAAc,EACd,YAAoB;IAEpB,MAAM,eAAe,GAAG,MAAM,eAAe,CAAC,YAAY,CAAC,CAAC;IAC5D,MAAM,aAAa,GAAG,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IAExD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,QAAQ,GAAG,gBAAgB,CAAC,eAAe,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC;QAC5E,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CACb,0CAA0C,QAAQ,SAAS;gBAC3D,IAAI,eAAe,iCAAiC,OAAO,EAAE,CAC9D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,MAAM,cAAc,CAAC;QAC1C,SAAS,EAAE,eAAe;QAC1B,aAAa;QACb,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,SAAS;QAC9C,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,GAAG,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAEvD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC;QACrC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,sGAAsG,EAAE;QACnI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE;KACzC,CAAC,CAAC;IAEH,OAAO,MAAM,IAAI,IAAI,CAAC;AACxB,CAAC"}
|
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
* - "secret" — DD-26 secret hard-block: the agent continues, no pause.
|
|
33
33
|
* - "capture-error" — CAS staging failed, write kept on the deny-gate.
|
|
34
34
|
* - "fail-closed" — approval state file missing, everything gated denies.
|
|
35
|
+
* - "disabled" — enabled_tools manifest exclusion (issue #350): the
|
|
36
|
+
* agent continues, no pause, permanent for the run.
|
|
35
37
|
* Only approval-kind records carry the captured tool_input: a secret write's
|
|
36
38
|
* content must never be persisted (DD-26), a capture-error's content is
|
|
37
39
|
* UNCLASSIFIED (the staging error means secret classification may never have
|
|
@@ -84,8 +86,16 @@
|
|
|
84
86
|
* set, allow the rest" — matching the native harness and avoiding denial of
|
|
85
87
|
* auto-approved MCP tools (which are absent from mcpToolPolicies):
|
|
86
88
|
* 0. Scope guard: not the runner's own agent → allow (never touch the ledger)
|
|
87
|
-
* 1. Missing state file → deny (fail-closed)
|
|
88
|
-
*
|
|
89
|
+
* 1. Missing state file → deny (fail-closed)
|
|
90
|
+
* 1a. beforeMCPExecution event → tool excluded by the server's
|
|
91
|
+
* mcpServerEnabledTools allow-list → record kind "disabled", deny.
|
|
92
|
+
* Deliberately BEFORE autoApproveAll and the grant checks: enabled_tools
|
|
93
|
+
* is a capability manifest, not an approval gate (issue #350) — no bypass
|
|
94
|
+
* may resurrect a disabled tool, and no human may be offered "approve" on
|
|
95
|
+
* one. Server-scoped via the payload's mcp_server_name; an absent slug in
|
|
96
|
+
* the map means unrestricted.
|
|
97
|
+
* 1b. autoApproveAll (the pre-armed spec.auto_approve_all global bypass) →
|
|
98
|
+
* allow
|
|
89
99
|
* 2. beforeMCPExecution event → MCP tool present in mcpToolPolicies
|
|
90
100
|
* (require-approval):
|
|
91
101
|
* a. name token in approvedGrantTokens → allow (reinvocation grant)
|
|
@@ -117,7 +127,8 @@
|
|
|
117
127
|
*
|
|
118
128
|
* From the pointer the script reads the current turn's approval-state file (the
|
|
119
129
|
* single source of truth for the dynamic inputs: autoApproveAll, leasedCategories,
|
|
120
|
-
* mcpToolPolicies, approvedGrantTokens), denial ledger,
|
|
130
|
+
* mcpToolPolicies, mcpServerEnabledTools, approvedGrantTokens), denial ledger,
|
|
131
|
+
* and runner PID. The
|
|
121
132
|
* static policy (which built-ins are gated, their categories, the salient arg
|
|
122
133
|
* fields) is baked at generation time from approval-policy.ts.
|
|
123
134
|
*
|
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
* - "secret" — DD-26 secret hard-block: the agent continues, no pause.
|
|
33
33
|
* - "capture-error" — CAS staging failed, write kept on the deny-gate.
|
|
34
34
|
* - "fail-closed" — approval state file missing, everything gated denies.
|
|
35
|
+
* - "disabled" — enabled_tools manifest exclusion (issue #350): the
|
|
36
|
+
* agent continues, no pause, permanent for the run.
|
|
35
37
|
* Only approval-kind records carry the captured tool_input: a secret write's
|
|
36
38
|
* content must never be persisted (DD-26), a capture-error's content is
|
|
37
39
|
* UNCLASSIFIED (the staging error means secret classification may never have
|
|
@@ -84,8 +86,16 @@
|
|
|
84
86
|
* set, allow the rest" — matching the native harness and avoiding denial of
|
|
85
87
|
* auto-approved MCP tools (which are absent from mcpToolPolicies):
|
|
86
88
|
* 0. Scope guard: not the runner's own agent → allow (never touch the ledger)
|
|
87
|
-
* 1. Missing state file → deny (fail-closed)
|
|
88
|
-
*
|
|
89
|
+
* 1. Missing state file → deny (fail-closed)
|
|
90
|
+
* 1a. beforeMCPExecution event → tool excluded by the server's
|
|
91
|
+
* mcpServerEnabledTools allow-list → record kind "disabled", deny.
|
|
92
|
+
* Deliberately BEFORE autoApproveAll and the grant checks: enabled_tools
|
|
93
|
+
* is a capability manifest, not an approval gate (issue #350) — no bypass
|
|
94
|
+
* may resurrect a disabled tool, and no human may be offered "approve" on
|
|
95
|
+
* one. Server-scoped via the payload's mcp_server_name; an absent slug in
|
|
96
|
+
* the map means unrestricted.
|
|
97
|
+
* 1b. autoApproveAll (the pre-armed spec.auto_approve_all global bypass) →
|
|
98
|
+
* allow
|
|
89
99
|
* 2. beforeMCPExecution event → MCP tool present in mcpToolPolicies
|
|
90
100
|
* (require-approval):
|
|
91
101
|
* a. name token in approvedGrantTokens → allow (reinvocation grant)
|
|
@@ -143,6 +153,21 @@ const SECRET_BLOCKED_AGENT_MESSAGE = "This file was blocked for security because
|
|
|
143
153
|
"misconfiguration, so never tell the user to change Cursor settings or enable " +
|
|
144
154
|
"hooks. Do not retry this write or attempt a workaround; the write will not be " +
|
|
145
155
|
"applied. Continue with the rest of the task.";
|
|
156
|
+
// Shown to the model when an MCP tool call is denied because the agent's
|
|
157
|
+
// enabled_tools manifest excludes the tool (issue #350). Like
|
|
158
|
+
// SECRET_BLOCKED_AGENT_MESSAGE this must NOT promise a resume — the exclusion
|
|
159
|
+
// is permanent for the run and mode-independent (it is a capability manifest,
|
|
160
|
+
// not an approval, so nothing can be granted). Same embedding constraint
|
|
161
|
+
// (single-quoted bash echo of a JSON object): no double quotes, apostrophes,
|
|
162
|
+
// or backslashes.
|
|
163
|
+
const DISABLED_TOOL_AGENT_MESSAGE = "This tool is not enabled for this agent: the MCP server exposes it, but the " +
|
|
164
|
+
"agent manifest (enabled_tools) excludes it. This is the platform capability " +
|
|
165
|
+
"manifest working as intended — it is not an error and not a Cursor " +
|
|
166
|
+
"misconfiguration, so never tell the user to change Cursor settings or enable " +
|
|
167
|
+
"hooks. Do not retry this tool or attempt a workaround; it will stay " +
|
|
168
|
+
"unavailable for this entire run. Use a different tool or adapt your plan, " +
|
|
169
|
+
"and if the task cannot proceed without it, tell the user plainly what was " +
|
|
170
|
+
"unavailable.";
|
|
146
171
|
/**
|
|
147
172
|
* Build the bash `case` arms that map an incoming hook `tool_name` to its
|
|
148
173
|
* canonical approval category. Generated from approval-policy.ts so the hook and
|
|
@@ -195,14 +220,17 @@ function buildContentDigestScript() {
|
|
|
195
220
|
* Build the inline Node.js identity extractor embedded in the hook script.
|
|
196
221
|
*
|
|
197
222
|
* Parses the hook's stdin JSON properly (the bash fallback's grep truncates
|
|
198
|
-
* string values at the first escaped quote) and emits
|
|
223
|
+
* string values at the first escaped quote) and emits NINE lines: tool_name,
|
|
199
224
|
* canonical category, coarse identity token, MCP name-token, hook_event_name
|
|
200
225
|
* (the event discriminator: `preToolUse` for built-ins, `beforeMCPExecution`
|
|
201
226
|
* for MCP), base64(JSON(tool_input)) — the authoritative pre-execution args the
|
|
202
|
-
* runner overlays onto the gated tool call for the approval preview —
|
|
227
|
+
* runner overlays onto the gated tool call for the approval preview — the
|
|
203
228
|
* CONTENT token (base64(category \n salient \n contentDigest), empty when the
|
|
204
|
-
* tool has no edit content)
|
|
205
|
-
*
|
|
229
|
+
* tool has no edit content), base64(salient), and mcp_server_name (the MCP
|
|
230
|
+
* server slug the beforeMCPExecution payload carries; empty for built-ins) —
|
|
231
|
+
* the server scope for the enabled_tools manifest arm. The token encodings
|
|
232
|
+
* must stay byte-identical to grantToken()/contentToken() in
|
|
233
|
+
* approval-state.ts.
|
|
206
234
|
*
|
|
207
235
|
* Authored as a single-quoted bash string, so the JS must not contain single
|
|
208
236
|
* quotes. The category map, salient field list, and edit/content field lists are
|
|
@@ -233,6 +261,7 @@ function buildNodeIdentityScript() {
|
|
|
233
261
|
// is "" for a non-edit tool, in which case the content token (line 7) is "".
|
|
234
262
|
buildContentDigestScript(),
|
|
235
263
|
`const ev=typeof t.hook_event_name==="string"?t.hook_event_name:"";`,
|
|
264
|
+
`const srv=typeof t.mcp_server_name==="string"?t.mcp_server_name:"";`,
|
|
236
265
|
// Line 6 is base64(JSON(tool_input)): the AUTHORITATIVE pre-execution args
|
|
237
266
|
// the runner overlays onto the gated tool call so the approval card can show
|
|
238
267
|
// the proposed change before the user approves. Base64 keeps the bash side
|
|
@@ -241,8 +270,10 @@ function buildNodeIdentityScript() {
|
|
|
241
270
|
// grant the runner authorizes for a file edit. Line 8 is base64(salient) —
|
|
242
271
|
// the raw resource value (file path / command) capture mode needs to run
|
|
243
272
|
// `git check-ignore` on a file path; base64 keeps newlines/quotes out of the
|
|
244
|
-
// line-oriented bash parse.
|
|
245
|
-
|
|
273
|
+
// line-oriented bash parse. Line 9 is mcp_server_name — the server scope
|
|
274
|
+
// the enabled_tools manifest arm matches against mcpServerEnabledTools
|
|
275
|
+
// (a bare slug, never quoted/escaped, so it rides as a plain line).
|
|
276
|
+
`process.stdout.write(name+"\\n"+cat+"\\n"+b(cat+"\\n"+s)+"\\n"+b(name+"\\n")+"\\n"+ev+"\\n"+b(JSON.stringify(a))+"\\n"+(dig?b(cat+"\\n"+s+"\\n"+dig):"")+"\\n"+b(s)+"\\n"+srv);`,
|
|
246
277
|
].join("");
|
|
247
278
|
}
|
|
248
279
|
/**
|
|
@@ -264,7 +295,8 @@ function buildNodeIdentityScript() {
|
|
|
264
295
|
*
|
|
265
296
|
* From the pointer the script reads the current turn's approval-state file (the
|
|
266
297
|
* single source of truth for the dynamic inputs: autoApproveAll, leasedCategories,
|
|
267
|
-
* mcpToolPolicies, approvedGrantTokens), denial ledger,
|
|
298
|
+
* mcpToolPolicies, mcpServerEnabledTools, approvedGrantTokens), denial ledger,
|
|
299
|
+
* and runner PID. The
|
|
268
300
|
* static policy (which built-ins are gated, their categories, the salient arg
|
|
269
301
|
* fields) is baked at generation time from approval-policy.ts.
|
|
270
302
|
*
|
|
@@ -425,6 +457,9 @@ if [ -n "$IDENTITY" ]; then
|
|
|
425
457
|
# Raw salient (base64) — the file path / command. Capture mode decodes it to
|
|
426
458
|
# run git check-ignore on a file path.
|
|
427
459
|
SALIENT=$(printf '%s\\n' "$IDENTITY" | sed -n 8p | base64 -d 2>/dev/null || true)
|
|
460
|
+
# MCP server slug (beforeMCPExecution payloads only; empty for built-ins) —
|
|
461
|
+
# the server scope for the enabled_tools manifest arm.
|
|
462
|
+
MCP_SERVER=$(printf '%s\\n' "$IDENTITY" | sed -n 9p)
|
|
428
463
|
else
|
|
429
464
|
# Fallback when the Node binary cannot run: grep/cut extraction. Best-effort
|
|
430
465
|
# only — '"field":"[^"]*"' truncates at the first JSON-escaped quote, so the
|
|
@@ -434,6 +469,10 @@ else
|
|
|
434
469
|
# would otherwise abort the script and emit no decision.
|
|
435
470
|
TOOL_NAME=$(echo "$INPUT" | grep -o '"tool_name":"[^"]*"' | head -1 | cut -d'"' -f4 || true)
|
|
436
471
|
HOOK_EVENT=$(echo "$INPUT" | grep -o '"hook_event_name":"[^"]*"' | head -1 | cut -d'"' -f4 || true)
|
|
472
|
+
# Server slugs are plain identifiers (no JSON-escaped quotes), so the grep
|
|
473
|
+
# fallback extracts mcp_server_name reliably — the manifest arm keeps its
|
|
474
|
+
# full precision even without the Node binary.
|
|
475
|
+
MCP_SERVER=$(echo "$INPUT" | grep -o '"mcp_server_name":"[^"]*"' | head -1 | cut -d'"' -f4 || true)
|
|
437
476
|
SALIENT=""
|
|
438
477
|
for field in ${salientFields}; do
|
|
439
478
|
v=$(echo "$INPUT" | grep -o "\\"$field\\":\\"[^\\"]*\\"" | head -1 | cut -d'"' -f4 || true)
|
|
@@ -573,7 +612,30 @@ if [ "$CAPTURE_IGNORED" = "true" ] && [ "$CATEGORY" = "write" ] && [ -n "$SALIEN
|
|
|
573
612
|
fi
|
|
574
613
|
fi
|
|
575
614
|
|
|
576
|
-
# ---
|
|
615
|
+
# --- 1a. MCP capability manifest: enabled_tools (issue #350) ---
|
|
616
|
+
# Runs BEFORE the auto-approve-all shortcut and every grant check because
|
|
617
|
+
# enabled_tools is a capability manifest, not an approval gate: no bypass may
|
|
618
|
+
# resurrect a disabled tool and no human may be offered approval on one.
|
|
619
|
+
# mcpServerEnabledTools holds ONLY restricted servers (an absent slug means
|
|
620
|
+
# unrestricted, so this arm is inert for the common case). Matching is
|
|
621
|
+
# server-scoped via the payload's mcp_server_name — equal tool names on
|
|
622
|
+
# different servers cannot cross-grant — and the quoted-name membership check
|
|
623
|
+
# is exact, never a substring match. Kind "disabled": attributable,
|
|
624
|
+
# non-pausing (the model adapts, same consumer semantics as "secret"), and
|
|
625
|
+
# permanent for the run.
|
|
626
|
+
if [ "$HOOK_EVENT" = "beforeMCPExecution" ] && [ -n "$MCP_SERVER" ] && [ -n "$TOOL_NAME" ]; then
|
|
627
|
+
ENABLED_MAP=$(echo "$STATE" | grep -o '"mcpServerEnabledTools":{[^}]*}' | head -1 || true)
|
|
628
|
+
if [ -n "$ENABLED_MAP" ]; then
|
|
629
|
+
SERVER_ENABLED=$(echo "$ENABLED_MAP" | grep -o "\\"$MCP_SERVER\\":\\[[^]]*\\]" | head -1 || true)
|
|
630
|
+
if [ -n "$SERVER_ENABLED" ] && ! echo "$SERVER_ENABLED" | grep -qF "\\"$TOOL_NAME\\""; then
|
|
631
|
+
record_denial "$MCP_TOKEN" "disabled"
|
|
632
|
+
echo '{"permission":"deny","agent_message":"${DISABLED_TOOL_AGENT_MESSAGE}","user_message":"Tool not enabled for this agent: '"$TOOL_NAME"'"}'
|
|
633
|
+
exit 0
|
|
634
|
+
fi
|
|
635
|
+
fi
|
|
636
|
+
fi
|
|
637
|
+
|
|
638
|
+
# --- 1b. Auto-approve all ---
|
|
577
639
|
if echo "$STATE" | grep -q '"autoApproveAll":true'; then
|
|
578
640
|
echo '{"permission":"allow"}'
|
|
579
641
|
exit 0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-script.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/hook-script.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"hook-script.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/hook-script.ts"],"names":[],"mappings":"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"}
|
|
@@ -52,7 +52,11 @@ export interface BuildPromptInput {
|
|
|
52
52
|
subAgents: import("@stigmer/protos/ai/stigmer/agentic/agent/v1/spec_pb").SubAgent[];
|
|
53
53
|
workspaceDirs: string[];
|
|
54
54
|
workspaceFileRefs: string[];
|
|
55
|
-
|
|
55
|
+
/**
|
|
56
|
+
* This turn's resolved attachments for the `<input_files>` section —
|
|
57
|
+
* final paths plus duplicate-rename disclosure (attachment-resolver.ts).
|
|
58
|
+
*/
|
|
59
|
+
attachments: import("./prompt-builder.js").AttachmentPromptEntry[];
|
|
56
60
|
/**
|
|
57
61
|
* Vision facts for the input-files section (T04): which attachments the
|
|
58
62
|
* model sees inline and which degraded to path-only. PER-TURN like the
|
|
@@ -48,6 +48,7 @@ import { withholdSecretContentFromMessages } from "../../shared/tool-row.js";
|
|
|
48
48
|
import { StallTimeoutError, formatStallFailure } from "../../shared/stall-watchdog.js";
|
|
49
49
|
import { resolveUsableArtifactStorage, loadArtifactStorageConfig } from "../../shared/artifact-storage.js";
|
|
50
50
|
import { CURSOR_VISION_PROFILE, VisionBudget, toCursorImages, } from "../../shared/attachment-vision.js";
|
|
51
|
+
import { getModelVisionCapability } from "../../shared/model-registry.js";
|
|
51
52
|
import { publishPlanArtifact } from "../../shared/plan-artifact.js";
|
|
52
53
|
import { DeltaEnricher } from "./delta-enricher.js";
|
|
53
54
|
import { TodoTracker } from "./todo-tracker.js";
|
|
@@ -61,6 +62,7 @@ import { readChannelConversationId, synthesizeConversationAttachment, } from "..
|
|
|
61
62
|
import { injectSynthesizedAttachment } from "../../shared/synthesized-attachment.js";
|
|
62
63
|
import { mergeApprovalPolicies } from "./approval-policy.js";
|
|
63
64
|
import { deriveActiveLeases, isUnattendedApprovalMode } from "../../shared/approval-policy.js";
|
|
65
|
+
import { enabledToolsBySlug } from "../../shared/mcp-enabled-tools.js";
|
|
64
66
|
import { backfillMcpServersIfNeeded } from "./connect-backfill.js";
|
|
65
67
|
import { resolveExecutionEnv } from "./env-resolver.js";
|
|
66
68
|
import { resolveBlueprint } from "./blueprint-resolver.js";
|
|
@@ -95,7 +97,7 @@ import { StreamingUsageSummarySchema } from "@stigmer/protos/ai/stigmer/agentic/
|
|
|
95
97
|
import { activityStarted, activityFinished } from "../../idle-watchdog.js";
|
|
96
98
|
import { normalizeActivityInput } from "../../shared/activity-input.js";
|
|
97
99
|
import { getCapturedRejection, clearCapturedRejection } from "./rejection-capture.js";
|
|
98
|
-
import { synthesizeError, formatClassifiedError, shouldRetryWithFreshAgent } from "./error-classifier.js";
|
|
100
|
+
import { synthesizeError, formatClassifiedError, shouldRetryWithFreshAgent, extractRunErrorSources } from "./error-classifier.js";
|
|
99
101
|
import { createAgent, createCloudAgent } from "./session-lifecycle.js";
|
|
100
102
|
import { setMaxListeners } from "node:events";
|
|
101
103
|
import { startHeartbeat } from "../../shared/heartbeat.js";
|
|
@@ -535,8 +537,20 @@ turnSeq) {
|
|
|
535
537
|
// the messaging reach; undefined lets a cloud sandbox runner's
|
|
536
538
|
// ambient session-scoped token or OSS's no-auth apply). The
|
|
537
539
|
// attachment header falls back to the ambient credential where no
|
|
538
|
-
// exchange happens.
|
|
539
|
-
|
|
540
|
+
// exchange happens. Unlike the env read (which hard-fails on a broken
|
|
541
|
+
// exchange — secrets are load-bearing there), this exchange is
|
|
542
|
+
// opportunistic: every consumer below degrades to an empty answer by
|
|
543
|
+
// contract, and the server refuses the ambient fallback safely, so a
|
|
544
|
+
// failed exchange must not kill the run.
|
|
545
|
+
let exchangedRunnerToken;
|
|
546
|
+
try {
|
|
547
|
+
exchangedRunnerToken =
|
|
548
|
+
await client.acquireScopedRunnerToken({ agentExecutionId: executionId });
|
|
549
|
+
}
|
|
550
|
+
catch (err) {
|
|
551
|
+
console.warn("[execute-cursor] Scoped-token exchange failed for attachment/discovery " +
|
|
552
|
+
`reads; degrading to the ambient credential: ${err instanceof Error ? err.message : err}`);
|
|
553
|
+
}
|
|
540
554
|
const attachmentCredential = exchangedRunnerToken
|
|
541
555
|
?? config.stigmerTokenRef?.current
|
|
542
556
|
?? config.stigmerToken;
|
|
@@ -611,9 +625,10 @@ turnSeq) {
|
|
|
611
625
|
// them as auto-approved); the global bypass empties the map entirely.
|
|
612
626
|
const leases = deriveActiveLeases(execution);
|
|
613
627
|
const globalBypass = leases.global;
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
628
|
+
// Layer-3 overrides ride each resolved server from its merged usage —
|
|
629
|
+
// see ResolvedMcpServer.toolApprovalOverrides (issue #349) — so there
|
|
630
|
+
// is no separate override input to pass here.
|
|
631
|
+
const mergedPolicies = mergeApprovalPolicies(mcpResolution.resolvedServers, leases);
|
|
617
632
|
heartbeat();
|
|
618
633
|
// Phase 4c: Validate MCP server env health (diagnostic, non-blocking)
|
|
619
634
|
const mcpWarnings = validateMcpServerEnv(mcpResolution.resolvedServers, blueprint.mergedMcpServerUsages, envVars);
|
|
@@ -635,7 +650,13 @@ turnSeq) {
|
|
|
635
650
|
// artifactStorage resolved for status offload above. The vision budget
|
|
636
651
|
// rides along so image attachments are selected for inline delivery while
|
|
637
652
|
// their bytes are already in hand (attachment-vision.ts owns all policy).
|
|
638
|
-
|
|
653
|
+
// The budget also carries the requested model's registry vision
|
|
654
|
+
// capability, looked up from the raw executionConfig name — full model
|
|
655
|
+
// validation (Phase 6) isn't needed for this, and ""/"default" (the Auto
|
|
656
|
+
// pool) resolves to unknown, which the policy treats as sighted.
|
|
657
|
+
const visionBudget = new VisionBudget(CURSOR_VISION_PROFILE, {
|
|
658
|
+
modelVision: await getModelVisionCapability(spec.executionConfig?.modelName ?? ""),
|
|
659
|
+
});
|
|
639
660
|
const attachmentResults = await resolveAttachments(spec.attachments, {
|
|
640
661
|
sessionId,
|
|
641
662
|
primaryWorkspaceDir,
|
|
@@ -643,7 +664,10 @@ turnSeq) {
|
|
|
643
664
|
storage: artifactStorage,
|
|
644
665
|
visionBudget,
|
|
645
666
|
});
|
|
646
|
-
const
|
|
667
|
+
const attachmentEntries = attachmentResults.map((a) => ({
|
|
668
|
+
path: a.relativePath,
|
|
669
|
+
...(a.renamedFrom !== undefined ? { renamedFrom: a.renamedFrom } : {}),
|
|
670
|
+
}));
|
|
647
671
|
// Vision facts, derived once from the single resolution result: the
|
|
648
672
|
// images the model will see inline (in attachment order) and the ones
|
|
649
673
|
// that degraded to path-only, disclosed in the prompt.
|
|
@@ -744,7 +768,12 @@ turnSeq) {
|
|
|
744
768
|
// approval denials with the non-pausing "unattended" kind, so the
|
|
745
769
|
// first-denial stop never fires and the turn boundary settles the denied
|
|
746
770
|
// calls as SKIPPED instead of pausing a turn nobody can approve.
|
|
747
|
-
const approvalState = buildApprovalState(mergedPolicies, globalBypass, leases.categories, approvalGrants, captureMode, captureIgnored, gitWorkspace, isUnattendedApprovalMode(execution)
|
|
771
|
+
const approvalState = buildApprovalState(mergedPolicies, globalBypass, leases.categories, approvalGrants, captureMode, captureIgnored, gitWorkspace, isUnattendedApprovalMode(execution),
|
|
772
|
+
// The enabled_tools capability manifest (issue #350): restricted
|
|
773
|
+
// servers' allow-lists, enforced by the hook's "disabled" arm ahead of
|
|
774
|
+
// every approval bypass. The Cursor SDK config cannot hide a server's
|
|
775
|
+
// tools, so this deny-at-call is the harness's enforcement.
|
|
776
|
+
enabledToolsBySlug(mcpResolution.resolvedServers));
|
|
748
777
|
const hitlGate = await installHitlGate({
|
|
749
778
|
workspaceRoot: primaryWorkspaceDir,
|
|
750
779
|
hitlDir,
|
|
@@ -937,7 +966,7 @@ turnSeq) {
|
|
|
937
966
|
subAgents: blueprint.subAgents,
|
|
938
967
|
workspaceDirs: blueprint.workspaceDirs,
|
|
939
968
|
workspaceFileRefs: spec.workspaceFileRefs ?? [],
|
|
940
|
-
|
|
969
|
+
attachments: attachmentEntries,
|
|
941
970
|
vision: visionPromptInfo,
|
|
942
971
|
pendingApprovals: adjudicatedApprovals,
|
|
943
972
|
appliedToolCallIds,
|
|
@@ -1443,12 +1472,10 @@ turnSeq) {
|
|
|
1443
1472
|
status.phase = ExecutionPhase.EXECUTION_COMPLETED;
|
|
1444
1473
|
break;
|
|
1445
1474
|
case "error": {
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
?? resultAny.reason;
|
|
1451
|
-
const sdkErrorStr = sdkError ? String(sdkError) : undefined;
|
|
1475
|
+
// Shape-aware extraction, NOT String(): the result's error fields are
|
|
1476
|
+
// structured at runtime often enough that a bare coercion showed users
|
|
1477
|
+
// "[object Object]" and shadowed every fallback source below (oss#299).
|
|
1478
|
+
const runErrorSources = extractRunErrorSources(result);
|
|
1452
1479
|
// The SDK frequently resolves run.wait() to a bare { status: "error" }
|
|
1453
1480
|
// while the real reason (e.g. the original grpc-status 12 routing
|
|
1454
1481
|
// failure) lives on the failing conversation turn. Capture it here so
|
|
@@ -1458,7 +1485,8 @@ turnSeq) {
|
|
|
1458
1485
|
if (capturedRejection)
|
|
1459
1486
|
clearCapturedRejection(executionId);
|
|
1460
1487
|
const classified = synthesizeError({
|
|
1461
|
-
|
|
1488
|
+
sdkError: runErrorSources.sdkError,
|
|
1489
|
+
sdkResultFields: runErrorSources.sdkResultFields,
|
|
1462
1490
|
streamErrorMessage: turnState.streamErrorMessage,
|
|
1463
1491
|
capturedRejection,
|
|
1464
1492
|
conversationErrorText,
|
|
@@ -1501,7 +1529,7 @@ turnSeq) {
|
|
|
1501
1529
|
subAgents: blueprint.subAgents,
|
|
1502
1530
|
workspaceDirs: blueprint.workspaceDirs,
|
|
1503
1531
|
workspaceFileRefs: spec.workspaceFileRefs ?? [],
|
|
1504
|
-
|
|
1532
|
+
attachments: attachmentEntries,
|
|
1505
1533
|
vision: visionPromptInfo,
|
|
1506
1534
|
pendingApprovals: adjudicatedApprovals,
|
|
1507
1535
|
interactionMode,
|
|
@@ -1557,8 +1585,14 @@ turnSeq) {
|
|
|
1557
1585
|
if (retryRejection)
|
|
1558
1586
|
clearCapturedRejection(executionId);
|
|
1559
1587
|
const retryConversationErrorText = await introspectConversation(retryRun, executionId);
|
|
1588
|
+
// Same shape-aware extraction as the primary error arm — the retry
|
|
1589
|
+
// previously String()-coerced result.result alone, so a structured
|
|
1590
|
+
// retry failure both read "[object Object]" and ignored the
|
|
1591
|
+
// error/message/reason fields the primary arm consults.
|
|
1592
|
+
const retryErrorSources = extractRunErrorSources(retryResult);
|
|
1560
1593
|
const retryClassified = synthesizeError({
|
|
1561
|
-
|
|
1594
|
+
sdkError: retryErrorSources.sdkError,
|
|
1595
|
+
sdkResultFields: retryErrorSources.sdkResultFields,
|
|
1562
1596
|
streamErrorMessage: turnState.streamErrorMessage,
|
|
1563
1597
|
capturedRejection: retryRejection,
|
|
1564
1598
|
conversationErrorText: retryConversationErrorText,
|
|
@@ -1654,6 +1688,7 @@ turnSeq) {
|
|
|
1654
1688
|
console.log(`ExecuteCursor text extraction failed, trying LLM extraction: execution=${executionId}, ` +
|
|
1655
1689
|
`finalTextLength=${finalText.length}`);
|
|
1656
1690
|
try {
|
|
1691
|
+
const { extractStructuredOutput } = await import("./extract-structured-output.js");
|
|
1657
1692
|
structuredOutput = await extractStructuredOutput(finalText, structuredOutputSchema, config, requestedModel);
|
|
1658
1693
|
if (structuredOutput !== undefined) {
|
|
1659
1694
|
console.log(`ExecuteCursor structured output extracted (LLM): execution=${executionId}`);
|
|
@@ -1942,40 +1977,6 @@ function seedCursorTranscriptFromExecution(status, execution) {
|
|
|
1942
1977
|
}
|
|
1943
1978
|
return persisted.subAgentExecutions.map((sub) => clone(SubAgentExecutionSchema, sub));
|
|
1944
1979
|
}
|
|
1945
|
-
// ---------------------------------------------------------------------------
|
|
1946
|
-
// Structured Output Extraction (Cursor Harness Tier 2)
|
|
1947
|
-
// ---------------------------------------------------------------------------
|
|
1948
|
-
/**
|
|
1949
|
-
* Extract structured data from an agent's free-text response using an
|
|
1950
|
-
* economy-tier LLM with withStructuredOutput (function-calling).
|
|
1951
|
-
* Guarantees schema-conformant JSON output via the API's tool-use mechanism.
|
|
1952
|
-
*
|
|
1953
|
-
* Construction (registry-id resolution, provider inference, proxy wiring) is
|
|
1954
|
-
* delegated to the shared buildChatModel so the economy model's registry id is
|
|
1955
|
-
* always resolved to a provider API id before the call.
|
|
1956
|
-
*/
|
|
1957
|
-
async function extractStructuredOutput(agentResponse, schema, config, primaryModel) {
|
|
1958
|
-
const { getEconomyModel } = await import("../../shared/model-registry.js");
|
|
1959
|
-
const { buildChatModel } = await import("../../shared/model-client.js");
|
|
1960
|
-
const extractionModel = await getEconomyModel(primaryModel);
|
|
1961
|
-
const proxyEndpoint = config.proxyEndpoint ?? config.stigmerBackendEndpoint;
|
|
1962
|
-
const { model: llm } = await buildChatModel({
|
|
1963
|
-
modelName: extractionModel,
|
|
1964
|
-
proxyEndpoint,
|
|
1965
|
-
stigmerToken: config.stigmerToken ?? undefined,
|
|
1966
|
-
maxTokens: 4096,
|
|
1967
|
-
});
|
|
1968
|
-
const zodSchema = jsonSchemaToZod(schema);
|
|
1969
|
-
const structured = llm.withStructuredOutput(zodSchema);
|
|
1970
|
-
const result = await structured.invoke([
|
|
1971
|
-
{ role: "system", content: "Extract the structured data from the agent's response. Return only the data that matches the schema." },
|
|
1972
|
-
{ role: "user", content: agentResponse },
|
|
1973
|
-
]);
|
|
1974
|
-
return result ?? null;
|
|
1975
|
-
}
|
|
1976
|
-
// Re-export for use within this module; shared implementation eliminates
|
|
1977
|
-
// the three duplicate converters that previously drifted independently.
|
|
1978
|
-
import { jsonSchemaToZod } from "../../shared/json-schema-to-zod.js";
|
|
1979
1980
|
/**
|
|
1980
1981
|
* Select and build the appropriate prompt based on resolution reason and
|
|
1981
1982
|
* HITL state.
|
|
@@ -2004,7 +2005,7 @@ export function isHitlReinvocation(approvalDecisions) {
|
|
|
2004
2005
|
return approvalDecisions !== undefined && approvalDecisions.size > 0;
|
|
2005
2006
|
}
|
|
2006
2007
|
export function buildPrompt(input) {
|
|
2007
|
-
const { resolution, approvalDecisions, instructions, userMessage, skills, subAgents, workspaceDirs, workspaceFileRefs,
|
|
2008
|
+
const { resolution, approvalDecisions, instructions, userMessage, skills, subAgents, workspaceDirs, workspaceFileRefs, attachments, interactionMode, buildFromPlan, conversationCatchup, } = input;
|
|
2008
2009
|
// HITL reinvocation: the agent is resumed, so its native context carries the
|
|
2009
2010
|
// prior conversation; the reinvocation prompt conveys the approval decisions
|
|
2010
2011
|
// (and which approved writes the runner already exact-applied).
|
|
@@ -2028,9 +2029,9 @@ export function buildPrompt(input) {
|
|
|
2028
2029
|
if (resolution.reason === "resumed_successfully") {
|
|
2029
2030
|
const prefixes = [
|
|
2030
2031
|
formatInteractionModePrefix(interactionMode),
|
|
2031
|
-
formatImplementPlanSection(buildFromPlan,
|
|
2032
|
-
|
|
2033
|
-
? formatInputFiles(
|
|
2032
|
+
formatImplementPlanSection(buildFromPlan, attachments),
|
|
2033
|
+
attachments.length > 0
|
|
2034
|
+
? formatInputFiles(attachments, input.vision)
|
|
2034
2035
|
: undefined,
|
|
2035
2036
|
conversationCatchup !== undefined
|
|
2036
2037
|
? formatConversationCatchupSection(conversationCatchup)
|
|
@@ -2052,7 +2053,7 @@ export function buildPrompt(input) {
|
|
|
2052
2053
|
subAgents,
|
|
2053
2054
|
workspaceDirs,
|
|
2054
2055
|
workspaceFileRefs,
|
|
2055
|
-
|
|
2056
|
+
attachments,
|
|
2056
2057
|
vision: input.vision,
|
|
2057
2058
|
interactionMode,
|
|
2058
2059
|
buildFromPlan,
|