@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.
Files changed (111) hide show
  1. package/dist/.build-fingerprint +1 -1
  2. package/dist/activities/execute-cursor/attachment-resolver.d.ts +49 -8
  3. package/dist/activities/execute-cursor/attachment-resolver.js +83 -26
  4. package/dist/activities/execute-cursor/attachment-resolver.js.map +1 -1
  5. package/dist/activities/execute-cursor/cas-observations.d.ts +13 -0
  6. package/dist/activities/execute-cursor/cas-observations.js +39 -5
  7. package/dist/activities/execute-cursor/cas-observations.js.map +1 -1
  8. package/dist/activities/execute-cursor/hook-script.js +19 -1
  9. package/dist/activities/execute-cursor/hook-script.js.map +1 -1
  10. package/dist/activities/execute-cursor/index.d.ts +5 -0
  11. package/dist/activities/execute-cursor/index.js +102 -22
  12. package/dist/activities/execute-cursor/index.js.map +1 -1
  13. package/dist/activities/execute-cursor/message-translator.js +10 -1
  14. package/dist/activities/execute-cursor/message-translator.js.map +1 -1
  15. package/dist/activities/execute-cursor/prompt-builder.d.ts +32 -7
  16. package/dist/activities/execute-cursor/prompt-builder.js +38 -20
  17. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  18. package/dist/activities/execute-cursor/skill-resolver.d.ts +1 -10
  19. package/dist/activities/execute-cursor/skill-resolver.js +3 -55
  20. package/dist/activities/execute-cursor/skill-resolver.js.map +1 -1
  21. package/dist/activities/execute-cursor/stigmer-link.d.ts +35 -0
  22. package/dist/activities/execute-cursor/stigmer-link.js +73 -0
  23. package/dist/activities/execute-cursor/stigmer-link.js.map +1 -0
  24. package/dist/activities/execute-cursor/workspace-provision.d.ts +3 -2
  25. package/dist/activities/execute-cursor/workspace-provision.js +5 -3
  26. package/dist/activities/execute-cursor/workspace-provision.js.map +1 -1
  27. package/dist/activities/execute-deep-agent/index.js +59 -2
  28. package/dist/activities/execute-deep-agent/index.js.map +1 -1
  29. package/dist/activities/execute-deep-agent/prompt-builder.d.ts +17 -0
  30. package/dist/activities/execute-deep-agent/prompt-builder.js +16 -0
  31. package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
  32. package/dist/activities/execute-deep-agent/setup.js +22 -16
  33. package/dist/activities/execute-deep-agent/setup.js.map +1 -1
  34. package/dist/config.d.ts +7 -0
  35. package/dist/config.js +9 -0
  36. package/dist/config.js.map +1 -1
  37. package/dist/middleware/approval-gate.js +36 -6
  38. package/dist/middleware/approval-gate.js.map +1 -1
  39. package/dist/runner-manager.d.ts +2 -0
  40. package/dist/runner-manager.js +2 -1
  41. package/dist/runner-manager.js.map +1 -1
  42. package/dist/runner.d.ts +2 -0
  43. package/dist/runner.js +2 -1
  44. package/dist/runner.js.map +1 -1
  45. package/dist/shared/artifact-storage.d.ts +32 -0
  46. package/dist/shared/artifact-storage.js +74 -1
  47. package/dist/shared/artifact-storage.js.map +1 -1
  48. package/dist/shared/implement-plan-prompt.d.ts +42 -0
  49. package/dist/shared/implement-plan-prompt.js +66 -0
  50. package/dist/shared/implement-plan-prompt.js.map +1 -0
  51. package/dist/shared/plan-mode-prompt.d.ts +30 -0
  52. package/dist/shared/plan-mode-prompt.js +51 -0
  53. package/dist/shared/plan-mode-prompt.js.map +1 -0
  54. package/dist/shared/status.d.ts +11 -0
  55. package/dist/shared/status.js +12 -2
  56. package/dist/shared/status.js.map +1 -1
  57. package/dist/shared/tool-row.d.ts +30 -1
  58. package/dist/shared/tool-row.js +53 -4
  59. package/dist/shared/tool-row.js.map +1 -1
  60. package/dist/shared/workspace/session-root.d.ts +35 -0
  61. package/dist/shared/workspace/session-root.js +53 -0
  62. package/dist/shared/workspace/session-root.js.map +1 -0
  63. package/dist/shared/workspace/workspace-lock.d.ts +86 -0
  64. package/dist/shared/workspace/workspace-lock.js +187 -0
  65. package/dist/shared/workspace/workspace-lock.js.map +1 -0
  66. package/package.json +7 -5
  67. package/src/__tests__/config.test.ts +8 -0
  68. package/src/activities/__tests__/classify-tool-approvals.test.ts +1 -0
  69. package/src/activities/__tests__/discover-mcp-server.test.ts +1 -0
  70. package/src/activities/execute-cursor/__tests__/attachment-resolver.test.ts +161 -0
  71. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +125 -2
  72. package/src/activities/execute-cursor/__tests__/cas-observations.test.ts +55 -0
  73. package/src/activities/execute-cursor/__tests__/hitl-ledger.test.ts +28 -0
  74. package/src/activities/execute-cursor/__tests__/hook-script.test.ts +63 -4
  75. package/src/activities/execute-cursor/__tests__/workspace-provision.test.ts +36 -2
  76. package/src/activities/execute-cursor/attachment-resolver.ts +116 -34
  77. package/src/activities/execute-cursor/cas-observations.ts +41 -5
  78. package/src/activities/execute-cursor/hook-script.ts +19 -1
  79. package/src/activities/execute-cursor/index.ts +114 -26
  80. package/src/activities/execute-cursor/message-translator.ts +9 -1
  81. package/src/activities/execute-cursor/prompt-builder.ts +60 -22
  82. package/src/activities/execute-cursor/skill-resolver.ts +3 -59
  83. package/src/activities/execute-cursor/stigmer-link.ts +78 -0
  84. package/src/activities/execute-cursor/workspace-provision.ts +5 -3
  85. package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +7 -2
  86. package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +7 -2
  87. package/src/activities/execute-deep-agent/__tests__/index.test.ts +4 -1
  88. package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +88 -0
  89. package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +7 -2
  90. package/src/activities/execute-deep-agent/index.ts +70 -2
  91. package/src/activities/execute-deep-agent/prompt-builder.ts +39 -0
  92. package/src/activities/execute-deep-agent/setup.ts +25 -17
  93. package/src/config.ts +17 -0
  94. package/src/middleware/__tests__/approval-gate.test.ts +77 -12
  95. package/src/middleware/approval-gate.ts +39 -7
  96. package/src/runner-manager.ts +6 -1
  97. package/src/runner.ts +6 -1
  98. package/src/shared/__tests__/artifact-storage.test.ts +111 -1
  99. package/src/shared/__tests__/implement-plan-prompt.test.ts +48 -0
  100. package/src/shared/__tests__/secret-leak-scan.test.ts +105 -0
  101. package/src/shared/__tests__/status.test.ts +7 -2
  102. package/src/shared/__tests__/tool-row.test.ts +136 -1
  103. package/src/shared/artifact-storage.ts +83 -1
  104. package/src/shared/implement-plan-prompt.ts +73 -0
  105. package/src/shared/plan-mode-prompt.ts +51 -0
  106. package/src/shared/status.ts +11 -1
  107. package/src/shared/tool-row.ts +58 -5
  108. package/src/shared/workspace/__tests__/session-root.test.ts +42 -0
  109. package/src/shared/workspace/__tests__/workspace-lock.test.ts +200 -0
  110. package/src/shared/workspace/session-root.ts +60 -0
  111. package/src/shared/workspace/workspace-lock.ts +241 -0
@@ -1 +1 @@
1
- {"hash":"cdec4dda6bfadae6","builtAt":"2026-07-02T20:57:56.801Z","fileCount":229}
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
- * In local mode, they are read directly from localPath. The files are
6
- * placed under .stigmer/inputs/ using the platform mount pattern.
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
- * Resolve attachments by copying them to the platform-managed directory.
15
- *
16
- * In local mode: copies from localPath to .stigmer/inputs/{filename}
17
- * Returns relative paths for prompt injection.
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[], sessionId: string, primaryWorkspaceDir: string, mode: "local" | "cloud"): Promise<ResolvedAttachment[]>;
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
- * In local mode, they are read directly from localPath. The files are
6
- * placed under .stigmer/inputs/ using the platform mount pattern.
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
- const STIGMER_LOCAL_STATE_DIR = ".stigmer";
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
- * Resolve attachments by copying them to the platform-managed directory.
15
- *
16
- * In local mode: copies from localPath to .stigmer/inputs/{filename}
17
- * Returns relative paths for prompt injection.
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, sessionId, primaryWorkspaceDir, mode) {
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
- try {
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, mode) {
39
- if (mode === "local" && attachment.localPath) {
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
- const destPath = join(inputsDir, filename);
42
- await copyFile(attachment.localPath, destPath);
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
- // Cloud mode: download from storage_key (not yet implemented for cursor-runner)
49
- console.warn(`Attachment ${attachment.filename}: cloud storage download not yet implemented for cursor-runner`);
50
- return null;
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;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAExE,MAAM,uBAAuB,GAAG,UAAU,CAAC;AAC3C,MAAM,aAAa,GAAG,QAAQ,CAAC;AAO/B;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAAyB,EACzB,SAAiB,EACjB,mBAA2B,EAC3B,IAAuB;IAEvB,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAExC,MAAM,WAAW,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IACnD,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5C,MAAM,OAAO,GAAyB,EAAE,CAAC;IAEzC,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,UAAU,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;YACtE,IAAI,QAAQ;gBAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CACV,gCAAgC,UAAU,CAAC,QAAQ,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CACnG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,iBAAiB,CAC9B,UAAsB,EACtB,SAAiB,EACjB,IAAuB;IAEvB,IAAI,IAAI,KAAK,OAAO,IAAI,UAAU,CAAC,SAAS,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,IAAI,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC3C,MAAM,QAAQ,CAAC,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAE/C,OAAO;YACL,QAAQ;YACR,YAAY,EAAE,IAAI,CAAC,uBAAuB,EAAE,aAAa,EAAE,QAAQ,CAAC;SACrE,CAAC;IACJ,CAAC;IAED,gFAAgF;IAChF,OAAO,CAAC,IAAI,CACV,cAAc,UAAU,CAAC,QAAQ,gEAAgE,CAClG,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC"}
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
- `const baseName=(p)=>{const n=p.replace(/\\\\/g,"/");const i=n.lastIndexOf("/");return i>=0?n.slice(i+1):n;};`,
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":"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"}
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":"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"}
1
+ {"version":3,"file":"hook-script.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/hook-script.ts"],"names":[],"mappings":"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"}
@@ -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 { createArtifactStorage, loadArtifactStorageConfig } from "../../shared/artifact-storage.js";
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, removeStigmerSymlink } from "./skill-resolver.js";
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
- // Created once here so it is available to EVERY persist below. Best-effort:
135
- // if it can't be built (e.g. proxy mode without a token), offload is disabled
136
- // but persistStatus still enforces the aggregate size cap, so persistence can
137
- // never silently blow past the gRPC limit.
138
- let artifactStorage;
139
- try {
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) => persistStatus(client, executionId, s, { offload: statusOffload });
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
- const attachmentResults = await resolveAttachments(spec.attachments, sessionId, primaryWorkspaceDir, config.mode);
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
- return userMessage;
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
  // ---------------------------------------------------------------------------