svamp-cli 0.2.252 → 0.2.254

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 (25) hide show
  1. package/dist/{agentCommands-BfK_xRcZ.mjs → agentCommands-Dtd2mICX.mjs} +5 -5
  2. package/dist/{auth-dxadQh0v.mjs → auth-C5b7_Wpo.mjs} +1 -1
  3. package/dist/cli.mjs +70 -62
  4. package/dist/{commands-BkroIJOC.mjs → commands-Bz3xxhDd.mjs} +23 -2
  5. package/dist/{commands-CuSO_rgw.mjs → commands-CTwhZ-Sj.mjs} +2 -2
  6. package/dist/{commands-Cg3yUhp1.mjs → commands-Ch_nKvnk.mjs} +1 -1
  7. package/dist/{commands-B9eaRA8O.mjs → commands-D8gpDICH.mjs} +2 -2
  8. package/dist/{commands-Blddcoeh.mjs → commands-DH1pGrZe.mjs} +1 -1
  9. package/dist/{commands-eDMV8Q8q.mjs → commands-DI-5Eder.mjs} +1 -1
  10. package/dist/{commands-BAtj5B4a.mjs → commands-rLFKP45k.mjs} +6 -6
  11. package/dist/{fleet-CNmpoV_Q.mjs → fleet-u7T-s_4u.mjs} +2 -2
  12. package/dist/{frpc-DoUUUoZn.mjs → frpc-BBkDE65f.mjs} +1 -1
  13. package/dist/{headlessCli-DGTHnPjT.mjs → headlessCli-D7JwS_Bf.mjs} +2 -2
  14. package/dist/index.mjs +1 -1
  15. package/dist/{package-C2CWKEW-.mjs → package-QKAEHd-5.mjs} +1 -1
  16. package/dist/{pinnedClaudeCode-ivV7xAp3.mjs → pinnedClaudeCode-DuLXaoGP.mjs} +1 -1
  17. package/dist/{rpc-_rwZwiHD.mjs → rpc-CMS1F7Qu.mjs} +1 -1
  18. package/dist/{rpc-665-sPRQ.mjs → rpc-DTU-aE0V.mjs} +1 -1
  19. package/dist/{run-BPNXOI3F.mjs → run-BJYwPKDR.mjs} +1 -1
  20. package/dist/{run-BNxaekWa.mjs → run-BqqMOMZN.mjs} +232 -56
  21. package/dist/{scheduler-DjMTCoWS.mjs → scheduler-B92Z_kox.mjs} +1 -1
  22. package/dist/{serveCommands-Bc6fchAf.mjs → serveCommands-BEJg71HK.mjs} +5 -5
  23. package/dist/{serveManager-DAuIjPuV.mjs → serveManager-BBRgdCJp.mjs} +2 -2
  24. package/dist/{sideband-D6vgqNiq.mjs → sideband-c7SyYkOC.mjs} +1 -1
  25. package/package.json +1 -1
@@ -2985,7 +2985,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
2985
2985
  const tunnels = handlers.tunnels;
2986
2986
  if (!tunnels) throw new Error("Tunnel management not available");
2987
2987
  if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
2988
- const { FrpcTunnel } = await import('./frpc-DoUUUoZn.mjs');
2988
+ const { FrpcTunnel } = await import('./frpc-BBkDE65f.mjs');
2989
2989
  const tunnel = new FrpcTunnel({
2990
2990
  name: params.name,
2991
2991
  ports: params.ports,
@@ -3452,7 +3452,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3452
3452
  }
3453
3453
  const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
3454
3454
  const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
3455
- const { toolsForRole } = await import('./sideband-D6vgqNiq.mjs');
3455
+ const { toolsForRole } = await import('./sideband-c7SyYkOC.mjs');
3456
3456
  const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
3457
3457
  return fmt(r2);
3458
3458
  }
@@ -3551,7 +3551,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3551
3551
  if (r.error || !r.sender) return { error: r.error || "unauthorized" };
3552
3552
  const callId = "call_" + Math.random().toString(16).slice(2, 12);
3553
3553
  const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
3554
- const { queryCore } = await import('./commands-BkroIJOC.mjs');
3554
+ const { queryCore } = await import('./commands-Bz3xxhDd.mjs');
3555
3555
  const timeout = c.reply?.timeout_sec || 120;
3556
3556
  let result;
3557
3557
  try {
@@ -3813,14 +3813,19 @@ function registerAwaitingReply(sessionId, threadId, now = Date.now()) {
3813
3813
  }
3814
3814
  writeAwaiting(sessionId, map);
3815
3815
  }
3816
- function takeAwaitingReply(sessionId, threadId, now = Date.now()) {
3816
+ function peekAwaitingReply(sessionId, threadId, now = Date.now()) {
3817
3817
  if (!sessionId || !threadId) return false;
3818
3818
  const map = readAwaiting(sessionId);
3819
3819
  const at = map[threadId];
3820
3820
  if (typeof at !== "number") return false;
3821
+ return now - at <= AWAIT_TTL_MS;
3822
+ }
3823
+ function consumeAwaitingReply(sessionId, threadId) {
3824
+ if (!sessionId || !threadId) return;
3825
+ const map = readAwaiting(sessionId);
3826
+ if (!(threadId in map)) return;
3821
3827
  delete map[threadId];
3822
3828
  writeAwaiting(sessionId, map);
3823
- return now - at <= AWAIT_TTL_MS;
3824
3829
  }
3825
3830
  function computeOutboundHop(sessionId) {
3826
3831
  if (!sessionId) return { hopCount: 0, fromInboxTurn: false };
@@ -4177,6 +4182,26 @@ function isStructuredMessage(msg) {
4177
4182
  function escapeXml(s) {
4178
4183
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
4179
4184
  }
4185
+ function sanitizeInboundInboxMessage(message, opts) {
4186
+ if (opts.trusted) return message;
4187
+ const email = opts.callerEmail;
4188
+ return {
4189
+ ...message,
4190
+ // Attribute to the SERVER-KNOWN identity, never the caller's self-asserted `from`.
4191
+ // An authenticated collaborator is an honest verified human (`user:<email>` → may
4192
+ // wake, attributed to THEM not the owner); an anonymous caller cannot claim human.
4193
+ from: email ? `user:${email}` : "agent:anonymous",
4194
+ verified: !!email,
4195
+ // Strip self-asserted channel provenance + async-reply routing a direct caller
4196
+ // has no business setting (legit channel ingress goes via the trusted channel path).
4197
+ channel: void 0,
4198
+ channelId: void 0,
4199
+ correlationId: void 0,
4200
+ // Escape the body so a nested </svamp-message> can't break out and forge a second
4201
+ // attributed envelope in the agent's transcript.
4202
+ body: xmlEscape(String(message.body ?? "").slice(0, 16 * 1024))
4203
+ };
4204
+ }
4180
4205
  function formatInboxMessageXml(msg) {
4181
4206
  if (!isStructuredMessage(msg)) return msg.body;
4182
4207
  const attrs = [`message-id="${escapeXml(msg.messageId)}"`];
@@ -5305,12 +5330,14 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
5305
5330
  // ── Inbox ──
5306
5331
  sendInboxMessage: async (message, context) => {
5307
5332
  authorizeRequest(context, metadata.sharing, "interact");
5308
- inbox.push(message);
5333
+ const trusted = getEffectiveRole(context, metadata.sharing) === "admin";
5334
+ const msg = sanitizeInboundInboxMessage(message, { trusted, callerEmail: context?.user?.email });
5335
+ inbox.push(msg);
5309
5336
  while (inbox.length > INBOX_MAX) inbox.shift();
5310
5337
  syncInboxToMetadata();
5311
- callbacks.onInboxMessage?.(message);
5312
- notifyListeners({ type: "inbox-update", sessionId, message });
5313
- return { success: true, messageId: message.messageId };
5338
+ callbacks.onInboxMessage?.(msg);
5339
+ notifyListeners({ type: "inbox-update", sessionId, message: msg });
5340
+ return { success: true, messageId: msg.messageId };
5314
5341
  },
5315
5342
  getInbox: async (opts, context) => {
5316
5343
  authorizeRequest(context, metadata.sharing, "view");
@@ -6279,12 +6306,10 @@ async function registerDebugService(server, machineId, deps) {
6279
6306
  // require_context: true → Hypha injects the caller context as the last arg of every
6280
6307
  // method, which the authorize() gate below enforces. 'unlisted' only hides discovery.
6281
6308
  config: { visibility: "unlisted", require_context: true },
6282
- healthCheck: async () => ({
6283
- ok: true,
6284
- machineId,
6285
- uptime: process.uptime(),
6286
- timestamp: Date.now()
6287
- }),
6309
+ healthCheck: async (context) => {
6310
+ authorize(context, "view");
6311
+ return { ok: true, machineId, uptime: process.uptime(), timestamp: Date.now() };
6312
+ },
6288
6313
  getSessionStates: async (context) => {
6289
6314
  authorize(context, "view");
6290
6315
  const sessions = deps.getTrackedSessions();
@@ -10127,6 +10152,18 @@ var api = /*#__PURE__*/Object.freeze({
10127
10152
  searchSkills: searchSkills
10128
10153
  });
10129
10154
 
10155
+ function signalProcessTree(child, signal) {
10156
+ const pid = child.pid;
10157
+ if (pid === void 0) return;
10158
+ try {
10159
+ process.kill(-pid, signal);
10160
+ } catch {
10161
+ try {
10162
+ child.kill(signal);
10163
+ } catch {
10164
+ }
10165
+ }
10166
+ }
10130
10167
  const DEFAULT_PROBE_INTERVAL_S = 10;
10131
10168
  const DEFAULT_PROBE_TIMEOUT_S = 5;
10132
10169
  const DEFAULT_PROBE_FAILURE_THRESHOLD = 3;
@@ -10458,7 +10495,13 @@ class ProcessSupervisor {
10458
10495
  const child = spawn$1(spec.command, spec.args, {
10459
10496
  cwd: spec.workdir,
10460
10497
  env,
10461
- stdio: ["ignore", "pipe", "pipe"]
10498
+ stdio: ["ignore", "pipe", "pipe"],
10499
+ // #0316: run each supervised process in its OWN process group (setsid) so a
10500
+ // shell-wrapping spec (`bash -c …`, `npm start`) can be torn down with the whole
10501
+ // tree — otherwise stop/restart/TTL only kills the direct PID and leaks
10502
+ // grandchildren. Kills below signal the group (negative PID). POSIX only; on
10503
+ // Windows `detached` just decouples the console and the group-kill falls back.
10504
+ detached: process.platform !== "win32"
10462
10505
  });
10463
10506
  entry.child = child;
10464
10507
  state.status = "running";
@@ -10620,10 +10663,7 @@ class ProcessSupervisor {
10620
10663
  console.warn(`[SUPERVISOR] Probe failed for '${entry.spec.name}' \u2014 killing to trigger restart`);
10621
10664
  entry.state.consecutiveProbeFailures = 0;
10622
10665
  if (entry.child) {
10623
- try {
10624
- entry.child.kill("SIGTERM");
10625
- } catch {
10626
- }
10666
+ signalProcessTree(entry.child, "SIGTERM");
10627
10667
  }
10628
10668
  }
10629
10669
  // ── TTL ───────────────────────────────────────────────────────────────────
@@ -10668,15 +10708,9 @@ class ProcessSupervisor {
10668
10708
  };
10669
10709
  child.once("exit", done);
10670
10710
  child.once("close", done);
10671
- try {
10672
- child.kill("SIGTERM");
10673
- } catch {
10674
- }
10711
+ signalProcessTree(child, "SIGTERM");
10675
10712
  forceKillTimer = setTimeout(() => {
10676
- try {
10677
- child.kill("SIGKILL");
10678
- } catch {
10679
- }
10713
+ signalProcessTree(child, "SIGKILL");
10680
10714
  hardDeadlineTimer = setTimeout(() => {
10681
10715
  if (!resolved) {
10682
10716
  resolved = true;
@@ -11379,6 +11413,9 @@ function fireIdleWorkflows(root, sessionId, deps = {}) {
11379
11413
  }
11380
11414
 
11381
11415
  const UNBOUNDED_LOOP_ITERATIONS = 1e6;
11416
+ function loopCancelledDuringVerify(current) {
11417
+ return !!current && (current.phase === "cancelled" || current.active === false);
11418
+ }
11382
11419
  function backlogOraclePending(projectRoot, sessionId) {
11383
11420
  const inScope = (i) => !sessionId || isVisibleTo(i, sessionId);
11384
11421
  return listIssues(projectRoot).filter(inScope).filter((i) => i.status === "ready" || i.status === "in_progress").map((i) => ({ id: i.id }));
@@ -11505,8 +11542,55 @@ function computeSoftTurnCap(hardMax, cadence) {
11505
11542
  const chunks = Math.ceil(hardMax / c);
11506
11543
  return Math.ceil(hardMax / chunks);
11507
11544
  }
11545
+ const DEFAULT_LOOP_MAX_EXTENSIONS = 3;
11546
+ const DEFAULT_LOOP_EXTENSION_TURNS = 25;
11547
+ function resolveMaxExtensions(envValue) {
11548
+ const n = Number(envValue);
11549
+ if (Number.isFinite(n) && n >= 0) return Math.floor(n);
11550
+ return DEFAULT_LOOP_MAX_EXTENSIONS;
11551
+ }
11552
+ function resolveExtensionTurns(envValue) {
11553
+ const n = Number(envValue);
11554
+ if (Number.isFinite(n) && n >= 1) return Math.floor(n);
11555
+ return DEFAULT_LOOP_EXTENSION_TURNS;
11556
+ }
11557
+ function grantedExtensionCount(extensions, maxExtensions = DEFAULT_LOOP_MAX_EXTENSIONS) {
11558
+ if (!Array.isArray(extensions)) return 0;
11559
+ const cap = Number.isFinite(maxExtensions) && maxExtensions >= 0 ? Math.floor(maxExtensions) : DEFAULT_LOOP_MAX_EXTENSIONS;
11560
+ return Math.min(extensions.filter((e) => e && typeof e === "object").length, cap);
11561
+ }
11562
+ function extensionTurns(extensions, maxExtensions = DEFAULT_LOOP_MAX_EXTENSIONS) {
11563
+ if (!Array.isArray(extensions)) return 0;
11564
+ const cap = Number.isFinite(maxExtensions) && maxExtensions >= 0 ? Math.floor(maxExtensions) : DEFAULT_LOOP_MAX_EXTENSIONS;
11565
+ return extensions.slice(0, cap).reduce((s, e) => {
11566
+ const g = e && Number.isFinite(e.grantedTurns) && e.grantedTurns > 0 ? Math.floor(e.grantedTurns) : 0;
11567
+ return s + g;
11568
+ }, 0);
11569
+ }
11570
+ function effectiveSoftCap(baseMax, extensions, maxExtensions = DEFAULT_LOOP_MAX_EXTENSIONS) {
11571
+ if (typeof baseMax !== "number" || !Number.isFinite(baseMax) || baseMax <= 0 || baseMax >= UNBOUNDED_LOOP_ITERATIONS) return baseMax;
11572
+ return baseMax + extensionTurns(extensions, maxExtensions);
11573
+ }
11574
+ function grantLoopExtension(prior, o) {
11575
+ const max = Number.isFinite(o.maxExtensions) && o.maxExtensions >= 0 ? Math.floor(o.maxExtensions) : DEFAULT_LOOP_MAX_EXTENSIONS;
11576
+ const base = Array.isArray(prior) ? prior.filter((e) => e && typeof e === "object") : [];
11577
+ if (base.length >= max) return { granted: false, used: base.length, max };
11578
+ const extension = {
11579
+ reason: (o.reason || "").trim().slice(0, 500) || "(no reason given)",
11580
+ ts: o.ts,
11581
+ grantedTurns: Number.isFinite(o.grantedTurns) && o.grantedTurns > 0 ? Math.floor(o.grantedTurns) : DEFAULT_LOOP_EXTENSION_TURNS
11582
+ };
11583
+ return { granted: true, extension, extensions: [...base, extension], used: base.length + 1, max };
11584
+ }
11585
+ function summarizeExtensions(extensions, maxExtensions = DEFAULT_LOOP_MAX_EXTENSIONS) {
11586
+ const n = grantedExtensionCount(extensions, maxExtensions);
11587
+ if (n === 0) return "";
11588
+ const turns = extensionTurns(extensions, maxExtensions);
11589
+ const reasons = extensions.slice(0, maxExtensions).map((e) => (e.reason || "").trim()).filter(Boolean).join("; ").slice(0, 300);
11590
+ return `${n} extension${n === 1 ? "" : "s"} granted (+${turns} turns)${reasons ? `: ${reasons}` : ""}`;
11591
+ }
11508
11592
  const DEFAULT_LOOP_STUCK_CHECKPOINTS = 3;
11509
- const DEFAULT_LOOP_MAX_AUTORESUMES = 3;
11593
+ const DEFAULT_LOOP_MAX_AUTORESUMES = Number.POSITIVE_INFINITY;
11510
11594
  const PROGRESS_HISTORY_CAP = 12;
11511
11595
  function resolveStuckLimit(envValue) {
11512
11596
  const n = Number(envValue);
@@ -11551,7 +11635,8 @@ function decideLoopContinuation(input) {
11551
11635
  return { action: "continue", kind: "progress", consumeAutoResume: false, reason: `still making progress (${stuckCheckpoints}/${stuckLimit} stuck checkpoints)` };
11552
11636
  }
11553
11637
  if (autoResumes < maxAutoResumes) {
11554
- return { action: "continue", kind: "autoresume", consumeAutoResume: true, reason: `no progress across ${stuckCheckpoints} checkpoints \u2014 auto-resume ${autoResumes + 1}/${maxAutoResumes}` };
11638
+ const label = Number.isFinite(maxAutoResumes) ? `auto-resume ${autoResumes + 1}/${maxAutoResumes}` : `auto-resume ${autoResumes + 1} (unbounded \u2014 bounded only by the cost/iteration ceiling)`;
11639
+ return { action: "continue", kind: "autoresume", consumeAutoResume: true, reason: `no progress across ${stuckCheckpoints} checkpoints \u2014 ${label}` };
11555
11640
  }
11556
11641
  const tail = `no progress across ${stuckCheckpoints} checkpoints; ${maxAutoResumes} auto-resume(s) exhausted`;
11557
11642
  return { action: "stop", kind: "stuck", reason: input.stuckReason ? `${input.stuckReason} \u2014 ${tail}` : tail };
@@ -12523,9 +12608,17 @@ function classifyOptsForMessage(sessionId, message, ownerEmail) {
12523
12608
  const fromVal = message.from || "";
12524
12609
  const senderEmail = message.verified && fromVal.includes("@") ? fromVal.replace(/^user:/, "").toLowerCase() : void 0;
12525
12610
  const self = !!(ownerEmail && senderEmail && senderEmail === String(ownerEmail).toLowerCase());
12526
- const openThreadReply = takeAwaitingReply(sessionId, message.threadId);
12611
+ const openThreadReply = peekAwaitingReply(sessionId, message.threadId);
12527
12612
  return { self, openThreadReply };
12528
12613
  }
12614
+ function classifyInboundForSession(sessionId, message, ownerEmail) {
12615
+ const opts = classifyOptsForMessage(sessionId, message, ownerEmail);
12616
+ const decision = classifyInbound(message, Date.now(), opts);
12617
+ if (opts.openThreadReply && decision.action === "wake") {
12618
+ consumeAwaitingReply(sessionId, message.threadId);
12619
+ }
12620
+ return decision;
12621
+ }
12529
12622
  function writeGoalLoopState(directory, sessionId, state) {
12530
12623
  try {
12531
12624
  const dir = getLoopDir(directory, sessionId);
@@ -12870,8 +12963,45 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
12870
12963
  }
12871
12964
  const task = cfg && typeof cfg.task === "string" && cfg.task.trim() ? cfg.task.trim() : void 0;
12872
12965
  const until = cfg && typeof cfg.until === "string" && cfg.until.trim() ? cfg.until.trim() : cfg && typeof cfg.criteria === "string" && cfg.criteria.trim() ? cfg.criteria.trim() : void 0;
12873
- const onlyLimit = cfg && typeof cfg === "object" && !task && !until && (typeof cfg.max_iterations === "number" || typeof cfg.max_rounds === "number" || cfg.budget);
12874
- if (onlyLimit) {
12966
+ const extendReq = cfg && typeof cfg === "object" && cfg.extend && typeof cfg.extend === "object" ? cfg.extend : null;
12967
+ const onlyLimit = cfg && typeof cfg === "object" && !task && !until && !extendReq && (typeof cfg.max_iterations === "number" || typeof cfg.max_rounds === "number" || cfg.budget);
12968
+ if (extendReq) {
12969
+ const existing = readLoopState(directory, sessionId);
12970
+ if (!existing) {
12971
+ sessionService.pushMessage({ type: "message", message: 'No loop to extend \u2014 start one with: svamp session loop <id> "<task>" --until "<criteria>" --max N', level: "warning" }, "event");
12972
+ } else {
12973
+ const maxExtensions = resolveMaxExtensions(process.env.SVAMP_LOOP_MAX_EXTENSIONS);
12974
+ const grantTurns = typeof extendReq.grantedTurns === "number" && extendReq.grantedTurns > 0 ? Math.floor(extendReq.grantedTurns) : resolveExtensionTurns(process.env.SVAMP_LOOP_EXTENSION_TURNS);
12975
+ const grant = grantLoopExtension(existing.extensions, {
12976
+ reason: typeof extendReq.reason === "string" ? extendReq.reason : "",
12977
+ ts: Date.now(),
12978
+ grantedTurns: grantTurns,
12979
+ maxExtensions
12980
+ });
12981
+ if (!grant.granted) {
12982
+ sessionService.pushMessage({ type: "message", message: `\u{1F6D1} Extension refused \u2014 this loop already used all ${grant.max} allowed extension${grant.max === 1 ? "" : "s"} (${grant.used}/${grant.max}). A hard cap is a hard cap; do the remaining work or raise the limit manually: svamp session loop-resume ${sessionId} --max <N>`, level: "warning" }, "event");
12983
+ logger.log(`[svampConfig] loop-extend refused (bounded ${grant.used}/${grant.max})`);
12984
+ } else {
12985
+ const wasStopped = existing.active === false || existing.phase === "done" || existing.phase === "gave_up" || existing.phase === "cancelled";
12986
+ try {
12987
+ const sp = join$1(getLoopDir(directory, sessionId), "loop-state.json");
12988
+ atomicWriteLoopState(sp, {
12989
+ ...existing,
12990
+ extensions: grant.extensions,
12991
+ ...wasStopped ? { active: true, phase: "continue", iteration: 0, completed_at: void 0, gave_up_reason: void 0, stall_hinted: false, resumed_at: Date.now() } : {}
12992
+ });
12993
+ } catch {
12994
+ }
12995
+ if (wasStopped) {
12996
+ const eq = getMetadata().messageQueue || [];
12997
+ setMetadata((m) => ({ ...m, messageQueue: [...eq, { id: randomUUID$1(), text: loopResumeMessage(directory, sessionId), displayText: "\u{1F501} Resuming loop (extended)", createdAt: Date.now() }] }));
12998
+ onLoopActivated?.();
12999
+ }
13000
+ sessionService.pushMessage({ type: "message", message: `\u{1F501} Loop extended (+${grant.extension.grantedTurns} turns, ${grant.used}/${grant.max} used)${wasStopped ? " \u2014 resuming" : ""}. Reason: ${grant.extension.reason}`, level: "warning" }, "event");
13001
+ logger.log(`[svampConfig] loop-extend granted (+${grant.extension.grantedTurns}, ${grant.used}/${grant.max}, resumed=${wasStopped}): ${grant.extension.reason}`);
13002
+ }
13003
+ }
13004
+ } else if (onlyLimit) {
12875
13005
  const existing = readLoopState(directory, sessionId);
12876
13006
  if (!existing) {
12877
13007
  sessionService.pushMessage({ type: "message", message: 'No loop to modify \u2014 start one with: svamp session loop <id> "<task>" --until "<criteria>" --max N', level: "warning" }, "event");
@@ -13403,7 +13533,7 @@ async function startDaemon(options) {
13403
13533
  try {
13404
13534
  const dir = loadSessionIndex()[sessionId]?.directory;
13405
13535
  if (!dir) return;
13406
- const { reconcileServiceLinks } = await import('./agentCommands-BfK_xRcZ.mjs');
13536
+ const { reconcileServiceLinks } = await import('./agentCommands-Dtd2mICX.mjs');
13407
13537
  const configPath = getSvampConfigPath(dir, sessionId);
13408
13538
  const config = readSvampConfig(configPath);
13409
13539
  const entries = Array.from(urls.entries());
@@ -13421,7 +13551,7 @@ async function startDaemon(options) {
13421
13551
  }
13422
13552
  }
13423
13553
  async function createExposedTunnel(spec) {
13424
- const { FrpcTunnel } = await import('./frpc-DoUUUoZn.mjs');
13554
+ const { FrpcTunnel } = await import('./frpc-BBkDE65f.mjs');
13425
13555
  const tunnel = new FrpcTunnel({
13426
13556
  name: spec.name,
13427
13557
  ports: spec.ports,
@@ -13441,14 +13571,14 @@ async function startDaemon(options) {
13441
13571
  return tunnel;
13442
13572
  }
13443
13573
  const tunnelRecreateState = /* @__PURE__ */ new Map();
13444
- const { ServeManager } = await import('./serveManager-DAuIjPuV.mjs');
13574
+ const { ServeManager } = await import('./serveManager-BBRgdCJp.mjs');
13445
13575
  const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
13446
13576
  ensureAutoInstalledSkills(logger).catch(() => {
13447
13577
  });
13448
13578
  ensureAutoInstalledCommands(logger);
13449
13579
  (async () => {
13450
13580
  try {
13451
- const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-ivV7xAp3.mjs');
13581
+ const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-DuLXaoGP.mjs');
13452
13582
  beginClaudeVersionReconcile((msg) => logger.log(msg));
13453
13583
  } catch (e) {
13454
13584
  logger.log(`[claude-version] check failed: ${e?.message || e}`);
@@ -14119,9 +14249,14 @@ ${parts.join("\n")}`);
14119
14249
  log: (m) => logger.log(`[Session ${sessionId}] ${m}`)
14120
14250
  }
14121
14251
  );
14252
+ if (loopCancelledDuringVerify(readLoopState(directory, sessionId))) {
14253
+ logger.log(`[Session ${sessionId}] loop was cancelled during verification \u2014 not re-kicking (#0308)`);
14254
+ return;
14255
+ }
14122
14256
  const ledger = ls.ledger;
14123
14257
  const budget = ls.budget;
14124
14258
  const progress_history = ls.progress_history;
14259
+ const extensions = ls.extensions;
14125
14260
  if (result.action === "rekick") {
14126
14261
  const cond = ls.task || "";
14127
14262
  writeGoalLoopState(directory, sessionId, {
@@ -14138,7 +14273,8 @@ ${parts.join("\n")}`);
14138
14273
  holds: holds + 1,
14139
14274
  ledger,
14140
14275
  progress_history,
14141
- auto_resumes: ls.auto_resumes
14276
+ auto_resumes: ls.auto_resumes,
14277
+ extensions
14142
14278
  });
14143
14279
  const guidance = result.guidance.replace(/\s+/g, " ").slice(0, 1200);
14144
14280
  sessionService.pushMessage({ type: "message", message: `\u{1F501} Loop not done \u2014 independent review: ${result.reason}`, level: "warning" }, "event");
@@ -14150,7 +14286,9 @@ ${parts.join("\n")}`);
14150
14286
  progressHistory: progress_history || [],
14151
14287
  stuckLimit: resolveStuckLimit(process.env.SVAMP_LOOP_STUCK_CHECKPOINTS),
14152
14288
  autoResumes: typeof ls.auto_resumes === "number" ? ls.auto_resumes : 0,
14153
- maxAutoResumes: resolveMaxAutoResumes(process.env.SVAMP_LOOP_MAX_AUTORESUMES),
14289
+ // #0320: extensions also raise a FINITE auto-resume cap (one extra resume each). With
14290
+ // the unbounded #0319 default this is a no-op (Infinity + n = Infinity).
14291
+ maxAutoResumes: resolveMaxAutoResumes(process.env.SVAMP_LOOP_MAX_AUTORESUMES) + grantedExtensionCount(ls.extensions, resolveMaxExtensions(process.env.SVAMP_LOOP_MAX_EXTENSIONS)),
14154
14292
  stuckReason: result.reason
14155
14293
  });
14156
14294
  if (decision.action === "continue") {
@@ -14167,10 +14305,13 @@ ${parts.join("\n")}`);
14167
14305
  max_iterations: ls.max_iterations,
14168
14306
  budget,
14169
14307
  started_at: startedAt,
14170
- holds,
14308
+ // #0319: a PROGRESSING loop resets its hold counter so the evaluator-guided re-kick
14309
+ // mechanism re-engages fresh (it's making headway, not stuck); an auto-resume keeps it.
14310
+ holds: decision.kind === "progress" ? 0 : holds,
14171
14311
  ledger,
14172
14312
  progress_history,
14173
- auto_resumes: nextAuto
14313
+ auto_resumes: nextAuto,
14314
+ extensions
14174
14315
  });
14175
14316
  const label = decision.kind === "autoresume" ? "\u{1F501} Auto-resuming stalled loop" : "\u{1F501} Loop still progressing";
14176
14317
  sessionService.pushMessage({ type: "message", message: `${label} \u2014 ${decision.reason}` }, "event");
@@ -14179,12 +14320,12 @@ ${parts.join("\n")}`);
14179
14320
  enqueueLoopMessage(push, label);
14180
14321
  if (!trackedSession.stopped) setTimeout(() => processMessageQueueRef?.(), 200);
14181
14322
  } else {
14182
- writeGoalLoopState(directory, sessionId, { active: false, phase: "gave_up", completed_at: Date.now(), gave_up_reason: decision.reason, holds, ledger, progress_history, auto_resumes: ls.auto_resumes });
14323
+ writeGoalLoopState(directory, sessionId, { active: false, phase: "gave_up", completed_at: Date.now(), gave_up_reason: decision.reason, holds, ledger, progress_history, auto_resumes: ls.auto_resumes, extensions });
14183
14324
  logger.log(`[Session ${sessionId}] [loop-persist] stuck-stop: ${decision.reason}`);
14184
14325
  checkSvampConfig?.();
14185
14326
  }
14186
14327
  } else {
14187
- writeGoalLoopState(directory, sessionId, { active: false, phase: "done", completed_at: Date.now(), holds, ledger, progress_history, auto_resumes: ls.auto_resumes });
14328
+ writeGoalLoopState(directory, sessionId, { active: false, phase: "done", completed_at: Date.now(), holds, ledger, progress_history, auto_resumes: ls.auto_resumes, extensions });
14188
14329
  checkSvampConfig?.();
14189
14330
  }
14190
14331
  } catch (e) {
@@ -14653,7 +14794,9 @@ ${parts.join("\n")}`);
14653
14794
  });
14654
14795
  ls.ledger = ledger;
14655
14796
  const startedAt = typeof ls.started_at === "number" ? ls.started_at : typeof ls.resumed_at === "number" ? ls.resumed_at : void 0;
14656
- const budgetCheck = process.env.SVAMP_LOOP_BUDGET === "0" ? { exceeded: false, kind: void 0 } : checkLoopBudget(ledger, { max_iterations: ls.max_iterations, ...ls.budget || {} }, now, startedAt);
14797
+ const maxExt = resolveMaxExtensions(process.env.SVAMP_LOOP_MAX_EXTENSIONS);
14798
+ const effMaxIters = effectiveSoftCap(ls.max_iterations, ls.extensions, maxExt);
14799
+ const budgetCheck = process.env.SVAMP_LOOP_BUDGET === "0" ? { exceeded: false, kind: void 0 } : checkLoopBudget(ledger, { max_iterations: effMaxIters, ...ls.budget || {} }, now, startedAt);
14657
14800
  const pending = safeBacklogPendingCount(resolveProjectRoot(directory), sessionId);
14658
14801
  const prog = updateLoopProgress(ls, pending, now);
14659
14802
  ls.progress_history = prog.progress_history;
@@ -14670,7 +14813,9 @@ ${parts.join("\n")}`);
14670
14813
  progress_history: prog.progress_history,
14671
14814
  auto_resumes: prog.auto_resumes
14672
14815
  });
14673
- const detail = iterStop ? `${budgetCheck.reason} (hard iteration ceiling \u2014 will not auto-resume). Resume & extend with: svamp session loop-resume ${sessionId} --max <N>` : `${budgetCheck.reason} (hard cost ceiling \u2014 will not auto-resume). Resume & extend with: svamp session loop-resume ${sessionId} --max <N>`;
14816
+ const extNote = summarizeExtensions(ls.extensions, maxExt);
14817
+ const resumeHint = iterStop ? `Extend with a reason: svamp session loop-extend ${sessionId} --reason "<why>" \xB7 or set a new cap: svamp session loop-resume ${sessionId} --max <N>` : `Resume & extend with: svamp session loop-resume ${sessionId} --max <N>`;
14818
+ const detail = `${budgetCheck.reason} (hard ${iterStop ? "iteration ceiling" : "cost ceiling"} \u2014 will not auto-resume)${extNote ? ` [${extNote}]` : ""}. ${resumeHint}`;
14674
14819
  sessionService.pushMessage({ type: "message", message: `\u{1F6D1} Loop stopped \u2014 ${detail}`, level: "warning" }, "event");
14675
14820
  checkSvampConfig?.();
14676
14821
  } else if (process.env.SVAMP_LOOP_VERIFY === "0") {
@@ -15433,7 +15578,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
15433
15578
  },
15434
15579
  onInboxMessage: (message) => {
15435
15580
  if (trackedSession?.stopped) return;
15436
- const decision = classifyInbound(message, Date.now(), classifyOptsForMessage(sessionId, message, sessionMetadata?.sharing?.owner));
15581
+ const decision = classifyInboundForSession(sessionId, message, sessionMetadata?.sharing?.owner);
15437
15582
  logger.log(`[Session ${sessionId}] Inbox message (urgency: ${message.urgency || "normal"}, hop: ${message.hopCount ?? 0}, from: ${message.from || "unknown"}) \u2192 ${decision.action} (${decision.reason})`);
15438
15583
  if (decision.breakerTripped) {
15439
15584
  logger.error(`[Session ${sessionId}] Inbox circuit breaker TRIPPED \u2014 cross-agent wake rate too high; auto-waking paused.`);
@@ -15460,6 +15605,19 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
15460
15605
  }
15461
15606
  signalProcessing(true);
15462
15607
  sessionWasProcessing = true;
15608
+ if (claudeResumeId && !trackedSession?.stopped) {
15609
+ saveSession({
15610
+ sessionId,
15611
+ directory,
15612
+ claudeResumeId,
15613
+ permissionMode: currentPermissionMode,
15614
+ spawnMeta: lastSpawnMeta,
15615
+ metadata: sessionMetadata,
15616
+ createdAt: sessionCreatedAt,
15617
+ machineId,
15618
+ wasProcessing: true
15619
+ });
15620
+ }
15463
15621
  } else {
15464
15622
  logger.log(`[Session ${sessionId}] Queuing inbox message for idle processing`);
15465
15623
  const existingQueue = sessionMetadata.messageQueue || [];
@@ -15529,11 +15687,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
15529
15687
  });
15530
15688
  },
15531
15689
  onIssue: async (params) => {
15532
- const { issueRpc } = await import('./rpc-_rwZwiHD.mjs');
15690
+ const { issueRpc } = await import('./rpc-CMS1F7Qu.mjs');
15533
15691
  return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
15534
15692
  },
15535
15693
  onWorkflow: async (params) => {
15536
- const { workflowRpc } = await import('./rpc-665-sPRQ.mjs');
15694
+ const { workflowRpc } = await import('./rpc-DTU-aE0V.mjs');
15537
15695
  return workflowRpc(params?.cwd || directory, params || {});
15538
15696
  },
15539
15697
  onRipgrep: async (args, cwd) => {
@@ -15672,6 +15830,19 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
15672
15830
  message: { role: "user", content: next.text }
15673
15831
  });
15674
15832
  claudeProcess.stdin?.write(stdinMsg + "\n");
15833
+ if (claudeResumeId && !trackedSession?.stopped) {
15834
+ saveSession({
15835
+ sessionId,
15836
+ directory,
15837
+ claudeResumeId,
15838
+ permissionMode: currentPermissionMode,
15839
+ spawnMeta: lastSpawnMeta,
15840
+ metadata: sessionMetadata,
15841
+ createdAt: sessionCreatedAt,
15842
+ machineId,
15843
+ wasProcessing: true
15844
+ });
15845
+ }
15675
15846
  }
15676
15847
  } catch (err) {
15677
15848
  logger.log(`[Session ${sessionId}] Error in processMessageQueue spawn: ${err.message}`);
@@ -15942,7 +16113,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
15942
16113
  },
15943
16114
  onInboxMessage: (message) => {
15944
16115
  if (acpStopped) return;
15945
- const decision = classifyInbound(message, Date.now(), classifyOptsForMessage(sessionId, message, sessionMetadata?.sharing?.owner));
16116
+ const decision = classifyInboundForSession(sessionId, message, sessionMetadata?.sharing?.owner);
15946
16117
  logger.log(`[${agentName} Session ${sessionId}] Inbox message (urgency: ${message.urgency || "normal"}, hop: ${message.hopCount ?? 0}, from: ${message.from || "unknown"}) \u2192 ${decision.action} (${decision.reason})`);
15947
16118
  if (decision.breakerTripped) {
15948
16119
  logger.error(`[${agentName} Session ${sessionId}] Inbox circuit breaker TRIPPED \u2014 auto-waking paused.`);
@@ -16069,11 +16240,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
16069
16240
  });
16070
16241
  },
16071
16242
  onIssue: async (params) => {
16072
- const { issueRpc } = await import('./rpc-_rwZwiHD.mjs');
16243
+ const { issueRpc } = await import('./rpc-CMS1F7Qu.mjs');
16073
16244
  return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
16074
16245
  },
16075
16246
  onWorkflow: async (params) => {
16076
- const { workflowRpc } = await import('./rpc-665-sPRQ.mjs');
16247
+ const { workflowRpc } = await import('./rpc-DTU-aE0V.mjs');
16077
16248
  return workflowRpc(params?.cwd || directory, params || {});
16078
16249
  },
16079
16250
  onRipgrep: async (args, cwd) => {
@@ -16286,7 +16457,9 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
16286
16457
  return { ok: pending.length === 0, output: pending.length ? `${pending.length} pending: ${pending.map((i) => "#" + i.id).join(" ")}` : "No pending issues." };
16287
16458
  };
16288
16459
  try {
16289
- const budgetCheck = process.env.SVAMP_LOOP_BUDGET === "0" ? { exceeded: false, kind: void 0 } : checkLoopBudget(ledger, { max_iterations: ls.max_iterations, ...ls.budget || {} }, now, startedAt);
16460
+ const acpMaxExt = resolveMaxExtensions(process.env.SVAMP_LOOP_MAX_EXTENSIONS);
16461
+ const acpEffMax = effectiveSoftCap(ls.max_iterations, ls.extensions, acpMaxExt);
16462
+ const budgetCheck = process.env.SVAMP_LOOP_BUDGET === "0" ? { exceeded: false, kind: void 0 } : checkLoopBudget(ledger, { max_iterations: acpEffMax, ...ls.budget || {} }, now, startedAt);
16290
16463
  const acpPending = safeBacklogPendingCount(projectRoot, sessionId);
16291
16464
  const acpProg = updateLoopProgress(ls, acpPending, now);
16292
16465
  ls.progress_history = acpProg.progress_history;
@@ -16294,7 +16467,9 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
16294
16467
  if (budgetCheck.exceeded && isHardBudgetStop(budgetCheck.kind)) {
16295
16468
  const iterStop = !isCostCeiling(budgetCheck.kind);
16296
16469
  writeGoalLoopState(directory, sessionId, { ...ls, active: false, phase: "gave_up", completed_at: now, gave_up_reason: `resource budget exhausted \u2014 ${budgetCheck.reason}`, ledger });
16297
- sessionService.pushMessage({ type: "message", message: `\u{1F6D1} Loop stopped \u2014 ${budgetCheck.reason} (hard ${iterStop ? "iteration ceiling" : "cost ceiling"} \u2014 will not auto-resume). Resume & extend with: svamp session loop-resume ${sessionId} --max <N>`, level: "warning" }, "event");
16470
+ const acpExtNote = summarizeExtensions(ls.extensions, acpMaxExt);
16471
+ const acpResumeHint = iterStop ? `Extend with a reason: svamp session loop-extend ${sessionId} --reason "<why>" \xB7 or set a new cap: svamp session loop-resume ${sessionId} --max <N>` : `Resume & extend with: svamp session loop-resume ${sessionId} --max <N>`;
16472
+ sessionService.pushMessage({ type: "message", message: `\u{1F6D1} Loop stopped \u2014 ${budgetCheck.reason} (hard ${iterStop ? "iteration ceiling" : "cost ceiling"} \u2014 will not auto-resume)${acpExtNote ? ` [${acpExtNote}]` : ""}. ${acpResumeHint}`, level: "warning" }, "event");
16298
16473
  checkSvampConfig?.();
16299
16474
  return;
16300
16475
  }
@@ -16331,7 +16506,8 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
16331
16506
  progressHistory: ls.progress_history || [],
16332
16507
  stuckLimit: resolveStuckLimit(process.env.SVAMP_LOOP_STUCK_CHECKPOINTS),
16333
16508
  autoResumes: typeof ls.auto_resumes === "number" ? ls.auto_resumes : 0,
16334
- maxAutoResumes: resolveMaxAutoResumes(process.env.SVAMP_LOOP_MAX_AUTORESUMES),
16509
+ // #0320: extensions also raise a FINITE auto-resume cap (no-op under the unbounded default).
16510
+ maxAutoResumes: resolveMaxAutoResumes(process.env.SVAMP_LOOP_MAX_AUTORESUMES) + grantedExtensionCount(ls.extensions, acpMaxExt),
16335
16511
  stuckReason: result.reason
16336
16512
  });
16337
16513
  if (decision.action === "continue") {
@@ -16835,7 +17011,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
16835
17011
  }
16836
17012
  if (persistedSessions.length > 0) {
16837
17013
  try {
16838
- const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-ivV7xAp3.mjs');
17014
+ const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-DuLXaoGP.mjs');
16839
17015
  await awaitClaudeVersionReady();
16840
17016
  } catch {
16841
17017
  }
@@ -17032,7 +17208,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
17032
17208
  const PING_TIMEOUT_MS = 15e3;
17033
17209
  const POST_RECONNECT_GRACE_MS = 2e4;
17034
17210
  const RECONNECT_JITTER_MS = 2500;
17035
- const { WorkflowScheduler } = await import('./scheduler-DjMTCoWS.mjs');
17211
+ const { WorkflowScheduler } = await import('./scheduler-B92Z_kox.mjs');
17036
17212
  const workflowScheduler = new WorkflowScheduler({
17037
17213
  projectRoots: () => {
17038
17214
  const dirs = /* @__PURE__ */ new Set();
@@ -1,4 +1,4 @@
1
- import { n as resolveProjectRoot, H as listWorkflows, I as isWorkflowEnabled, J as workflowCrons, C as runWorkflow, K as cronMatches } from './run-BNxaekWa.mjs';
1
+ import { n as resolveProjectRoot, H as listWorkflows, I as isWorkflowEnabled, J as workflowCrons, C as runWorkflow, K as cronMatches } from './run-BqqMOMZN.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-BkroIJOC.mjs');
57
+ const { connectAndGetMachine } = await import('./commands-Bz3xxhDd.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-BkroIJOC.mjs');
96
+ const { connectAndGetMachine } = await import('./commands-Bz3xxhDd.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-BkroIJOC.mjs');
185
+ const { connectAndGetMachine } = await import('./commands-Bz3xxhDd.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-BkroIJOC.mjs');
205
+ const { connectAndGetMachine } = await import('./commands-Bz3xxhDd.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-BkroIJOC.mjs');
238
+ const { connectAndGetMachine } = await import('./commands-Bz3xxhDd.mjs');
239
239
  const { machine, server } = await connectAndGetMachine(machineId);
240
240
  try {
241
241
  const info = await machine.serveInfo();
@@ -4,7 +4,7 @@ import * as fs from 'fs';
4
4
  import * as http from 'http';
5
5
  import * as net from 'net';
6
6
  import * as path from 'path';
7
- import { l as getHyphaServerUrl, S as ServeAuth, m as hasCookieToken } from './run-BNxaekWa.mjs';
7
+ import { l as getHyphaServerUrl, S as ServeAuth, m as hasCookieToken } from './run-BqqMOMZN.mjs';
8
8
  import 'os';
9
9
  import 'fs/promises';
10
10
  import 'url';
@@ -751,7 +751,7 @@ class ServeManager {
751
751
  const mount = this.mounts.get(mountName);
752
752
  const subdomainOverride = mount?.access === "link" && mount.linkToken ? /* @__PURE__ */ new Map([[this.port, buildLinkSubdomain(subdomainSafe, mount.linkToken)]]) : void 0;
753
753
  try {
754
- const { FrpcTunnel } = await import('./frpc-DoUUUoZn.mjs');
754
+ const { FrpcTunnel } = await import('./frpc-BBkDE65f.mjs');
755
755
  let tunnel;
756
756
  tunnel = new FrpcTunnel({
757
757
  name: tunnelName,