svamp-cli 0.2.92 → 0.2.94

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.
@@ -2,7 +2,7 @@ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(im
2
2
  import os from 'node:os';
3
3
  import { resolve, join } from 'node:path';
4
4
  import { existsSync, readFileSync, watch } from 'node:fs';
5
- import { c as connectToHypha, a as registerSessionService, M as generateHookSettings } from './run-BF4AmFz8.mjs';
5
+ import { c as connectToHypha, a as registerSessionService, P as generateHookSettings } from './run-NLlVMcrd.mjs';
6
6
  import { createServer } from 'node:http';
7
7
  import { spawn } from 'node:child_process';
8
8
  import { createInterface } from 'node:readline';
@@ -13,7 +13,6 @@ import 'path';
13
13
  import 'url';
14
14
  import 'child_process';
15
15
  import 'crypto';
16
- import 'ws';
17
16
  import 'util';
18
17
  import '@agentclientprotocol/sdk';
19
18
  import '@modelcontextprotocol/sdk/client/index.js';
@@ -7,7 +7,6 @@ import { execFile, spawn as spawn$1, execSync as execSync$1 } from 'child_proces
7
7
  import { randomUUID as randomUUID$1 } from 'crypto';
8
8
  import { existsSync, readFileSync, mkdirSync as mkdirSync$1, readdirSync, writeFileSync as writeFileSync$1, renameSync as renameSync$1, rmSync, appendFileSync, unlinkSync } from 'node:fs';
9
9
  import { exec, spawn, execSync, execFile as execFile$1, execFileSync } from 'node:child_process';
10
- import { WebSocket } from 'ws';
11
10
  import { promisify } from 'util';
12
11
  import { randomBytes, randomUUID, createHash } from 'node:crypto';
13
12
  import { join as join$1 } from 'node:path';
@@ -941,239 +940,6 @@ function buildSessionDeps(rpc, opts = {}) {
941
940
  };
942
941
  }
943
942
 
944
- function toolsForRole(role) {
945
- if (role === "admin") return [...READ_ONLY_TOOLS, "run_bash", "send_to_session"];
946
- if (role === "interact") return [...READ_ONLY_TOOLS, "send_to_session"];
947
- return [...READ_ONLY_TOOLS];
948
- }
949
- function toRealtimeTools(tools) {
950
- return tools.map((t) => ({ type: "function", name: t.name, description: t.description, parameters: t.parameters }));
951
- }
952
- function buildVoiceSessionUpdate(instructions, tools, opts) {
953
- return {
954
- type: "session.update",
955
- session: {
956
- type: "realtime",
957
- instructions,
958
- tools: toRealtimeTools(tools),
959
- tool_choice: "auto",
960
- ...opts?.voice ? { audio: { output: { voice: opts.voice } } } : {}
961
- }
962
- };
963
- }
964
- async function handleRealtimeEvent(ev, tools, send, nameByCallId) {
965
- if (ev?.type === "response.output_item.added" && ev.item?.type === "function_call") {
966
- if (nameByCallId && ev.item.call_id) nameByCallId.set(ev.item.call_id, ev.item.name);
967
- return null;
968
- }
969
- if (!ev || ev.type !== "response.function_call_arguments.done") return null;
970
- const callId = ev.call_id;
971
- const name = ev.name || nameByCallId && nameByCallId.get(callId) || "";
972
- let args = {};
973
- try {
974
- args = ev.arguments ? JSON.parse(ev.arguments) : {};
975
- } catch {
976
- }
977
- const tool = tools.find((t) => t.name === name);
978
- let output;
979
- if (!tool) {
980
- output = `error: tool "${name}" is not available to this caller`;
981
- } else {
982
- try {
983
- output = await tool.run(args);
984
- } catch (e) {
985
- output = `error: ${e?.message || e}`;
986
- }
987
- }
988
- send({ type: "conversation.item.create", item: { type: "function_call_output", call_id: callId, output } });
989
- send({ type: "response.create" });
990
- return { name, output };
991
- }
992
- async function initVoiceSession(init) {
993
- const ctx = await loadWiseAgentContext(init.deps, init.config);
994
- const instructions = buildWiseAgentInstructions(ctx, init.config);
995
- const granted = gateTools(buildTools(init.deps, ctx.skills), init.config, init.sender.name);
996
- return { tools: granted, sessionUpdate: buildVoiceSessionUpdate(instructions, granted, { voice: init.voice }) };
997
- }
998
- async function initMachineVoiceSession(init) {
999
- const ctx = await loadMachineContext(init.deps);
1000
- const instructions = buildMachineInstructions(ctx);
1001
- const allow = new Set(machineToolsForRole(init.role ?? "admin"));
1002
- const granted = buildMachineTools(init.deps, ctx.skills).filter((t) => allow.has(t.name));
1003
- return { tools: granted, sessionUpdate: buildVoiceSessionUpdate(instructions, granted, { voice: init.voice }) };
1004
- }
1005
-
1006
- var sideband = /*#__PURE__*/Object.freeze({
1007
- __proto__: null,
1008
- buildVoiceSessionUpdate: buildVoiceSessionUpdate,
1009
- handleRealtimeEvent: handleRealtimeEvent,
1010
- initMachineVoiceSession: initMachineVoiceSession,
1011
- initVoiceSession: initVoiceSession,
1012
- toRealtimeTools: toRealtimeTools,
1013
- toolsForRole: toolsForRole
1014
- });
1015
-
1016
- const realFactory = (url, headers) => new WebSocket(url, { headers });
1017
- let _testFactory;
1018
- const NOISY_EVENTS = /* @__PURE__ */ new Set([
1019
- "response.audio.delta",
1020
- "response.output_audio.delta",
1021
- "response.audio_transcript.delta",
1022
- "response.output_audio_transcript.delta",
1023
- "response.text.delta",
1024
- "response.output_text.delta",
1025
- "response.function_call_arguments.delta",
1026
- "input_audio_buffer.append",
1027
- "rate_limits.updated",
1028
- "conversation.item.input_audio_transcription.delta"
1029
- ]);
1030
- const tag = (callId) => `[wise-voice callId=${callId.slice(0, 14)}\u2026]`;
1031
- async function openSideband(opts) {
1032
- const base = (opts.baseUrl || "https://api.openai.com").replace(/^http/, "ws").replace(/\/+$/, "");
1033
- const url = `${base}/v1/realtime?call_id=${encodeURIComponent(opts.callId)}`;
1034
- const { tools, sessionUpdate } = opts.prepared ?? await initVoiceSession(opts.init);
1035
- const T = tag(opts.callId);
1036
- const toolNames = tools.map((t) => t.name);
1037
- console.log(`${T} OPENING sideband \u2192 ${base}/v1/realtime (key=${opts.apiKey ? "set:" + opts.apiKey.slice(0, 7) + "\u2026" : "MISSING"}) | ${toolNames.length} tools: [${toolNames.join(", ")}]`);
1038
- const ws = (opts.wsFactory || _testFactory || realFactory)(url, { Authorization: `Bearer ${opts.apiKey}` });
1039
- const send = (e) => {
1040
- try {
1041
- ws.send(JSON.stringify(e));
1042
- } catch (err) {
1043
- console.error(`${T} send failed (socket gone): ${err?.message || err}`);
1044
- }
1045
- };
1046
- const nameByCallId = /* @__PURE__ */ new Map();
1047
- let eventCount = 0;
1048
- let toolCalls = 0;
1049
- let closed = false;
1050
- const idleMs = opts.idleTimeoutMs ?? 12e4;
1051
- const lifeMs = opts.maxLifetimeMs ?? 30 * 6e4;
1052
- let idleTimer;
1053
- let lifeTimer;
1054
- const clearTimers = () => {
1055
- if (idleTimer) clearTimeout(idleTimer);
1056
- if (lifeTimer) clearTimeout(lifeTimer);
1057
- };
1058
- const closeWith = (why) => {
1059
- if (closed) return;
1060
- console.log(`${T} CLOSING (${why})`);
1061
- clearTimers();
1062
- try {
1063
- ws.close();
1064
- } catch {
1065
- }
1066
- };
1067
- const bumpIdle = () => {
1068
- if (idleTimer) clearTimeout(idleTimer);
1069
- idleTimer = setTimeout(() => closeWith(`idle ${idleMs / 1e3}s \u2014 no events, treating call as abandoned`), idleMs);
1070
- };
1071
- lifeTimer = setTimeout(() => closeWith(`max lifetime ${lifeMs / 6e4}min reached`), lifeMs);
1072
- bumpIdle();
1073
- let settled = false;
1074
- let resolveOpen;
1075
- let rejectOpen;
1076
- const opened = new Promise((res, rej) => {
1077
- resolveOpen = res;
1078
- rejectOpen = rej;
1079
- });
1080
- const connectTimer = setTimeout(() => {
1081
- if (!settled) {
1082
- settled = true;
1083
- rejectOpen(new Error("sideband connect timeout (10s)"));
1084
- try {
1085
- ws.close();
1086
- } catch {
1087
- }
1088
- }
1089
- }, 1e4);
1090
- ws.on("open", () => {
1091
- console.log(`${T} WS OPEN \u2713 \u2014 OpenAI Realtime connected; sending session.update (instructions + ${toolNames.length} tools)`);
1092
- send(sessionUpdate);
1093
- if (!settled) {
1094
- settled = true;
1095
- clearTimeout(connectTimer);
1096
- resolveOpen();
1097
- }
1098
- });
1099
- const wantTools = new Set(toolNames);
1100
- let reasserts = 0;
1101
- ws.on("message", async (data) => {
1102
- let ev;
1103
- try {
1104
- ev = JSON.parse(typeof data === "string" ? data : data?.toString?.() ?? "");
1105
- } catch {
1106
- return;
1107
- }
1108
- eventCount++;
1109
- bumpIdle();
1110
- if (ev?.type && !NOISY_EVENTS.has(ev.type)) {
1111
- if (ev.type === "error") {
1112
- console.error(`${T} \u26A0\uFE0F OpenAI error event: ${JSON.stringify(ev.error || ev).slice(0, 400)}`);
1113
- } else if (ev.type === "response.output_item.added" && ev.item?.type === "function_call") {
1114
- console.log(`${T} \u2190 model requested tool "${ev.item?.name}" (call_id=${ev.item?.call_id})`);
1115
- try {
1116
- opts.onTool?.({ callId: ev.item.call_id, name: ev.item.name || "", args: "", output: "", phase: "start" });
1117
- } catch {
1118
- }
1119
- } else if (ev.type === "response.function_call_arguments.done") {
1120
- toolCalls++;
1121
- console.log(`${T} \u2190 function_call_arguments.done call_id=${ev.call_id} name=${ev.name || nameByCallId.get(ev.call_id) || "?"} args=${String(ev.arguments || "").slice(0, 200)}`);
1122
- } else if (ev.type === "session.updated") {
1123
- const have = (ev.session?.tools || []).map((t) => t?.name);
1124
- console.log(`${T} session.updated \u2014 server now has tools: [${have.join(", ") || "NONE"}]`);
1125
- } else {
1126
- console.log(`${T} \u2190 ${ev.type}`);
1127
- }
1128
- }
1129
- if (ev?.type === "session.updated" && wantTools.size) {
1130
- const have = new Set((ev.session?.tools || []).map((t) => t?.name));
1131
- const missing = [...wantTools].some((n) => !have.has(n));
1132
- if (missing && reasserts < 10) {
1133
- reasserts++;
1134
- console.log(`${T} \u27F3 tools were clobbered (peer session.update) \u2014 re-asserting #${reasserts}`);
1135
- send(sessionUpdate);
1136
- return;
1137
- }
1138
- }
1139
- try {
1140
- const r = await handleRealtimeEvent(ev, tools, send, nameByCallId);
1141
- if (r) {
1142
- console.log(`${T} \u2192 tool "${r.name}" executed \u2192 ${String(r.output).slice(0, 160).replace(/\n/g, " ")}`);
1143
- try {
1144
- opts.onTool?.({ callId: ev.call_id, name: r.name, args: String(ev.arguments || ""), output: r.output, phase: "done" });
1145
- } catch {
1146
- }
1147
- }
1148
- } catch (e) {
1149
- console.error(`${T} dispatch error: ${e?.message || e}`);
1150
- opts.onError?.(e instanceof Error ? e : new Error(String(e)));
1151
- }
1152
- });
1153
- ws.on("close", (code, reason) => {
1154
- closed = true;
1155
- clearTimers();
1156
- clearTimeout(connectTimer);
1157
- console.log(`${T} WS CLOSED (code=${code ?? "?"} reason=${reason ? String(reason).slice(0, 120) : ""}) \u2014 ${eventCount} events, ${toolCalls} tool calls`);
1158
- if (!settled) {
1159
- settled = true;
1160
- rejectOpen(new Error(`sideband closed before open (code ${code ?? "?"})`));
1161
- }
1162
- opts.onClose?.();
1163
- });
1164
- ws.on("error", (e) => {
1165
- console.error(`${T} WS ERROR: ${e?.message || e}`);
1166
- clearTimeout(connectTimer);
1167
- if (!settled) {
1168
- settled = true;
1169
- rejectOpen(e instanceof Error ? e : new Error(String(e)));
1170
- }
1171
- opts.onError?.(e instanceof Error ? e : new Error(String(e)));
1172
- });
1173
- await opened;
1174
- return { callId: opts.callId, close: () => closeWith("explicit close (release / new attach / shutdown)") };
1175
- }
1176
-
1177
943
  const execFileAsync$1 = promisify(execFile);
1178
944
  function parseEtime(s) {
1179
945
  if (!s) return 0;
@@ -1352,51 +1118,6 @@ async function registerMachineService(server, machineId, metadata, daemonState,
1352
1118
  lastInboundRpcAt = Date.now();
1353
1119
  };
1354
1120
  const listeners = [];
1355
- const voiceSidebands = /* @__PURE__ */ new Map();
1356
- const prepareVoiceSession = async (params, context) => {
1357
- const senderName = context?.user?.email || context?.user?.id || "user";
1358
- if (params.sessionId) {
1359
- const rpc = handlers.getSessionRPCHandlers?.(params.sessionId);
1360
- if (!rpc) return { ok: false, error: "Session not found on this machine" };
1361
- let cwd;
1362
- let owner;
1363
- let role2;
1364
- try {
1365
- role2 = (await rpc.getEffectiveRole(context))?.role ?? null;
1366
- if (!role2) return { ok: false, error: "Not authorized for this session" };
1367
- const metaRes = await rpc.getMetadata(context);
1368
- cwd = metaRes?.metadata?.path;
1369
- owner = metaRes?.metadata?.sharing?.owner;
1370
- } catch {
1371
- return { ok: false, error: "Not authorized for this session" };
1372
- }
1373
- try {
1374
- const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
1375
- const prepared = await initVoiceSession({ deps, config: { tools: toolsForRole(role2) }, sender: { name: senderName, kind: "user", verified: true }, voice: params.voice });
1376
- return { ok: true, prepared, role: role2, scope: "session" };
1377
- } catch (e) {
1378
- return { ok: false, error: e?.message || "Failed to prepare voice session" };
1379
- }
1380
- }
1381
- const role = getEffectiveRole(context, currentMetadata.sharing);
1382
- if (!role) return { ok: false, error: "Not authorized on this machine" };
1383
- try {
1384
- const machineDeps = buildMachineDeps(
1385
- {
1386
- getSessionIds: handlers.getSessionIds,
1387
- getSessionRPCHandlers: handlers.getSessionRPCHandlers,
1388
- getTrackedSessions: handlers.getTrackedSessions,
1389
- spawnSession: handlers.spawnSession,
1390
- getDaemonStatus: () => ({ status: currentDaemonState.status, machineId, pid: currentDaemonState.pid })
1391
- },
1392
- { cwd: process.env.HOME, ownerEmail: currentMetadata.sharing?.owner }
1393
- );
1394
- const prepared = await initMachineVoiceSession({ deps: machineDeps, role, voice: params.voice });
1395
- return { ok: true, prepared, role, scope: "machine" };
1396
- } catch (e) {
1397
- return { ok: false, error: e?.message || "Failed to prepare voice session" };
1398
- }
1399
- };
1400
1121
  const removeListener = (listener, reason) => {
1401
1122
  const idx = listeners.indexOf(listener);
1402
1123
  if (idx >= 0) {
@@ -2332,7 +2053,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
2332
2053
  const tunnels = handlers.tunnels;
2333
2054
  if (!tunnels) throw new Error("Tunnel management not available");
2334
2055
  if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
2335
- const { FrpcTunnel } = await import('./frpc-D_1pEpUY.mjs');
2056
+ const { FrpcTunnel } = await import('./frpc-ki1LtiZY.mjs');
2336
2057
  const tunnel = new FrpcTunnel({
2337
2058
  name: params.name,
2338
2059
  ports: params.ports,
@@ -2489,159 +2210,6 @@ async function registerMachineService(server, machineId, metadata, daemonState,
2489
2210
  return { success: false, error: error instanceof Error ? error.message : "Failed to create token" };
2490
2211
  }
2491
2212
  },
2492
- // Attach the daemon's server-side control to an in-flight Realtime voice
2493
- // session (browser holds the WebRTC audio leg). The browser passes the
2494
- // `call_id` it got from OpenAI's SDP answer; the daemon opens the sideband
2495
- // WS and runs WISE tools server-side, gated by the caller's session role.
2496
- wiseAttachSideband: async (params, context) => {
2497
- trackInbound();
2498
- if (context && (!context.user || context.user.is_anonymous)) {
2499
- return { success: false, error: "Sign in to use WISE voice." };
2500
- }
2501
- if (!params?.callId) return { success: false, error: "callId is required" };
2502
- const resolved = resolveModel({ provider: "openai" }, process.env);
2503
- const misconfig = describeMisconfiguration(resolved);
2504
- if (misconfig || !resolved.apiKey) {
2505
- return { success: false, error: misconfig || "WISE voice is not configured on this machine." };
2506
- }
2507
- const userKey = (context?.user?.email || context?.user?.id || "anon").toLowerCase();
2508
- const prep = await prepareVoiceSession(params, context);
2509
- if (!prep.ok) return { success: false, error: prep.error };
2510
- const { prepared, role } = prep;
2511
- const scope = params.sessionId ? `session ${params.sessionId}` : "machine-global";
2512
- console.log(`[wise-voice] ATTACH request user=${userKey} scope=${scope} role=${role} callId=${params.callId.slice(0, 16)}\u2026 tools=[${(prepared.tools || []).map((t) => t.name).join(", ")}]`);
2513
- const prior = voiceSidebands.get(userKey);
2514
- if (prior) {
2515
- console.log(`[wise-voice] superseding prior sideband for user=${userKey} (callId=${prior.callId.slice(0, 16)}\u2026) \u2014 closing to avoid a double bill`);
2516
- prior.close();
2517
- voiceSidebands.delete(userKey);
2518
- }
2519
- try {
2520
- const ephemeralKey = await mintRealtimeEphemeralKey(resolved.baseUrl, resolved.apiKey, { model: params.model, voice: params.voice });
2521
- const handle = await openSideband({
2522
- callId: params.callId,
2523
- apiKey: ephemeralKey,
2524
- baseUrl: resolved.baseUrl,
2525
- prepared,
2526
- onClose: () => {
2527
- if (voiceSidebands.get(userKey)?.callId === params.callId) voiceSidebands.delete(userKey);
2528
- }
2529
- });
2530
- voiceSidebands.set(userKey, handle);
2531
- console.log(`[wise-voice] ATTACH ok user=${userKey} scope=${scope} \u2014 sideband live (active sidebands: ${voiceSidebands.size})`);
2532
- return { success: true, role, scope: params.sessionId ? "session" : "machine" };
2533
- } catch (e) {
2534
- console.error(`[wise-voice] ATTACH FAILED user=${userKey}: ${e?.message || e}`);
2535
- return { success: false, error: e?.message || "Failed to attach voice sideband" };
2536
- }
2537
- },
2538
- // UNIFIED INTERFACE (fixes the call_id 404): the DAEMON creates the WebRTC
2539
- // call so it OWNS it and can attach the sideband WS. The browser sends its SDP
2540
- // offer here (via the connect-time fetch reroute); we POST it to
2541
- // /v1/realtime/calls with the machine key + WISE session config (tools +
2542
- // instructions baked in), open the sideband to execute tools, and return the
2543
- // SDP answer + call_id for the browser to finish the WebRTC handshake.
2544
- wiseCreateCall: async (params, context) => {
2545
- trackInbound();
2546
- if (context && (!context.user || context.user.is_anonymous)) {
2547
- return { success: false, error: "Sign in to use WISE voice." };
2548
- }
2549
- if (!params?.sdp) return { success: false, error: "sdp offer is required" };
2550
- const resolved = resolveModel({ provider: "openai" }, process.env);
2551
- const misconfig = describeMisconfiguration(resolved);
2552
- if (misconfig || !resolved.apiKey) {
2553
- return { success: false, error: misconfig || "WISE voice is not configured on this machine." };
2554
- }
2555
- const userKey = (context?.user?.email || context?.user?.id || "anon").toLowerCase();
2556
- const prep = await prepareVoiceSession(params, context);
2557
- if (!prep.ok) return { success: false, error: prep.error };
2558
- const { prepared, role } = prep;
2559
- const scope = params.sessionId ? `session ${params.sessionId}` : "machine-global";
2560
- const model = params.model || "gpt-realtime-mini";
2561
- console.log(`[wise-voice] CREATE-CALL request user=${userKey} scope=${scope} role=${role} model=${model} tools=[${(prepared.tools || []).map((t) => t.name).join(", ")}]`);
2562
- const base = resolved.baseUrl || "https://api.openai.com";
2563
- let answerSdp;
2564
- let callId;
2565
- try {
2566
- const sessionConfig = { ...prepared.sessionUpdate.session, model };
2567
- const fd = new FormData();
2568
- fd.set("sdp", params.sdp);
2569
- fd.set("session", JSON.stringify(sessionConfig));
2570
- const ctrl = new AbortController();
2571
- const timer = setTimeout(() => ctrl.abort(), 2e4);
2572
- let resp;
2573
- try {
2574
- resp = await fetch(`${base}/v1/realtime/calls`, {
2575
- method: "POST",
2576
- headers: { "Authorization": `Bearer ${resolved.apiKey}` },
2577
- body: fd,
2578
- signal: ctrl.signal
2579
- });
2580
- } finally {
2581
- clearTimeout(timer);
2582
- }
2583
- if (!resp.ok) {
2584
- const body = await resp.text().catch(() => "");
2585
- console.error(`[wise-voice] CREATE-CALL OpenAI error ${resp.status}: ${body.slice(0, 300)}`);
2586
- return { success: false, error: `OpenAI call create failed (HTTP ${resp.status})${body ? `: ${body.slice(0, 160)}` : ""}` };
2587
- }
2588
- answerSdp = await resp.text();
2589
- const loc = resp.headers.get("Location") || "";
2590
- callId = loc.split("/").filter(Boolean).pop() || "";
2591
- console.log(`[wise-voice] CREATE-CALL ok callId=${callId.slice(0, 16)}\u2026 (SDP answer ${answerSdp.length}b, Location="${loc}")`);
2592
- } catch (e) {
2593
- console.error(`[wise-voice] CREATE-CALL failed: ${e?.message || e}`);
2594
- return { success: false, error: e?.message || "Failed to create voice call" };
2595
- }
2596
- let sidebandOk = false;
2597
- let sidebandError;
2598
- if (callId) {
2599
- voiceSidebands.get(userKey)?.close();
2600
- voiceSidebands.delete(userKey);
2601
- try {
2602
- const handle = await openSideband({
2603
- callId,
2604
- apiKey: resolved.apiKey,
2605
- baseUrl: resolved.baseUrl,
2606
- prepared,
2607
- onClose: () => {
2608
- if (voiceSidebands.get(userKey)?.callId === callId) voiceSidebands.delete(userKey);
2609
- },
2610
- // Push each tool's name/args/output to the browser (fire-and-forget)
2611
- // so it can render accurate tool rows the SDK can't provide.
2612
- onTool: params.onToolEvent ? (ev) => {
2613
- try {
2614
- const p = params.onToolEvent(ev);
2615
- if (p && typeof p.catch === "function") p.catch(() => {
2616
- });
2617
- } catch {
2618
- }
2619
- } : void 0
2620
- });
2621
- voiceSidebands.set(userKey, handle);
2622
- sidebandOk = true;
2623
- console.log(`[wise-voice] CREATE-CALL sideband live user=${userKey} (active: ${voiceSidebands.size})`);
2624
- } catch (e) {
2625
- sidebandError = e?.message || String(e);
2626
- console.error(`[wise-voice] CREATE-CALL sideband FAILED: ${sidebandError}`);
2627
- }
2628
- } else {
2629
- sidebandError = "no call_id in OpenAI response";
2630
- }
2631
- return { success: true, sdp: answerSdp, callId, sidebandOk, sidebandError, role, scope: params.sessionId ? "session" : "machine" };
2632
- },
2633
- // Explicit teardown of the caller's active voice sideband (e.g. on hang-up,
2634
- // Stop, tab close/unload). The frontend calls this so we don't wait for the
2635
- // idle backstop — release the billable OpenAI session immediately.
2636
- wiseReleaseVoice: async (_params, context) => {
2637
- trackInbound();
2638
- const userKey = (context?.user?.email || context?.user?.id || "anon").toLowerCase();
2639
- const had = voiceSidebands.get(userKey);
2640
- if (had) console.log(`[wise-voice] RELEASE user=${userKey} callId=${had.callId.slice(0, 16)}\u2026 (explicit hang-up)`);
2641
- had?.close();
2642
- voiceSidebands.delete(userKey);
2643
- return { success: true };
2644
- },
2645
2213
  // Text WISE turn. GLOBAL (machine-manager) by default; pass sessionId to
2646
2214
  // scope to a session. Runs server-side and returns the reply synchronously.
2647
2215
  wiseAsk: async (params, context) => {
@@ -2673,8 +2241,8 @@ async function registerMachineService(server, machineId, metadata, daemonState,
2673
2241
  }
2674
2242
  const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
2675
2243
  const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
2676
- const { toolsForRole: toolsForRole2 } = await Promise.resolve().then(function () { return sideband; });
2677
- const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole2(role2) }, deps, transport, model: resolved.model });
2244
+ const { toolsForRole } = await import('./sideband-BFBWJWur.mjs');
2245
+ const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
2678
2246
  return fmt(r2);
2679
2247
  }
2680
2248
  const role = getEffectiveRole(context, currentMetadata.sharing);
@@ -2782,13 +2350,6 @@ ${d?.error || "not found"}`;
2782
2350
  for (const listener of toRemove) {
2783
2351
  removeListener(listener, "disconnect");
2784
2352
  }
2785
- for (const h of voiceSidebands.values()) {
2786
- try {
2787
- h.close();
2788
- } catch {
2789
- }
2790
- }
2791
- voiceSidebands.clear();
2792
2353
  await server.unregisterService(serviceInfo.id);
2793
2354
  await server.unregisterService(channelsServiceInfo.id).catch(() => {
2794
2355
  });
@@ -3534,7 +3095,7 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
3534
3095
  const skillUrlsFor = (c) => {
3535
3096
  if (!channelsServiceId) return { skillUrl: void 0, sendUrl: void 0 };
3536
3097
  const gw = gatewayBase(channelsServiceId, channelsBaseUrl);
3537
- return { skillUrl: `${gw}/describe?channel=${c.id}`, sendUrl: `${gw}/send` };
3098
+ return { skillUrl: `${gw}/skill?channel=${c.id}`, sendUrl: `${gw}/send` };
3538
3099
  };
3539
3100
  const syncChannelsToMetadata = () => {
3540
3101
  metadata.channels = channelStore.list();
@@ -10416,7 +9977,7 @@ async function startDaemon(options) {
10416
9977
  const list = loadExposedTunnels().filter((t) => t.name !== name);
10417
9978
  saveExposedTunnels(list);
10418
9979
  }
10419
- const { ServeManager } = await import('./serveManager-DNlqB0r6.mjs');
9980
+ const { ServeManager } = await import('./serveManager-oYpBlNC6.mjs');
10420
9981
  const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
10421
9982
  ensureAutoInstalledSkills(logger).catch(() => {
10422
9983
  });
@@ -13044,7 +12605,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
13044
12605
  const specs = loadExposedTunnels();
13045
12606
  if (specs.length === 0) return;
13046
12607
  logger.log(`[exposed-tunnels] Restoring ${specs.length} tunnel(s) from ${EXPOSED_TUNNELS_FILE}`);
13047
- const { FrpcTunnel } = await import('./frpc-D_1pEpUY.mjs');
12608
+ const { FrpcTunnel } = await import('./frpc-ki1LtiZY.mjs');
13048
12609
  for (const spec of specs) {
13049
12610
  if (tunnels.has(spec.name)) continue;
13050
12611
  try {
@@ -13816,4 +13377,4 @@ var run = /*#__PURE__*/Object.freeze({
13816
13377
  writeStopMarker: writeStopMarker
13817
13378
  });
13818
13379
 
13819
- export { normalizeAllowedUser as A, loadSecurityContextConfig as B, resolveSecurityContext as C, buildSecurityContextFromFlags as D, mergeSecurityContexts as E, buildSessionShareUrl as F, computeOutboundHop as G, buildMachineShareUrl as H, handleRealtimeEvent as I, describeMisconfiguration as J, buildMachineDeps as K, initMachineVoiceSession as L, generateHookSettings as M, DefaultTransport$1 as N, acpBackend as O, acpAgentConfig as P, codexMcpBackend as Q, RoutineStore as R, ServeAuth as S, GeminiTransport$1 as T, claudeAuth as U, instanceConfig as V, api as W, run as X, registerSessionService as a, stopDaemon as b, connectToHypha as c, daemonStatus as d, clearStopMarker as e, stopMarkerExists as f, getHyphaServerUrl$1 as g, getFrpsSubdomainHost as h, getFrpsServerPort as i, getFrpsServerAddr as j, getHyphaServerUrl as k, hasCookieToken as l, RoutineRunner as m, getSkillsServer as n, getSkillsWorkspaceName as o, parseFrontmatter as p, getSkillsCollectionName as q, registerMachineService as r, startDaemon as s, fetchWithTimeout as t, searchSkills as u, SKILLS_DIR as v, getSkillInfo as w, downloadSkillFile as x, listSkillFiles as y, resolveModel as z };
13380
+ export { loadMachineContext as A, buildMachineInstructions as B, machineToolsForRole as C, buildMachineTools as D, resolveModel as E, normalizeAllowedUser as F, loadSecurityContextConfig as G, resolveSecurityContext as H, buildSecurityContextFromFlags as I, mergeSecurityContexts as J, buildSessionShareUrl as K, computeOutboundHop as L, buildMachineShareUrl as M, describeMisconfiguration as N, buildMachineDeps as O, generateHookSettings as P, DefaultTransport$1 as Q, RoutineStore as R, ServeAuth as S, acpBackend as T, acpAgentConfig as U, codexMcpBackend as V, GeminiTransport$1 as W, claudeAuth as X, instanceConfig as Y, api as Z, run as _, registerSessionService as a, stopDaemon as b, connectToHypha as c, daemonStatus as d, clearStopMarker as e, stopMarkerExists as f, getHyphaServerUrl$1 as g, getFrpsSubdomainHost as h, getFrpsServerPort as i, getFrpsServerAddr as j, getHyphaServerUrl as k, hasCookieToken as l, RoutineRunner as m, getSkillsServer as n, getSkillsWorkspaceName as o, parseFrontmatter as p, getSkillsCollectionName as q, registerMachineService as r, startDaemon as s, fetchWithTimeout as t, searchSkills as u, SKILLS_DIR as v, getSkillInfo as w, downloadSkillFile as x, listSkillFiles as y, READ_ONLY_TOOLS as z };
@@ -54,7 +54,7 @@ async function handleServeCommand() {
54
54
  }
55
55
  }
56
56
  async function serveAdd(args, machineId) {
57
- const { connectAndGetMachine } = await import('./commands-BR_lAgZ0.mjs');
57
+ const { connectAndGetMachine } = await import('./commands-fEd97uFk.mjs');
58
58
  const pos = positionalArgs(args);
59
59
  const name = pos[0];
60
60
  if (!name) {
@@ -93,7 +93,7 @@ async function serveAdd(args, machineId) {
93
93
  }
94
94
  }
95
95
  async function serveApply(args, machineId) {
96
- const { connectAndGetMachine } = await import('./commands-BR_lAgZ0.mjs');
96
+ const { connectAndGetMachine } = await import('./commands-fEd97uFk.mjs');
97
97
  const fs = await import('fs');
98
98
  const yaml = await import('yaml');
99
99
  const file = positionalArgs(args)[0];
@@ -182,7 +182,7 @@ async function serveApply(args, machineId) {
182
182
  }
183
183
  }
184
184
  async function serveRemove(args, machineId) {
185
- const { connectAndGetMachine } = await import('./commands-BR_lAgZ0.mjs');
185
+ const { connectAndGetMachine } = await import('./commands-fEd97uFk.mjs');
186
186
  const pos = positionalArgs(args);
187
187
  const name = pos[0];
188
188
  if (!name) {
@@ -202,7 +202,7 @@ async function serveRemove(args, machineId) {
202
202
  }
203
203
  }
204
204
  async function serveList(args, machineId) {
205
- const { connectAndGetMachine } = await import('./commands-BR_lAgZ0.mjs');
205
+ const { connectAndGetMachine } = await import('./commands-fEd97uFk.mjs');
206
206
  const all = hasFlag(args, "--all", "-a");
207
207
  const json = hasFlag(args, "--json");
208
208
  const sessionId = getFlag(args, "--session");
@@ -235,7 +235,7 @@ async function serveList(args, machineId) {
235
235
  }
236
236
  }
237
237
  async function serveInfo(machineId) {
238
- const { connectAndGetMachine } = await import('./commands-BR_lAgZ0.mjs');
238
+ const { connectAndGetMachine } = await import('./commands-fEd97uFk.mjs');
239
239
  const { machine, server } = await connectAndGetMachine(machineId);
240
240
  try {
241
241
  const info = await machine.serveInfo();
@@ -4,13 +4,12 @@ import * as fs from 'fs';
4
4
  import * as http from 'http';
5
5
  import * as net from 'net';
6
6
  import * as path from 'path';
7
- import { k as getHyphaServerUrl, S as ServeAuth, l as hasCookieToken } from './run-BF4AmFz8.mjs';
7
+ import { k as getHyphaServerUrl, S as ServeAuth, l as hasCookieToken } from './run-NLlVMcrd.mjs';
8
8
  import 'os';
9
9
  import 'fs/promises';
10
10
  import 'url';
11
11
  import 'node:fs';
12
12
  import 'node:child_process';
13
- import 'ws';
14
13
  import 'util';
15
14
  import 'node:crypto';
16
15
  import 'node:path';
@@ -713,7 +712,7 @@ class ServeManager {
713
712
  const mount = this.mounts.get(mountName);
714
713
  const subdomainOverride = mount?.access === "link" && mount.linkToken ? /* @__PURE__ */ new Map([[this.port, `static-${subdomainSafe}-${mount.linkToken}`]]) : void 0;
715
714
  try {
716
- const { FrpcTunnel } = await import('./frpc-D_1pEpUY.mjs');
715
+ const { FrpcTunnel } = await import('./frpc-ki1LtiZY.mjs');
717
716
  let tunnel;
718
717
  tunnel = new FrpcTunnel({
719
718
  name: tunnelName,