@schoolai/shipyard 3.3.1-rc.20260428.2 → 3.3.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.
package/dist/index.js CHANGED
@@ -111,7 +111,7 @@ async function handleSubcommand() {
111
111
  return true;
112
112
  }
113
113
  if (subcommand === "start") {
114
- const { startCommand } = await import("./start-3CVST3BN.js");
114
+ const { startCommand } = await import("./start-OWA2D3PI.js");
115
115
  await startCommand();
116
116
  return true;
117
117
  }
@@ -128,7 +128,7 @@ async function main() {
128
128
  const args = parseCliArgs();
129
129
  if (args.serve) {
130
130
  await loadAuthFromConfig(env);
131
- const { serve } = await import("./serve-6UUDZZYJ.js");
131
+ const { serve } = await import("./serve-K7MIPIVY.js");
132
132
  return serve({ isDev: env.SHIPYARD_DEV });
133
133
  }
134
134
  logger.error("Use `shipyard start` to run the daemon. Use --help for usage.");
@@ -43328,15 +43328,23 @@ function buildSetTaskCwdHandler(deps) {
43328
43328
  };
43329
43329
  }
43330
43330
  function buildEnvironmentChangedHandler(deps) {
43331
- const { daemon } = deps;
43331
+ const { daemon, refreshCapabilities } = deps;
43332
43332
  let debounceTimer = null;
43333
43333
  return {
43334
43334
  onEnvironmentChanged: (cwd) => {
43335
+ if (daemon.preWarmManager.cwd === cwd) {
43336
+ if (debounceTimer) {
43337
+ clearTimeout(debounceTimer);
43338
+ debounceTimer = null;
43339
+ }
43340
+ return;
43341
+ }
43335
43342
  daemon.taskManager.setWorkspaceRoot(cwd);
43336
43343
  if (debounceTimer) clearTimeout(debounceTimer);
43337
43344
  debounceTimer = setTimeout(() => {
43338
43345
  daemon.preWarmManager.kill();
43339
43346
  daemon.preWarmManager.start(cwd);
43347
+ refreshCapabilities();
43340
43348
  }, 200);
43341
43349
  }
43342
43350
  };
@@ -45766,26 +45774,30 @@ function wireControlChannel(rawChannel, daemon, logAdapter, deps) {
45766
45774
  const resourceUnsubs = /* @__PURE__ */ new Map();
45767
45775
  const commentCrud = wireCommentCrud(daemon, () => controlHandler, logAdapter);
45768
45776
  const todoHandlers = buildTodoHandlers(daemon, logAdapter);
45777
+ let capRefreshGeneration = 0;
45778
+ const refreshCapabilitiesAndPush = (failureEvent) => {
45779
+ const gen = ++capRefreshGeneration;
45780
+ daemon.userSettingsStore.getSettings().then(
45781
+ (userSettings) => detectCapabilities(
45782
+ deps?.tokenStore,
45783
+ userSettings.preferredAnthropicAuth ?? void 0,
45784
+ daemon.capabilities?.anthropicAuth
45785
+ )
45786
+ ).then((fresh) => {
45787
+ if (gen !== capRefreshGeneration) return;
45788
+ daemon.capabilities = fresh;
45789
+ sendCapabilities(controlHandler);
45790
+ }).catch((err) => {
45791
+ logAdapter({
45792
+ event: failureEvent,
45793
+ error: err instanceof Error ? err.message : String(err)
45794
+ });
45795
+ });
45796
+ };
45769
45797
  const worktreeRunner = new WorktreeMachineRunner({
45770
45798
  io: defaultWorktreeIO,
45771
45799
  sendControl: (msg) => controlHandler.sendControl(msg),
45772
- refreshCapabilities: () => {
45773
- daemon.userSettingsStore.getSettings().then(
45774
- (userSettings) => detectCapabilities(
45775
- deps?.tokenStore,
45776
- userSettings.preferredAnthropicAuth ?? void 0,
45777
- daemon.capabilities?.anthropicAuth
45778
- )
45779
- ).then((fresh) => {
45780
- daemon.capabilities = fresh;
45781
- sendCapabilities(controlHandler);
45782
- }).catch((err) => {
45783
- logAdapter({
45784
- event: "capability_refresh_after_worktree_failed",
45785
- error: err instanceof Error ? err.message : String(err)
45786
- });
45787
- });
45788
- },
45800
+ refreshCapabilities: () => refreshCapabilitiesAndPush("capability_refresh_after_worktree_failed"),
45789
45801
  log: logAdapter
45790
45802
  });
45791
45803
  const lazyInfraDeps = {
@@ -45797,6 +45809,7 @@ function wireControlChannel(rawChannel, daemon, logAdapter, deps) {
45797
45809
  tokenStore: deps?.tokenStore,
45798
45810
  logAdapter,
45799
45811
  sendCapabilities,
45812
+ refreshCapabilities: () => refreshCapabilitiesAndPush("capability_refresh_after_env_changed_failed"),
45800
45813
  worktreeRunner
45801
45814
  };
45802
45815
  const worktreeHandlers = buildWorktreeHandlers(lazyInfraDeps);
@@ -48200,9 +48213,10 @@ function handleFileIOChannel(initialCwd, send, log, deps) {
48200
48213
  respondError(requestId, "Path is not a directory");
48201
48214
  return;
48202
48215
  }
48216
+ respond({ type: "set_cwd_ack", requestId, canonical });
48217
+ if (canonical === cwd) return;
48203
48218
  cwd = canonical;
48204
48219
  log({ event: "file_io_cwd_changed", cwd: canonical });
48205
- respond({ type: "set_cwd_ack", requestId, canonical });
48206
48220
  for (const listener of cwdChangeListeners) {
48207
48221
  listener(canonical);
48208
48222
  }
@@ -49076,7 +49090,7 @@ function createPtyManager() {
49076
49090
  }
49077
49091
  log.info({ shell, cwd: options.cwd, cols, rows }, "Spawning PTY");
49078
49092
  try {
49079
- process2 = pty.spawn(shell, ["--login"], {
49093
+ process2 = pty.spawn(shell, [], {
49080
49094
  name: "xterm-256color",
49081
49095
  cols,
49082
49096
  rows,
@@ -49196,10 +49210,8 @@ function createPtyManager() {
49196
49210
 
49197
49211
  // src/services/terminal-handler.ts
49198
49212
  var MAX_PTYS_DEFAULT = 20;
49199
- var CWD_TIMEOUT_MS = 5e3;
49200
49213
  function handleTerminalChannel(taskId, terminalId, cwd, send, log, deps) {
49201
49214
  const maxPtys = deps.maxPtys ?? MAX_PTYS_DEFAULT;
49202
- let cwdTimer = null;
49203
49215
  let disposed = false;
49204
49216
  function sendControl(msg) {
49205
49217
  if (disposed) return;
@@ -49277,6 +49289,7 @@ function handleTerminalChannel(taskId, terminalId, cwd, send, log, deps) {
49277
49289
  });
49278
49290
  pty2.setExitSink((exitCode3) => {
49279
49291
  sendControl({ type: "exited", exitCode: exitCode3 });
49292
+ log({ event: "terminal_exited", taskId, terminalId, exitCode: exitCode3 });
49280
49293
  });
49281
49294
  log({ event: "terminal_reattached", taskId, terminalId, pid: pty2.pid });
49282
49295
  }
@@ -49292,25 +49305,7 @@ function handleTerminalChannel(taskId, terminalId, cwd, send, log, deps) {
49292
49305
  }
49293
49306
  return spawnPty(spawnCwd);
49294
49307
  }
49295
- let ptyRef = null;
49296
- let cwdReceived = false;
49297
- function cancelCwdTimer() {
49298
- if (cwdTimer) {
49299
- clearTimeout(cwdTimer);
49300
- cwdTimer = null;
49301
- }
49302
- }
49303
- function forceSpawn() {
49304
- cwdReceived = true;
49305
- cancelCwdTimer();
49306
- ptyRef = ensurePty(cwd);
49307
- }
49308
- cwdTimer = setTimeout(() => {
49309
- cwdTimer = null;
49310
- if (!cwdReceived && !disposed) {
49311
- ptyRef = ensurePty(cwd);
49312
- }
49313
- }, CWD_TIMEOUT_MS);
49308
+ let ptyRef = ensurePty(cwd);
49314
49309
  function handleControl(payload) {
49315
49310
  const parsed = TerminalControlMessageSchema.safeParse(JSON.parse(payload));
49316
49311
  if (!parsed.success) {
@@ -49320,8 +49315,6 @@ function handleTerminalChannel(taskId, terminalId, cwd, send, log, deps) {
49320
49315
  const msg = parsed.data;
49321
49316
  switch (msg.type) {
49322
49317
  case "cwd":
49323
- cwdReceived = true;
49324
- cancelCwdTimer();
49325
49318
  if (!ptyRef) ptyRef = ensurePty(msg.path);
49326
49319
  return;
49327
49320
  case "resize":
@@ -49344,15 +49337,11 @@ function handleTerminalChannel(taskId, terminalId, cwd, send, log, deps) {
49344
49337
  }
49345
49338
  return;
49346
49339
  }
49347
- if (!ptyRef) forceSpawn();
49340
+ if (!ptyRef) ptyRef = ensurePty(cwd);
49348
49341
  if (ptyRef?.alive) ptyRef.write(data);
49349
49342
  }
49350
49343
  function dispose() {
49351
49344
  disposed = true;
49352
- if (cwdTimer) {
49353
- clearTimeout(cwdTimer);
49354
- cwdTimer = null;
49355
- }
49356
49345
  if (ptyRef) {
49357
49346
  ptyRef.setDataSink(null);
49358
49347
  ptyRef.setExitSink(null);
@@ -100457,6 +100446,26 @@ function buildJsonlConversationStore(dataDir) {
100457
100446
  });
100458
100447
  return [...fresh];
100459
100448
  }
100449
+ async function refreshCacheAfterAppend(channelId, appended) {
100450
+ const hit = readCache.get(channelId);
100451
+ if (!hit) return;
100452
+ try {
100453
+ const s2 = await stat13(channelPath(channelId));
100454
+ hit.messages.push(appended);
100455
+ hit.size = s2.size;
100456
+ hit.mtimeMs = s2.mtimeMs;
100457
+ } catch (err) {
100458
+ if (isEnoent(err)) {
100459
+ readCache.delete(channelId);
100460
+ return;
100461
+ }
100462
+ logPerf({
100463
+ event: "perf_jsonl_cache_refresh_stat_failed",
100464
+ channelId,
100465
+ error: err instanceof Error ? err.message : String(err)
100466
+ });
100467
+ }
100468
+ }
100460
100469
  function contentFingerprint(message) {
100461
100470
  const texts = message.content.filter((b2) => b2.type === "text").map((b2) => b2.type === "text" ? b2.text : "").join("\0");
100462
100471
  return `${message.participantId}${texts}`;
@@ -100594,7 +100603,7 @@ function buildJsonlConversationStore(dataDir) {
100594
100603
  const prev = channelQueues.get(message.channelId) ?? Promise.resolve();
100595
100604
  const next = prev.then(async () => {
100596
100605
  await ensureDir();
100597
- const existing = await readLines(message.channelId);
100606
+ const existing = await readLinesCached(message.channelId);
100598
100607
  const currentSeq = seqCounters.get(message.channelId);
100599
100608
  const decision = classifyDedupResult(
100600
100609
  existing,
@@ -100616,7 +100625,11 @@ function buildJsonlConversationStore(dataDir) {
100616
100625
  const line = JSON.stringify(rest);
100617
100626
  await appendFile2(channelPath(message.channelId), `${line}
100618
100627
  `, "utf-8");
100619
- readCache.delete(message.channelId);
100628
+ await refreshCacheAfterAppend(message.channelId, {
100629
+ ...rest,
100630
+ channelId: message.channelId,
100631
+ seqNo: decision.seqNo
100632
+ });
100620
100633
  resolveResult({ seqNo: decision.seqNo, isDuplicate: false, dedupKey: null });
100621
100634
  }).catch((err) => {
100622
100635
  readCache.delete(message.channelId);
@@ -103654,6 +103667,13 @@ var Thread = class {
103654
103667
  #sendControlMessage;
103655
103668
  #streamDeltaSinks = /* @__PURE__ */ new Set();
103656
103669
  #latestSettings = {};
103670
+ /**
103671
+ * Last values pushed to the live subprocess. Reset to undefined whenever
103672
+ * a fresh subprocess is attached (settings get applied via spawn options
103673
+ * instead). Lets `#applySettingsToSubprocess` skip per-flush IPC roundtrips
103674
+ * when nothing has changed since the previous flush.
103675
+ */
103676
+ #appliedToSubprocess = {};
103657
103677
  #agentParticipantId = PENDING_AGENT_PARTICIPANT_ID;
103658
103678
  #ownSessionId = null;
103659
103679
  /** Fork retry state */
@@ -103720,6 +103740,7 @@ var Thread = class {
103720
103740
  if (config2.onCwdChanged) {
103721
103741
  this.#subprocess.setOnCwdChanged(config2.onCwdChanged);
103722
103742
  }
103743
+ this.#seedAppliedFromSpawnSettings();
103723
103744
  }
103724
103745
  const warmState = "warm_idle";
103725
103746
  const coldState = "cold_idle";
@@ -103954,6 +103975,7 @@ var Thread = class {
103954
103975
  this.#subprocess?.setOnCwdChanged(null);
103955
103976
  this.#subprocess?.close();
103956
103977
  this.#subprocess = null;
103978
+ this.#appliedToSubprocess = {};
103957
103979
  }
103958
103980
  setControlChannelSend(send) {
103959
103981
  this.#sendControlMessage = send;
@@ -104138,6 +104160,7 @@ ${conversationReplay}` : conversationReplay;
104138
104160
  this.#config.mode,
104139
104161
  this.#config.onCwdChanged
104140
104162
  );
104163
+ this.#seedAppliedFromSpawnSettings();
104141
104164
  };
104142
104165
  doSpawn().catch((err) => {
104143
104166
  const msg = err instanceof Error ? err.message : String(err);
@@ -104149,6 +104172,20 @@ ${conversationReplay}` : conversationReplay;
104149
104172
  this.#manager.notifySpawnFailed(msg, this.#lastSpawnInitialContent ?? void 0);
104150
104173
  });
104151
104174
  }
104175
+ /**
104176
+ * Subprocess spawn applies the latest settings via spawn options. Mirror
104177
+ * those into `#appliedToSubprocess` so the first flush after spawn doesn't
104178
+ * re-issue equivalent IPC roundtrips.
104179
+ */
104180
+ #seedAppliedFromSpawnSettings() {
104181
+ const s2 = this.#latestSettings;
104182
+ this.#appliedToSubprocess = {
104183
+ permissionMode: s2.permissionMode,
104184
+ model: s2.model,
104185
+ reasoningEffort: s2.reasoningEffort,
104186
+ fastMode: s2.fastMode
104187
+ };
104188
+ }
104152
104189
  /**
104153
104190
  * Flush #latestSettings to the running subprocess after spawn. Each setting
104154
104191
  * is awaited independently and errors are logged but don't abort the flush —
@@ -104163,23 +104200,28 @@ ${conversationReplay}` : conversationReplay;
104163
104200
  if (!this.#subprocess) return;
104164
104201
  const subprocess = this.#subprocess;
104165
104202
  const settings = this.#latestSettings;
104203
+ const applied = this.#appliedToSubprocess;
104166
104204
  const permissionMode = settings.permissionMode;
104167
- if (permissionMode !== void 0) {
104205
+ if (permissionMode !== void 0 && permissionMode !== applied.permissionMode) {
104168
104206
  await this.#tryFlush("permission_mode", () => subprocess.setPermissionMode(permissionMode));
104207
+ applied.permissionMode = permissionMode;
104169
104208
  }
104170
104209
  const model = settings.model;
104171
- if (model !== void 0) {
104210
+ if (model !== void 0 && model !== applied.model) {
104172
104211
  await this.#tryFlush("model", () => subprocess.setModel(model));
104212
+ applied.model = model;
104173
104213
  }
104174
- if (settings.reasoningEffort !== void 0) {
104214
+ if (settings.reasoningEffort !== void 0 && settings.reasoningEffort !== applied.reasoningEffort) {
104175
104215
  const patch = translateEffortToFlagSettings(settings.reasoningEffort);
104176
104216
  if (patch !== null) {
104177
104217
  await this.#tryFlush("effort", () => subprocess.applyFlagSettings(patch));
104178
104218
  }
104219
+ applied.reasoningEffort = settings.reasoningEffort;
104179
104220
  }
104180
- if (settings.fastMode !== void 0) {
104221
+ if (settings.fastMode !== void 0 && settings.fastMode !== applied.fastMode) {
104181
104222
  const fastMode = settings.fastMode;
104182
104223
  await this.#tryFlush("fast_mode", () => subprocess.applyFlagSettings({ fastMode }));
104224
+ applied.fastMode = fastMode;
104183
104225
  }
104184
104226
  }
104185
104227
  async #tryFlush(kind, fn) {
@@ -104235,10 +104277,12 @@ ${conversationReplay}` : conversationReplay;
104235
104277
  this.#subprocess?.setOnCwdChanged(null);
104236
104278
  this.#subprocess?.close();
104237
104279
  this.#subprocess = null;
104280
+ this.#appliedToSubprocess = {};
104238
104281
  }
104239
104282
  #handleForceKill() {
104240
104283
  this.#subprocess?.forceKill();
104241
104284
  this.#subprocess = null;
104285
+ this.#appliedToSubprocess = {};
104242
104286
  }
104243
104287
  /** ------------------------------------------------------------------ */
104244
104288
  /** SDK error retry logic */
@@ -117850,4 +117894,4 @@ export {
117850
117894
  decideWorkspaceScope,
117851
117895
  serve
117852
117896
  };
117853
- //# sourceMappingURL=serve-6UUDZZYJ.js.map
117897
+ //# sourceMappingURL=serve-K7MIPIVY.js.map