svamp-cli 0.2.275 → 0.2.277

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 (27) hide show
  1. package/dist/{agentCommands-DGT-1wjo.mjs → agentCommands-DN7hb_oL.mjs} +5 -5
  2. package/dist/{auth-CT55Rse4.mjs → auth-Ci70oWRc.mjs} +1 -1
  3. package/dist/{cli-DYTmO9JL.mjs → cli-Cgzvydbh.mjs} +97 -65
  4. package/dist/cli.mjs +2 -2
  5. package/dist/{commands-D7fIWAgz.mjs → commands-BryGjm3r.mjs} +1 -1
  6. package/dist/{commands-CVPcbtWV.mjs → commands-CT7m_PBV.mjs} +1 -1
  7. package/dist/{commands-C8Zc5axO.mjs → commands-CWVq7FHR.mjs} +7 -7
  8. package/dist/{commands-CXHZapLH.mjs → commands-Cc2BZW1I.mjs} +8 -7
  9. package/dist/{commands-C0rRjhMD.mjs → commands-CgQ3wXOd.mjs} +2 -2
  10. package/dist/{commands-BvmFXAmu.mjs → commands-Dx-uU1hc.mjs} +2 -2
  11. package/dist/{commands-BV_9mFkz.mjs → commands-KWZkSbXH.mjs} +2 -2
  12. package/dist/{fleet-BCel6ZVc.mjs → fleet-w3MI6ljI.mjs} +2 -2
  13. package/dist/{frpc-D3faR6iR.mjs → frpc-DnxvF_Mx.mjs} +7 -5
  14. package/dist/{headlessCli-DVBFm7og.mjs → headlessCli-CqxD6lTs.mjs} +2 -2
  15. package/dist/index.mjs +1 -1
  16. package/dist/notifyCommands-D8tYXxPA.mjs +128 -0
  17. package/dist/package-D5iaKvt5.mjs +64 -0
  18. package/dist/{pinnedClaudeCode-CgEtGkX2.mjs → pinnedClaudeCode-BaMR97BE.mjs} +1 -1
  19. package/dist/{rpc-xlV-4-BG.mjs → rpc-BKDKqllr.mjs} +1 -3
  20. package/dist/{rpc-BQ8zLSUc.mjs → rpc-BvXiNu-z.mjs} +1 -1
  21. package/dist/{run-C3Fcbk-H.mjs → run-CzXwL-ig.mjs} +277 -55
  22. package/dist/{run-DMPVho7i.mjs → run-pAWQPn46.mjs} +4 -3
  23. package/dist/{scheduler-DaFZY2ob.mjs → scheduler-D-25wsin.mjs} +1 -1
  24. package/dist/{serveCommands-B9Yhx1fl.mjs → serveCommands-DIckIiqD.mjs} +5 -5
  25. package/dist/{sideband-CYqmMG_m.mjs → sideband-Bk0GhfIX.mjs} +1 -1
  26. package/package.json +2 -2
  27. package/dist/package-DIuZ7Wfp.mjs +0 -64
@@ -3,7 +3,7 @@ import fs$1, { mkdir as mkdir$1, readdir as readdir$1, readFile, writeFile as wr
3
3
  import * as fs from 'fs';
4
4
  import fs__default, { existsSync as existsSync$1, rmSync, readFileSync as readFileSync$1, mkdirSync, writeFileSync, renameSync as renameSync$1, realpathSync as realpathSync$1, copyFileSync, unlinkSync as unlinkSync$1, readdirSync as readdirSync$1, watch, rmdirSync } from 'fs';
5
5
  import * as path from 'path';
6
- import path__default, { join, resolve as resolve$1, dirname as dirname$1, basename as basename$1 } from 'path';
6
+ import path__default, { join, delimiter, resolve as resolve$1, dirname as dirname$1, basename as basename$1 } from 'path';
7
7
  import { fileURLToPath } from 'url';
8
8
  import { execFile, spawn, execSync, exec as exec$1 } from 'child_process';
9
9
  import * as crypto from 'crypto';
@@ -1506,6 +1506,9 @@ const COOKIE_NAME = "svamp_serve_token";
1506
1506
  const SVAMP_SERVE_TOKEN_PARAM = "__svamp_serve_token";
1507
1507
  const DEFAULT_CACHE_TTL_MS = 5 * 60 * 1e3;
1508
1508
  const DEFAULT_CACHE_MAX_SIZE = 1e3;
1509
+ function jsStringLiteral(value) {
1510
+ return JSON.stringify(value).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
1511
+ }
1509
1512
  class TokenCache {
1510
1513
  cache = /* @__PURE__ */ new Map();
1511
1514
  ttlMs;
@@ -1710,10 +1713,10 @@ a:hover{text-decoration:underline}
1710
1713
  <div class="status" id="status"></div>
1711
1714
  </div>
1712
1715
  <script>
1713
- const hyphaServer = ${JSON.stringify(this.hyphaServerUrl)};
1714
- const redirectUrl = ${JSON.stringify(redirectUrl)};
1715
- const cookieName = ${JSON.stringify(COOKIE_NAME)};
1716
- const tokenParam = ${JSON.stringify(SVAMP_SERVE_TOKEN_PARAM)};
1716
+ const hyphaServer = ${jsStringLiteral(this.hyphaServerUrl)};
1717
+ const redirectUrl = ${jsStringLiteral(redirectUrl)};
1718
+ const cookieName = ${jsStringLiteral(COOKIE_NAME)};
1719
+ const tokenParam = ${jsStringLiteral(SVAMP_SERVE_TOKEN_PARAM)};
1717
1720
 
1718
1721
  const btn = document.getElementById('login-btn');
1719
1722
  const statusEl = document.getElementById('status');
@@ -2210,7 +2213,7 @@ class ServeManager {
2210
2213
  throw new Error(`Path does not exist: ${resolvedDir}`);
2211
2214
  }
2212
2215
  if (this.mounts.has(spec.name)) {
2213
- await this.removeMount(spec.name);
2216
+ await this.removeMount(spec.name, { replacing: true });
2214
2217
  }
2215
2218
  const access = spec.access ?? "link";
2216
2219
  if (access === "owner" && !spec.ownerEmail) {
@@ -2249,7 +2252,7 @@ class ServeManager {
2249
2252
  /**
2250
2253
  * Remove a mount. If no mounts remain, stop Caddy + auth proxy.
2251
2254
  */
2252
- async removeMount(name) {
2255
+ async removeMount(name, opts) {
2253
2256
  if (!this.mounts.has(name)) {
2254
2257
  throw new Error(`Mount '${name}' not found`);
2255
2258
  }
@@ -2269,7 +2272,7 @@ class ServeManager {
2269
2272
  }
2270
2273
  this.persist();
2271
2274
  this.log(`Mount removed: ${name}`);
2272
- if (this.mounts.size === 0) {
2275
+ if (this.mounts.size === 0 && !opts?.replacing) {
2273
2276
  await this.shutdown();
2274
2277
  }
2275
2278
  }
@@ -2708,12 +2711,27 @@ a{color:#0969da;text-decoration:none;font-weight:500}a:hover{text-decoration:und
2708
2711
  if (req.method === "PUT") {
2709
2712
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
2710
2713
  const ws = fs.createWriteStream(filePath);
2714
+ let done = false;
2715
+ const cleanupPartial = () => {
2716
+ if (done) return;
2717
+ done = true;
2718
+ ws.destroy();
2719
+ fs.unlink(filePath, () => {
2720
+ });
2721
+ };
2722
+ req.on("aborted", cleanupPartial);
2723
+ req.on("close", () => {
2724
+ if (!ws.writableFinished) cleanupPartial();
2725
+ });
2711
2726
  req.pipe(ws);
2712
2727
  ws.on("finish", () => {
2728
+ done = true;
2713
2729
  res.writeHead(201);
2714
2730
  res.end();
2715
2731
  });
2716
2732
  ws.on("error", (err) => {
2733
+ if (done) return;
2734
+ done = true;
2717
2735
  res.writeHead(500);
2718
2736
  res.end(err.message);
2719
2737
  });
@@ -2955,7 +2973,7 @@ Connection: close\r
2955
2973
  const mount = this.mounts.get(mountName);
2956
2974
  const subdomainOverride = mount?.access === "link" && mount.linkToken ? /* @__PURE__ */ new Map([[this.port, buildLinkSubdomain(subdomainSafe, mount.linkToken)]]) : void 0;
2957
2975
  try {
2958
- const { FrpcTunnel } = await import('./frpc-D3faR6iR.mjs');
2976
+ const { FrpcTunnel } = await import('./frpc-DnxvF_Mx.mjs');
2959
2977
  let tunnel;
2960
2978
  tunnel = new FrpcTunnel({
2961
2979
  name: tunnelName,
@@ -4350,6 +4368,14 @@ class ChannelOutbox {
4350
4368
  file;
4351
4369
  byChannel = /* @__PURE__ */ new Map();
4352
4370
  seqByChannel = /* @__PURE__ */ new Map();
4371
+ // #0500: per-channel MONOTONIC high-water seq that survives TTL eviction/compaction. seqByChannel
4372
+ // is rebuilt from RETAINED records on every reload, so once a channel's replies all age out (>1h
4373
+ // idle) its seq would reset to 1 — a long-poll receiver holding a pre-eviction cursor N would then
4374
+ // silently drop the new seq=1..N replies (since() filters seq > cursor). highWater is never lowered
4375
+ // and is persisted to a small sidecar (_outbox_seq.json) so seq stays monotonic across eviction AND
4376
+ // daemon restarts. Cross-process safe: re-read + max before each bump (via reload()).
4377
+ highWater = /* @__PURE__ */ new Map();
4378
+ seqFile;
4353
4379
  emitter = new EventEmitter();
4354
4380
  appendsSinceCompact = 0;
4355
4381
  // #0362: change-detection for reload() — a long-poll receiver reuses ONE instance and only
@@ -4360,12 +4386,14 @@ class ChannelOutbox {
4360
4386
  constructor(projectDir) {
4361
4387
  const dir = join$1(projectDir, ".svamp", "channels");
4362
4388
  this.file = join$1(dir, "_outbox.jsonl");
4389
+ this.seqFile = join$1(dir, "_outbox_seq.json");
4363
4390
  this.emitter.setMaxListeners(0);
4364
4391
  try {
4365
4392
  mkdirSync$1(dir, { recursive: true });
4366
4393
  } catch {
4367
4394
  }
4368
4395
  this._load();
4396
+ this._loadHighWater();
4369
4397
  }
4370
4398
  /** Current on-disk (mtimeMs, size), or (0, -1) if the file does not exist. */
4371
4399
  _stat() {
@@ -4388,6 +4416,7 @@ class ChannelOutbox {
4388
4416
  this.byChannel.clear();
4389
4417
  this.seqByChannel.clear();
4390
4418
  this._load();
4419
+ this._loadHighWater();
4391
4420
  return true;
4392
4421
  }
4393
4422
  _load() {
@@ -4410,6 +4439,7 @@ class ChannelOutbox {
4410
4439
  arr.push(reply);
4411
4440
  this.byChannel.set(channelId, arr);
4412
4441
  this.seqByChannel.set(channelId, Math.max(this.seqByChannel.get(channelId) || 0, reply.seq));
4442
+ this.highWater.set(channelId, Math.max(this.highWater.get(channelId) || 0, reply.seq));
4413
4443
  }
4414
4444
  for (const [ch, arr] of this.byChannel) if (arr.length > MAX_PER_CHANNEL) this.byChannel.set(ch, arr.slice(-MAX_PER_CHANNEL));
4415
4445
  } catch {
@@ -4437,6 +4467,30 @@ class ChannelOutbox {
4437
4467
  } catch {
4438
4468
  }
4439
4469
  }
4470
+ /** #0500: merge the durable per-channel high-water sidecar into memory (never lowers a value). */
4471
+ _loadHighWater() {
4472
+ try {
4473
+ if (!existsSync(this.seqFile)) return;
4474
+ const obj = JSON.parse(readFileSync(this.seqFile, "utf8"));
4475
+ for (const [ch, seq] of Object.entries(obj)) {
4476
+ if (typeof seq === "number" && Number.isFinite(seq)) {
4477
+ this.highWater.set(ch, Math.max(this.highWater.get(ch) || 0, seq));
4478
+ }
4479
+ }
4480
+ } catch {
4481
+ }
4482
+ }
4483
+ /** #0500: atomically persist the high-water map so seq survives eviction + restart. Best-effort. */
4484
+ _persistHighWater() {
4485
+ try {
4486
+ const obj = {};
4487
+ for (const [ch, seq] of this.highWater) obj[ch] = seq;
4488
+ const tmp = `${this.seqFile}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
4489
+ writeFileSync$1(tmp, JSON.stringify(obj));
4490
+ renameSync(tmp, this.seqFile);
4491
+ } catch {
4492
+ }
4493
+ }
4440
4494
  _evict(channelId) {
4441
4495
  const cutoff = Date.now() - TTL_MS;
4442
4496
  let arr = this.byChannel.get(channelId);
@@ -4448,8 +4502,10 @@ class ChannelOutbox {
4448
4502
  /** Append a reply addressed to `to`. Assigns seq + ts, persists, and wakes waiters. */
4449
4503
  append(channelId, r) {
4450
4504
  this.reload();
4451
- const seq = (this.seqByChannel.get(channelId) || 0) + 1;
4505
+ const seq = Math.max(this.highWater.get(channelId) || 0, this.seqByChannel.get(channelId) || 0) + 1;
4452
4506
  this.seqByChannel.set(channelId, seq);
4507
+ this.highWater.set(channelId, seq);
4508
+ this._persistHighWater();
4453
4509
  const reply = { seq, ts: Date.now(), to: r.to, body: r.body, ...r.correlationId ? { correlationId: r.correlationId } : {} };
4454
4510
  const arr = this.byChannel.get(channelId) || [];
4455
4511
  arr.push(reply);
@@ -5760,7 +5816,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
5760
5816
  const tunnels = handlers.tunnels;
5761
5817
  if (!tunnels) throw new Error("Tunnel management not available");
5762
5818
  if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
5763
- const { FrpcTunnel } = await import('./frpc-D3faR6iR.mjs');
5819
+ const { FrpcTunnel } = await import('./frpc-DnxvF_Mx.mjs');
5764
5820
  const tunnel = new FrpcTunnel({
5765
5821
  name: params.name,
5766
5822
  ports: params.ports,
@@ -5908,7 +5964,9 @@ async function registerMachineService(server, machineId, metadata, daemonState,
5908
5964
  authorizeRequest(context, currentMetadata.sharing, "view");
5909
5965
  const sm = handlers.serveManager;
5910
5966
  if (!sm) throw new Error("Serve manager not available");
5911
- return sm.getInfo();
5967
+ const info = sm.getInfo();
5968
+ const isAdmin = serveCallerTrusted(context);
5969
+ return { ...info, mounts: (info.mounts || []).map((m) => sanitizeMountForRole(m, isAdmin)) };
5912
5970
  },
5913
5971
  /**
5914
5972
  * Aggregate frpc tunnel health for all serve mounts.
@@ -6234,7 +6292,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
6234
6292
  }
6235
6293
  const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
6236
6294
  const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
6237
- const { toolsForRole } = await import('./sideband-CYqmMG_m.mjs');
6295
+ const { toolsForRole } = await import('./sideband-Bk0GhfIX.mjs');
6238
6296
  const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
6239
6297
  return fmt(r2);
6240
6298
  }
@@ -6333,7 +6391,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
6333
6391
  if (r.error || !r.sender) return { error: r.error || "unauthorized" };
6334
6392
  const callId = "call_" + Math.random().toString(16).slice(2, 12);
6335
6393
  const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
6336
- const { queryCore } = await import('./commands-D7fIWAgz.mjs');
6394
+ const { queryCore } = await import('./commands-BryGjm3r.mjs');
6337
6395
  const timeout = c.reply?.timeout_sec || 120;
6338
6396
  let result;
6339
6397
  try {
@@ -6766,11 +6824,10 @@ function computeRetryDelayMs(attempt, cfg = getRateLimitRetryConfig(), rng = Mat
6766
6824
  }
6767
6825
  function isRetryableRateLimit(text, apiErrorStatus) {
6768
6826
  const t = (text || "").toLowerCase();
6769
- const hardLimit = t.includes("quota") || // The Hypha LLM proxy emits a 429 "Daily spending limit reached Resets
6770
- // at 00:00 UTC. Contact the admin to raise your limit." That is a hard cap
6771
- // that resets at a fixed time / needs human action — NOT a transient
6772
- // upstream throttle. Retrying it 10× just delays surfacing the real error.
6773
- t.includes("spending limit") || t.includes("credit") || t.includes("balance") || t.includes("billing") || t.includes("payment") || t.includes("subscription") || t.includes("1m-context") || t.includes("1m context") || t.includes("insufficient") || t.includes("usage limit") && !t.includes("not your usage limit");
6827
+ const strongHardLimit = t.includes("quota") || t.includes("spending limit") || t.includes("payment") || t.includes("subscription") || t.includes("1m-context") || t.includes("1m context") || t.includes("insufficient") || t.includes("usage limit") && !t.includes("not your usage limit");
6828
+ const serverFaultSignal = typeof apiErrorStatus === "number" && apiErrorStatus >= 500 && apiErrorStatus <= 599 || /\b(500|502|503|504|520|521|522|523|524|529)\b/.test(t);
6829
+ const weakBillingLimit = !serverFaultSignal && (t.includes("credit") || t.includes("balance") || t.includes("billing"));
6830
+ const hardLimit = strongHardLimit || weakBillingLimit;
6774
6831
  if (hardLimit) return false;
6775
6832
  const authIssue = apiErrorStatus === 401 || apiErrorStatus === 403 || t.includes("unauthorized") || t.includes("invalid api key") || t.includes("authentication") || t.includes("forbidden");
6776
6833
  if (authIssue) return false;
@@ -7142,7 +7199,13 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
7142
7199
  const skillCtxFor = (c) => ({
7143
7200
  channelsServiceId,
7144
7201
  baseUrl: channelsBaseUrl,
7145
- key: c.identity?.shared_key || c.identity?.callers?.[0]?.key,
7202
+ // #0512: embed ONLY the channel-wide shared_key. The previous `|| callers?.[0]?.key`
7203
+ // fallback leaked one specific caller's PRIVATE per-key credential into the skill body,
7204
+ // which is served by the UNAUTHENTICATED channelDescribe (and view-tier getChannelSkill) —
7205
+ // an anonymous reader could then impersonate that caller (send as them + read their outbox).
7206
+ // For a per-key channel (no shared_key) key is undefined and generateSkillBody renders the
7207
+ // <your-key> placeholder, which is the correct instruction for a per-key caller.
7208
+ key: c.identity?.shared_key,
7146
7209
  // The session this skill is being copied FROM — baked into `dynamic` channel
7147
7210
  // instructions as the routing target so the copied link lands in THIS session.
7148
7211
  session: sessionId
@@ -7525,7 +7588,9 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
7525
7588
  name: c.name,
7526
7589
  skillUrl,
7527
7590
  sendUrl,
7528
- key: c.identity?.shared_key || c.identity?.callers?.[0]?.key,
7591
+ // #0512: only the channel-wide shared_key never an individual caller's private key
7592
+ // (this getChannelSkill result is reachable at view tier).
7593
+ key: c.identity?.shared_key,
7529
7594
  hyphaKeyless: (c.identity?.hypha_allow || []).length > 0,
7530
7595
  bind: c.bind,
7531
7596
  bindMode: mode,
@@ -8771,6 +8836,23 @@ function handleClaudeMessage(ctx, msg) {
8771
8836
  }
8772
8837
  }
8773
8838
 
8839
+ const KNOWN_AGENT_LAUNCHERS = ["claude", "codex", "gemini"];
8840
+ function agentLauncherExists(cmd, env = process.env) {
8841
+ if (!cmd) return false;
8842
+ if (cmd.includes("/") || cmd.includes("\\")) return existsSync$1(cmd);
8843
+ const dirs = (env.PATH || "").split(delimiter).filter(Boolean);
8844
+ const exts = process.platform === "win32" ? ["", ...(env.PATHEXT || ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean)] : [""];
8845
+ for (const dir of dirs) {
8846
+ for (const ext of exts) {
8847
+ if (existsSync$1(join(dir, cmd + ext))) return true;
8848
+ }
8849
+ }
8850
+ return false;
8851
+ }
8852
+ function detectAvailableAgents(env = process.env) {
8853
+ return KNOWN_AGENT_LAUNCHERS.filter((c) => agentLauncherExists(c, env));
8854
+ }
8855
+
8774
8856
  const INLINE_ARTIFACT_MAX_BYTES = 512 * 1024;
8775
8857
  const MAX_STORED_MESSAGE_BYTES = 2 * 1024 * 1024;
8776
8858
  const MIN_DATA_URI_SCAN_CHARS = INLINE_ARTIFACT_MAX_BYTES;
@@ -9581,6 +9663,52 @@ class SharingNotificationSync {
9581
9663
  this.log(`[USER EVENT] Failed to publish ${event.type} for ${event.recipientEmail}: ${err.message}`);
9582
9664
  }
9583
9665
  }
9666
+ /**
9667
+ * List user events addressed to `recipientEmail` (the bell inbox), newest first, excluding
9668
+ * expired ones. Reuses the global svamp-user-events collection — cross-workspace by design.
9669
+ * Mirrors the webapp's fetchUserEvents so the CLI reads the same source of truth.
9670
+ */
9671
+ async listUserEvents(recipientEmail) {
9672
+ if (!this.initialized || !this.eventCollectionId) return [];
9673
+ const normalized = recipientEmail.toLowerCase();
9674
+ try {
9675
+ const children = await this.artifactManager.list({
9676
+ parent_id: this.eventCollectionId,
9677
+ _rkwargs: true
9678
+ });
9679
+ if (!Array.isArray(children)) return [];
9680
+ const now = Date.now();
9681
+ return children.map((c) => c?.manifest).filter((m) => !!m && typeof m.recipientEmail === "string" && m.recipientEmail.toLowerCase() === normalized && (!m.expiresAt || m.expiresAt > now)).sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));
9682
+ } catch (err) {
9683
+ this.log(`[USER EVENT] listUserEvents failed for ${recipientEmail}: ${err.message}`);
9684
+ return [];
9685
+ }
9686
+ }
9687
+ /**
9688
+ * Acknowledge (dismiss) a user event by deleting its artifact. The alias is derived from the
9689
+ * event id + recipient email, so only the recipient's own event is removed. Returns true if a
9690
+ * matching event was deleted.
9691
+ */
9692
+ async ackUserEvent(eventId, recipientEmail) {
9693
+ if (!this.initialized || !this.eventCollectionId) return false;
9694
+ const normalized = recipientEmail.toLowerCase();
9695
+ try {
9696
+ const children = await this.artifactManager.list({ parent_id: this.eventCollectionId, _rkwargs: true });
9697
+ if (!Array.isArray(children)) return false;
9698
+ const short = eventId.slice(0, 8);
9699
+ const match = children.find((c) => {
9700
+ const m = c?.manifest;
9701
+ return m && typeof m.id === "string" && (m.id === eventId || m.id.slice(0, 8) === short) && typeof m.recipientEmail === "string" && m.recipientEmail.toLowerCase() === normalized;
9702
+ });
9703
+ if (!match) return false;
9704
+ await this.artifactManager.delete({ artifact_id: match.id, _rkwargs: true });
9705
+ this.log(`[USER EVENT] Acked ${eventId.slice(0, 8)} for ${recipientEmail}`);
9706
+ return true;
9707
+ } catch (err) {
9708
+ this.log(`[USER EVENT] ackUserEvent failed for ${recipientEmail}: ${err.message}`);
9709
+ return false;
9710
+ }
9711
+ }
9584
9712
  // ── Sharing config diff + sync ───────────────────────────────────
9585
9713
  async syncSharing(sessionId, oldSharing, newSharing, context) {
9586
9714
  if (!this.initialized) return;
@@ -11404,7 +11532,13 @@ class CodexAppServerBackend {
11404
11532
  if (avail === false) throw new Error("Installed Codex is too old for app-server \u2014 upgrade to >= 0.100 (`npm i -g @openai/codex@latest`).");
11405
11533
  await this.client.start();
11406
11534
  if (this.opts.resumeThreadId) {
11407
- await this.client.resumeThread({ threadId: this.opts.resumeThreadId, model: this.model, approvalPolicy: this.approvalPolicy, sandbox: this.sandbox });
11535
+ try {
11536
+ await this.client.resumeThread({ threadId: this.opts.resumeThreadId, model: this.model, approvalPolicy: this.approvalPolicy, sandbox: this.sandbox });
11537
+ } catch (err) {
11538
+ this.log(`[Codex] resumeThread(${this.opts.resumeThreadId}) failed (${err?.message ?? err}) \u2014 starting a fresh thread instead (context lost, session preserved)`);
11539
+ const r = await this.client.startThread({ model: this.model, approvalPolicy: this.approvalPolicy, sandbox: this.sandbox });
11540
+ this.model = this.model ?? r.model;
11541
+ }
11408
11542
  } else {
11409
11543
  const r = await this.client.startThread({ model: this.model, approvalPolicy: this.approvalPolicy, sandbox: this.sandbox });
11410
11544
  this.model = this.model ?? r.model;
@@ -12135,7 +12269,31 @@ async function verifyIsolation(method, binaryPath) {
12135
12269
  return { passed: false, error: `${method} is not supported for external isolation` };
12136
12270
  }
12137
12271
  }
12272
+ function isolationDisabledByEnv(env = process.env) {
12273
+ const v = (env.SVAMP_ISOLATION || "").trim().toLowerCase();
12274
+ if (v && ["none", "off", "disabled", "0", "false", "no"].includes(v)) return true;
12275
+ const truthy = (s) => !!s && !["", "0", "false", "no", "off"].includes(s.trim().toLowerCase());
12276
+ return truthy(env.SVAMP_NO_ISOLATION) || truthy(env.SVAMP_SKIP_NONO);
12277
+ }
12138
12278
  async function detectIsolationCapabilities() {
12279
+ if (isolationDisabledByEnv()) {
12280
+ console.log(
12281
+ "[isolation] Disabled via env (SVAMP_ISOLATION=none) \u2014 skipping nono auto-install + runtime detection. Sessions run WITHOUT per-session sandboxing; session sharing is allowed (rely on the container/host boundary)."
12282
+ );
12283
+ const notFound = () => ({ found: false });
12284
+ return {
12285
+ available: [],
12286
+ preferred: null,
12287
+ disabled: true,
12288
+ details: {
12289
+ nono: notFound(),
12290
+ docker: notFound(),
12291
+ podman: notFound(),
12292
+ srt: notFound(),
12293
+ bwrap: notFound()
12294
+ }
12295
+ };
12296
+ }
12139
12297
  const checks = {
12140
12298
  nono: checkCommand("nono", ["--version"]),
12141
12299
  docker: checkCommand("docker", ["--version"]),
@@ -12625,6 +12783,7 @@ class ProcessSupervisor {
12625
12783
  throw new Error(`Process '${entry.spec.name}' is already running`);
12626
12784
  }
12627
12785
  entry.stopping = false;
12786
+ this.clearTimers(entry);
12628
12787
  if (entry.spec.desiredStopped) {
12629
12788
  entry.spec.desiredStopped = false;
12630
12789
  await this.persistSpec(entry.spec);
@@ -12683,6 +12842,8 @@ class ProcessSupervisor {
12683
12842
  this.clearTimers(entry);
12684
12843
  await this.killChild(entry.child);
12685
12844
  entry.child = void 0;
12845
+ } else {
12846
+ this.clearTimers(entry);
12686
12847
  }
12687
12848
  if (entry.deleted) return;
12688
12849
  entry.stopping = false;
@@ -12914,6 +13075,7 @@ class ProcessSupervisor {
12914
13075
  spawnProcess(entry) {
12915
13076
  const { spec, state } = entry;
12916
13077
  if (entry.deleted) return;
13078
+ state.consecutiveProbeFailures = 0;
12917
13079
  try {
12918
13080
  const env = { ...process.env, ...spec.env ?? {} };
12919
13081
  const child = spawn(spec.command, spec.args, {
@@ -13018,6 +13180,7 @@ class ProcessSupervisor {
13018
13180
  );
13019
13181
  entry.restartTimer = setTimeout(() => {
13020
13182
  if (entry.deleted || entry.stopping) return;
13183
+ if (entry.child) return;
13021
13184
  state.restartCount++;
13022
13185
  state.status = "starting";
13023
13186
  this.spawnProcess(entry);
@@ -13046,6 +13209,8 @@ class ProcessSupervisor {
13046
13209
  const timeoutMs = (probe.timeout ?? DEFAULT_PROBE_TIMEOUT_S) * 1e3;
13047
13210
  const threshold = probe.failureThreshold ?? DEFAULT_PROBE_FAILURE_THRESHOLD;
13048
13211
  const url = `http://localhost:${probe.port}${urlPath}`;
13212
+ const probedChild = entry.child;
13213
+ const stillSameChild = () => !entry.deleted && !entry.stopping && entry.child === probedChild && entry.state.status === "running";
13049
13214
  try {
13050
13215
  const controller = new AbortController();
13051
13216
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -13055,6 +13220,7 @@ class ProcessSupervisor {
13055
13220
  } finally {
13056
13221
  clearTimeout(timer);
13057
13222
  }
13223
+ if (!stillSameChild()) return;
13058
13224
  const ok = resp.ok;
13059
13225
  entry.state.lastProbe = { ok, timestamp: Date.now(), statusCode: resp.status };
13060
13226
  if (ok) {
@@ -13069,6 +13235,7 @@ class ProcessSupervisor {
13069
13235
  }
13070
13236
  }
13071
13237
  } catch (err) {
13238
+ if (!stillSameChild()) return;
13072
13239
  entry.state.lastProbe = { ok: false, timestamp: Date.now(), error: err.message };
13073
13240
  entry.state.consecutiveProbeFailures++;
13074
13241
  console.warn(
@@ -13099,7 +13266,12 @@ class ProcessSupervisor {
13099
13266
  return;
13100
13267
  }
13101
13268
  console.log(`[SUPERVISOR] Process '${entry.spec.name}' TTL: expires in ${remainingS.toFixed(0)}s`);
13102
- entry.ttlTimer = setTimeout(() => this.expireProcess(entry), remainingS * 1e3);
13269
+ const MAX_DELAY = 2147483647;
13270
+ const delayMs = remainingS * 1e3;
13271
+ entry.ttlTimer = setTimeout(
13272
+ () => delayMs > MAX_DELAY ? this.setupTTL(entry) : this.expireProcess(entry),
13273
+ Math.min(delayMs, MAX_DELAY)
13274
+ );
13103
13275
  }
13104
13276
  expireProcess(entry) {
13105
13277
  if (entry.deleted) return;
@@ -14031,7 +14203,7 @@ function summarizeExtensions(extensions, maxExtensions = DEFAULT_LOOP_MAX_EXTENS
14031
14203
  const n = grantedExtensionCount(extensions, maxExtensions);
14032
14204
  if (n === 0) return "";
14033
14205
  const turns = extensionTurns(extensions, maxExtensions);
14034
- const reasons = extensions.slice(0, maxExtensions).map((e) => (e.reason || "").trim()).filter(Boolean).join("; ").slice(0, 300);
14206
+ const reasons = extensions.slice(0, maxExtensions).map((e) => (e?.reason || "").trim()).filter(Boolean).join("; ").slice(0, 300);
14035
14207
  return `${n} extension${n === 1 ? "" : "s"} granted (+${turns} turns)${reasons ? `: ${reasons}` : ""}`;
14036
14208
  }
14037
14209
  const DEFAULT_LOOP_STUCK_CHECKPOINTS = 3;
@@ -14039,7 +14211,7 @@ const DEFAULT_LOOP_MAX_AUTORESUMES = Number.POSITIVE_INFINITY;
14039
14211
  const PROGRESS_HISTORY_CAP = 12;
14040
14212
  function resolveStuckLimit(envValue) {
14041
14213
  const n = Number(envValue);
14042
- if (Number.isFinite(n) && n >= 1) return Math.floor(n);
14214
+ if (Number.isFinite(n) && n >= 1) return Math.min(Math.floor(n), PROGRESS_HISTORY_CAP - 1);
14043
14215
  return DEFAULT_LOOP_STUCK_CHECKPOINTS;
14044
14216
  }
14045
14217
  function resolveMaxAutoResumes(envValue) {
@@ -14097,7 +14269,8 @@ function resolveEvaluatorModel(envValue) {
14097
14269
  function parseEvaluatorVerdict(text) {
14098
14270
  if (!text) return null;
14099
14271
  const candidates = [];
14100
- let depth = 0, start = -1, inStr = false, esc = false;
14272
+ const openStack = [];
14273
+ let inStr = false, esc = false;
14101
14274
  for (let i = 0; i < text.length; i++) {
14102
14275
  const c = text[i];
14103
14276
  if (inStr) {
@@ -14113,15 +14286,11 @@ function parseEvaluatorVerdict(text) {
14113
14286
  if (c === '"') {
14114
14287
  inStr = true;
14115
14288
  } else if (c === "{") {
14116
- if (depth === 0) start = i;
14117
- depth++;
14289
+ openStack.push(i);
14118
14290
  } else if (c === "}") {
14119
- if (depth > 0) {
14120
- depth--;
14121
- if (depth === 0 && start >= 0) {
14122
- candidates.push(text.slice(start, i + 1));
14123
- start = -1;
14124
- }
14291
+ const open = openStack.pop();
14292
+ if (open !== void 0) {
14293
+ candidates.push(text.slice(open, i + 1));
14125
14294
  }
14126
14295
  }
14127
14296
  }
@@ -15387,7 +15556,7 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
15387
15556
  const key = s.started_at ?? "\u2205";
15388
15557
  if (key === announcedLoopStart) return;
15389
15558
  announcedLoopStart = key;
15390
- const iter = typeof s.iteration === "number" ? s.iteration : 0;
15559
+ const iter = s.ledger && typeof s.ledger.turns === "number" && s.ledger.turns > 0 ? s.ledger.turns : typeof s.iteration === "number" ? s.iteration : 0;
15391
15560
  const plural = iter === 1 ? "" : "s";
15392
15561
  const gaveUpCfg = readLoopJson(directory, sessionId, "loop.config.json") || {};
15393
15562
  const curMax = typeof gaveUpCfg.max_iterations === "number" ? gaveUpCfg.max_iterations : iter;
@@ -16015,7 +16184,7 @@ async function startDaemon(options) {
16015
16184
  try {
16016
16185
  const dir = loadSessionIndex()[sessionId]?.directory;
16017
16186
  if (!dir) return;
16018
- const { reconcileServiceLinks } = await import('./agentCommands-DGT-1wjo.mjs');
16187
+ const { reconcileServiceLinks } = await import('./agentCommands-DN7hb_oL.mjs');
16019
16188
  const configPath = getSvampConfigPath(dir, sessionId);
16020
16189
  const config = readSvampConfig(configPath);
16021
16190
  const entries = Array.from(urls.entries());
@@ -16033,7 +16202,7 @@ async function startDaemon(options) {
16033
16202
  }
16034
16203
  }
16035
16204
  async function createExposedTunnel(spec) {
16036
- const { FrpcTunnel } = await import('./frpc-D3faR6iR.mjs');
16205
+ const { FrpcTunnel } = await import('./frpc-DnxvF_Mx.mjs');
16037
16206
  const tunnel = new FrpcTunnel({
16038
16207
  name: spec.name,
16039
16208
  ports: spec.ports,
@@ -16061,7 +16230,7 @@ async function startDaemon(options) {
16061
16230
  ensureAutoInstalledCommands(logger);
16062
16231
  (async () => {
16063
16232
  try {
16064
- const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-CgEtGkX2.mjs');
16233
+ const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-BaMR97BE.mjs');
16065
16234
  beginClaudeVersionReconcile((msg) => logger.log(msg));
16066
16235
  } catch (e) {
16067
16236
  logger.log(`[claude-version] check failed: ${e?.message || e}`);
@@ -16743,10 +16912,11 @@ ${parts.join("\n")}`);
16743
16912
  logger.log(`[Session ${sessionId}] loop was cancelled during verification \u2014 not re-kicking (#0308)`);
16744
16913
  return;
16745
16914
  }
16746
- const ledger = ls.ledger;
16915
+ const curLs = readLoopState(directory, sessionId) || ls;
16916
+ const ledger = curLs.ledger ?? ls.ledger;
16747
16917
  const budget = ls.budget;
16748
- const progress_history = ls.progress_history;
16749
- const extensions = ls.extensions;
16918
+ const progress_history = curLs.progress_history ?? ls.progress_history;
16919
+ const extensions = curLs.extensions ?? ls.extensions;
16750
16920
  if (result.action === "rekick") {
16751
16921
  const cond = ls.task || "";
16752
16922
  writeGoalLoopState(directory, sessionId, {
@@ -16875,6 +17045,7 @@ ${parts.join("\n")}`);
16875
17045
  let consecutiveOverloadRetries = 0;
16876
17046
  let overloadBailedThisTurn = false;
16877
17047
  let pendingCompactTurn = false;
17048
+ let noIsolationWarned = false;
16878
17049
  const RATELIMIT_CFG = getRateLimitRetryConfig();
16879
17050
  let currentTurnMessage;
16880
17051
  let rateLimitRetryCount = 0;
@@ -16982,6 +17153,14 @@ ${parts.join("\n")}`);
16982
17153
  } else if (shouldIsolateSession()) {
16983
17154
  logger.log(`[Session ${sessionId}] WARNING: No isolation runtime (nono/docker/podman) available. Session is NOT sandboxed.`);
16984
17155
  sessionMetadata = { ...sessionMetadata, isolationMethod: void 0 };
17156
+ if (!isolationCapabilities.disabled && !noIsolationWarned) {
17157
+ noIsolationWarned = true;
17158
+ sessionService.pushMessage({
17159
+ type: "message",
17160
+ level: "warning",
17161
+ message: "This session requested sandboxing (a security context / --isolate) but no isolation runtime (nono, docker, or podman) is available on this machine \u2014 it is running WITHOUT a sandbox. Install a runtime, or set SVAMP_ISOLATION=none to run unsandboxed intentionally (e.g. when the machine is already a container)."
17162
+ }, "event");
17163
+ }
16985
17164
  } else {
16986
17165
  sessionMetadata = { ...sessionMetadata, isolationMethod: void 0 };
16987
17166
  }
@@ -17030,6 +17209,22 @@ ${parts.join("\n")}`);
17030
17209
  spawnEnv.SVAMP_SANDBOXED = "1";
17031
17210
  logger.log(`[Session ${sessionId}] Credential staging: HOME=${stagedCredentials.homePath}, SVAMP_SANDBOXED=1`);
17032
17211
  }
17212
+ if (!agentLauncherExists("claude")) {
17213
+ const others = detectAvailableAgents().filter((a) => a !== "claude");
17214
+ const hint = others.length ? ` This machine has: ${others.join(", ")} \u2014 start a ${others[0]} session instead.` : " No agent launchers were found on this machine.";
17215
+ logger.log(`[Session ${sessionId}] Claude launcher ('claude') not found on PATH \u2014 aborting spawn (available: ${detectAvailableAgents().join(", ") || "none"})`);
17216
+ sessionService.pushMessage({
17217
+ type: "message",
17218
+ level: "error",
17219
+ message: `Claude Code (\`claude\`) isn't installed on this machine (${os$1.hostname()}), so this Claude session can't run.${hint}`
17220
+ }, "event");
17221
+ sessionMetadata = { ...sessionMetadata, lifecycleState: "idle" };
17222
+ sessionService.updateMetadata(sessionMetadata);
17223
+ sessionWasProcessing = false;
17224
+ signalProcessing(false);
17225
+ sessionService.sendSessionEnd();
17226
+ return void 0;
17227
+ }
17033
17228
  const child = spawn(spawnCommand, spawnArgs, {
17034
17229
  cwd: directory,
17035
17230
  stdio: ["pipe", "pipe", "pipe"],
@@ -17046,7 +17241,7 @@ ${parts.join("\n")}`);
17046
17241
  child.on("error", (err) => {
17047
17242
  logger.log(`[Session ${sessionId}] Claude process error: ${err.message}`);
17048
17243
  sessionService.pushMessage(
17049
- { type: "message", message: `Agent process exited unexpectedly: ${err.message}. Please ensure Claude Code CLI is installed.` },
17244
+ { type: "message", level: "error", message: `Agent process failed to start: ${err.message}. Please ensure the Claude Code CLI is installed on this machine.` },
17050
17245
  "event"
17051
17246
  );
17052
17247
  sessionWasProcessing = false;
@@ -18223,18 +18418,19 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
18223
18418
  });
18224
18419
  },
18225
18420
  onIssue: async (params) => {
18226
- const { issueRpc } = await import('./rpc-BQ8zLSUc.mjs');
18421
+ const { issueRpc } = await import('./rpc-BvXiNu-z.mjs');
18227
18422
  return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
18228
18423
  },
18229
18424
  onWorkflow: async (params) => {
18230
- const { workflowRpc } = await import('./rpc-xlV-4-BG.mjs');
18425
+ const { workflowRpc } = await import('./rpc-BKDKqllr.mjs');
18231
18426
  return workflowRpc(params?.cwd || directory, params || {});
18232
18427
  },
18233
18428
  onRipgrep: async (args, cwd) => {
18234
- const { exec: exec2 } = await import('child_process');
18429
+ const { execFile } = await import('child_process');
18235
18430
  const rgCwd = cwd || directory;
18431
+ const argv = Array.isArray(args) ? args : String(args).split(/\s+/).filter(Boolean);
18236
18432
  return new Promise((resolve2, reject) => {
18237
- exec2(`rg ${args}`, { cwd: rgCwd, timeout: 3e4, maxBuffer: 5 * 1024 * 1024 }, (err, stdout) => {
18433
+ execFile("rg", argv, { cwd: rgCwd, timeout: 3e4, maxBuffer: 5 * 1024 * 1024 }, (err, stdout) => {
18238
18434
  if (err && !stdout) {
18239
18435
  reject(new Error(err.message));
18240
18436
  } else {
@@ -18838,18 +19034,19 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
18838
19034
  });
18839
19035
  },
18840
19036
  onIssue: async (params) => {
18841
- const { issueRpc } = await import('./rpc-BQ8zLSUc.mjs');
19037
+ const { issueRpc } = await import('./rpc-BvXiNu-z.mjs');
18842
19038
  return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
18843
19039
  },
18844
19040
  onWorkflow: async (params) => {
18845
- const { workflowRpc } = await import('./rpc-xlV-4-BG.mjs');
19041
+ const { workflowRpc } = await import('./rpc-BKDKqllr.mjs');
18846
19042
  return workflowRpc(params?.cwd || directory, params || {});
18847
19043
  },
18848
19044
  onRipgrep: async (args, cwd) => {
18849
- const { exec: exec2 } = await import('child_process');
19045
+ const { execFile } = await import('child_process');
18850
19046
  const rgCwd = cwd || directory;
19047
+ const argv = Array.isArray(args) ? args : String(args).split(/\s+/).filter(Boolean);
18851
19048
  return new Promise((resolve2, reject) => {
18852
- exec2(`rg ${args}`, { cwd: rgCwd, timeout: 3e4, maxBuffer: 5 * 1024 * 1024 }, (err, stdout) => {
19049
+ execFile("rg", argv, { cwd: rgCwd, timeout: 3e4, maxBuffer: 5 * 1024 * 1024 }, (err, stdout) => {
18853
19050
  if (err && !stdout) {
18854
19051
  reject(new Error(err.message));
18855
19052
  } else {
@@ -19343,7 +19540,11 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
19343
19540
  "event"
19344
19541
  );
19345
19542
  sessionService.sendSessionEnd();
19346
- deleteSession(sessionId);
19543
+ if (!acpPersisted) {
19544
+ deleteSession(sessionId);
19545
+ } else {
19546
+ logger.log(`[Agent Session ${sessionId}] start failed on restore \u2014 preserving persisted session for a later retry (not deleting)`);
19547
+ }
19347
19548
  });
19348
19549
  return {
19349
19550
  type: "success",
@@ -19485,13 +19686,16 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
19485
19686
  logger.log(`Restored machine metadata (sharing=${!!persistedMachineMeta.sharing}, securityContextConfig=${!!persistedMachineMeta.securityContextConfig}, injectPlatformGuidance=${persistedMachineMeta.injectPlatformGuidance})`);
19486
19687
  }
19487
19688
  let seededSharing = persistedMachineMeta?.sharing;
19689
+ let seedShareNotify = null;
19488
19690
  const seedEmails = parseDaemonShareEmails(process.env[DAEMON_SHARE_EMAILS_ENV]);
19489
19691
  if (seedEmails.length > 0) {
19490
19692
  const ownerEmail = parseJwtEmail(process.env.HYPHA_TOKEN || "") || void 0;
19693
+ const preSeedSharing = seededSharing;
19491
19694
  const result = applyDaemonShareSeed(seededSharing, seedEmails, ownerEmail);
19492
19695
  seededSharing = result.sharing;
19493
19696
  if (result.addedEmails.length > 0) {
19494
19697
  logger.log(`Seeded machine sharing with ${result.addedEmails.length} user(s): ${result.addedEmails.join(", ")}`);
19698
+ if (seededSharing) seedShareNotify = { oldSharing: preSeedSharing, newSharing: seededSharing, added: result.addedEmails };
19495
19699
  } else {
19496
19700
  logger.log(`Daemon --share seed: no new users (all already in allowedUsers).`);
19497
19701
  }
@@ -19528,7 +19732,25 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
19528
19732
  startedAt: Date.now()
19529
19733
  };
19530
19734
  const sharingNotificationSync = new SharingNotificationSync(server, logger.log);
19531
- sharingNotificationSync.init().catch(() => {
19735
+ const seedNotifyWorkspace = server.config.workspace;
19736
+ const seedNotifyClientId = server.config.client_id;
19737
+ sharingNotificationSync.init().then(() => {
19738
+ if (seedShareNotify) {
19739
+ sharingNotificationSync.syncSharing(
19740
+ `machine-${machineId}`,
19741
+ seedShareNotify.oldSharing,
19742
+ seedShareNotify.newSharing,
19743
+ {
19744
+ machineId,
19745
+ machineServiceId: `${seedNotifyWorkspace}/${seedNotifyClientId}:default`,
19746
+ ownerWorkspace: seedNotifyWorkspace,
19747
+ ownerEmail: seedShareNotify.newSharing.owner || "",
19748
+ label: process.env.SVAMP_DISPLAY_NAME || machineId,
19749
+ shareType: "machine"
19750
+ }
19751
+ ).catch((e) => logger.log(`[share-seed] notify failed: ${e?.message || e}`));
19752
+ }
19753
+ }).catch(() => {
19532
19754
  });
19533
19755
  const buildMachineHandlers = () => ({
19534
19756
  spawnSession,
@@ -19734,7 +19956,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
19734
19956
  }
19735
19957
  if (persistedSessions.length > 0) {
19736
19958
  try {
19737
- const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-CgEtGkX2.mjs');
19959
+ const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-BaMR97BE.mjs');
19738
19960
  await awaitClaudeVersionReady();
19739
19961
  } catch {
19740
19962
  }
@@ -19931,7 +20153,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
19931
20153
  const PING_TIMEOUT_MS = 15e3;
19932
20154
  const POST_RECONNECT_GRACE_MS = 2e4;
19933
20155
  const RECONNECT_JITTER_MS = 2500;
19934
- const { WorkflowScheduler } = await import('./scheduler-DaFZY2ob.mjs');
20156
+ const { WorkflowScheduler } = await import('./scheduler-D-25wsin.mjs');
19935
20157
  const workflowScheduler = new WorkflowScheduler({
19936
20158
  projectRoots: () => {
19937
20159
  const dirs = /* @__PURE__ */ new Set();
@@ -20554,4 +20776,4 @@ var run = /*#__PURE__*/Object.freeze({
20554
20776
  writeStopMarker: writeStopMarker
20555
20777
  });
20556
20778
 
20557
- export { formatHandle as $, removeWorkflow as A, saveWorkflow as B, rawWorkflow as C, listWorkflows as D, isWorkflowEnabled as E, workflowCrons as F, cronMatches as G, summarize as H, workflowSteps as I, loadMachineContext as J, buildMachineInstructions as K, machineToolsForRole as L, buildMachineTools as M, parseFrontmatter as N, getSkillsServer as O, getSkillsWorkspaceName as P, getSkillsCollectionName as Q, READ_ONLY_TOOLS as R, fetchWithTimeout as S, searchSkills as T, SKILLS_DIR as U, getSkillInfo as V, downloadSkillFile as W, listSkillFiles as X, resolveModel as Y, clearStopMarker as Z, stopMarkerExists as _, createSessionStore as a, normalizeAllowedUser as a0, loadSecurityContextConfig as a1, resolveSecurityContext as a2, buildSecurityContextFromFlags as a3, mergeSecurityContexts as a4, buildSessionShareUrl as a5, computeOutboundHop as a6, registerAwaitingReply as a7, buildMachineShareUrl as a8, parseHandle as a9, handleMatchesMetadata as aa, describeMisconfiguration as ab, buildMachineDeps as ac, applyClaudeProxyEnv as ad, composeSessionId as ae, generateFriendlyName as af, generateHookSettings as ag, staticFileServer as ah, instanceConfig as ai, claudeAuth as aj, codexProvider as ak, projectInfo as al, DefaultTransport$1 as am, acpBackend as an, acpAgentConfig as ao, codexAppServerBackend as ap, GeminiTransport$1 as aq, api as ar, run as as, 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 };
20779
+ export { clearStopMarker as $, removeWorkflow as A, saveWorkflow as B, rawWorkflow as C, listWorkflows as D, isWorkflowEnabled as E, workflowCrons as F, cronMatches as G, summarize as H, workflowSteps as I, parseJwtEmail as J, loadMachineContext as K, buildMachineInstructions as L, machineToolsForRole as M, buildMachineTools as N, parseFrontmatter as O, getSkillsServer as P, getSkillsWorkspaceName as Q, READ_ONLY_TOOLS as R, SharingNotificationSync as S, getSkillsCollectionName as T, fetchWithTimeout as U, searchSkills as V, SKILLS_DIR as W, getSkillInfo as X, downloadSkillFile as Y, listSkillFiles as Z, resolveModel as _, createSessionStore as a, stopMarkerExists as a0, formatHandle as a1, normalizeAllowedUser as a2, loadSecurityContextConfig as a3, resolveSecurityContext as a4, buildSecurityContextFromFlags as a5, mergeSecurityContexts as a6, buildSessionShareUrl as a7, computeOutboundHop as a8, registerAwaitingReply as a9, buildMachineShareUrl as aa, parseHandle as ab, handleMatchesMetadata as ac, describeMisconfiguration as ad, buildMachineDeps as ae, applyClaudeProxyEnv as af, composeSessionId as ag, generateFriendlyName as ah, generateHookSettings as ai, staticFileServer as aj, instanceConfig as ak, claudeAuth as al, codexProvider as am, projectInfo as an, DefaultTransport$1 as ao, acpBackend as ap, acpAgentConfig as aq, codexAppServerBackend as ar, GeminiTransport$1 as as, api as at, run as au, 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 };