svamp-cli 0.2.342 → 0.2.346
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.
- package/bin/skills/artifact/SKILL.md +1 -1
- package/bin/skills/crew/SKILL.md +21 -3
- package/dist/DefaultTransport-vfp78fug.mjs +47 -0
- package/dist/GeminiTransport-3OkhtFRO.mjs +130 -0
- package/dist/KimiTransport-D8n009Tr.mjs +141 -0
- package/dist/acpAgentConfig-CObOPlA8.mjs +48 -0
- package/dist/acpBackend-BZ1MJhQV.mjs +922 -0
- package/dist/{adminCommands-CRTBDZan.mjs → adminCommands-BvsYnRDA.mjs} +2 -18
- package/dist/{agentCommands-BuO988tJ.mjs → agentCommands-B2Nl7Fof.mjs} +6 -23
- package/dist/api-BHjyC0Ph.mjs +157 -0
- package/dist/{auth--WS0wezs.mjs → auth-CRz8TsEl.mjs} +1 -22
- package/dist/bootStatus-DqtNgJch.mjs +143 -0
- package/dist/claudeAuth-iAerdaMr.mjs +247 -0
- package/dist/cli.mjs +2939 -5
- package/dist/codexAppServerBackend-VGsYtbqg.mjs +786 -0
- package/dist/codexProvider-BtxYc5Rj.mjs +68 -0
- package/dist/{commands-DXM17M6n.mjs → commands-BB1Kcyt-.mjs} +13 -19
- package/dist/{commands-DU4B-4Gh.mjs → commands-BOpoVBOM.mjs} +55 -25
- package/dist/{commands-C0HQiu-S.mjs → commands-C2aODeVc.mjs} +58 -33
- package/dist/{commands-Cwxy-C5N.mjs → commands-CiRCwjkJ.mjs} +10 -32
- package/dist/{commands-DDuGBSsz.mjs → commands-D1pZq3d4.mjs} +103 -41
- package/dist/{commands-BKb1g8Gq.mjs → commands-D69mnkYu.mjs} +20 -30
- package/dist/{commands-BsKgZ5sQ.mjs → commands-DW5RnHZw.mjs} +2 -19
- package/dist/{commands-DXLOKJOj.mjs → commands-H4PsTGn8.mjs} +58 -26
- package/dist/cron-hHGdb5kU.mjs +132 -0
- package/dist/detectIsolation-CzoDCEnh.mjs +319 -0
- package/dist/envFlag-COOuJ3AH.mjs +11 -0
- package/dist/fileLock-BMPwCrB6.mjs +80 -0
- package/dist/{fleet-BHzlAvnu.mjs → fleet-DM_Q7w69.mjs} +85 -24
- package/dist/flushExit-C-i6SGjj.mjs +37 -0
- package/dist/friendlyName-1UZcyDhu.mjs +586 -0
- package/dist/{headlessCli-C2gvOpOt.mjs → headlessCli-C8HzQvis.mjs} +3 -21
- package/dist/hookSettings-DYMTUgVA.mjs +10399 -0
- package/dist/{httpServer-Be1Hcddr.mjs → httpServer-BzEDbOGn.mjs} +14 -5
- package/dist/hyphaClient-ybivOjZo.mjs +49 -0
- package/dist/index.mjs +40 -8
- package/dist/instance-CeHz5C8v.mjs +203 -0
- package/dist/kimiProvider-DbxODebR.mjs +69 -0
- package/dist/kimiSetup-NGsoYB5h.mjs +48 -0
- package/dist/loopVerify-B47sSjxe.mjs +444 -0
- package/dist/machineTools-DygM1jXW.mjs +580 -0
- package/dist/model-CUHojegn.mjs +66 -0
- package/dist/{notifyCommands-0aZUwKQK.mjs → notifyCommands-D-IKIxiq.mjs} +3 -18
- package/dist/package-DtuINqf5.mjs +64 -0
- package/dist/pinnedCodex-XW4qvOAe.mjs +11 -0
- package/dist/pinnedNono-5ZHb6328.mjs +15 -0
- package/dist/{rpc-rAQQ1q8t.mjs → rpc-BB6RF4-F.mjs} +38 -6
- package/dist/{rpc-B2Lc2BAB.mjs → rpc-Cf0grcru.mjs} +28 -20
- package/dist/{run-C7QVfyw4.mjs → run-ChdsgaSD.mjs} +17 -9
- package/dist/run-DkjwF-zA.mjs +9695 -0
- package/dist/sandboxDetect-Cdzsj5TC.mjs +19 -0
- package/dist/{scheduler-CzF4lOe_.mjs → scheduler-BY30ZrB9.mjs} +38 -6
- package/dist/securityContext-Cm5UTCuD.mjs +218 -0
- package/dist/serveAuth-DbVLRCRR.mjs +418 -0
- package/dist/{serveCommands-EIEqdaVu.mjs → serveCommands-CUHUiYnZ.mjs} +13 -34
- package/dist/sharingNotificationSync-qGIQ-3yf.mjs +359 -0
- package/dist/{sideband-BoutiRJi.mjs → sideband-68HhGT0i.mjs} +1 -21
- package/dist/store-Bn0ByYev.mjs +347 -0
- package/dist/store-fi0f-ff1.mjs +184 -0
- package/dist/types-57J3Re0n.mjs +17 -0
- package/dist/wantsHelp-6bMOeNUz.mjs +5 -0
- package/dist/wrapCommand-CdNawSaa.mjs +117 -0
- package/package.json +3 -3
- package/dist/cli-B24CmIli.mjs +0 -2780
- package/dist/package-CTbGYaJG.mjs +0 -64
- package/dist/run-CF4kAVPW.mjs +0 -26508
- package/dist/sandboxDetect-DNTcbgWD.mjs +0 -12
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
import { exec } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
const SKILL_START = "<<<WISE_SKILL ";
|
|
4
|
+
const SKILL_END = "<<<END_WISE_SKILL>>>";
|
|
5
|
+
function buildSkillsScanCommand() {
|
|
6
|
+
return [
|
|
7
|
+
'for d in "$HOME/.svamp/skills" ".svamp/skills"; do',
|
|
8
|
+
' for f in "$d"/*/SKILL.md; do',
|
|
9
|
+
' [ -f "$f" ] || continue;',
|
|
10
|
+
` printf '${SKILL_START}%s>>>\\n' "$f";`,
|
|
11
|
+
' cat "$f";',
|
|
12
|
+
` printf '\\n${SKILL_END}\\n';`,
|
|
13
|
+
" done;",
|
|
14
|
+
"done"
|
|
15
|
+
].join("\n");
|
|
16
|
+
}
|
|
17
|
+
function parseWiseMd(raw) {
|
|
18
|
+
const m = raw.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
|
|
19
|
+
if (!m) return { body: raw.trim() };
|
|
20
|
+
return { body: m[2].trim() };
|
|
21
|
+
}
|
|
22
|
+
function parseSkillFrontmatter(md) {
|
|
23
|
+
const m = md.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
|
|
24
|
+
if (!m) return { name: "", description: "", body: md.trim() };
|
|
25
|
+
const fm = m[1];
|
|
26
|
+
const body = m[2].trim();
|
|
27
|
+
const field = (key) => {
|
|
28
|
+
const fmatch = fm.match(new RegExp(`^${key}\\s*:\\s*(.+)$`, "m"));
|
|
29
|
+
return fmatch ? fmatch[1].trim().replace(/^["']|["']$/g, "").trim() : "";
|
|
30
|
+
};
|
|
31
|
+
return { name: field("name"), description: field("description"), body };
|
|
32
|
+
}
|
|
33
|
+
function parseSkillsDump(dump) {
|
|
34
|
+
if (!dump?.trim()) return [];
|
|
35
|
+
const byName = /* @__PURE__ */ new Map();
|
|
36
|
+
const blocks = dump.split(SKILL_START).slice(1);
|
|
37
|
+
for (const block of blocks) {
|
|
38
|
+
const headerEnd = block.indexOf(">>>\n");
|
|
39
|
+
if (headerEnd === -1) continue;
|
|
40
|
+
const path = block.slice(0, headerEnd).trim();
|
|
41
|
+
const rest = block.slice(headerEnd + 4);
|
|
42
|
+
const endIdx = rest.indexOf(SKILL_END);
|
|
43
|
+
const md = (endIdx === -1 ? rest : rest.slice(0, endIdx)).replace(/\n$/, "");
|
|
44
|
+
const { name, description, body } = parseSkillFrontmatter(md);
|
|
45
|
+
const skillName = name || path.split("/").slice(-2, -1)[0] || path;
|
|
46
|
+
if (!description && !body) continue;
|
|
47
|
+
byName.set(skillName, { name: skillName, description, path, body });
|
|
48
|
+
}
|
|
49
|
+
return Array.from(byName.values());
|
|
50
|
+
}
|
|
51
|
+
function findSkill(skills, name) {
|
|
52
|
+
const n = (name || "").trim().toLowerCase();
|
|
53
|
+
if (!n) return void 0;
|
|
54
|
+
return skills.find((s) => s.name.toLowerCase() === n) ?? skills.find((s) => s.name.toLowerCase().includes(n));
|
|
55
|
+
}
|
|
56
|
+
function formatSkillIndex(skills, maxDescLen = 140) {
|
|
57
|
+
if (skills.length === 0) return "";
|
|
58
|
+
return skills.map((s) => {
|
|
59
|
+
const d = s.description.length > maxDescLen ? s.description.slice(0, maxDescLen - 1) + "\u2026" : s.description;
|
|
60
|
+
return `- ${s.name} \u2014 ${d || "(no description)"}`;
|
|
61
|
+
}).join("\n");
|
|
62
|
+
}
|
|
63
|
+
function buildSkillsPromptSection(skills) {
|
|
64
|
+
if (skills.length === 0) return "";
|
|
65
|
+
return [
|
|
66
|
+
"## Skills (load on demand \u2014 do NOT assume the steps)",
|
|
67
|
+
"These are named procedures for this project. Only their names + descriptions are shown.",
|
|
68
|
+
'When a request matches one, call use_skill("name") to load its full steps, then carry them',
|
|
69
|
+
"out with run_bash. Skip skills you don't need.",
|
|
70
|
+
"",
|
|
71
|
+
"Available skills:",
|
|
72
|
+
formatSkillIndex(skills)
|
|
73
|
+
].join("\n");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const READ_ONLY_TOOLS = ["get_context", "ask_session", "summarize_session", "use_skill"];
|
|
77
|
+
const MUTATING_TOOLS = ["run_bash", "send_to_session", "create_loop", "set_checklist", "stop_loop", "create_channel"];
|
|
78
|
+
const str$1 = (v) => v == null ? "" : String(v);
|
|
79
|
+
function buildTools(deps, skills) {
|
|
80
|
+
return [
|
|
81
|
+
{
|
|
82
|
+
name: "get_context",
|
|
83
|
+
readOnly: true,
|
|
84
|
+
description: 'Orient: status and recent activity of the bound session/machine. Call first for "what is happening / its status" questions.',
|
|
85
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
86
|
+
run: async () => JSON.stringify(await deps.getContext())
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "ask_session",
|
|
90
|
+
readOnly: true,
|
|
91
|
+
description: "Ask the deep Claude session agent a read-only question about its current state/work. Does not modify its history. Slower than get_context.",
|
|
92
|
+
parameters: { type: "object", properties: { question: { type: "string", description: "The question to ask." } }, required: ["question"], additionalProperties: false },
|
|
93
|
+
run: async (a) => deps.askSession(str$1(a?.question))
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "summarize_session",
|
|
97
|
+
readOnly: true,
|
|
98
|
+
description: "Get a short summary of the deep agent's transcript answering a specific question. Keeps long history out of your context.",
|
|
99
|
+
parameters: { type: "object", properties: { question: { type: "string", description: "What to summarize / find out." } }, required: ["question"], additionalProperties: false },
|
|
100
|
+
run: async (a) => deps.summarizeSession(str$1(a?.question))
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "use_skill",
|
|
104
|
+
readOnly: true,
|
|
105
|
+
description: "Load the full steps of a project skill from the Skills index by name (progressive disclosure), then carry them out.",
|
|
106
|
+
parameters: { type: "object", properties: { name: { type: "string", description: "Skill name from the Skills index." } }, required: ["name"], additionalProperties: false },
|
|
107
|
+
run: async (a) => {
|
|
108
|
+
const s = findSkill(skills, str$1(a?.name));
|
|
109
|
+
return s ? s.body || `(skill "${s.name}" has no body)` : `No skill named "${str$1(a?.name)}" in the index.`;
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: "run_bash",
|
|
114
|
+
readOnly: false,
|
|
115
|
+
description: "Run a shell command on the bound session's machine. Returns stdout/stderr/exit code. Use for quick reads and short tasks.",
|
|
116
|
+
parameters: { type: "object", properties: { command: { type: "string", description: "The shell command." }, cwd: { type: "string", description: "Optional working directory." } }, required: ["command"], additionalProperties: false },
|
|
117
|
+
run: async (a) => JSON.stringify(await deps.runBash(str$1(a?.command), { cwd: a?.cwd ? str$1(a.cwd) : void 0 }))
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "send_to_session",
|
|
121
|
+
readOnly: false,
|
|
122
|
+
description: "Hand a clear, reformulated instruction to the deep Claude session agent (when the caller wants it to DO something). Two modes: wait=true (SYNC) blocks for its reply and returns it \u2014 for QUICK tasks; wait=false (ASYNC, default) fires-and-forgets \u2014 the deep agent works in the background and reports back, and you can poll get_context. Use ASYNC for heavy work (build/fix/analysis/slides).",
|
|
123
|
+
parameters: { type: "object", properties: { message: { type: "string", description: "The instruction for the coding agent." }, wait: { type: "boolean", description: "true = SYNC (block for reply, quick tasks); false = ASYNC report-back (default)." } }, required: ["message"], additionalProperties: false },
|
|
124
|
+
run: async (a) => {
|
|
125
|
+
const msg = str$1(a?.message);
|
|
126
|
+
if (a?.wait) {
|
|
127
|
+
const reply = (await deps.askSession(msg) || "").trim();
|
|
128
|
+
return reply || "(the deep agent did not reply in time \u2014 poll get_context, or it will report back when done)";
|
|
129
|
+
}
|
|
130
|
+
await deps.sessionSend(msg);
|
|
131
|
+
return "(handed off to the deep agent \u2014 it works asynchronously and will report the result back; call get_context to poll progress)";
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
// Create loop / channel. ONLY call after the caller confirmed the proposed
|
|
135
|
+
// config (see the propose-then-confirm instruction in context.ts).
|
|
136
|
+
{
|
|
137
|
+
name: "create_loop",
|
|
138
|
+
readOnly: false,
|
|
139
|
+
description: "Start a self-verifying loop in the bound session (iterates until an evaluator + optional oracle confirm done). ONLY after the caller confirmed the proposal.",
|
|
140
|
+
parameters: { type: "object", properties: {
|
|
141
|
+
task: { type: "string", description: "What the loop should accomplish." },
|
|
142
|
+
criteria: { type: "string", description: "How we know it is genuinely done." },
|
|
143
|
+
oracle: { type: "string", description: 'Optional pass/fail command (e.g. "npm test").' },
|
|
144
|
+
max_iterations: { type: "number", description: "Iteration ceiling (default 20)." }
|
|
145
|
+
}, required: ["task"], additionalProperties: false },
|
|
146
|
+
run: async (a) => {
|
|
147
|
+
await deps.startLoop({
|
|
148
|
+
task: str$1(a?.task),
|
|
149
|
+
criteria: a?.criteria ? str$1(a.criteria) : void 0,
|
|
150
|
+
oracle: a?.oracle ? str$1(a.oracle) : void 0,
|
|
151
|
+
maxIterations: typeof a?.max_iterations === "number" ? a.max_iterations : void 0
|
|
152
|
+
});
|
|
153
|
+
return `Started a loop: "${str$1(a?.task).slice(0, 80)}".`;
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: "set_checklist",
|
|
158
|
+
readOnly: false,
|
|
159
|
+
description: "Turn the caller's goals into tracked ISSUES (the backlog = the loop's success criteria) and start the self-verifying loop. Each item becomes one concrete, checkable issue (optional per-item oracle \u2192 its verify-cmd). ONLY after the caller confirmed the proposal.",
|
|
160
|
+
parameters: { type: "object", properties: {
|
|
161
|
+
items: { type: "array", description: "The checklist items (success criteria).", items: { type: "object", properties: {
|
|
162
|
+
text: { type: "string", description: "A concrete, checkable requirement." },
|
|
163
|
+
oracle: { type: "string", description: "Optional pass/fail command verifying this item." }
|
|
164
|
+
}, required: ["text"], additionalProperties: false } },
|
|
165
|
+
prompt: { type: "string", description: "Optional overall goal/instruction for the loop." },
|
|
166
|
+
max_iterations: { type: "number", description: "Iteration ceiling (default 20)." }
|
|
167
|
+
}, required: ["items"], additionalProperties: false },
|
|
168
|
+
run: async (a) => {
|
|
169
|
+
const rawItems = Array.isArray(a?.items) ? a.items : [];
|
|
170
|
+
const items = rawItems.map((i) => ({ text: str$1(i?.text), oracle: i?.oracle ? str$1(i.oracle) : void 0 })).filter((i) => i.text.trim());
|
|
171
|
+
if (!items.length) return "No checklist items were provided.";
|
|
172
|
+
const r = await deps.applyChecklist(items, {
|
|
173
|
+
prompt: a?.prompt ? str$1(a.prompt) : void 0,
|
|
174
|
+
maxIterations: typeof a?.max_iterations === "number" ? a.max_iterations : void 0
|
|
175
|
+
});
|
|
176
|
+
return r.ok ? `Created ${items.length} issue(s) and started the backlog loop.` : `Could not create the issues: ${r.error || "unknown error"}.`;
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: "stop_loop",
|
|
181
|
+
readOnly: false,
|
|
182
|
+
description: "Stop/cancel the running loop in the bound session. ONLY after the caller asked to stop.",
|
|
183
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
184
|
+
run: async () => {
|
|
185
|
+
await deps.stopLoop();
|
|
186
|
+
return "Stopped the loop.";
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "create_channel",
|
|
191
|
+
readOnly: false,
|
|
192
|
+
description: "Create an inbound channel so other users/agents can message this session (identity-tagged). ONLY after the caller confirmed the proposal.",
|
|
193
|
+
parameters: { type: "object", properties: {
|
|
194
|
+
name: { type: "string", description: "Short channel name." },
|
|
195
|
+
description: { type: "string", description: "What this channel is for." },
|
|
196
|
+
identity_mode: { type: "string", enum: ["per-key", "caller-supplied", "fixed"], description: 'How callers are identified (default "per-key").' }
|
|
197
|
+
}, required: ["name"], additionalProperties: false },
|
|
198
|
+
run: async (a) => {
|
|
199
|
+
const mode = a?.identity_mode ? str$1(a.identity_mode) : "per-key";
|
|
200
|
+
const channel = {
|
|
201
|
+
name: str$1(a?.name),
|
|
202
|
+
...a?.description ? { description: str$1(a.description) } : {},
|
|
203
|
+
enabled: true,
|
|
204
|
+
identity: mode === "fixed" ? { mode, fixed: { name: "caller", kind: "agent" } } : { mode },
|
|
205
|
+
action: { kind: "message" }
|
|
206
|
+
};
|
|
207
|
+
const r = await deps.saveChannel(channel);
|
|
208
|
+
return r.success ? `Created channel "${str$1(a?.name)}" (${r.channel?.id || "saved"}).` : `Could not create channel: ${r.error || "unknown error"}.`;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
];
|
|
212
|
+
}
|
|
213
|
+
function gateTools(all, config, senderName) {
|
|
214
|
+
const per = config?.per_caller?.[senderName]?.tools;
|
|
215
|
+
const allow = per ?? config?.tools ?? READ_ONLY_TOOLS;
|
|
216
|
+
const set = new Set(allow);
|
|
217
|
+
return all.filter((t) => set.has(t.name));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const defaultExec = (command, opts) => new Promise((resolve) => {
|
|
221
|
+
exec(command, { cwd: opts.cwd, timeout: opts.timeoutMs ?? 6e4, maxBuffer: 8 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
222
|
+
resolve({ stdout: stdout || "", stderr: stderr || "", exitCode: err ? typeof err.code === "number" ? err.code : 1 : 0 });
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
function textOfMsg(m) {
|
|
226
|
+
const role = m?.content?.role || m?.role || "?";
|
|
227
|
+
const text = m?.content?.content?.text ?? m?.content?.text ?? (typeof m?.content === "string" ? m.content : "");
|
|
228
|
+
const t = String(text || "").replace(/\s+/g, " ").trim();
|
|
229
|
+
return t ? `[${role}] ${t.slice(0, 240)}` : "";
|
|
230
|
+
}
|
|
231
|
+
function buildMachineDeps(h, opts = {}) {
|
|
232
|
+
const ctx = opts.ownerEmail ? { user: { email: opts.ownerEmail, id: opts.ownerEmail } } : void 0;
|
|
233
|
+
const execFn = opts.exec || defaultExec;
|
|
234
|
+
return {
|
|
235
|
+
cwd: opts.cwd,
|
|
236
|
+
async runBash(command, o) {
|
|
237
|
+
return execFn(command, { cwd: o?.cwd ?? opts.cwd, timeoutMs: o?.timeoutMs });
|
|
238
|
+
},
|
|
239
|
+
async daemonStatus() {
|
|
240
|
+
const base = await (h.getDaemonStatus?.() ?? {});
|
|
241
|
+
return { ...base, sessionCount: (h.getSessionIds?.() ?? []).length };
|
|
242
|
+
},
|
|
243
|
+
async listSessions() {
|
|
244
|
+
const ids = h.getSessionIds?.() ?? [];
|
|
245
|
+
const out = [];
|
|
246
|
+
for (const id of ids) {
|
|
247
|
+
const rpc = h.getSessionRPCHandlers?.(id);
|
|
248
|
+
if (!rpc) {
|
|
249
|
+
out.push({ id });
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
const m = await rpc.getMetadata?.(ctx);
|
|
254
|
+
out.push({ id, title: m?.metadata?.title, path: m?.metadata?.path });
|
|
255
|
+
} catch {
|
|
256
|
+
out.push({ id, access: "denied" });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return out;
|
|
260
|
+
},
|
|
261
|
+
async readSession(sessionId, o) {
|
|
262
|
+
const rpc = h.getSessionRPCHandlers?.(sessionId);
|
|
263
|
+
if (!rpc) return `No session "${sessionId}" on this machine.`;
|
|
264
|
+
try {
|
|
265
|
+
const r = await rpc.getLatestMessages?.(void 0, o?.limit ?? 8, ctx);
|
|
266
|
+
const msgs = Array.isArray(r) ? r : r?.messages ?? [];
|
|
267
|
+
const lines = msgs.map(textOfMsg).filter(Boolean);
|
|
268
|
+
return lines.length ? lines.join("\n") : "(no recent messages)";
|
|
269
|
+
} catch (e) {
|
|
270
|
+
return `error: ${e?.message || e}`;
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
async sendToSession(sessionId, text) {
|
|
274
|
+
const rpc = h.getSessionRPCHandlers?.(sessionId);
|
|
275
|
+
if (!rpc) return `No session "${sessionId}" on this machine.`;
|
|
276
|
+
try {
|
|
277
|
+
const content = JSON.stringify({ role: "user", content: { type: "text", text } });
|
|
278
|
+
await rpc.sendMessage?.(content, void 0, { sentFrom: "wise-machine" }, ctx);
|
|
279
|
+
return `(sent to ${sessionId})`;
|
|
280
|
+
} catch (e) {
|
|
281
|
+
return `error: ${e?.message || e}`;
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
async spawnSession(directory, o) {
|
|
285
|
+
if (!h.spawnSession) return { error: "spawn not supported on this machine" };
|
|
286
|
+
try {
|
|
287
|
+
const r = await h.spawnSession({ directory, ...o || {} });
|
|
288
|
+
return { sessionId: r?.sessionId ?? r?.id, ...r || {} };
|
|
289
|
+
} catch (e) {
|
|
290
|
+
return { error: e?.message || String(e) };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function loadWiseAgentContext(deps, config) {
|
|
297
|
+
const cwd = deps.cwd || "~";
|
|
298
|
+
let projectInstructions = "";
|
|
299
|
+
try {
|
|
300
|
+
const r = await deps.runBash('cat WISE.md "$HOME/.svamp/wise.md" .svamp/wise.md 2>/dev/null || true', { cwd });
|
|
301
|
+
projectInstructions = parseWiseMd((r.stdout || "").trim()).body;
|
|
302
|
+
} catch {
|
|
303
|
+
}
|
|
304
|
+
let skills = [];
|
|
305
|
+
try {
|
|
306
|
+
const r = await deps.runBash(buildSkillsScanCommand(), { cwd });
|
|
307
|
+
skills = parseSkillsDump(r.stdout || "");
|
|
308
|
+
} catch {
|
|
309
|
+
}
|
|
310
|
+
if (config?.skills && config.skills.length) {
|
|
311
|
+
const allow = new Set(config.skills.map((s) => s.toLowerCase()));
|
|
312
|
+
skills = skills.filter((s) => allow.has(s.name.toLowerCase()));
|
|
313
|
+
}
|
|
314
|
+
return { projectInstructions, skills };
|
|
315
|
+
}
|
|
316
|
+
function buildWiseAgentInstructions(ctx, config) {
|
|
317
|
+
const base = `# Role & Objective
|
|
318
|
+
You are WISE Agent, a fast, text-mode companion to the deep coding agent (Claude) working in this session. You answer quickly and run short tasks against the session's machine, CLIs, skills, and services on a caller's behalf. Success = answering or doing what was asked, then reporting back briefly.
|
|
319
|
+
|
|
320
|
+
# Personality & Tone
|
|
321
|
+
- A capable, hands-on colleague \u2014 direct, concise, no preamble or narration.
|
|
322
|
+
- Keep replies to 1\u20133 short sentences. Summarize results; never paste raw JSON, logs, or long output back.
|
|
323
|
+
- Act now; don't promise to "check back later." No time estimates.
|
|
324
|
+
|
|
325
|
+
# Context
|
|
326
|
+
- "the agent" / "the coding agent" = the separate, deep Claude agent working in this session. You are WISE Agent, not that agent \u2014 you are the fast lane beside it.
|
|
327
|
+
- Your tools act on the bound session and its machine.
|
|
328
|
+
- Callers reach you over HTTP/RPC and may be other agents, CI, or people. Each message carries provenance (who sent it, and whether it is verified) \u2014 weigh it before doing anything destructive.
|
|
329
|
+
|
|
330
|
+
# Tools
|
|
331
|
+
- get_context \u2014 status + recent activity of the session. Use first for "what's happening / its status".
|
|
332
|
+
- ask_session \u2014 ask the deep coding agent a read-only question about its own work (slower).
|
|
333
|
+
- summarize_session \u2014 a cheap subagent that summarizes the deep agent's transcript for a specific question.
|
|
334
|
+
- use_skill \u2014 load a project skill's full steps by name, then carry them out with run_bash.
|
|
335
|
+
- run_bash \u2014 run a shell command on the session's machine (when granted).
|
|
336
|
+
- send_to_session \u2014 hand a clear, reformulated instruction to the deep coding agent (when granted); pass wait=true to block for its reply.
|
|
337
|
+
- create_loop / create_channel \u2014 start a self-verifying loop, or set up an inbound channel for this session (when granted). For SCHEDULED automation, hand the deep agent a "svamp workflow add \u2026 --on schedule --cron ..." instruction via send_to_session. ALWAYS propose first and confirm before calling these (see below).
|
|
338
|
+
- set_checklist / stop_loop \u2014 turn the caller's goals into tracked ISSUES (the backlog = the loop's success criteria) and start the self-verifying loop, or stop a running loop (when granted). Each item becomes one concrete, checkable issue (optional per-item oracle command \u2192 its verify-cmd). ALWAYS propose the items first and confirm before calling.
|
|
339
|
+
|
|
340
|
+
# Instructions
|
|
341
|
+
- Answer general questions and questions about yourself directly. Use tools only to act on the machine/session.
|
|
342
|
+
- Take the cheap path: read state directly; delegate anything LONG to summarize_session \u2014 keep your own context small.
|
|
343
|
+
- To create a loop, channel, or checklist: first restate the resolved config (for set_checklist, list the items) in one line and ask the caller to reply "confirm" to proceed. Only call create_loop / create_channel / set_checklist / stop_loop after they confirm in a follow-up message. Never create or stop without confirmation.
|
|
344
|
+
- For destructive actions (deleting, stopping, killing), require a verified caller and confirm intent; for safe reads, just do it.
|
|
345
|
+
- If a tool fails or returns nothing useful, say so plainly \u2014 never fabricate a result.
|
|
346
|
+
- Report the outcome in one line.`;
|
|
347
|
+
const parts = [base];
|
|
348
|
+
const custom = ctx.projectInstructions?.trim();
|
|
349
|
+
if (custom) parts.push(`# Project notes (WISE.md)
|
|
350
|
+
${custom}`);
|
|
351
|
+
const extra = config?.system?.trim();
|
|
352
|
+
if (extra) parts.push(`# Channel brief
|
|
353
|
+
${extra}`);
|
|
354
|
+
const skillsSection = buildSkillsPromptSection(ctx.skills);
|
|
355
|
+
if (skillsSection) parts.push(skillsSection);
|
|
356
|
+
return parts.join("\n\n");
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const DEFAULT_TIMEOUT_SEC = 30;
|
|
360
|
+
const DEFAULT_MAX_STEPS = 6;
|
|
361
|
+
function toChatTools(tools) {
|
|
362
|
+
return tools.map((t) => ({ type: "function", function: { name: t.name, description: t.description, parameters: t.parameters } }));
|
|
363
|
+
}
|
|
364
|
+
const addUsage = (a, b) => ({
|
|
365
|
+
prompt_tokens: (a?.prompt_tokens || 0) + (b?.prompt_tokens || 0),
|
|
366
|
+
completion_tokens: (a?.completion_tokens || 0) + (b?.completion_tokens || 0),
|
|
367
|
+
total_tokens: (a?.total_tokens || 0) + (b?.total_tokens || 0)
|
|
368
|
+
});
|
|
369
|
+
async function runToolLoop(args) {
|
|
370
|
+
const { system, message, tools, transport, model } = args;
|
|
371
|
+
const timeoutMs = (args.timeoutSec ?? DEFAULT_TIMEOUT_SEC) * 1e3;
|
|
372
|
+
const maxSteps = args.maxSteps ?? DEFAULT_MAX_STEPS;
|
|
373
|
+
const toolCalls = [];
|
|
374
|
+
let usage;
|
|
375
|
+
const byName = new Map(tools.map((t) => [t.name, t]));
|
|
376
|
+
const chatTools = toChatTools(tools);
|
|
377
|
+
const turn = async () => {
|
|
378
|
+
const messages = [
|
|
379
|
+
{ role: "system", content: system },
|
|
380
|
+
{ role: "user", content: message }
|
|
381
|
+
];
|
|
382
|
+
for (let step = 0; step < maxSteps; step++) {
|
|
383
|
+
const res2 = await transport({
|
|
384
|
+
model,
|
|
385
|
+
messages,
|
|
386
|
+
...chatTools.length ? { tools: chatTools, tool_choice: "auto" } : {}
|
|
387
|
+
});
|
|
388
|
+
if (res2.usage) usage = addUsage(usage, res2.usage);
|
|
389
|
+
const choice = res2.choices?.[0]?.message;
|
|
390
|
+
if (!choice) return { status: "error", reply: "", toolCalls, usage, error: "empty model response" };
|
|
391
|
+
const calls = choice.tool_calls || [];
|
|
392
|
+
if (calls.length === 0) {
|
|
393
|
+
return { status: "completed", reply: (choice.content || "").trim(), toolCalls, usage };
|
|
394
|
+
}
|
|
395
|
+
messages.push({ role: "assistant", content: choice.content ?? "", tool_calls: calls });
|
|
396
|
+
for (const call of calls) {
|
|
397
|
+
let cargs = {};
|
|
398
|
+
try {
|
|
399
|
+
cargs = call.function.arguments ? JSON.parse(call.function.arguments) : {};
|
|
400
|
+
} catch {
|
|
401
|
+
}
|
|
402
|
+
const tool = byName.get(call.function.name);
|
|
403
|
+
let result;
|
|
404
|
+
let error;
|
|
405
|
+
if (!tool) {
|
|
406
|
+
error = `tool "${call.function.name}" is not available to this caller`;
|
|
407
|
+
result = error;
|
|
408
|
+
} else {
|
|
409
|
+
try {
|
|
410
|
+
result = await tool.run(cargs);
|
|
411
|
+
} catch (e) {
|
|
412
|
+
error = String(e?.message || e);
|
|
413
|
+
result = `error: ${error}`;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
toolCalls.push({ name: call.function.name, args: cargs, result: error ? void 0 : result, error });
|
|
417
|
+
messages.push({ role: "tool", tool_call_id: call.id, name: call.function.name, content: result });
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const res = await transport({ model, messages, tool_choice: "none" });
|
|
421
|
+
if (res.usage) usage = addUsage(usage, res.usage);
|
|
422
|
+
const final = res.choices?.[0]?.message?.content || "";
|
|
423
|
+
return { status: "completed", reply: final.trim() || "(reached step limit without a final answer)", toolCalls, usage };
|
|
424
|
+
};
|
|
425
|
+
let timer;
|
|
426
|
+
const timeout = new Promise((resolve) => {
|
|
427
|
+
timer = setTimeout(() => resolve({ status: "timeout", reply: "(timed out)", toolCalls, usage }), timeoutMs);
|
|
428
|
+
});
|
|
429
|
+
try {
|
|
430
|
+
return await Promise.race([turn(), timeout]);
|
|
431
|
+
} catch (e) {
|
|
432
|
+
return { status: "error", reply: "", toolCalls, usage, error: String(e?.message || e) };
|
|
433
|
+
} finally {
|
|
434
|
+
if (timer) clearTimeout(timer);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
async function runWiseAgent(args) {
|
|
438
|
+
const { message, sender, config, deps, transport, model } = args;
|
|
439
|
+
let system;
|
|
440
|
+
let tools;
|
|
441
|
+
try {
|
|
442
|
+
const ctx = await loadWiseAgentContext(deps, config);
|
|
443
|
+
system = buildWiseAgentInstructions(ctx, config);
|
|
444
|
+
tools = gateTools(buildTools(deps, ctx.skills), config, sender.name);
|
|
445
|
+
} catch (e) {
|
|
446
|
+
return { status: "error", reply: "", toolCalls: [], error: String(e?.message || e) };
|
|
447
|
+
}
|
|
448
|
+
return runToolLoop({ system, message, tools, transport, model, timeoutSec: config?.timeout_sec, maxSteps: config?.max_steps });
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const MACHINE_READ_ONLY = ["daemon_status", "list_sessions", "read_session", "use_skill"];
|
|
452
|
+
const MACHINE_MUTATING = ["run_bash", "send_to_session", "spawn_session"];
|
|
453
|
+
const ALL_MACHINE_TOOLS = [...MACHINE_READ_ONLY, ...MACHINE_MUTATING];
|
|
454
|
+
const str = (v) => v == null ? "" : String(v);
|
|
455
|
+
function machineToolsForRole(role) {
|
|
456
|
+
if (role === "admin") return [...ALL_MACHINE_TOOLS];
|
|
457
|
+
if (role === "interact") return [...MACHINE_READ_ONLY, "send_to_session"];
|
|
458
|
+
return [...MACHINE_READ_ONLY];
|
|
459
|
+
}
|
|
460
|
+
function buildMachineTools(deps, skills) {
|
|
461
|
+
return [
|
|
462
|
+
{
|
|
463
|
+
name: "daemon_status",
|
|
464
|
+
readOnly: true,
|
|
465
|
+
description: 'Status of the svamp daemon on this machine: version, health, session count. Call first for "what is the machine / daemon doing".',
|
|
466
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
467
|
+
run: async () => JSON.stringify(await deps.daemonStatus())
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
name: "list_sessions",
|
|
471
|
+
readOnly: true,
|
|
472
|
+
description: "List all agent sessions on this machine (id, title, path). Use before acting on a specific session.",
|
|
473
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
474
|
+
run: async () => JSON.stringify(await deps.listSessions())
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
name: "read_session",
|
|
478
|
+
readOnly: true,
|
|
479
|
+
description: "Read the recent messages of a session by id (to inspect what an agent is doing).",
|
|
480
|
+
parameters: { type: "object", properties: { session_id: { type: "string" }, limit: { type: "number" } }, required: ["session_id"], additionalProperties: false },
|
|
481
|
+
run: async (a) => deps.readSession(str(a?.session_id), { limit: a?.limit })
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
name: "use_skill",
|
|
485
|
+
readOnly: true,
|
|
486
|
+
description: "Load a machine-level skill's full steps by name, then carry them out.",
|
|
487
|
+
parameters: { type: "object", properties: { name: { type: "string" } }, required: ["name"], additionalProperties: false },
|
|
488
|
+
run: async (a) => {
|
|
489
|
+
const s = findSkill(skills, str(a?.name));
|
|
490
|
+
return s ? s.body || `(skill "${s.name}" has no body)` : `No skill named "${str(a?.name)}".`;
|
|
491
|
+
}
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
name: "run_bash",
|
|
495
|
+
readOnly: false,
|
|
496
|
+
description: "Run a shell command on this machine (the daemon host). Returns stdout/stderr/exit code. For quick machine ops (status, disk, processes).",
|
|
497
|
+
parameters: { type: "object", properties: { command: { type: "string" }, cwd: { type: "string" } }, required: ["command"], additionalProperties: false },
|
|
498
|
+
run: async (a) => JSON.stringify(await deps.runBash(str(a?.command), { cwd: a?.cwd ? str(a.cwd) : void 0 }))
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
name: "send_to_session",
|
|
502
|
+
readOnly: false,
|
|
503
|
+
description: "Hand an instruction to a specific session's deep Claude agent (the slow, powerful worker) by id.",
|
|
504
|
+
parameters: { type: "object", properties: { session_id: { type: "string" }, message: { type: "string" } }, required: ["session_id", "message"], additionalProperties: false },
|
|
505
|
+
run: async (a) => deps.sendToSession(str(a?.session_id), str(a?.message))
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
name: "spawn_session",
|
|
509
|
+
readOnly: false,
|
|
510
|
+
description: "Start a new agent session in a directory on this machine.",
|
|
511
|
+
parameters: { type: "object", properties: { directory: { type: "string" } }, required: ["directory"], additionalProperties: false },
|
|
512
|
+
run: async (a) => JSON.stringify(await deps.spawnSession(str(a?.directory)))
|
|
513
|
+
}
|
|
514
|
+
];
|
|
515
|
+
}
|
|
516
|
+
async function loadMachineContext(deps) {
|
|
517
|
+
const cwd = deps.cwd || "~";
|
|
518
|
+
let projectInstructions = "";
|
|
519
|
+
try {
|
|
520
|
+
const r = await deps.runBash('cat WISE.md "$HOME/.svamp/wise.md" 2>/dev/null || true', { cwd });
|
|
521
|
+
projectInstructions = parseWiseMd((r.stdout || "").trim()).body;
|
|
522
|
+
} catch {
|
|
523
|
+
}
|
|
524
|
+
let skills = [];
|
|
525
|
+
try {
|
|
526
|
+
const r = await deps.runBash(buildSkillsScanCommand(), { cwd });
|
|
527
|
+
skills = parseSkillsDump(r.stdout || "");
|
|
528
|
+
} catch {
|
|
529
|
+
}
|
|
530
|
+
return { projectInstructions, skills };
|
|
531
|
+
}
|
|
532
|
+
function buildMachineInstructions(ctx, extra) {
|
|
533
|
+
const base = `# Role & Objective
|
|
534
|
+
You are WISE in machine-manager (global) mode \u2014 a fast cockpit over the svamp daemon on this machine. You answer about and operate the MACHINE: daemon status, running commands, and inspecting/spawning/driving any agent session \u2014 WITHOUT involving the slow, powerful Claude coding agent unless the user wants it (then use send_to_session).
|
|
535
|
+
|
|
536
|
+
# Personality & Tone
|
|
537
|
+
- A capable ops colleague \u2014 direct, concise, no preamble. 1\u20133 short sentences.
|
|
538
|
+
- Summarize results; never paste raw JSON or long logs back.
|
|
539
|
+
|
|
540
|
+
# Context
|
|
541
|
+
- You are NOT bound to a session \u2014 you manage the whole machine and can see every session.
|
|
542
|
+
- "the agent" / "the coding agent" = the deep Claude agent inside a session (slow but powerful). You are the fast lane beside all of them.
|
|
543
|
+
|
|
544
|
+
# Tools
|
|
545
|
+
- daemon_status \u2014 daemon health / version / session count. Use first for machine status.
|
|
546
|
+
- list_sessions / read_session \u2014 see all sessions and inspect any one.
|
|
547
|
+
- run_bash \u2014 run quick commands / scripts on the machine.
|
|
548
|
+
- send_to_session(id, message) \u2014 delegate a task to a specific session's Claude agent.
|
|
549
|
+
- spawn_session(directory) \u2014 start a new session.
|
|
550
|
+
- use_skill \u2014 load a machine-level procedure.
|
|
551
|
+
|
|
552
|
+
# Instructions
|
|
553
|
+
- Take the cheap path: read status/sessions directly; only delegate to a Claude agent when the user wants deep work.
|
|
554
|
+
- For destructive machine actions, confirm intent. Report the outcome in one line.
|
|
555
|
+
- If a tool fails, say so plainly \u2014 never fabricate a result.`;
|
|
556
|
+
const parts = [base];
|
|
557
|
+
const custom = ctx.projectInstructions?.trim();
|
|
558
|
+
if (custom) parts.push(`# Machine notes (WISE.md)
|
|
559
|
+
${custom}`);
|
|
560
|
+
if (extra?.trim()) parts.push(`# Extra brief
|
|
561
|
+
${extra.trim()}`);
|
|
562
|
+
const skillsSection = buildSkillsPromptSection(ctx.skills);
|
|
563
|
+
if (skillsSection) parts.push(skillsSection);
|
|
564
|
+
return parts.join("\n\n");
|
|
565
|
+
}
|
|
566
|
+
async function runMachineAgent(args) {
|
|
567
|
+
let system;
|
|
568
|
+
let tools;
|
|
569
|
+
try {
|
|
570
|
+
const ctx = await loadMachineContext(args.deps);
|
|
571
|
+
system = buildMachineInstructions(ctx, args.extraSystem);
|
|
572
|
+
const allow = new Set(machineToolsForRole(args.role ?? "admin"));
|
|
573
|
+
tools = buildMachineTools(args.deps, ctx.skills).filter((t) => allow.has(t.name));
|
|
574
|
+
} catch (e) {
|
|
575
|
+
return { status: "error", reply: "", toolCalls: [], error: String(e?.message || e) };
|
|
576
|
+
}
|
|
577
|
+
return runToolLoop({ system, message: args.message, tools, transport: args.transport, model: args.model, timeoutSec: args.timeoutSec, maxSteps: args.maxSteps });
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
export { MUTATING_TOOLS as M, READ_ONLY_TOOLS as R, buildMachineTools as a, buildMachineInstructions as b, buildMachineDeps as c, loadWiseAgentContext as d, buildWiseAgentInstructions as e, buildTools as f, gateTools as g, runMachineAgent as h, loadMachineContext as l, machineToolsForRole as m, runWiseAgent as r };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const DEFAULTS = {
|
|
2
|
+
// Fast, cheap default — the snappy companion tier.
|
|
3
|
+
"openai": { baseUrl: "https://api.openai.com", model: "gpt-5-mini" },
|
|
4
|
+
// Quota-governed gateway. NOTE: the Hypha proxy is Anthropic-shaped today; an
|
|
5
|
+
// OpenAI-compatible route may be required for non-Claude models (docs §6).
|
|
6
|
+
"hypha-proxy": { baseUrl: "", model: "gpt-5-mini" },
|
|
7
|
+
// Fallback when no OpenAI access — Claude Haiku via the same proxy path.
|
|
8
|
+
"claude-haiku": { baseUrl: "", model: "claude-haiku-4-5" }
|
|
9
|
+
};
|
|
10
|
+
const DEFAULT_OPENAI_BASE = "https://api.openai.com";
|
|
11
|
+
function resolveModel(config, env) {
|
|
12
|
+
const provider = config?.provider || env.WISE_AGENT_PROVIDER || "openai";
|
|
13
|
+
const d = DEFAULTS[provider] || DEFAULTS.openai;
|
|
14
|
+
const model = config?.model || env.WISE_AGENT_MODEL || d.model;
|
|
15
|
+
let baseUrl = env.WISE_AGENT_BASE_URL || "";
|
|
16
|
+
let apiKey = env.WISE_AGENT_API_KEY || "";
|
|
17
|
+
if (provider === "openai") {
|
|
18
|
+
baseUrl = baseUrl || env.OPENAI_BASE_URL || env.OPENAI_API_BASE || d.baseUrl;
|
|
19
|
+
apiKey = apiKey || env.OPENAI_API_KEY || "";
|
|
20
|
+
} else if (provider === "hypha-proxy" || provider === "claude-haiku") {
|
|
21
|
+
baseUrl = baseUrl || (env.SVAMP_HYPHA_PROXY_URL || "").replace(/\/+$/, "");
|
|
22
|
+
apiKey = apiKey || env.HYPHA_TOKEN || "";
|
|
23
|
+
} else {
|
|
24
|
+
baseUrl = baseUrl || d.baseUrl;
|
|
25
|
+
}
|
|
26
|
+
baseUrl = baseUrl.replace(/\/v1\/?$/, "");
|
|
27
|
+
return { provider, baseUrl, apiKey, model };
|
|
28
|
+
}
|
|
29
|
+
function describeMisconfiguration(resolved) {
|
|
30
|
+
const { provider, baseUrl, apiKey } = resolved;
|
|
31
|
+
if (!baseUrl) {
|
|
32
|
+
if (provider === "hypha-proxy" || provider === "claude-haiku") {
|
|
33
|
+
return `WISE Agent (${provider}) is not configured: no proxy base URL. Run \`svamp wise-agent auth use-hypha-proxy <URL>\` (or set SVAMP_HYPHA_PROXY_URL), then \`svamp daemon restart\`.`;
|
|
34
|
+
}
|
|
35
|
+
return "WISE Agent is not configured: no base URL. Run `svamp wise-agent auth set <URL> <KEY>` (or set WISE_AGENT_BASE_URL / OPENAI_BASE_URL), then `svamp daemon restart`.";
|
|
36
|
+
}
|
|
37
|
+
if (provider === "hypha-proxy" || provider === "claude-haiku") {
|
|
38
|
+
if (!apiKey) return `WISE Agent (${provider}) is not configured: no auth token. Ensure HYPHA_TOKEN is set (\`svamp login\`), then \`svamp daemon restart\`.`;
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
if (!apiKey && baseUrl.replace(/\/v1\/?$/, "") === DEFAULT_OPENAI_BASE) {
|
|
42
|
+
return "WISE Agent is not configured: no OpenAI API key. Run `svamp wise-agent auth use-openai <KEY>` (or set OPENAI_API_KEY / WISE_AGENT_API_KEY), then `svamp daemon restart`.";
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
function makeHttpTransport(resolved, fetchImpl = fetch) {
|
|
47
|
+
return async (req) => {
|
|
48
|
+
if (!resolved.baseUrl) throw new Error(`WISE Agent: no base URL for provider "${resolved.provider}" (set WISE_AGENT_BASE_URL or SVAMP_HYPHA_PROXY_URL)`);
|
|
49
|
+
const url = `${resolved.baseUrl}/v1/chat/completions`;
|
|
50
|
+
const res = await fetchImpl(url, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
headers: {
|
|
53
|
+
"content-type": "application/json",
|
|
54
|
+
...resolved.apiKey ? { authorization: `Bearer ${resolved.apiKey}` } : {}
|
|
55
|
+
},
|
|
56
|
+
body: JSON.stringify({ ...req, model: req.model || resolved.model })
|
|
57
|
+
});
|
|
58
|
+
if (!res.ok) {
|
|
59
|
+
const text = await res.text().catch(() => "");
|
|
60
|
+
throw new Error(`WISE Agent chat ${res.status}: ${text.slice(0, 300)}`);
|
|
61
|
+
}
|
|
62
|
+
return await res.json();
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { describeMisconfiguration as d, makeHttpTransport as m, resolveModel as r };
|
|
@@ -1,25 +1,10 @@
|
|
|
1
|
-
import { c as connectToHypha
|
|
1
|
+
import { c as connectToHypha } from './hyphaClient-ybivOjZo.mjs';
|
|
2
|
+
import { S as SharingNotificationSync } from './sharingNotificationSync-qGIQ-3yf.mjs';
|
|
3
|
+
import { p as parseJwtEmail } from './serveAuth-DbVLRCRR.mjs';
|
|
2
4
|
import { randomUUID } from 'node:crypto';
|
|
3
5
|
import { existsSync, readFileSync } from 'node:fs';
|
|
4
6
|
import { join } from 'node:path';
|
|
5
7
|
import os from 'node:os';
|
|
6
|
-
import 'os';
|
|
7
|
-
import 'fs/promises';
|
|
8
|
-
import 'fs';
|
|
9
|
-
import 'path';
|
|
10
|
-
import 'url';
|
|
11
|
-
import 'child_process';
|
|
12
|
-
import 'crypto';
|
|
13
|
-
import 'node:child_process';
|
|
14
|
-
import 'util';
|
|
15
|
-
import 'http';
|
|
16
|
-
import 'net';
|
|
17
|
-
import 'node:events';
|
|
18
|
-
import '@agentclientprotocol/sdk';
|
|
19
|
-
import 'node:util';
|
|
20
|
-
import 'node:readline';
|
|
21
|
-
import 'node:fs/promises';
|
|
22
|
-
import 'yaml';
|
|
23
8
|
|
|
24
9
|
function loadEnv() {
|
|
25
10
|
const SVAMP_HOME = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
|