svamp-cli 0.2.177 → 0.2.179

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 (24) hide show
  1. package/dist/{agentCommands-DWvXxX8p.mjs → agentCommands-go20pLE-.mjs} +5 -5
  2. package/dist/{auth-Dxka4cE6.mjs → auth-BeRv91cp.mjs} +1 -1
  3. package/dist/cli.mjs +65 -63
  4. package/dist/{commands-Bf-TovMw.mjs → commands-BxyASXjK.mjs} +1 -1
  5. package/dist/{commands-CwdYZQ-a.mjs → commands-CXueSoWn.mjs} +10 -10
  6. package/dist/{commands-DVKFo1pt.mjs → commands-D2LkJbHZ.mjs} +2 -2
  7. package/dist/{commands-iXMwyExn.mjs → commands-DTPTgiQX.mjs} +2 -2
  8. package/dist/{commands-BwBxmMxJ.mjs → commands-DYVY9T-f.mjs} +1 -1
  9. package/dist/{commands--dKRqMv8.mjs → commands-DbaPB6M3.mjs} +5 -5
  10. package/dist/{commands-B4u6CUWH.mjs → commands-j4nqt7yR.mjs} +25 -4
  11. package/dist/{fleet-plROHfB8.mjs → fleet-DgV81okW.mjs} +1 -1
  12. package/dist/{frpc-yc5-4EUe.mjs → frpc-Bi1a1Oqy.mjs} +1 -1
  13. package/dist/{headlessCli-D7vLrl7K.mjs → headlessCli-DjSZYq-Z.mjs} +2 -2
  14. package/dist/index.mjs +1 -1
  15. package/dist/{package-Do-iBoUv.mjs → package-BL1oH1qy.mjs} +1 -1
  16. package/dist/{rpc-DUZqKPK4.mjs → rpc-CAi59ZCd.mjs} +7 -7
  17. package/dist/{rpc-D1gr74eF.mjs → rpc-CND7PIf-.mjs} +3 -2
  18. package/dist/{run-21-bMHCc.mjs → run-CLeF1N9s.mjs} +152 -143
  19. package/dist/{run-C__yViCA.mjs → run-bBeQGLfT.mjs} +1 -1
  20. package/dist/{scheduler-DweevMw_.mjs → scheduler-CsaIxFN7.mjs} +8 -6
  21. package/dist/{serveCommands-BZmiAjxi.mjs → serveCommands-mDL4FmeZ.mjs} +5 -5
  22. package/dist/{serveManager-AbWv-Akt.mjs → serveManager-CqhigbGu.mjs} +2 -2
  23. package/dist/{sideband-CzzHtakf.mjs → sideband-DLku7gcO.mjs} +1 -1
  24. package/package.json +1 -1
@@ -58,7 +58,7 @@ async function serviceExpose(args) {
58
58
  process.exit(1);
59
59
  }
60
60
  if (foreground) {
61
- const { runFrpcTunnel } = await import('./frpc-yc5-4EUe.mjs');
61
+ const { runFrpcTunnel } = await import('./frpc-Bi1a1Oqy.mjs');
62
62
  await runFrpcTunnel(name, ports, void 0, {
63
63
  group,
64
64
  groupKey,
@@ -68,7 +68,7 @@ async function serviceExpose(args) {
68
68
  });
69
69
  return;
70
70
  }
71
- const { connectAndGetMachine } = await import('./commands-B4u6CUWH.mjs');
71
+ const { connectAndGetMachine } = await import('./commands-j4nqt7yR.mjs');
72
72
  const { server, machine } = await connectAndGetMachine();
73
73
  try {
74
74
  const status = await machine.tunnelStart({
@@ -123,7 +123,7 @@ async function serviceServe(args) {
123
123
  };
124
124
  process.on("SIGINT", cleanup);
125
125
  process.on("SIGTERM", cleanup);
126
- const { runFrpcTunnel } = await import('./frpc-yc5-4EUe.mjs');
126
+ const { runFrpcTunnel } = await import('./frpc-Bi1a1Oqy.mjs');
127
127
  await runFrpcTunnel(name, [caddyPort]);
128
128
  } catch (err) {
129
129
  console.error(`Error serving directory: ${err.message}`);
@@ -132,7 +132,7 @@ async function serviceServe(args) {
132
132
  }
133
133
  async function serviceList(_args) {
134
134
  try {
135
- const { connectAndGetMachine } = await import('./commands-B4u6CUWH.mjs');
135
+ const { connectAndGetMachine } = await import('./commands-j4nqt7yR.mjs');
136
136
  const { server, machine } = await connectAndGetMachine();
137
137
  try {
138
138
  const tunnels = await machine.tunnelList({});
@@ -172,7 +172,7 @@ async function serviceDelete(args) {
172
172
  process.exit(1);
173
173
  }
174
174
  try {
175
- const { connectAndGetMachine } = await import('./commands-B4u6CUWH.mjs');
175
+ const { connectAndGetMachine } = await import('./commands-j4nqt7yR.mjs');
176
176
  const { server, machine } = await connectAndGetMachine();
177
177
  try {
178
178
  await machine.tunnelStop({ name });
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { execSync } from 'node:child_process';
3
3
  import { basename, resolve, join, isAbsolute } from 'node:path';
4
4
  import os from 'node:os';
5
- import { N as formatHandle, O as normalizeAllowedUser, P as loadSecurityContextConfig, Q as resolveSecurityContext, T as buildSecurityContextFromFlags, U as mergeSecurityContexts, c as connectToHypha, V as buildSessionShareUrl, W as computeOutboundHop, x as shortId, X as buildMachineShareUrl, Y as parseHandle, Z as handleMatchesMetadata } from './run-21-bMHCc.mjs';
5
+ import { N as formatHandle, O as normalizeAllowedUser, P as loadSecurityContextConfig, Q as resolveSecurityContext, T as buildSecurityContextFromFlags, U as mergeSecurityContexts, c as connectToHypha, V as buildSessionShareUrl, W as computeOutboundHop, x as shortId, X as buildMachineShareUrl, Y as parseHandle, Z as handleMatchesMetadata } from './run-CLeF1N9s.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
8
8
  import 'fs';
@@ -569,6 +569,15 @@ function resolveSessionId(sessions, partial) {
569
569
  process.exit(1);
570
570
  }
571
571
  async function connectAndResolveSession(sessionId, machineId) {
572
+ if (sessionId === ".") {
573
+ const self = process.env.SVAMP_SESSION_ID;
574
+ if (!self) {
575
+ console.error("'.' means the current session, but SVAMP_SESSION_ID is not set here.");
576
+ console.error('In a workflow step, give the workflow an owner session (so the scheduler can resolve "."), or use an explicit session id/handle.');
577
+ process.exit(1);
578
+ }
579
+ sessionId = self;
580
+ }
572
581
  let { server, machine } = await connectAndGetMachine(machineId);
573
582
  const sessions = await machine.listSessions();
574
583
  const localMatch = findSessionMatch(sessions, sessionId);
@@ -1655,6 +1664,9 @@ async function collectAssistantResponse(machine, fullId, afterSeq) {
1655
1664
  if (collected.length) return { text: collected.join("\n").trim(), messageSeqs: seqs };
1656
1665
  return { text: resultText.join("\n").trim(), messageSeqs: resultSeqs };
1657
1666
  }
1667
+ function isSelfBtw(fullId, btw, selfSessionId) {
1668
+ return !!btw && !!selfSessionId && fullId === selfSessionId;
1669
+ }
1658
1670
  function validateSendOptions(opts) {
1659
1671
  if (opts?.plain) {
1660
1672
  const bad = [];
@@ -1844,6 +1856,14 @@ async function sessionSend(sessionId, message, machineId, opts) {
1844
1856
  process.exit(1);
1845
1857
  }
1846
1858
  const { server, machine, fullId } = await connectAndResolveSession(sessionId, machineId);
1859
+ if (isSelfBtw(fullId, opts?.btw, process.env.SVAMP_SESSION_ID)) {
1860
+ console.error("Refusing to --btw your own session: a /btw forks a query that resumes this session, which would deadlock the active turn (and the watchdog would kill it). You are already inside this session \u2014 just answer/think directly, or send to a DIFFERENT session.");
1861
+ try {
1862
+ await server.disconnect();
1863
+ } catch {
1864
+ }
1865
+ process.exit(1);
1866
+ }
1847
1867
  try {
1848
1868
  const r = await sendCore(machine, fullId, message, opts);
1849
1869
  if (r.mode === "btw") {
@@ -2690,11 +2710,12 @@ async function sessionInboxReply(sessionIdPartial, messageId, body, machineId) {
2690
2710
  async function sessionInboxClear(sessionIdPartial, machineId, opts) {
2691
2711
  const { server, machine, fullId } = await connectAndResolveSession(sessionIdPartial, machineId);
2692
2712
  try {
2693
- const result = await machine.sessionRPC(fullId, "clearInbox", { opts: { all: opts?.all } });
2694
- console.log(`Cleared inbox on session ${fullId.slice(0, 8)} (${result.remaining} remaining)`);
2713
+ const result = await machine.sessionRPC(fullId, "clearInbox", { opts: { all: opts?.all, messageId: opts?.messageId } });
2714
+ const what = opts?.messageId ? `message ${opts.messageId}` : opts?.all ? "all messages" : "read messages";
2715
+ console.log(`Cleared ${what} on session ${fullId.slice(0, 8)} (removed ${result.removed ?? "?"}, ${result.remaining} remaining)`);
2695
2716
  } finally {
2696
2717
  await server.disconnect();
2697
2718
  }
2698
2719
  }
2699
2720
 
2700
- export { collectAssistantResponse, connectAndGetMachine, connectAndResolveSession, createWorktree, generateWorktreeName, machineExec, machineInfo, machineLs, machineShare, parseShareArg, queryCore, renderMessage, resolveSessionId, sendCore, sessionApprove, sessionArchive, sessionAttach, sessionDelete, sessionDeny, sessionEditMessage, sessionInboxClear, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxSend, sessionInfo, sessionList, sessionLoopCancel, sessionLoopStart, sessionLoopStatus, sessionMachines, sessionMessages, sessionQuery, sessionRefineLastReply, sessionResume, sessionSend, sessionShare, sessionSpawn, sessionUndoEdit, sessionWait, sessionWhoami, snapshotLatestSeq, validateSendOptions, wiseAnnounceCli, wiseAskCli, wiseJoinMeetingCli, wiseLeaveMeetingCli, wiseMeetingsCli };
2721
+ export { collectAssistantResponse, connectAndGetMachine, connectAndResolveSession, createWorktree, generateWorktreeName, isSelfBtw, machineExec, machineInfo, machineLs, machineShare, parseShareArg, queryCore, renderMessage, resolveSessionId, sendCore, sessionApprove, sessionArchive, sessionAttach, sessionDelete, sessionDeny, sessionEditMessage, sessionInboxClear, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxSend, sessionInfo, sessionList, sessionLoopCancel, sessionLoopStart, sessionLoopStatus, sessionMachines, sessionMessages, sessionQuery, sessionRefineLastReply, sessionResume, sessionSend, sessionShare, sessionSpawn, sessionUndoEdit, sessionWait, sessionWhoami, snapshotLatestSeq, validateSendOptions, wiseAnnounceCli, wiseAskCli, wiseJoinMeetingCli, wiseLeaveMeetingCli, wiseMeetingsCli };
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import os from 'node:os';
4
- import { c as connectToHypha } from './run-21-bMHCc.mjs';
4
+ import { c as connectToHypha } from './run-CLeF1N9s.mjs';
5
5
  import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-HydRNEt7.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
@@ -4,7 +4,7 @@ import { mkdirSync, writeFileSync, unlinkSync, existsSync, chmodSync, readFileSy
4
4
  import { join } from 'path';
5
5
  import { homedir, platform, arch } from 'os';
6
6
  import { randomUUID, createHash } from 'crypto';
7
- import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-21-bMHCc.mjs';
7
+ import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-CLeF1N9s.mjs';
8
8
  import 'fs/promises';
9
9
  import 'url';
10
10
  import 'node:crypto';
@@ -1,5 +1,5 @@
1
- import { M as resolveModel, _ as describeMisconfiguration, $ as buildMachineDeps } from './run-21-bMHCc.mjs';
2
- import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-CzzHtakf.mjs';
1
+ import { M as resolveModel, _ as describeMisconfiguration, $ as buildMachineDeps } from './run-CLeF1N9s.mjs';
2
+ import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-DLku7gcO.mjs';
3
3
  import { WebSocket } from 'ws';
4
4
  import { execSync, spawn } from 'child_process';
5
5
  import 'os';
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-21-bMHCc.mjs';
1
+ export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-CLeF1N9s.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -1,5 +1,5 @@
1
1
  var name = "svamp-cli";
2
- var version = "0.2.177";
2
+ var version = "0.2.179";
3
3
  var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
4
4
  var author = "Amun AI AB";
5
5
  var license = "SEE LICENSE IN LICENSE";
@@ -1,4 +1,4 @@
1
- import { m as resolveProjectRoot, u as updateIssue, n as getIssue, o as addComment, p as addIssue, q as listIssues, t as searchIssues } from './run-21-bMHCc.mjs';
1
+ import { m as resolveProjectRoot, u as updateIssue, n as getIssue, o as addComment, p as addIssue, q as listIssues, t as searchIssues } from './run-CLeF1N9s.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -39,11 +39,11 @@ function ownerScoped(items, session) {
39
39
  const STATUS_MAP = {
40
40
  ready: "ready",
41
41
  start: "in_progress",
42
- close: "done",
43
- done: "done",
42
+ close: "archived",
43
+ done: "archived",
44
44
  reopen: "ready",
45
45
  archive: "archived",
46
- backlog: "backlog"
46
+ backlog: "ready"
47
47
  };
48
48
  function issueRpc(cwd, params) {
49
49
  const root = resolveProjectRoot(cwd || process.cwd());
@@ -61,7 +61,7 @@ function issueRpc(cwd, params) {
61
61
  case "search":
62
62
  return ownerScoped(searchIssues(root, String(params.query || "")), params.session).map(compact);
63
63
  case "pending": {
64
- const items = ownerScoped(listIssues(root), params.session).filter((i) => i.status === "ready" || i.status === "in_progress" || i.status === "backlog" && !i.triaged);
64
+ const items = ownerScoped(listIssues(root), params.session).filter((i) => i.status === "ready" || i.status === "in_progress");
65
65
  return { pending: items.map((i) => i.id), count: items.length };
66
66
  }
67
67
  case "add": {
@@ -80,7 +80,7 @@ function issueRpc(cwd, params) {
80
80
  }
81
81
  return addIssue(root, {
82
82
  title,
83
- status: params.ready ? "ready" : "backlog",
83
+ status: "ready",
84
84
  scope,
85
85
  labels: Array.isArray(params.labels) ? params.labels.map(String) : [],
86
86
  verify: buildVerify(params) ?? null,
@@ -138,7 +138,7 @@ function issueRpc(cwd, params) {
138
138
  const v = buildVerify(params);
139
139
  if (v) patch.verify = v;
140
140
  if (params.body != null) patch.body = String(params.body);
141
- patch.status = params.backlog ? "backlog" : "ready";
141
+ patch.status = "ready";
142
142
  const updated = updateIssue(root, String(params.id), patch);
143
143
  if (!updated) throw new Error(`Issue not found: ${params.id}`);
144
144
  return updated;
@@ -1,5 +1,5 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import { m as resolveProjectRoot } from './run-21-bMHCc.mjs';
2
+ import { m as resolveProjectRoot } from './run-CLeF1N9s.mjs';
3
3
  import { g as getWorkflow, w as workflowSteps, r as removeWorkflow, s as saveWorkflow, a as rawWorkflow, l as listWorkflows } from './store-BPL__e4D.mjs';
4
4
  import 'os';
5
5
  import 'fs/promises';
@@ -57,11 +57,12 @@ function workflowRpc(cwd, params) {
57
57
  const wf = getWorkflow(root, String(params.name));
58
58
  if (!wf) throw new Error(`Workflow not found: ${params.name}`);
59
59
  const steps = workflowSteps(wf);
60
+ const stepEnv = wf.session ? { ...process.env, SVAMP_SESSION_ID: wf.session } : process.env;
60
61
  const out = [`\u25B6 running workflow "${wf.name}" (${steps.length} step${steps.length === 1 ? "" : "s"})`];
61
62
  for (let i = 0; i < steps.length; i++) {
62
63
  const step = steps[i];
63
64
  out.push(` [${i + 1}/${steps.length}] $ ${step.run}`);
64
- const r = spawnSync("sh", ["-c", step.run], { cwd: root, encoding: "utf-8", timeout: 12e4 });
65
+ const r = spawnSync("sh", ["-c", step.run], { cwd: root, encoding: "utf-8", timeout: 12e4, env: stepEnv });
65
66
  if (r.stdout) out.push(r.stdout.trimEnd());
66
67
  if (r.stderr) out.push(r.stderr.trimEnd());
67
68
  if (r.status !== 0) {
@@ -2699,7 +2699,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
2699
2699
  const tunnels = handlers.tunnels;
2700
2700
  if (!tunnels) throw new Error("Tunnel management not available");
2701
2701
  if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
2702
- const { FrpcTunnel } = await import('./frpc-yc5-4EUe.mjs');
2702
+ const { FrpcTunnel } = await import('./frpc-Bi1a1Oqy.mjs');
2703
2703
  const tunnel = new FrpcTunnel({
2704
2704
  name: params.name,
2705
2705
  ports: params.ports,
@@ -3146,7 +3146,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3146
3146
  }
3147
3147
  const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
3148
3148
  const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
3149
- const { toolsForRole } = await import('./sideband-CzzHtakf.mjs');
3149
+ const { toolsForRole } = await import('./sideband-DLku7gcO.mjs');
3150
3150
  const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
3151
3151
  return fmt(r2);
3152
3152
  }
@@ -3245,7 +3245,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3245
3245
  if (r.error || !r.sender) return { error: r.error || "unauthorized" };
3246
3246
  const callId = "call_" + Math.random().toString(16).slice(2, 12);
3247
3247
  const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
3248
- const { queryCore } = await import('./commands-B4u6CUWH.mjs');
3248
+ const { queryCore } = await import('./commands-j4nqt7yR.mjs');
3249
3249
  const timeout = c.reply?.timeout_sec || 120;
3250
3250
  let result;
3251
3251
  try {
@@ -3389,6 +3389,132 @@ ${d?.error || "not found"}`;
3389
3389
  };
3390
3390
  }
3391
3391
 
3392
+ const SVAMP_HOME$2 = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
3393
+ const num = (key, def) => {
3394
+ const v = Number(process.env[key]);
3395
+ return Number.isFinite(v) && v > 0 ? v : def;
3396
+ };
3397
+ const MAX_AGENT_HOPS = num("SVAMP_INBOX_MAX_HOPS", 3);
3398
+ const URGENT_BURST = num("SVAMP_INBOX_URGENT_BURST", 5);
3399
+ const URGENT_REFILL_MS = num("SVAMP_INBOX_URGENT_REFILL_MS", 6e4);
3400
+ const BREAKER_WINDOW_MS = num("SVAMP_INBOX_BREAKER_WINDOW_MS", 6e4);
3401
+ const BREAKER_MAX_WAKES = num("SVAMP_INBOX_BREAKER_MAX_WAKES", 30);
3402
+ const BREAKER_COOLDOWN_MS = num("SVAMP_INBOX_BREAKER_COOLDOWN_MS", 12e4);
3403
+ const CTX_STALE_MS = num("SVAMP_INBOX_CTX_STALE_MS", 30 * 6e4);
3404
+ function ctxPath(sessionId) {
3405
+ return join(SVAMP_HOME$2, "inbound", `${sessionId}.json`);
3406
+ }
3407
+ function writeInboundContext(sessionId, ctx) {
3408
+ try {
3409
+ const p = ctxPath(sessionId);
3410
+ mkdirSync(join(SVAMP_HOME$2, "inbound"), { recursive: true });
3411
+ const tmp = p + ".tmp";
3412
+ writeFileSync(tmp, JSON.stringify({ ...ctx, deliveredAt: Date.now() }));
3413
+ try {
3414
+ rmSync(p, { force: true });
3415
+ } catch {
3416
+ }
3417
+ writeFileSync(p, readFileSync(tmp));
3418
+ try {
3419
+ rmSync(tmp, { force: true });
3420
+ } catch {
3421
+ }
3422
+ } catch {
3423
+ }
3424
+ }
3425
+ function clearInboundContext(sessionId) {
3426
+ try {
3427
+ rmSync(ctxPath(sessionId), { force: true });
3428
+ } catch {
3429
+ }
3430
+ }
3431
+ function readInboundContext(sessionId) {
3432
+ try {
3433
+ const p = ctxPath(sessionId);
3434
+ if (!existsSync(p)) return null;
3435
+ const ctx = JSON.parse(readFileSync(p, "utf-8"));
3436
+ if (!ctx || typeof ctx.hopCount !== "number") return null;
3437
+ if (Date.now() - (ctx.deliveredAt || 0) > CTX_STALE_MS) return null;
3438
+ return ctx;
3439
+ } catch {
3440
+ return null;
3441
+ }
3442
+ }
3443
+ function computeOutboundHop(sessionId) {
3444
+ if (!sessionId) return { hopCount: 0, fromInboxTurn: false };
3445
+ const ctx = readInboundContext(sessionId);
3446
+ if (ctx) return { hopCount: (ctx.hopCount || 0) + 1, threadId: ctx.threadId, fromInboxTurn: true };
3447
+ return { hopCount: 1, fromInboxTurn: false };
3448
+ }
3449
+ const buckets = /* @__PURE__ */ new Map();
3450
+ function takeUrgentToken(sender, now) {
3451
+ let b = buckets.get(sender);
3452
+ if (!b) {
3453
+ b = { tokens: URGENT_BURST, last: now };
3454
+ buckets.set(sender, b);
3455
+ }
3456
+ const refill = Math.floor((now - b.last) / URGENT_REFILL_MS);
3457
+ if (refill > 0) {
3458
+ b.tokens = Math.min(URGENT_BURST, b.tokens + refill);
3459
+ b.last = now;
3460
+ }
3461
+ if (b.tokens > 0) {
3462
+ b.tokens -= 1;
3463
+ return true;
3464
+ }
3465
+ return false;
3466
+ }
3467
+ let wakeTimes = [];
3468
+ let breakerOpenUntil = 0;
3469
+ function isBreakerOpenAt(now) {
3470
+ return now < breakerOpenUntil;
3471
+ }
3472
+ function recordWake(now) {
3473
+ wakeTimes = wakeTimes.filter((t) => now - t < BREAKER_WINDOW_MS);
3474
+ wakeTimes.push(now);
3475
+ if (wakeTimes.length > BREAKER_MAX_WAKES) {
3476
+ breakerOpenUntil = now + BREAKER_COOLDOWN_MS;
3477
+ return true;
3478
+ }
3479
+ return false;
3480
+ }
3481
+ function senderIsHuman(message) {
3482
+ const f = message.from || "";
3483
+ return f.startsWith("cli:") || f.startsWith("user:");
3484
+ }
3485
+ function senderKey(message) {
3486
+ return message.fromSession || message.from || "unknown";
3487
+ }
3488
+ function classifyInbound(message, now = Date.now()) {
3489
+ const hop = message.hopCount ?? 0;
3490
+ const human = senderIsHuman(message);
3491
+ if (!human && !message.channel && hop > MAX_AGENT_HOPS) {
3492
+ return { action: "silent", reason: `loop-guard: hop ${hop} > ${MAX_AGENT_HOPS}` };
3493
+ }
3494
+ if (message.urgency !== "urgent") {
3495
+ return { action: "queue", reason: "non-urgent" };
3496
+ }
3497
+ if (human) {
3498
+ const tripped2 = recordWake(now);
3499
+ return { action: "wake", reason: "urgent (human)", breakerTripped: tripped2 };
3500
+ }
3501
+ if (isBreakerOpenAt(now)) {
3502
+ return { action: "queue", reason: "circuit-breaker-open" };
3503
+ }
3504
+ if (!takeUrgentToken(senderKey(message), now)) {
3505
+ return { action: "queue", reason: "rate-limited" };
3506
+ }
3507
+ const tripped = recordWake(now);
3508
+ return { action: "wake", reason: "urgent", breakerTripped: tripped };
3509
+ }
3510
+ function applyInboxClear(inbox, opts) {
3511
+ let kept;
3512
+ if (opts?.messageId) kept = inbox.filter((m) => m.messageId !== opts.messageId);
3513
+ else if (opts?.all) kept = [];
3514
+ else kept = inbox.filter((m) => !m.read);
3515
+ return { kept, removed: inbox.length - kept.length };
3516
+ }
3517
+
3392
3518
  const PARTICIPANTS_CHANNEL_ID = "sys-participants";
3393
3519
  function channelPublicView(c) {
3394
3520
  return { id: c.id, name: c.name, description: c.description, identity: { mode: c.identity?.mode }, action: c.action?.kind, bind: c.bind };
@@ -4453,16 +4579,12 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
4453
4579
  },
4454
4580
  clearInbox: async (opts, context) => {
4455
4581
  authorizeRequest(context, metadata.sharing, "admin");
4456
- if (opts?.all) {
4457
- inbox.length = 0;
4458
- } else {
4459
- for (let i = inbox.length - 1; i >= 0; i--) {
4460
- if (inbox[i].read) inbox.splice(i, 1);
4461
- }
4462
- }
4582
+ const { kept, removed } = applyInboxClear(inbox, opts);
4583
+ inbox.length = 0;
4584
+ inbox.push(...kept);
4463
4585
  syncInboxToMetadata();
4464
4586
  notifyListeners({ type: "inbox-update", sessionId, cleared: true });
4465
- return { success: true, remaining: inbox.length };
4587
+ return { success: true, removed, remaining: inbox.length };
4466
4588
  },
4467
4589
  // ── Listener Registration ──
4468
4590
  registerListener: async (callback, context) => {
@@ -5005,125 +5127,6 @@ function handleMatchesMetadata(parsed, metadata) {
5005
5127
  return true;
5006
5128
  }
5007
5129
 
5008
- const SVAMP_HOME$2 = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
5009
- const num = (key, def) => {
5010
- const v = Number(process.env[key]);
5011
- return Number.isFinite(v) && v > 0 ? v : def;
5012
- };
5013
- const MAX_AGENT_HOPS = num("SVAMP_INBOX_MAX_HOPS", 3);
5014
- const URGENT_BURST = num("SVAMP_INBOX_URGENT_BURST", 5);
5015
- const URGENT_REFILL_MS = num("SVAMP_INBOX_URGENT_REFILL_MS", 6e4);
5016
- const BREAKER_WINDOW_MS = num("SVAMP_INBOX_BREAKER_WINDOW_MS", 6e4);
5017
- const BREAKER_MAX_WAKES = num("SVAMP_INBOX_BREAKER_MAX_WAKES", 30);
5018
- const BREAKER_COOLDOWN_MS = num("SVAMP_INBOX_BREAKER_COOLDOWN_MS", 12e4);
5019
- const CTX_STALE_MS = num("SVAMP_INBOX_CTX_STALE_MS", 30 * 6e4);
5020
- function ctxPath(sessionId) {
5021
- return join(SVAMP_HOME$2, "inbound", `${sessionId}.json`);
5022
- }
5023
- function writeInboundContext(sessionId, ctx) {
5024
- try {
5025
- const p = ctxPath(sessionId);
5026
- mkdirSync(join(SVAMP_HOME$2, "inbound"), { recursive: true });
5027
- const tmp = p + ".tmp";
5028
- writeFileSync(tmp, JSON.stringify({ ...ctx, deliveredAt: Date.now() }));
5029
- try {
5030
- rmSync(p, { force: true });
5031
- } catch {
5032
- }
5033
- writeFileSync(p, readFileSync(tmp));
5034
- try {
5035
- rmSync(tmp, { force: true });
5036
- } catch {
5037
- }
5038
- } catch {
5039
- }
5040
- }
5041
- function clearInboundContext(sessionId) {
5042
- try {
5043
- rmSync(ctxPath(sessionId), { force: true });
5044
- } catch {
5045
- }
5046
- }
5047
- function readInboundContext(sessionId) {
5048
- try {
5049
- const p = ctxPath(sessionId);
5050
- if (!existsSync(p)) return null;
5051
- const ctx = JSON.parse(readFileSync(p, "utf-8"));
5052
- if (!ctx || typeof ctx.hopCount !== "number") return null;
5053
- if (Date.now() - (ctx.deliveredAt || 0) > CTX_STALE_MS) return null;
5054
- return ctx;
5055
- } catch {
5056
- return null;
5057
- }
5058
- }
5059
- function computeOutboundHop(sessionId) {
5060
- if (!sessionId) return { hopCount: 0, fromInboxTurn: false };
5061
- const ctx = readInboundContext(sessionId);
5062
- if (ctx) return { hopCount: (ctx.hopCount || 0) + 1, threadId: ctx.threadId, fromInboxTurn: true };
5063
- return { hopCount: 1, fromInboxTurn: false };
5064
- }
5065
- const buckets = /* @__PURE__ */ new Map();
5066
- function takeUrgentToken(sender, now) {
5067
- let b = buckets.get(sender);
5068
- if (!b) {
5069
- b = { tokens: URGENT_BURST, last: now };
5070
- buckets.set(sender, b);
5071
- }
5072
- const refill = Math.floor((now - b.last) / URGENT_REFILL_MS);
5073
- if (refill > 0) {
5074
- b.tokens = Math.min(URGENT_BURST, b.tokens + refill);
5075
- b.last = now;
5076
- }
5077
- if (b.tokens > 0) {
5078
- b.tokens -= 1;
5079
- return true;
5080
- }
5081
- return false;
5082
- }
5083
- let wakeTimes = [];
5084
- let breakerOpenUntil = 0;
5085
- function isBreakerOpenAt(now) {
5086
- return now < breakerOpenUntil;
5087
- }
5088
- function recordWake(now) {
5089
- wakeTimes = wakeTimes.filter((t) => now - t < BREAKER_WINDOW_MS);
5090
- wakeTimes.push(now);
5091
- if (wakeTimes.length > BREAKER_MAX_WAKES) {
5092
- breakerOpenUntil = now + BREAKER_COOLDOWN_MS;
5093
- return true;
5094
- }
5095
- return false;
5096
- }
5097
- function senderIsHuman(message) {
5098
- const f = message.from || "";
5099
- return f.startsWith("cli:") || f.startsWith("user:");
5100
- }
5101
- function senderKey(message) {
5102
- return message.fromSession || message.from || "unknown";
5103
- }
5104
- function classifyInbound(message, now = Date.now()) {
5105
- const hop = message.hopCount ?? 0;
5106
- const human = senderIsHuman(message);
5107
- if (!human && !message.channel && hop > MAX_AGENT_HOPS) {
5108
- return { action: "silent", reason: `loop-guard: hop ${hop} > ${MAX_AGENT_HOPS}` };
5109
- }
5110
- if (message.urgency !== "urgent") {
5111
- return { action: "queue", reason: "non-urgent" };
5112
- }
5113
- if (human) {
5114
- const tripped2 = recordWake(now);
5115
- return { action: "wake", reason: "urgent (human)", breakerTripped: tripped2 };
5116
- }
5117
- if (isBreakerOpenAt(now)) {
5118
- return { action: "queue", reason: "circuit-breaker-open" };
5119
- }
5120
- if (!takeUrgentToken(senderKey(message), now)) {
5121
- return { action: "queue", reason: "rate-limited" };
5122
- }
5123
- const tripped = recordWake(now);
5124
- return { action: "wake", reason: "urgent", breakerTripped: tripped };
5125
- }
5126
-
5127
5130
  function resolveProjectRoot$1(directory) {
5128
5131
  try {
5129
5132
  const top = execSync("git rev-parse --show-toplevel", {
@@ -9812,6 +9815,11 @@ class ProcessSupervisor {
9812
9815
  }
9813
9816
  }
9814
9817
 
9818
+ function normalizeStatus(s) {
9819
+ if (s === "backlog") return "ready";
9820
+ if (s === "done") return "archived";
9821
+ return s === "ready" || s === "in_progress" || s === "archived" ? s : "ready";
9822
+ }
9815
9823
  const FIELD_ORDER = ["id", "title", "status", "scope", "labels", "verify", "disposition", "triaged", "branch", "session", "original", "created", "closed"];
9816
9824
  function resolveProjectRoot(start = process.cwd()) {
9817
9825
  let dir = start;
@@ -9861,7 +9869,7 @@ function parseIssue(content) {
9861
9869
  return {
9862
9870
  id: String(fm.id),
9863
9871
  title: String(fm.title ?? ""),
9864
- status: fm.status ?? "backlog",
9872
+ status: normalizeStatus(fm.status),
9865
9873
  scope: fm.scope ?? "session",
9866
9874
  labels: Array.isArray(fm.labels) ? fm.labels.map(String) : [],
9867
9875
  verify: fm.verify ?? null,
@@ -9924,7 +9932,7 @@ function addIssue(projectRoot, fields) {
9924
9932
  const issue = {
9925
9933
  id: nextId(projectRoot),
9926
9934
  title: fields.title.trim(),
9927
- status: fields.status ?? "backlog",
9935
+ status: fields.status ?? "ready",
9928
9936
  scope: fields.scope ?? "session",
9929
9937
  labels: fields.labels ?? [],
9930
9938
  verify: fields.verify ?? null,
@@ -9945,9 +9953,10 @@ function updateIssue(projectRoot, id, patch) {
9945
9953
  if (!cur) return null;
9946
9954
  const wasArchived = cur.status === "archived";
9947
9955
  const next = { ...cur, ...patch, id: cur.id };
9956
+ if (patch.status) next.status = normalizeStatus(patch.status);
9948
9957
  const nowArchived = next.status === "archived";
9949
- if ((next.status === "done" || nowArchived) && !next.closed) next.closed = (/* @__PURE__ */ new Date()).toISOString();
9950
- if (next.status !== "done" && !nowArchived) next.closed = null;
9958
+ if (nowArchived && !next.closed) next.closed = (/* @__PURE__ */ new Date()).toISOString();
9959
+ if (!nowArchived) next.closed = null;
9951
9960
  atomicWrite(issuePath(projectRoot, next.id, nowArchived), serializeIssue(next));
9952
9961
  if (wasArchived !== nowArchived) {
9953
9962
  const oldPath = issuePath(projectRoot, cur.id, wasArchived);
@@ -9975,7 +9984,7 @@ function routeCrewVerdict(projectRoot, childSessionId, v) {
9975
9984
  if (!linked) return null;
9976
9985
  const meta = [v.round != null ? `round ${v.round}` : "", v.judge ? `judge:${v.judge}` : ""].filter(Boolean).join(", ");
9977
9986
  addComment(projectRoot, linked.id, `Crew child ${childSessionId.slice(0, 8)} \u2192 ${v.verdict}${meta ? ` (${meta})` : ""}.${v.guidance ? " " + v.guidance : ""}`);
9978
- return v.verdict === "approved" ? updateIssue(projectRoot, linked.id, { status: "done" }) ?? null : getIssue(projectRoot, linked.id);
9987
+ return v.verdict === "approved" ? updateIssue(projectRoot, linked.id, { status: "archived" }) ?? null : getIssue(projectRoot, linked.id);
9979
9988
  }
9980
9989
  function searchIssues(projectRoot, query) {
9981
9990
  const q = query.trim();
@@ -9999,7 +10008,7 @@ function searchIssues(projectRoot, query) {
9999
10008
  }
10000
10009
  function summarize(issues) {
10001
10010
  const by = (s) => issues.filter((i) => i.status === s).length;
10002
- return { total: issues.length, backlog: by("backlog"), ready: by("ready"), in_progress: by("in_progress"), done: by("done"), archived: by("archived") };
10011
+ return { total: issues.length, ready: by("ready"), in_progress: by("in_progress"), archived: by("archived") };
10003
10012
  }
10004
10013
 
10005
10014
  const OAUTH_TOKEN_ENDPOINT = "https://platform.claude.com/v1/oauth/token";
@@ -11541,7 +11550,7 @@ async function startDaemon(options) {
11541
11550
  saveExposedTunnels(list);
11542
11551
  }
11543
11552
  async function createExposedTunnel(spec) {
11544
- const { FrpcTunnel } = await import('./frpc-yc5-4EUe.mjs');
11553
+ const { FrpcTunnel } = await import('./frpc-Bi1a1Oqy.mjs');
11545
11554
  const tunnel = new FrpcTunnel({
11546
11555
  name: spec.name,
11547
11556
  ports: spec.ports,
@@ -11561,7 +11570,7 @@ async function startDaemon(options) {
11561
11570
  return tunnel;
11562
11571
  }
11563
11572
  const tunnelRecreateState = /* @__PURE__ */ new Map();
11564
- const { ServeManager } = await import('./serveManager-AbWv-Akt.mjs');
11573
+ const { ServeManager } = await import('./serveManager-CqhigbGu.mjs');
11565
11574
  const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
11566
11575
  ensureAutoInstalledSkills(logger).catch(() => {
11567
11576
  });
@@ -13352,11 +13361,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
13352
13361
  });
13353
13362
  },
13354
13363
  onIssue: async (params) => {
13355
- const { issueRpc } = await import('./rpc-DUZqKPK4.mjs');
13364
+ const { issueRpc } = await import('./rpc-CAi59ZCd.mjs');
13356
13365
  return issueRpc(params?.cwd || directory, params || {});
13357
13366
  },
13358
13367
  onWorkflow: async (params) => {
13359
- const { workflowRpc } = await import('./rpc-D1gr74eF.mjs');
13368
+ const { workflowRpc } = await import('./rpc-CND7PIf-.mjs');
13360
13369
  return workflowRpc(params?.cwd || directory, params || {});
13361
13370
  },
13362
13371
  onRipgrep: async (args, cwd) => {
@@ -13862,11 +13871,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
13862
13871
  });
13863
13872
  },
13864
13873
  onIssue: async (params) => {
13865
- const { issueRpc } = await import('./rpc-DUZqKPK4.mjs');
13874
+ const { issueRpc } = await import('./rpc-CAi59ZCd.mjs');
13866
13875
  return issueRpc(params?.cwd || directory, params || {});
13867
13876
  },
13868
13877
  onWorkflow: async (params) => {
13869
- const { workflowRpc } = await import('./rpc-D1gr74eF.mjs');
13878
+ const { workflowRpc } = await import('./rpc-CND7PIf-.mjs');
13870
13879
  return workflowRpc(params?.cwd || directory, params || {});
13871
13880
  },
13872
13881
  onRipgrep: async (args, cwd) => {
@@ -14619,7 +14628,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
14619
14628
  const PING_TIMEOUT_MS = 15e3;
14620
14629
  const POST_RECONNECT_GRACE_MS = 2e4;
14621
14630
  const RECONNECT_JITTER_MS = 2500;
14622
- const { WorkflowScheduler } = await import('./scheduler-DweevMw_.mjs');
14631
+ const { WorkflowScheduler } = await import('./scheduler-CsaIxFN7.mjs');
14623
14632
  const workflowScheduler = new WorkflowScheduler({
14624
14633
  projectRoots: () => {
14625
14634
  const dirs = /* @__PURE__ */ new Set();
@@ -1,4 +1,4 @@
1
- import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { a0 as composeSessionId, a1 as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a2 as generateHookSettings } from './run-21-bMHCc.mjs';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { a0 as composeSessionId, a1 as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a2 as generateHookSettings } from './run-CLeF1N9s.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';