@stigmer/runner 3.12.4 → 3.12.6
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-agent.js +19 -6
- package/dist/activities/call-agent.js.map +1 -1
- package/dist/activities/execute-cursor/cursor-mcp-config.d.ts +45 -0
- package/dist/activities/execute-cursor/cursor-mcp-config.js +76 -0
- package/dist/activities/execute-cursor/cursor-mcp-config.js.map +1 -0
- package/dist/activities/execute-cursor/error-classifier.d.ts +9 -0
- package/dist/activities/execute-cursor/error-classifier.js +30 -1
- package/dist/activities/execute-cursor/error-classifier.js.map +1 -1
- package/dist/activities/execute-cursor/index.d.ts +10 -1
- package/dist/activities/execute-cursor/index.js +62 -36
- package/dist/activities/execute-cursor/index.js.map +1 -1
- package/dist/activities/execute-cursor/prompt-builder.d.ts +13 -2
- package/dist/activities/execute-cursor/prompt-builder.js +11 -0
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- package/dist/activities/execute-cursor/service-tier.d.ts +38 -29
- package/dist/activities/execute-cursor/service-tier.js +92 -63
- package/dist/activities/execute-cursor/service-tier.js.map +1 -1
- package/dist/activities/execute-cursor/session-lifecycle.d.ts +1 -1
- package/dist/activities/execute-cursor/skill-resolver.d.ts +2 -21
- package/dist/activities/execute-cursor/skill-resolver.js +5 -108
- package/dist/activities/execute-cursor/skill-resolver.js.map +1 -1
- package/dist/activities/execute-cursor/usage-accumulator.d.ts +16 -2
- package/dist/activities/execute-cursor/usage-accumulator.js +12 -2
- package/dist/activities/execute-cursor/usage-accumulator.js.map +1 -1
- package/dist/activities/execute-deep-agent/approval-file-change.d.ts +9 -0
- package/dist/activities/execute-deep-agent/approval-file-change.js +2 -1
- package/dist/activities/execute-deep-agent/approval-file-change.js.map +1 -1
- package/dist/activities/execute-deep-agent/cas-capture-backend.d.ts +18 -1
- package/dist/activities/execute-deep-agent/cas-capture-backend.js +20 -3
- package/dist/activities/execute-deep-agent/cas-capture-backend.js.map +1 -1
- package/dist/activities/execute-deep-agent/index.js +57 -5
- package/dist/activities/execute-deep-agent/index.js.map +1 -1
- package/dist/activities/execute-deep-agent/prompt-builder.d.ts +9 -0
- package/dist/activities/execute-deep-agent/prompt-builder.js +10 -0
- package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/setup.js +17 -16
- package/dist/activities/execute-deep-agent/setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.js +17 -11
- package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/middleware/path-normalization.d.ts +49 -49
- package/dist/middleware/path-normalization.js +73 -84
- package/dist/middleware/path-normalization.js.map +1 -1
- package/dist/middleware/types.d.ts +3 -3
- package/dist/runner-manager.d.ts +1 -1
- package/dist/runner-manager.js +22 -15
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner.js +11 -0
- package/dist/runner.js.map +1 -1
- package/dist/shared/args-preview.d.ts +8 -0
- package/dist/shared/args-preview.js +14 -3
- package/dist/shared/args-preview.js.map +1 -1
- package/dist/shared/artifact-storage.d.ts +11 -1
- package/dist/shared/artifact-storage.js +10 -1
- package/dist/shared/artifact-storage.js.map +1 -1
- package/dist/shared/caller-identity.d.ts +10 -7
- package/dist/shared/caller-identity.js +10 -7
- package/dist/shared/caller-identity.js.map +1 -1
- package/dist/shared/declared-preferences.d.ts +47 -0
- package/dist/shared/declared-preferences.js +64 -0
- package/dist/shared/declared-preferences.js.map +1 -0
- package/dist/shared/mcp-enabled-tools.d.ts +2 -3
- package/dist/shared/mcp-enabled-tools.js +2 -3
- package/dist/shared/mcp-enabled-tools.js.map +1 -1
- package/dist/shared/mcp-resolver.d.ts +20 -15
- package/dist/shared/mcp-resolver.js +11 -12
- package/dist/shared/mcp-resolver.js.map +1 -1
- package/dist/shared/placeholder-resolver.d.ts +9 -2
- package/dist/shared/placeholder-resolver.js +9 -2
- package/dist/shared/placeholder-resolver.js.map +1 -1
- package/dist/shared/plan-mode-permissions.d.ts +26 -46
- package/dist/shared/plan-mode-permissions.js +27 -55
- package/dist/shared/plan-mode-permissions.js.map +1 -1
- package/dist/shared/skill-mount.d.ts +89 -0
- package/dist/shared/skill-mount.js +142 -0
- package/dist/shared/skill-mount.js.map +1 -0
- package/dist/shared/skill-writer.d.ts +28 -26
- package/dist/shared/skill-writer.js +79 -102
- package/dist/shared/skill-writer.js.map +1 -1
- package/dist/shared/thinking-mode.d.ts +35 -0
- package/dist/shared/thinking-mode.js +43 -0
- package/dist/shared/thinking-mode.js.map +1 -0
- package/dist/shared/worker-shutdown.d.ts +67 -0
- package/dist/shared/worker-shutdown.js +79 -0
- package/dist/shared/worker-shutdown.js.map +1 -0
- package/dist/shared/workspace/types.d.ts +3 -2
- package/dist/workflow-engine/loader.js +82 -6
- package/dist/workflow-engine/loader.js.map +1 -1
- package/dist/workflow-engine/tasks/human-input.d.ts +2 -1
- package/dist/workflow-engine/tasks/human-input.js +31 -3
- package/dist/workflow-engine/tasks/human-input.js.map +1 -1
- package/dist/workflow-engine/types.d.ts +17 -3
- package/dist/workflow-engine/types.js.map +1 -1
- package/dist/workflows/human-input-orchestrator.d.ts +4 -0
- package/dist/workflows/human-input-orchestrator.js +13 -0
- package/dist/workflows/human-input-orchestrator.js.map +1 -1
- package/package.json +15 -11
- package/src/activities/call-agent.ts +20 -6
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +50 -0
- package/src/activities/execute-cursor/__tests__/cursor-mcp-config.test.ts +111 -0
- package/src/activities/execute-cursor/__tests__/error-classifier-billing.test.ts +67 -0
- package/src/activities/execute-cursor/__tests__/service-tier.test.ts +67 -7
- package/src/activities/execute-cursor/__tests__/skill-resolver.test.ts +3 -94
- package/src/activities/execute-cursor/__tests__/usage-accumulator.test.ts +33 -1
- package/src/activities/execute-cursor/cursor-mcp-config.ts +107 -0
- package/src/activities/execute-cursor/error-classifier.ts +42 -1
- package/src/activities/execute-cursor/index.ts +78 -41
- package/src/activities/execute-cursor/prompt-builder.ts +29 -2
- package/src/activities/execute-cursor/service-tier.ts +94 -63
- package/src/activities/execute-cursor/session-lifecycle.ts +1 -1
- package/src/activities/execute-cursor/skill-resolver.ts +10 -137
- package/src/activities/execute-cursor/usage-accumulator.ts +11 -1
- package/src/activities/execute-deep-agent/__tests__/approval-file-change.test.ts +15 -0
- package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +75 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-reject.test.ts +1 -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__/index.test.ts +1 -0
- package/src/activities/execute-deep-agent/__tests__/plan-mode-path-normalization.test.ts +42 -29
- package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +52 -0
- 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 +11 -8
- package/src/activities/execute-deep-agent/approval-file-change.ts +11 -1
- package/src/activities/execute-deep-agent/cas-capture-backend.ts +20 -3
- package/src/activities/execute-deep-agent/index.ts +57 -5
- package/src/activities/execute-deep-agent/prompt-builder.ts +22 -0
- package/src/activities/execute-deep-agent/setup.ts +21 -19
- package/src/activities/execute-deep-agent/subagent-transformer.ts +19 -12
- package/src/activities/execute-deep-agent/subagent-wiring.ts +3 -3
- package/src/index.ts +1 -1
- package/src/middleware/__tests__/path-normalization.test.ts +32 -32
- package/src/middleware/path-normalization.ts +78 -90
- package/src/middleware/types.ts +3 -3
- package/src/runner-manager.ts +26 -16
- package/src/runner.ts +14 -0
- package/src/shared/__tests__/artifact-storage.test.ts +28 -0
- package/src/shared/__tests__/bedrock-seam.test.ts +5 -5
- package/src/shared/__tests__/declared-preferences.test.ts +109 -0
- package/src/shared/__tests__/foundry-seam.test.ts +5 -5
- package/src/shared/__tests__/plan-mode-permissions.test.ts +15 -44
- package/src/shared/__tests__/skill-mount.test.ts +238 -0
- package/src/shared/__tests__/skill-writer.test.ts +151 -128
- package/src/shared/__tests__/vertex-adapter.test.ts +30 -3
- package/src/shared/__tests__/vertex-seam.test.ts +5 -5
- package/src/shared/__tests__/worker-shutdown.test.ts +101 -0
- package/src/shared/args-preview.ts +17 -3
- package/src/shared/artifact-storage.ts +20 -1
- package/src/shared/caller-identity.ts +10 -7
- package/src/shared/declared-preferences.ts +84 -0
- package/src/shared/mcp-enabled-tools.ts +2 -3
- package/src/shared/mcp-resolver.ts +20 -20
- package/src/shared/placeholder-resolver.ts +9 -2
- package/src/shared/plan-mode-permissions.ts +27 -58
- package/src/shared/skill-mount.ts +179 -0
- package/src/shared/skill-writer.ts +96 -130
- package/src/shared/thinking-mode.ts +52 -0
- package/src/shared/worker-shutdown.ts +99 -0
- package/src/shared/workspace/types.ts +3 -2
- package/src/workflow-engine/__tests__/loader.test.ts +95 -0
- package/src/workflow-engine/__tests__/tasks/human-input.test.ts +156 -0
- package/src/workflow-engine/loader.ts +97 -6
- package/src/workflow-engine/tasks/human-input.ts +41 -6
- package/src/workflow-engine/types.ts +18 -3
- package/src/workflows/human-input-orchestrator.ts +20 -2
- package/dist/activities/execute-cursor/connect-backfill.d.ts +0 -19
- package/dist/activities/execute-cursor/connect-backfill.js +0 -27
- package/dist/activities/execute-cursor/connect-backfill.js.map +0 -1
- package/dist/activities/execute-cursor/mcp-resolver.d.ts +0 -120
- package/dist/activities/execute-cursor/mcp-resolver.js +0 -194
- package/dist/activities/execute-cursor/mcp-resolver.js.map +0 -1
- package/dist/activities/execute-cursor/placeholder-resolver.d.ts +0 -34
- package/dist/activities/execute-cursor/placeholder-resolver.js +0 -82
- package/dist/activities/execute-cursor/placeholder-resolver.js.map +0 -1
- package/src/activities/execute-cursor/__tests__/mcp-resolver.test.ts +0 -125
- package/src/activities/execute-cursor/connect-backfill.ts +0 -52
- package/src/activities/execute-cursor/mcp-resolver.ts +0 -324
- package/src/activities/execute-cursor/placeholder-resolver.ts +0 -109
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
*
|
|
5
5
|
* This is NOT a unit test of our code (there is no production vertex code
|
|
6
6
|
* yet). It pins the exact cross-package behavior production will rely on:
|
|
7
|
-
* the REAL `ChatAnthropic` (@langchain/anthropic
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* the REAL `ChatAnthropic` (@langchain/anthropic) driving the REAL
|
|
8
|
+
* `AnthropicVertex` client (@anthropic-ai/vertex-sdk), both resolving the
|
|
9
|
+
* single override-pinned @anthropic-ai/sdk copy. If a future bump of
|
|
10
10
|
* either side changes request shaping, streaming event handling, tool-call
|
|
11
11
|
* assembly, or usage accounting across this seam, this suite fails in CI
|
|
12
|
-
* instead of production. See scripts/check-langchain-deps.sh for
|
|
13
|
-
*
|
|
12
|
+
* instead of production. See scripts/check-langchain-deps.sh for the
|
|
13
|
+
* override rationale and the single-copy invariant it guards.
|
|
14
14
|
*
|
|
15
15
|
* Determinism: zero credentials, zero network. Google auth is bypassed by
|
|
16
16
|
* injecting a fake `authClient` (the SDK's supported constructor option —
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
classifyTurnInterruption,
|
|
4
|
+
getShutdownSignalForQueue,
|
|
5
|
+
registerWorkerShutdownSignal,
|
|
6
|
+
signalWorkerShutdown,
|
|
7
|
+
unregisterWorkerShutdownSignal,
|
|
8
|
+
} from "../worker-shutdown.js";
|
|
9
|
+
|
|
10
|
+
describe("worker-shutdown signal registry", () => {
|
|
11
|
+
it("registers a live signal an activity can resolve by queue name", () => {
|
|
12
|
+
const controller = registerWorkerShutdownSignal("session:reg-1");
|
|
13
|
+
const signal = getShutdownSignalForQueue("session:reg-1");
|
|
14
|
+
expect(signal).toBe(controller.signal);
|
|
15
|
+
expect(signal?.aborted).toBe(false);
|
|
16
|
+
unregisterWorkerShutdownSignal("session:reg-1");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("aborting via signalWorkerShutdown marks the queue's signal (idempotent, unknown queues are no-ops)", () => {
|
|
20
|
+
registerWorkerShutdownSignal("session:abort-1");
|
|
21
|
+
signalWorkerShutdown("session:abort-1");
|
|
22
|
+
signalWorkerShutdown("session:abort-1");
|
|
23
|
+
expect(getShutdownSignalForQueue("session:abort-1")?.aborted).toBe(true);
|
|
24
|
+
expect(() => signalWorkerShutdown("session:never-registered")).not.toThrow();
|
|
25
|
+
unregisterWorkerShutdownSignal("session:abort-1");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("re-registering a reused queue replaces an aborted signal with a fresh one", () => {
|
|
29
|
+
// A re-created worker on a reused queue (desktop re-opens a session) must
|
|
30
|
+
// not observe the previous worker's aborted signal as its own shutdown.
|
|
31
|
+
const first = registerWorkerShutdownSignal("session:reuse-1");
|
|
32
|
+
first.abort();
|
|
33
|
+
const second = registerWorkerShutdownSignal("session:reuse-1");
|
|
34
|
+
expect(getShutdownSignalForQueue("session:reuse-1")).toBe(second.signal);
|
|
35
|
+
expect(getShutdownSignalForQueue("session:reuse-1")?.aborted).toBe(false);
|
|
36
|
+
unregisterWorkerShutdownSignal("session:reuse-1");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("unregistering removes the signal", () => {
|
|
40
|
+
registerWorkerShutdownSignal("session:gone-1");
|
|
41
|
+
unregisterWorkerShutdownSignal("session:gone-1");
|
|
42
|
+
expect(getShutdownSignalForQueue("session:gone-1")).toBeUndefined();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("classifyTurnInterruption", () => {
|
|
47
|
+
const none = {
|
|
48
|
+
heartbeatCancelled: false,
|
|
49
|
+
heartbeatWorkerShutdown: false,
|
|
50
|
+
cancellationSignalAborted: false,
|
|
51
|
+
shutdownSignalAborted: false,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
it("an uninterrupted turn is none", () => {
|
|
55
|
+
expect(classifyTurnInterruption(none)).toBe("none");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("GRACE-WINDOW GUARD (#776): an aborted shutdown signal alone stays none — a run that completed inside the drain grace window must not be failed", () => {
|
|
59
|
+
expect(
|
|
60
|
+
classifyTurnInterruption({ ...none, shutdownSignalAborted: true }),
|
|
61
|
+
).toBe("none");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("heartbeat cancellation without a shutdown signal is the orchestrator's pause", () => {
|
|
65
|
+
expect(
|
|
66
|
+
classifyTurnInterruption({ ...none, heartbeatCancelled: true }),
|
|
67
|
+
).toBe("pause");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("heartbeat cancellation WITH the shutdown signal aborted is a worker shutdown, not a pause (the #776 misclassification)", () => {
|
|
71
|
+
expect(
|
|
72
|
+
classifyTurnInterruption({
|
|
73
|
+
...none,
|
|
74
|
+
heartbeatCancelled: true,
|
|
75
|
+
shutdownSignalAborted: true,
|
|
76
|
+
}),
|
|
77
|
+
).toBe("worker-shutdown");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("the heartbeat's own workerShutdown flag classifies directly", () => {
|
|
81
|
+
expect(
|
|
82
|
+
classifyTurnInterruption({ ...none, heartbeatWorkerShutdown: true }),
|
|
83
|
+
).toBe("worker-shutdown");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("a delivered cancellation with the shutdown signal aborted is a worker shutdown (signal races the heartbeat tick)", () => {
|
|
87
|
+
expect(
|
|
88
|
+
classifyTurnInterruption({
|
|
89
|
+
...none,
|
|
90
|
+
cancellationSignalAborted: true,
|
|
91
|
+
shutdownSignalAborted: true,
|
|
92
|
+
}),
|
|
93
|
+
).toBe("worker-shutdown");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("a delivered cancellation alone (heartbeat timeout / infra cancel) is neither shutdown nor pause", () => {
|
|
97
|
+
expect(
|
|
98
|
+
classifyTurnInterruption({ ...none, cancellationSignalAborted: true }),
|
|
99
|
+
).toBe("none");
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -34,11 +34,25 @@ export const MAX_ARGS_PREVIEW_LENGTH = 500;
|
|
|
34
34
|
* Cursor gate path must instead use {@link buildElidedArgsPreview}, which keeps
|
|
35
35
|
* the JSON valid and preserves the salient identity fields.
|
|
36
36
|
*/
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Redact secret-keyed values (see {@link SENSITIVE_ARG_KEYS}), preserving every
|
|
39
|
+
* other entry verbatim. The shared first step of both preview builders, and the
|
|
40
|
+
* shape stamped as `args` on interrupt-placeholder tool calls (issue #754's
|
|
41
|
+
* header fix): full enough for the UI's path/primary-arg extraction, never
|
|
42
|
+
* carrying a secret value.
|
|
43
|
+
*/
|
|
44
|
+
export function redactSensitiveArgs(
|
|
45
|
+
args: Record<string, unknown>,
|
|
46
|
+
): Record<string, unknown> {
|
|
47
|
+
const redacted: Record<string, unknown> = {};
|
|
39
48
|
for (const [key, value] of Object.entries(args)) {
|
|
40
|
-
|
|
49
|
+
redacted[key] = SENSITIVE_ARG_KEYS.has(key.toLowerCase()) ? "[REDACTED]" : value;
|
|
41
50
|
}
|
|
51
|
+
return redacted;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function sanitizeArgsPreview(args: Record<string, unknown>): string {
|
|
55
|
+
const sanitized = redactSensitiveArgs(args);
|
|
42
56
|
try {
|
|
43
57
|
const json = JSON.stringify(sanitized);
|
|
44
58
|
return json.length > MAX_ARGS_PREVIEW_LENGTH
|
|
@@ -56,7 +56,17 @@ export interface ArtifactStorage {
|
|
|
56
56
|
exists(key: string): Promise<boolean>;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
/**
|
|
60
|
+
* "none" is the explicit no-store posture: capture degrades to the deny-gate
|
|
61
|
+
* (file writes gated pre-execution, DD-22/DD-26) and offload is disabled —
|
|
62
|
+
* the same first-class absent-store state a proxy misconfig or unwritable
|
|
63
|
+
* local path resolves to, but chosen deliberately (ARTIFACT_STORAGE_TYPE=none)
|
|
64
|
+
* instead of reached by failure. The e2e file-gate stack boots with it, and
|
|
65
|
+
* an operator can use it to force gate-everything behavior. It is only valid
|
|
66
|
+
* where an absent store is: claimcheck (fail-hard by contract) refuses it at
|
|
67
|
+
* construction.
|
|
68
|
+
*/
|
|
69
|
+
export type ArtifactStorageType = "local" | "proxy" | "none";
|
|
60
70
|
|
|
61
71
|
// ── Local Backend ────────────────────────────────────────────────────
|
|
62
72
|
|
|
@@ -371,6 +381,7 @@ export function loadArtifactStorageConfig(config: Config): ArtifactStorageConfig
|
|
|
371
381
|
const type: ArtifactStorageType =
|
|
372
382
|
envType === "proxy" ? "proxy" :
|
|
373
383
|
envType === "local" ? "local" :
|
|
384
|
+
envType === "none" ? "none" :
|
|
374
385
|
config.proxyEndpoint ? "proxy" : "local";
|
|
375
386
|
|
|
376
387
|
return {
|
|
@@ -387,6 +398,14 @@ export function loadArtifactStorageConfig(config: Config): ArtifactStorageConfig
|
|
|
387
398
|
}
|
|
388
399
|
|
|
389
400
|
export function createArtifactStorage(cfg: ArtifactStorageConfig): ArtifactStorage {
|
|
401
|
+
if (cfg.type === "none") {
|
|
402
|
+
// Deliberate no-store posture. Throwing keeps both consumers honest:
|
|
403
|
+
// resolveUsableArtifactStorage catches and degrades to the deny-gate
|
|
404
|
+
// (the intent of "none"), while claimcheck's direct fail-hard call
|
|
405
|
+
// surfaces the config contradiction at boot instead of silently
|
|
406
|
+
// dropping Temporal payloads.
|
|
407
|
+
throw new Error("ARTIFACT_STORAGE_TYPE=none: artifact storage deliberately disabled");
|
|
408
|
+
}
|
|
390
409
|
if (cfg.type === "proxy") {
|
|
391
410
|
if (!cfg.proxyEndpoint) {
|
|
392
411
|
throw new Error("Proxy artifact storage requires STIGMER_PROXY_ENDPOINT");
|
|
@@ -58,15 +58,18 @@ export const ANONYMOUS_KIND = "anonymous";
|
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* Audit-actor id that backends stamp when NO caller identity exists —
|
|
61
|
-
* the OSS server writes it on every create
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
61
|
+
* the OSS server writes it on every create unless the deployment
|
|
62
|
+
* configured an operator identity (STIGMER_OPERATOR_EMAIL,
|
|
63
|
+
* stigmer/stigmer#400; a configured install stamps a real actor whose
|
|
64
|
+
* email resolves below like any other), and the cloud's
|
|
65
|
+
* AuditActorBuilder falls back to it for caller-less internal writes.
|
|
66
|
+
* It names "nobody in particular": unrelated sessions from unrelated
|
|
67
|
+
* people all carry it, so presenting it as a caller identity would make
|
|
68
|
+
* the one string a grantable value that silently covers ALL such
|
|
69
|
+
* traffic in an MCP server's binding sheet. A creator matching this
|
|
67
70
|
* sentinel (and carrying no email) is therefore unresolvable and falls
|
|
68
71
|
* to anonymous — the deny-by-default the docs guide already promises
|
|
69
|
-
* for self-hosted backends.
|
|
72
|
+
* for unconfigured self-hosted backends.
|
|
70
73
|
*/
|
|
71
74
|
export const SYSTEM_CREATOR_SENTINEL = "system";
|
|
72
75
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declared preferences (stigmer/stigmer#293, DD-002): standing free-text
|
|
3
|
+
* preferences the organization and the calling user declared once — "keep
|
|
4
|
+
* answers terse", "we deploy to us-east-1" — that every eligible execution
|
|
5
|
+
* receives without the user re-typing them.
|
|
6
|
+
*
|
|
7
|
+
* The server composes the CONTENT at execution create: the create pipeline
|
|
8
|
+
* snapshots `Organization.spec.preferences.standing_context` and (cloud, for
|
|
9
|
+
* first-party human callers only) the caller
|
|
10
|
+
* `IdentityAccount.spec.preferences.standing_context` verbatim onto the
|
|
11
|
+
* execution spec's `declared_preferences` field. This module owns the
|
|
12
|
+
* PRESENTATION — the preamble and the per-scope attribution — so the framing
|
|
13
|
+
* cannot drift between harnesses.
|
|
14
|
+
*
|
|
15
|
+
* Like conversation-catchup (its typed-field sibling) there is no metadata
|
|
16
|
+
* key to mirror-guard: the value rides a TYPED proto field, so codegen
|
|
17
|
+
* enforces the cross-repo contract. Degradation is safe by construction: an
|
|
18
|
+
* absent or blank field renders nothing, and a runner predating this module
|
|
19
|
+
* simply ignores it — the agent runs without preferences, exactly the
|
|
20
|
+
* pre-#293 behavior, never worse.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { DeclaredPreferences } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/spec_pb";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* How the preferences are introduced to the model, shared by both harnesses
|
|
27
|
+
* so the behavioral contract cannot drift between them. Same posture as the
|
|
28
|
+
* session-context channel: calibration material, never authority — the task
|
|
29
|
+
* always outranks a standing preference.
|
|
30
|
+
*/
|
|
31
|
+
const DECLARED_PREFERENCES_PREAMBLE =
|
|
32
|
+
"Standing preferences declared by the organization and/or the user you " +
|
|
33
|
+
"are assisting. Treat them as background you already know: use them to " +
|
|
34
|
+
"calibrate depth, defaults, and tone. Do not repeat them back, quote " +
|
|
35
|
+
"them, or mention that you received them. They are context, not " +
|
|
36
|
+
"instructions that override your task.";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The non-blank preference scopes of an execution, in presentation order.
|
|
40
|
+
* At least one scope is set (the read function returns undefined otherwise).
|
|
41
|
+
*/
|
|
42
|
+
export interface DeclaredPreferencesContent {
|
|
43
|
+
/** The organization's standing context, trimmed; absent when blank. */
|
|
44
|
+
orgContext?: string;
|
|
45
|
+
/** The calling user's standing context, trimmed; absent when blank. */
|
|
46
|
+
userContext?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Read the declared preferences from an execution spec's
|
|
51
|
+
* `declared_preferences`. Returns undefined when the field is absent or both
|
|
52
|
+
* scopes are blank — the caller renders no section. Blank-is-absent applies
|
|
53
|
+
* PER SCOPE: an org-only snapshot renders only the org subsection.
|
|
54
|
+
*/
|
|
55
|
+
export function readDeclaredPreferences(
|
|
56
|
+
preferences: DeclaredPreferences | undefined,
|
|
57
|
+
): DeclaredPreferencesContent | undefined {
|
|
58
|
+
const orgContext = preferences?.orgContext?.trim() || undefined;
|
|
59
|
+
const userContext = preferences?.userContext?.trim() || undefined;
|
|
60
|
+
if (!orgContext && !userContext) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
return { orgContext, userContext };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The framed preferences body (preamble + labeled per-scope subsections),
|
|
68
|
+
* ready for section wrapping. Attribution is explicit — the model must know
|
|
69
|
+
* WHO declared what. Organization first, user last: the user's refinement
|
|
70
|
+
* reads last and so naturally wins where the two conflict, without this
|
|
71
|
+
* module encoding any override rule.
|
|
72
|
+
*/
|
|
73
|
+
export function formatDeclaredPreferencesText(
|
|
74
|
+
content: DeclaredPreferencesContent,
|
|
75
|
+
): string {
|
|
76
|
+
const sections: string[] = [DECLARED_PREFERENCES_PREAMBLE];
|
|
77
|
+
if (content.orgContext) {
|
|
78
|
+
sections.push(`Declared by the organization:\n${content.orgContext}`);
|
|
79
|
+
}
|
|
80
|
+
if (content.userContext) {
|
|
81
|
+
sections.push(`Declared by the user:\n${content.userContext}`);
|
|
82
|
+
}
|
|
83
|
+
return sections.join("\n\n");
|
|
84
|
+
}
|
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
* - An empty usage list falls back to the server's default_enabled_tools.
|
|
8
8
|
* - Both empty means NO restriction — every discovered tool is available.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* duplicated resolvers — see the header of shared/mcp-resolver.ts):
|
|
10
|
+
* Resolution is shared (shared/mcp-resolver.ts threads the effective list
|
|
11
|
+
* onto every ResolvedMcpServer), but ENFORCEMENT is per-harness:
|
|
13
12
|
* - The deep-agent harness filters the discovered toolset before it reaches
|
|
14
13
|
* the model (shared/mcp-manager.ts connectMcpServers).
|
|
15
14
|
* - The Cursor harness cannot hide tools (the Cursor SDK's McpServerConfig
|
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
* Resolves Stigmer McpServerUsage references into an intermediate
|
|
3
3
|
* ResolvedMcpServer format.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
5
|
+
* THE single resolver for both harnesses (oss#387 retired the Cursor
|
|
6
|
+
* harness's near-duplicate): the deep-agent harness
|
|
7
|
+
* (execute-deep-agent/setup.ts), the Cursor harness
|
|
8
|
+
* (execute-cursor/index.ts), the connect backfill
|
|
9
|
+
* (shared/connect-backfill.ts), and the discovery activity
|
|
10
|
+
* (activities/discover-mcp-server.ts, via mcpServerToResolved) all consume
|
|
11
|
+
* it. Each harness maps the result into its SDK format at the last hop:
|
|
12
|
+
* toMcpClientConfig (shared/mcp-manager.ts) for LangChain,
|
|
13
|
+
* toCursorMcpConfig (execute-cursor/cursor-mcp-config.ts) for the Cursor
|
|
14
|
+
* SDK — so a behavioral change here (like the transport guard) lands in
|
|
15
|
+
* both execution paths by construction.
|
|
12
16
|
*/
|
|
13
17
|
|
|
14
18
|
import type { McpServerUsage, ToolApprovalOverride } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/spec_pb";
|
|
@@ -51,8 +55,11 @@ export interface ResolvedMcpServer {
|
|
|
51
55
|
* enabled_tools, falling back to the server's default_enabled_tools when
|
|
52
56
|
* the usage's list is empty (see shared/mcp-enabled-tools.ts). Absent when
|
|
53
57
|
* unrestricted — which keeps synthesized attachment servers (built without
|
|
54
|
-
* a usage) and the discovery path unfiltered by construction.
|
|
55
|
-
*
|
|
58
|
+
* a usage) and the discovery path unfiltered by construction. Enforcement
|
|
59
|
+
* is per-harness: the deep-agent path filters at connect time
|
|
60
|
+
* (connectMcpServers, shared/mcp-manager.ts); the Cursor SDK config cannot
|
|
61
|
+
* hide tools, so that harness denies non-enabled calls in the HITL hook
|
|
62
|
+
* (see hook-script.ts).
|
|
56
63
|
*/
|
|
57
64
|
enabledTools?: string[];
|
|
58
65
|
/**
|
|
@@ -61,11 +68,10 @@ export interface ResolvedMcpServer {
|
|
|
61
68
|
* override to its own server — a flat cross-server list is how an
|
|
62
69
|
* override once leaked onto (or silently un-gated) a same-named tool on
|
|
63
70
|
* another server. Deliberately REQUIRED, not optional: empty means "no
|
|
64
|
-
* overrides", and every construction site — including the
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* (shared/approval-policy.ts).
|
|
71
|
+
* overrides", and every construction site — including the synthesized
|
|
72
|
+
* attachments, which have no usage and therefore no layer 3 — must say so
|
|
73
|
+
* explicitly, so a forgotten site cannot compile. Consumed by
|
|
74
|
+
* mergeApprovalPolicies (shared/approval-policy.ts).
|
|
69
75
|
*/
|
|
70
76
|
toolApprovalOverrides: ToolApprovalOverride[];
|
|
71
77
|
}
|
|
@@ -202,12 +208,6 @@ export function mcpServerToResolved(
|
|
|
202
208
|
}
|
|
203
209
|
}
|
|
204
210
|
|
|
205
|
-
export function extractMcpServerSlugs(usages: McpServerUsage[]): string[] {
|
|
206
|
-
return usages
|
|
207
|
-
.map((u) => u.mcpServerRef?.slug)
|
|
208
|
-
.filter((s): s is string => !!s);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
211
|
/**
|
|
212
212
|
* Merge MCP server usages from agent (base) and session (overlay).
|
|
213
213
|
*
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Strict placeholder resolver for ${VAR_NAME} syntax in MCP server configs.
|
|
3
3
|
*
|
|
4
|
+
* Port of the Python agent-runner's PlaceholderResolver (strict mode only).
|
|
4
5
|
* Always raises on unresolved placeholders — sending literal ${VAR} as an
|
|
5
|
-
* HTTP header value
|
|
6
|
+
* HTTP header value (e.g. Authorization: Bearer ${API_KEY}) produces
|
|
7
|
+
* cryptic auth failures from the remote server.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
10
|
const PLACEHOLDER_RE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
@@ -41,6 +43,9 @@ export function resolvePlaceholders(
|
|
|
41
43
|
|
|
42
44
|
/**
|
|
43
45
|
* Resolve placeholders in all values of a headers map.
|
|
46
|
+
*
|
|
47
|
+
* @throws PlaceholderResolutionError if any header value contains an
|
|
48
|
+
* unresolvable placeholder.
|
|
44
49
|
*/
|
|
45
50
|
export function resolveHeaders(
|
|
46
51
|
headers: Record<string, string>,
|
|
@@ -55,7 +60,9 @@ export function resolveHeaders(
|
|
|
55
60
|
|
|
56
61
|
/**
|
|
57
62
|
* Filter env vars to only keys declared in the MCP server's spec.env.
|
|
58
|
-
*
|
|
63
|
+
*
|
|
64
|
+
* Mirrors the agent-runner's _filter_env_to_declared_keys: prevents
|
|
65
|
+
* secret over-sharing by restricting the subprocess/HTTP environment to
|
|
59
66
|
* explicitly declared variables.
|
|
60
67
|
*/
|
|
61
68
|
export function filterEnvToDeclaredKeys(
|
|
@@ -2,26 +2,25 @@
|
|
|
2
2
|
* The Plan-mode filesystem permission rules — the enforcement twin of
|
|
3
3
|
* `plan-mode-prompt.ts` (which carries the instruction half of the contract).
|
|
4
4
|
*
|
|
5
|
-
* Plan mode is
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
5
|
+
* Plan mode's policy is: reads anywhere in the workspace, writes nowhere.
|
|
6
|
+
* Only the write half needs a RULE. The read boundary that issue #528 built
|
|
7
|
+
* out of rules (workspace read-allow + read-deny, glob-escaped real root) is
|
|
8
|
+
* now STRUCTURAL: every native-harness backend is virtual-rooted
|
|
9
|
+
* (`virtualMode: true` — see cas-capture-backend.ts, issue #754), so every
|
|
10
|
+
* path a tool can express resolves inside the workspace root and traversal
|
|
11
|
+
* is rejected at resolution. A rule cannot widen that, and no workspace-root
|
|
12
|
+
* glob is needed to narrow it — which also retires the whole
|
|
13
|
+
* special-characters-in-the-root hazard (`escapeGlobLiteral` and its
|
|
14
|
+
* end-to-end matcher suite) that the real-path rules carried.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
* path (
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* both; do not "harden" this into one.
|
|
16
|
+
* Paths in these rules are VIRTUAL: "/" is the workspace root, matched by
|
|
17
|
+
* micromatch against the raw tool-call path (dot:true) after the
|
|
18
|
+
* path-normalization middleware has canonicalized it (relative → "/"-rooted;
|
|
19
|
+
* see middleware/path-normalization.ts). The legitimate platform-dir reads
|
|
20
|
+
* (skills, attachments, the approved plan) keep working exactly as before:
|
|
21
|
+
* they are addressed through the `{workspace}/.stigmer` symlink
|
|
22
|
+
* (shared/workspace/stigmer-link.ts), an in-root path string in either
|
|
23
|
+
* dialect.
|
|
25
24
|
*
|
|
26
25
|
* Applied in execute-deep-agent/setup.ts to the parent graph AND threaded
|
|
27
26
|
* into every compiled sub-agent graph: deepagents' parent-permission
|
|
@@ -30,13 +29,6 @@
|
|
|
30
29
|
* (issue #255). Kept as its own side-effect-free module so tests can pin the
|
|
31
30
|
* production rules without dragging in setup.ts's import graph.
|
|
32
31
|
*
|
|
33
|
-
* Rules travel with a companion: every graph that carries them also installs
|
|
34
|
-
* the path-normalization middleware (middleware/path-normalization.ts,
|
|
35
|
-
* issues #429/#528), because deepagents' rule validation refuses
|
|
36
|
-
* workspace-relative paths outright and its `ls`/`glob`/`grep` schema default
|
|
37
|
-
* of "/" would deny the bare first listing. Both are derived from the same
|
|
38
|
-
* expression at each composition site so they cannot drift apart.
|
|
39
|
-
*
|
|
40
32
|
* Invariant: never combine these rules with a shell-capable (sandbox)
|
|
41
33
|
* backend — deepagents rejects that pairing at graph construction (see the
|
|
42
34
|
* cas-capture-backend.ts header). Plan mode guarantees it by construction:
|
|
@@ -44,41 +36,18 @@
|
|
|
44
36
|
* for shell capability on both the parent and sub-agent backends.
|
|
45
37
|
*/
|
|
46
38
|
|
|
47
|
-
import { resolve } from "node:path";
|
|
48
39
|
import type { FilesystemPermission } from "deepagents";
|
|
49
40
|
|
|
50
41
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* silently never match — bricking every plan-mode read for that workspace.
|
|
56
|
-
* Semantics are pinned end-to-end through deepagents' own matcher by the
|
|
57
|
-
* special-character workspace suite in plan-mode-path-normalization.test.ts.
|
|
58
|
-
*/
|
|
59
|
-
export function escapeGlobLiteral(literal: string): string {
|
|
60
|
-
return literal.replace(/[\\*?()[\]{}!+@]/g, "\\$&");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Build the plan-mode rule set for a graph whose filesystem backend is
|
|
65
|
-
* rooted at `workspaceRootDir`. The three rules read as the policy:
|
|
66
|
-
* reads allowed in the workspace, reads denied everywhere else, writes
|
|
67
|
-
* denied everywhere.
|
|
42
|
+
* Build the plan-mode rule set. One rule IS the policy: writes denied
|
|
43
|
+
* everywhere ("/**" is every virtual path; deepagents evaluates
|
|
44
|
+
* first-match-wins with a permissive default, so reads stay allowed —
|
|
45
|
+
* workspace-confined structurally, not by rule).
|
|
68
46
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* paths (collapsed slashes, no trailing separator) before matching — a
|
|
73
|
-
* trailing slash in the pattern would silently match nothing.
|
|
47
|
+
* Deliberately parameterless: the pre-#754 signature took the workspace root
|
|
48
|
+
* to build a real-path read fence; resurrecting a root-derived rule would
|
|
49
|
+
* silently mismatch the virtual dialect the enforcement now sees.
|
|
74
50
|
*/
|
|
75
|
-
export function buildPlanModePermissions(
|
|
76
|
-
|
|
77
|
-
): FilesystemPermission[] {
|
|
78
|
-
const canonicalRoot = resolve(workspaceRootDir);
|
|
79
|
-
return [
|
|
80
|
-
{ operations: ["read"], paths: [`${escapeGlobLiteral(canonicalRoot)}/**`] },
|
|
81
|
-
{ operations: ["read"], paths: ["/**"], mode: "deny" },
|
|
82
|
-
{ operations: ["write"], paths: ["/**"], mode: "deny" },
|
|
83
|
-
];
|
|
51
|
+
export function buildPlanModePermissions(): FilesystemPermission[] {
|
|
52
|
+
return [{ operations: ["write"], paths: ["/**"], mode: "deny" }];
|
|
84
53
|
}
|