@stigmer/runner 3.1.5 → 3.1.7

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 (53) hide show
  1. package/dist/.build-fingerprint +1 -1
  2. package/dist/activities/discover-mcp-server.js +23 -0
  3. package/dist/activities/discover-mcp-server.js.map +1 -1
  4. package/dist/activities/execute-cursor/index.js +39 -1
  5. package/dist/activities/execute-cursor/index.js.map +1 -1
  6. package/dist/activities/execute-cursor/workspace-provision.d.ts +23 -4
  7. package/dist/activities/execute-cursor/workspace-provision.js +17 -6
  8. package/dist/activities/execute-cursor/workspace-provision.js.map +1 -1
  9. package/dist/activities/execute-deep-agent/index.js +8 -1
  10. package/dist/activities/execute-deep-agent/index.js.map +1 -1
  11. package/dist/activities/execute-deep-agent/inline-publisher.d.ts +1 -1
  12. package/dist/activities/execute-deep-agent/post-stream.d.ts +1 -1
  13. package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +1 -1
  14. package/dist/activities/execute-deep-agent/streaming-terminal.d.ts +1 -1
  15. package/dist/activities/execute-deep-agent/streaming.d.ts +1 -1
  16. package/dist/activities/execute-deep-agent/v3-status-builder.d.ts +1 -1
  17. package/dist/shared/execution-status-writer.d.ts +31 -0
  18. package/dist/shared/execution-status-writer.js +43 -0
  19. package/dist/shared/execution-status-writer.js.map +1 -0
  20. package/dist/shared/mcp-oauth-detect.d.ts +53 -0
  21. package/dist/shared/mcp-oauth-detect.js +99 -0
  22. package/dist/shared/mcp-oauth-detect.js.map +1 -0
  23. package/dist/shared/workspace/writeback-coordinator.d.ts +120 -0
  24. package/dist/shared/workspace/writeback-coordinator.js +393 -0
  25. package/dist/shared/workspace/writeback-coordinator.js.map +1 -0
  26. package/package.json +2 -2
  27. package/src/activities/discover-mcp-server.ts +25 -0
  28. package/src/activities/execute-cursor/__tests__/workspace-provision.test.ts +23 -15
  29. package/src/activities/execute-cursor/index.ts +41 -1
  30. package/src/activities/execute-cursor/workspace-provision.ts +39 -7
  31. package/src/activities/execute-deep-agent/__tests__/post-stream.test.ts +1 -1
  32. package/src/activities/execute-deep-agent/index.ts +8 -1
  33. package/src/activities/execute-deep-agent/inline-publisher.ts +1 -1
  34. package/src/activities/execute-deep-agent/post-stream.ts +1 -1
  35. package/src/activities/execute-deep-agent/status-builder.ts +1 -1
  36. package/src/activities/execute-deep-agent/streaming-side-effects.ts +1 -1
  37. package/src/activities/execute-deep-agent/streaming-terminal.ts +1 -1
  38. package/src/activities/execute-deep-agent/streaming.ts +1 -1
  39. package/src/activities/execute-deep-agent/v3-status-builder.ts +1 -1
  40. package/src/shared/__tests__/mcp-oauth-detect.test.ts +147 -0
  41. package/src/shared/execution-status-writer.ts +56 -0
  42. package/src/shared/mcp-oauth-detect.ts +112 -0
  43. package/src/shared/workspace/__tests__/writeback-coordinator.integration.test.ts +230 -0
  44. package/src/shared/workspace/__tests__/writeback-coordinator.test.ts +477 -0
  45. package/src/{activities/execute-deep-agent → shared/workspace}/writeback-coordinator.ts +206 -94
  46. package/dist/activities/execute-deep-agent/execution-status-writer.d.ts +0 -17
  47. package/dist/activities/execute-deep-agent/execution-status-writer.js +0 -9
  48. package/dist/activities/execute-deep-agent/execution-status-writer.js.map +0 -1
  49. package/dist/activities/execute-deep-agent/writeback-coordinator.d.ts +0 -71
  50. package/dist/activities/execute-deep-agent/writeback-coordinator.js +0 -299
  51. package/dist/activities/execute-deep-agent/writeback-coordinator.js.map +0 -1
  52. package/src/activities/execute-deep-agent/__tests__/writeback-coordinator.test.ts +0 -426
  53. package/src/activities/execute-deep-agent/execution-status-writer.ts +0 -19
@@ -0,0 +1,393 @@
1
+ /**
2
+ * Git write-back coordinator for workspace entries.
3
+ *
4
+ * Commits the workspace's changes to the session's write-back branch, pushes,
5
+ * and keeps one pull request open per session. The branch and PR are
6
+ * SESSION-scoped (`stigmer/<session-id>`): a session is one workstream, so
7
+ * every approved turn appends commits to the same branch and the same PR —
8
+ * mirroring how Cursor cloud agents and Codex present a session's deliverable.
9
+ * Execution-scoped branches were a modeling bug: each turn branched off the
10
+ * previous turn's branch, leaving a trail of superseded PRs.
11
+ *
12
+ * Because the branch outlives any single execution, every git/GitHub step is
13
+ * idempotent across coordinator instances: the branch is checked out if it
14
+ * already exists (locally, or on the remote after a sandbox re-provision), and
15
+ * an already-open PR for the branch is adopted instead of re-created.
16
+ *
17
+ * When this runs depends on the harness's file-review mode:
18
+ * - Capture mode (apply-then-review — every git workspace): the streaming
19
+ * trigger is suppressed and `finalize()` runs exactly once on the APPROVED
20
+ * tree, after review decisions reconcile (see processCaptureWriteback in
21
+ * index.ts). Speculative mid-turn edits never reach GitHub.
22
+ * - Legacy non-capture turns: `onFileModified(path)` triggers an incremental
23
+ * commit/push per file-modifying tool call (DD-5), with `finalize()` as the
24
+ * post-stream safety net.
25
+ *
26
+ * Concurrency: one mutex per workspace entry serializes git operations.
27
+ */
28
+ import { create } from "@bufbuild/protobuf";
29
+ import { WorkspaceWriteBackSchema, } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/writeback_pb";
30
+ import { WorkspaceWriteBackPhase, } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/writeback_pb";
31
+ import { GitWriteBackMode } from "@stigmer/protos/ai/stigmer/agentic/session/v1/enum_pb";
32
+ import { SourceType } from "./types.js";
33
+ import { gitCommitAsAgent } from "./git-identity.js";
34
+ const WRITE_BACK_ENABLED_MODES = new Set([
35
+ GitWriteBackMode.GIT_WRITE_BACK_MODE_UNSPECIFIED,
36
+ GitWriteBackMode.GIT_WRITE_BACK_BRANCH_AND_PR,
37
+ ]);
38
+ const GITHUB_API = "https://api.github.com";
39
+ export class WriteBackCoordinator {
40
+ statusWriter;
41
+ executionId;
42
+ workspaceBackend;
43
+ branchName;
44
+ /**
45
+ * Token for the GitHub PR API, plumbed explicitly from the resolved
46
+ * execution env (the same GITHUB_TOKEN that credentials the clone/push).
47
+ * Empty when the session has none — commit/push may still succeed via the
48
+ * repo-local credential store, so an empty token degrades to PUSHED with an
49
+ * actionable error rather than blocking the write-back.
50
+ */
51
+ githubToken;
52
+ eligible = new Map();
53
+ state = new Map();
54
+ locks = new Map();
55
+ constructor(opts) {
56
+ this.statusWriter = opts.statusWriter;
57
+ this.executionId = opts.executionId;
58
+ this.workspaceBackend = opts.workspaceBackend;
59
+ this.githubToken = opts.githubToken;
60
+ // The FULL session id: a truncated ULID is timestamp-dominated, so two
61
+ // sessions created near-simultaneously would collide on a short prefix.
62
+ this.branchName = `stigmer/${opts.sessionId}`;
63
+ this.initEligibleEntries(opts.provisionResults, opts.workspaceEntries);
64
+ }
65
+ get hasEligibleEntries() {
66
+ return this.eligible.size > 0;
67
+ }
68
+ /**
69
+ * Called after a file-modifying tool completes (legacy non-capture turns
70
+ * only). Resolves the path to a workspace entry and runs an incremental
71
+ * commit/push cycle. Fire-and-forget: errors are logged, never thrown.
72
+ */
73
+ async onFileModified(path) {
74
+ try {
75
+ const entryName = this.resolveEntry(path);
76
+ if (!entryName)
77
+ return;
78
+ await this.withLock(entryName, () => this.writeBackEntry(entryName));
79
+ }
80
+ catch (err) {
81
+ console.warn(`[WriteBack] execution=${this.executionId} — ` +
82
+ `onFileModified error for '${path}': ${err}`);
83
+ }
84
+ }
85
+ /**
86
+ * Commits and pushes every eligible workspace entry's remaining uncommitted
87
+ * changes. In capture mode this is THE write-back — invoked once on the
88
+ * approved tree after review reconcile; on legacy turns it is the
89
+ * post-stream safety net.
90
+ */
91
+ async finalize() {
92
+ for (const entryName of this.eligible.keys()) {
93
+ try {
94
+ await this.withLock(entryName, () => this.writeBackEntry(entryName));
95
+ }
96
+ catch (err) {
97
+ console.warn(`[WriteBack] execution=${this.executionId} entry=${entryName} — ` +
98
+ `finalize error: ${err}`);
99
+ }
100
+ }
101
+ }
102
+ // ── Initialization ──────────────────────────────────────────────────
103
+ initEligibleEntries(provisionResults, workspaceEntries) {
104
+ const modeMap = new Map();
105
+ for (const entry of workspaceEntries) {
106
+ const source = entry.source;
107
+ if (source?.source.case === "gitRepo") {
108
+ modeMap.set(entry.name, source.source.value.writeBackMode);
109
+ }
110
+ }
111
+ for (const pr of provisionResults) {
112
+ if (pr.sourceType !== SourceType.GIT_REPO)
113
+ continue;
114
+ if (!pr.gitMetadata)
115
+ continue;
116
+ if (!pr.gitMetadata.gitCredentialsConfigured)
117
+ continue;
118
+ const mode = modeMap.get(pr.entryName) ?? GitWriteBackMode.GIT_WRITE_BACK_MODE_UNSPECIFIED;
119
+ if (!WRITE_BACK_ENABLED_MODES.has(mode))
120
+ continue;
121
+ this.eligible.set(pr.entryName, {
122
+ provisionResult: pr,
123
+ baseBranch: pr.gitMetadata.branch,
124
+ rootDir: pr.rootDir,
125
+ entryName: pr.entryName,
126
+ });
127
+ this.state.set(pr.entryName, {
128
+ branchReady: false,
129
+ prCreated: false,
130
+ prUrl: "",
131
+ prNumber: 0,
132
+ commitCount: 0,
133
+ lastCommitSha: "",
134
+ githubOwner: "",
135
+ githubRepo: "",
136
+ });
137
+ }
138
+ if (this.eligible.size > 0) {
139
+ console.log(`[WriteBack] execution=${this.executionId} — coordinator initialized with ` +
140
+ `${this.eligible.size} eligible workspace(s): ${[...this.eligible.keys()].join(", ")}`);
141
+ }
142
+ }
143
+ // ── Path Resolution ─────────────────────────────────────────────────
144
+ resolveEntry(path) {
145
+ if (this.eligible.size === 0)
146
+ return null;
147
+ if (this.eligible.size === 1)
148
+ return this.eligible.keys().next().value;
149
+ const normalized = path.replace(/^\/+/, "");
150
+ for (const entryName of this.eligible.keys()) {
151
+ if (normalized.startsWith(entryName + "/") || normalized === entryName) {
152
+ return entryName;
153
+ }
154
+ }
155
+ return null;
156
+ }
157
+ // ── Core Write-Back Cycle ───────────────────────────────────────────
158
+ /**
159
+ * One write-back cycle for an entry: branch → commit → push → PR → status.
160
+ *
161
+ * Failure semantics are split by what actually succeeded (the phases are
162
+ * facts, not a single verdict):
163
+ * - branch/commit/push failure → FAILED (the work did not reach GitHub);
164
+ * - PR failure after a successful push → PUSHED with the PR error carried
165
+ * in `error` — the branch is live and usable, and saying "failed" for a
166
+ * pushed branch would be dishonest (the original cloud regression).
167
+ */
168
+ async writeBackEntry(entryName) {
169
+ const entry = this.eligible.get(entryName);
170
+ const entryState = this.state.get(entryName);
171
+ const rootDir = entry.rootDir;
172
+ const exec = async (cmd) => {
173
+ return this.workspaceBackend.execute(`cd ${rootDir} && ${cmd}`);
174
+ };
175
+ try {
176
+ const hasChanges = await this.hasChanges(exec);
177
+ if (!hasChanges)
178
+ return;
179
+ if (!entryState.branchReady) {
180
+ await this.ensureBranch(entryName, entryState, exec);
181
+ }
182
+ await this.commitAndPush(entryName, entryState, exec);
183
+ }
184
+ catch (err) {
185
+ console.warn(`[WriteBack] execution=${this.executionId} entry=${entryName} — ` +
186
+ `commit/push error: ${err}`);
187
+ const wb = create(WorkspaceWriteBackSchema, {
188
+ workspaceEntryName: entryName,
189
+ baseBranch: entry.baseBranch,
190
+ branchName: entryState.branchReady ? this.branchName : "",
191
+ phase: WorkspaceWriteBackPhase.WORKSPACE_WRITE_BACK_FAILED,
192
+ error: String(err),
193
+ });
194
+ if (entryState.prCreated) {
195
+ wb.pullRequestUrl = entryState.prUrl;
196
+ wb.pullRequestNumber = entryState.prNumber;
197
+ }
198
+ this.statusWriter.addWriteBack(wb);
199
+ return;
200
+ }
201
+ let prError = "";
202
+ if (!entryState.prCreated) {
203
+ try {
204
+ await this.ensurePr(entryName, entryState, entry);
205
+ }
206
+ catch (err) {
207
+ console.warn(`[WriteBack] execution=${this.executionId} entry=${entryName} — ` +
208
+ `PR error (branch is pushed): ${err}`);
209
+ prError = String(err);
210
+ }
211
+ }
212
+ await this.updateStatus(entryName, entryState, entry, exec, prError);
213
+ }
214
+ // ── Git Operations ──────────────────────────────────────────────────
215
+ async hasChanges(exec) {
216
+ const diff = await exec("git diff --stat").catch(() => "");
217
+ const staged = await exec("git diff --cached --stat").catch(() => "");
218
+ if (diff.trim() || staged.trim())
219
+ return true;
220
+ const untracked = await exec("git ls-files --others --exclude-standard").catch(() => "");
221
+ return untracked.trim().length > 0;
222
+ }
223
+ /**
224
+ * Put the working tree on the session branch, wherever the branch already
225
+ * lives. Three cases, in order:
226
+ * 1. HEAD is already on it — a later turn in the same workspace (the common
227
+ * multi-turn path; the previous turn's cycle left HEAD there).
228
+ * 2. It exists locally or on the remote — a re-provisioned workspace whose
229
+ * clone lost the local branch. Checked out (tracking the remote ref when
230
+ * that is the only copy). The checkout carries this turn's uncommitted
231
+ * changes along; if they collide with the branch's prior commits git
232
+ * refuses, and the error surfaces as an honest FAILED record — we never
233
+ * force (-B) over the session's pushed history.
234
+ * 3. Neither — the session's first write-back creates it.
235
+ */
236
+ async ensureBranch(entryName, entryState, exec) {
237
+ const current = (await exec("git branch --show-current").catch(() => "")).trim();
238
+ if (current === this.branchName) {
239
+ entryState.branchReady = true;
240
+ return;
241
+ }
242
+ const localRef = await exec(`git rev-parse --verify --quiet refs/heads/${this.branchName}`).then((out) => out.trim(), () => "");
243
+ if (localRef) {
244
+ await exec(`git checkout ${this.branchName}`);
245
+ }
246
+ else {
247
+ const remoteRef = (await exec(`git ls-remote --heads origin ${this.branchName}`).catch(() => "")).trim();
248
+ if (remoteRef) {
249
+ await exec(`git fetch origin ${this.branchName}`);
250
+ await exec(`git checkout -b ${this.branchName} origin/${this.branchName}`);
251
+ }
252
+ else {
253
+ await exec(`git checkout -b ${this.branchName}`);
254
+ }
255
+ }
256
+ entryState.branchReady = true;
257
+ console.log(`[WriteBack] execution=${this.executionId} entry=${entryName} — ` +
258
+ `on branch ${this.branchName}`);
259
+ }
260
+ async commitAndPush(entryName, entryState, exec) {
261
+ await exec("git add -A");
262
+ // Committed as the agent identity: the cloud sandbox has no git identity
263
+ // configured (a bare commit fails with "Author identity unknown"), and in
264
+ // local mode the agent's work should not be attributed to the host user.
265
+ // The execution id in the message links each commit back to its turn.
266
+ await exec(gitCommitAsAgent(`agent changes (${this.executionId})`));
267
+ entryState.commitCount++;
268
+ const shaOutput = await exec("git rev-parse HEAD");
269
+ entryState.lastCommitSha = shaOutput.trim();
270
+ // Always -u: idempotent whether this push creates the remote branch or
271
+ // appends to it, and it (re-)establishes tracking after a re-provision.
272
+ await exec(`git push -u origin ${this.branchName}`);
273
+ console.log(`[WriteBack] execution=${this.executionId} entry=${entryName} — ` +
274
+ `commit pushed to ${this.branchName} (sha=${entryState.lastCommitSha.slice(0, 12)})`);
275
+ }
276
+ // ── GitHub PR ───────────────────────────────────────────────────────
277
+ /**
278
+ * Ensure one open PR exists for the session branch: adopt an already-open
279
+ * one (a prior turn — possibly a prior coordinator instance — created it),
280
+ * else create it. Runs only after a successful push; a thrown error here is
281
+ * reported as PUSHED + error, never FAILED.
282
+ */
283
+ async ensurePr(entryName, entryState, entry) {
284
+ const meta = entry.provisionResult.gitMetadata;
285
+ if (!entryState.githubOwner) {
286
+ const { owner, repo } = parseGithubRepo(meta.repoUrl);
287
+ entryState.githubOwner = owner;
288
+ entryState.githubRepo = repo;
289
+ }
290
+ if (!this.githubToken) {
291
+ throw new Error("No GitHub token available to open a pull request. The branch " +
292
+ `'${this.branchName}' was pushed — open the PR manually, or configure ` +
293
+ "GITHUB_TOKEN for the session so PRs are created automatically.");
294
+ }
295
+ const existing = await this.findOpenPr(entryState);
296
+ if (existing) {
297
+ entryState.prCreated = true;
298
+ entryState.prUrl = existing.url;
299
+ entryState.prNumber = existing.number;
300
+ console.log(`[WriteBack] execution=${this.executionId} entry=${entryName} — ` +
301
+ `adopted open PR #${existing.number}: ${existing.url}`);
302
+ return;
303
+ }
304
+ const shortSessionId = this.branchName.replace(/^stigmer\//, "");
305
+ const prBody = `Automated pull request from a Stigmer agent session.\n\n` +
306
+ `**Session:** \`${shortSessionId}\`\n` +
307
+ `**Workspace:** \`${entryName}\`\n\n` +
308
+ `Each approved turn appends its commits to this pull request.\n`;
309
+ const resp = await fetch(`${GITHUB_API}/repos/${entryState.githubOwner}/${entryState.githubRepo}/pulls`, {
310
+ method: "POST",
311
+ headers: this.githubHeaders(),
312
+ body: JSON.stringify({
313
+ title: `Stigmer agent changes (${shortSessionId})`,
314
+ body: prBody,
315
+ head: this.branchName,
316
+ base: entry.baseBranch,
317
+ }),
318
+ });
319
+ if (!resp.ok) {
320
+ const body = await resp.text();
321
+ throw new Error(`GitHub API error (HTTP ${resp.status}): ${body}`);
322
+ }
323
+ const data = await resp.json();
324
+ entryState.prCreated = true;
325
+ entryState.prUrl = data.html_url ?? "";
326
+ entryState.prNumber = data.number ?? 0;
327
+ console.log(`[WriteBack] execution=${this.executionId} entry=${entryName} — ` +
328
+ `PR #${entryState.prNumber} created: ${entryState.prUrl}`);
329
+ }
330
+ /** The open PR whose head is the session branch, if one exists. */
331
+ async findOpenPr(entryState) {
332
+ const head = `${entryState.githubOwner}:${this.branchName}`;
333
+ const resp = await fetch(`${GITHUB_API}/repos/${entryState.githubOwner}/${entryState.githubRepo}` +
334
+ `/pulls?head=${encodeURIComponent(head)}&state=open`, { headers: this.githubHeaders() });
335
+ if (!resp.ok) {
336
+ const body = await resp.text();
337
+ throw new Error(`GitHub API error listing PRs (HTTP ${resp.status}): ${body}`);
338
+ }
339
+ const data = await resp.json();
340
+ const pr = data[0];
341
+ if (!pr)
342
+ return null;
343
+ return { url: pr.html_url ?? "", number: pr.number ?? 0 };
344
+ }
345
+ githubHeaders() {
346
+ return {
347
+ "Authorization": `Bearer ${this.githubToken}`,
348
+ "Accept": "application/vnd.github+json",
349
+ "Content-Type": "application/json",
350
+ };
351
+ }
352
+ // ── Status Reporting ────────────────────────────────────────────────
353
+ async updateStatus(entryName, entryState, entry, exec, prError) {
354
+ const summaryOutput = await exec(`git diff --stat ${entry.baseBranch}...HEAD`).catch(() => "");
355
+ const phase = entryState.prCreated
356
+ ? WorkspaceWriteBackPhase.WORKSPACE_WRITE_BACK_PR_CREATED
357
+ : WorkspaceWriteBackPhase.WORKSPACE_WRITE_BACK_PUSHED;
358
+ const wb = create(WorkspaceWriteBackSchema, {
359
+ workspaceEntryName: entryName,
360
+ branchName: this.branchName,
361
+ baseBranch: entry.baseBranch,
362
+ commitSha: entryState.lastCommitSha,
363
+ pullRequestUrl: entryState.prUrl,
364
+ pullRequestNumber: entryState.prNumber,
365
+ diffSummary: summaryOutput.trim(),
366
+ phase,
367
+ // Non-empty only for a PUSHED record whose PR step failed: the branch
368
+ // is live, and the error tells the user why there is no PR link yet.
369
+ error: prError,
370
+ });
371
+ this.statusWriter.addWriteBack(wb);
372
+ }
373
+ // ── Concurrency ─────────────────────────────────────────────────────
374
+ async withLock(entryName, fn) {
375
+ const existing = this.locks.get(entryName) ?? Promise.resolve();
376
+ const next = existing.then(fn, fn);
377
+ this.locks.set(entryName, next);
378
+ await next;
379
+ }
380
+ }
381
+ // ── Helpers ───────────────────────────────────────────────────────────
382
+ /**
383
+ * Extract GitHub owner and repo from a clone URL.
384
+ * Supports HTTPS (with or without .git) and SSH formats.
385
+ */
386
+ export function parseGithubRepo(repoUrl) {
387
+ const httpsMatch = repoUrl.match(/github\.com[/:]([^/]+)\/([^/.]+?)(?:\.git)?$/);
388
+ if (httpsMatch) {
389
+ return { owner: httpsMatch[1], repo: httpsMatch[2] };
390
+ }
391
+ throw new Error(`Cannot parse GitHub owner/repo from URL: ${repoUrl}`);
392
+ }
393
+ //# sourceMappingURL=writeback-coordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writeback-coordinator.js","sourceRoot":"","sources":["../../../src/shared/workspace/writeback-coordinator.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stigmer/runner",
3
- "version": "3.1.5",
3
+ "version": "3.1.7",
4
4
  "description": "Embeddable Temporal worker for the Stigmer AI agent platform — handles agent execution, workflow orchestration, and MCP server management",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -85,7 +85,7 @@
85
85
  "@opentelemetry/sdk-metrics": "^2.0.0",
86
86
  "@opentelemetry/sdk-trace-base": "^2.0.0",
87
87
  "@opentelemetry/sdk-trace-node": "^2.0.0",
88
- "@stigmer/protos": "3.1.5",
88
+ "@stigmer/protos": "3.1.7",
89
89
  "@temporalio/activity": "^1.11.0",
90
90
  "@temporalio/client": "^1.11.0",
91
91
  "@temporalio/common": "^1.11.0",
@@ -24,6 +24,7 @@ import { activityStarted, activityFinished } from "../idle-watchdog.js";
24
24
  import { StigmerClient } from "../client/stigmer-client.js";
25
25
  import { mcpServerToResolved } from "../shared/mcp-resolver.js";
26
26
  import { toMcpClientConfig } from "../shared/mcp-manager.js";
27
+ import { detectOAuthChallenge } from "../shared/mcp-oauth-detect.js";
27
28
  import type { McpServer } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/api_pb";
28
29
  import type { Config } from "../config.js";
29
30
 
@@ -374,6 +375,14 @@ async function connectAndDiscover(
374
375
  );
375
376
  }
376
377
  });
378
+ } catch (err) {
379
+ // The MCP client surfaces a 401 OAuth challenge as an opaque aggregate
380
+ // ("unhandled errors in a TaskGroup"). For HTTP servers, re-probe once to
381
+ // see if the endpoint is actually asking for OAuth and, if so, replace the
382
+ // useless error with an actionable one. Non-OAuth failures rethrow as-is.
383
+ const oauthError = await classifyHttpOAuthFailure(slug, connectionConfig);
384
+ if (oauthError) throw oauthError;
385
+ throw err;
377
386
  } finally {
378
387
  await client.close().catch((err: unknown) => {
379
388
  console.warn(
@@ -386,6 +395,22 @@ async function connectAndDiscover(
386
395
  return { tools, resourceTemplates };
387
396
  }
388
397
 
398
+ /**
399
+ * If the server uses HTTP transport, probe its endpoint to classify a discovery
400
+ * failure as an OAuth challenge. Returns the actionable error to throw, or
401
+ * `null` for stdio servers and non-OAuth failures (caller rethrows original).
402
+ */
403
+ async function classifyHttpOAuthFailure(
404
+ slug: string,
405
+ connectionConfig: ReturnType<typeof toMcpClientConfig>,
406
+ ): Promise<Error | null> {
407
+ const connection = connectionConfig[slug];
408
+ if (!connection || connection.transport !== "http" || !connection.url) {
409
+ return null;
410
+ }
411
+ return detectOAuthChallenge(connection.url, connection.headers, slug);
412
+ }
413
+
389
414
  async function withTimeout<T>(
390
415
  ms: number,
391
416
  serverSlug: string,
@@ -138,12 +138,13 @@ describe("provisionCursorWorkspace", () => {
138
138
  const workspaceRoot = join(tmpRoot, "workspace");
139
139
  mkdirSync(workspaceRoot, { recursive: true });
140
140
 
141
- const dirs = await provisionCursorWorkspace(
142
- makeConfig(workspaceRoot),
143
- gitRepoSession(source),
144
- {},
145
- "test-session-clone",
146
- );
141
+ const { workspaceDirs: dirs, provisionResults, workspaceBackend } =
142
+ await provisionCursorWorkspace(
143
+ makeConfig(workspaceRoot),
144
+ gitRepoSession(source),
145
+ {},
146
+ "test-session-clone",
147
+ );
147
148
 
148
149
  expect(dirs).toHaveLength(1);
149
150
  const clonedFile = join(dirs[0], MARKER_FILE);
@@ -151,6 +152,12 @@ describe("provisionCursorWorkspace", () => {
151
152
  expect(readFileSync(clonedFile, "utf-8")).toBe(MARKER_CONTENT);
152
153
  // The clone must be a real git repo (so the agent can run git in it).
153
154
  expect(existsSync(join(dirs[0], ".git"))).toBe(true);
155
+
156
+ // The provision results carry the git metadata the write-back
157
+ // coordinator consumes (repo URL, base branch), through the same backend.
158
+ expect(provisionResults).toHaveLength(1);
159
+ expect(provisionResults[0].gitMetadata?.branch).toBe("main");
160
+ expect(workspaceBackend).toBeDefined();
154
161
  }, GIT_TEST_TIMEOUT_MS);
155
162
 
156
163
  it("clones into a workspace root that already contains lost+found (PVC simulation)", async () => {
@@ -161,7 +168,7 @@ describe("provisionCursorWorkspace", () => {
161
168
  mkdirSync(join(workspaceRoot, "lost+found"), { recursive: true });
162
169
  writeFileSync(join(workspaceRoot, "lost+found", "stray"), "fsck-artifact");
163
170
 
164
- const dirs = await provisionCursorWorkspace(
171
+ const { workspaceDirs: dirs } = await provisionCursorWorkspace(
165
172
  makeConfig(workspaceRoot),
166
173
  gitRepoSession(source),
167
174
  {},
@@ -196,7 +203,7 @@ describe("provisionCursorWorkspace", () => {
196
203
  const workspaceRoot = join(tmpRoot, "default-branch-workspace");
197
204
  mkdirSync(workspaceRoot, { recursive: true });
198
205
 
199
- const dirs = await provisionCursorWorkspace(
206
+ const { workspaceDirs: dirs } = await provisionCursorWorkspace(
200
207
  makeConfig(workspaceRoot),
201
208
  gitRepoSessionNoBranch(source),
202
209
  {},
@@ -221,7 +228,7 @@ describe("provisionCursorWorkspace", () => {
221
228
  // a worktree here, so it must be left untouched.
222
229
  mkdirSync(join(workspaceRoot, "lost+found"), { recursive: true });
223
230
 
224
- const dirs = await provisionCursorWorkspace(
231
+ const { workspaceDirs: dirs } = await provisionCursorWorkspace(
225
232
  makeConfig(workspaceRoot),
226
233
  multiGitRepoSession([
227
234
  { name: "frontend", url: frontend },
@@ -259,13 +266,14 @@ describe("provisionCursorWorkspace", () => {
259
266
  // Leftovers from other sessions at the shared root must not be visible.
260
267
  writeFileSync(join(workspaceRoot, "other-session-leftover.md"), "not yours");
261
268
 
262
- const dirs = await provisionCursorWorkspace(
269
+ const { workspaceDirs: dirs, provisionResults } = await provisionCursorWorkspace(
263
270
  makeConfig(workspaceRoot),
264
271
  emptySession(),
265
272
  {},
266
273
  "test-session-empty",
267
274
  );
268
275
 
276
+ expect(provisionResults).toEqual([]);
269
277
  expect(dirs).toEqual([join(workspaceRoot, "sessions", "test-session-empty")]);
270
278
  expect(existsSync(dirs[0])).toBe(true);
271
279
  expect(existsSync(join(dirs[0], "other-session-leftover.md"))).toBe(false);
@@ -276,9 +284,9 @@ describe("provisionCursorWorkspace", () => {
276
284
  mkdirSync(workspaceRoot, { recursive: true });
277
285
  const config = makeConfig(workspaceRoot);
278
286
 
279
- const turn1 = await provisionCursorWorkspace(config, emptySession(), {}, "stable-session");
287
+ const turn1 = (await provisionCursorWorkspace(config, emptySession(), {}, "stable-session")).workspaceDirs;
280
288
  writeFileSync(join(turn1[0], "notes.md"), "turn 1 output");
281
- const turn2 = await provisionCursorWorkspace(config, emptySession(), {}, "stable-session");
289
+ const turn2 = (await provisionCursorWorkspace(config, emptySession(), {}, "stable-session")).workspaceDirs;
282
290
 
283
291
  expect(turn2).toEqual(turn1);
284
292
  expect(readFileSync(join(turn2[0], "notes.md"), "utf-8")).toBe("turn 1 output");
@@ -289,8 +297,8 @@ describe("provisionCursorWorkspace", () => {
289
297
  mkdirSync(workspaceRoot, { recursive: true });
290
298
  const config = makeConfig(workspaceRoot);
291
299
 
292
- const [dirA] = await provisionCursorWorkspace(config, emptySession(), {}, "session-a");
293
- const [dirB] = await provisionCursorWorkspace(config, emptySession(), {}, "session-b");
300
+ const [dirA] = (await provisionCursorWorkspace(config, emptySession(), {}, "session-a")).workspaceDirs;
301
+ const [dirB] = (await provisionCursorWorkspace(config, emptySession(), {}, "session-b")).workspaceDirs;
294
302
 
295
303
  expect(dirA).not.toBe(dirB);
296
304
  writeFileSync(join(dirA, "a.md"), "session a");
@@ -304,7 +312,7 @@ describe("provisionCursorWorkspace", () => {
304
312
  const workspaceRoot = join(tmpRoot, "workspace-local");
305
313
  mkdirSync(workspaceRoot, { recursive: true });
306
314
 
307
- const dirs = await provisionCursorWorkspace(
315
+ const { workspaceDirs: dirs } = await provisionCursorWorkspace(
308
316
  makeConfig(workspaceRoot),
309
317
  localPathSession(projectDir),
310
318
  {},
@@ -99,6 +99,8 @@ import {
99
99
  import { deriveExecutionFingerprintKey } from "../../shared/approval-fingerprint.js";
100
100
  import { getRunnerHitlMasterSecret } from "../../shared/fingerprint-secret.js";
101
101
  import { provisionCursorWorkspace } from "./workspace-provision.js";
102
+ import { WriteBackCoordinator } from "../../shared/workspace/writeback-coordinator.js";
103
+ import { statusProtoWriter } from "../../shared/execution-status-writer.js";
102
104
  import { setInterceptorExecutionId, runWithExecutionContext } from "./fetch-interceptor.js";
103
105
  import { closeProxySessions } from "./http2-interceptor.js";
104
106
  import { resolveModelId, ensureLoaded as ensurePricingLoaded } from "./model-pricing.js";
@@ -277,11 +279,32 @@ async function executeCursorInner(
277
279
  // native harness. Git provisioning is idempotent across multi-turn and
278
280
  // HITL reinvocations.
279
281
  await reportSetupProgress(client, executionId, "Provisioning workspace");
280
- blueprint.workspaceDirs = await provisionCursorWorkspace(
282
+ const workspaceProvision = await provisionCursorWorkspace(
281
283
  config, session, envVars, sessionId ?? "",
282
284
  );
285
+ blueprint.workspaceDirs = workspaceProvision.workspaceDirs;
283
286
  heartbeat();
284
287
 
288
+ // Git write-back: pushes the session's APPROVED tree to the session
289
+ // branch (stigmer/<session-id>) and keeps one PR open — the same
290
+ // approval-gated model as the deep-agent harness (its
291
+ // processCaptureWriteback). Finalize runs at exactly two seams below:
292
+ // the pure-file-review resume (after decisions reconcile) and terminal
293
+ // completion. Never mid-turn: the working tree is speculative until
294
+ // reviewed. Non-eligible workspaces (local paths, no credentials) make
295
+ // this a no-op coordinator.
296
+ const writebackCoordinator = workspaceProvision.provisionResults.length > 0
297
+ ? new WriteBackCoordinator({
298
+ statusWriter: statusProtoWriter(status),
299
+ executionId,
300
+ sessionId: sessionId ?? "",
301
+ githubToken: envVars.GITHUB_TOKEN ?? "",
302
+ provisionResults: workspaceProvision.provisionResults,
303
+ workspaceEntries: session.spec?.workspaceEntries ?? [],
304
+ workspaceBackend: workspaceProvision.workspaceBackend,
305
+ })
306
+ : null;
307
+
285
308
  // Apply-then-review is the universal file-review model (Slice 2c). When the
286
309
  // primary workspace is a real git work tree, file edits flow during the turn
287
310
  // and are captured per-file from the git diff at the turn boundary
@@ -478,6 +501,14 @@ async function executeCursorInner(
478
501
  // (Cursor-like). The reconcile is done; the execution is complete.
479
502
  status.phase = ExecutionPhase.EXECUTION_COMPLETED;
480
503
  status.completedAt = utcTimestamp();
504
+ // Push the APPROVED tree — reconcile snapped rejected files back to
505
+ // baseline, so what finalize commits is exactly what the user kept.
506
+ // Mirrors the deep-agent's processCaptureWriteback: after reconcile,
507
+ // before persist, never on a failed reconcile (diverged bytes must
508
+ // not reach the remote).
509
+ if (!fileReviewFailed && writebackCoordinator) {
510
+ await writebackCoordinator.finalize();
511
+ }
481
512
  if (fileReviewFailed) {
482
513
  // What-you-approve-is-what-applies could not be honored (on-disk bytes
483
514
  // diverged from the approved digest). Surface it to the human; the
@@ -1566,6 +1597,15 @@ async function executeCursorInner(
1566
1597
  );
1567
1598
  }
1568
1599
 
1600
+ // Write-back safety net on terminal completion. A capture-mode turn with
1601
+ // captured changes always paused above (boundary.waiting), so reaching
1602
+ // here means no reviewable delta this turn and this is normally a no-op —
1603
+ // it exists for the same reason the deep-agent finalizes on completion:
1604
+ // stragglers outside the capture (and it never runs mid-turn).
1605
+ if (status.phase === ExecutionPhase.EXECUTION_COMPLETED && writebackCoordinator) {
1606
+ await writebackCoordinator.finalize();
1607
+ }
1608
+
1569
1609
  // NOW persist — subscriber sees COMPLETED + structured_output atomically
1570
1610
  await persist(status);
1571
1611