svamp-cli 0.2.119 → 0.2.120
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/loop/SKILL.md +1 -1
- package/bin/skills/loop/bin/checklist.mjs +4 -1
- package/bin/skills/loop/bin/loop-init.mjs +4 -1
- package/bin/skills/loop/bin/stop-gate.mjs +24 -3
- package/dist/{agentCommands-BTkU0PQb.mjs → agentCommands-DIfofhT-.mjs} +4 -4
- package/dist/{auth-DimbhOMP.mjs → auth-zcVYRjJ8.mjs} +1 -1
- package/dist/cli.mjs +78 -54
- package/dist/{commands-Bw2V_awn.mjs → commands-BFpGoTq8.mjs} +1 -1
- package/dist/{commands-BJfRk4KT.mjs → commands-BOCpNFZX.mjs} +2 -2
- package/dist/{commands-3FsdWpJO.mjs → commands-BYsoZ6Fn.mjs} +2 -2
- package/dist/{commands-BEjlVtvS.mjs → commands-CPsUPDnI.mjs} +1 -1
- package/dist/{commands-B5rek8XG.mjs → commands-CuY9G_88.mjs} +94 -14
- package/dist/{commands-fbQs3jLx.mjs → commands-DOtJfJG7.mjs} +5 -5
- package/dist/{fleet-D5dNVJIp.mjs → fleet-CEAB4PS0.mjs} +1 -1
- package/dist/{frpc-CdcXdQde.mjs → frpc-DlsBjcRf.mjs} +1 -1
- package/dist/{headlessCli-Lk2OU1Gh.mjs → headlessCli-DuY4WQVa.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{package-B-M4dhbv.mjs → package-DS33M8qt.mjs} +1 -1
- package/dist/{run-DIoR81Ev.mjs → run-6Pp8yTPw.mjs} +1 -1
- package/dist/{run-9C2ogsuu.mjs → run-C4BsPJ_p.mjs} +832 -66
- package/dist/{serveCommands-BqApmjmR.mjs → serveCommands-UDH0noeg.mjs} +5 -5
- package/dist/{serveManager-XsXnI804.mjs → serveManager-QZooKtI4.mjs} +2 -2
- package/dist/{sideband-BHWq1P8E.mjs → sideband-Wfli3n7U.mjs} +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import os$1, { homedir as homedir$1 } from 'os';
|
|
2
2
|
import fs, { mkdir as mkdir$1, readdir as readdir$1, readFile, writeFile as writeFile$1, rename, unlink } from 'fs/promises';
|
|
3
|
-
import { readFileSync as readFileSync$1, mkdirSync as mkdirSync$1, writeFileSync as writeFileSync$1, renameSync as renameSync$1, existsSync as existsSync$1, rmSync as rmSync$1, unlinkSync as unlinkSync$1,
|
|
4
|
-
import path__default, { join as join$1, dirname as dirname$1, basename as basename$1
|
|
3
|
+
import { readFileSync as readFileSync$1, mkdirSync as mkdirSync$1, writeFileSync as writeFileSync$1, renameSync as renameSync$1, existsSync as existsSync$1, realpathSync, rmSync as rmSync$1, copyFileSync, unlinkSync as unlinkSync$1, readdirSync as readdirSync$1, watch, rmdirSync } from 'fs';
|
|
4
|
+
import path__default, { join as join$1, resolve, dirname as dirname$1, basename as basename$1 } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { execFile, spawn as spawn$1, execSync as execSync$1, spawnSync } from 'child_process';
|
|
7
7
|
import { randomUUID as randomUUID$1 } from 'crypto';
|
|
@@ -2676,7 +2676,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2676
2676
|
const tunnels = handlers.tunnels;
|
|
2677
2677
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
2678
2678
|
if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
|
|
2679
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
2679
|
+
const { FrpcTunnel } = await import('./frpc-DlsBjcRf.mjs');
|
|
2680
2680
|
const tunnel = new FrpcTunnel({
|
|
2681
2681
|
name: params.name,
|
|
2682
2682
|
ports: params.ports,
|
|
@@ -2937,7 +2937,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
2937
2937
|
}
|
|
2938
2938
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
2939
2939
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
2940
|
-
const { toolsForRole } = await import('./sideband-
|
|
2940
|
+
const { toolsForRole } = await import('./sideband-Wfli3n7U.mjs');
|
|
2941
2941
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
2942
2942
|
return fmt(r2);
|
|
2943
2943
|
}
|
|
@@ -3036,7 +3036,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3036
3036
|
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
3037
3037
|
const callId = "call_" + Math.random().toString(16).slice(2, 12);
|
|
3038
3038
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
3039
|
-
const { queryCore } = await import('./commands-
|
|
3039
|
+
const { queryCore } = await import('./commands-CuY9G_88.mjs');
|
|
3040
3040
|
const timeout = c.reply?.timeout_sec || 120;
|
|
3041
3041
|
let result;
|
|
3042
3042
|
try {
|
|
@@ -3389,6 +3389,7 @@ function formatInboxMessageXml(msg) {
|
|
|
3389
3389
|
if (msg.from) attrs.push(`from="${escapeXml(msg.from)}"`);
|
|
3390
3390
|
if (msg.channel) attrs.push(`channel="${escapeXml(msg.channel)}"`);
|
|
3391
3391
|
if (msg.verified !== void 0) attrs.push(`verified="${msg.verified === true}"`);
|
|
3392
|
+
if (msg.channelId && msg.correlationId) attrs.push(`awaiting-reply="true"`);
|
|
3392
3393
|
if (msg.to) attrs.push(`to="${escapeXml(msg.to)}"`);
|
|
3393
3394
|
if (msg.subject) attrs.push(`subject="${escapeXml(msg.subject)}"`);
|
|
3394
3395
|
if (msg.urgency) attrs.push(`urgency="${msg.urgency}"`);
|
|
@@ -3479,6 +3480,48 @@ function appendMessage(messagesDir, sessionId, msg) {
|
|
|
3479
3480
|
console.error(`[HYPHA SESSION ${sessionId}] Failed to persist message: ${err?.message ?? err}`);
|
|
3480
3481
|
}
|
|
3481
3482
|
}
|
|
3483
|
+
function editableInfo(msg) {
|
|
3484
|
+
const c = msg?.content;
|
|
3485
|
+
if (!c) return null;
|
|
3486
|
+
if (c.role === "user" && c.content?.type === "text") {
|
|
3487
|
+
return { role: "user", text: String(c.content.text ?? "") };
|
|
3488
|
+
}
|
|
3489
|
+
if (c.role === "agent" && c.content?.type === "output") {
|
|
3490
|
+
const data = c.content.data;
|
|
3491
|
+
if (data?.type === "assistant" && Array.isArray(data?.message?.content)) {
|
|
3492
|
+
const textBlocks = data.message.content.filter((b) => b && b.type === "text" && typeof b.text === "string");
|
|
3493
|
+
if (textBlocks.length >= 1) {
|
|
3494
|
+
return { role: "assistant", text: textBlocks.map((b) => b.text).join("\n") };
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
if (data?.type === "user" && typeof data?.message?.content === "string") {
|
|
3498
|
+
return { role: "user", text: data.message.content };
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
return null;
|
|
3502
|
+
}
|
|
3503
|
+
function patchStoredText(msg, newText) {
|
|
3504
|
+
const c = msg?.content;
|
|
3505
|
+
if (c?.role === "user" && c.content?.type === "text") {
|
|
3506
|
+
c.content.text = newText;
|
|
3507
|
+
return true;
|
|
3508
|
+
}
|
|
3509
|
+
if (c?.role === "agent" && c.content?.type === "output") {
|
|
3510
|
+
const data = c.content.data;
|
|
3511
|
+
if (data?.type === "user" && typeof data?.message?.content === "string") {
|
|
3512
|
+
data.message.content = newText;
|
|
3513
|
+
return true;
|
|
3514
|
+
}
|
|
3515
|
+
if (data?.type === "assistant" && Array.isArray(data?.message?.content)) {
|
|
3516
|
+
const textBlocks = data.message.content.filter((b) => b && b.type === "text" && typeof b.text === "string");
|
|
3517
|
+
if (textBlocks.length === 1) {
|
|
3518
|
+
textBlocks[0].text = newText;
|
|
3519
|
+
return true;
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
return false;
|
|
3524
|
+
}
|
|
3482
3525
|
function createSessionStore(server, sessionId, initialMetadata, initialAgentState, callbacks, options) {
|
|
3483
3526
|
const messages = options?.messagesDir ? loadMessages(options.messagesDir) : [];
|
|
3484
3527
|
let nextSeq = messages.length > 0 ? messages[messages.length - 1].seq + 1 : 1;
|
|
@@ -3624,6 +3667,100 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
3624
3667
|
notifyListeners({ type: "update-session", sessionId, metadata: { value: metadata, version: metadataVersion } });
|
|
3625
3668
|
callbacks.onMetadataUpdate?.(metadata);
|
|
3626
3669
|
};
|
|
3670
|
+
const forkClaudePrint = async (prompt, claudeSessionId, cwd, maxTurns = 6) => {
|
|
3671
|
+
const { spawn } = await import('child_process');
|
|
3672
|
+
return new Promise((resolve) => {
|
|
3673
|
+
const child = spawn("claude", [
|
|
3674
|
+
"--print",
|
|
3675
|
+
prompt,
|
|
3676
|
+
"--resume",
|
|
3677
|
+
claudeSessionId,
|
|
3678
|
+
"--fork-session",
|
|
3679
|
+
"--no-session-persistence",
|
|
3680
|
+
"--permission-mode",
|
|
3681
|
+
"bypassPermissions",
|
|
3682
|
+
"--output-format",
|
|
3683
|
+
"json",
|
|
3684
|
+
"--max-turns",
|
|
3685
|
+
String(maxTurns)
|
|
3686
|
+
], {
|
|
3687
|
+
cwd,
|
|
3688
|
+
timeout: 6e4,
|
|
3689
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3690
|
+
env: { ...process.env, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" }
|
|
3691
|
+
});
|
|
3692
|
+
let stdout = "";
|
|
3693
|
+
let stderr = "";
|
|
3694
|
+
child.stdout?.on("data", (d) => {
|
|
3695
|
+
stdout += d.toString();
|
|
3696
|
+
});
|
|
3697
|
+
child.stderr?.on("data", (d) => {
|
|
3698
|
+
stderr += d.toString();
|
|
3699
|
+
});
|
|
3700
|
+
child.on("close", (code) => {
|
|
3701
|
+
if (code !== 0 && !stdout) {
|
|
3702
|
+
resolve({ success: false, error: stderr || `claude exited with code ${code}` });
|
|
3703
|
+
return;
|
|
3704
|
+
}
|
|
3705
|
+
try {
|
|
3706
|
+
const result = JSON.parse(stdout);
|
|
3707
|
+
resolve({ success: true, text: result.result || result.text || stdout });
|
|
3708
|
+
} catch {
|
|
3709
|
+
resolve({ success: true, text: stdout.trim() });
|
|
3710
|
+
}
|
|
3711
|
+
});
|
|
3712
|
+
child.on("error", (err) => resolve({ success: false, error: err.message }));
|
|
3713
|
+
});
|
|
3714
|
+
};
|
|
3715
|
+
const performEdit = async (target, newText) => {
|
|
3716
|
+
if (!callbacks.onEditTranscript) return { success: false, message: "Editing history is not supported for this session." };
|
|
3717
|
+
const info = editableInfo(target);
|
|
3718
|
+
if (!info) return { success: false, message: "This message cannot be edited." };
|
|
3719
|
+
const text = typeof newText === "string" ? newText : "";
|
|
3720
|
+
if (!text.trim()) return { success: false, message: "New text is empty." };
|
|
3721
|
+
let fromEnd = 0;
|
|
3722
|
+
for (const m of messages) {
|
|
3723
|
+
if (m.seq <= target.seq) continue;
|
|
3724
|
+
const i2 = editableInfo(m);
|
|
3725
|
+
if (i2 && i2.role === info.role) fromEnd++;
|
|
3726
|
+
}
|
|
3727
|
+
const anchorSeq = target.seq;
|
|
3728
|
+
const applyStoreA = () => {
|
|
3729
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
3730
|
+
if (messages[i].seq > anchorSeq) messages.splice(i, 1);
|
|
3731
|
+
}
|
|
3732
|
+
const tgt = messages.find((m) => m.seq === anchorSeq);
|
|
3733
|
+
if (tgt) {
|
|
3734
|
+
patchStoredText(tgt, text);
|
|
3735
|
+
tgt.updatedAt = Date.now();
|
|
3736
|
+
}
|
|
3737
|
+
nextSeq = anchorSeq + 1;
|
|
3738
|
+
if (options?.messagesDir) {
|
|
3739
|
+
const filePath = join(options.messagesDir, "messages.jsonl");
|
|
3740
|
+
try {
|
|
3741
|
+
const lines = existsSync(filePath) ? readFileSync(filePath, "utf-8").split("\n").filter((l) => l.trim()) : [];
|
|
3742
|
+
const kept = [];
|
|
3743
|
+
for (const line of lines) {
|
|
3744
|
+
let m;
|
|
3745
|
+
try {
|
|
3746
|
+
m = JSON.parse(line);
|
|
3747
|
+
} catch {
|
|
3748
|
+
continue;
|
|
3749
|
+
}
|
|
3750
|
+
if (typeof m.seq === "number" && m.seq > anchorSeq) continue;
|
|
3751
|
+
kept.push(m.seq === anchorSeq && tgt ? JSON.stringify(tgt) : line);
|
|
3752
|
+
}
|
|
3753
|
+
const tmp = `${filePath}.tmp-${process.pid}`;
|
|
3754
|
+
writeFileSync(tmp, kept.length ? kept.join("\n") + "\n" : "");
|
|
3755
|
+
renameSync(tmp, filePath);
|
|
3756
|
+
} catch (err) {
|
|
3757
|
+
console.error(`[HYPHA SESSION ${sessionId}] Store A rewrite failed: ${err?.message ?? err}`);
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3760
|
+
notifyListeners({ type: "messages-edited", sessionId, latestSeq: nextSeq - 1 });
|
|
3761
|
+
};
|
|
3762
|
+
return callbacks.onEditTranscript({ role: info.role, fromEnd, oldText: info.text, newText: text, applyStoreA });
|
|
3763
|
+
};
|
|
3627
3764
|
const rpcHandlers = {
|
|
3628
3765
|
// ── Messages ──
|
|
3629
3766
|
getMessages: async (afterSeq, limit, context) => {
|
|
@@ -4303,59 +4440,72 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
4303
4440
|
if (!claudeSessionId) {
|
|
4304
4441
|
return { success: false, error: "No active Claude session to query" };
|
|
4305
4442
|
}
|
|
4306
|
-
const
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
const
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4443
|
+
const r = await forkClaudePrint(question, claudeSessionId, metadata.path || process.cwd());
|
|
4444
|
+
return r.success ? { success: true, answer: r.text } : { success: false, error: r.error };
|
|
4445
|
+
},
|
|
4446
|
+
editMessage: async (messageId, newText, context) => {
|
|
4447
|
+
authorizeRequest(context, metadata.sharing, "admin");
|
|
4448
|
+
if (!messageId) return { success: false, message: "messageId is required." };
|
|
4449
|
+
const target = messages.find((m) => m.id === messageId);
|
|
4450
|
+
if (!target) {
|
|
4451
|
+
return { success: false, message: "Message not found in the active window \u2014 only recent messages can be edited." };
|
|
4452
|
+
}
|
|
4453
|
+
return performEdit(target, newText);
|
|
4454
|
+
},
|
|
4455
|
+
refineLastReply: async (instruction, context) => {
|
|
4456
|
+
authorizeRequest(context, metadata.sharing, "admin");
|
|
4457
|
+
if (!instruction || typeof instruction !== "string") {
|
|
4458
|
+
return { success: false, message: "An instruction is required." };
|
|
4459
|
+
}
|
|
4460
|
+
const claudeSessionId = metadata.claudeSessionId;
|
|
4461
|
+
if (!claudeSessionId) return { success: false, message: "No active Claude session to refine." };
|
|
4462
|
+
let last;
|
|
4463
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
4464
|
+
const info = editableInfo(messages[i]);
|
|
4465
|
+
if (info && info.role === "assistant") {
|
|
4466
|
+
last = messages[i];
|
|
4467
|
+
break;
|
|
4468
|
+
}
|
|
4469
|
+
}
|
|
4470
|
+
if (!last) return { success: false, message: "No assistant reply to refine." };
|
|
4471
|
+
const oldText = editableInfo(last).text;
|
|
4472
|
+
const prompt = `You previously wrote this reply:
|
|
4473
|
+
|
|
4474
|
+
<previous_reply>
|
|
4475
|
+
${oldText}
|
|
4476
|
+
</previous_reply>
|
|
4477
|
+
|
|
4478
|
+
Revise it according to this instruction:
|
|
4479
|
+
<instruction>
|
|
4480
|
+
${instruction}
|
|
4481
|
+
</instruction>
|
|
4482
|
+
|
|
4483
|
+
Output ONLY the full revised reply text \u2014 no preamble, no commentary, no surrounding quotes or code fences.`;
|
|
4484
|
+
const revised = await forkClaudePrint(prompt, claudeSessionId, metadata.path || process.cwd());
|
|
4485
|
+
if (!revised.success || !revised.text?.trim()) {
|
|
4486
|
+
return { success: false, message: revised.error || "Failed to generate a revised reply." };
|
|
4487
|
+
}
|
|
4488
|
+
return performEdit(last, revised.text.trim());
|
|
4489
|
+
},
|
|
4490
|
+
undoLastEdit: async (context) => {
|
|
4491
|
+
authorizeRequest(context, metadata.sharing, "admin");
|
|
4492
|
+
if (!callbacks.onUndoEdit) return { success: false, message: "Undo is not supported for this session." };
|
|
4493
|
+
const restoreStoreA = () => {
|
|
4494
|
+
if (!options?.messagesDir) return;
|
|
4495
|
+
try {
|
|
4496
|
+
const undoBackup = join(options.messagesDir, "undo", "messages.jsonl");
|
|
4497
|
+
const target = join(options.messagesDir, "messages.jsonl");
|
|
4498
|
+
if (existsSync(undoBackup)) writeFileSync(target, readFileSync(undoBackup));
|
|
4499
|
+
const reloaded = loadMessages(options.messagesDir, sessionId);
|
|
4500
|
+
messages.length = 0;
|
|
4501
|
+
messages.push(...reloaded);
|
|
4502
|
+
nextSeq = messages.length ? messages[messages.length - 1].seq + 1 : 1;
|
|
4503
|
+
notifyListeners({ type: "messages-edited", sessionId, latestSeq: nextSeq - 1 });
|
|
4504
|
+
} catch (err) {
|
|
4505
|
+
console.error(`[HYPHA SESSION ${sessionId}] Undo Store A restore failed: ${err?.message ?? err}`);
|
|
4506
|
+
}
|
|
4507
|
+
};
|
|
4508
|
+
return callbacks.onUndoEdit({ restoreStoreA });
|
|
4359
4509
|
}
|
|
4360
4510
|
};
|
|
4361
4511
|
const store = {
|
|
@@ -4447,6 +4597,298 @@ function shortId(length = 10) {
|
|
|
4447
4597
|
return out;
|
|
4448
4598
|
}
|
|
4449
4599
|
|
|
4600
|
+
const ADJECTIVES = [
|
|
4601
|
+
"able",
|
|
4602
|
+
"amber",
|
|
4603
|
+
"amused",
|
|
4604
|
+
"ancient",
|
|
4605
|
+
"arctic",
|
|
4606
|
+
"autumn",
|
|
4607
|
+
"azure",
|
|
4608
|
+
"blithe",
|
|
4609
|
+
"bold",
|
|
4610
|
+
"brave",
|
|
4611
|
+
"breezy",
|
|
4612
|
+
"bright",
|
|
4613
|
+
"brisk",
|
|
4614
|
+
"calm",
|
|
4615
|
+
"candid",
|
|
4616
|
+
"cheery",
|
|
4617
|
+
"chill",
|
|
4618
|
+
"clever",
|
|
4619
|
+
"cobalt",
|
|
4620
|
+
"cosmic",
|
|
4621
|
+
"cozy",
|
|
4622
|
+
"crimson",
|
|
4623
|
+
"crisp",
|
|
4624
|
+
"curious",
|
|
4625
|
+
"dapper",
|
|
4626
|
+
"daring",
|
|
4627
|
+
"dawn",
|
|
4628
|
+
"deft",
|
|
4629
|
+
"dewy",
|
|
4630
|
+
"eager",
|
|
4631
|
+
"early",
|
|
4632
|
+
"easy",
|
|
4633
|
+
"electric",
|
|
4634
|
+
"fancy",
|
|
4635
|
+
"feisty",
|
|
4636
|
+
"fleet",
|
|
4637
|
+
"fond",
|
|
4638
|
+
"frosty",
|
|
4639
|
+
"gallant",
|
|
4640
|
+
"gentle",
|
|
4641
|
+
"giddy",
|
|
4642
|
+
"glad",
|
|
4643
|
+
"gleaming",
|
|
4644
|
+
"golden",
|
|
4645
|
+
"graceful",
|
|
4646
|
+
"grand",
|
|
4647
|
+
"hardy",
|
|
4648
|
+
"hazel",
|
|
4649
|
+
"hearty",
|
|
4650
|
+
"honest",
|
|
4651
|
+
"humble",
|
|
4652
|
+
"jolly",
|
|
4653
|
+
"jovial",
|
|
4654
|
+
"keen",
|
|
4655
|
+
"kind",
|
|
4656
|
+
"lively",
|
|
4657
|
+
"loyal",
|
|
4658
|
+
"lucky",
|
|
4659
|
+
"lunar",
|
|
4660
|
+
"mellow",
|
|
4661
|
+
"merry",
|
|
4662
|
+
"mighty",
|
|
4663
|
+
"mint",
|
|
4664
|
+
"misty",
|
|
4665
|
+
"nimble",
|
|
4666
|
+
"noble",
|
|
4667
|
+
"opal",
|
|
4668
|
+
"patient",
|
|
4669
|
+
"peppy",
|
|
4670
|
+
"placid",
|
|
4671
|
+
"plucky",
|
|
4672
|
+
"polar",
|
|
4673
|
+
"prim",
|
|
4674
|
+
"proud",
|
|
4675
|
+
"quick",
|
|
4676
|
+
"quiet",
|
|
4677
|
+
"quirky",
|
|
4678
|
+
"radiant",
|
|
4679
|
+
"rapid",
|
|
4680
|
+
"ready",
|
|
4681
|
+
"regal",
|
|
4682
|
+
"rosy",
|
|
4683
|
+
"royal",
|
|
4684
|
+
"rugged",
|
|
4685
|
+
"sage",
|
|
4686
|
+
"sandy",
|
|
4687
|
+
"scarlet",
|
|
4688
|
+
"serene",
|
|
4689
|
+
"sharp",
|
|
4690
|
+
"shiny",
|
|
4691
|
+
"silent",
|
|
4692
|
+
"silver",
|
|
4693
|
+
"sleek",
|
|
4694
|
+
"smooth",
|
|
4695
|
+
"snappy",
|
|
4696
|
+
"snowy",
|
|
4697
|
+
"solar",
|
|
4698
|
+
"spry",
|
|
4699
|
+
"stellar",
|
|
4700
|
+
"sturdy",
|
|
4701
|
+
"sunny",
|
|
4702
|
+
"swift",
|
|
4703
|
+
"tender",
|
|
4704
|
+
"tidal",
|
|
4705
|
+
"tidy",
|
|
4706
|
+
"tranquil",
|
|
4707
|
+
"trusty",
|
|
4708
|
+
"upbeat",
|
|
4709
|
+
"valiant",
|
|
4710
|
+
"vivid",
|
|
4711
|
+
"warm",
|
|
4712
|
+
"whimsical",
|
|
4713
|
+
"wise",
|
|
4714
|
+
"witty",
|
|
4715
|
+
"zesty",
|
|
4716
|
+
"zippy"
|
|
4717
|
+
];
|
|
4718
|
+
const ANIMALS = [
|
|
4719
|
+
"ant",
|
|
4720
|
+
"badger",
|
|
4721
|
+
"bat",
|
|
4722
|
+
"bear",
|
|
4723
|
+
"beaver",
|
|
4724
|
+
"bee",
|
|
4725
|
+
"bison",
|
|
4726
|
+
"boar",
|
|
4727
|
+
"bobcat",
|
|
4728
|
+
"buffalo",
|
|
4729
|
+
"camel",
|
|
4730
|
+
"caribou",
|
|
4731
|
+
"cat",
|
|
4732
|
+
"cheetah",
|
|
4733
|
+
"cobra",
|
|
4734
|
+
"condor",
|
|
4735
|
+
"cougar",
|
|
4736
|
+
"coyote",
|
|
4737
|
+
"crab",
|
|
4738
|
+
"crane",
|
|
4739
|
+
"cricket",
|
|
4740
|
+
"crow",
|
|
4741
|
+
"deer",
|
|
4742
|
+
"dingo",
|
|
4743
|
+
"dolphin",
|
|
4744
|
+
"donkey",
|
|
4745
|
+
"dove",
|
|
4746
|
+
"dragon",
|
|
4747
|
+
"duck",
|
|
4748
|
+
"eagle",
|
|
4749
|
+
"eel",
|
|
4750
|
+
"egret",
|
|
4751
|
+
"elk",
|
|
4752
|
+
"falcon",
|
|
4753
|
+
"ferret",
|
|
4754
|
+
"finch",
|
|
4755
|
+
"fox",
|
|
4756
|
+
"frog",
|
|
4757
|
+
"gecko",
|
|
4758
|
+
"gibbon",
|
|
4759
|
+
"goat",
|
|
4760
|
+
"goose",
|
|
4761
|
+
"gopher",
|
|
4762
|
+
"hare",
|
|
4763
|
+
"hawk",
|
|
4764
|
+
"hedgehog",
|
|
4765
|
+
"heron",
|
|
4766
|
+
"hippo",
|
|
4767
|
+
"horse",
|
|
4768
|
+
"ibex",
|
|
4769
|
+
"ibis",
|
|
4770
|
+
"iguana",
|
|
4771
|
+
"jackal",
|
|
4772
|
+
"jaguar",
|
|
4773
|
+
"jay",
|
|
4774
|
+
"kestrel",
|
|
4775
|
+
"koala",
|
|
4776
|
+
"krill",
|
|
4777
|
+
"lark",
|
|
4778
|
+
"lemur",
|
|
4779
|
+
"leopard",
|
|
4780
|
+
"lion",
|
|
4781
|
+
"llama",
|
|
4782
|
+
"lynx",
|
|
4783
|
+
"macaw",
|
|
4784
|
+
"magpie",
|
|
4785
|
+
"mantis",
|
|
4786
|
+
"marmot",
|
|
4787
|
+
"marten",
|
|
4788
|
+
"meerkat",
|
|
4789
|
+
"mink",
|
|
4790
|
+
"mole",
|
|
4791
|
+
"moose",
|
|
4792
|
+
"moth",
|
|
4793
|
+
"mouse",
|
|
4794
|
+
"newt",
|
|
4795
|
+
"ocelot",
|
|
4796
|
+
"octopus",
|
|
4797
|
+
"orca",
|
|
4798
|
+
"osprey",
|
|
4799
|
+
"otter",
|
|
4800
|
+
"owl",
|
|
4801
|
+
"ox",
|
|
4802
|
+
"panda",
|
|
4803
|
+
"panther",
|
|
4804
|
+
"parrot",
|
|
4805
|
+
"pelican",
|
|
4806
|
+
"penguin",
|
|
4807
|
+
"pheasant",
|
|
4808
|
+
"pigeon",
|
|
4809
|
+
"puffin",
|
|
4810
|
+
"puma",
|
|
4811
|
+
"quail",
|
|
4812
|
+
"rabbit",
|
|
4813
|
+
"raccoon",
|
|
4814
|
+
"ram",
|
|
4815
|
+
"raven",
|
|
4816
|
+
"robin",
|
|
4817
|
+
"salmon",
|
|
4818
|
+
"seal",
|
|
4819
|
+
"shark",
|
|
4820
|
+
"sheep",
|
|
4821
|
+
"shrew",
|
|
4822
|
+
"skunk",
|
|
4823
|
+
"sloth",
|
|
4824
|
+
"snail",
|
|
4825
|
+
"sparrow",
|
|
4826
|
+
"spider",
|
|
4827
|
+
"squid",
|
|
4828
|
+
"stag",
|
|
4829
|
+
"stoat",
|
|
4830
|
+
"stork",
|
|
4831
|
+
"swan",
|
|
4832
|
+
"tapir",
|
|
4833
|
+
"tiger",
|
|
4834
|
+
"toad",
|
|
4835
|
+
"trout",
|
|
4836
|
+
"turtle",
|
|
4837
|
+
"viper",
|
|
4838
|
+
"vole",
|
|
4839
|
+
"walrus",
|
|
4840
|
+
"weasel",
|
|
4841
|
+
"whale",
|
|
4842
|
+
"wolf",
|
|
4843
|
+
"wombat",
|
|
4844
|
+
"wren",
|
|
4845
|
+
"yak",
|
|
4846
|
+
"zebra"
|
|
4847
|
+
];
|
|
4848
|
+
function pick(arr) {
|
|
4849
|
+
const n = arr.length;
|
|
4850
|
+
const max = Math.floor(256 / n) * n;
|
|
4851
|
+
let b;
|
|
4852
|
+
do {
|
|
4853
|
+
b = randomBytes(1)[0];
|
|
4854
|
+
} while (b >= max);
|
|
4855
|
+
return arr[b % n];
|
|
4856
|
+
}
|
|
4857
|
+
function generateFriendlyName(taken = []) {
|
|
4858
|
+
const used = taken instanceof Set ? taken : new Set(taken);
|
|
4859
|
+
for (let i = 0; i < 1e3; i++) {
|
|
4860
|
+
const name = `${pick(ADJECTIVES)}-${pick(ANIMALS)}`;
|
|
4861
|
+
if (!used.has(name)) return name;
|
|
4862
|
+
}
|
|
4863
|
+
const base = `${pick(ADJECTIVES)}-${pick(ANIMALS)}`;
|
|
4864
|
+
let n = 2;
|
|
4865
|
+
while (used.has(`${base}-${n}`)) n++;
|
|
4866
|
+
return `${base}-${n}`;
|
|
4867
|
+
}
|
|
4868
|
+
function sanitizeSegment(s) {
|
|
4869
|
+
return String(s || "").toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
4870
|
+
}
|
|
4871
|
+
function formatHandle(projectName, friendlyName) {
|
|
4872
|
+
if (!friendlyName) return void 0;
|
|
4873
|
+
const proj = sanitizeSegment(projectName || "");
|
|
4874
|
+
return proj ? `${proj}:${friendlyName}` : friendlyName;
|
|
4875
|
+
}
|
|
4876
|
+
function parseHandle(input) {
|
|
4877
|
+
const str = String(input || "").trim().replace(/^@/, "");
|
|
4878
|
+
if (!str) return null;
|
|
4879
|
+
const i = str.indexOf(":");
|
|
4880
|
+
if (i === -1) return { name: str.toLowerCase() };
|
|
4881
|
+
return { project: sanitizeSegment(str.slice(0, i)), name: str.slice(i + 1).toLowerCase() };
|
|
4882
|
+
}
|
|
4883
|
+
function handleMatchesMetadata(parsed, metadata) {
|
|
4884
|
+
const fn = String(metadata?.friendlyName || "").toLowerCase();
|
|
4885
|
+
if (!fn || fn !== parsed.name) return false;
|
|
4886
|
+
if (parsed.project) {
|
|
4887
|
+
return sanitizeSegment(metadata?.projectName || "") === parsed.project;
|
|
4888
|
+
}
|
|
4889
|
+
return true;
|
|
4890
|
+
}
|
|
4891
|
+
|
|
4450
4892
|
const SVAMP_HOME$2 = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
|
|
4451
4893
|
const num = (key, def) => {
|
|
4452
4894
|
const v = Number(process.env[key]);
|
|
@@ -7189,6 +7631,171 @@ var GeminiTransport$1 = /*#__PURE__*/Object.freeze({
|
|
|
7189
7631
|
GeminiTransport: GeminiTransport
|
|
7190
7632
|
});
|
|
7191
7633
|
|
|
7634
|
+
function resolveTranscriptPath(cwd, claudeSessionId) {
|
|
7635
|
+
let real;
|
|
7636
|
+
try {
|
|
7637
|
+
real = realpathSync(cwd);
|
|
7638
|
+
} catch {
|
|
7639
|
+
real = resolve(cwd);
|
|
7640
|
+
}
|
|
7641
|
+
const projectId = real.replace(/[^a-zA-Z0-9-]/g, "-");
|
|
7642
|
+
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || join$1(os$1.homedir(), ".claude");
|
|
7643
|
+
return join$1(claudeConfigDir, "projects", projectId, `${claudeSessionId}.jsonl`);
|
|
7644
|
+
}
|
|
7645
|
+
function normalizeText(t) {
|
|
7646
|
+
return (t || "").replace(/\s+/g, " ").trim();
|
|
7647
|
+
}
|
|
7648
|
+
function parseTranscript(file) {
|
|
7649
|
+
const content = readFileSync$1(file, "utf-8");
|
|
7650
|
+
return content.split("\n").filter((l) => l.trim().length > 0).map((raw) => {
|
|
7651
|
+
let obj = null;
|
|
7652
|
+
try {
|
|
7653
|
+
obj = JSON.parse(raw);
|
|
7654
|
+
} catch {
|
|
7655
|
+
}
|
|
7656
|
+
return { raw, obj };
|
|
7657
|
+
});
|
|
7658
|
+
}
|
|
7659
|
+
function assistantText(obj) {
|
|
7660
|
+
const content = obj?.message?.content;
|
|
7661
|
+
if (typeof content === "string") return content;
|
|
7662
|
+
if (!Array.isArray(content)) return "";
|
|
7663
|
+
return content.filter((b) => b && b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n");
|
|
7664
|
+
}
|
|
7665
|
+
function isRealUserRecord(obj) {
|
|
7666
|
+
return !!obj && obj.type === "user" && !obj.isSidechain && typeof obj?.message?.content === "string";
|
|
7667
|
+
}
|
|
7668
|
+
function isAgentTextRecord(obj) {
|
|
7669
|
+
if (!obj || obj.type !== "assistant" || obj.isSidechain) return false;
|
|
7670
|
+
const content = obj?.message?.content;
|
|
7671
|
+
if (!Array.isArray(content)) return false;
|
|
7672
|
+
return content.some((b) => b && b.type === "text" && typeof b.text === "string");
|
|
7673
|
+
}
|
|
7674
|
+
function isRoleTextRecord(obj, role) {
|
|
7675
|
+
return role === "user" ? isRealUserRecord(obj) : isAgentTextRecord(obj);
|
|
7676
|
+
}
|
|
7677
|
+
function recordText(obj, role) {
|
|
7678
|
+
return role === "user" ? String(obj?.message?.content ?? "") : assistantText(obj);
|
|
7679
|
+
}
|
|
7680
|
+
function findAnchorIndex(lines, anchor) {
|
|
7681
|
+
const roleIdx = [];
|
|
7682
|
+
for (let i = 0; i < lines.length; i++) {
|
|
7683
|
+
if (isRoleTextRecord(lines[i].obj, anchor.role)) roleIdx.push(i);
|
|
7684
|
+
}
|
|
7685
|
+
if (roleIdx.length === 0) {
|
|
7686
|
+
return { ok: false, reason: `no ${anchor.role} text records in transcript` };
|
|
7687
|
+
}
|
|
7688
|
+
const pos = roleIdx.length - 1 - anchor.fromEnd;
|
|
7689
|
+
if (pos < 0 || pos >= roleIdx.length) {
|
|
7690
|
+
return { ok: false, reason: `anchor position out of range (fromEnd=${anchor.fromEnd}, have ${roleIdx.length})` };
|
|
7691
|
+
}
|
|
7692
|
+
const index = roleIdx[pos];
|
|
7693
|
+
const got = normalizeText(recordText(lines[index].obj, anchor.role));
|
|
7694
|
+
const want = normalizeText(anchor.oldText);
|
|
7695
|
+
if (got !== want) {
|
|
7696
|
+
if (!(want.length > 0 && got.startsWith(want))) {
|
|
7697
|
+
return { ok: false, reason: "anchor text mismatch \u2014 stores out of sync, refusing to edit" };
|
|
7698
|
+
}
|
|
7699
|
+
}
|
|
7700
|
+
return { ok: true, index };
|
|
7701
|
+
}
|
|
7702
|
+
function orphanCheckObjs(objs) {
|
|
7703
|
+
const toolUseIds = /* @__PURE__ */ new Set();
|
|
7704
|
+
const toolResultIds = /* @__PURE__ */ new Set();
|
|
7705
|
+
for (const obj of objs) {
|
|
7706
|
+
const content = obj?.message?.content;
|
|
7707
|
+
if (!Array.isArray(content)) continue;
|
|
7708
|
+
for (const b of content) {
|
|
7709
|
+
if (b?.type === "tool_use" && b.id) toolUseIds.add(b.id);
|
|
7710
|
+
if (b?.type === "tool_result" && b.tool_use_id) toolResultIds.add(b.tool_use_id);
|
|
7711
|
+
}
|
|
7712
|
+
}
|
|
7713
|
+
for (const id of toolUseIds) {
|
|
7714
|
+
if (!toolResultIds.has(id)) return true;
|
|
7715
|
+
}
|
|
7716
|
+
return false;
|
|
7717
|
+
}
|
|
7718
|
+
function applyTranscriptEdit(file, index, newText) {
|
|
7719
|
+
if (!existsSync$1(file)) return { ok: false, reason: `transcript not found: ${file}` };
|
|
7720
|
+
const lines = parseTranscript(file);
|
|
7721
|
+
if (index < 0 || index >= lines.length) return { ok: false, reason: "index out of range" };
|
|
7722
|
+
const target = lines[index].obj;
|
|
7723
|
+
if (!target) return { ok: false, reason: "target line not parseable" };
|
|
7724
|
+
if (target.type === "assistant") {
|
|
7725
|
+
const content = target?.message?.content;
|
|
7726
|
+
if (!Array.isArray(content)) return { ok: false, reason: "assistant content is not a block array" };
|
|
7727
|
+
const textBlocks = content.filter((b) => b && b.type === "text" && typeof b.text === "string");
|
|
7728
|
+
if (textBlocks.length !== 1) {
|
|
7729
|
+
return { ok: false, reason: `expected exactly 1 text block, found ${textBlocks.length}` };
|
|
7730
|
+
}
|
|
7731
|
+
textBlocks[0].text = newText;
|
|
7732
|
+
} else if (target.type === "user") {
|
|
7733
|
+
if (typeof target?.message?.content !== "string") {
|
|
7734
|
+
return { ok: false, reason: "user content is not a string" };
|
|
7735
|
+
}
|
|
7736
|
+
target.message.content = newText;
|
|
7737
|
+
} else {
|
|
7738
|
+
return { ok: false, reason: `unsupported record type: ${target.type}` };
|
|
7739
|
+
}
|
|
7740
|
+
const keptObjs = [];
|
|
7741
|
+
const kept = [];
|
|
7742
|
+
for (let i = 0; i <= index; i++) {
|
|
7743
|
+
kept.push(i === index ? JSON.stringify(target) : lines[i].raw);
|
|
7744
|
+
keptObjs.push(lines[i].obj);
|
|
7745
|
+
}
|
|
7746
|
+
if (orphanCheckObjs(keptObjs)) {
|
|
7747
|
+
return { ok: false, reason: "edit would orphan a tool_use (truncation drops its tool_result)" };
|
|
7748
|
+
}
|
|
7749
|
+
const out = kept.join("\n") + "\n";
|
|
7750
|
+
const tmp = `${file}.tmp-${process.pid}`;
|
|
7751
|
+
try {
|
|
7752
|
+
writeFileSync$1(tmp, out);
|
|
7753
|
+
renameSync$1(tmp, file);
|
|
7754
|
+
} catch (err) {
|
|
7755
|
+
return { ok: false, reason: `write failed: ${err?.message ?? err}` };
|
|
7756
|
+
}
|
|
7757
|
+
return { ok: true, truncatedAfter: index, totalBefore: lines.length };
|
|
7758
|
+
}
|
|
7759
|
+
function saveUndoSnapshot(undoDir, transcriptFile, messagesFile, meta) {
|
|
7760
|
+
mkdirSync$1(undoDir, { recursive: true });
|
|
7761
|
+
if (existsSync$1(transcriptFile)) copyFileSync(transcriptFile, join$1(undoDir, "transcript.jsonl"));
|
|
7762
|
+
if (existsSync$1(messagesFile)) copyFileSync(messagesFile, join$1(undoDir, "messages.jsonl"));
|
|
7763
|
+
writeFileSync$1(join$1(undoDir, "meta.json"), JSON.stringify(meta));
|
|
7764
|
+
}
|
|
7765
|
+
function readUndoMeta(undoDir) {
|
|
7766
|
+
const p = join$1(undoDir, "meta.json");
|
|
7767
|
+
if (!existsSync$1(p)) return null;
|
|
7768
|
+
try {
|
|
7769
|
+
return JSON.parse(readFileSync$1(p, "utf-8"));
|
|
7770
|
+
} catch {
|
|
7771
|
+
return null;
|
|
7772
|
+
}
|
|
7773
|
+
}
|
|
7774
|
+
function restoreTranscriptFromUndo(undoDir, targetFile) {
|
|
7775
|
+
const backup = join$1(undoDir, "transcript.jsonl");
|
|
7776
|
+
if (!existsSync$1(backup)) return false;
|
|
7777
|
+
const tmp = `${targetFile}.tmp-${process.pid}`;
|
|
7778
|
+
writeFileSync$1(tmp, readFileSync$1(backup));
|
|
7779
|
+
renameSync$1(tmp, targetFile);
|
|
7780
|
+
return true;
|
|
7781
|
+
}
|
|
7782
|
+
function clearUndoSnapshot(undoDir) {
|
|
7783
|
+
try {
|
|
7784
|
+
rmSync$1(undoDir, { recursive: true, force: true });
|
|
7785
|
+
} catch {
|
|
7786
|
+
}
|
|
7787
|
+
}
|
|
7788
|
+
function transcriptSessionIdMatches(file, expectedSessionId) {
|
|
7789
|
+
if (!existsSync$1(file)) return false;
|
|
7790
|
+
const lines = parseTranscript(file);
|
|
7791
|
+
for (const { obj } of lines) {
|
|
7792
|
+
if (obj && typeof obj.sessionId === "string") {
|
|
7793
|
+
return obj.sessionId === expectedSessionId;
|
|
7794
|
+
}
|
|
7795
|
+
}
|
|
7796
|
+
return false;
|
|
7797
|
+
}
|
|
7798
|
+
|
|
7192
7799
|
const execFileAsync = promisify$1(execFile$1);
|
|
7193
7800
|
const SVAMP_TOOLS_DIR = join(homedir(), ".svamp", "tools");
|
|
7194
7801
|
const SVAMP_BIN_DIR = join(SVAMP_TOOLS_DIR, "bin");
|
|
@@ -9162,10 +9769,10 @@ async function checkAndRefreshOAuthToken(force = false, logger) {
|
|
|
9162
9769
|
}
|
|
9163
9770
|
}
|
|
9164
9771
|
|
|
9165
|
-
function buildBaselineSystemPrompt(sessionId) {
|
|
9772
|
+
function buildBaselineSystemPrompt(sessionId, handle) {
|
|
9166
9773
|
return `# Svamp Session
|
|
9167
9774
|
|
|
9168
|
-
You are running inside a Svamp session (id: ${sessionId}) on Hypha Cloud
|
|
9775
|
+
You are running inside a Svamp session (id: ${sessionId}${handle ? `, handle: ${handle}` : ""}) on Hypha Cloud.${handle ? ` Other agents and the user refer to you as \`${handle}\` (a \`project:name\` handle) \u2014 use it when introducing yourself, and refer to peers by their handles.` : ""} Use the \`svamp\` CLI to manage session state (title, link, notify) and the \`hypha\` CLI for artifacts, tokens, and RPC services. Installed skills live in \`~/.claude/skills/\` \u2014 read them for full references.
|
|
9169
9776
|
|
|
9170
9777
|
**Session state:**
|
|
9171
9778
|
- \`svamp session set-title "<topic>"\` \u2014 your session topic: a short sentence describing what you're working on. It's the index the user and other agents see when listing sessions, so set it after your first reply and update it as focus shifts (else it's auto-summarized for you).
|
|
@@ -9178,7 +9785,7 @@ You are running inside a Svamp session (id: ${sessionId}) on Hypha Cloud. Use th
|
|
|
9178
9785
|
|
|
9179
9786
|
You share this machine and project folder with other agent sessions (same user, possibly other machines too). When collaboration, coordination, or avoiding edit conflicts becomes relevant, run \`svamp session whoami\` \u2014 it shows who's around and how to reach them. Keep cross-agent contact purposeful: only initiate with a concrete reason, avoid ping-pong loops.
|
|
9180
9787
|
|
|
9181
|
-
**Inbox messages from other agents** arrive wrapped as \`<svamp-message message-id="\u2026" from="
|
|
9788
|
+
**Inbox messages from other agents** arrive wrapped as \`<svamp-message message-id="\u2026" from="<sender-handle>" \u2026>BODY</svamp-message>\` (a plain user turn has no wrapper); \`from\` is the sender's \`project:name\` handle. Reply only when useful: \`svamp session inbox reply <message-id> "<body>"\` \u2014 the message-id is all you need; routing back to the sender is automatic. **If a message carries \`awaiting-reply="true"\`, the sender is actively polling for your answer through a channel \u2014 you MUST respond with \`svamp session inbox reply <message-id> "<body>"\`; a normal chat reply will NOT reach them.**
|
|
9182
9789
|
`;
|
|
9183
9790
|
}
|
|
9184
9791
|
|
|
@@ -10097,6 +10704,8 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
10097
10704
|
messageQueue: [...existingQueue, {
|
|
10098
10705
|
id: randomUUID$1(),
|
|
10099
10706
|
text: kickoff,
|
|
10707
|
+
// Compact chat badge; full task lives in LOOP.md. Show an ellipsis
|
|
10708
|
+
// when truncated so it's clear the preview is shortened, not the task.
|
|
10100
10709
|
displayText: `\u{1F501} Loop started: ${lp.task.trim().slice(0, 100)}${lp.task.trim().length > 100 ? "\u2026" : ""}`,
|
|
10101
10710
|
createdAt: Date.now()
|
|
10102
10711
|
}]
|
|
@@ -10596,7 +11205,7 @@ async function startDaemon(options) {
|
|
|
10596
11205
|
const list = loadExposedTunnels().filter((t) => t.name !== name);
|
|
10597
11206
|
saveExposedTunnels(list);
|
|
10598
11207
|
}
|
|
10599
|
-
const { ServeManager } = await import('./serveManager-
|
|
11208
|
+
const { ServeManager } = await import('./serveManager-QZooKtI4.mjs');
|
|
10600
11209
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
10601
11210
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
10602
11211
|
});
|
|
@@ -10622,6 +11231,16 @@ async function startDaemon(options) {
|
|
|
10622
11231
|
clientId: hyphaClientId || machineId
|
|
10623
11232
|
});
|
|
10624
11233
|
logger.log(`Connected to Hypha (workspace: ${server.config.workspace})`);
|
|
11234
|
+
writeDaemonStateFile({
|
|
11235
|
+
pid: process.pid,
|
|
11236
|
+
startTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11237
|
+
version: DAEMON_VERSION,
|
|
11238
|
+
hyphaServerUrl,
|
|
11239
|
+
workspace: server.config.workspace,
|
|
11240
|
+
machineId,
|
|
11241
|
+
hyphaClientId: server.config.client_id,
|
|
11242
|
+
supervised: process.env.SVAMP_SUPERVISED === "1"
|
|
11243
|
+
});
|
|
10625
11244
|
server.on("disconnected", (reason) => {
|
|
10626
11245
|
logger.log(`Hypha connection permanently lost: ${reason}. Daemon continues running \u2014 restart manually to reconnect.`);
|
|
10627
11246
|
});
|
|
@@ -10668,6 +11287,8 @@ ${v.guidance ? v.guidance + "\n" : ""}Criteria: ${v.criteria || "(none)"}
|
|
|
10668
11287
|
metadata = {
|
|
10669
11288
|
flavor: m.flavor,
|
|
10670
11289
|
name: m.name,
|
|
11290
|
+
friendlyName: m.friendlyName,
|
|
11291
|
+
projectName: m.projectName,
|
|
10671
11292
|
path: m.path,
|
|
10672
11293
|
host: m.host,
|
|
10673
11294
|
lifecycleState: m.lifecycleState,
|
|
@@ -10689,6 +11310,24 @@ ${v.guidance ? v.guidance + "\n" : ""}Criteria: ${v.criteria || "(none)"}
|
|
|
10689
11310
|
};
|
|
10690
11311
|
});
|
|
10691
11312
|
};
|
|
11313
|
+
const collectKnownFriendlyNames = () => {
|
|
11314
|
+
const taken = /* @__PURE__ */ new Set();
|
|
11315
|
+
for (const s of pidToTrackedSession.values()) {
|
|
11316
|
+
try {
|
|
11317
|
+
const fn = s.hyphaService?.getMetadata?.()?.friendlyName;
|
|
11318
|
+
if (fn) taken.add(fn);
|
|
11319
|
+
} catch {
|
|
11320
|
+
}
|
|
11321
|
+
}
|
|
11322
|
+
try {
|
|
11323
|
+
for (const p of loadPersistedSessions()) {
|
|
11324
|
+
const fn = p.metadata?.friendlyName;
|
|
11325
|
+
if (fn) taken.add(fn);
|
|
11326
|
+
}
|
|
11327
|
+
} catch {
|
|
11328
|
+
}
|
|
11329
|
+
return taken;
|
|
11330
|
+
};
|
|
10692
11331
|
let machineServiceRef = null;
|
|
10693
11332
|
const spawnSession = async (options2) => {
|
|
10694
11333
|
logger.log("Spawning session:", JSON.stringify(options2));
|
|
@@ -10807,6 +11446,8 @@ ${v.guidance ? v.guidance + "\n" : ""}Criteria: ${v.criteria || "(none)"}
|
|
|
10807
11446
|
var parseBashPermission = parseBashPermission2, shouldAutoAllow = shouldAutoAllow2, killAndWaitForExit = killAndWaitForExit2, buildIsolationConfig = buildIsolationConfig2;
|
|
10808
11447
|
let sessionMetadata = {
|
|
10809
11448
|
path: directory,
|
|
11449
|
+
// Project namespace for the friendly handle (folder/repo basename).
|
|
11450
|
+
projectName: projectName(directory),
|
|
10810
11451
|
host: os$1.hostname(),
|
|
10811
11452
|
version: "0.1.0",
|
|
10812
11453
|
machineId,
|
|
@@ -10833,6 +11474,10 @@ ${v.guidance ? v.guidance + "\n" : ""}Criteria: ${v.criteria || "(none)"}
|
|
|
10833
11474
|
const allPersisted = loadPersistedSessions();
|
|
10834
11475
|
const persisted = allPersisted.find((p) => p.sessionId === sessionId) || (resumeSessionId ? allPersisted.find((p) => p.claudeResumeId === resumeSessionId) : void 0);
|
|
10835
11476
|
let claudeResumeId = persisted?.claudeResumeId || (resumeSessionId || void 0);
|
|
11477
|
+
sessionMetadata = {
|
|
11478
|
+
...sessionMetadata,
|
|
11479
|
+
friendlyName: persisted?.metadata?.friendlyName || generateFriendlyName(collectKnownFriendlyNames())
|
|
11480
|
+
};
|
|
10836
11481
|
let currentPermissionMode = options2.permissionMode || persisted?.permissionMode || "bypassPermissions";
|
|
10837
11482
|
const sessionCreatedAt = persisted?.createdAt || Date.now();
|
|
10838
11483
|
let lastSpawnMeta = persisted?.spawnMeta || {};
|
|
@@ -11117,7 +11762,7 @@ ${parts.join("\n")}`);
|
|
|
11117
11762
|
const permissionMode = toClaudePermissionMode(rawPermissionMode);
|
|
11118
11763
|
currentPermissionMode = permissionMode;
|
|
11119
11764
|
const model = effectiveMeta.model || agentConfig.default_model || void 0;
|
|
11120
|
-
const appendSystemPrompt = effectiveMeta.appendSystemPrompt || agentConfig.append_system_prompt || buildBaselineSystemPrompt(sessionId);
|
|
11765
|
+
const appendSystemPrompt = effectiveMeta.appendSystemPrompt || agentConfig.append_system_prompt || buildBaselineSystemPrompt(sessionId, formatHandle(sessionMetadata.projectName, sessionMetadata.friendlyName));
|
|
11121
11766
|
const args = [
|
|
11122
11767
|
"--output-format",
|
|
11123
11768
|
"stream-json",
|
|
@@ -11179,6 +11824,10 @@ ${parts.join("\n")}`);
|
|
|
11179
11824
|
let spawnEnv = { ...process.env, ...extraEnv };
|
|
11180
11825
|
delete spawnEnv.CLAUDECODE;
|
|
11181
11826
|
spawnEnv.SVAMP_SESSION_ID = sessionId;
|
|
11827
|
+
{
|
|
11828
|
+
const handle = formatHandle(sessionMetadata.projectName, sessionMetadata.friendlyName);
|
|
11829
|
+
if (handle) spawnEnv.SVAMP_SESSION_HANDLE = handle;
|
|
11830
|
+
}
|
|
11182
11831
|
delete spawnEnv.SVAMP_SANDBOXED;
|
|
11183
11832
|
const proxyDesc = applyClaudeProxyEnv(spawnEnv);
|
|
11184
11833
|
if (proxyDesc) {
|
|
@@ -11673,12 +12322,13 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11673
12322
|
}
|
|
11674
12323
|
return child;
|
|
11675
12324
|
};
|
|
11676
|
-
const restartClaudeHandler = async () => {
|
|
12325
|
+
const restartClaudeHandler = async (opts) => {
|
|
11677
12326
|
logger.log(`[Session ${sessionId}] Restart Claude requested`);
|
|
11678
12327
|
if (isRestartingClaude || isSwitchingMode) {
|
|
11679
12328
|
return { success: false, message: "Restart already in progress." };
|
|
11680
12329
|
}
|
|
11681
12330
|
isRestartingClaude = true;
|
|
12331
|
+
let beforeRespawnError;
|
|
11682
12332
|
try {
|
|
11683
12333
|
if (claudeProcess && claudeProcess.exitCode === null) {
|
|
11684
12334
|
isKillingClaude = true;
|
|
@@ -11690,6 +12340,14 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11690
12340
|
if (trackedSession?.stopped) {
|
|
11691
12341
|
return { success: false, message: "Session was stopped during restart." };
|
|
11692
12342
|
}
|
|
12343
|
+
if (opts?.beforeRespawn) {
|
|
12344
|
+
try {
|
|
12345
|
+
await opts.beforeRespawn();
|
|
12346
|
+
} catch (hookErr) {
|
|
12347
|
+
beforeRespawnError = hookErr?.message ?? String(hookErr);
|
|
12348
|
+
logger.log(`[Session ${sessionId}] beforeRespawn hook failed: ${beforeRespawnError}`);
|
|
12349
|
+
}
|
|
12350
|
+
}
|
|
11693
12351
|
if (claudeResumeId) {
|
|
11694
12352
|
if (!stagedCredentials && shouldIsolateSession()) {
|
|
11695
12353
|
try {
|
|
@@ -11702,6 +12360,9 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11702
12360
|
spawnClaude(void 0, { permissionMode: currentPermissionMode });
|
|
11703
12361
|
sessionService.updateMetadata(sessionMetadata);
|
|
11704
12362
|
logger.log(`[Session ${sessionId}] Claude respawned with --resume ${claudeResumeId}`);
|
|
12363
|
+
if (beforeRespawnError) {
|
|
12364
|
+
return { success: false, message: `Edit failed (session restored): ${beforeRespawnError}` };
|
|
12365
|
+
}
|
|
11705
12366
|
return { success: true, message: "Claude process restarted successfully." };
|
|
11706
12367
|
} else {
|
|
11707
12368
|
logger.log(`[Session ${sessionId}] No resume ID \u2014 cannot restart`);
|
|
@@ -11715,6 +12376,26 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11715
12376
|
isRestartingClaude = false;
|
|
11716
12377
|
}
|
|
11717
12378
|
};
|
|
12379
|
+
const editHistoryIdleGuard = () => {
|
|
12380
|
+
const lifecycle = sessionMetadata.lifecycleState;
|
|
12381
|
+
if (lifecycle === "running" || lifecycle === "restarting") {
|
|
12382
|
+
return { success: false, message: "Cannot edit while the agent is working \u2014 wait until it is idle." };
|
|
12383
|
+
}
|
|
12384
|
+
if (isRestartingClaude || isSwitchingMode || isKillingClaude) {
|
|
12385
|
+
return { success: false, message: "Cannot edit during a restart/mode switch \u2014 try again in a moment." };
|
|
12386
|
+
}
|
|
12387
|
+
const queueLen = sessionMetadata.messageQueue?.length ?? 0;
|
|
12388
|
+
if (queueLen > 0) {
|
|
12389
|
+
return { success: false, message: "Cannot edit while messages are queued." };
|
|
12390
|
+
}
|
|
12391
|
+
if (isLoopActiveForSession(directory, sessionId)) {
|
|
12392
|
+
return { success: false, message: "Cannot edit history while a loop is active." };
|
|
12393
|
+
}
|
|
12394
|
+
if (!claudeResumeId) {
|
|
12395
|
+
return { success: false, message: "No Claude transcript to edit yet." };
|
|
12396
|
+
}
|
|
12397
|
+
return null;
|
|
12398
|
+
};
|
|
11718
12399
|
if (shouldIsolateSession()) {
|
|
11719
12400
|
try {
|
|
11720
12401
|
stagedCredentials = await stageCredentialsForSharing(sessionId);
|
|
@@ -11735,6 +12416,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11735
12416
|
logger.log(`[Session ${sessionId}] User message received`);
|
|
11736
12417
|
userMessagePending = true;
|
|
11737
12418
|
turnInitiatedByUser = true;
|
|
12419
|
+
clearUndoSnapshot(join$1(getSessionDir(directory, sessionId), "undo"));
|
|
11738
12420
|
let text;
|
|
11739
12421
|
let msgMeta = meta;
|
|
11740
12422
|
try {
|
|
@@ -11954,6 +12636,86 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11954
12636
|
}
|
|
11955
12637
|
},
|
|
11956
12638
|
onRestartClaude: restartClaudeHandler,
|
|
12639
|
+
onEditTranscript: async ({ role, fromEnd, oldText, newText, applyStoreA }) => {
|
|
12640
|
+
const gate = editHistoryIdleGuard();
|
|
12641
|
+
if (gate) return gate;
|
|
12642
|
+
logger.log(`[Session ${sessionId}] Edit history: role=${role} fromEnd=${fromEnd} \u2192 kill + rewrite + resume`);
|
|
12643
|
+
const result = await restartClaudeHandler({
|
|
12644
|
+
beforeRespawn: async () => {
|
|
12645
|
+
const file = resolveTranscriptPath(directory, claudeResumeId);
|
|
12646
|
+
if (!transcriptSessionIdMatches(file, claudeResumeId)) {
|
|
12647
|
+
throw new Error("transcript file id mismatch (session rotated) \u2014 aborting edit");
|
|
12648
|
+
}
|
|
12649
|
+
const lines = parseTranscript(file);
|
|
12650
|
+
const anchor = findAnchorIndex(lines, { role, fromEnd, oldText });
|
|
12651
|
+
if (!anchor.ok) throw new Error(anchor.reason);
|
|
12652
|
+
const sessDir = getSessionDir(directory, sessionId);
|
|
12653
|
+
saveUndoSnapshot(join$1(sessDir, "undo"), file, join$1(sessDir, "messages.jsonl"), {
|
|
12654
|
+
preEditClaudeSessionId: claudeResumeId,
|
|
12655
|
+
createdAt: Date.now(),
|
|
12656
|
+
label: "edit"
|
|
12657
|
+
});
|
|
12658
|
+
const edit = applyTranscriptEdit(file, anchor.index, newText);
|
|
12659
|
+
if (!edit.ok) throw new Error(edit.reason);
|
|
12660
|
+
applyStoreA();
|
|
12661
|
+
logger.log(`[Session ${sessionId}] Edit applied \u2014 transcript truncated after line ${anchor.index}`);
|
|
12662
|
+
}
|
|
12663
|
+
});
|
|
12664
|
+
if (result.success && claudeResumeId && !trackedSession.stopped) {
|
|
12665
|
+
saveSession({
|
|
12666
|
+
sessionId,
|
|
12667
|
+
directory,
|
|
12668
|
+
claudeResumeId,
|
|
12669
|
+
permissionMode: currentPermissionMode,
|
|
12670
|
+
spawnMeta: lastSpawnMeta,
|
|
12671
|
+
metadata: sessionMetadata,
|
|
12672
|
+
createdAt: sessionCreatedAt,
|
|
12673
|
+
machineId,
|
|
12674
|
+
wasProcessing: false
|
|
12675
|
+
});
|
|
12676
|
+
artifactSync.syncSession(sessionId, getSessionDir(directory, sessionId), sessionMetadata, machineId).catch(() => {
|
|
12677
|
+
});
|
|
12678
|
+
}
|
|
12679
|
+
return result;
|
|
12680
|
+
},
|
|
12681
|
+
onUndoEdit: async ({ restoreStoreA }) => {
|
|
12682
|
+
const gate = editHistoryIdleGuard();
|
|
12683
|
+
if (gate) return gate;
|
|
12684
|
+
const undoDir = join$1(getSessionDir(directory, sessionId), "undo");
|
|
12685
|
+
const meta = readUndoMeta(undoDir);
|
|
12686
|
+
if (!meta) return { success: false, message: "Nothing to undo." };
|
|
12687
|
+
logger.log(`[Session ${sessionId}] Undo edit \u2192 restore pre-edit transcript ${meta.preEditClaudeSessionId} + messages, resume`);
|
|
12688
|
+
const result = await restartClaudeHandler({
|
|
12689
|
+
beforeRespawn: async () => {
|
|
12690
|
+
const target = resolveTranscriptPath(directory, meta.preEditClaudeSessionId);
|
|
12691
|
+
if (!restoreTranscriptFromUndo(undoDir, target)) {
|
|
12692
|
+
throw new Error("undo snapshot missing transcript backup");
|
|
12693
|
+
}
|
|
12694
|
+
restoreStoreA();
|
|
12695
|
+
claudeResumeId = meta.preEditClaudeSessionId;
|
|
12696
|
+
sessionMetadata = { ...sessionMetadata, claudeSessionId: claudeResumeId };
|
|
12697
|
+
}
|
|
12698
|
+
});
|
|
12699
|
+
if (result.success) {
|
|
12700
|
+
clearUndoSnapshot(undoDir);
|
|
12701
|
+
if (claudeResumeId && !trackedSession.stopped) {
|
|
12702
|
+
saveSession({
|
|
12703
|
+
sessionId,
|
|
12704
|
+
directory,
|
|
12705
|
+
claudeResumeId,
|
|
12706
|
+
permissionMode: currentPermissionMode,
|
|
12707
|
+
spawnMeta: lastSpawnMeta,
|
|
12708
|
+
metadata: sessionMetadata,
|
|
12709
|
+
createdAt: sessionCreatedAt,
|
|
12710
|
+
machineId,
|
|
12711
|
+
wasProcessing: false
|
|
12712
|
+
});
|
|
12713
|
+
artifactSync.syncSession(sessionId, getSessionDir(directory, sessionId), sessionMetadata, machineId).catch(() => {
|
|
12714
|
+
});
|
|
12715
|
+
}
|
|
12716
|
+
}
|
|
12717
|
+
return result;
|
|
12718
|
+
},
|
|
11957
12719
|
onUpdateSecurityContext: async (newSecurityContext) => {
|
|
11958
12720
|
logger.log(`[Session ${sessionId}] Security context update requested \u2014 restarting agent`);
|
|
11959
12721
|
sessionMetadata = { ...sessionMetadata, securityContext: newSecurityContext };
|
|
@@ -12333,8 +13095,12 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12333
13095
|
return false;
|
|
12334
13096
|
};
|
|
12335
13097
|
var parseBashPermission = parseBashPermission2, shouldAutoAllow = shouldAutoAllow2;
|
|
13098
|
+
const acpPersistedName = loadPersistedSessions().find((p) => p.sessionId === sessionId)?.metadata?.friendlyName;
|
|
12336
13099
|
let sessionMetadata = {
|
|
12337
13100
|
path: directory,
|
|
13101
|
+
// Project namespace for the friendly handle (folder/repo basename).
|
|
13102
|
+
projectName: projectName(directory),
|
|
13103
|
+
friendlyName: acpPersistedName || generateFriendlyName(collectKnownFriendlyNames()),
|
|
12338
13104
|
host: os$1.hostname(),
|
|
12339
13105
|
version: "0.1.0",
|
|
12340
13106
|
machineId,
|
|
@@ -13108,7 +13874,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
13108
13874
|
const specs = loadExposedTunnels();
|
|
13109
13875
|
if (specs.length === 0) return;
|
|
13110
13876
|
logger.log(`[exposed-tunnels] Restoring ${specs.length} tunnel(s) from ${EXPOSED_TUNNELS_FILE}`);
|
|
13111
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
13877
|
+
const { FrpcTunnel } = await import('./frpc-DlsBjcRf.mjs');
|
|
13112
13878
|
for (const spec of specs) {
|
|
13113
13879
|
if (tunnels.has(spec.name)) continue;
|
|
13114
13880
|
try {
|
|
@@ -13884,4 +14650,4 @@ var run = /*#__PURE__*/Object.freeze({
|
|
|
13884
14650
|
writeStopMarker: writeStopMarker
|
|
13885
14651
|
});
|
|
13886
14652
|
|
|
13887
|
-
export {
|
|
14653
|
+
export { GeminiTransport$1 as $, READ_ONLY_TOOLS as A, loadMachineContext as B, buildMachineInstructions as C, machineToolsForRole as D, buildMachineTools as E, resolveModel as F, formatHandle as G, normalizeAllowedUser as H, loadSecurityContextConfig as I, resolveSecurityContext as J, buildSecurityContextFromFlags as K, mergeSecurityContexts as L, buildSessionShareUrl as M, computeOutboundHop as N, buildMachineShareUrl as O, parseHandle as P, handleMatchesMetadata as Q, RoutineStore as R, ServeAuth as S, describeMisconfiguration as T, buildMachineDeps as U, generateHookSettings as V, projectInfo as W, DefaultTransport$1 as X, acpBackend as Y, acpAgentConfig as Z, codexMcpBackend as _, createSessionStore as a, claudeAuth as a0, instanceConfig as a1, api as a2, run as a3, 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, shortId as n, getSkillsServer as o, parseFrontmatter as p, getSkillsWorkspaceName as q, registerMachineService as r, startDaemon as s, getSkillsCollectionName as t, fetchWithTimeout as u, searchSkills as v, SKILLS_DIR as w, getSkillInfo as x, downloadSkillFile as y, listSkillFiles as z };
|