@stigmer/runner 3.1.0 → 3.1.2
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/execute-cursor/attachment-resolver.d.ts +49 -8
- package/dist/activities/execute-cursor/attachment-resolver.js +83 -26
- package/dist/activities/execute-cursor/attachment-resolver.js.map +1 -1
- package/dist/activities/execute-cursor/cas-observations.d.ts +13 -0
- package/dist/activities/execute-cursor/cas-observations.js +39 -5
- package/dist/activities/execute-cursor/cas-observations.js.map +1 -1
- package/dist/activities/execute-cursor/hook-script.js +19 -1
- package/dist/activities/execute-cursor/hook-script.js.map +1 -1
- package/dist/activities/execute-cursor/index.d.ts +5 -0
- package/dist/activities/execute-cursor/index.js +102 -22
- package/dist/activities/execute-cursor/index.js.map +1 -1
- package/dist/activities/execute-cursor/message-translator.js +10 -1
- package/dist/activities/execute-cursor/message-translator.js.map +1 -1
- package/dist/activities/execute-cursor/prompt-builder.d.ts +32 -7
- package/dist/activities/execute-cursor/prompt-builder.js +38 -20
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- package/dist/activities/execute-cursor/skill-resolver.d.ts +1 -10
- package/dist/activities/execute-cursor/skill-resolver.js +3 -55
- package/dist/activities/execute-cursor/skill-resolver.js.map +1 -1
- package/dist/activities/execute-cursor/stigmer-link.d.ts +35 -0
- package/dist/activities/execute-cursor/stigmer-link.js +73 -0
- package/dist/activities/execute-cursor/stigmer-link.js.map +1 -0
- package/dist/activities/execute-cursor/workspace-provision.d.ts +3 -2
- package/dist/activities/execute-cursor/workspace-provision.js +5 -3
- package/dist/activities/execute-cursor/workspace-provision.js.map +1 -1
- package/dist/activities/execute-deep-agent/index.js +59 -2
- package/dist/activities/execute-deep-agent/index.js.map +1 -1
- package/dist/activities/execute-deep-agent/prompt-builder.d.ts +17 -0
- package/dist/activities/execute-deep-agent/prompt-builder.js +16 -0
- package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/setup.js +22 -16
- package/dist/activities/execute-deep-agent/setup.js.map +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/middleware/approval-gate.js +36 -6
- package/dist/middleware/approval-gate.js.map +1 -1
- package/dist/runner-manager.d.ts +2 -0
- package/dist/runner-manager.js +2 -1
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner.d.ts +2 -0
- package/dist/runner.js +2 -1
- package/dist/runner.js.map +1 -1
- package/dist/shared/artifact-storage.d.ts +32 -0
- package/dist/shared/artifact-storage.js +74 -1
- package/dist/shared/artifact-storage.js.map +1 -1
- package/dist/shared/implement-plan-prompt.d.ts +42 -0
- package/dist/shared/implement-plan-prompt.js +66 -0
- package/dist/shared/implement-plan-prompt.js.map +1 -0
- package/dist/shared/plan-mode-prompt.d.ts +30 -0
- package/dist/shared/plan-mode-prompt.js +51 -0
- package/dist/shared/plan-mode-prompt.js.map +1 -0
- package/dist/shared/status.d.ts +11 -0
- package/dist/shared/status.js +12 -2
- package/dist/shared/status.js.map +1 -1
- package/dist/shared/tool-row.d.ts +30 -1
- package/dist/shared/tool-row.js +53 -4
- package/dist/shared/tool-row.js.map +1 -1
- package/dist/shared/workspace/session-root.d.ts +35 -0
- package/dist/shared/workspace/session-root.js +53 -0
- package/dist/shared/workspace/session-root.js.map +1 -0
- package/dist/shared/workspace/workspace-lock.d.ts +86 -0
- package/dist/shared/workspace/workspace-lock.js +187 -0
- package/dist/shared/workspace/workspace-lock.js.map +1 -0
- package/package.json +7 -5
- package/src/__tests__/config.test.ts +8 -0
- package/src/activities/__tests__/classify-tool-approvals.test.ts +1 -0
- package/src/activities/__tests__/discover-mcp-server.test.ts +1 -0
- package/src/activities/execute-cursor/__tests__/attachment-resolver.test.ts +161 -0
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +125 -2
- package/src/activities/execute-cursor/__tests__/cas-observations.test.ts +55 -0
- package/src/activities/execute-cursor/__tests__/hitl-ledger.test.ts +28 -0
- package/src/activities/execute-cursor/__tests__/hook-script.test.ts +63 -4
- package/src/activities/execute-cursor/__tests__/workspace-provision.test.ts +36 -2
- package/src/activities/execute-cursor/attachment-resolver.ts +116 -34
- package/src/activities/execute-cursor/cas-observations.ts +41 -5
- package/src/activities/execute-cursor/hook-script.ts +19 -1
- package/src/activities/execute-cursor/index.ts +114 -26
- package/src/activities/execute-cursor/message-translator.ts +9 -1
- package/src/activities/execute-cursor/prompt-builder.ts +60 -22
- package/src/activities/execute-cursor/skill-resolver.ts +3 -59
- package/src/activities/execute-cursor/stigmer-link.ts +78 -0
- package/src/activities/execute-cursor/workspace-provision.ts +5 -3
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +7 -2
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +7 -2
- package/src/activities/execute-deep-agent/__tests__/index.test.ts +4 -1
- package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +88 -0
- package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +7 -2
- package/src/activities/execute-deep-agent/index.ts +70 -2
- package/src/activities/execute-deep-agent/prompt-builder.ts +39 -0
- package/src/activities/execute-deep-agent/setup.ts +25 -17
- package/src/config.ts +17 -0
- package/src/middleware/__tests__/approval-gate.test.ts +77 -12
- package/src/middleware/approval-gate.ts +39 -7
- package/src/runner-manager.ts +6 -1
- package/src/runner.ts +6 -1
- package/src/shared/__tests__/artifact-storage.test.ts +111 -1
- package/src/shared/__tests__/implement-plan-prompt.test.ts +48 -0
- package/src/shared/__tests__/secret-leak-scan.test.ts +105 -0
- package/src/shared/__tests__/status.test.ts +7 -2
- package/src/shared/__tests__/tool-row.test.ts +136 -1
- package/src/shared/artifact-storage.ts +83 -1
- package/src/shared/implement-plan-prompt.ts +73 -0
- package/src/shared/plan-mode-prompt.ts +51 -0
- package/src/shared/status.ts +11 -1
- package/src/shared/tool-row.ts +58 -5
- package/src/shared/workspace/__tests__/session-root.test.ts +42 -0
- package/src/shared/workspace/__tests__/workspace-lock.test.ts +200 -0
- package/src/shared/workspace/session-root.ts +60 -0
- package/src/shared/workspace/workspace-lock.ts +241 -0
package/dist/.build-fingerprint
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"hash":"
|
|
1
|
+
{"hash":"63dd2c451162293e","builtAt":"2026-07-04T19:34:42.064Z","fileCount":234}
|
|
@@ -1,19 +1,60 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Resolves execution attachments into the platform-managed directory.
|
|
3
3
|
*
|
|
4
|
-
* Attachments are files provided as inputs to the agent execution.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Attachments are files provided as inputs to the agent execution. Each is
|
|
5
|
+
* materialized under the session platform dir's `inputs/` subdirectory and
|
|
6
|
+
* surfaced to the agent as `.stigmer/inputs/{filename}` through the workspace
|
|
7
|
+
* `.stigmer` symlink (see stigmer-link.ts), which this resolver guarantees —
|
|
8
|
+
* an agent with attachments but no skills still gets the link.
|
|
9
|
+
*
|
|
10
|
+
* Content sources, in order:
|
|
11
|
+
* - `localPath` (local mode only): read straight off the caller's disk — the
|
|
12
|
+
* CLI fast path, no storage round-trip.
|
|
13
|
+
* - `storageKey`: downloaded via {@link ArtifactStorage} — the universal path;
|
|
14
|
+
* the backend's create pipeline requires a storage key on every attachment
|
|
15
|
+
* it accepts, and in local mode the storage reads directly off disk.
|
|
16
|
+
*
|
|
17
|
+
* Placement is always `inputs/{filename}` — the platform namespace this
|
|
18
|
+
* harness can surface in the workspace. An attachment's `mountPath` is
|
|
19
|
+
* honored by convention, not mechanism: the standard mounts (e.g. the
|
|
20
|
+
* approved plan at `.stigmer/inputs/plan.md`) resolve to exactly this
|
|
21
|
+
* placement, and the prompt's `<input_files>` section plus any path-derived
|
|
22
|
+
* directives are built from the RESOLVED paths, so prompt and filesystem can
|
|
23
|
+
* never disagree.
|
|
24
|
+
*
|
|
25
|
+
* Error model: fail-hard, matching the native harness's attachment injector.
|
|
26
|
+
* Attachments are explicit user inputs — an execution that silently runs
|
|
27
|
+
* without one produces silently incorrect results (the "plan file wasn't
|
|
28
|
+
* found" class of failure). Any attachment that cannot be materialized aborts
|
|
29
|
+
* the execution with an actionable error.
|
|
7
30
|
*/
|
|
8
31
|
import type { Attachment } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/spec_pb";
|
|
32
|
+
import type { ArtifactStorage } from "../../shared/artifact-storage.js";
|
|
9
33
|
export interface ResolvedAttachment {
|
|
10
34
|
filename: string;
|
|
35
|
+
/** Workspace-relative path the agent reads (`.stigmer/inputs/{filename}`). */
|
|
11
36
|
relativePath: string;
|
|
12
37
|
}
|
|
38
|
+
export interface AttachmentResolverOptions {
|
|
39
|
+
sessionId: string;
|
|
40
|
+
primaryWorkspaceDir: string;
|
|
41
|
+
mode: "local" | "cloud";
|
|
42
|
+
/**
|
|
43
|
+
* Artifact storage for `storageKey` downloads. `undefined` when the runner
|
|
44
|
+
* could not build a store (proxy misconfig) — a storage-backed attachment
|
|
45
|
+
* then fails with an actionable error rather than a silent skip.
|
|
46
|
+
*/
|
|
47
|
+
storage: ArtifactStorage | undefined;
|
|
48
|
+
}
|
|
49
|
+
export declare class AttachmentResolutionError extends Error {
|
|
50
|
+
readonly attachmentFilename: string;
|
|
51
|
+
readonly reason: string;
|
|
52
|
+
constructor(attachmentFilename: string, reason: string);
|
|
53
|
+
}
|
|
13
54
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
55
|
+
* Materialize all attachments under `.stigmer/inputs/` and return their
|
|
56
|
+
* workspace-relative paths for prompt injection. Throws
|
|
57
|
+
* {@link AttachmentResolutionError} on the first attachment that cannot be
|
|
58
|
+
* materialized (fail-hard — see module doc).
|
|
18
59
|
*/
|
|
19
|
-
export declare function resolveAttachments(attachments: Attachment[],
|
|
60
|
+
export declare function resolveAttachments(attachments: Attachment[], options: AttachmentResolverOptions): Promise<ResolvedAttachment[]>;
|
|
@@ -1,52 +1,109 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Resolves execution attachments into the platform-managed directory.
|
|
3
3
|
*
|
|
4
|
-
* Attachments are files provided as inputs to the agent execution.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Attachments are files provided as inputs to the agent execution. Each is
|
|
5
|
+
* materialized under the session platform dir's `inputs/` subdirectory and
|
|
6
|
+
* surfaced to the agent as `.stigmer/inputs/{filename}` through the workspace
|
|
7
|
+
* `.stigmer` symlink (see stigmer-link.ts), which this resolver guarantees —
|
|
8
|
+
* an agent with attachments but no skills still gets the link.
|
|
9
|
+
*
|
|
10
|
+
* Content sources, in order:
|
|
11
|
+
* - `localPath` (local mode only): read straight off the caller's disk — the
|
|
12
|
+
* CLI fast path, no storage round-trip.
|
|
13
|
+
* - `storageKey`: downloaded via {@link ArtifactStorage} — the universal path;
|
|
14
|
+
* the backend's create pipeline requires a storage key on every attachment
|
|
15
|
+
* it accepts, and in local mode the storage reads directly off disk.
|
|
16
|
+
*
|
|
17
|
+
* Placement is always `inputs/{filename}` — the platform namespace this
|
|
18
|
+
* harness can surface in the workspace. An attachment's `mountPath` is
|
|
19
|
+
* honored by convention, not mechanism: the standard mounts (e.g. the
|
|
20
|
+
* approved plan at `.stigmer/inputs/plan.md`) resolve to exactly this
|
|
21
|
+
* placement, and the prompt's `<input_files>` section plus any path-derived
|
|
22
|
+
* directives are built from the RESOLVED paths, so prompt and filesystem can
|
|
23
|
+
* never disagree.
|
|
24
|
+
*
|
|
25
|
+
* Error model: fail-hard, matching the native harness's attachment injector.
|
|
26
|
+
* Attachments are explicit user inputs — an execution that silently runs
|
|
27
|
+
* without one produces silently incorrect results (the "plan file wasn't
|
|
28
|
+
* found" class of failure). Any attachment that cannot be materialized aborts
|
|
29
|
+
* the execution with an actionable error.
|
|
7
30
|
*/
|
|
8
|
-
import { mkdir, copyFile } from "node:fs/promises";
|
|
31
|
+
import { mkdir, copyFile, writeFile } from "node:fs/promises";
|
|
9
32
|
import { join, basename } from "node:path";
|
|
10
33
|
import { getPlatformDir } from "../../shared/workspace/platform-dir.js";
|
|
11
|
-
|
|
34
|
+
import { ensureStigmerSymlink, STIGMER_LOCAL_STATE_DIR } from "./stigmer-link.js";
|
|
12
35
|
const INPUTS_SUBDIR = "inputs";
|
|
36
|
+
export class AttachmentResolutionError extends Error {
|
|
37
|
+
attachmentFilename;
|
|
38
|
+
reason;
|
|
39
|
+
constructor(attachmentFilename, reason) {
|
|
40
|
+
super(`Attachment '${attachmentFilename}': ${reason}`);
|
|
41
|
+
this.name = "AttachmentResolutionError";
|
|
42
|
+
this.attachmentFilename = attachmentFilename;
|
|
43
|
+
this.reason = reason;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
13
46
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
47
|
+
* Materialize all attachments under `.stigmer/inputs/` and return their
|
|
48
|
+
* workspace-relative paths for prompt injection. Throws
|
|
49
|
+
* {@link AttachmentResolutionError} on the first attachment that cannot be
|
|
50
|
+
* materialized (fail-hard — see module doc).
|
|
18
51
|
*/
|
|
19
|
-
export async function resolveAttachments(attachments,
|
|
52
|
+
export async function resolveAttachments(attachments, options) {
|
|
20
53
|
if (attachments.length === 0)
|
|
21
54
|
return [];
|
|
22
|
-
const platformDir = getPlatformDir(sessionId);
|
|
55
|
+
const platformDir = getPlatformDir(options.sessionId);
|
|
23
56
|
const inputsDir = join(platformDir, INPUTS_SUBDIR);
|
|
24
57
|
await mkdir(inputsDir, { recursive: true });
|
|
58
|
+
// The symlink is what makes `inputs/` visible in the workspace; without it
|
|
59
|
+
// every resolved path below would dangle (the skill resolver also ensures
|
|
60
|
+
// it, but only when the agent has skills).
|
|
61
|
+
await ensureStigmerSymlink(options.primaryWorkspaceDir, platformDir);
|
|
25
62
|
const results = [];
|
|
26
63
|
for (const attachment of attachments) {
|
|
27
|
-
|
|
28
|
-
const resolved = await resolveAttachment(attachment, inputsDir, mode);
|
|
29
|
-
if (resolved)
|
|
30
|
-
results.push(resolved);
|
|
31
|
-
}
|
|
32
|
-
catch (err) {
|
|
33
|
-
console.warn(`Failed to resolve attachment ${attachment.filename}: ${err instanceof Error ? err.message : err}`);
|
|
34
|
-
}
|
|
64
|
+
results.push(await resolveAttachment(attachment, inputsDir, options));
|
|
35
65
|
}
|
|
66
|
+
console.log(`[attachment-resolver] resolved ${results.length} attachment(s): ` +
|
|
67
|
+
results.map((r) => r.relativePath).join(", "));
|
|
36
68
|
return results;
|
|
37
69
|
}
|
|
38
|
-
async function resolveAttachment(attachment, inputsDir,
|
|
39
|
-
|
|
70
|
+
async function resolveAttachment(attachment, inputsDir, options) {
|
|
71
|
+
// Local-mode fast path: the file is already on this machine's disk.
|
|
72
|
+
if (options.mode === "local" && attachment.localPath) {
|
|
40
73
|
const filename = attachment.filename || basename(attachment.localPath);
|
|
41
|
-
|
|
42
|
-
|
|
74
|
+
try {
|
|
75
|
+
await copyFile(attachment.localPath, join(inputsDir, filename));
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
throw new AttachmentResolutionError(attachment.filename, `failed to copy local file '${attachment.localPath}': ` +
|
|
79
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
80
|
+
}
|
|
43
81
|
return {
|
|
44
82
|
filename,
|
|
45
83
|
relativePath: join(STIGMER_LOCAL_STATE_DIR, INPUTS_SUBDIR, filename),
|
|
46
84
|
};
|
|
47
85
|
}
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
86
|
+
// Universal path: download the uploaded content by storage key.
|
|
87
|
+
if (!attachment.storageKey) {
|
|
88
|
+
throw new AttachmentResolutionError(attachment.filename, "missing storageKey — cannot download attachment from storage");
|
|
89
|
+
}
|
|
90
|
+
if (!options.storage) {
|
|
91
|
+
throw new AttachmentResolutionError(attachment.filename, `artifact storage is unavailable, so this attachment ` +
|
|
92
|
+
`(key: ${attachment.storageKey}) cannot be downloaded`);
|
|
93
|
+
}
|
|
94
|
+
const filename = attachment.filename || basename(attachment.storageKey);
|
|
95
|
+
let content;
|
|
96
|
+
try {
|
|
97
|
+
content = await options.storage.download(attachment.storageKey);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
throw new AttachmentResolutionError(attachment.filename, `failed to download from storage (key: ${attachment.storageKey}): ` +
|
|
101
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
102
|
+
}
|
|
103
|
+
await writeFile(join(inputsDir, filename), content);
|
|
104
|
+
return {
|
|
105
|
+
filename,
|
|
106
|
+
relativePath: join(STIGMER_LOCAL_STATE_DIR, INPUTS_SUBDIR, filename),
|
|
107
|
+
};
|
|
51
108
|
}
|
|
52
109
|
//# sourceMappingURL=attachment-resolver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachment-resolver.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/attachment-resolver.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"attachment-resolver.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/attachment-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -72,6 +72,19 @@ export interface CasObservations {
|
|
|
72
72
|
* than mis-captured). Entries are returned in a deterministic (sorted) order.
|
|
73
73
|
*/
|
|
74
74
|
export declare function readCasObservations(hitlDir: string): Promise<CasObservations>;
|
|
75
|
+
/**
|
|
76
|
+
* Build a standalone Node.js script that CLASSIFIES a single path as secret-like
|
|
77
|
+
* without staging anything — the deny-gate (no-capture-substrate) counterpart of
|
|
78
|
+
* {@link buildObservationStagingScript}.
|
|
79
|
+
*
|
|
80
|
+
* Invoked as `printf %s "$SALIENT" | node -e '<this>'` — the path rides stdin (no
|
|
81
|
+
* argv escaping). Prints `secret` for a secret-like path, `ok` otherwise. Empty
|
|
82
|
+
* input classifies as `secret` (fail-closed, matching {@link isSecretLikePath}).
|
|
83
|
+
* The Cursor hook uses this to hard-block a secret write on the deny-gate so its
|
|
84
|
+
* content never reaches an approval (DD-26 follow-up #2); it shares the classifier
|
|
85
|
+
* fragment above, so its verdict equals the runner's {@link isSecretLikePath}.
|
|
86
|
+
*/
|
|
87
|
+
export declare function buildSecretClassifyScript(): string;
|
|
75
88
|
/**
|
|
76
89
|
* Build the standalone Node.js script the hook runs to observe a single
|
|
77
90
|
* gitignored write — the disk-backed mirror of `CasCaptureFilesystemBackend`'s
|
|
@@ -122,6 +122,44 @@ export async function readCasObservations(hitlDir) {
|
|
|
122
122
|
function regexLiteral(re) {
|
|
123
123
|
return `new RegExp(${JSON.stringify(re.source)},${JSON.stringify(re.flags)})`;
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* The JS lines that define `isSecret(p)` inside a hook-embedded script — a
|
|
127
|
+
* byte-identical mirror of {@link isSecretLikePath} generated from the SAME
|
|
128
|
+
* {@link SECRET_BASENAME_PATTERNS}/{@link SECRET_PATH_PATTERNS} arrays.
|
|
129
|
+
*
|
|
130
|
+
* ONE definition, so every hook-side classifier (the capture-mode staging script
|
|
131
|
+
* and the deny-gate classify-only script) agrees with the runner-side classifier
|
|
132
|
+
* by construction — the "no bash-regex, no divergence" invariant this module owns.
|
|
133
|
+
* Authored as single-quoted-bash strings, so the JS must contain no single quotes.
|
|
134
|
+
*/
|
|
135
|
+
function secretClassifierFragment() {
|
|
136
|
+
const base = SECRET_BASENAME_PATTERNS.map(regexLiteral).join(",");
|
|
137
|
+
const pathPatterns = SECRET_PATH_PATTERNS.map(regexLiteral).join(",");
|
|
138
|
+
return [
|
|
139
|
+
`const baseName=(p)=>{const n=p.replace(/\\\\/g,"/");const i=n.lastIndexOf("/");return i>=0?n.slice(i+1):n;};`,
|
|
140
|
+
`const BASE=[${base}];const PATHP=[${pathPatterns}];`,
|
|
141
|
+
`const isSecret=(p)=>{if(!p)return true;const nm=p.replace(/\\\\/g,"/");const b=baseName(nm).toLowerCase();for(const r of BASE){if(r.test(b))return true;}const lp=nm.toLowerCase();for(const r of PATHP){if(r.test(lp))return true;}return false;};`,
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Build a standalone Node.js script that CLASSIFIES a single path as secret-like
|
|
146
|
+
* without staging anything — the deny-gate (no-capture-substrate) counterpart of
|
|
147
|
+
* {@link buildObservationStagingScript}.
|
|
148
|
+
*
|
|
149
|
+
* Invoked as `printf %s "$SALIENT" | node -e '<this>'` — the path rides stdin (no
|
|
150
|
+
* argv escaping). Prints `secret` for a secret-like path, `ok` otherwise. Empty
|
|
151
|
+
* input classifies as `secret` (fail-closed, matching {@link isSecretLikePath}).
|
|
152
|
+
* The Cursor hook uses this to hard-block a secret write on the deny-gate so its
|
|
153
|
+
* content never reaches an approval (DD-26 follow-up #2); it shares the classifier
|
|
154
|
+
* fragment above, so its verdict equals the runner's {@link isSecretLikePath}.
|
|
155
|
+
*/
|
|
156
|
+
export function buildSecretClassifyScript() {
|
|
157
|
+
return [
|
|
158
|
+
`let salient="";try{salient=require("fs").readFileSync(0,"utf8");}catch(e){salient="";}`,
|
|
159
|
+
...secretClassifierFragment(),
|
|
160
|
+
`process.stdout.write(isSecret(salient)?"secret":"ok");`,
|
|
161
|
+
].join("");
|
|
162
|
+
}
|
|
125
163
|
/**
|
|
126
164
|
* Build the standalone Node.js script the hook runs to observe a single
|
|
127
165
|
* gitignored write — the disk-backed mirror of `CasCaptureFilesystemBackend`'s
|
|
@@ -144,8 +182,6 @@ function regexLiteral(re) {
|
|
|
144
182
|
* single-quoted-bash string, so the JS must contain no single quotes.
|
|
145
183
|
*/
|
|
146
184
|
export function buildObservationStagingScript() {
|
|
147
|
-
const base = SECRET_BASENAME_PATTERNS.map(regexLiteral).join(",");
|
|
148
|
-
const pathPatterns = SECRET_PATH_PATTERNS.map(regexLiteral).join(",");
|
|
149
185
|
const metaSuffix = JSON.stringify(META_SUFFIX);
|
|
150
186
|
const blobSuffix = JSON.stringify(BLOB_SUFFIX);
|
|
151
187
|
return [
|
|
@@ -161,9 +197,7 @@ export function buildObservationStagingScript() {
|
|
|
161
197
|
`let rel=path.relative(wsRoot,abs).split(path.sep).join("/");`,
|
|
162
198
|
`if(!rel||rel===".."||rel.startsWith("../")){process.stdout.write("error");process.exit(0);}`,
|
|
163
199
|
// --- secret classifier: byte-identical mirror of isSecretLikePath ---
|
|
164
|
-
|
|
165
|
-
`const BASE=[${base}];const PATHP=[${pathPatterns}];`,
|
|
166
|
-
`const isSecret=(p)=>{if(!p)return true;const nm=p.replace(/\\\\/g,"/");const b=baseName(nm).toLowerCase();for(const r of BASE){if(r.test(b))return true;}const lp=nm.toLowerCase();for(const r of PATHP){if(r.test(lp))return true;}return false;};`,
|
|
200
|
+
...secretClassifierFragment(),
|
|
167
201
|
`const key=crypto.createHash("sha256").update(rel,"utf8").digest("hex");`,
|
|
168
202
|
`const metaPath=path.join(obsDir,key+${metaSuffix});const blobPath=path.join(obsDir,key+${blobSuffix});`,
|
|
169
203
|
`try{fs.mkdirSync(obsDir,{recursive:true});}catch(e){}`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cas-observations.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/cas-observations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AAEzG,yDAAyD;AACzD,MAAM,CAAC,MAAM,wBAAwB,GAAG,kBAAkB,CAAC;AAE3D,iFAAiF;AACjF,MAAM,WAAW,GAAG,YAAY,CAAC;AACjC,8EAA8E;AAC9E,MAAM,WAAW,GAAG,OAAO,CAAC;AAE5B,kEAAkE;AAClE,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,OAAO,IAAI,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;AACjD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAAe;IACxD,MAAM,GAAG,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,OAAO,GAAG,CAAC;AACb,CAAC;AAkBD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAAe;IACvD,MAAM,GAAG,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,QAAQ,GAA4B,EAAE,CAAC;IAC7C,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAChC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;YAAE,SAAS;QAC1C,IAAI,IAA2D,CAAC;QAChE,IAAI,CAAC;YACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,CAAC,oCAAoC;QAChD,CAAC;QACD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,SAAS;QAEnE,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC3B,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5B,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC7B,IAAI,MAAM,GAAsB,IAAI,CAAC;YACrC,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;gBAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC;gBAC7E,IAAI,CAAC;oBACH,MAAM,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBACpC,CAAC;gBAAC,MAAM,CAAC;oBACP,SAAS,CAAC,0DAA0D;gBACtE,CAAC;YACH,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CAAC,EAAU;IAC9B,OAAO,cAAc,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC;AAChF,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"cas-observations.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/cas-observations.ts"],"names":[],"mappings":"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"}
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
*/
|
|
88
88
|
import { SALIENT_ARG_FIELDS, getBuiltInGatedCategories } from "./approval-policy.js";
|
|
89
89
|
import { EDIT_OLD_FIELDS, EDIT_NEW_FIELDS, WRITE_CONTENT_FIELDS, } from "../../shared/file-tools.js";
|
|
90
|
-
import { buildObservationStagingScript, CAS_OBSERVATIONS_DIRNAME } from "./cas-observations.js";
|
|
90
|
+
import { buildObservationStagingScript, buildSecretClassifyScript, CAS_OBSERVATIONS_DIRNAME } from "./cas-observations.js";
|
|
91
91
|
// Shown to the model when the gate denies a tool call. It must NOT teach the
|
|
92
92
|
// model to ask for permission in prose or to "stop and wait" — that framing
|
|
93
93
|
// makes the model narrate approval requests instead of invoking tools (the
|
|
@@ -262,6 +262,7 @@ export function generateHookScript(activePointerPath, workspaceRoot = "") {
|
|
|
262
262
|
const categoryCaseArms = buildCategoryCaseArms();
|
|
263
263
|
const nodeIdentityScript = buildNodeIdentityScript();
|
|
264
264
|
const observationStagingScript = buildObservationStagingScript();
|
|
265
|
+
const secretClassifyScript = buildSecretClassifyScript();
|
|
265
266
|
const nodeBin = process.execPath;
|
|
266
267
|
return `#!/bin/bash
|
|
267
268
|
# Stigmer HITL approval hook for Cursor (preToolUse + beforeMCPExecution).
|
|
@@ -556,6 +557,23 @@ if [ -n "$CATEGORY" ]; then
|
|
|
556
557
|
exit 0
|
|
557
558
|
fi
|
|
558
559
|
fi
|
|
560
|
+
# Deny-gate secret hard-block (DD-26 #2): a secret-like WRITE that reaches the
|
|
561
|
+
# deny-gate (no capture substrate for it — capture off, or captureIgnored off in
|
|
562
|
+
# a git-no-storage workspace) must not surface its content for approval. Classify
|
|
563
|
+
# byte-identically to isSecretLikePath (no staging) and, on a match, hard-block
|
|
564
|
+
# with the security message: the agent continues, nothing is written, and NO
|
|
565
|
+
# content is recorded to the ledger. Placed before the grant/lease checks so a
|
|
566
|
+
# write-category lease can never bypass it; only writes are content-bearing
|
|
567
|
+
# (deletes stay gated). Fail-open on a classify error — the write falls to the
|
|
568
|
+
# normal deny-gate and the runner-side backstop still withholds any secret
|
|
569
|
+
# content before persist.
|
|
570
|
+
if [ "$CATEGORY" = "write" ] && [ -n "$SALIENT" ]; then
|
|
571
|
+
SECRET_RESULT=$(printf '%s' "$SALIENT" | ELECTRON_RUN_AS_NODE=1 "$NODE_BIN" -e '${secretClassifyScript}' 2>/dev/null || echo ok)
|
|
572
|
+
if [ "$SECRET_RESULT" = "secret" ]; then
|
|
573
|
+
echo '{"permission":"deny","agent_message":"${SECRET_BLOCKED_AGENT_MESSAGE}","user_message":"Blocked for security: this file matches a secret-like path and was not written."}'
|
|
574
|
+
exit 0
|
|
575
|
+
fi
|
|
576
|
+
fi
|
|
559
577
|
# Reinvocation grant: allow when the CONTENT-exact token (a file edit approved
|
|
560
578
|
# earlier with this exact content) OR the COARSE token (a shell/delete, or the
|
|
561
579
|
# content-less degrade) is in approvedGrantTokens. A sibling edit to the same
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-script.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/hook-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFG;AAEH,OAAO,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACrF,OAAO,EACL,eAAe,EACf,eAAe,EACf,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,6BAA6B,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"hook-script.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/hook-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFG;AAEH,OAAO,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACrF,OAAO,EACL,eAAe,EACf,eAAe,EACf,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,6BAA6B,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAE3H,6EAA6E;AAC7E,4EAA4E;AAC5E,2EAA2E;AAC3E,gEAAgE;AAChE,+EAA+E;AAC/E,6EAA6E;AAC7E,oEAAoE;AACpE,+EAA+E;AAC/E,uCAAuC;AACvC,MAAM,+BAA+B,GACnC,6EAA6E;IAC7E,gFAAgF;IAChF,+EAA+E;IAC/E,8EAA8E;IAC9E,qEAAqE;IACrE,6EAA6E,CAAC;AAEhF,iFAAiF;AACjF,kFAAkF;AAClF,kFAAkF;AAClF,gFAAgF;AAChF,oEAAoE;AACpE,MAAM,4BAA4B,GAChC,4EAA4E;IAC5E,gFAAgF;IAChF,iFAAiF;IACjF,+EAA+E;IAC/E,gFAAgF;IAChF,8CAA8C,CAAC;AAEjD;;;;GAIG;AACH,SAAS,qBAAqB;IAC5B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC/C,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,yBAAyB,EAAE,EAAE,CAAC;QAC3D,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC7C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IACD,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,CAAC,SAAS,OAAO,eAAe,QAAQ,MAAM,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,SAAS,wBAAwB;IAC/B,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IAChD,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IAC3C,OAAO;QACL,mGAAmG;QACnG,uFAAuF;QACvF,aAAa;QACb,kBAAkB,EAAE,IAAI;QACxB,qDAAqD;QACrD,sBAAsB,EAAE,aAAa,EAAE,8FAA8F;KACtI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAS,uBAAuB;IAC9B,MAAM,WAAW,GAA2B,EAAE,CAAC;IAC/C,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,yBAAyB,EAAE,EAAE,CAAC;QAC3D,WAAW,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC;IAC/B,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAClD,OAAO;QACL,2DAA2D;QAC3D,0DAA0D;QAC1D,cAAc,UAAU,cAAc;QACtC,2EAA2E;QAC3E,wEAAwE;QACxE,gDAAgD;QAChD,WAAW;QACX,mEAAmE;QACnE,yHAAyH;QACzH,WAAW;QACX,kBAAkB,MAAM,wDAAwD;QAChF,wDAAwD;QACxD,4EAA4E;QAC5E,6EAA6E;QAC7E,wBAAwB,EAAE;QAC1B,oEAAoE;QACpE,2EAA2E;QAC3E,6EAA6E;QAC7E,2EAA2E;QAC3E,4EAA4E;QAC5E,0EAA0E;QAC1E,2EAA2E;QAC3E,yEAAyE;QACzE,6EAA6E;QAC7E,4BAA4B;QAC5B,uKAAuK;KACxK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,UAAU,kBAAkB,CAAC,iBAAyB,EAAE,aAAa,GAAG,EAAE;IAC9E,MAAM,aAAa,GAAG,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnD,MAAM,gBAAgB,GAAG,qBAAqB,EAAE,CAAC;IACjD,MAAM,kBAAkB,GAAG,uBAAuB,EAAE,CAAC;IACrD,MAAM,wBAAwB,GAAG,6BAA6B,EAAE,CAAC;IACjE,MAAM,oBAAoB,GAAG,yBAAyB,EAAE,CAAC;IACzD,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IACjC,OAAO;;;;;;;;;;;;;;;YAeG,OAAO;eACJ,iBAAiB;;;YAGpB,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2EAgGkD,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2B5E,aAAa;;;;;;EAM5B,gBAAgB;;;;;;;;;;;;;;gDAc8B,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sCAiDzC,wBAAwB;iFACmB,wBAAwB;;;;;kDAKvD,4BAA4B;;;;;;kDAM5B,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oDA2C7B,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sFAsCG,oBAAoB;;oDAEtD,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;gDA4BhC,+BAA+B;;;;;;;;;CAS9E,CAAC;AACF,CAAC"}
|
|
@@ -54,6 +54,11 @@ export interface BuildPromptInput {
|
|
|
54
54
|
*/
|
|
55
55
|
appliedToolCallIds?: ReadonlySet<string>;
|
|
56
56
|
interactionMode?: InteractionMode;
|
|
57
|
+
/**
|
|
58
|
+
* The execution is a Build-from-plan turn (spec.execution_config
|
|
59
|
+
* .build_from_plan): both prompt paths carry the implement-plan directive.
|
|
60
|
+
*/
|
|
61
|
+
buildFromPlan?: boolean;
|
|
57
62
|
}
|
|
58
63
|
/**
|
|
59
64
|
* Select and build the appropriate prompt based on resolution reason and
|
|
@@ -37,9 +37,9 @@ import { CursorMode } from "@stigmer/protos/ai/stigmer/agentic/session/v1/enum_p
|
|
|
37
37
|
import { determineCursorMode, isCloudMode } from "./cursor-mode.js";
|
|
38
38
|
import { MessageAccumulator, reconcileDeniedToolCalls, clearProvisionalPostDenialNarration, cancelInProgressSubAgentProtos, collapseRedundantToolCallTwins } from "./message-translator.js";
|
|
39
39
|
import { utcTimestamp, persistStatus, reportSetupProgress, slimStatus } from "../../shared/status.js";
|
|
40
|
-
import { collectSubAgentToolCallIds } from "../../shared/tool-row.js";
|
|
40
|
+
import { collectSubAgentToolCallIds, withholdSecretContentFromMessages } from "../../shared/tool-row.js";
|
|
41
41
|
import { startStallWatchdog, StallTimeoutError, formatStallFailure } from "../../shared/stall-watchdog.js";
|
|
42
|
-
import {
|
|
42
|
+
import { resolveUsableArtifactStorage, loadArtifactStorageConfig } from "../../shared/artifact-storage.js";
|
|
43
43
|
import { publishPlanArtifact } from "../../shared/plan-artifact.js";
|
|
44
44
|
import { DeltaEnricher } from "./delta-enricher.js";
|
|
45
45
|
import { TodoTracker } from "./todo-tracker.js";
|
|
@@ -53,11 +53,13 @@ import { backfillMcpServersIfNeeded } from "./connect-backfill.js";
|
|
|
53
53
|
import { resolveExecutionEnv } from "./env-resolver.js";
|
|
54
54
|
import { resolveBlueprint } from "./blueprint-resolver.js";
|
|
55
55
|
import { buildCursorSubAgentDefinitions } from "./subagent-config.js";
|
|
56
|
-
import { resolveSkills
|
|
56
|
+
import { resolveSkills } from "./skill-resolver.js";
|
|
57
|
+
import { removeStigmerSymlink } from "./stigmer-link.js";
|
|
57
58
|
import { resolveAttachments } from "./attachment-resolver.js";
|
|
58
|
-
import { buildEnhancedPrompt, buildReinvocationPrompt } from "./prompt-builder.js";
|
|
59
|
+
import { buildEnhancedPrompt, buildReinvocationPrompt, formatInteractionModePrefix, formatImplementPlanSection } from "./prompt-builder.js";
|
|
59
60
|
import { installHitlGate, removeHitlGate } from "./workspace-setup.js";
|
|
60
61
|
import { ensureHitlDir } from "../../shared/workspace/platform-dir.js";
|
|
62
|
+
import { acquireWorkspaceLock, WorkspaceLockCancelledError, WorkspaceLockTimeoutError, } from "../../shared/workspace/workspace-lock.js";
|
|
61
63
|
import { LocalWorkspaceBackend } from "../../shared/workspace/local-backend.js";
|
|
62
64
|
import { buildApprovalState, buildApprovalGrants, emitCursorGrantReceipts, primaryToken, readDenialLedger, reconstructAdjudicatedApprovals, watchDenialLedger } from "./approval-state.js";
|
|
63
65
|
import { deriveTurnCommandProvenance } from "./command-provenance.js";
|
|
@@ -131,25 +133,30 @@ turnSeq) {
|
|
|
131
133
|
});
|
|
132
134
|
// Artifact storage for offloading oversized tool outputs (screenshots, giant
|
|
133
135
|
// dumps) out of the persisted status, and for publishing the plan artifact.
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
artifactStorage = createArtifactStorage(loadArtifactStorageConfig(config));
|
|
141
|
-
}
|
|
142
|
-
catch (storageErr) {
|
|
143
|
-
console.warn(`ExecuteCursor artifact storage unavailable — tool-output offload disabled ` +
|
|
144
|
-
`(aggregate size guard still active): execution=${executionId}, error=${storageErr}`);
|
|
145
|
-
}
|
|
136
|
+
// Resolved once here so it is available to EVERY persist below. Best-effort via
|
|
137
|
+
// the shared resolver (identical to the deep-agent harness): `undefined` — never
|
|
138
|
+
// a throw — when there is no working substrate (proxy misconfig OR an unwritable
|
|
139
|
+
// local path). An absent store disables offload (persistStatus still enforces
|
|
140
|
+
// the aggregate size cap) and flips capture mode off (deny-gate fallback).
|
|
141
|
+
const artifactStorage = await resolveUsableArtifactStorage(loadArtifactStorageConfig(config), { executionId });
|
|
146
142
|
const statusOffload = artifactStorage
|
|
147
143
|
? { artifactStorage, executionId }
|
|
148
144
|
: undefined;
|
|
149
145
|
// ALL status persistence in this activity flows through `persist`, so the
|
|
150
146
|
// single size-bounding guard (offload + aggregate elision) is unforgeable and
|
|
151
147
|
// a future call site cannot accidentally skip it.
|
|
152
|
-
const persist = (s = status) =>
|
|
148
|
+
const persist = (s = status) => {
|
|
149
|
+
// Never-persist-secret backstop (DD-26 #2): before EVERY persist, withhold
|
|
150
|
+
// content from any built-in write row targeting a secret-like path (top-level
|
|
151
|
+
// + sub-agent). This is the single airtight choke point for the Cursor harness
|
|
152
|
+
// — the deny-gate analog of capture mode's stamping scrub, and the only
|
|
153
|
+
// guarantee under auto_approve_all (where the hook installs no gate). Safe on
|
|
154
|
+
// every call: Cursor sets tool args atomically from the SDK tool_call event
|
|
155
|
+
// (buildToolCallProto), so there is no mid-stream partial-args hazard, and the
|
|
156
|
+
// pass only ever touches secret-like write rows (idempotent, else a no-op).
|
|
157
|
+
withholdSecretContentFromMessages(s.messages, s.subAgentExecutions);
|
|
158
|
+
return persistStatus(client, executionId, s, { offload: statusOffload });
|
|
159
|
+
};
|
|
153
160
|
let sessionId;
|
|
154
161
|
let session;
|
|
155
162
|
let pauseDetected = false;
|
|
@@ -195,6 +202,14 @@ turnSeq) {
|
|
|
195
202
|
// (issue #173). Runs in the finally, covering every success/error/approval
|
|
196
203
|
// exit path. Undefined until the gate is installed.
|
|
197
204
|
let hitlCleanup;
|
|
205
|
+
// Exclusive turn lock on the primary workspace working tree. Held across the
|
|
206
|
+
// ENTIRE tree-mutating window (decision reconcile, HITL gate install, the
|
|
207
|
+
// agent's own writes, candidate capture) so a concurrent execution sharing
|
|
208
|
+
// this directory can never write between this turn's baseline and candidate
|
|
209
|
+
// snapshots — the misattribution that showed another session's file as this
|
|
210
|
+
// turn's change. Released in the finally AFTER hitlCleanup (which still
|
|
211
|
+
// mutates the tree). See shared/workspace/workspace-lock.ts.
|
|
212
|
+
let releaseWorkspaceLock;
|
|
198
213
|
// Carries model/mode/agentId out to the outer catch so a thrown CursorSdkError
|
|
199
214
|
// can be classified with the same context as the run.wait() error path.
|
|
200
215
|
let errorContext = { model: "default", mode: "local", agentId: "" };
|
|
@@ -247,6 +262,44 @@ turnSeq) {
|
|
|
247
262
|
// resume (which never authors a baseline) is harmless.
|
|
248
263
|
const changeSetId = `${executionId}:${turnSeq}`;
|
|
249
264
|
heartbeat();
|
|
265
|
+
// Serialize this turn against every other execution sharing this working
|
|
266
|
+
// tree — sessions declaring the same localPath (or the shared runner root)
|
|
267
|
+
// resolve to ONE directory, and an unserialized concurrent write lands
|
|
268
|
+
// inside this turn's baseline→candidate window, misattributing another
|
|
269
|
+
// session's file to this turn's review. Acquired before ANY tree mutation
|
|
270
|
+
// below (decision reconcile, gate install, agent writes, capture). While
|
|
271
|
+
// another turn holds the lock this surfaces a visible waiting state and
|
|
272
|
+
// heartbeats; a cancel aborts the wait immediately.
|
|
273
|
+
if (primaryWorkspaceDir) {
|
|
274
|
+
try {
|
|
275
|
+
releaseWorkspaceLock = await acquireWorkspaceLock(primaryWorkspaceDir, {
|
|
276
|
+
onWaiting: () => reportSetupProgress(client, executionId, "Waiting for workspace — in use by another session"),
|
|
277
|
+
heartbeat,
|
|
278
|
+
signal: Context.current().cancellationSignal,
|
|
279
|
+
timeoutMs: config.workspaceLockTimeoutMs,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
catch (lockErr) {
|
|
283
|
+
if (lockErr instanceof WorkspaceLockCancelledError) {
|
|
284
|
+
throw new CancelledFailure("Activity cancelled while waiting for the workspace lock");
|
|
285
|
+
}
|
|
286
|
+
if (lockErr instanceof WorkspaceLockTimeoutError) {
|
|
287
|
+
status.phase = ExecutionPhase.EXECUTION_FAILED;
|
|
288
|
+
status.error = lockErr.message;
|
|
289
|
+
status.completedAt = utcTimestamp();
|
|
290
|
+
status.messages.push(create(AgentMessageSchema, {
|
|
291
|
+
type: MessageType.MESSAGE_SYSTEM,
|
|
292
|
+
content: `Execution failed: ${lockErr.message}`,
|
|
293
|
+
timestamp: utcTimestamp(),
|
|
294
|
+
}));
|
|
295
|
+
await persist(status);
|
|
296
|
+
console.warn(`ExecuteCursor workspace lock timeout: execution=${executionId}`);
|
|
297
|
+
return slimStatus(status);
|
|
298
|
+
}
|
|
299
|
+
throw lockErr;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
heartbeat();
|
|
250
303
|
// Set OTel baggage so downstream calls carry execution context.
|
|
251
304
|
try {
|
|
252
305
|
const { setBaggage, BAGGAGE_EXECUTION_ID, BAGGAGE_SESSION_ID, BAGGAGE_ORG_ID } = await import("../../otel.js");
|
|
@@ -430,8 +483,15 @@ turnSeq) {
|
|
|
430
483
|
primaryWorkspaceDir,
|
|
431
484
|
});
|
|
432
485
|
heartbeat();
|
|
433
|
-
// Phase 5b: Resolve attachments
|
|
434
|
-
|
|
486
|
+
// Phase 5b: Resolve attachments (fail-hard — explicit user inputs; see
|
|
487
|
+
// attachment-resolver.ts). Downloads by storage key through the same
|
|
488
|
+
// artifactStorage resolved for status offload above.
|
|
489
|
+
const attachmentResults = await resolveAttachments(spec.attachments, {
|
|
490
|
+
sessionId,
|
|
491
|
+
primaryWorkspaceDir,
|
|
492
|
+
mode: config.mode,
|
|
493
|
+
storage: artifactStorage,
|
|
494
|
+
});
|
|
435
495
|
const attachmentPaths = attachmentResults.map((a) => a.relativePath);
|
|
436
496
|
// Phase 5b3: Exact-apply approved whole-file writes (HITL "what you approve
|
|
437
497
|
// is what gets applied"). The Cursor deny-only harness reinvokes the model,
|
|
@@ -611,6 +671,7 @@ turnSeq) {
|
|
|
611
671
|
// Phase 10: Build the prompt
|
|
612
672
|
const interactionMode = spec.executionConfig?.interactionMode
|
|
613
673
|
?? InteractionMode.UNSPECIFIED;
|
|
674
|
+
const buildFromPlan = spec.executionConfig?.buildFromPlan ?? false;
|
|
614
675
|
const prompt = buildPrompt({
|
|
615
676
|
resolution,
|
|
616
677
|
approvalDecisions,
|
|
@@ -624,6 +685,7 @@ turnSeq) {
|
|
|
624
685
|
pendingApprovals: adjudicatedApprovals,
|
|
625
686
|
appliedToolCallIds,
|
|
626
687
|
interactionMode,
|
|
688
|
+
buildFromPlan,
|
|
627
689
|
});
|
|
628
690
|
// Phase 10a: Inject structured output instruction for Cursor harness
|
|
629
691
|
let effectivePrompt = prompt;
|
|
@@ -1613,6 +1675,12 @@ turnSeq) {
|
|
|
1613
1675
|
`execution=${executionId}, error=${cleanupErr instanceof Error ? cleanupErr.message : cleanupErr}`);
|
|
1614
1676
|
}
|
|
1615
1677
|
}
|
|
1678
|
+
// Release the workspace turn lock LAST — hitlCleanup above still mutates
|
|
1679
|
+
// the tree (restores .cursor/hooks.json), and the next queued turn must
|
|
1680
|
+
// not baseline until every mutation of this one has landed. Idempotent
|
|
1681
|
+
// and non-throwing (see workspace-lock.ts), so it can never mask the
|
|
1682
|
+
// turn's real outcome.
|
|
1683
|
+
await releaseWorkspaceLock?.();
|
|
1616
1684
|
}
|
|
1617
1685
|
}
|
|
1618
1686
|
/**
|
|
@@ -1702,7 +1770,7 @@ import { jsonSchemaToZod } from "../../shared/json-schema-to-zod.js";
|
|
|
1702
1770
|
* agent after resume failure skills; no prior conversation to inherit)
|
|
1703
1771
|
*/
|
|
1704
1772
|
export function buildPrompt(input) {
|
|
1705
|
-
const { resolution, approvalDecisions, instructions, userMessage, skills, subAgents, workspaceDirs, workspaceFileRefs, attachmentPaths, interactionMode, } = input;
|
|
1773
|
+
const { resolution, approvalDecisions, instructions, userMessage, skills, subAgents, workspaceDirs, workspaceFileRefs, attachmentPaths, interactionMode, buildFromPlan, } = input;
|
|
1706
1774
|
const isHitlReinvocation = approvalDecisions !== undefined && approvalDecisions.size > 0;
|
|
1707
1775
|
// HITL reinvocation: the agent is resumed, so its native context carries the
|
|
1708
1776
|
// prior conversation; the reinvocation prompt conveys the approval decisions
|
|
@@ -1711,9 +1779,20 @@ export function buildPrompt(input) {
|
|
|
1711
1779
|
return buildReinvocationPrompt(input.pendingApprovals, approvalDecisions, input.appliedToolCallIds);
|
|
1712
1780
|
}
|
|
1713
1781
|
// A successfully resumed agent carries its own conversation context via the
|
|
1714
|
-
// SDK's native store — send the raw user message with no preamble.
|
|
1782
|
+
// SDK's native store — send the raw user message with no preamble. The
|
|
1783
|
+
// exceptions are the per-EXECUTION directives, which never inherit from the
|
|
1784
|
+
// session's first turn: the interaction-mode prefix (a follow-up can switch
|
|
1785
|
+
// Agent→Plan mid-session, and for Cursor the prompt is the only plan-mode
|
|
1786
|
+
// enforcement) and the implement-plan directive (the build turn is usually
|
|
1787
|
+
// a follow-up on a resumed agent).
|
|
1715
1788
|
if (resolution.reason === "resumed_successfully") {
|
|
1716
|
-
|
|
1789
|
+
const prefixes = [
|
|
1790
|
+
formatInteractionModePrefix(interactionMode),
|
|
1791
|
+
formatImplementPlanSection(buildFromPlan, attachmentPaths),
|
|
1792
|
+
].filter((p) => p !== undefined);
|
|
1793
|
+
return prefixes.length > 0
|
|
1794
|
+
? [...prefixes, userMessage].join("\n\n")
|
|
1795
|
+
: userMessage;
|
|
1717
1796
|
}
|
|
1718
1797
|
// First execution, or a fresh agent created after a resume failure: there is
|
|
1719
1798
|
// no prior conversation to inherit, so start a new turn with full context.
|
|
@@ -1726,6 +1805,7 @@ export function buildPrompt(input) {
|
|
|
1726
1805
|
workspaceFileRefs,
|
|
1727
1806
|
attachmentPaths,
|
|
1728
1807
|
interactionMode,
|
|
1808
|
+
buildFromPlan,
|
|
1729
1809
|
});
|
|
1730
1810
|
}
|
|
1731
1811
|
// ---------------------------------------------------------------------------
|