svamp-cli 0.2.324 → 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-CHE1wq5m.mjs → adminCommands-D1NzA4Hb.mjs} +1 -1
  4. package/dist/{agentCommands-D7CV6DKL.mjs → agentCommands-DUHUBzrj.mjs} +5 -5
  5. package/dist/{auth-BfimMIpL.mjs → auth-DogM3PPm.mjs} +3 -3
  6. package/dist/{cli-Dv0vM2P7.mjs → cli-PWoHWBkF.mjs} +79 -79
  7. package/dist/cli.mjs +4 -4
  8. package/dist/{commands-CuyCq5eZ.mjs → commands-B5bC9eXz.mjs} +3 -3
  9. package/dist/{commands-CQK2nD3o.mjs → commands-CBk4dLUP.mjs} +3 -3
  10. package/dist/{commands-CdbvHh2p.mjs → commands-Ck4pP86Q.mjs} +4 -4
  11. package/dist/{commands-BA4LVlQW.mjs → commands-CqqQYRzg.mjs} +2 -2
  12. package/dist/{commands-GRfwAHgR.mjs → commands-CxfUr2Qr.mjs} +1 -1
  13. package/dist/{commands-4hjEGB-I.mjs → commands-D-4ry2I6.mjs} +13 -13
  14. package/dist/{commands-BQ7UFowe.mjs → commands-Dm9KNB-p.mjs} +3 -3
  15. package/dist/{commands-jaaQooYW.mjs → commands-Dt_MecCB.mjs} +3 -3
  16. package/dist/{fleet-CSh3MUcM.mjs → fleet-Bzg3YxM4.mjs} +2 -2
  17. package/dist/{headlessCli-Zl5Yu4CL.mjs → headlessCli-DvJI9DRh.mjs} +4 -4
  18. package/dist/index.mjs +3 -3
  19. package/dist/{notifyCommands-BQZXhfLt.mjs → notifyCommands-BpMErHcv.mjs} +1 -1
  20. package/dist/{package-DgboZg2o.mjs → package-D_BgajkI.mjs} +2 -2
  21. package/dist/{pinnedClaudeCode-8SZDu7O-.mjs → pinnedClaudeCode-BO0Qs_kE.mjs} +1 -1
  22. package/dist/{rpc-C1KIoUyz.mjs → rpc-27xBajCe.mjs} +3 -3
  23. package/dist/{rpc-BIzMNjqC.mjs → rpc-Qcj4M_LQ.mjs} +3 -3
  24. package/dist/{run-CEp9YpUZ.mjs → run-BBBr4jkI.mjs} +202 -47
  25. package/dist/{run-DKAROgSg.mjs → run-CocRShOR.mjs} +1 -1
  26. package/dist/{scheduler-DIYulmdV.mjs → scheduler-DjAVEyn3.mjs} +3 -3
  27. package/dist/{serveCommands-Bq5Cw3mE.mjs → serveCommands-BK-wWc_i.mjs} +12 -12
  28. package/dist/{sideband-C83128hf.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,26 +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 sharing = target.sharing;
3797
- if (!sharing || !sharing.enabled) {
3798
- this.log(`Files-gateway DENY '${sessionId}': ${userEmail} not owner and session not shared`);
3799
- deny(403, "Access denied");
3824
+ if (!capabilityGranted) {
3825
+ const userEmail = this.auth ? await this.auth.authenticate(req).catch(() => null) : null;
3826
+ if (!userEmail) {
3827
+ deny(401, "Authentication required");
3800
3828
  return;
3801
3829
  }
3802
- try {
3803
- authorizeRequest({ user: { email: userEmail } }, sharing, "admin");
3804
- } catch (err) {
3805
- this.log(`Files-gateway DENY '${sessionId}': ${userEmail} \u2014 ${err?.message || "not admin"}`);
3806
- deny(403, "Access denied");
3807
- return;
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;
3855
+ }
3808
3856
  }
3809
3857
  }
3810
3858
  const rootDir = target.cwd;
@@ -5603,6 +5651,12 @@ function validateChannel(c) {
5603
5651
  if (m === "fixed" && c.identity.fixed?.name && unsafe.test(c.identity.fixed.name)) errs.push(`identity.fixed.name must not contain < > " ' & or newlines`);
5604
5652
  for (const cl of c.identity?.callers || []) if (unsafe.test(cl.name || "")) errs.push(`caller name "${cl.name}" must not contain < > " ' & or newlines`);
5605
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
+ }
5606
5660
  return errs;
5607
5661
  }
5608
5662
  function normalizeBind(c) {
@@ -5626,6 +5680,24 @@ function channelAsyncRepliable(c) {
5626
5680
  if ((c.action?.kind ?? "message") !== "message") return false;
5627
5681
  return bindMode(c) !== "stateless";
5628
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
+ }
5629
5701
  class ChannelStore {
5630
5702
  dir;
5631
5703
  constructor(projectDir) {
@@ -5654,7 +5726,7 @@ class ChannelStore {
5654
5726
  if (errs.length) throw new Error("invalid channel: " + errs.join("; "));
5655
5727
  mkdirSync$1(this.dir, { recursive: true });
5656
5728
  const tmp = `${this._path(c.id)}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
5657
- writeFileSync$1(tmp, JSON.stringify(c, null, 2));
5729
+ writeFileSync$1(tmp, JSON.stringify(c, null, 2), { mode: 384 });
5658
5730
  renameSync(tmp, this._path(c.id));
5659
5731
  return c;
5660
5732
  }
@@ -5836,6 +5908,32 @@ curl -X POST "${uploadUrl}" \\
5836
5908
  -d '{"kwargs": {"channel": "${channel.id}", "filename": "report.pdf", "content_base64": "<base64>", "content_type": "application/pdf", "from": "your-name", "key": "${key}"}}'
5837
5909
  \`\`\``;
5838
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.` : "";
5839
5937
  return `---
5840
5938
  name: ${name}
5841
5939
  description: ${desc}
@@ -5868,7 +5966,7 @@ curl -X POST "${sendUrl}" \\
5868
5966
  -d '{"kwargs": {"channel": "${channel.id}", "message": "your message here", "from": "your-name", "key": "${key}"${sessionKv}}}'
5869
5967
  \`\`\`
5870
5968
 
5871
- 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}`;
5872
5970
  }
5873
5971
 
5874
5972
  function keysMatch(a, b) {
@@ -8091,7 +8189,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
8091
8189
  }
8092
8190
  const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
8093
8191
  const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
8094
- const { toolsForRole } = await import('./sideband-C83128hf.mjs');
8192
+ const { toolsForRole } = await import('./sideband-5ck3CDWM.mjs');
8095
8193
  const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
8096
8194
  return fmt(r2);
8097
8195
  }
@@ -8196,7 +8294,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
8196
8294
  return { ok: false, call_id: callId, status: "busy", error: "channel is busy (too many concurrent requests) \u2014 retry shortly" };
8197
8295
  }
8198
8296
  const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
8199
- const { queryCore } = await import('./commands-GRfwAHgR.mjs');
8297
+ const { queryCore } = await import('./commands-CxfUr2Qr.mjs');
8200
8298
  const timeout = c.reply?.timeout_sec || 120;
8201
8299
  let result;
8202
8300
  try {
@@ -9664,7 +9762,11 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
9664
9762
  key: c.identity?.shared_key,
9665
9763
  // The session this skill is being copied FROM — baked into `dynamic` channel
9666
9764
  // instructions as the routing target so the copied link lands in THIS session.
9667
- 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
9668
9770
  });
9669
9771
  const skillUrlsFor = (c) => {
9670
9772
  if (!channelsServiceId) return { skillUrl: void 0, sendUrl: void 0 };
@@ -10240,10 +10342,17 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
10240
10342
  return { error: e?.message || String(e) };
10241
10343
  }
10242
10344
  },
10243
- recordMeetingActivity: async (params) => {
10345
+ recordMeetingActivity: async (params, context) => {
10244
10346
  const c = channelStore.get(params.channel);
10245
10347
  if (!c || c.enabled === false) return { error: "channel not found" };
10246
- 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
+ });
10247
10356
  if (r.error || !r.sender) return { error: r.error || "unauthorized" };
10248
10357
  const text = String(params.text || "").replace(/\s+/g, " ").trim().slice(0, 1e3);
10249
10358
  if (!text) return { ok: true };
@@ -11453,6 +11562,25 @@ function handleClaudeMessage(ctx, msg) {
11453
11562
  }
11454
11563
  }
11455
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
+
11456
11584
  const KNOWN_AGENT_LAUNCHERS = ["claude", "codex", "gemini", "kimi"];
11457
11585
  function agentLauncherExists(cmd, env = process.env) {
11458
11586
  if (!cmd) return false;
@@ -19954,7 +20082,7 @@ async function startDaemon(options) {
19954
20082
  try {
19955
20083
  const dir = loadSessionIndex()[sessionId]?.directory;
19956
20084
  if (!dir) return;
19957
- const { reconcileServiceLinks } = await import('./agentCommands-D7CV6DKL.mjs');
20085
+ const { reconcileServiceLinks } = await import('./agentCommands-DUHUBzrj.mjs');
19958
20086
  const configPath = getSvampConfigPath(dir, sessionId);
19959
20087
  const config = readSvampConfig(configPath);
19960
20088
  const entries = Array.from(urls.entries());
@@ -19976,7 +20104,7 @@ async function startDaemon(options) {
19976
20104
  try {
19977
20105
  const dir = loadSessionIndex()[sessionId]?.directory;
19978
20106
  if (!dir) return;
19979
- const { reconcileServiceLinks } = await import('./agentCommands-D7CV6DKL.mjs');
20107
+ const { reconcileServiceLinks } = await import('./agentCommands-DUHUBzrj.mjs');
19980
20108
  const configPath = getSvampConfigPath(dir, sessionId);
19981
20109
  const config = readSvampConfig(configPath);
19982
20110
  const incoming = [{
@@ -19997,7 +20125,7 @@ async function startDaemon(options) {
19997
20125
  try {
19998
20126
  const dir = loadSessionIndex()[sessionId]?.directory;
19999
20127
  if (!dir) return;
20000
- const { dropServiceLinks } = await import('./agentCommands-D7CV6DKL.mjs');
20128
+ const { dropServiceLinks } = await import('./agentCommands-DUHUBzrj.mjs');
20001
20129
  const configPath = getSvampConfigPath(dir, sessionId);
20002
20130
  const config = readSvampConfig(configPath);
20003
20131
  if (dropServiceLinks(config, "serve", mountName)) {
@@ -20040,7 +20168,7 @@ async function startDaemon(options) {
20040
20168
  ensureAutoInstalledCommands(logger);
20041
20169
  (async () => {
20042
20170
  try {
20043
- const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-8SZDu7O-.mjs');
20171
+ const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-BO0Qs_kE.mjs');
20044
20172
  beginClaudeVersionReconcile((msg) => logger.log(msg));
20045
20173
  } catch (e) {
20046
20174
  logger.log(`[claude-version] check failed: ${e?.message || e}`);
@@ -22304,11 +22432,13 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
22304
22432
  } else {
22305
22433
  logger.log(`[Session ${sessionId}] Queuing inbox message for idle processing`);
22306
22434
  const existingQueue = sessionMetadata.messageQueue || [];
22307
- sessionMetadata = {
22308
- ...sessionMetadata,
22309
- messageQueue: [...existingQueue, { id: message.messageId, text: formatted, displayText: formatted, createdAt: Date.now(), inboxHop: message.hopCount ?? 0, inboxThreadId: message.threadId || message.messageId }]
22310
- };
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 };
22311
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
+ }
22312
22442
  if (!sessionWasProcessing) {
22313
22443
  setTimeout(() => processMessageQueueRef?.(), 200);
22314
22444
  }
@@ -22367,11 +22497,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
22367
22497
  });
22368
22498
  },
22369
22499
  onIssue: async (params) => {
22370
- const { issueRpc } = await import('./rpc-BIzMNjqC.mjs');
22500
+ const { issueRpc } = await import('./rpc-Qcj4M_LQ.mjs');
22371
22501
  return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
22372
22502
  },
22373
22503
  onWorkflow: async (params) => {
22374
- const { workflowRpc } = await import('./rpc-C1KIoUyz.mjs');
22504
+ const { workflowRpc } = await import('./rpc-27xBajCe.mjs');
22375
22505
  return workflowRpc(params?.cwd || directory, params || {});
22376
22506
  },
22377
22507
  onRipgrep: async (args, cwd) => {
@@ -22982,11 +23112,13 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
22982
23112
  } else {
22983
23113
  logger.log(`[${agentName} Session ${sessionId}] Queuing inbox message for idle processing`);
22984
23114
  const existingQueue = sessionMetadata.messageQueue || [];
22985
- sessionMetadata = {
22986
- ...sessionMetadata,
22987
- messageQueue: [...existingQueue, { id: message.messageId, text: formatted, displayText: formatted, createdAt: Date.now(), inboxHop: message.hopCount ?? 0, inboxThreadId: message.threadId || message.messageId }]
22988
- };
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 };
22989
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
+ }
22990
23122
  if (sessionMetadata.lifecycleState === "idle" && acpBackendReady) {
22991
23123
  const queue = sessionMetadata.messageQueue;
22992
23124
  if (queue && queue.length > 0) {
@@ -23075,11 +23207,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
23075
23207
  });
23076
23208
  },
23077
23209
  onIssue: async (params) => {
23078
- const { issueRpc } = await import('./rpc-BIzMNjqC.mjs');
23210
+ const { issueRpc } = await import('./rpc-Qcj4M_LQ.mjs');
23079
23211
  return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
23080
23212
  },
23081
23213
  onWorkflow: async (params) => {
23082
- const { workflowRpc } = await import('./rpc-C1KIoUyz.mjs');
23214
+ const { workflowRpc } = await import('./rpc-27xBajCe.mjs');
23083
23215
  return workflowRpc(params?.cwd || directory, params || {});
23084
23216
  },
23085
23217
  onRipgrep: async (args, cwd) => {
@@ -24087,20 +24219,43 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
24087
24219
  } catch {
24088
24220
  sharing = null;
24089
24221
  }
24222
+ let machineSharing = null;
24223
+ try {
24224
+ machineSharing = machineService.getCurrentMetadata()?.sharing ?? null;
24225
+ } catch {
24226
+ machineSharing = null;
24227
+ }
24090
24228
  return {
24091
24229
  cwd: session.directory,
24092
24230
  sharing,
24093
- ownerEmail: sharing?.owner || daemonOwnerEmail
24231
+ ownerEmail: sharing?.owner || daemonOwnerEmail,
24232
+ machineSharing
24094
24233
  };
24095
24234
  }
24096
24235
  }
24097
24236
  return null;
24098
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
+ });
24099
24254
  const sessionFilesToken = createHash("sha256").update(`session-files:${machineId}`).digest("hex").slice(0, 20);
24100
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}`));
24101
24256
  const persistedSessions = loadPersistedSessions();
24257
+ const knownIds = new Set(persistedSessions.map((s) => s.sessionId).concat(Object.keys(loadSessionIndex())));
24102
24258
  try {
24103
- const knownIds = new Set(persistedSessions.map((s) => s.sessionId).concat(Object.keys(loadSessionIndex())));
24104
24259
  const { removed } = await sweepOrphanedStagedHomes(knownIds);
24105
24260
  if (removed.length > 0) {
24106
24261
  logger.log(`Cleaned up ${removed.length} orphaned staged home(s) from prior runs.`);
@@ -24111,7 +24266,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
24111
24266
  const sessionsToAutoContinue = [];
24112
24267
  const sessionsToLoopResume = [];
24113
24268
  {
24114
- const knownSessionIds = new Set(persistedSessions.map((p) => p.sessionId));
24269
+ const knownSessionIds = knownIds;
24115
24270
  const sweptDirs = /* @__PURE__ */ new Set();
24116
24271
  for (const p of persistedSessions) {
24117
24272
  if (sweptDirs.has(p.directory)) continue;
@@ -24136,7 +24291,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
24136
24291
  }
24137
24292
  if (persistedSessions.length > 0) {
24138
24293
  try {
24139
- const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-8SZDu7O-.mjs');
24294
+ const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-BO0Qs_kE.mjs');
24140
24295
  await awaitClaudeVersionReady();
24141
24296
  } catch {
24142
24297
  }
@@ -24393,7 +24548,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
24393
24548
  const PING_TIMEOUT_MS = 15e3;
24394
24549
  const POST_RECONNECT_GRACE_MS = 2e4;
24395
24550
  const RECONNECT_JITTER_MS = 2500;
24396
- const { WorkflowScheduler } = await import('./scheduler-DIYulmdV.mjs');
24551
+ const { WorkflowScheduler } = await import('./scheduler-DjAVEyn3.mjs');
24397
24552
  const workflowProjectRoots = () => {
24398
24553
  const dirs = /* @__PURE__ */ new Set();
24399
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-CEp9YpUZ.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-CEp9YpUZ.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-Dv0vM2P7.mjs';
3
- import './run-CEp9YpUZ.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-GRfwAHgR.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-D7CV6DKL.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-GRfwAHgR.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-D7CV6DKL.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-GRfwAHgR.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-D7CV6DKL.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-GRfwAHgR.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-GRfwAHgR.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-CEp9YpUZ.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';