svamp-cli 0.2.306 → 0.2.308

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 (28) hide show
  1. package/dist/{adminCommands-Cx_Rp-TR.mjs → adminCommands-CtecEU3a.mjs} +1 -1
  2. package/dist/{agentCommands-CUcyA_KE.mjs → agentCommands-DEJpgdgc.mjs} +5 -5
  3. package/dist/{auth-BBjbREk1.mjs → auth-BTodo5Et.mjs} +1 -1
  4. package/dist/{cli-Jj5S5UOL.mjs → cli-Ko4XpjRo.mjs} +76 -70
  5. package/dist/cli.mjs +2 -2
  6. package/dist/{commands-BRQBNTsJ.mjs → commands-B-u0fICz.mjs} +2 -2
  7. package/dist/{commands-DkKdjnPE.mjs → commands-B2ARVwag.mjs} +2 -2
  8. package/dist/{commands-CvwLQ3MM.mjs → commands-D3Nu5cYE.mjs} +1 -1
  9. package/dist/{commands-DlyOpWwF.mjs → commands-DA7rAZqn.mjs} +1 -1
  10. package/dist/{commands-3UnC4Lnb.mjs → commands-DKOYCcmo.mjs} +1 -1
  11. package/dist/{commands-BeD9b-Eh.mjs → commands-DfbOYRLs.mjs} +2 -2
  12. package/dist/{commands-DJt9EFd7.mjs → commands-GNplzSQX.mjs} +2 -2
  13. package/dist/{commands-ByP8QKuO.mjs → commands-dBiQcDJ1.mjs} +7 -7
  14. package/dist/{fleet-cBng1VMn.mjs → fleet-CXixARxe.mjs} +38 -2
  15. package/dist/{frpc-C1jS23Sn.mjs → frpc-DV_IIYpM.mjs} +1 -1
  16. package/dist/{headlessCli-CsnDECCN.mjs → headlessCli-DfkgeGnQ.mjs} +2 -2
  17. package/dist/index.mjs +1 -1
  18. package/dist/{notifyCommands-D7Ja6442.mjs → notifyCommands-BUE_-OCX.mjs} +1 -1
  19. package/dist/package-GdCOuQCx.mjs +64 -0
  20. package/dist/{rpc-BxmbyDga.mjs → rpc-BjKmyKdA.mjs} +1 -1
  21. package/dist/{rpc-iNafaHoh.mjs → rpc-wL-45Raf.mjs} +1 -1
  22. package/dist/{run-Bd57zohL.mjs → run-VQIHMUdo.mjs} +203 -66
  23. package/dist/{run-D351s_uv.mjs → run-ofbo9uzA.mjs} +1 -1
  24. package/dist/{scheduler-CZYOanXt.mjs → scheduler-Dia9Akaw.mjs} +1 -1
  25. package/dist/{serveCommands-DotLju8m.mjs → serveCommands-DehmO1cO.mjs} +5 -5
  26. package/dist/{sideband-DM1-IAV7.mjs → sideband-CGiNKfNE.mjs} +1 -1
  27. package/package.json +2 -2
  28. package/dist/package-DUF-safU.mjs +0 -64
@@ -3047,7 +3047,7 @@ Connection: close\r
3047
3047
  const mount = this.mounts.get(mountName);
3048
3048
  const subdomainOverride = mount?.access === "link" && mount.linkToken ? /* @__PURE__ */ new Map([[this.port, buildLinkSubdomain(subdomainSafe, mount.linkToken)]]) : void 0;
3049
3049
  try {
3050
- const { FrpcTunnel } = await import('./frpc-C1jS23Sn.mjs');
3050
+ const { FrpcTunnel } = await import('./frpc-DV_IIYpM.mjs');
3051
3051
  let tunnel;
3052
3052
  tunnel = new FrpcTunnel({
3053
3053
  name: tunnelName,
@@ -4857,8 +4857,43 @@ const SESSION_METHOD_MIN_ROLE = {
4857
4857
  refineLastReply: "admin",
4858
4858
  undoLastEdit: "admin"
4859
4859
  };
4860
+ const SESSION_VIEW_METHODS = /* @__PURE__ */ new Set([
4861
+ // read-only (authorizeRequest at view-role)
4862
+ "getMessages",
4863
+ "getLatestMessages",
4864
+ "getMessageCount",
4865
+ "getMetadata",
4866
+ "getSharing",
4867
+ "getInbox",
4868
+ "getEffectiveRole",
4869
+ "getActivityState",
4870
+ "getAgentState",
4871
+ "getChannelSkill",
4872
+ "getMeetingActivity",
4873
+ "listChannels",
4874
+ "outpostList",
4875
+ "registerListener",
4876
+ // self-authenticating (own capability key / token) or liveness — machine role stays view
4877
+ "channelDescribe",
4878
+ "channelExecTool",
4879
+ "channelGetProfile",
4880
+ "channelList",
4881
+ "channelOwns",
4882
+ "channelReceive",
4883
+ "channelSend",
4884
+ "channelUpload",
4885
+ "outpostHello",
4886
+ "outpostPoll",
4887
+ "outpostResult",
4888
+ "disconnect",
4889
+ "reregister",
4890
+ "recordMeetingActivity"
4891
+ ]);
4860
4892
  function sessionMethodMinRole(method) {
4861
- return SESSION_METHOD_MIN_ROLE[method] ?? "view";
4893
+ const mapped = SESSION_METHOD_MIN_ROLE[method];
4894
+ if (mapped) return mapped;
4895
+ if (SESSION_VIEW_METHODS.has(method)) return "view";
4896
+ return "admin";
4862
4897
  }
4863
4898
  function getParamNames(fn) {
4864
4899
  const src = fn.toString();
@@ -6088,7 +6123,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
6088
6123
  const tunnels = handlers.tunnels;
6089
6124
  if (!tunnels) throw new Error("Tunnel management not available");
6090
6125
  if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
6091
- const { FrpcTunnel } = await import('./frpc-C1jS23Sn.mjs');
6126
+ const { FrpcTunnel } = await import('./frpc-DV_IIYpM.mjs');
6092
6127
  const tunnel = new FrpcTunnel({
6093
6128
  name: params.name,
6094
6129
  ports: params.ports,
@@ -6573,7 +6608,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
6573
6608
  let owner;
6574
6609
  try {
6575
6610
  role2 = (await rpc.getEffectiveRole(context))?.role ?? null;
6576
- if (!role2) return { success: false, error: "Not authorized for this session" };
6611
+ if (!roleAtLeast(role2, "interact")) return { success: false, error: "Not authorized: WISE ask requires interact access to this session." };
6577
6612
  const m = await rpc.getMetadata(context);
6578
6613
  cwd = m?.metadata?.path;
6579
6614
  owner = m?.metadata?.sharing?.owner;
@@ -6582,12 +6617,12 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
6582
6617
  }
6583
6618
  const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
6584
6619
  const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
6585
- const { toolsForRole } = await import('./sideband-DM1-IAV7.mjs');
6620
+ const { toolsForRole } = await import('./sideband-CGiNKfNE.mjs');
6586
6621
  const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
6587
6622
  return fmt(r2);
6588
6623
  }
6589
6624
  const role = getEffectiveRole(context, currentMetadata.sharing);
6590
- if (!role) return { success: false, error: "Not authorized on this machine" };
6625
+ if (!roleAtLeast(role, "interact")) return { success: false, error: "Not authorized: WISE ask requires interact access to this machine." };
6591
6626
  const machineDeps = buildMachineDeps(
6592
6627
  {
6593
6628
  getSessionIds: handlers.getSessionIds,
@@ -6687,7 +6722,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
6687
6722
  return { ok: false, call_id: callId, status: "busy", error: "channel is busy (too many concurrent requests) \u2014 retry shortly" };
6688
6723
  }
6689
6724
  const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
6690
- const { queryCore } = await import('./commands-CvwLQ3MM.mjs');
6725
+ const { queryCore } = await import('./commands-D3Nu5cYE.mjs');
6691
6726
  const timeout = c.reply?.timeout_sec || 120;
6692
6727
  let result;
6693
6728
  try {
@@ -7457,6 +7492,7 @@ class OutpostCoordinator {
7457
7492
  clearTimer;
7458
7493
  execTimeoutMs;
7459
7494
  staleMs;
7495
+ randToken;
7460
7496
  sweeper = null;
7461
7497
  constructor(opts = {}) {
7462
7498
  this.now = opts.now || (() => Date.now());
@@ -7464,6 +7500,7 @@ class OutpostCoordinator {
7464
7500
  this.clearTimer = opts.clearTimer || ((t) => clearTimeout(t));
7465
7501
  this.execTimeoutMs = opts.execTimeoutMs ?? OUTPOST_EXEC_TIMEOUT_MS;
7466
7502
  this.staleMs = opts.staleMs ?? OUTPOST_STALE_MS;
7503
+ this.randToken = opts.randToken || (() => randomBytes(9).toString("base64url"));
7467
7504
  }
7468
7505
  isStale(c, at) {
7469
7506
  return at - c.lastSeen > this.staleMs;
@@ -7575,7 +7612,7 @@ class OutpostCoordinator {
7575
7612
  * machine is not connected.
7576
7613
  */
7577
7614
  enqueueExec(machine, cmd, cwd) {
7578
- const id = `x${(++this.seq).toString(36)}_${this.now().toString(36)}`;
7615
+ const id = `x${(++this.seq).toString(36)}_${this.now().toString(36)}_${this.randToken()}`;
7579
7616
  if (!this.isConnected(machine)) {
7580
7617
  return {
7581
7618
  id,
@@ -10470,8 +10507,23 @@ function computeCollectionConfigUpdate(existingConfig, extra) {
10470
10507
  function emailHash(email) {
10471
10508
  return createHash("sha256").update(email.toLowerCase()).digest("hex").slice(0, 12);
10472
10509
  }
10510
+ function sessionIdHash(sessionId) {
10511
+ return createHash("sha256").update(sessionId).digest("hex").slice(0, 12);
10512
+ }
10513
+ function roleLabel(role) {
10514
+ switch (role) {
10515
+ case "admin":
10516
+ return "full access";
10517
+ case "interact":
10518
+ return "can interact";
10519
+ case "view":
10520
+ return "view only";
10521
+ default:
10522
+ return String(role);
10523
+ }
10524
+ }
10473
10525
  function shareAlias(sessionId, recipientEmail) {
10474
- return `share-${sessionId.slice(0, 8)}-${emailHash(recipientEmail)}`;
10526
+ return `share-${sessionIdHash(sessionId)}-${emailHash(recipientEmail)}`;
10475
10527
  }
10476
10528
  function eventAlias(eventId, recipientEmail) {
10477
10529
  return `evt-${emailHash(recipientEmail)}-${eventId.slice(0, 8)}`;
@@ -10479,18 +10531,56 @@ function eventAlias(eventId, recipientEmail) {
10479
10531
  class SharingNotificationSync {
10480
10532
  server;
10481
10533
  artifactManager = null;
10534
+ amResolvedAt = 0;
10482
10535
  shareCollectionId = null;
10483
10536
  eventCollectionId = null;
10484
10537
  initialized = false;
10485
10538
  log;
10539
+ // A resolved `public/artifact-manager` proxy is bound to a SPECIFIC backend client_id. When that
10540
+ // backend pod is replaced (deploy/restart/idle-evict), the new instance registers under the same
10541
+ // public alias but a fresh client_id — the cached proxy dangles and every call TIMES OUT (~30s).
10542
+ // Before this fix that silently broke ALL zero-click share writes fleet-wide (empty
10543
+ // svamp-shared-sessions → recipients never auto-discovered). So we re-resolve on a short TTL and,
10544
+ // on any RPC error, force one fresh re-resolve + retry (a dead pod's proxy always fails first).
10545
+ static AM_TTL_MS = 3e4;
10486
10546
  constructor(server, log) {
10487
10547
  this.server = server;
10488
10548
  this.log = log;
10489
10549
  }
10550
+ /**
10551
+ * Resolve `public/artifact-manager` on the daemon's live connection. hypha-rpc re-queries the
10552
+ * server registry on each getService() call, so a fresh resolve yields the CURRENTLY-registered
10553
+ * live instance (not the dangling cached one). Cached within a short TTL to avoid a lookup per
10554
+ * write; `force` bypasses the cache after a failure.
10555
+ */
10556
+ async getAM(force = false) {
10557
+ const now = Date.now();
10558
+ if (!force && this.artifactManager && now - this.amResolvedAt < SharingNotificationSync.AM_TTL_MS) {
10559
+ return this.artifactManager;
10560
+ }
10561
+ const am = await this.server.getService("public/artifact-manager");
10562
+ this.artifactManager = am;
10563
+ this.amResolvedAt = Date.now();
10564
+ return am;
10565
+ }
10566
+ /**
10567
+ * Run an artifact-manager operation with automatic recovery from a stale/dead proxy: try with the
10568
+ * (possibly cached) instance; on ANY error, force a fresh re-resolve and retry ONCE. This is the
10569
+ * heart of the stale-proxy fix — a replaced backend pod makes the first call time out, the retry
10570
+ * re-binds to the live instance and succeeds. The caller still catches the final error.
10571
+ */
10572
+ async withAM(op) {
10573
+ try {
10574
+ return await op(await this.getAM(false));
10575
+ } catch (err) {
10576
+ this.log(`[SHARING NOTIFY] artifact-manager call failed (${err?.message}); re-resolving + retrying`);
10577
+ return await op(await this.getAM(true));
10578
+ }
10579
+ }
10490
10580
  async init() {
10491
10581
  try {
10492
- this.artifactManager = await this.server.getService("public/artifact-manager");
10493
- if (!this.artifactManager) {
10582
+ const am = await this.getAM(true);
10583
+ if (!am) {
10494
10584
  this.log("[SHARING NOTIFY] Artifact manager not available");
10495
10585
  return;
10496
10586
  }
@@ -10519,15 +10609,17 @@ class SharingNotificationSync {
10519
10609
  shareType: params.shareType || "session"
10520
10610
  };
10521
10611
  try {
10522
- const artifact = await this.artifactManager.create({
10523
- alias,
10524
- parent_id: this.shareCollectionId,
10525
- type: "share-notification",
10526
- stage: true,
10527
- manifest,
10528
- _rkwargs: true
10529
- });
10530
- await this.artifactManager.commit({ artifact_id: artifact.id, _rkwargs: true }).catch(() => {
10612
+ await this.withAM(async (am) => {
10613
+ const artifact = await am.create({
10614
+ alias,
10615
+ parent_id: this.shareCollectionId,
10616
+ type: "share-notification",
10617
+ stage: true,
10618
+ manifest,
10619
+ _rkwargs: true
10620
+ });
10621
+ await am.commit({ artifact_id: artifact.id, _rkwargs: true }).catch(() => {
10622
+ });
10531
10623
  });
10532
10624
  } catch (createErr) {
10533
10625
  this.log(`[SHARING NOTIFY] share-notification attach skipped for ${params.recipientEmail}: ${createErr.message}`);
@@ -10545,8 +10637,10 @@ class SharingNotificationSync {
10545
10637
  userEmail: params.ownerEmail
10546
10638
  },
10547
10639
  title: `${typeLabel} shared with you`,
10548
- // Per-user tiers were retired; explicit users always have full access.
10549
- body: `${params.ownerEmail} shared "${params.label || "Untitled"}" with you (full access)`,
10640
+ // #0689: render the recipient's ACTUAL role (view/interact/admin) authorize.ts genuinely
10641
+ // enforces per-user roles, so the old hardcoded "(full access)" misstated a view/interact
10642
+ // invitee's access.
10643
+ body: `${params.ownerEmail} shared "${params.label || "Untitled"}" with you (${roleLabel(params.role)})`,
10550
10644
  level: "info",
10551
10645
  action: shareType === "session" ? {
10552
10646
  type: "add-session-bookmark",
@@ -10569,14 +10663,16 @@ class SharingNotificationSync {
10569
10663
  if (!this.initialized || !this.shareCollectionId) return;
10570
10664
  const alias = shareAlias(sessionId, recipientEmail);
10571
10665
  try {
10572
- const existing = await this.artifactManager.read({
10573
- artifact_id: alias,
10574
- parent_id: this.shareCollectionId,
10575
- _rkwargs: true
10576
- });
10577
- await this.artifactManager.delete({
10578
- artifact_id: existing.id,
10579
- _rkwargs: true
10666
+ await this.withAM(async (am) => {
10667
+ const existing = await am.read({
10668
+ artifact_id: alias,
10669
+ parent_id: this.shareCollectionId,
10670
+ _rkwargs: true
10671
+ });
10672
+ await am.delete({
10673
+ artifact_id: existing.id,
10674
+ _rkwargs: true
10675
+ });
10580
10676
  });
10581
10677
  this.log(`[SHARING NOTIFY] Removed share notification for ${recipientEmail} on ${sessionId.slice(0, 8)}`);
10582
10678
  } catch {
@@ -10607,17 +10703,19 @@ class SharingNotificationSync {
10607
10703
  if (!this.initialized || !this.eventCollectionId) return;
10608
10704
  const alias = eventAlias(event.id, event.recipientEmail);
10609
10705
  try {
10610
- const artifact = await this.artifactManager.create({
10611
- alias,
10612
- parent_id: this.eventCollectionId,
10613
- type: "user-event",
10614
- stage: true,
10615
- manifest: event,
10616
- _rkwargs: true
10617
- });
10618
- await this.artifactManager.commit({
10619
- artifact_id: artifact.id,
10620
- _rkwargs: true
10706
+ await this.withAM(async (am) => {
10707
+ const artifact = await am.create({
10708
+ alias,
10709
+ parent_id: this.eventCollectionId,
10710
+ type: "user-event",
10711
+ stage: true,
10712
+ manifest: event,
10713
+ _rkwargs: true
10714
+ });
10715
+ await am.commit({
10716
+ artifact_id: artifact.id,
10717
+ _rkwargs: true
10718
+ });
10621
10719
  });
10622
10720
  this.log(`[USER EVENT] Published ${event.type} to ${event.recipientEmail}`);
10623
10721
  try {
@@ -10644,7 +10742,7 @@ class SharingNotificationSync {
10644
10742
  const all = [];
10645
10743
  const seen = /* @__PURE__ */ new Set();
10646
10744
  for (let offset = 0; ; offset += PAGE) {
10647
- const page = await this.artifactManager.list({ parent_id: parentId, offset, limit: PAGE, _rkwargs: true });
10745
+ const page = await this.withAM((am) => am.list({ parent_id: parentId, offset, limit: PAGE, _rkwargs: true }));
10648
10746
  if (!Array.isArray(page) || page.length === 0) break;
10649
10747
  let fresh = 0;
10650
10748
  for (const c of page) {
@@ -10685,7 +10783,7 @@ class SharingNotificationSync {
10685
10783
  live.push(m);
10686
10784
  }
10687
10785
  for (const id of expiredOwnedIds) {
10688
- this.artifactManager.delete({ artifact_id: id, _rkwargs: true }).catch(() => {
10786
+ this.withAM((am) => am.delete({ artifact_id: id, _rkwargs: true })).catch(() => {
10689
10787
  });
10690
10788
  }
10691
10789
  return live.sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));
@@ -10711,7 +10809,7 @@ class SharingNotificationSync {
10711
10809
  return m && typeof m.id === "string" && (m.id === eventId || m.id.slice(0, 8) === short) && typeof m.recipientEmail === "string" && m.recipientEmail.toLowerCase() === normalized;
10712
10810
  });
10713
10811
  if (!match) return false;
10714
- await this.artifactManager.delete({ artifact_id: match.id, _rkwargs: true });
10812
+ await this.withAM((am) => am.delete({ artifact_id: match.id, _rkwargs: true }));
10715
10813
  this.log(`[USER EVENT] Acked ${eventId.slice(0, 8)} for ${recipientEmail}`);
10716
10814
  return true;
10717
10815
  } catch (err) {
@@ -10722,13 +10820,15 @@ class SharingNotificationSync {
10722
10820
  // ── Sharing config diff + sync ───────────────────────────────────
10723
10821
  async syncSharing(sessionId, oldSharing, newSharing, context) {
10724
10822
  if (!this.initialized) return;
10725
- const oldEmails = new Set(
10726
- (oldSharing?.allowedUsers || []).map((u) => u.email.toLowerCase())
10823
+ const oldRoleByEmail = new Map(
10824
+ (oldSharing?.allowedUsers || []).map((u) => [u.email.toLowerCase(), u.role])
10727
10825
  );
10826
+ const oldEmails = new Set(oldRoleByEmail.keys());
10728
10827
  const newUsers = newSharing.allowedUsers || [];
10729
10828
  const newEmails = new Set(newUsers.map((u) => u.email.toLowerCase()));
10730
10829
  for (const user of newUsers) {
10731
- if (!oldEmails.has(user.email.toLowerCase())) {
10830
+ const prevRole = oldRoleByEmail.get(user.email.toLowerCase());
10831
+ if (prevRole === void 0 || prevRole !== user.role) {
10732
10832
  this.notifyShare({
10733
10833
  recipientEmail: user.email,
10734
10834
  sessionId,
@@ -12165,6 +12265,16 @@ function codexCumulativeTokens(payload) {
12165
12265
  return nonCachedInput + output + Math.round(cached * 0.1);
12166
12266
  }
12167
12267
 
12268
+ const PINNED_CODEX_VERSION = "0.146.0";
12269
+ const MIN_CODEX_STEER_VERSION = { major: 0, minor: 129, patch: 0 };
12270
+ function cmpVer(a, b) {
12271
+ return a.major - b.major || a.minor - b.minor || a.patch - b.patch;
12272
+ }
12273
+ function codexSupportsSteer(v) {
12274
+ if (!v) return false;
12275
+ return cmpVer(v, MIN_CODEX_STEER_VERSION) >= 0;
12276
+ }
12277
+
12168
12278
  function parseCodexVersion(v) {
12169
12279
  const m = v.match(/codex-cli\s+(\d+)\.(\d+)\.(\d+)/);
12170
12280
  if (!m) return null;
@@ -12181,6 +12291,16 @@ function codexAppServerAvailable() {
12181
12291
  return null;
12182
12292
  }
12183
12293
  }
12294
+ let _steerSupport = null;
12295
+ function codexSupportsSteerInstalled() {
12296
+ if (_steerSupport !== null) return _steerSupport;
12297
+ try {
12298
+ _steerSupport = codexSupportsSteer(parseCodexVersion(execSync$1("codex --version", { encoding: "utf8" }).trim()));
12299
+ } catch {
12300
+ _steerSupport = false;
12301
+ }
12302
+ return _steerSupport;
12303
+ }
12184
12304
  class CodexAppServerClient {
12185
12305
  constructor(opts) {
12186
12306
  this.opts = opts;
@@ -12337,19 +12457,36 @@ class CodexAppServerClient {
12337
12457
  /**
12338
12458
  * Inject additional input into the CURRENTLY RUNNING turn (Claude-Code-style mid-turn steering).
12339
12459
  *
12340
- * codex app-server FOLDS a second `turn/start` sent mid-turn into the active turn: the new input's
12341
- * items are emitted under the RUNNING turn's id and NO separate `turn/completed` fires for the
12342
- * injected turn (verified empirically probe-codex-midturn). So we send it WITHOUT arming a new
12343
- * `pendingTurn` slot and WITHOUT overwriting `_turnId` (which must keep pointing at the turn to
12344
- * interrupt). The single in-flight completion still resolves the original `sendTurnAndWait`.
12460
+ * Uses codex app-server's NATIVE `turn/steer` the correct primitive for "append this message to
12461
+ * the in-flight turn without interrupting it." The steered input is applied at the next model step
12462
+ * (it does NOT abort a running tool call), the assistant finishes the current work and drains the
12463
+ * steered message as user input before its next LLM call, and NO separate `turn/started` /
12464
+ * `turn/completed` fires — the original single in-flight completion still resolves `sendTurnAndWait`.
12465
+ * So we send it WITHOUT arming a new `pendingTurn` slot and WITHOUT overwriting `_turnId`.
12466
+ *
12467
+ * (The earlier #86 implementation fired a second `turn/start` mid-turn; on codex 0.144 that
12468
+ * ABORTS/REPLACES the running turn instead of folding — the "cuts off the previous task" bug.)
12345
12469
  *
12346
- * Returns false when there is NO active turn to fold into (the caller then falls back to a normal
12347
- * turn via sendPrompt). `pendingTurn` is the in-flight signal.
12470
+ * Returns false when there is no active turn to steer, or when the server REJECTS the steer
12471
+ * (no active turn / expectedTurnId mismatch / non-steerable turn kind such as review or manual
12472
+ * compact). The caller then falls back to client-side queueing (post-turn drain via sendPrompt),
12473
+ * so a rejected steer degrades gracefully rather than losing the message.
12348
12474
  */
12349
- async injectInput(prompt, o) {
12350
- if (!this._threadId || !this.pendingTurn) return false;
12351
- await this.request("turn/start", this.buildTurnParams(prompt, o));
12352
- return true;
12475
+ async injectInput(prompt, _o) {
12476
+ if (!this._threadId || !this.pendingTurn || !this._turnId) return false;
12477
+ if (!codexSupportsSteerInstalled()) return false;
12478
+ const params = {
12479
+ threadId: this._threadId,
12480
+ expectedTurnId: this._turnId,
12481
+ input: [{ type: "text", text: prompt }]
12482
+ };
12483
+ try {
12484
+ await this.request("turn/steer", params);
12485
+ return true;
12486
+ } catch (e) {
12487
+ this.log("[codex-app-server] turn/steer rejected \u2014 falling back to post-turn queue", e);
12488
+ return false;
12489
+ }
12353
12490
  }
12354
12491
  /** Interrupt the in-flight turn (real server-side abort — the old MCP path couldn't do this). */
12355
12492
  async interrupt() {
@@ -17475,7 +17612,7 @@ async function startDaemon(options) {
17475
17612
  try {
17476
17613
  const dir = loadSessionIndex()[sessionId]?.directory;
17477
17614
  if (!dir) return;
17478
- const { reconcileServiceLinks } = await import('./agentCommands-CUcyA_KE.mjs');
17615
+ const { reconcileServiceLinks } = await import('./agentCommands-DEJpgdgc.mjs');
17479
17616
  const configPath = getSvampConfigPath(dir, sessionId);
17480
17617
  const config = readSvampConfig(configPath);
17481
17618
  const entries = Array.from(urls.entries());
@@ -17493,7 +17630,7 @@ async function startDaemon(options) {
17493
17630
  }
17494
17631
  }
17495
17632
  async function createExposedTunnel(spec) {
17496
- const { FrpcTunnel } = await import('./frpc-C1jS23Sn.mjs');
17633
+ const { FrpcTunnel } = await import('./frpc-DV_IIYpM.mjs');
17497
17634
  const tunnel = new FrpcTunnel({
17498
17635
  name: spec.name,
17499
17636
  ports: spec.ports,
@@ -19711,11 +19848,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
19711
19848
  });
19712
19849
  },
19713
19850
  onIssue: async (params) => {
19714
- const { issueRpc } = await import('./rpc-iNafaHoh.mjs');
19851
+ const { issueRpc } = await import('./rpc-wL-45Raf.mjs');
19715
19852
  return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
19716
19853
  },
19717
19854
  onWorkflow: async (params) => {
19718
- const { workflowRpc } = await import('./rpc-BxmbyDga.mjs');
19855
+ const { workflowRpc } = await import('./rpc-BjKmyKdA.mjs');
19719
19856
  return workflowRpc(params?.cwd || directory, params || {});
19720
19857
  },
19721
19858
  onRipgrep: async (args, cwd) => {
@@ -20347,11 +20484,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
20347
20484
  });
20348
20485
  },
20349
20486
  onIssue: async (params) => {
20350
- const { issueRpc } = await import('./rpc-iNafaHoh.mjs');
20487
+ const { issueRpc } = await import('./rpc-wL-45Raf.mjs');
20351
20488
  return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
20352
20489
  },
20353
20490
  onWorkflow: async (params) => {
20354
- const { workflowRpc } = await import('./rpc-BxmbyDga.mjs');
20491
+ const { workflowRpc } = await import('./rpc-BjKmyKdA.mjs');
20355
20492
  return workflowRpc(params?.cwd || directory, params || {});
20356
20493
  },
20357
20494
  onRipgrep: async (args, cwd) => {
@@ -21502,7 +21639,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
21502
21639
  const PING_TIMEOUT_MS = 15e3;
21503
21640
  const POST_RECONNECT_GRACE_MS = 2e4;
21504
21641
  const RECONNECT_JITTER_MS = 2500;
21505
- const { WorkflowScheduler } = await import('./scheduler-CZYOanXt.mjs');
21642
+ const { WorkflowScheduler } = await import('./scheduler-Dia9Akaw.mjs');
21506
21643
  const workflowProjectRoots = () => {
21507
21644
  const dirs = /* @__PURE__ */ new Set();
21508
21645
  for (const s of pidToTrackedSession.values()) {
@@ -22135,4 +22272,4 @@ var run = /*#__PURE__*/Object.freeze({
22135
22272
  writeStopMarker: writeStopMarker
22136
22273
  });
22137
22274
 
22138
- export { downloadSkillFile as $, removeWorkflow as A, validateWorkflowName as B, saveWorkflow as C, rawWorkflow as D, listWorkflows as E, isWorkflowEnabled as F, workflowCrons as G, cronMatches as H, summarize as I, workflowSteps as J, parseJwtEmail as K, computeCollectionConfigUpdate as L, SYSTEM_COLLECTION_CONFIG as M, loadMachineContext as N, buildMachineInstructions as O, machineToolsForRole as P, buildMachineTools as Q, READ_ONLY_TOOLS as R, SharingNotificationSync as S, parseFrontmatter as T, getSkillsServer as U, getSkillsWorkspaceName as V, getSkillsCollectionName as W, fetchWithTimeout as X, searchSkills as Y, SKILLS_DIR as Z, getSkillInfo as _, createSessionStore as a, listSkillFiles as a0, resolveModel as a1, clearStopMarker as a2, stopMarkerExists as a3, formatHandle as a4, normalizeAllowedUser as a5, loadSecurityContextConfig as a6, resolveSecurityContext as a7, buildSecurityContextFromFlags as a8, mergeSecurityContexts as a9, buildSessionShareUrl as aa, computeOutboundHop as ab, registerAwaitingReply as ac, buildMachineShareUrl as ad, parseHandle as ae, handleMatchesMetadata as af, describeMisconfiguration as ag, buildMachineDeps as ah, applyClaudeProxyEnv as ai, composeSessionId as aj, generateFriendlyName as ak, generateHookSettings as al, staticFileServer as am, instanceConfig as an, claudeAuth as ao, codexProvider as ap, projectInfo as aq, DefaultTransport$1 as ar, acpBackend as as, acpAgentConfig as at, codexAppServerBackend as au, GeminiTransport$1 as av, api as aw, run as ax, stopDaemon as b, connectToHypha as c, daemonStatus as d, getFrpsSubdomainHost as e, getFrpsServerPort as f, getHyphaServerUrl$1 as g, getFrpsServerAddr as h, shortId as i, resolveProjectRoot as j, getIssue as k, resumeIssue as l, addComment as m, addIssue as n, listIssues as o, pauseIssue as p, searchIssues as q, registerMachineService as r, startDaemon as s, isVisibleTo as t, updateIssue as u, getRun as v, listRuns as w, getWorkflow as x, runWorkflow as y, setWorkflowEnabled as z };
22275
+ export { getSkillInfo as $, removeWorkflow as A, validateWorkflowName as B, saveWorkflow as C, rawWorkflow as D, listWorkflows as E, isWorkflowEnabled as F, workflowCrons as G, cronMatches as H, summarize as I, workflowSteps as J, parseJwtEmail as K, computeCollectionConfigUpdate as L, SYSTEM_COLLECTION_CONFIG as M, loadMachineContext as N, buildMachineInstructions as O, machineToolsForRole as P, buildMachineTools as Q, READ_ONLY_TOOLS as R, SharingNotificationSync as S, PINNED_CODEX_VERSION as T, parseFrontmatter as U, getSkillsServer as V, getSkillsWorkspaceName as W, getSkillsCollectionName as X, fetchWithTimeout as Y, searchSkills as Z, SKILLS_DIR as _, createSessionStore as a, downloadSkillFile as a0, listSkillFiles as a1, resolveModel as a2, clearStopMarker as a3, stopMarkerExists as a4, formatHandle as a5, normalizeAllowedUser as a6, loadSecurityContextConfig as a7, resolveSecurityContext as a8, buildSecurityContextFromFlags as a9, mergeSecurityContexts as aa, buildSessionShareUrl as ab, computeOutboundHop as ac, registerAwaitingReply as ad, buildMachineShareUrl as ae, parseHandle as af, handleMatchesMetadata as ag, describeMisconfiguration as ah, buildMachineDeps as ai, applyClaudeProxyEnv as aj, composeSessionId as ak, generateFriendlyName as al, generateHookSettings as am, staticFileServer as an, instanceConfig as ao, claudeAuth as ap, codexProvider as aq, projectInfo as ar, DefaultTransport$1 as as, acpBackend as at, acpAgentConfig as au, codexAppServerBackend as av, GeminiTransport$1 as aw, api as ax, run as ay, stopDaemon as b, connectToHypha as c, daemonStatus as d, getFrpsSubdomainHost as e, getFrpsServerPort as f, getHyphaServerUrl$1 as g, getFrpsServerAddr as h, shortId as i, resolveProjectRoot as j, getIssue as k, resumeIssue as l, addComment as m, addIssue as n, listIssues as o, pauseIssue as p, searchIssues as q, registerMachineService as r, startDaemon as s, isVisibleTo as t, updateIssue as u, getRun as v, listRuns as w, getWorkflow as x, runWorkflow as y, setWorkflowEnabled as z };
@@ -1,4 +1,4 @@
1
- import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { ai as applyClaudeProxyEnv, aj as composeSessionId, ak as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, al as generateHookSettings } from './run-Bd57zohL.mjs';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { aj as applyClaudeProxyEnv, ak as composeSessionId, al as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, am as generateHookSettings } from './run-VQIHMUdo.mjs';
2
2
  import os from 'node:os';
3
3
  import { resolve, join } from 'node:path';
4
4
  import { existsSync, readFileSync, watch } from 'node:fs';
@@ -1,4 +1,4 @@
1
- import { j as resolveProjectRoot, E as listWorkflows, F as isWorkflowEnabled, G as workflowCrons, y as runWorkflow, H as cronMatches } from './run-Bd57zohL.mjs';
1
+ import { j as resolveProjectRoot, E as listWorkflows, F as isWorkflowEnabled, G as workflowCrons, y as runWorkflow, H as cronMatches } from './run-VQIHMUdo.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -54,7 +54,7 @@ async function handleServeCommand() {
54
54
  }
55
55
  }
56
56
  async function serveAdd(args, machineId) {
57
- const { connectAndGetMachine } = await import('./commands-CvwLQ3MM.mjs');
57
+ const { connectAndGetMachine } = await import('./commands-D3Nu5cYE.mjs');
58
58
  const pos = positionalArgs(args);
59
59
  const name = pos[0];
60
60
  if (!name) {
@@ -93,7 +93,7 @@ async function serveAdd(args, machineId) {
93
93
  }
94
94
  }
95
95
  async function serveApply(args, machineId) {
96
- const { connectAndGetMachine } = await import('./commands-CvwLQ3MM.mjs');
96
+ const { connectAndGetMachine } = await import('./commands-D3Nu5cYE.mjs');
97
97
  const fs = await import('fs');
98
98
  const yaml = await import('yaml');
99
99
  const file = positionalArgs(args)[0];
@@ -182,7 +182,7 @@ async function serveApply(args, machineId) {
182
182
  }
183
183
  }
184
184
  async function serveRemove(args, machineId) {
185
- const { connectAndGetMachine } = await import('./commands-CvwLQ3MM.mjs');
185
+ const { connectAndGetMachine } = await import('./commands-D3Nu5cYE.mjs');
186
186
  const pos = positionalArgs(args);
187
187
  const name = pos[0];
188
188
  if (!name) {
@@ -202,7 +202,7 @@ async function serveRemove(args, machineId) {
202
202
  }
203
203
  }
204
204
  async function serveList(args, machineId) {
205
- const { connectAndGetMachine } = await import('./commands-CvwLQ3MM.mjs');
205
+ const { connectAndGetMachine } = await import('./commands-D3Nu5cYE.mjs');
206
206
  const all = hasFlag(args, "--all", "-a");
207
207
  const json = hasFlag(args, "--json");
208
208
  const sessionId = getFlag(args, "--session");
@@ -235,7 +235,7 @@ async function serveList(args, machineId) {
235
235
  }
236
236
  }
237
237
  async function serveInfo(machineId) {
238
- const { connectAndGetMachine } = await import('./commands-CvwLQ3MM.mjs');
238
+ const { connectAndGetMachine } = await import('./commands-D3Nu5cYE.mjs');
239
239
  const { machine, server } = await connectAndGetMachine(machineId);
240
240
  try {
241
241
  const info = await machine.serveInfo();
@@ -1,4 +1,4 @@
1
- import { R as READ_ONLY_TOOLS, N as loadMachineContext, O as buildMachineInstructions, P as machineToolsForRole, Q as buildMachineTools } from './run-Bd57zohL.mjs';
1
+ import { R as READ_ONLY_TOOLS, N as loadMachineContext, O as buildMachineInstructions, P as machineToolsForRole, Q as buildMachineTools } from './run-VQIHMUdo.mjs';
2
2
  import 'node:child_process';
3
3
  import 'os';
4
4
  import 'fs/promises';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svamp-cli",
3
- "version": "0.2.306",
3
+ "version": "0.2.308",
4
4
  "description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
5
5
  "author": "Amun AI AB",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -20,7 +20,7 @@
20
20
  "scripts": {
21
21
  "build": "rm -rf dist bin/skills bin/commands && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/crew bin/skills/crew && cp -r ../../commands bin/commands && tsc --noEmit && pkgroll",
22
22
  "typecheck": "tsc --noEmit",
23
- "test": "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-sharing-notify-sync.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-backend-accounts.mjs && npx tsx test/test-codex-force-model.mjs && npx tsx test/test-backend-oauth.mjs && npx tsx test/test-btw-proxy-env.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-user-events.mjs && npx tsx test/test-migrate-children.mjs && npx tsx test/test-outpost-install.mjs && npx tsx test/test-outpost-setup.mjs && npx tsx test/test-outpost-pairing.mjs && npx tsx test/test-outpost-coordinator.mjs && npx tsx test/e2e-outpost-transport.mjs && npx tsx test/test-inbox-reconcile.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-isolation-disable.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-session-access-fallback.mjs && npx tsx test/test-session-rpc-role-map.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-app-server.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-listener-backoff.mjs && npx tsx test/test-session-identity-env.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-compact-detect.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox-store.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-inbox-cross-machine.mjs && npx tsx test/test-issue-store.mjs && npx tsx test/test-issue-store-lock.mjs && npx tsx test/test-issue-close-gate.mjs && npx tsx test/test-loop-verify.mjs && npx tsx test/test-restore-decision.mjs && npx tsx test/test-issue-rpc.mjs && npx tsx test/test-issue-pause.mjs && npx tsx test/test-workflow-store.mjs && npx tsx test/test-workflow-rpc.mjs && npx tsx test/test-workflow-scheduler.mjs && npx tsx test/test-cron.mjs && npx tsx test/test-workflow-runs.mjs && npx tsx test/test-workflow-runs-lock.mjs && npx tsx test/test-workflow-idle.mjs && npx tsx test/test-workflow-reconcile.mjs && npx tsx test/test-serve-link-subdomain.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-ws-upgrade.mjs && npx tsx test/test-serve-auth.mjs && npx tsx test/test-static-file-server.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-hotreload-seam.mjs && npx tsx test/test-hot-reload.mjs && npx tsx test/test-hot-reload-live.mjs && npx tsx test/test-session-core.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-outbox-reload.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-store-lock.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-stateless-dispatch-limiter.mjs && npx tsx test/test-shared-session-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-channel-upload.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs && npx tsx test/test-crew-verdict-routing.mjs && npx tsx test/test-crew-standalone.mjs && npx tsx test/test-session-links.mjs && npx tsx test/test-issue-loop-pause-guard.mjs && npx tsx test/test-artifact-guard.mjs && npx tsx test/test-artifact-sync-pagination.mjs && npx tsx test/test-graceful-restart.mjs && npx tsx test/test-flush-exit.mjs && npx tsx test/test-codex-skills.mjs",
23
+ "test": "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-sharing-notify-sync.mjs && npx tsx test/test-sharing-notify-resolve.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-backend-accounts.mjs && npx tsx test/test-codex-force-model.mjs && npx tsx test/test-backend-oauth.mjs && npx tsx test/test-btw-proxy-env.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-user-events.mjs && npx tsx test/test-migrate-children.mjs && npx tsx test/test-outpost-install.mjs && npx tsx test/test-outpost-setup.mjs && npx tsx test/test-outpost-pairing.mjs && npx tsx test/test-outpost-coordinator.mjs && npx tsx test/e2e-outpost-transport.mjs && npx tsx test/test-inbox-reconcile.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-isolation-disable.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-session-access-fallback.mjs && npx tsx test/test-session-rpc-role-map.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-pinned-codex.mjs && npx tsx test/test-codex-app-server.mjs && npx tsx test/e2e-codex-midturn-steer.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-listener-backoff.mjs && npx tsx test/test-session-identity-env.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-compact-detect.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox-store.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-inbox-cross-machine.mjs && npx tsx test/test-issue-store.mjs && npx tsx test/test-issue-store-lock.mjs && npx tsx test/test-issue-close-gate.mjs && npx tsx test/test-loop-verify.mjs && npx tsx test/test-restore-decision.mjs && npx tsx test/test-issue-rpc.mjs && npx tsx test/test-issue-pause.mjs && npx tsx test/test-workflow-store.mjs && npx tsx test/test-workflow-rpc.mjs && npx tsx test/test-workflow-scheduler.mjs && npx tsx test/test-cron.mjs && npx tsx test/test-workflow-runs.mjs && npx tsx test/test-workflow-runs-lock.mjs && npx tsx test/test-workflow-idle.mjs && npx tsx test/test-workflow-reconcile.mjs && npx tsx test/test-serve-link-subdomain.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-ws-upgrade.mjs && npx tsx test/test-serve-auth.mjs && npx tsx test/test-static-file-server.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-hotreload-seam.mjs && npx tsx test/test-hot-reload.mjs && npx tsx test/test-hot-reload-live.mjs && npx tsx test/test-session-core.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-outbox-reload.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-store-lock.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-stateless-dispatch-limiter.mjs && npx tsx test/test-shared-session-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-channel-upload.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs && npx tsx test/test-crew-verdict-routing.mjs && npx tsx test/test-crew-standalone.mjs && npx tsx test/test-session-links.mjs && npx tsx test/test-issue-loop-pause-guard.mjs && npx tsx test/test-artifact-guard.mjs && npx tsx test/test-artifact-sync-pagination.mjs && npx tsx test/test-graceful-restart.mjs && npx tsx test/test-flush-exit.mjs && npx tsx test/test-codex-skills.mjs",
24
24
  "test:hypha": "node --no-warnings test/test-hypha-service.mjs",
25
25
  "dev": "tsx src/cli.ts",
26
26
  "dev:daemon": "tsx src/cli.ts daemon start-sync",