@schoolai/shipyard 3.35.0-rc.20260928.1 → 3.35.0-rc.20260928.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.
@@ -71,7 +71,7 @@ async function main() {
71
71
  await loadAuthFromConfig(env);
72
72
  crumb("post-auth-load");
73
73
  crumb("pre-serve-import");
74
- const { serve } = await import("./serve-4BO7AOJM.js");
74
+ const { serve } = await import("./serve-FI5KNQA2.js");
75
75
  crumb("post-serve-import");
76
76
  const portQueue = [];
77
77
  let acceptor = null;
package/dist/index.js CHANGED
@@ -137,7 +137,7 @@ async function handleSubcommand() {
137
137
  return true;
138
138
  }
139
139
  if (subcommand === "start") {
140
- const { startCommand } = await import("./start-LRDGHHRC.js");
140
+ const { startCommand } = await import("./start-PVWQLLNL.js");
141
141
  await startCommand();
142
142
  return true;
143
143
  }
@@ -159,7 +159,7 @@ async function main() {
159
159
  const args = parseCliArgs();
160
160
  if (args.serve) {
161
161
  await loadAuthFromConfig(env);
162
- const { serve } = await import("./serve-4BO7AOJM.js");
162
+ const { serve } = await import("./serve-FI5KNQA2.js");
163
163
  return serve({ isDev: env.SHIPYARD_DEV });
164
164
  }
165
165
  logger.error("Use `shipyard start` to run the daemon. Use --help for usage.");
@@ -39455,8 +39455,13 @@ var MessageAdmissionCancelledError = class extends Error {
39455
39455
  }
39456
39456
  };
39457
39457
  var AutomaticTurnRefusedError = class extends MessageAdmissionCancelledError {
39458
- constructor() {
39459
- super("Automatic turn refused: the task was stopped or the daemon is shutting down");
39458
+ /** `stopped`: the task was stopped, paused, completed or archived. `shutdown`: the daemon is going down, so the turn is owed to the next boot. */
39459
+ reason;
39460
+ constructor(reason) {
39461
+ super(
39462
+ `Automatic turn refused: ${reason === "stopped" ? "the task was stopped" : "the daemon is shutting down"}`
39463
+ );
39464
+ this.reason = reason;
39460
39465
  }
39461
39466
  };
39462
39467
  var TaskConversationMutations = class {
@@ -39707,6 +39712,7 @@ async function bootInterruptedTurnResume(deps) {
39707
39712
  resumeBlocker: deps.resumeBlocker,
39708
39713
  sendResume: deps.sendResume,
39709
39714
  automaticEpoch: deps.automaticEpoch,
39715
+ carryToNextBoot: deps.carryToNextBoot,
39710
39716
  queue: deps.queue,
39711
39717
  tracker: deps.tracker
39712
39718
  });
@@ -39773,6 +39779,10 @@ function slotLossReason(reason) {
39773
39779
  return assertNever(reason.loss);
39774
39780
  }
39775
39781
  }
39782
+ function isShutdownRefusal(err3, deps) {
39783
+ if (err3 instanceof AutomaticTurnRefusedError) return err3.reason === "shutdown";
39784
+ return err3 instanceof MessageAdmissionCancelledError && deps.isCancelled();
39785
+ }
39776
39786
  var ResumeLostError = class extends Error {
39777
39787
  reason;
39778
39788
  constructor(reason) {
@@ -39823,26 +39833,31 @@ async function sendOneResume(taskId, attempts, deps, slot) {
39823
39833
  } catch (err3) {
39824
39834
  slot.signal.removeEventListener("abort", onLost);
39825
39835
  if (lostEarly) return false;
39826
- if (sent && err3 instanceof MessageAdmissionCancelledError) {
39827
- deps.tracker.markSendFailed(taskId);
39828
- await attempts.release(taskId);
39829
- return abandon(taskId, "cancelled_before_turn", deps);
39830
- }
39831
- if (sent) {
39832
- deps.tracker.markSendFailed(taskId);
39833
- } else if (err3 instanceof ResumeLostError) {
39834
- await attempts.release(taskId);
39835
- return abandon(taskId, err3.reason, deps);
39836
- } else {
39837
- deps.tracker.markDropped(taskId);
39838
- }
39839
- deps.log({
39840
- event: "interrupted_resume_failed",
39841
- taskId,
39842
- error: errorMessage4(err3)
39843
- });
39844
- return false;
39836
+ return settleFailedSend(taskId, err3, sent, attempts, deps);
39837
+ }
39838
+ }
39839
+ async function settleFailedSend(taskId, err3, sent, attempts, deps) {
39840
+ if (sent && isShutdownRefusal(err3, deps)) {
39841
+ deps.tracker.markSendFailed(taskId);
39842
+ await attempts.release(taskId);
39843
+ deps.carryToNextBoot(taskId);
39844
+ return abandon(taskId, "shutdown", deps);
39845
+ }
39846
+ if (sent && err3 instanceof MessageAdmissionCancelledError) {
39847
+ deps.tracker.markSendFailed(taskId);
39848
+ await attempts.release(taskId);
39849
+ return abandon(taskId, "cancelled_before_turn", deps);
39850
+ }
39851
+ if (sent) {
39852
+ deps.tracker.markSendFailed(taskId);
39853
+ } else if (err3 instanceof ResumeLostError) {
39854
+ await attempts.release(taskId);
39855
+ return abandon(taskId, err3.reason, deps);
39856
+ } else {
39857
+ deps.tracker.markDropped(taskId);
39845
39858
  }
39859
+ deps.log({ event: "interrupted_resume_failed", taskId, error: errorMessage4(err3) });
39860
+ return false;
39846
39861
  }
39847
39862
  function decideResumeExpiry(hold, ceilingConfirmed) {
39848
39863
  switch (hold) {
@@ -39983,14 +39998,15 @@ function resumeBlockerFor(sessionState, taskStatus) {
39983
39998
  }
39984
39999
  function createBootResumeRuntime(deps) {
39985
40000
  const snapshotBoundaryAt = Date.now();
39986
- const tracker = new BootResumeTracker(
39987
- (pending) => writeInterruptedTurnsSnapshot(
40001
+ const tracker = new BootResumeTracker((pending) => {
40002
+ if (disposed) return;
40003
+ writeInterruptedTurnsSnapshot(
39988
40004
  deps.dataDir,
39989
40005
  deps.stopReason() === "quit" ? [] : pending,
39990
40006
  Date.now(),
39991
40007
  deps.log
39992
- )
39993
- );
40008
+ );
40009
+ });
39994
40010
  let pendingAtShutdown = null;
39995
40011
  const watched = /* @__PURE__ */ new Set();
39996
40012
  const queue = new BootSpawnQueue({
@@ -40005,6 +40021,7 @@ function createBootResumeRuntime(deps) {
40005
40021
  let handle = null;
40006
40022
  let disposed = false;
40007
40023
  let provisional = [];
40024
+ let boundary = null;
40008
40025
  const overtake = (taskId, reason) => {
40009
40026
  watched.delete(taskId);
40010
40027
  pendingAtShutdown?.delete(taskId);
@@ -40060,6 +40077,7 @@ function createBootResumeRuntime(deps) {
40060
40077
  recordShutdownInterruptions(tasks) {
40061
40078
  unsubscribe();
40062
40079
  provisional = [];
40080
+ boundary = tasks;
40063
40081
  writeStopList(tasks);
40064
40082
  },
40065
40083
  async schedule(input) {
@@ -40093,6 +40111,11 @@ function createBootResumeRuntime(deps) {
40093
40111
  resumeBlocker,
40094
40112
  /** Restore first, then recheck the slot: a restore that outlived it must not spawn. */
40095
40113
  automaticEpoch: (taskId) => taskManager.automaticEpoch(taskId),
40114
+ carryToNextBoot: (taskId) => {
40115
+ if (!pendingAtShutdown) return;
40116
+ pendingAtShutdown.add(taskId);
40117
+ writeStopList(boundary ?? provisional);
40118
+ },
40096
40119
  sendResume: async (taskId, slot, automaticEpoch) => {
40097
40120
  await taskManager.restoreTask(taskId);
40098
40121
  slot.beginSpawn();
@@ -40113,6 +40136,7 @@ function createBootResumeRuntime(deps) {
40113
40136
  if (disposed) {
40114
40137
  pendingAtShutdown = /* @__PURE__ */ new Set([...pendingAtShutdown ?? [], ...tracker.pendingTaskIds()]);
40115
40138
  scheduled?.cancel();
40139
+ writeStopList(boundary ?? provisional);
40116
40140
  return;
40117
40141
  }
40118
40142
  handle = scheduled;
@@ -67089,7 +67113,7 @@ var AgentWakeupService = class {
67089
67113
  await this.#deps.deliver(record2.taskId, message, ticket);
67090
67114
  } catch (err3) {
67091
67115
  ticket.release();
67092
- if (err3 instanceof MessageAdmissionCancelledError && !this.#disposed) {
67116
+ if (isStopOfTask(err3) && !this.#disposed) {
67093
67117
  await this.#consumeStoppedFire(record2, now);
67094
67118
  return;
67095
67119
  }
@@ -67098,6 +67122,10 @@ var AgentWakeupService = class {
67098
67122
  this.#logDeferralOnce(record2, `boot_queue_slot_${err3.loss}`);
67099
67123
  return;
67100
67124
  }
67125
+ if (err3 instanceof AutomaticTurnRefusedError) {
67126
+ this.#logDeferralOnce(record2, "daemon_shutting_down");
67127
+ return;
67128
+ }
67101
67129
  this.#deps.log({
67102
67130
  event: "agent_wakeup_fire_failed",
67103
67131
  ...logFields(record2),
@@ -67196,6 +67224,10 @@ function matchesSelector(record2, selector) {
67196
67224
  if ("id" in selector) return record2.id === selector.id;
67197
67225
  return record2.key === selector.key;
67198
67226
  }
67227
+ function isStopOfTask(err3) {
67228
+ if (err3 instanceof AutomaticTurnRefusedError) return err3.reason === "stopped";
67229
+ return err3 instanceof MessageAdmissionCancelledError;
67230
+ }
67199
67231
  function deferralKey(record2) {
67200
67232
  return `${record2.id}:${record2.nextFireAt}`;
67201
67233
  }
@@ -112408,10 +112440,21 @@ function trackPlanFileCreation(content, onPlanFile) {
112408
112440
  }
112409
112441
 
112410
112442
  // src/services/task/orchestrator/task-unpushed-selection.ts
112443
+ var AUTOMATIC_MESSAGE_CORRELATION_PREFIX = "automsg_";
112444
+ function isAutomaticContent(content) {
112445
+ const [first] = content;
112446
+ if (content.length !== 1 || first?.type !== "text") return false;
112447
+ return first.text === INTERRUPTED_TURN_RESUME_PROMPT || first.text.startsWith("<system-reminder>");
112448
+ }
112449
+ function dropAutomaticQueueEntries(entries) {
112450
+ return entries.filter((entry) => !isAutomaticContent(entry.content));
112451
+ }
112411
112452
  function isUnconfirmedHuman(message, excludeCorrelationIds) {
112412
112453
  if (message.senderKind !== "human") return false;
112413
112454
  const { correlationId } = message;
112414
112455
  if (!correlationId) return false;
112456
+ if (correlationId.startsWith(AUTOMATIC_MESSAGE_CORRELATION_PREFIX)) return false;
112457
+ if (message.isSynthetic === true || isAutomaticContent(message.content)) return false;
112415
112458
  if (message.sdkUuid) return false;
112416
112459
  return !excludeCorrelationIds?.has(correlationId);
112417
112460
  }
@@ -114199,7 +114242,7 @@ var Task = class {
114199
114242
  const persistence = this.#deps.collabQueuePersistence;
114200
114243
  if (!persistence) return /* @__PURE__ */ new Set();
114201
114244
  const key = mainQueueKey(this.#deps.taskId);
114202
- const entries = await persistence.read(key);
114245
+ const entries = dropAutomaticQueueEntries(await persistence.read(key));
114203
114246
  if (entries.length === 0) return /* @__PURE__ */ new Set();
114204
114247
  await this.#collabQueue.rehydrate(entries);
114205
114248
  const messages = await this.#deps.store.getMessages(this.#deps.channelId);
@@ -117201,7 +117244,8 @@ async function prepareUserMessageForDispatch(deps, task, message) {
117201
117244
  senderDisplayName,
117202
117245
  senderKind,
117203
117246
  correlationId,
117204
- isSynthetic
117247
+ isSynthetic,
117248
+ automatic: message.automaticEpoch !== null
117205
117249
  });
117206
117250
  onPersisted?.();
117207
117251
  let preparedContent = content;
@@ -117250,10 +117294,10 @@ function shouldSuppressDuplicate(taskStateStore, taskId, persisted, redriveDupli
117250
117294
  const spawnRequest = taskStateStore.getTaskSync(taskId)?.spawnRequest;
117251
117295
  return !(spawnRequest?.seedStatus === "pending" && spawnRequest.seedCorrelationId === persisted.correlationId);
117252
117296
  }
117253
- function resolveCorrelationIdForEchoDedup(runtimeId, correlationId) {
117297
+ function resolveCorrelationIdForEchoDedup(runtimeId, correlationId, automatic) {
117254
117298
  if (correlationId !== void 0) return correlationId;
117255
117299
  if (runtimeId === AGENT_SYSTEM_CURSOR) return void 0;
117256
- return `taskmsg_${randomUUID30()}`;
117300
+ return `${automatic ? AUTOMATIC_MESSAGE_CORRELATION_PREFIX : "taskmsg_"}${randomUUID30()}`;
117257
117301
  }
117258
117302
  function requireExistingMessage(args) {
117259
117303
  if (args.existingMessage) return args.existingMessage;
@@ -117263,7 +117307,8 @@ async function persistUserMessageAtChokepoint(args) {
117263
117307
  const effectiveSettings = { ...args.latestSettings, ...args.settings ?? {} };
117264
117308
  const persistedCorrelationId = resolveCorrelationIdForEchoDedup(
117265
117309
  args.runtimeId,
117266
- args.correlationId
117310
+ args.correlationId,
117311
+ args.automatic
117267
117312
  );
117268
117313
  const appendResult = await args.store.appendMessageDeduped(
117269
117314
  {
@@ -117450,6 +117495,7 @@ var TaskManager = class {
117450
117495
  #notifyAgentAction;
117451
117496
  #idleUnload;
117452
117497
  #unsubscribeAnnotations;
117498
+ #unsubscribeTerminalMoves;
117453
117499
  constructor(deps) {
117454
117500
  this.#deps = deps;
117455
117501
  this.#notifyAgentAction = createAgentActionNotifier({
@@ -117472,6 +117518,10 @@ var TaskManager = class {
117472
117518
  ),
117473
117519
  deps.idleUnload
117474
117520
  );
117521
+ this.#unsubscribeTerminalMoves = deps.taskStateStore.subscribe((event) => {
117522
+ const terminal = event.kind !== "set" || event.data.archivedAt !== null || event.data.status === "completed" || event.data.status === "canceled";
117523
+ if (terminal) this.#bumpAutomaticEpoch(event.id);
117524
+ });
117475
117525
  this.#unsubscribeAnnotations = deps.annotationStore.subscribe((event) => {
117476
117526
  if (event.kind !== "set") return;
117477
117527
  if (!this.#idleUnload.hasNewNotifiableComment(event.id, event.data.records)) return;
@@ -118950,8 +119000,12 @@ var TaskManager = class {
118950
119000
  }
118951
119001
  #refuseStaleAutomatic(message) {
118952
119002
  if (message.automaticEpoch === null) return;
118953
- const stale = message.automaticEpoch < this.automaticEpoch(message.taskId);
118954
- if (stale || this.#automaticEpochs.shutDown) throw new AutomaticTurnRefusedError();
119003
+ const record2 = this.#deps.taskStateStore.getTaskSync(message.taskId);
119004
+ const terminal = record2 !== null && (record2.archivedAt != null || record2.status === "completed" || record2.status === "canceled");
119005
+ if (terminal || message.automaticEpoch < this.automaticEpoch(message.taskId)) {
119006
+ throw new AutomaticTurnRefusedError("stopped");
119007
+ }
119008
+ if (this.#automaticEpochs.shutDown) throw new AutomaticTurnRefusedError("shutdown");
118955
119009
  }
118956
119010
  /** Keeps the channel's persisted acknowledgement inside the TaskManager persistence boundary. */
118957
119011
  async handleChannelUserMessage(message) {
@@ -119287,6 +119341,7 @@ var TaskManager = class {
119287
119341
  }
119288
119342
  async disposeAll(opts) {
119289
119343
  this.#unsubscribeAnnotations();
119344
+ this.#unsubscribeTerminalMoves();
119290
119345
  await this.#idleUnload.stop();
119291
119346
  this.#conversationMutations.cancelAllMessages();
119292
119347
  this.#deps.log({ event: "dispose_all_started", taskCount: this.#tasks.size });
@@ -125935,4 +125990,4 @@ export {
125935
125990
  decideWorkspaceScope,
125936
125991
  serve
125937
125992
  };
125938
- //# sourceMappingURL=serve-4BO7AOJM.js.map
125993
+ //# sourceMappingURL=serve-FI5KNQA2.js.map