@sideboard-ai/core 0.1.291 → 0.1.293

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 (33) hide show
  1. package/dist/agents/cursor-runner.cjs +329 -36
  2. package/dist/agents/cursor-runner.js +318 -17
  3. package/dist/{agents-4BBCKITL.js → agents-SITR5V6F.js} +3 -3
  4. package/dist/{agents-XAKSP653.js → agents-WT72O5A3.js} +3 -3
  5. package/dist/{chunk-2IXKTAIT.js → chunk-2VGLZIU6.js} +37 -13
  6. package/dist/{chunk-GBAACE3C.js → chunk-4YXXLWDI.js} +3 -3
  7. package/dist/{chunk-4VXWLY3L.js → chunk-IP5WSADC.js} +9 -9
  8. package/dist/{chunk-QOUTSISS.js → chunk-LSHGJI4S.js} +10 -10
  9. package/dist/{chunk-O4324MXE.js → chunk-PO3PDNMV.js} +1 -1
  10. package/dist/{chunk-LUQ7MIVD.js → chunk-RPCKGO3J.js} +3 -3
  11. package/dist/{chunk-5HPMKLJ7.js → chunk-UO5C2L2G.js} +37 -13
  12. package/dist/{chunk-G74TSBJ3.js → chunk-WRDKJRWF.js} +3 -3
  13. package/dist/{chunk-VNCPRMIK.js → chunk-XZW3BXZW.js} +3 -3
  14. package/dist/{chunk-V3BSO2O6.js → chunk-ZRQWV3MS.js} +1 -1
  15. package/dist/{conductor-UFJXJPTF.js → conductor-MAYZSFL6.js} +3 -3
  16. package/dist/{conductor-5MNGC25D.js → conductor-P7XPGYF4.js} +3 -3
  17. package/dist/{coordinator-prompt-5Q66Y5KW.js → coordinator-prompt-NAM5JHLT.js} +2 -2
  18. package/dist/{coordinator-prompt-BYPEW6DE.js → coordinator-prompt-OZFKNCM3.js} +2 -2
  19. package/dist/{global-workspace-BTH2YGDZ.js → global-workspace-CTFQDG7G.js} +2 -2
  20. package/dist/{global-workspace-YKOYXSB2.js → global-workspace-UGR6FQXI.js} +2 -2
  21. package/dist/index.cjs +166 -89
  22. package/dist/index.d.cts +10 -4
  23. package/dist/index.d.ts +10 -4
  24. package/dist/index.js +135 -82
  25. package/dist/mcp/run-stdio.cjs +37 -13
  26. package/dist/mcp/run-stdio.js +5 -5
  27. package/dist/{orchestrator-EQSK2RMI.js → orchestrator-4QVF6CE2.js} +5 -5
  28. package/dist/{orchestrator-GSOW3RPA.js → orchestrator-BOGVTCWI.js} +5 -5
  29. package/dist/{workspaces-ELO2YVTU.js → workspaces-QNKFBA63.js} +2 -2
  30. package/dist/{workspaces-RFD6I4FM.js → workspaces-YNZXVBSF.js} +2 -2
  31. package/dist/{worktree-ADVQ6X6C.js → worktree-EMCWDPZJ.js} +1 -1
  32. package/dist/{worktree-TN7DZDJZ.js → worktree-ERRH4TAG.js} +1 -1
  33. package/package.json +1 -1
@@ -35,9 +35,291 @@ import {
35
35
  import "../chunk-EHT2DRT4.js";
36
36
 
37
37
  // src/agents/cursor-runner.ts
38
- import { mkdirSync } from "fs";
38
+ import { mkdirSync as mkdirSync2 } from "fs";
39
39
  import { createInterface } from "readline";
40
40
 
41
+ // src/agents/cursor-catalog.ts
42
+ import {
43
+ existsSync,
44
+ mkdirSync,
45
+ readdirSync,
46
+ readFileSync,
47
+ renameSync,
48
+ unlinkSync,
49
+ writeFileSync
50
+ } from "fs";
51
+ import { basename, dirname, join } from "path";
52
+ import lockfile from "proper-lockfile";
53
+ var CATALOG_FILES = ["agents.ndjson", "runs.ndjson", "run_events.ndjson", "checkpoints.ndjson"];
54
+ var INTERRUPTED_TMP = /^\.(agents|runs|run_events|checkpoints)\.ndjson\.[0-9a-f]{16}\.tmp$/;
55
+ async function acquireCursorCatalogLock(storeDir) {
56
+ mkdirSync(storeDir, { recursive: true });
57
+ const lockPath = join(storeDir, ".catalog.lock");
58
+ writeFileSync(lockPath, "", { flag: "a" });
59
+ try {
60
+ return await lockfile.lock(lockPath, {
61
+ retries: { retries: 40, minTimeout: 50, maxTimeout: 500 },
62
+ stale: 5e3,
63
+ update: 2e3
64
+ });
65
+ } catch {
66
+ return async () => {
67
+ };
68
+ }
69
+ }
70
+ var heldCatalogRelease;
71
+ function publishRepair(repair, note) {
72
+ for (const line of repair.notes) note(line);
73
+ }
74
+ async function openCursorCatalogTurn(storeDir, note) {
75
+ mkdirSync(storeDir, { recursive: true });
76
+ heldCatalogRelease = await acquireCursorCatalogLock(storeDir);
77
+ publishRepair(repairCursorLocalCatalog(storeDir), note);
78
+ }
79
+ function repairCursorCatalogForResume(storeDir, note) {
80
+ const repair = repairCursorLocalCatalog(storeDir);
81
+ publishRepair(repair, note);
82
+ return repair.repaired;
83
+ }
84
+ async function closeCursorCatalogTurn() {
85
+ const release = heldCatalogRelease;
86
+ heldCatalogRelease = void 0;
87
+ if (release) await release().catch(() => void 0);
88
+ }
89
+ function repairCursorLocalCatalog(storeDir) {
90
+ if (!existsSync(storeDir)) return { repaired: false, notes: [] };
91
+ const notes = [];
92
+ let repaired = false;
93
+ const loaded = /* @__PURE__ */ new Map();
94
+ for (const file of CATALOG_FILES) {
95
+ loaded.set(file, loadCatalogFile(storeDir, file));
96
+ }
97
+ const checkpoints = mergeByKey(
98
+ loaded.get("checkpoints.ndjson").main,
99
+ loaded.get("checkpoints.ndjson").tmpRows,
100
+ (row) => checkpointKey(row),
101
+ (main2) => main2
102
+ );
103
+ const blobs = checkpointBlobs(checkpoints.rows);
104
+ const agents = mergeByKey(
105
+ loaded.get("agents.ndjson").main,
106
+ loaded.get("agents.ndjson").tmpRows,
107
+ (row) => typeof row.agentId === "string" ? row.agentId : null,
108
+ (main2, extra) => preferAgent(main2, extra, blobs)
109
+ );
110
+ const runs = mergeByKey(
111
+ loaded.get("runs.ndjson").main,
112
+ loaded.get("runs.ndjson").tmpRows,
113
+ (row) => runKey(row),
114
+ (main2, extra) => num(extra.updatedAt) >= num(main2.updatedAt) ? extra : main2
115
+ );
116
+ const events = mergeByKey(
117
+ loaded.get("run_events.ndjson").main,
118
+ loaded.get("run_events.ndjson").tmpRows,
119
+ (row) => eventKey(row),
120
+ (main2) => main2
121
+ );
122
+ const merged = {
123
+ "checkpoints.ndjson": checkpoints.rows,
124
+ "agents.ndjson": agents.rows,
125
+ "runs.ndjson": runs.rows,
126
+ "run_events.ndjson": events.rows
127
+ };
128
+ for (const agent of merged["agents.ndjson"]) {
129
+ if (restoreAgentCheckpoint(agent, merged["runs.ndjson"], blobs, notes)) repaired = true;
130
+ if (clearMissingActiveRun(agent, merged["runs.ndjson"], notes)) repaired = true;
131
+ }
132
+ for (const file of CATALOG_FILES) {
133
+ const prior = loaded.get(file);
134
+ const next = merged[file];
135
+ const recovered = countNewKeys(prior.main, next, keyFor(file));
136
+ if (recovered > 0) {
137
+ notes.push(`Cursor catalog recovered ${recovered} ${file} row(s) from an interrupted write`);
138
+ repaired = true;
139
+ }
140
+ if (!sameRows(prior.main, next)) {
141
+ writeCatalogFile(join(storeDir, file), next);
142
+ repaired = true;
143
+ }
144
+ for (const tmp of prior.tmps) {
145
+ try {
146
+ unlinkSync(tmp);
147
+ } catch {
148
+ }
149
+ }
150
+ }
151
+ return { repaired, notes };
152
+ }
153
+ function loadCatalogFile(storeDir, file) {
154
+ const tmps = interruptedTmps(storeDir, file);
155
+ return {
156
+ main: readRows(join(storeDir, file)),
157
+ tmps,
158
+ tmpRows: tmps.flatMap((tmp) => readRows(tmp))
159
+ };
160
+ }
161
+ function keyFor(file) {
162
+ if (file === "checkpoints.ndjson") return checkpointKey;
163
+ if (file === "agents.ndjson") return (row) => typeof row.agentId === "string" ? row.agentId : null;
164
+ if (file === "runs.ndjson") return runKey;
165
+ return eventKey;
166
+ }
167
+ function countNewKeys(main2, next, keyOf) {
168
+ const had = new Set(main2.map(keyOf).filter((key) => Boolean(key)));
169
+ let n = 0;
170
+ for (const row of next) {
171
+ const key = keyOf(row);
172
+ if (key && !had.has(key)) n += 1;
173
+ }
174
+ return n;
175
+ }
176
+ function interruptedTmps(storeDir, file) {
177
+ let names = [];
178
+ try {
179
+ names = readdirSync(storeDir);
180
+ } catch {
181
+ return [];
182
+ }
183
+ const prefix = `.${file}.`;
184
+ return names.filter((name) => name.startsWith(prefix) && INTERRUPTED_TMP.test(name)).map((name) => join(storeDir, name));
185
+ }
186
+ function readRows(file) {
187
+ if (!existsSync(file)) return [];
188
+ let raw = "";
189
+ try {
190
+ raw = readFileSync(file, "utf8");
191
+ } catch {
192
+ return [];
193
+ }
194
+ const rows = [];
195
+ for (const line of raw.split("\n")) {
196
+ const trimmed = line.trim();
197
+ if (!trimmed) continue;
198
+ try {
199
+ const parsed = JSON.parse(trimmed);
200
+ if (isRow(parsed)) rows.push(parsed);
201
+ } catch {
202
+ }
203
+ }
204
+ return rows;
205
+ }
206
+ function writeCatalogFile(file, rows) {
207
+ if (rows.length === 0 && !existsSync(file)) return;
208
+ const body = rows.map((row) => JSON.stringify(row)).join("\n");
209
+ const dir = dirname(file);
210
+ mkdirSync(dir, { recursive: true });
211
+ const tmp = join(dir, `.${basename(file)}.repair.tmp`);
212
+ writeFileSync(tmp, body);
213
+ renameSync(tmp, file);
214
+ }
215
+ function mergeByKey(main2, extra, keyOf, prefer) {
216
+ const out = [];
217
+ const index = /* @__PURE__ */ new Map();
218
+ for (const row of main2) {
219
+ const key = keyOf(row);
220
+ if (!key || index.has(key)) continue;
221
+ index.set(key, out.length);
222
+ out.push(structuredClone(row));
223
+ }
224
+ for (const row of extra) {
225
+ const key = keyOf(row);
226
+ if (!key) continue;
227
+ const at = index.get(key);
228
+ if (at == null) {
229
+ index.set(key, out.length);
230
+ out.push(structuredClone(row));
231
+ continue;
232
+ }
233
+ out[at] = structuredClone(prefer(out[at], row));
234
+ }
235
+ return { rows: out };
236
+ }
237
+ function restoreAgentCheckpoint(agent, runs, blobs, notes) {
238
+ const agentId = typeof agent.agentId === "string" ? agent.agentId : "";
239
+ const current = rootBlobId(agent.latestCheckpoint);
240
+ if (!agentId || !current || blobs.has(`${agentId}\0${current}`)) return false;
241
+ const fallback = fallbackCheckpoint(runs, agentId, blobs);
242
+ const fallbackId = rootBlobId(fallback);
243
+ if (!fallback || !fallbackId) return false;
244
+ agent.latestCheckpoint = fallback;
245
+ agent.updatedAt = Date.now();
246
+ notes.push(
247
+ `Cursor catalog restored agent ${agentId} to checkpoint ${fallbackId} (root blob ${current} was missing)`
248
+ );
249
+ return true;
250
+ }
251
+ function clearMissingActiveRun(agent, runs, notes) {
252
+ const agentId = typeof agent.agentId === "string" ? agent.agentId : "";
253
+ const active = typeof agent.activeRunId === "string" ? agent.activeRunId : "";
254
+ if (!agentId || !active) return false;
255
+ const live = runs.some((run) => run.agentId === agentId && run.runId === active);
256
+ if (live) return false;
257
+ agent.activeRunId = null;
258
+ if (agent.status === "running") agent.status = "idle";
259
+ notes.push(`Cursor catalog cleared missing run ${active} on agent ${agentId}`);
260
+ return true;
261
+ }
262
+ function fallbackCheckpoint(runs, agentId, blobs) {
263
+ const mine = runs.filter((run) => run.agentId === agentId).slice().sort((a, b) => num(b.updatedAt) - num(a.updatedAt));
264
+ for (const run of mine) {
265
+ for (const field of ["latestCheckpointRef", "startCheckpointRef"]) {
266
+ const ref = run[field];
267
+ const id = rootBlobId(ref);
268
+ if (id && blobs.has(`${agentId}\0${id}`) && isRow(ref)) return ref;
269
+ }
270
+ }
271
+ return null;
272
+ }
273
+ function preferAgent(main2, extra, blobs) {
274
+ const mainOk = checkpointPresent(main2, blobs);
275
+ const extraOk = checkpointPresent(extra, blobs);
276
+ if (mainOk && !extraOk) return main2;
277
+ if (extraOk && !mainOk) return extra;
278
+ return num(extra.updatedAt) >= num(main2.updatedAt) ? extra : main2;
279
+ }
280
+ function checkpointPresent(agent, blobs) {
281
+ const agentId = typeof agent.agentId === "string" ? agent.agentId : "";
282
+ const id = rootBlobId(agent.latestCheckpoint);
283
+ if (!id) return true;
284
+ return blobs.has(`${agentId}\0${id}`);
285
+ }
286
+ function checkpointBlobs(rows) {
287
+ const ids = /* @__PURE__ */ new Set();
288
+ for (const row of rows) {
289
+ if (typeof row.agentId !== "string" || typeof row.blobId !== "string") continue;
290
+ if (typeof row.dataBase64 !== "string" || !row.dataBase64) continue;
291
+ ids.add(`${row.agentId}\0${row.blobId}`);
292
+ }
293
+ return ids;
294
+ }
295
+ function checkpointKey(row) {
296
+ if (typeof row.agentId !== "string" || typeof row.blobId !== "string") return null;
297
+ return `${row.agentId}\0${row.blobId}`;
298
+ }
299
+ function runKey(row) {
300
+ if (typeof row.agentId !== "string" || typeof row.runId !== "string") return null;
301
+ return `${row.agentId}\0${row.runId}`;
302
+ }
303
+ function eventKey(row) {
304
+ if (typeof row.runId !== "string") return null;
305
+ if (typeof row.seq === "number") return `${row.runId}\0${row.seq}`;
306
+ if (typeof row.offset === "string") return `${row.runId}\0${row.offset}`;
307
+ return null;
308
+ }
309
+ function rootBlobId(ref) {
310
+ if (!isRow(ref) || typeof ref.rootBlobId !== "string" || !ref.rootBlobId) return null;
311
+ return ref.rootBlobId;
312
+ }
313
+ function num(value) {
314
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
315
+ }
316
+ function sameRows(a, b) {
317
+ return JSON.stringify(a) === JSON.stringify(b);
318
+ }
319
+ function isRow(value) {
320
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
321
+ }
322
+
41
323
  // src/agents/cursor-session.ts
42
324
  function cursorErrorMessage(err) {
43
325
  if (err instanceof Error) return err.message.trim();
@@ -146,7 +428,7 @@ async function* iterateUntilIdle(iterable, idleMs, onIdle, activity) {
146
428
  }
147
429
 
148
430
  // src/agents/sliced-file-read.ts
149
- import { readFileSync, realpathSync, statSync } from "fs";
431
+ import { readFileSync as readFileSync2, realpathSync, statSync } from "fs";
150
432
  import { isAbsolute, relative, resolve, sep } from "path";
151
433
  var SLICED_READ_DEFAULT_LIMIT = 200;
152
434
  var SLICED_READ_MAX_LINE_CHARS = 2e3;
@@ -221,7 +503,7 @@ function executeSlicedFileRead(cwd, args) {
221
503
  try {
222
504
  const st = statSync(abs);
223
505
  if (!st.isFile()) return `Error: not a file: ${rawPath}`;
224
- buf = readFileSync(abs);
506
+ buf = readFileSync2(abs);
225
507
  } catch (err) {
226
508
  return `Error: ${err instanceof Error ? err.message : String(err)}`;
227
509
  }
@@ -306,7 +588,7 @@ function emit(event) {
306
588
  }
307
589
  function localAgentStore(Store, threadId) {
308
590
  const root = cursorSdkStoreDir(threadId);
309
- mkdirSync(root, { recursive: true });
591
+ mkdirSync2(root, { recursive: true });
310
592
  return new Store(root);
311
593
  }
312
594
  async function cancelStaleRuns(Agent, agentId, opts) {
@@ -396,6 +678,9 @@ async function main() {
396
678
  fast: Boolean(req.fast)
397
679
  });
398
680
  const mode = req.planMode ? "plan" : "agent";
681
+ const storeRoot = cursorSdkStoreDir(req.threadId);
682
+ const note = (line) => emit({ type: "stderr", data: line });
683
+ await openCursorCatalogTurn(storeRoot, note);
399
684
  const store = localAgentStore(JsonlLocalAgentStore, req.threadId);
400
685
  const isolateSources = [];
401
686
  let onRpcProgress = () => {
@@ -444,20 +729,22 @@ async function main() {
444
729
  async function createAgent() {
445
730
  return retryTransport(() => Agent.create(createOpts));
446
731
  }
732
+ function resumeAgent(agentId) {
733
+ const { name: _name, ...opts } = createOpts;
734
+ return retryTransport(() => Agent.resume(agentId, opts));
735
+ }
447
736
  async function openAgent() {
448
737
  try {
449
- return req.agentId ? await retryTransport(
450
- () => Agent.resume(req.agentId, {
451
- apiKey,
452
- model,
453
- mode,
454
- local,
455
- disallowedTools: ["read"],
456
- ...mcpServers ? { mcpServers } : {}
457
- })
458
- ) : await createAgent();
738
+ return req.agentId ? await resumeAgent(req.agentId) : await createAgent();
459
739
  } catch (err) {
460
740
  if (!isUnresumableCursorSession(err)) throw err;
741
+ if (repairCursorCatalogForResume(storeRoot, note) && req.agentId) {
742
+ try {
743
+ return await resumeAgent(req.agentId);
744
+ } catch (err2) {
745
+ if (!isUnresumableCursorSession(err2)) throw err2;
746
+ }
747
+ }
461
748
  emit({
462
749
  type: "stderr",
463
750
  data: cursorSessionRecoveryMessage(err, req.agentId)
@@ -511,7 +798,9 @@ async function main() {
511
798
  new Promise((resolve2) => {
512
799
  setTimeout(resolve2, 2e3);
513
800
  })
514
- ]).finally(() => process.exit(1));
801
+ ]).finally(() => {
802
+ void closeCursorCatalogTurn().finally(() => process.exit(1));
803
+ });
515
804
  };
516
805
  process.once("SIGTERM", onSignal);
517
806
  process.once("SIGINT", onSignal);
@@ -609,11 +898,21 @@ async function main() {
609
898
  return await runTurn(agent);
610
899
  }
611
900
  if (!isUnresumableCursorSession(err)) throw err;
901
+ const agentId = agent.agentId;
902
+ await agent[Symbol.asyncDispose]().catch(() => void 0);
903
+ const repaired = repairCursorCatalogForResume(storeRoot, note);
904
+ if (repaired) {
905
+ try {
906
+ agent = await resumeAgent(agentId);
907
+ return await runTurn(agent);
908
+ } catch (err2) {
909
+ if (!isUnresumableCursorSession(err2)) throw err2;
910
+ }
911
+ }
612
912
  emit({
613
913
  type: "stderr",
614
- data: cursorSessionRecoveryMessage(err, agent.agentId)
914
+ data: cursorSessionRecoveryMessage(err, agentId)
615
915
  });
616
- await agent[Symbol.asyncDispose]().catch(() => void 0);
617
916
  agent = await createAgent();
618
917
  return await runTurn(agent);
619
918
  }
@@ -631,6 +930,8 @@ async function main() {
631
930
  const message = err instanceof Error ? err.message : String(err);
632
931
  emit({ type: "stderr", data: message });
633
932
  return 1;
933
+ } finally {
934
+ await closeCursorCatalogTurn();
634
935
  }
635
936
  }
636
937
  main().then(
@@ -37,7 +37,7 @@ import {
37
37
  planModeTurnInstruction,
38
38
  resolveCursorModelId,
39
39
  resolveQuotaFallbackAgent
40
- } from "./chunk-VNCPRMIK.js";
40
+ } from "./chunk-XZW3BXZW.js";
41
41
  import "./chunk-VGNLMGBK.js";
42
42
  import "./chunk-YSLCWCMJ.js";
43
43
  import {
@@ -52,8 +52,8 @@ import {
52
52
  resolveLoginCommand,
53
53
  whichOnPath
54
54
  } from "./chunk-NXXQFLNQ.js";
55
- import "./chunk-V3BSO2O6.js";
56
- import "./chunk-2IXKTAIT.js";
55
+ import "./chunk-ZRQWV3MS.js";
56
+ import "./chunk-2VGLZIU6.js";
57
57
  import "./chunk-QB5MDOHL.js";
58
58
  import "./chunk-2P4XKWV2.js";
59
59
  import "./chunk-33KDPEK4.js";
@@ -46,7 +46,7 @@ import {
46
46
  resolveCursorSdkRoot,
47
47
  resolveQuotaFallbackAgent,
48
48
  turnCostUsdFromCursorUsage
49
- } from "./chunk-LUQ7MIVD.js";
49
+ } from "./chunk-RPCKGO3J.js";
50
50
  import {
51
51
  getAgentSetupInfo,
52
52
  installAgent,
@@ -63,8 +63,8 @@ import "./chunk-7IAMHN2N.js";
63
63
  import "./chunk-PXMLMBMI.js";
64
64
  import "./chunk-2GPQZZLY.js";
65
65
  import "./chunk-AFLNFGGB.js";
66
- import "./chunk-O4324MXE.js";
67
- import "./chunk-5HPMKLJ7.js";
66
+ import "./chunk-PO3PDNMV.js";
67
+ import "./chunk-UO5C2L2G.js";
68
68
  import "./chunk-N2WW62LF.js";
69
69
  import "./chunk-DD67JWTN.js";
70
70
  import {
@@ -2021,15 +2021,8 @@ async function fetchOriginForWorktree(repoPath, sourceRef, opts) {
2021
2021
  async function fastForwardMainCheckoutIfSafe(repoPath, opts) {
2022
2022
  try {
2023
2023
  const branch = opts?.branch?.trim() || await resolveDefaultBranch(repoPath, { network: false });
2024
- const head = await git(["rev-parse", "--abbrev-ref", "HEAD"], repoPath, {
2025
- reject: false
2026
- });
2027
- const current = head.stdout.trim();
2028
- if (head.exitCode !== 0 || !current || current === "HEAD" || current !== branch) {
2029
- return { updated: false, reason: "not-on-default" };
2030
- }
2031
- if (await isDirty(repoPath)) {
2032
- return { updated: false, reason: "dirty" };
2024
+ if (!branch || branch === "HEAD" || branch.includes("..")) {
2025
+ return { updated: false, reason: "ff-failed" };
2033
2026
  }
2034
2027
  const remote = `origin/${branch}`;
2035
2028
  const remoteOk = await git(["rev-parse", "--verify", remote], repoPath, {
@@ -2038,8 +2031,27 @@ async function fastForwardMainCheckoutIfSafe(repoPath, opts) {
2038
2031
  if (remoteOk.exitCode !== 0) {
2039
2032
  return { updated: false, reason: "no-origin-tip" };
2040
2033
  }
2034
+ const worktrees = await listWorktrees(repoPath);
2035
+ if (worktrees.length === 0) {
2036
+ return { updated: false, reason: "ff-failed" };
2037
+ }
2038
+ const checkout = worktrees.find((entry) => entry.branch === branch) ?? null;
2039
+ const localRef = `refs/heads/${branch}`;
2040
+ const localOk = await git(["rev-parse", "--verify", localRef], repoPath, {
2041
+ reject: false
2042
+ });
2043
+ if (localOk.exitCode !== 0) {
2044
+ if (checkout) return { updated: false, reason: "ff-failed" };
2045
+ const created = await git(["branch", "--track", branch, remote], repoPath, {
2046
+ reject: false
2047
+ });
2048
+ if (created.exitCode !== 0) {
2049
+ return { updated: false, reason: "ff-failed" };
2050
+ }
2051
+ return { updated: true, reason: "updated" };
2052
+ }
2041
2053
  const ancestor = await git(
2042
- ["merge-base", "--is-ancestor", "HEAD", remote],
2054
+ ["merge-base", "--is-ancestor", localRef, remote],
2043
2055
  repoPath,
2044
2056
  { reject: false }
2045
2057
  );
@@ -2047,7 +2059,7 @@ async function fastForwardMainCheckoutIfSafe(repoPath, opts) {
2047
2059
  return { updated: false, reason: "diverged" };
2048
2060
  }
2049
2061
  const behind = await git(
2050
- ["rev-list", "--count", `HEAD..${remote}`],
2062
+ ["rev-list", "--count", `${localRef}..${remote}`],
2051
2063
  repoPath,
2052
2064
  { reject: false }
2053
2065
  );
@@ -2055,10 +2067,22 @@ async function fastForwardMainCheckoutIfSafe(repoPath, opts) {
2055
2067
  if (behind.exitCode !== 0 || !Number.isFinite(n) || n <= 0) {
2056
2068
  return { updated: false, reason: "already-current" };
2057
2069
  }
2058
- const ff = await git(["merge", "--ff-only", remote], repoPath, {
2070
+ if (checkout) {
2071
+ if (await isDirty(checkout.path)) {
2072
+ return { updated: false, reason: "dirty" };
2073
+ }
2074
+ const ff = await git(["merge", "--ff-only", remote], checkout.path, {
2075
+ reject: false
2076
+ });
2077
+ if (ff.exitCode !== 0) {
2078
+ return { updated: false, reason: "ff-failed" };
2079
+ }
2080
+ return { updated: true, reason: "updated" };
2081
+ }
2082
+ const moved = await git(["branch", "-f", branch, remote], repoPath, {
2059
2083
  reject: false
2060
2084
  });
2061
- if (ff.exitCode !== 0) {
2085
+ if (moved.exitCode !== 0) {
2062
2086
  return { updated: false, reason: "ff-failed" };
2063
2087
  }
2064
2088
  return { updated: true, reason: "updated" };
@@ -8,11 +8,11 @@ import {
8
8
  isGlobalThread,
9
9
  isOrchestratorThread,
10
10
  isSlackCoordinatorThread
11
- } from "./chunk-V3BSO2O6.js";
11
+ } from "./chunk-ZRQWV3MS.js";
12
12
  import {
13
13
  mergeAgentGitAuthEnv,
14
14
  resolveAgentGitAuthEnv
15
- } from "./chunk-2IXKTAIT.js";
15
+ } from "./chunk-2VGLZIU6.js";
16
16
  import {
17
17
  allocateTeamName,
18
18
  createEmptyThread,
@@ -1776,7 +1776,7 @@ var PORT_RANGE_SIZE = 10;
1776
1776
  var RECLAIM_RETRY_DELAY_MS = 40;
1777
1777
  var RECLAIM_TRIES = 8;
1778
1778
  async function killReclaimable(ports, worktreePath, signal) {
1779
- const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-UFJXJPTF.js");
1779
+ const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-MAYZSFL6.js");
1780
1780
  if (worktreePath) killStaleWorktreeListeners2(ports, worktreePath, signal);
1781
1781
  else killListenersOnPorts2(ports);
1782
1782
  }
@@ -27,7 +27,7 @@ import {
27
27
  parseSessionQuotaResetAt,
28
28
  planModeTurnInstruction,
29
29
  resolveQuotaFallbackAgent
30
- } from "./chunk-LUQ7MIVD.js";
30
+ } from "./chunk-RPCKGO3J.js";
31
31
  import {
32
32
  REVIEW_TAB_TITLE,
33
33
  applyWorktreePkgCacheEnv,
@@ -66,7 +66,7 @@ import {
66
66
  worktreeDevProcessKey,
67
67
  worktreeRunProcessKey,
68
68
  worktreeSetupProcessKey
69
- } from "./chunk-G74TSBJ3.js";
69
+ } from "./chunk-WRDKJRWF.js";
70
70
  import {
71
71
  applyAgentEvent,
72
72
  clipAgentEventForPaint,
@@ -118,7 +118,7 @@ import {
118
118
  stageAbsolutePathsAsAttachments,
119
119
  stageBuffersAsAttachments,
120
120
  syncWorkspacesFromThreads
121
- } from "./chunk-O4324MXE.js";
121
+ } from "./chunk-PO3PDNMV.js";
122
122
  import {
123
123
  addPrStackLayer,
124
124
  allocateTeamSlug,
@@ -160,7 +160,7 @@ import {
160
160
  resolveRepoRoot,
161
161
  switchWorktreeBranch,
162
162
  withRepoGitLock
163
- } from "./chunk-5HPMKLJ7.js";
163
+ } from "./chunk-UO5C2L2G.js";
164
164
  import {
165
165
  clearStaleIndexLocks,
166
166
  enrichPathWithNpmGlobalBin,
@@ -503,9 +503,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
503
503
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
504
504
  );
505
505
  }
506
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-BTH2YGDZ.js");
506
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-CTFQDG7G.js");
507
507
  if (isGlobalThread2(thread)) {
508
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-BYPEW6DE.js");
508
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-OZFKNCM3.js");
509
509
  ensureGlobalCoordinatorCwd2(
510
510
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
511
511
  );
@@ -3119,7 +3119,7 @@ async function adoptThread(input) {
3119
3119
  messages: input.messages ?? []
3120
3120
  });
3121
3121
  writeThread(thread);
3122
- const { addWorkspace: addWorkspace2 } = await import("./workspaces-ELO2YVTU.js");
3122
+ const { addWorkspace: addWorkspace2 } = await import("./workspaces-QNKFBA63.js");
3123
3123
  await addWorkspace2(repoPath);
3124
3124
  const { ensureWorktreeSideboardIgnored } = await import("./worktree-exclude-5N2KTYIT.js");
3125
3125
  await ensureWorktreeSideboardIgnored(input.worktreePath);
@@ -5994,7 +5994,7 @@ function formatScheduledPrompt(name, prompt) {
5994
5994
  ${prompt}`;
5995
5995
  }
5996
5996
  async function defaultDeps() {
5997
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-EQSK2RMI.js");
5997
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-4QVF6CE2.js");
5998
5998
  const orch = getOrchestrator2();
5999
5999
  return {
6000
6000
  findThread: (id) => findThreadByRef(id),
@@ -8866,7 +8866,7 @@ async function startOrchestration(opts) {
8866
8866
  sourceRef: "default",
8867
8867
  ...createOpts
8868
8868
  }).catch(async () => {
8869
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-ADVQ6X6C.js");
8869
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-EMCWDPZJ.js");
8870
8870
  const repo = await resolveRepoRoot2(repoPath);
8871
8871
  const def = await resolveDefaultBranch2(repo);
8872
8872
  return orch.createThread({