svamp-cli 0.2.325 → 0.2.326

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 (29) hide show
  1. package/README.md +2 -3
  2. package/bin/skills/crew/SKILL.md +10 -2
  3. package/dist/{adminCommands-CMSvATDd.mjs → adminCommands-D1NzA4Hb.mjs} +1 -1
  4. package/dist/{agentCommands-BE3bFu8e.mjs → agentCommands-DUHUBzrj.mjs} +5 -5
  5. package/dist/{auth-BmFYRinf.mjs → auth-DogM3PPm.mjs} +3 -3
  6. package/dist/{cli-DiCcPggc.mjs → cli-PWoHWBkF.mjs} +79 -79
  7. package/dist/cli.mjs +4 -4
  8. package/dist/{commands-B6fVaVgM.mjs → commands-B5bC9eXz.mjs} +3 -3
  9. package/dist/{commands-Coy5CnDf.mjs → commands-CBk4dLUP.mjs} +3 -3
  10. package/dist/{commands-WDZS1Clb.mjs → commands-Ck4pP86Q.mjs} +4 -4
  11. package/dist/{commands-7ExCePUv.mjs → commands-CqqQYRzg.mjs} +2 -2
  12. package/dist/{commands-D4R66JMe.mjs → commands-CxfUr2Qr.mjs} +1 -1
  13. package/dist/{commands-C3Bkw-Km.mjs → commands-D-4ry2I6.mjs} +13 -13
  14. package/dist/{commands-uYZfcxk0.mjs → commands-Dm9KNB-p.mjs} +3 -3
  15. package/dist/{commands-Z_44kVdm.mjs → commands-Dt_MecCB.mjs} +3 -3
  16. package/dist/{fleet-DJj0ns7e.mjs → fleet-Bzg3YxM4.mjs} +2 -2
  17. package/dist/{headlessCli-Bx41HZIx.mjs → headlessCli-DvJI9DRh.mjs} +4 -4
  18. package/dist/index.mjs +3 -3
  19. package/dist/{notifyCommands-BV4ajmhc.mjs → notifyCommands-BpMErHcv.mjs} +1 -1
  20. package/dist/{package-C3Usc86E.mjs → package-D_BgajkI.mjs} +2 -2
  21. package/dist/{pinnedClaudeCode-8SZDu7O-.mjs → pinnedClaudeCode-BO0Qs_kE.mjs} +1 -1
  22. package/dist/{rpc-D9LFUW93.mjs → rpc-27xBajCe.mjs} +3 -3
  23. package/dist/{rpc-D_pFjWr1.mjs → rpc-Qcj4M_LQ.mjs} +3 -3
  24. package/dist/{run-CO4F7ZaJ.mjs → run-BBBr4jkI.mjs} +194 -56
  25. package/dist/{run-D7G5CDp7.mjs → run-CocRShOR.mjs} +1 -1
  26. package/dist/{scheduler-bgEMu7PK.mjs → scheduler-DjAVEyn3.mjs} +3 -3
  27. package/dist/{serveCommands-MbZuPWD4.mjs → serveCommands-BK-wWc_i.mjs} +12 -12
  28. package/dist/{sideband-CadJ3gg1.mjs → sideband-5ck3CDWM.mjs} +3 -3
  29. package/package.json +3 -3
@@ -8,15 +8,15 @@ import { fileURLToPath } from 'url';
8
8
  import { execFile, execSync, spawn, exec as exec$1 } from 'child_process';
9
9
  import * as crypto from 'crypto';
10
10
  import { randomUUID, createHash } from 'crypto';
11
- import { randomUUID as randomUUID$1, randomBytes, createHash as createHash$1, timingSafeEqual } from 'node:crypto';
12
11
  import { existsSync, readFileSync, mkdirSync as mkdirSync$1, writeFileSync as writeFileSync$1, chmodSync as chmodSync$1, rmSync as rmSync$1, cpSync, statSync, realpathSync, readdirSync, renameSync, appendFileSync, openSync, readSync, closeSync, unlinkSync as unlinkSync$1, writeSync } from 'node:fs';
12
+ import { join as join$1, extname, resolve, sep, basename, dirname } from 'node:path';
13
+ import { randomUUID as randomUUID$1, randomBytes, createHash as createHash$1, timingSafeEqual } from 'node:crypto';
13
14
  import { exec, spawn as spawn$1, execSync as execSync$1, execFile as execFile$1, execFileSync } from 'node:child_process';
14
15
  import { promisify } from 'util';
15
16
  import * as http from 'http';
16
17
  import * as net from 'net';
17
18
  import { createServer } from 'net';
18
19
  import os, { homedir as homedir$1, platform as platform$1 } from 'node:os';
19
- import { join as join$1, extname, resolve, sep, basename, dirname } from 'node:path';
20
20
  import { EventEmitter } from 'node:events';
21
21
  import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk';
22
22
  import { promisify as promisify$1 } from 'node:util';
@@ -3160,6 +3160,9 @@ class ServeManager {
3160
3160
  hyphaServerUrl;
3161
3161
  /** #126: daemon-provided lookup of sessionId → { cwd, sharing, ownerEmail } for the files gateway. */
3162
3162
  sessionResolver = null;
3163
+ /** Channel-capability authorizer: a `fileAccess`-enabled channel's caller key grants
3164
+ * whole-project file management for its session. Checked BEFORE email auth. */
3165
+ capabilityAuthorizer = null;
3163
3166
  /**
3164
3167
  * #0944/#0945: daemon-provided "this mount no longer backs that session's Launch Pad entry"
3165
3168
  * hook. Fired with the OWNING session id, which is the only correct target — the CLI-side
@@ -3189,6 +3192,30 @@ class ServeManager {
3189
3192
  setSessionResolver(fn) {
3190
3193
  this.sessionResolver = fn;
3191
3194
  }
3195
+ /**
3196
+ * Register the channel-capability authorizer used by the session-files gateway to admit
3197
+ * a `fileAccess`-enabled channel's caller key (ck_… bearer). Call once at daemon start.
3198
+ */
3199
+ setCapabilityAuthorizer(fn) {
3200
+ this.capabilityAuthorizer = fn;
3201
+ }
3202
+ /**
3203
+ * Extract a channel capability token from the request, if one is present. A channel caller
3204
+ * key is `ck_`-prefixed; we ONLY return a token that carries that prefix so a Hypha JWT
3205
+ * bearer (used by the email-identity path) never collides with the capability authorizer.
3206
+ * Accepted carriers: `Authorization: Bearer ck_…` header, or `?key=ck_…` query param.
3207
+ * Returns null when no capability token is presented. NEVER log the returned value.
3208
+ */
3209
+ extractCapabilityToken(req, url) {
3210
+ const auth = req.headers["authorization"];
3211
+ if (typeof auth === "string") {
3212
+ const m = /^Bearer\s+(\S+)$/i.exec(auth.trim());
3213
+ if (m && m[1].startsWith("ck_")) return m[1];
3214
+ }
3215
+ const q = url.searchParams.get("key");
3216
+ if (q && q.startsWith("ck_")) return q;
3217
+ return null;
3218
+ }
3192
3219
  /**
3193
3220
  * #0944/#0945: register the daemon's "drop this session's Launch Pad link for this mount"
3194
3221
  * callback. Fired when a mount stops backing a session's Pad entry — on a real removal, and
@@ -3785,36 +3812,47 @@ a{color:#0969da;text-decoration:none;font-weight:500}a:hover{text-decoration:und
3785
3812
  deny(404, "Unknown session");
3786
3813
  return;
3787
3814
  }
3788
- const userEmail = this.auth ? await this.auth.authenticate(req).catch(() => null) : null;
3789
- if (!userEmail) {
3790
- deny(401, "Authentication required");
3791
- return;
3815
+ let capabilityGranted = false;
3816
+ const capToken = this.extractCapabilityToken(req, url);
3817
+ if (capToken && this.capabilityAuthorizer) {
3818
+ try {
3819
+ capabilityGranted = this.capabilityAuthorizer(sessionId, capToken);
3820
+ } catch {
3821
+ capabilityGranted = false;
3822
+ }
3792
3823
  }
3793
- const ownerEmail = (target.ownerEmail || "").toLowerCase();
3794
- const isOwner = !!ownerEmail && userEmail.toLowerCase() === ownerEmail;
3795
- if (!isOwner) {
3796
- const principal = { user: { email: userEmail } };
3797
- let granted = false;
3798
- const sharing = target.sharing;
3799
- if (sharing && sharing.enabled) {
3800
- try {
3801
- authorizeRequest(principal, sharing, "admin");
3802
- granted = true;
3803
- } catch {
3804
- }
3824
+ if (!capabilityGranted) {
3825
+ const userEmail = this.auth ? await this.auth.authenticate(req).catch(() => null) : null;
3826
+ if (!userEmail) {
3827
+ deny(401, "Authentication required");
3828
+ return;
3805
3829
  }
3806
- const machineSharing = target.machineSharing;
3807
- if (!granted && machineSharing && machineSharing.enabled) {
3808
- try {
3809
- authorizeRequest(principal, machineSharing, "admin");
3810
- granted = true;
3811
- } catch {
3830
+ const ownerEmail = (target.ownerEmail || "").toLowerCase();
3831
+ const isOwner = !!ownerEmail && userEmail.toLowerCase() === ownerEmail;
3832
+ if (!isOwner) {
3833
+ const principal = { user: { email: userEmail } };
3834
+ let granted = false;
3835
+ const sharing = target.sharing;
3836
+ if (sharing && sharing.enabled) {
3837
+ try {
3838
+ authorizeRequest(principal, sharing, "admin");
3839
+ granted = true;
3840
+ } catch {
3841
+ }
3842
+ }
3843
+ const machineSharing = target.machineSharing;
3844
+ if (!granted && machineSharing && machineSharing.enabled) {
3845
+ try {
3846
+ authorizeRequest(principal, machineSharing, "admin");
3847
+ granted = true;
3848
+ } catch {
3849
+ }
3850
+ }
3851
+ if (!granted) {
3852
+ this.log(`Files-gateway DENY '${sessionId}': ${userEmail} not owner and not a session/machine admin`);
3853
+ deny(403, "Access denied");
3854
+ return;
3812
3855
  }
3813
- }
3814
- if (!granted) {
3815
- this.log(`Files-gateway DENY '${sessionId}': ${userEmail} not owner and not a session/machine admin`);
3816
- deny(403, "Access denied");
3817
- return;
3818
3856
  }
3819
3857
  }
3820
3858
  const rootDir = target.cwd;
@@ -5613,6 +5651,12 @@ function validateChannel(c) {
5613
5651
  if (m === "fixed" && c.identity.fixed?.name && unsafe.test(c.identity.fixed.name)) errs.push(`identity.fixed.name must not contain < > " ' & or newlines`);
5614
5652
  for (const cl of c.identity?.callers || []) if (unsafe.test(cl.name || "")) errs.push(`caller name "${cl.name}" must not contain < > " ' & or newlines`);
5615
5653
  errs.push(...validateUploadConfig(c));
5654
+ if (c.fileAccess !== void 0) {
5655
+ if (typeof c.fileAccess !== "object" || c.fileAccess === null || Array.isArray(c.fileAccess))
5656
+ errs.push("fileAccess must be an object");
5657
+ else if (c.fileAccess.enabled !== void 0 && typeof c.fileAccess.enabled !== "boolean")
5658
+ errs.push("fileAccess.enabled must be a boolean");
5659
+ }
5616
5660
  return errs;
5617
5661
  }
5618
5662
  function normalizeBind(c) {
@@ -5636,6 +5680,24 @@ function channelAsyncRepliable(c) {
5636
5680
  if ((c.action?.kind ?? "message") !== "message") return false;
5637
5681
  return bindMode(c) !== "stateless";
5638
5682
  }
5683
+ function channelKeyGrantsFileAccess(channels, token) {
5684
+ if (!token || !token.startsWith("ck_")) return false;
5685
+ const presented = createHash$1("sha256").update(token).digest();
5686
+ const eq = (candidate) => {
5687
+ if (!candidate) return false;
5688
+ try {
5689
+ return timingSafeEqual(presented, createHash$1("sha256").update(candidate).digest());
5690
+ } catch {
5691
+ return false;
5692
+ }
5693
+ };
5694
+ for (const ch of channels) {
5695
+ if (!ch.fileAccess?.enabled) continue;
5696
+ if (eq(ch.identity?.shared_key)) return true;
5697
+ for (const caller of ch.identity?.callers || []) if (eq(caller.key)) return true;
5698
+ }
5699
+ return false;
5700
+ }
5639
5701
  class ChannelStore {
5640
5702
  dir;
5641
5703
  constructor(projectDir) {
@@ -5664,7 +5726,7 @@ class ChannelStore {
5664
5726
  if (errs.length) throw new Error("invalid channel: " + errs.join("; "));
5665
5727
  mkdirSync$1(this.dir, { recursive: true });
5666
5728
  const tmp = `${this._path(c.id)}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
5667
- writeFileSync$1(tmp, JSON.stringify(c, null, 2));
5729
+ writeFileSync$1(tmp, JSON.stringify(c, null, 2), { mode: 384 });
5668
5730
  renameSync(tmp, this._path(c.id));
5669
5731
  return c;
5670
5732
  }
@@ -5846,6 +5908,32 @@ curl -X POST "${uploadUrl}" \\
5846
5908
  -d '{"kwargs": {"channel": "${channel.id}", "filename": "report.pdf", "content_base64": "<base64>", "content_type": "application/pdf", "from": "your-name", "key": "${key}"}}'
5847
5909
  \`\`\``;
5848
5910
  })() : "";
5911
+ const filesBase = ctx?.filesEndpoint;
5912
+ const fileSection = channel.fileAccess?.enabled && filesBase ? `
5913
+
5914
+ ## Managing project files
5915
+ This channel also grants **full file management of the whole project folder** \u2014 list,
5916
+ download, upload, and delete any file in the working directory (this is NOT the sandboxed
5917
+ \`upload\` above). Authenticate every request with your channel key as a Bearer token:
5918
+
5919
+ Base: \`${filesBase}\` \u2014 append a project-relative path (URL-encode as needed).
5920
+
5921
+ - **List a directory:** \`GET ${filesBase}/<dir>/?list=1\` \u2192 JSON of entries
5922
+ - **Download a file:** \`GET ${filesBase}/<path>\`
5923
+ - **Upload / overwrite:** \`PUT ${filesBase}/<path>\` with the raw file bytes as the body
5924
+ - **Delete a file:** \`DELETE ${filesBase}/<path>\`
5925
+
5926
+ \`\`\`bash
5927
+ # list the project root
5928
+ curl -H "Authorization: Bearer ${key}" "${filesBase}/?list=1"
5929
+ # download a file
5930
+ curl -H "Authorization: Bearer ${key}" "${filesBase}/README.md" -o README.md
5931
+ # upload / overwrite a file (raw bytes)
5932
+ curl -X PUT -H "Authorization: Bearer ${key}" --data-binary @local.txt "${filesBase}/notes/local.txt"
5933
+ # delete a file
5934
+ curl -X DELETE -H "Authorization: Bearer ${key}" "${filesBase}/notes/local.txt"
5935
+ \`\`\`
5936
+ > \u26A0\uFE0F Anyone holding this key can read, write, and **delete** any file in this project. Revoke it when you're done.` : "";
5849
5937
  return `---
5850
5938
  name: ${name}
5851
5939
  description: ${desc}
@@ -5878,7 +5966,7 @@ curl -X POST "${sendUrl}" \\
5878
5966
  -d '{"kwargs": {"channel": "${channel.id}", "message": "your message here", "from": "your-name", "key": "${key}"${sessionKv}}}'
5879
5967
  \`\`\`
5880
5968
 
5881
- Always-current copy of this skill (all ids filled in): ${skillUrl}${uploadSection}${queueSection}`;
5969
+ Always-current copy of this skill (all ids filled in): ${skillUrl}${uploadSection}${fileSection}${queueSection}`;
5882
5970
  }
5883
5971
 
5884
5972
  function keysMatch(a, b) {
@@ -8101,7 +8189,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
8101
8189
  }
8102
8190
  const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
8103
8191
  const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
8104
- const { toolsForRole } = await import('./sideband-CadJ3gg1.mjs');
8192
+ const { toolsForRole } = await import('./sideband-5ck3CDWM.mjs');
8105
8193
  const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
8106
8194
  return fmt(r2);
8107
8195
  }
@@ -8206,7 +8294,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
8206
8294
  return { ok: false, call_id: callId, status: "busy", error: "channel is busy (too many concurrent requests) \u2014 retry shortly" };
8207
8295
  }
8208
8296
  const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
8209
- const { queryCore } = await import('./commands-D4R66JMe.mjs');
8297
+ const { queryCore } = await import('./commands-CxfUr2Qr.mjs');
8210
8298
  const timeout = c.reply?.timeout_sec || 120;
8211
8299
  let result;
8212
8300
  try {
@@ -9674,7 +9762,11 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
9674
9762
  key: c.identity?.shared_key,
9675
9763
  // The session this skill is being copied FROM — baked into `dynamic` channel
9676
9764
  // instructions as the routing target so the copied link lands in THIS session.
9677
- session: sessionId
9765
+ session: sessionId,
9766
+ // For a fileAccess-enabled channel, the session-files REST gateway prefix
9767
+ // (`<base>/s/<sessionId>/`) so generateSkillBody can render the file curl block.
9768
+ // Null until the always-on gateway tunnel is up ⇒ the file section is omitted.
9769
+ filesEndpoint: callbacks.onFilesEndpoint?.() ?? void 0
9678
9770
  });
9679
9771
  const skillUrlsFor = (c) => {
9680
9772
  if (!channelsServiceId) return { skillUrl: void 0, sendUrl: void 0 };
@@ -10250,10 +10342,17 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
10250
10342
  return { error: e?.message || String(e) };
10251
10343
  }
10252
10344
  },
10253
- recordMeetingActivity: async (params) => {
10345
+ recordMeetingActivity: async (params, context) => {
10254
10346
  const c = channelStore.get(params.channel);
10255
10347
  if (!c || c.enabled === false) return { error: "channel not found" };
10256
- const r = resolveSender(c, { key: params.key, from: params.from });
10348
+ const u = context?.user;
10349
+ const r = resolveSender(c, {
10350
+ key: params.key,
10351
+ from: params.from,
10352
+ hyphaUser: u && u.is_anonymous !== true ? u.email || u.id : void 0,
10353
+ hyphaAnonymous: u?.is_anonymous === true,
10354
+ hyphaWorkspace: u?.scope?.current_workspace
10355
+ });
10257
10356
  if (r.error || !r.sender) return { error: r.error || "unauthorized" };
10258
10357
  const text = String(params.text || "").replace(/\s+/g, " ").trim().slice(0, 1e3);
10259
10358
  if (!text) return { ok: true };
@@ -11463,6 +11562,25 @@ function handleClaudeMessage(ctx, msg) {
11463
11562
  }
11464
11563
  }
11465
11564
 
11565
+ function sessionQueueMax(env = process.env) {
11566
+ return Math.max(10, Number(env.SVAMP_SESSION_QUEUE_MAX) || 200);
11567
+ }
11568
+ function appendToMessageQueue(existing, entry, max = sessionQueueMax()) {
11569
+ const next = [...existing, entry];
11570
+ if (next.length <= max) return { queue: next, dropped: 0 };
11571
+ const overflow = next.length - max;
11572
+ let budget = overflow;
11573
+ const kept = [];
11574
+ for (const item of next) {
11575
+ if (budget > 0 && item !== entry && item.inboxHop !== void 0) {
11576
+ budget--;
11577
+ continue;
11578
+ }
11579
+ kept.push(item);
11580
+ }
11581
+ return { queue: kept, dropped: overflow - budget };
11582
+ }
11583
+
11466
11584
  const KNOWN_AGENT_LAUNCHERS = ["claude", "codex", "gemini", "kimi"];
11467
11585
  function agentLauncherExists(cmd, env = process.env) {
11468
11586
  if (!cmd) return false;
@@ -19964,7 +20082,7 @@ async function startDaemon(options) {
19964
20082
  try {
19965
20083
  const dir = loadSessionIndex()[sessionId]?.directory;
19966
20084
  if (!dir) return;
19967
- const { reconcileServiceLinks } = await import('./agentCommands-BE3bFu8e.mjs');
20085
+ const { reconcileServiceLinks } = await import('./agentCommands-DUHUBzrj.mjs');
19968
20086
  const configPath = getSvampConfigPath(dir, sessionId);
19969
20087
  const config = readSvampConfig(configPath);
19970
20088
  const entries = Array.from(urls.entries());
@@ -19986,7 +20104,7 @@ async function startDaemon(options) {
19986
20104
  try {
19987
20105
  const dir = loadSessionIndex()[sessionId]?.directory;
19988
20106
  if (!dir) return;
19989
- const { reconcileServiceLinks } = await import('./agentCommands-BE3bFu8e.mjs');
20107
+ const { reconcileServiceLinks } = await import('./agentCommands-DUHUBzrj.mjs');
19990
20108
  const configPath = getSvampConfigPath(dir, sessionId);
19991
20109
  const config = readSvampConfig(configPath);
19992
20110
  const incoming = [{
@@ -20007,7 +20125,7 @@ async function startDaemon(options) {
20007
20125
  try {
20008
20126
  const dir = loadSessionIndex()[sessionId]?.directory;
20009
20127
  if (!dir) return;
20010
- const { dropServiceLinks } = await import('./agentCommands-BE3bFu8e.mjs');
20128
+ const { dropServiceLinks } = await import('./agentCommands-DUHUBzrj.mjs');
20011
20129
  const configPath = getSvampConfigPath(dir, sessionId);
20012
20130
  const config = readSvampConfig(configPath);
20013
20131
  if (dropServiceLinks(config, "serve", mountName)) {
@@ -20050,7 +20168,7 @@ async function startDaemon(options) {
20050
20168
  ensureAutoInstalledCommands(logger);
20051
20169
  (async () => {
20052
20170
  try {
20053
- const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-8SZDu7O-.mjs');
20171
+ const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-BO0Qs_kE.mjs');
20054
20172
  beginClaudeVersionReconcile((msg) => logger.log(msg));
20055
20173
  } catch (e) {
20056
20174
  logger.log(`[claude-version] check failed: ${e?.message || e}`);
@@ -22314,11 +22432,13 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
22314
22432
  } else {
22315
22433
  logger.log(`[Session ${sessionId}] Queuing inbox message for idle processing`);
22316
22434
  const existingQueue = sessionMetadata.messageQueue || [];
22317
- sessionMetadata = {
22318
- ...sessionMetadata,
22319
- messageQueue: [...existingQueue, { id: message.messageId, text: formatted, displayText: formatted, createdAt: Date.now(), inboxHop: message.hopCount ?? 0, inboxThreadId: message.threadId || message.messageId }]
22320
- };
22435
+ const { queue: cappedQueue, dropped } = appendToMessageQueue(existingQueue, { id: message.messageId, text: formatted, displayText: formatted, createdAt: Date.now(), inboxHop: message.hopCount ?? 0, inboxThreadId: message.threadId || message.messageId });
22436
+ sessionMetadata = { ...sessionMetadata, messageQueue: cappedQueue };
22321
22437
  sessionService.updateMetadata(sessionMetadata);
22438
+ if (dropped > 0) {
22439
+ logger.log(`[Session ${sessionId}] Inbox queue over ${sessionQueueMax()}; dropped ${dropped} oldest queued inbox message(s)`);
22440
+ sessionService.pushMessage({ type: "message", message: `Inbox queue full (${sessionQueueMax()}) \u2014 dropped ${dropped} older queued message(s).`, level: "warning" }, "event");
22441
+ }
22322
22442
  if (!sessionWasProcessing) {
22323
22443
  setTimeout(() => processMessageQueueRef?.(), 200);
22324
22444
  }
@@ -22377,11 +22497,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
22377
22497
  });
22378
22498
  },
22379
22499
  onIssue: async (params) => {
22380
- const { issueRpc } = await import('./rpc-D_pFjWr1.mjs');
22500
+ const { issueRpc } = await import('./rpc-Qcj4M_LQ.mjs');
22381
22501
  return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
22382
22502
  },
22383
22503
  onWorkflow: async (params) => {
22384
- const { workflowRpc } = await import('./rpc-D9LFUW93.mjs');
22504
+ const { workflowRpc } = await import('./rpc-27xBajCe.mjs');
22385
22505
  return workflowRpc(params?.cwd || directory, params || {});
22386
22506
  },
22387
22507
  onRipgrep: async (args, cwd) => {
@@ -22992,11 +23112,13 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
22992
23112
  } else {
22993
23113
  logger.log(`[${agentName} Session ${sessionId}] Queuing inbox message for idle processing`);
22994
23114
  const existingQueue = sessionMetadata.messageQueue || [];
22995
- sessionMetadata = {
22996
- ...sessionMetadata,
22997
- messageQueue: [...existingQueue, { id: message.messageId, text: formatted, displayText: formatted, createdAt: Date.now(), inboxHop: message.hopCount ?? 0, inboxThreadId: message.threadId || message.messageId }]
22998
- };
23115
+ const { queue: cappedQueue, dropped } = appendToMessageQueue(existingQueue, { id: message.messageId, text: formatted, displayText: formatted, createdAt: Date.now(), inboxHop: message.hopCount ?? 0, inboxThreadId: message.threadId || message.messageId });
23116
+ sessionMetadata = { ...sessionMetadata, messageQueue: cappedQueue };
22999
23117
  sessionService.updateMetadata(sessionMetadata);
23118
+ if (dropped > 0) {
23119
+ logger.log(`[${agentName} Session ${sessionId}] Inbox queue over ${sessionQueueMax()}; dropped ${dropped} oldest queued inbox message(s)`);
23120
+ sessionService.pushMessage({ type: "message", message: `Inbox queue full (${sessionQueueMax()}) \u2014 dropped ${dropped} older queued message(s).`, level: "warning" }, "event");
23121
+ }
23000
23122
  if (sessionMetadata.lifecycleState === "idle" && acpBackendReady) {
23001
23123
  const queue = sessionMetadata.messageQueue;
23002
23124
  if (queue && queue.length > 0) {
@@ -23085,11 +23207,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
23085
23207
  });
23086
23208
  },
23087
23209
  onIssue: async (params) => {
23088
- const { issueRpc } = await import('./rpc-D_pFjWr1.mjs');
23210
+ const { issueRpc } = await import('./rpc-Qcj4M_LQ.mjs');
23089
23211
  return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
23090
23212
  },
23091
23213
  onWorkflow: async (params) => {
23092
- const { workflowRpc } = await import('./rpc-D9LFUW93.mjs');
23214
+ const { workflowRpc } = await import('./rpc-27xBajCe.mjs');
23093
23215
  return workflowRpc(params?.cwd || directory, params || {});
23094
23216
  },
23095
23217
  onRipgrep: async (args, cwd) => {
@@ -24113,11 +24235,27 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
24113
24235
  }
24114
24236
  return null;
24115
24237
  });
24238
+ serveManager$1.setCapabilityAuthorizer((sessionId, token) => {
24239
+ if (!token || !token.startsWith("ck_")) return false;
24240
+ let cwd = null;
24241
+ for (const [, session] of pidToTrackedSession) {
24242
+ if (session.svampSessionId === sessionId && !session.stopped && session.directory) {
24243
+ cwd = session.directory;
24244
+ break;
24245
+ }
24246
+ }
24247
+ if (!cwd) return false;
24248
+ try {
24249
+ return channelKeyGrantsFileAccess(new ChannelStore(cwd).list(), token);
24250
+ } catch {
24251
+ return false;
24252
+ }
24253
+ });
24116
24254
  const sessionFilesToken = createHash("sha256").update(`session-files:${machineId}`).digest("hex").slice(0, 20);
24117
24255
  serveManager$1.ensureSessionFilesGateway(sessionFilesToken).then((base) => logger.log(`[serve] session-files gateway: ${base || "(tunnel pending)"}`)).catch((err) => logger.error(`[serve] session-files gateway failed: ${err?.message || err}`));
24118
24256
  const persistedSessions = loadPersistedSessions();
24257
+ const knownIds = new Set(persistedSessions.map((s) => s.sessionId).concat(Object.keys(loadSessionIndex())));
24119
24258
  try {
24120
- const knownIds = new Set(persistedSessions.map((s) => s.sessionId).concat(Object.keys(loadSessionIndex())));
24121
24259
  const { removed } = await sweepOrphanedStagedHomes(knownIds);
24122
24260
  if (removed.length > 0) {
24123
24261
  logger.log(`Cleaned up ${removed.length} orphaned staged home(s) from prior runs.`);
@@ -24128,7 +24266,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
24128
24266
  const sessionsToAutoContinue = [];
24129
24267
  const sessionsToLoopResume = [];
24130
24268
  {
24131
- const knownSessionIds = new Set(persistedSessions.map((p) => p.sessionId));
24269
+ const knownSessionIds = knownIds;
24132
24270
  const sweptDirs = /* @__PURE__ */ new Set();
24133
24271
  for (const p of persistedSessions) {
24134
24272
  if (sweptDirs.has(p.directory)) continue;
@@ -24153,7 +24291,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
24153
24291
  }
24154
24292
  if (persistedSessions.length > 0) {
24155
24293
  try {
24156
- const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-8SZDu7O-.mjs');
24294
+ const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-BO0Qs_kE.mjs');
24157
24295
  await awaitClaudeVersionReady();
24158
24296
  } catch {
24159
24297
  }
@@ -24410,7 +24548,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
24410
24548
  const PING_TIMEOUT_MS = 15e3;
24411
24549
  const POST_RECONNECT_GRACE_MS = 2e4;
24412
24550
  const RECONNECT_JITTER_MS = 2500;
24413
- const { WorkflowScheduler } = await import('./scheduler-bgEMu7PK.mjs');
24551
+ const { WorkflowScheduler } = await import('./scheduler-DjAVEyn3.mjs');
24414
24552
  const workflowProjectRoots = () => {
24415
24553
  const dirs = /* @__PURE__ */ new Set();
24416
24554
  for (const s of pidToTrackedSession.values()) {
@@ -1,4 +1,4 @@
1
- import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { aj as applyClaudeProxyEnv, ak as composeSessionId, al as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, am as generateHookSettings } from './run-CO4F7ZaJ.mjs';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { aj as applyClaudeProxyEnv, ak as composeSessionId, al as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, am as generateHookSettings } from './run-BBBr4jkI.mjs';
2
2
  import os from 'node:os';
3
3
  import { resolve, join } from 'node:path';
4
4
  import { existsSync, readFileSync, watch } from 'node:fs';
@@ -1,4 +1,4 @@
1
- import { f as resolveProjectRoot, C as listWorkflows, D as isWorkflowEnabled, E as workflowSchedules, F as inZone, w as runWorkflow, G as cronMatches } from './run-CO4F7ZaJ.mjs';
1
+ import { f as resolveProjectRoot, C as listWorkflows, D as isWorkflowEnabled, E as workflowSchedules, F as inZone, w as runWorkflow, G as cronMatches } from './run-BBBr4jkI.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -6,14 +6,14 @@ import 'path';
6
6
  import 'url';
7
7
  import 'child_process';
8
8
  import 'crypto';
9
- import 'node:crypto';
10
9
  import 'node:fs';
10
+ import 'node:path';
11
+ import 'node:crypto';
11
12
  import 'node:child_process';
12
13
  import 'util';
13
14
  import 'http';
14
15
  import 'net';
15
16
  import 'node:os';
16
- import 'node:path';
17
17
  import 'node:events';
18
18
  import '@agentclientprotocol/sdk';
19
19
  import 'node:util';
@@ -1,20 +1,20 @@
1
1
  import * as path from 'path';
2
- import { w as wantsHelp } from './cli-DiCcPggc.mjs';
3
- import './run-CO4F7ZaJ.mjs';
2
+ import { w as wantsHelp } from './cli-PWoHWBkF.mjs';
3
+ import './run-BBBr4jkI.mjs';
4
4
  import 'os';
5
5
  import 'fs/promises';
6
6
  import 'fs';
7
7
  import 'url';
8
8
  import 'child_process';
9
9
  import 'crypto';
10
- import 'node:crypto';
11
10
  import 'node:fs';
11
+ import 'node:path';
12
+ import 'node:crypto';
12
13
  import 'node:child_process';
13
14
  import 'util';
14
15
  import 'http';
15
16
  import 'net';
16
17
  import 'node:os';
17
- import 'node:path';
18
18
  import 'node:events';
19
19
  import '@agentclientprotocol/sdk';
20
20
  import 'node:util';
@@ -77,7 +77,7 @@ async function handleServeCommand() {
77
77
  }
78
78
  }
79
79
  async function serveAdd(args, machineId) {
80
- const { connectAndGetMachine } = await import('./commands-D4R66JMe.mjs');
80
+ const { connectAndGetMachine } = await import('./commands-CxfUr2Qr.mjs');
81
81
  const pos = positionalArgs(args);
82
82
  const name = pos[0];
83
83
  if (!name) {
@@ -110,7 +110,7 @@ async function serveAdd(args, machineId) {
110
110
  }
111
111
  if (sessionId && result?.url) {
112
112
  try {
113
- const { autoAddSessionLink } = await import('./agentCommands-BE3bFu8e.mjs');
113
+ const { autoAddSessionLink } = await import('./agentCommands-DUHUBzrj.mjs');
114
114
  autoAddSessionLink(String(result.url), name, void 0, { kind: "serve", name });
115
115
  } catch {
116
116
  }
@@ -124,7 +124,7 @@ async function serveAdd(args, machineId) {
124
124
  }
125
125
  }
126
126
  async function serveApply(args, machineId) {
127
- const { connectAndGetMachine } = await import('./commands-D4R66JMe.mjs');
127
+ const { connectAndGetMachine } = await import('./commands-CxfUr2Qr.mjs');
128
128
  const fs = await import('fs');
129
129
  const yaml = await import('yaml');
130
130
  const file = positionalArgs(args)[0];
@@ -206,7 +206,7 @@ async function serveApply(args, machineId) {
206
206
  console.log(`URL: ${result.url}`);
207
207
  if (params.sessionId && result?.url) {
208
208
  try {
209
- const { autoAddSessionLink } = await import('./agentCommands-BE3bFu8e.mjs');
209
+ const { autoAddSessionLink } = await import('./agentCommands-DUHUBzrj.mjs');
210
210
  const prevSession = process.env.SVAMP_SESSION_ID;
211
211
  process.env.SVAMP_SESSION_ID = String(params.sessionId);
212
212
  try {
@@ -227,7 +227,7 @@ async function serveApply(args, machineId) {
227
227
  }
228
228
  }
229
229
  async function serveRemove(args, machineId) {
230
- const { connectAndGetMachine } = await import('./commands-D4R66JMe.mjs');
230
+ const { connectAndGetMachine } = await import('./commands-CxfUr2Qr.mjs');
231
231
  const pos = positionalArgs(args);
232
232
  const name = pos[0];
233
233
  if (!name) {
@@ -237,7 +237,7 @@ async function serveRemove(args, machineId) {
237
237
  const { machine, server } = await connectAndGetMachine(machineId);
238
238
  try {
239
239
  await machine.serveRemove({ name });
240
- const { removeSessionLinkByService } = await import('./agentCommands-BE3bFu8e.mjs');
240
+ const { removeSessionLinkByService } = await import('./agentCommands-DUHUBzrj.mjs');
241
241
  removeSessionLinkByService("serve", name);
242
242
  console.log(`Mount '${name}' removed.`);
243
243
  } catch (err) {
@@ -249,7 +249,7 @@ async function serveRemove(args, machineId) {
249
249
  }
250
250
  }
251
251
  async function serveList(args, machineId) {
252
- const { connectAndGetMachine } = await import('./commands-D4R66JMe.mjs');
252
+ const { connectAndGetMachine } = await import('./commands-CxfUr2Qr.mjs');
253
253
  const all = hasFlag(args, "--all", "-a");
254
254
  const json = hasFlag(args, "--json");
255
255
  const sessionId = getFlag(args, "--session");
@@ -283,7 +283,7 @@ async function serveList(args, machineId) {
283
283
  }
284
284
  }
285
285
  async function serveInfo(machineId) {
286
- const { connectAndGetMachine } = await import('./commands-D4R66JMe.mjs');
286
+ const { connectAndGetMachine } = await import('./commands-CxfUr2Qr.mjs');
287
287
  const { machine, server } = await connectAndGetMachine(machineId);
288
288
  try {
289
289
  const info = await machine.serveInfo();
@@ -1,4 +1,4 @@
1
- import { R as READ_ONLY_TOOLS, M as loadMachineContext, N as buildMachineInstructions, O as machineToolsForRole, P as buildMachineTools } from './run-CO4F7ZaJ.mjs';
1
+ import { R as READ_ONLY_TOOLS, M as loadMachineContext, N as buildMachineInstructions, O as machineToolsForRole, P as buildMachineTools } from './run-BBBr4jkI.mjs';
2
2
  import 'node:child_process';
3
3
  import 'os';
4
4
  import 'fs/promises';
@@ -7,13 +7,13 @@ import 'path';
7
7
  import 'url';
8
8
  import 'child_process';
9
9
  import 'crypto';
10
- import 'node:crypto';
11
10
  import 'node:fs';
11
+ import 'node:path';
12
+ import 'node:crypto';
12
13
  import 'util';
13
14
  import 'http';
14
15
  import 'net';
15
16
  import 'node:os';
16
- import 'node:path';
17
17
  import 'node:events';
18
18
  import '@agentclientprotocol/sdk';
19
19
  import 'node:util';