@cydm/pie 2.5.0 → 2.5.1

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.
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
2
  import {
3
3
  createAskUserCapability
4
- } from "../../../chunks/chunk-JXY3YLDS.js";
4
+ } from "../../../chunks/chunk-REP6QANY.js";
5
5
  import "../../../chunks/chunk-ZKHMNEXL.js";
6
6
  import "../../../chunks/chunk-YRXXRLMQ.js";
7
7
  import "../../../chunks/chunk-XXGYUYXI.js";
@@ -136,7 +136,42 @@ function spawnHerdrReport(binPath, args, timeoutMs = REPORT_TIMEOUT_MS) {
136
136
  } catch {
137
137
  }
138
138
  }
139
- function attachHerdrReporter(host, herdr, transport = (args) => spawnHerdrReport(herdr.binPath, args), sequencer = createReportSequencer()) {
139
+ var KILL_GRACE_MS = 1e3;
140
+ async function runHerdrReportBounded(binPath, args, timeoutMs = REPORT_TIMEOUT_MS) {
141
+ let child;
142
+ try {
143
+ child = spawn(binPath, args, { detached: true, stdio: "ignore" });
144
+ } catch {
145
+ return { outcome: "spawn-error", exitCode: null };
146
+ }
147
+ return await new Promise((resolve) => {
148
+ let killTimer;
149
+ let settled = false;
150
+ const finish = (outcome, exitCode) => {
151
+ if (settled) return;
152
+ settled = true;
153
+ clearTimeout(timer);
154
+ if (killTimer) clearTimeout(killTimer);
155
+ resolve({ outcome, exitCode });
156
+ };
157
+ const timer = setTimeout(() => {
158
+ try {
159
+ child.kill("SIGTERM");
160
+ } catch {
161
+ }
162
+ killTimer = setTimeout(() => {
163
+ try {
164
+ if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
165
+ } catch {
166
+ }
167
+ killTimer = setTimeout(() => finish("escalated", child.exitCode), 250);
168
+ }, KILL_GRACE_MS);
169
+ }, timeoutMs);
170
+ child.on("error", () => finish("spawn-error", null));
171
+ child.on("exit", (code) => finish("exited", code));
172
+ });
173
+ }
174
+ function attachHerdrReporter(host, herdr, transport = (args) => spawnHerdrReport(herdr.binPath, args), sequencer = createReportSequencer(), shutdownTransport = (args) => runHerdrReportBounded(herdr.binPath, args)) {
140
175
  const nextSeq = sequencer;
141
176
  let agentActive = false;
142
177
  const blockingToolCalls = /* @__PURE__ */ new Map();
@@ -238,10 +273,10 @@ function attachHerdrReporter(host, herdr, transport = (args) => spawnHerdrReport
238
273
  }
239
274
  publishState();
240
275
  });
241
- host.registerCleanup?.((reason) => {
276
+ host.registerCleanup?.(async (reason) => {
242
277
  if (reason !== "shutdown") return;
243
278
  if (!heldPane) return;
244
- report(
279
+ await shutdownTransport(
245
280
  buildReleaseReportArgs({
246
281
  paneId: herdr.paneId,
247
282
  source: SOURCE_ID,
@@ -267,6 +302,7 @@ export {
267
302
  createReportSequencer,
268
303
  herdrAgentStateExtension as default,
269
304
  readHerdrEnvironment,
305
+ runHerdrReportBounded,
270
306
  spawnHerdrReport,
271
307
  summarizeAskUserQuestion
272
308
  };
@@ -24724,11 +24724,13 @@ var McpServerManager = class {
24724
24724
  };
24725
24725
  for (const tools of Object.values(this.readCacheFile())) {
24726
24726
  for (const tool of tools) {
24727
- if (!this.definitions.has(tool.server)) continue;
24727
+ const definition = this.definitions.get(tool.server);
24728
+ if (!definition || definition.disabled) continue;
24728
24729
  claim2(tool, false);
24729
24730
  }
24730
24731
  }
24731
24732
  for (const runtime of this.runtimes.values()) {
24733
+ if (runtime.definition.disabled) continue;
24732
24734
  for (const tool of runtime.tools ?? []) claim2(tool, true);
24733
24735
  }
24734
24736
  for (const name of conflicts) {
@@ -24849,6 +24851,20 @@ ${tail}` : "")
24849
24851
  error: `MCP call "${prefixedName}" was cancelled before the request was sent; nothing was executed.`
24850
24852
  };
24851
24853
  }
24854
+ const postConnectCatalog = this.loadCatalog();
24855
+ if (this.conflictedNames.has(prefixedName)) {
24856
+ return {
24857
+ delivery: "not_sent",
24858
+ error: `MCP tool name "${prefixedName}" became claimed by more than one server during connection. Rename one of the servers so every tool name is unambiguous; the call was NOT sent.`
24859
+ };
24860
+ }
24861
+ const postDescriptor = postConnectCatalog.find((tool) => tool.prefixedName === prefixedName);
24862
+ if (postDescriptor && (postDescriptor.server !== server || postDescriptor.tool !== toolName)) {
24863
+ return {
24864
+ delivery: "not_sent",
24865
+ error: `MCP tool "${prefixedName}" resolved to ${postDescriptor.server}/${postDescriptor.tool} after connecting; refusing to send the stale target ${server}/${toolName}. Retry the call.`
24866
+ };
24867
+ }
24852
24868
  try {
24853
24869
  const result = await withTimeout(
24854
24870
  client.callTool({ name: toolName, arguments: args }, void 0, signal !== void 0 ? { signal } : void 0),
@@ -25266,19 +25282,26 @@ function mcpAdapter(ctx) {
25266
25282
  ctx.log
25267
25283
  );
25268
25284
  async function reloadManager() {
25269
- await manager.dispose().catch(() => void 0);
25285
+ let replacement;
25270
25286
  try {
25271
- manager = new McpServerManager(
25272
- resolveMcpAdapterConfig(ctx.cwd, configDir).servers,
25273
- metadataCacheDir,
25274
- ctx.log
25275
- );
25287
+ const resolved = resolveMcpAdapterConfig(ctx.cwd, configDir);
25288
+ if (resolved.problems.length > 0) {
25289
+ const message = `config layers failed to parse: ${resolved.problems.join("; ")}`;
25290
+ ctx.log(`mcp: config reload failed, keeping the previous manager: ${message}`);
25291
+ if (ctx.hasUI) ctx.ui.notify(`MCP adapter config error (previous servers kept): ${message}`, "error");
25292
+ return { ok: false, error: message };
25293
+ }
25294
+ replacement = new McpServerManager(resolved.servers, metadataCacheDir, ctx.log);
25276
25295
  } catch (error2) {
25277
25296
  const message = error2 instanceof Error ? error2.message : String(error2);
25278
- ctx.log(`mcp: config reload failed: ${message}`);
25279
- if (ctx.hasUI) ctx.ui.notify(`MCP adapter config error: ${message}`, "error");
25280
- manager = new McpServerManager([], metadataCacheDir, ctx.log);
25297
+ ctx.log(`mcp: config reload failed, keeping the previous manager: ${message}`);
25298
+ if (ctx.hasUI) ctx.ui.notify(`MCP adapter config error (previous servers kept): ${message}`, "error");
25299
+ return { ok: false, error: message };
25281
25300
  }
25301
+ const previous = manager;
25302
+ manager = replacement;
25303
+ await previous.dispose().catch(() => void 0);
25304
+ return { ok: true };
25282
25305
  }
25283
25306
  ctx.registerCleanup?.(() => manager.dispose().catch(() => void 0));
25284
25307
  function readProjectOverride() {
@@ -25359,6 +25382,7 @@ function mcpAdapter(ctx) {
25359
25382
  }
25360
25383
  const overrides = readProjectOverride().mcpServers;
25361
25384
  const forceEnabled = overrides?.[definition.name]?.disabled === false;
25385
+ const previousOverride = overrides?.[definition.name]?.disabled;
25362
25386
  const filePath = writeProjectOverride(
25363
25387
  definition.name,
25364
25388
  // enable must lift a source-level disabled:true with an explicit
@@ -25367,8 +25391,24 @@ function mcpAdapter(ctx) {
25367
25391
  // existing force-enable on repeated enable commands too.
25368
25392
  sub === "disable" ? true : definition.disabled || forceEnabled ? false : null
25369
25393
  );
25370
- await reloadManager();
25371
- const effectiveDisabled = manager.getDefinition(definition.name)?.disabled ?? false;
25394
+ const reload = await reloadManager();
25395
+ if (!reload.ok) {
25396
+ writeProjectOverride(definition.name, previousOverride === void 0 ? null : previousOverride);
25397
+ commandCtx.ui.notify(
25398
+ `MCP server "${definition.name}" NOT ${sub === "disable" ? "disabled" : "enabled"}: config reload failed (${reload.error}); previous servers kept and the override was rolled back.`,
25399
+ "error"
25400
+ );
25401
+ return;
25402
+ }
25403
+ const reloaded = manager.getDefinition(definition.name);
25404
+ if (!reloaded) {
25405
+ commandCtx.ui.notify(
25406
+ `MCP server "${definition.name}" disappeared from the reloaded config (wrote ${filePath}); check project/global config merges.`,
25407
+ "error"
25408
+ );
25409
+ return;
25410
+ }
25411
+ const effectiveDisabled = reloaded.disabled;
25372
25412
  const stateLabel = effectiveDisabled ? "disabled" : sub === "disable" ? "still enabled" : "enabled";
25373
25413
  commandCtx.ui.notify(
25374
25414
  `MCP server "${definition.name}" ${stateLabel} (wrote ${filePath}). ` + (eagerRegistered.length > 0 ? "Restart or /reload to resync direct tools." : "Effective immediately."),
@@ -7,7 +7,7 @@ import {
7
7
  isPlanModeSafeCommand,
8
8
  markCompletedPlanSteps,
9
9
  restoreExecutionState
10
- } from "../../../chunks/chunk-JXY3YLDS.js";
10
+ } from "../../../chunks/chunk-REP6QANY.js";
11
11
  import "../../../chunks/chunk-ZKHMNEXL.js";
12
12
  import "../../../chunks/chunk-YRXXRLMQ.js";
13
13
  import {
@@ -1,11 +1,11 @@
1
1
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
2
  import {
3
3
  createCliHostCapabilities
4
- } from "../../../chunks/chunk-PU7IIEKH.js";
4
+ } from "../../../chunks/chunk-N2MK46AE.js";
5
5
  import {
6
6
  createSharedFileSystemTools,
7
7
  createSubagentCapability
8
- } from "../../../chunks/chunk-JXY3YLDS.js";
8
+ } from "../../../chunks/chunk-REP6QANY.js";
9
9
  import "../../../chunks/chunk-ZKHMNEXL.js";
10
10
  import "../../../chunks/chunk-YRXXRLMQ.js";
11
11
  import "../../../chunks/chunk-5353DEEC.js";
@@ -8,7 +8,7 @@ import {
8
8
  executeManageTodoList,
9
9
  executionStateToTodos,
10
10
  restoreExecutionState
11
- } from "../../../chunks/chunk-JXY3YLDS.js";
11
+ } from "../../../chunks/chunk-REP6QANY.js";
12
12
  import "../../../chunks/chunk-ZKHMNEXL.js";
13
13
  import "../../../chunks/chunk-YRXXRLMQ.js";
14
14
  import "../../../chunks/chunk-XXGYUYXI.js";
@@ -8,7 +8,7 @@ import {
8
8
  createSharedWebSearchTool,
9
9
  interpretShellExit,
10
10
  requestInteraction
11
- } from "./chunk-JXY3YLDS.js";
11
+ } from "./chunk-REP6QANY.js";
12
12
  import {
13
13
  getBuiltinDir,
14
14
  getConfigDir
@@ -543,9 +543,24 @@ function createBashTool(defaultCwd, options) {
543
543
  totalNewlines += data.reduce((count, byte) => count + (byte === 10 ? 1 : 0), 0);
544
544
  if (headBytes >= maxHeadBytes) return;
545
545
  const prefix = data.subarray(0, maxHeadBytes - headBytes);
546
- headChunks.push(prefix);
546
+ headChunks.push(Buffer.from(prefix));
547
547
  headBytes += prefix.length;
548
548
  };
549
+ const appendRollingChunk = (data) => {
550
+ const owned = Buffer.from(data);
551
+ chunks.push(owned);
552
+ chunksBytes += owned.length;
553
+ while (chunksBytes > maxChunksBytes && chunks.length > 1) {
554
+ const removed = chunks.shift();
555
+ chunksBytes -= removed.length;
556
+ }
557
+ if (chunks.length === 1 && chunksBytes > maxChunksBytes) {
558
+ const only = chunks[0];
559
+ const tail = only.subarray(only.length - maxChunksBytes);
560
+ chunks[0] = Buffer.from(tail);
561
+ chunksBytes = tail.length;
562
+ }
563
+ };
549
564
  let timedOut = false;
550
565
  let processExited = false;
551
566
  let exitCode = null;
@@ -641,12 +656,7 @@ function createBashTool(defaultCwd, options) {
641
656
  return;
642
657
  }
643
658
  }
644
- chunks.push(data);
645
- chunksBytes += data.length;
646
- while (chunksBytes > maxChunksBytes && chunks.length > 1) {
647
- const removed = chunks.shift();
648
- chunksBytes -= removed.length;
649
- }
659
+ appendRollingChunk(data);
650
660
  if (onUpdate) {
651
661
  const fullBuffer = Buffer.concat(chunks);
652
662
  const fullText = fullBuffer.toString("utf-8");
@@ -700,12 +710,7 @@ function createBashTool(defaultCwd, options) {
700
710
  return;
701
711
  }
702
712
  }
703
- chunks.push(data);
704
- chunksBytes += data.length;
705
- while (chunksBytes > maxChunksBytes && chunks.length > 1) {
706
- const removed = chunks.shift();
707
- chunksBytes -= removed.length;
708
- }
713
+ appendRollingChunk(data);
709
714
  });
710
715
  const finishExecution = async (code, sig, stdioIncomplete = false) => {
711
716
  if (settled) return;
@@ -1742,8 +1742,7 @@ function normalizeTaskDefs(args) {
1742
1742
  if (def.model !== void 0 && !modelOverride) {
1743
1743
  return { error: `Invalid task #${index + 1}: "model" must be a non-empty model reference such as "provider/model-id".` };
1744
1744
  }
1745
- const thinkingLevel = typeof def.thinkingLevel === "string" ? def.thinkingLevel.trim() : void 0;
1746
- if (def.thinkingLevel !== void 0 && (!thinkingLevel || !SUBAGENT_THINKING_LEVELS.includes(thinkingLevel))) {
1745
+ if (def.thinkingLevel !== void 0 && (typeof def.thinkingLevel !== "string" || !SUBAGENT_THINKING_LEVELS.includes(def.thinkingLevel))) {
1747
1746
  return { error: `Invalid task #${index + 1}: "thinkingLevel" must be one of ${SUBAGENT_THINKING_LEVELS.map((level) => `"${level}"`).join(", ")}.` };
1748
1747
  }
1749
1748
  normalized.push({
@@ -1752,7 +1751,7 @@ function normalizeTaskDefs(args) {
1752
1751
  priority,
1753
1752
  timeoutSeconds,
1754
1753
  modelOverride,
1755
- thinkingLevel
1754
+ thinkingLevel: def.thinkingLevel
1756
1755
  });
1757
1756
  }
1758
1757
  return normalized;
@@ -2968,6 +2967,9 @@ var FileSessionStore = class {
2968
2967
  throw new Error(`Session directory is a symbolic link; ${operation} refused: ${dir}`);
2969
2968
  }
2970
2969
  headStatus(sessionId) {
2970
+ if (sessionDirIsSymlink(this.pathsFor(sessionId).dir)) {
2971
+ return "missing";
2972
+ }
2971
2973
  const head = this.tryReadHeadFile(sessionId);
2972
2974
  if (head === "corrupt") {
2973
2975
  return "corrupt";
@@ -3023,6 +3025,9 @@ var FileSessionStore = class {
3023
3025
  * never reinitialize a damaged session to sequence 0.
3024
3026
  */
3025
3027
  tryReadHeadFile(sessionId) {
3028
+ if (sessionDirIsSymlink(this.pathsFor(sessionId).dir)) {
3029
+ return null;
3030
+ }
3026
3031
  const headPath = this.pathsFor(sessionId).headPath;
3027
3032
  const durable = readDurableFileSync(this.gateway, headPath, (content) => this.parseHeadContent(content));
3028
3033
  if (durable !== null) {
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  createCliWebResearchHealthStore,
8
8
  formatSize,
9
9
  truncateHeadTail
10
- } from "./chunks/chunk-PU7IIEKH.js";
10
+ } from "./chunks/chunk-N2MK46AE.js";
11
11
  import {
12
12
  AGENTS_CONTEXT_FILE_NAME,
13
13
  AgentSessionController,
@@ -60,7 +60,7 @@ import {
60
60
  setExtensionEnabledInSettings,
61
61
  shouldPreserveExecutionStateForUserText,
62
62
  supersedeExecutionState
63
- } from "./chunks/chunk-JXY3YLDS.js";
63
+ } from "./chunks/chunk-REP6QANY.js";
64
64
  import "./chunks/chunk-ZKHMNEXL.js";
65
65
  import {
66
66
  Deref,
@@ -55209,11 +55209,44 @@ function canonicalSessionDir(sessionsDir, sessionId) {
55209
55209
  function publishJournalPath(sessionsDir, safeTargetId) {
55210
55210
  return path7.join(sessionsDir, PUBLISH_JOURNAL_DIR, `${safeTargetId}.json`);
55211
55211
  }
55212
+ var FSYNC_UNSUPPORTED_CODES = /* @__PURE__ */ new Set(["EPERM", "EINVAL", "ENOTSUP", "EOPNOTSUPP", "EACCES", "EBADF"]);
55213
+ function fsyncDirectory(dirPath) {
55214
+ try {
55215
+ const fd = fs7.openSync(dirPath, "r");
55216
+ try {
55217
+ fs7.fsyncSync(fd);
55218
+ } finally {
55219
+ fs7.closeSync(fd);
55220
+ }
55221
+ } catch (error) {
55222
+ const code = error?.code;
55223
+ if (code !== void 0 && FSYNC_UNSUPPORTED_CODES.has(code)) return;
55224
+ throw error;
55225
+ }
55226
+ }
55212
55227
  function writePublishJournal(journalPath, record) {
55213
- fs7.mkdirSync(path7.dirname(journalPath), { recursive: true });
55228
+ const journalDir = path7.dirname(journalPath);
55229
+ fs7.mkdirSync(journalDir, { recursive: true });
55214
55230
  const tempPath = `${journalPath}.tmp`;
55215
55231
  fs7.writeFileSync(tempPath, JSON.stringify(record, void 0, " "));
55232
+ const tempFd = fs7.openSync(tempPath, "r+");
55233
+ try {
55234
+ fs7.fsyncSync(tempFd);
55235
+ } finally {
55236
+ fs7.closeSync(tempFd);
55237
+ }
55216
55238
  fs7.renameSync(tempPath, journalPath);
55239
+ fsyncDirectory(journalDir);
55240
+ fsyncDirectory(path7.dirname(journalDir));
55241
+ }
55242
+ function markPublishJournalFailed(journalPath) {
55243
+ if (!journalPath) return;
55244
+ try {
55245
+ const record = JSON.parse(fs7.readFileSync(journalPath, "utf8"));
55246
+ if (record?.v !== 1 || typeof record.failedAt === "number") return;
55247
+ writePublishJournal(journalPath, { ...record, failedAt: Date.now() });
55248
+ } catch {
55249
+ }
55217
55250
  }
55218
55251
  function clearPublishJournal(journalPath) {
55219
55252
  if (!journalPath) return;
@@ -55266,6 +55299,7 @@ async function rewindStoredSession(params) {
55266
55299
  }
55267
55300
  async function forkStoredSession(params) {
55268
55301
  let stagingId;
55302
+ let preserveStagingForRecovery = false;
55269
55303
  try {
55270
55304
  const sanitizedTargetId = params.targetSessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
55271
55305
  if (sanitizedTargetId.startsWith(STAGING_SESSION_ID_PREFIX)) {
@@ -55396,6 +55430,7 @@ async function forkStoredSession(params) {
55396
55430
  if (hadPreviousTarget) {
55397
55431
  try {
55398
55432
  params.store.moveSession(params.targetSessionId, backupId);
55433
+ if (params.sessionsDir) fsyncDirectory(params.sessionsDir);
55399
55434
  } catch (error) {
55400
55435
  clearPublishJournal(journalPath);
55401
55436
  throw error;
@@ -55403,6 +55438,7 @@ async function forkStoredSession(params) {
55403
55438
  }
55404
55439
  try {
55405
55440
  params.store.moveSession(stagingId, params.targetSessionId);
55441
+ if (params.sessionsDir) fsyncDirectory(params.sessionsDir);
55406
55442
  } catch (error) {
55407
55443
  if (hadPreviousTarget) {
55408
55444
  try {
@@ -55410,8 +55446,11 @@ async function forkStoredSession(params) {
55410
55446
  throw new Error("target path is occupied");
55411
55447
  }
55412
55448
  params.store.moveSession(backupId, params.targetSessionId);
55449
+ if (params.sessionsDir) fsyncDirectory(params.sessionsDir);
55413
55450
  clearPublishJournal(journalPath);
55414
55451
  } catch (restoreError) {
55452
+ markPublishJournalFailed(journalPath);
55453
+ preserveStagingForRecovery = true;
55415
55454
  throw new SessionControlError(
55416
55455
  "session_io_error",
55417
55456
  `Fork publication failed; previous session is preserved as ${backupId}. Restore failed: ${String(restoreError)}`,
@@ -55446,7 +55485,7 @@ async function forkStoredSession(params) {
55446
55485
  } catch (error) {
55447
55486
  throw toIoError(error);
55448
55487
  } finally {
55449
- if (stagingId) {
55488
+ if (stagingId && !preserveStagingForRecovery) {
55450
55489
  try {
55451
55490
  params.store.delete(stagingId);
55452
55491
  } catch {
@@ -55519,7 +55558,7 @@ function recoverOnePublishJournal(params) {
55519
55558
  try {
55520
55559
  const parsed = JSON.parse(fs7.readFileSync(params.journalPath, "utf8"));
55521
55560
  const safeId = /^[a-zA-Z0-9_-]+$/;
55522
- if (parsed?.v !== 1 || !safeId.test(parsed.targetId ?? "") || !safeId.test(parsed.stagingId ?? "") || !((parsed.backupDir ?? null) === null || safeId.test(parsed.backupDir))) {
55561
+ if (parsed?.v !== 1 || !safeId.test(parsed.targetId ?? "") || !safeId.test(parsed.stagingId ?? "") || !((parsed.backupDir ?? null) === null || safeId.test(parsed.backupDir)) || !((parsed.failedAt ?? void 0) === void 0 || typeof parsed.failedAt === "number")) {
55523
55562
  throw new Error("journal record has unexpected shape");
55524
55563
  }
55525
55564
  record = parsed;
@@ -55545,7 +55584,7 @@ function recoverOnePublishJournal(params) {
55545
55584
  const backupExists = record.backupDir ? dirExists(record.backupDir) : false;
55546
55585
  const stagingExists = dirExists(record.stagingId);
55547
55586
  try {
55548
- if (targetExists && !stagingExists) {
55587
+ if (targetExists && !stagingExists && record.failedAt === void 0) {
55549
55588
  if (backupExists && record.backupDir) removeSessionDir(record.backupDir);
55550
55589
  removeJournal();
55551
55590
  return { action: "publish-completed", targetId: record.targetId };
@@ -55555,22 +55594,32 @@ function recoverOnePublishJournal(params) {
55555
55594
  path7.join(params.sessionsDir, record.backupDir),
55556
55595
  path7.join(params.sessionsDir, record.targetId)
55557
55596
  );
55558
- removeJournal();
55597
+ fsyncDirectory(params.sessionsDir);
55559
55598
  if (stagingExists) {
55560
55599
  try {
55561
55600
  removeSessionDir(record.stagingId);
55562
55601
  } catch (error) {
55563
55602
  warn(`[session-control] Restored ${record.targetId} but could not remove its abandoned staging copy ${record.stagingId}: ${String(error)}`);
55603
+ return { action: "ambiguous-preserved", targetId: record.targetId, message: `Staging cleanup failed after restore; journal preserved: ${String(error)}` };
55564
55604
  }
55565
55605
  }
55606
+ removeJournal();
55566
55607
  return { action: "restored-previous-target", targetId: record.targetId };
55567
55608
  }
55568
55609
  if (!targetExists && !backupExists) {
55569
- if (stagingExists) removeSessionDir(record.stagingId);
55610
+ if (stagingExists) {
55611
+ try {
55612
+ removeSessionDir(record.stagingId);
55613
+ } catch (error) {
55614
+ warn(`[session-control] Could not remove abandoned staging copy ${record.stagingId}: ${String(error)}`);
55615
+ return { action: "ambiguous-preserved", targetId: record.targetId, message: `Staging cleanup failed; journal preserved: ${String(error)}` };
55616
+ }
55617
+ }
55570
55618
  removeJournal();
55571
55619
  return stagingExists ? { action: "discarded-incomplete-fork", targetId: record.targetId } : { action: "nothing-to-recover", targetId: record.targetId };
55572
55620
  }
55573
- const message = `[session-control] Ambiguous interrupted publish for target ${record.targetId} (target=${targetExists}, backup=${backupExists}, staging=${stagingExists}); nothing was deleted \u2014 inspect ${params.sessionsDir} and ${journalName} manually`;
55621
+ const reason = record.failedAt !== void 0 ? "the publish failed in-process (restore failed) and the backup was preserved" : `target=${targetExists}, backup=${backupExists}, staging=${stagingExists}`;
55622
+ const message = `[session-control] Ambiguous interrupted publish for target ${record.targetId} (${reason}); nothing was deleted \u2014 inspect ${params.sessionsDir} and ${journalName} manually`;
55574
55623
  warn(message);
55575
55624
  return { action: "ambiguous-preserved", targetId: record.targetId, message };
55576
55625
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cydm/pie",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
4
4
  "description": "Pie AI Agent CLI",
5
5
  "type": "module",
6
6
  "bin": {