svamp-cli 0.2.115 → 0.2.116
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/loop-init.mjs +3 -0
- package/dist/{agentCommands-CFWM6S7e.mjs → agentCommands--H31qHbm.mjs} +4 -4
- package/dist/{auth-B3NsDWG9.mjs → auth-CUzGJvRf.mjs} +1 -1
- package/dist/cli.mjs +51 -75
- package/dist/{commands-Dmh59asw.mjs → commands-C0715MEC.mjs} +2 -2
- package/dist/{commands-Vudp6ihZ.mjs → commands-C1ERmRw4.mjs} +5 -5
- package/dist/{commands-DbQ14J-R.mjs → commands-ClxBUkI3.mjs} +1 -1
- package/dist/{commands-B3NhziMR.mjs → commands-DdW5M7Le.mjs} +2 -47
- package/dist/{commands-Dj2M3sTB.mjs → commands-zMw02qH_.mjs} +1 -1
- package/dist/{fleet-F8KB5IcM.mjs → fleet-js8FwhM_.mjs} +1 -1
- package/dist/{frpc-1aSnPVrE.mjs → frpc-9qgaimIN.mjs} +1 -1
- package/dist/{headlessCli-6Cps9gnO.mjs → headlessCli-my-nvBDO.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{package-BuIQUz-N.mjs → package-BdLUrz6e.mjs} +2 -2
- package/dist/{run-DXzaCfex.mjs → run-DHPCWQUq.mjs} +167 -491
- package/dist/{run-BnPtZvoP.mjs → run-DnGdMH2k.mjs} +1 -1
- package/dist/{serveCommands-CFO3GtKq.mjs → serveCommands-D9KR-bC5.mjs} +5 -5
- package/dist/{serveManager-BnwAe-VX.mjs → serveManager-B19qVJeZ.mjs} +2 -2
- package/dist/{sideband-vimBRRDF.mjs → sideband-CYNK4foC.mjs} +1 -1
- package/package.json +2 -2
|
@@ -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,
|
|
4
|
-
import path__default, { join as join$1,
|
|
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, copyFileSync, readdirSync as readdirSync$1, watch, rmdirSync } from 'fs';
|
|
4
|
+
import path__default, { join as join$1, dirname as dirname$1, basename as basename$1, resolve } 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-9qgaimIN.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-CYNK4foC.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-DdW5M7Le.mjs');
|
|
3040
3040
|
const timeout = c.reply?.timeout_sec || 120;
|
|
3041
3041
|
let result;
|
|
3042
3042
|
try {
|
|
@@ -3479,48 +3479,6 @@ function appendMessage(messagesDir, sessionId, msg) {
|
|
|
3479
3479
|
console.error(`[HYPHA SESSION ${sessionId}] Failed to persist message: ${err?.message ?? err}`);
|
|
3480
3480
|
}
|
|
3481
3481
|
}
|
|
3482
|
-
function editableInfo(msg) {
|
|
3483
|
-
const c = msg?.content;
|
|
3484
|
-
if (!c) return null;
|
|
3485
|
-
if (c.role === "user" && c.content?.type === "text") {
|
|
3486
|
-
return { role: "user", text: String(c.content.text ?? "") };
|
|
3487
|
-
}
|
|
3488
|
-
if (c.role === "agent" && c.content?.type === "output") {
|
|
3489
|
-
const data = c.content.data;
|
|
3490
|
-
if (data?.type === "assistant" && Array.isArray(data?.message?.content)) {
|
|
3491
|
-
const textBlocks = data.message.content.filter((b) => b && b.type === "text" && typeof b.text === "string");
|
|
3492
|
-
if (textBlocks.length >= 1) {
|
|
3493
|
-
return { role: "assistant", text: textBlocks.map((b) => b.text).join("\n") };
|
|
3494
|
-
}
|
|
3495
|
-
}
|
|
3496
|
-
if (data?.type === "user" && typeof data?.message?.content === "string") {
|
|
3497
|
-
return { role: "user", text: data.message.content };
|
|
3498
|
-
}
|
|
3499
|
-
}
|
|
3500
|
-
return null;
|
|
3501
|
-
}
|
|
3502
|
-
function patchStoredText(msg, newText) {
|
|
3503
|
-
const c = msg?.content;
|
|
3504
|
-
if (c?.role === "user" && c.content?.type === "text") {
|
|
3505
|
-
c.content.text = newText;
|
|
3506
|
-
return true;
|
|
3507
|
-
}
|
|
3508
|
-
if (c?.role === "agent" && c.content?.type === "output") {
|
|
3509
|
-
const data = c.content.data;
|
|
3510
|
-
if (data?.type === "user" && typeof data?.message?.content === "string") {
|
|
3511
|
-
data.message.content = newText;
|
|
3512
|
-
return true;
|
|
3513
|
-
}
|
|
3514
|
-
if (data?.type === "assistant" && Array.isArray(data?.message?.content)) {
|
|
3515
|
-
const textBlocks = data.message.content.filter((b) => b && b.type === "text" && typeof b.text === "string");
|
|
3516
|
-
if (textBlocks.length === 1) {
|
|
3517
|
-
textBlocks[0].text = newText;
|
|
3518
|
-
return true;
|
|
3519
|
-
}
|
|
3520
|
-
}
|
|
3521
|
-
}
|
|
3522
|
-
return false;
|
|
3523
|
-
}
|
|
3524
3482
|
function createSessionStore(server, sessionId, initialMetadata, initialAgentState, callbacks, options) {
|
|
3525
3483
|
const messages = options?.messagesDir ? loadMessages(options.messagesDir) : [];
|
|
3526
3484
|
let nextSeq = messages.length > 0 ? messages[messages.length - 1].seq + 1 : 1;
|
|
@@ -3666,100 +3624,6 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
3666
3624
|
notifyListeners({ type: "update-session", sessionId, metadata: { value: metadata, version: metadataVersion } });
|
|
3667
3625
|
callbacks.onMetadataUpdate?.(metadata);
|
|
3668
3626
|
};
|
|
3669
|
-
const forkClaudePrint = async (prompt, claudeSessionId, cwd, maxTurns = 6) => {
|
|
3670
|
-
const { spawn } = await import('child_process');
|
|
3671
|
-
return new Promise((resolve) => {
|
|
3672
|
-
const child = spawn("claude", [
|
|
3673
|
-
"--print",
|
|
3674
|
-
prompt,
|
|
3675
|
-
"--resume",
|
|
3676
|
-
claudeSessionId,
|
|
3677
|
-
"--fork-session",
|
|
3678
|
-
"--no-session-persistence",
|
|
3679
|
-
"--permission-mode",
|
|
3680
|
-
"bypassPermissions",
|
|
3681
|
-
"--output-format",
|
|
3682
|
-
"json",
|
|
3683
|
-
"--max-turns",
|
|
3684
|
-
String(maxTurns)
|
|
3685
|
-
], {
|
|
3686
|
-
cwd,
|
|
3687
|
-
timeout: 6e4,
|
|
3688
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
3689
|
-
env: { ...process.env, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" }
|
|
3690
|
-
});
|
|
3691
|
-
let stdout = "";
|
|
3692
|
-
let stderr = "";
|
|
3693
|
-
child.stdout?.on("data", (d) => {
|
|
3694
|
-
stdout += d.toString();
|
|
3695
|
-
});
|
|
3696
|
-
child.stderr?.on("data", (d) => {
|
|
3697
|
-
stderr += d.toString();
|
|
3698
|
-
});
|
|
3699
|
-
child.on("close", (code) => {
|
|
3700
|
-
if (code !== 0 && !stdout) {
|
|
3701
|
-
resolve({ success: false, error: stderr || `claude exited with code ${code}` });
|
|
3702
|
-
return;
|
|
3703
|
-
}
|
|
3704
|
-
try {
|
|
3705
|
-
const result = JSON.parse(stdout);
|
|
3706
|
-
resolve({ success: true, text: result.result || result.text || stdout });
|
|
3707
|
-
} catch {
|
|
3708
|
-
resolve({ success: true, text: stdout.trim() });
|
|
3709
|
-
}
|
|
3710
|
-
});
|
|
3711
|
-
child.on("error", (err) => resolve({ success: false, error: err.message }));
|
|
3712
|
-
});
|
|
3713
|
-
};
|
|
3714
|
-
const performEdit = async (target, newText) => {
|
|
3715
|
-
if (!callbacks.onEditTranscript) return { success: false, message: "Editing history is not supported for this session." };
|
|
3716
|
-
const info = editableInfo(target);
|
|
3717
|
-
if (!info) return { success: false, message: "This message cannot be edited." };
|
|
3718
|
-
const text = typeof newText === "string" ? newText : "";
|
|
3719
|
-
if (!text.trim()) return { success: false, message: "New text is empty." };
|
|
3720
|
-
let fromEnd = 0;
|
|
3721
|
-
for (const m of messages) {
|
|
3722
|
-
if (m.seq <= target.seq) continue;
|
|
3723
|
-
const i2 = editableInfo(m);
|
|
3724
|
-
if (i2 && i2.role === info.role) fromEnd++;
|
|
3725
|
-
}
|
|
3726
|
-
const anchorSeq = target.seq;
|
|
3727
|
-
const applyStoreA = () => {
|
|
3728
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
3729
|
-
if (messages[i].seq > anchorSeq) messages.splice(i, 1);
|
|
3730
|
-
}
|
|
3731
|
-
const tgt = messages.find((m) => m.seq === anchorSeq);
|
|
3732
|
-
if (tgt) {
|
|
3733
|
-
patchStoredText(tgt, text);
|
|
3734
|
-
tgt.updatedAt = Date.now();
|
|
3735
|
-
}
|
|
3736
|
-
nextSeq = anchorSeq + 1;
|
|
3737
|
-
if (options?.messagesDir) {
|
|
3738
|
-
const filePath = join(options.messagesDir, "messages.jsonl");
|
|
3739
|
-
try {
|
|
3740
|
-
const lines = existsSync(filePath) ? readFileSync(filePath, "utf-8").split("\n").filter((l) => l.trim()) : [];
|
|
3741
|
-
const kept = [];
|
|
3742
|
-
for (const line of lines) {
|
|
3743
|
-
let m;
|
|
3744
|
-
try {
|
|
3745
|
-
m = JSON.parse(line);
|
|
3746
|
-
} catch {
|
|
3747
|
-
continue;
|
|
3748
|
-
}
|
|
3749
|
-
if (typeof m.seq === "number" && m.seq > anchorSeq) continue;
|
|
3750
|
-
kept.push(m.seq === anchorSeq && tgt ? JSON.stringify(tgt) : line);
|
|
3751
|
-
}
|
|
3752
|
-
const tmp = `${filePath}.tmp-${process.pid}`;
|
|
3753
|
-
writeFileSync(tmp, kept.length ? kept.join("\n") + "\n" : "");
|
|
3754
|
-
renameSync(tmp, filePath);
|
|
3755
|
-
} catch (err) {
|
|
3756
|
-
console.error(`[HYPHA SESSION ${sessionId}] Store A rewrite failed: ${err?.message ?? err}`);
|
|
3757
|
-
}
|
|
3758
|
-
}
|
|
3759
|
-
notifyListeners({ type: "messages-edited", sessionId, latestSeq: nextSeq - 1 });
|
|
3760
|
-
};
|
|
3761
|
-
return callbacks.onEditTranscript({ role: info.role, fromEnd, oldText: info.text, newText: text, applyStoreA });
|
|
3762
|
-
};
|
|
3763
3627
|
const rpcHandlers = {
|
|
3764
3628
|
// ── Messages ──
|
|
3765
3629
|
getMessages: async (afterSeq, limit, context) => {
|
|
@@ -4439,72 +4303,59 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
4439
4303
|
if (!claudeSessionId) {
|
|
4440
4304
|
return { success: false, error: "No active Claude session to query" };
|
|
4441
4305
|
}
|
|
4442
|
-
const
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
const
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
const undoBackup = join(options.messagesDir, "undo", "messages.jsonl");
|
|
4496
|
-
const target = join(options.messagesDir, "messages.jsonl");
|
|
4497
|
-
if (existsSync(undoBackup)) writeFileSync(target, readFileSync(undoBackup));
|
|
4498
|
-
const reloaded = loadMessages(options.messagesDir, sessionId);
|
|
4499
|
-
messages.length = 0;
|
|
4500
|
-
messages.push(...reloaded);
|
|
4501
|
-
nextSeq = messages.length ? messages[messages.length - 1].seq + 1 : 1;
|
|
4502
|
-
notifyListeners({ type: "messages-edited", sessionId, latestSeq: nextSeq - 1 });
|
|
4503
|
-
} catch (err) {
|
|
4504
|
-
console.error(`[HYPHA SESSION ${sessionId}] Undo Store A restore failed: ${err?.message ?? err}`);
|
|
4505
|
-
}
|
|
4506
|
-
};
|
|
4507
|
-
return callbacks.onUndoEdit({ restoreStoreA });
|
|
4306
|
+
const { spawn } = await import('child_process');
|
|
4307
|
+
const cwd = metadata.path || process.cwd();
|
|
4308
|
+
return new Promise((resolve) => {
|
|
4309
|
+
const args = [
|
|
4310
|
+
"--print",
|
|
4311
|
+
question,
|
|
4312
|
+
"--resume",
|
|
4313
|
+
claudeSessionId,
|
|
4314
|
+
"--fork-session",
|
|
4315
|
+
"--no-session-persistence",
|
|
4316
|
+
"--permission-mode",
|
|
4317
|
+
"bypassPermissions",
|
|
4318
|
+
"--output-format",
|
|
4319
|
+
"json",
|
|
4320
|
+
// Allow a few turns so questions that need a look-up (read a file,
|
|
4321
|
+
// check state) can make the tool call AND then answer. With 1 turn,
|
|
4322
|
+
// any tool-using question died instantly with error_max_turns
|
|
4323
|
+
// ("couldn't answer quickly"). The 60s subprocess timeout still bounds it.
|
|
4324
|
+
"--max-turns",
|
|
4325
|
+
"6"
|
|
4326
|
+
];
|
|
4327
|
+
const child = spawn("claude", args, {
|
|
4328
|
+
cwd,
|
|
4329
|
+
timeout: 6e4,
|
|
4330
|
+
// Ignore stdin: --print otherwise waits ~3s for piped input ("no stdin
|
|
4331
|
+
// data received in 3s, proceeding without it") — pure latency per /btw.
|
|
4332
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
4333
|
+
env: { ...process.env, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" }
|
|
4334
|
+
});
|
|
4335
|
+
let stdout = "";
|
|
4336
|
+
let stderr = "";
|
|
4337
|
+
child.stdout?.on("data", (d) => {
|
|
4338
|
+
stdout += d.toString();
|
|
4339
|
+
});
|
|
4340
|
+
child.stderr?.on("data", (d) => {
|
|
4341
|
+
stderr += d.toString();
|
|
4342
|
+
});
|
|
4343
|
+
child.on("close", (code) => {
|
|
4344
|
+
if (code !== 0 && !stdout) {
|
|
4345
|
+
resolve({ success: false, error: stderr || `claude exited with code ${code}` });
|
|
4346
|
+
return;
|
|
4347
|
+
}
|
|
4348
|
+
try {
|
|
4349
|
+
const result = JSON.parse(stdout);
|
|
4350
|
+
resolve({ success: true, answer: result.result || result.text || stdout });
|
|
4351
|
+
} catch {
|
|
4352
|
+
resolve({ success: true, answer: stdout.trim() });
|
|
4353
|
+
}
|
|
4354
|
+
});
|
|
4355
|
+
child.on("error", (err) => {
|
|
4356
|
+
resolve({ success: false, error: err.message });
|
|
4357
|
+
});
|
|
4358
|
+
});
|
|
4508
4359
|
}
|
|
4509
4360
|
};
|
|
4510
4361
|
const store = {
|
|
@@ -7338,171 +7189,6 @@ var GeminiTransport$1 = /*#__PURE__*/Object.freeze({
|
|
|
7338
7189
|
GeminiTransport: GeminiTransport
|
|
7339
7190
|
});
|
|
7340
7191
|
|
|
7341
|
-
function resolveTranscriptPath(cwd, claudeSessionId) {
|
|
7342
|
-
let real;
|
|
7343
|
-
try {
|
|
7344
|
-
real = realpathSync(cwd);
|
|
7345
|
-
} catch {
|
|
7346
|
-
real = resolve(cwd);
|
|
7347
|
-
}
|
|
7348
|
-
const projectId = real.replace(/[^a-zA-Z0-9-]/g, "-");
|
|
7349
|
-
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || join$1(os$1.homedir(), ".claude");
|
|
7350
|
-
return join$1(claudeConfigDir, "projects", projectId, `${claudeSessionId}.jsonl`);
|
|
7351
|
-
}
|
|
7352
|
-
function normalizeText(t) {
|
|
7353
|
-
return (t || "").replace(/\s+/g, " ").trim();
|
|
7354
|
-
}
|
|
7355
|
-
function parseTranscript(file) {
|
|
7356
|
-
const content = readFileSync$1(file, "utf-8");
|
|
7357
|
-
return content.split("\n").filter((l) => l.trim().length > 0).map((raw) => {
|
|
7358
|
-
let obj = null;
|
|
7359
|
-
try {
|
|
7360
|
-
obj = JSON.parse(raw);
|
|
7361
|
-
} catch {
|
|
7362
|
-
}
|
|
7363
|
-
return { raw, obj };
|
|
7364
|
-
});
|
|
7365
|
-
}
|
|
7366
|
-
function assistantText(obj) {
|
|
7367
|
-
const content = obj?.message?.content;
|
|
7368
|
-
if (typeof content === "string") return content;
|
|
7369
|
-
if (!Array.isArray(content)) return "";
|
|
7370
|
-
return content.filter((b) => b && b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n");
|
|
7371
|
-
}
|
|
7372
|
-
function isRealUserRecord(obj) {
|
|
7373
|
-
return !!obj && obj.type === "user" && !obj.isSidechain && typeof obj?.message?.content === "string";
|
|
7374
|
-
}
|
|
7375
|
-
function isAgentTextRecord(obj) {
|
|
7376
|
-
if (!obj || obj.type !== "assistant" || obj.isSidechain) return false;
|
|
7377
|
-
const content = obj?.message?.content;
|
|
7378
|
-
if (!Array.isArray(content)) return false;
|
|
7379
|
-
return content.some((b) => b && b.type === "text" && typeof b.text === "string");
|
|
7380
|
-
}
|
|
7381
|
-
function isRoleTextRecord(obj, role) {
|
|
7382
|
-
return role === "user" ? isRealUserRecord(obj) : isAgentTextRecord(obj);
|
|
7383
|
-
}
|
|
7384
|
-
function recordText(obj, role) {
|
|
7385
|
-
return role === "user" ? String(obj?.message?.content ?? "") : assistantText(obj);
|
|
7386
|
-
}
|
|
7387
|
-
function findAnchorIndex(lines, anchor) {
|
|
7388
|
-
const roleIdx = [];
|
|
7389
|
-
for (let i = 0; i < lines.length; i++) {
|
|
7390
|
-
if (isRoleTextRecord(lines[i].obj, anchor.role)) roleIdx.push(i);
|
|
7391
|
-
}
|
|
7392
|
-
if (roleIdx.length === 0) {
|
|
7393
|
-
return { ok: false, reason: `no ${anchor.role} text records in transcript` };
|
|
7394
|
-
}
|
|
7395
|
-
const pos = roleIdx.length - 1 - anchor.fromEnd;
|
|
7396
|
-
if (pos < 0 || pos >= roleIdx.length) {
|
|
7397
|
-
return { ok: false, reason: `anchor position out of range (fromEnd=${anchor.fromEnd}, have ${roleIdx.length})` };
|
|
7398
|
-
}
|
|
7399
|
-
const index = roleIdx[pos];
|
|
7400
|
-
const got = normalizeText(recordText(lines[index].obj, anchor.role));
|
|
7401
|
-
const want = normalizeText(anchor.oldText);
|
|
7402
|
-
if (got !== want) {
|
|
7403
|
-
if (!(want.length > 0 && got.startsWith(want))) {
|
|
7404
|
-
return { ok: false, reason: "anchor text mismatch \u2014 stores out of sync, refusing to edit" };
|
|
7405
|
-
}
|
|
7406
|
-
}
|
|
7407
|
-
return { ok: true, index };
|
|
7408
|
-
}
|
|
7409
|
-
function orphanCheckObjs(objs) {
|
|
7410
|
-
const toolUseIds = /* @__PURE__ */ new Set();
|
|
7411
|
-
const toolResultIds = /* @__PURE__ */ new Set();
|
|
7412
|
-
for (const obj of objs) {
|
|
7413
|
-
const content = obj?.message?.content;
|
|
7414
|
-
if (!Array.isArray(content)) continue;
|
|
7415
|
-
for (const b of content) {
|
|
7416
|
-
if (b?.type === "tool_use" && b.id) toolUseIds.add(b.id);
|
|
7417
|
-
if (b?.type === "tool_result" && b.tool_use_id) toolResultIds.add(b.tool_use_id);
|
|
7418
|
-
}
|
|
7419
|
-
}
|
|
7420
|
-
for (const id of toolUseIds) {
|
|
7421
|
-
if (!toolResultIds.has(id)) return true;
|
|
7422
|
-
}
|
|
7423
|
-
return false;
|
|
7424
|
-
}
|
|
7425
|
-
function applyTranscriptEdit(file, index, newText) {
|
|
7426
|
-
if (!existsSync$1(file)) return { ok: false, reason: `transcript not found: ${file}` };
|
|
7427
|
-
const lines = parseTranscript(file);
|
|
7428
|
-
if (index < 0 || index >= lines.length) return { ok: false, reason: "index out of range" };
|
|
7429
|
-
const target = lines[index].obj;
|
|
7430
|
-
if (!target) return { ok: false, reason: "target line not parseable" };
|
|
7431
|
-
if (target.type === "assistant") {
|
|
7432
|
-
const content = target?.message?.content;
|
|
7433
|
-
if (!Array.isArray(content)) return { ok: false, reason: "assistant content is not a block array" };
|
|
7434
|
-
const textBlocks = content.filter((b) => b && b.type === "text" && typeof b.text === "string");
|
|
7435
|
-
if (textBlocks.length !== 1) {
|
|
7436
|
-
return { ok: false, reason: `expected exactly 1 text block, found ${textBlocks.length}` };
|
|
7437
|
-
}
|
|
7438
|
-
textBlocks[0].text = newText;
|
|
7439
|
-
} else if (target.type === "user") {
|
|
7440
|
-
if (typeof target?.message?.content !== "string") {
|
|
7441
|
-
return { ok: false, reason: "user content is not a string" };
|
|
7442
|
-
}
|
|
7443
|
-
target.message.content = newText;
|
|
7444
|
-
} else {
|
|
7445
|
-
return { ok: false, reason: `unsupported record type: ${target.type}` };
|
|
7446
|
-
}
|
|
7447
|
-
const keptObjs = [];
|
|
7448
|
-
const kept = [];
|
|
7449
|
-
for (let i = 0; i <= index; i++) {
|
|
7450
|
-
kept.push(i === index ? JSON.stringify(target) : lines[i].raw);
|
|
7451
|
-
keptObjs.push(lines[i].obj);
|
|
7452
|
-
}
|
|
7453
|
-
if (orphanCheckObjs(keptObjs)) {
|
|
7454
|
-
return { ok: false, reason: "edit would orphan a tool_use (truncation drops its tool_result)" };
|
|
7455
|
-
}
|
|
7456
|
-
const out = kept.join("\n") + "\n";
|
|
7457
|
-
const tmp = `${file}.tmp-${process.pid}`;
|
|
7458
|
-
try {
|
|
7459
|
-
writeFileSync$1(tmp, out);
|
|
7460
|
-
renameSync$1(tmp, file);
|
|
7461
|
-
} catch (err) {
|
|
7462
|
-
return { ok: false, reason: `write failed: ${err?.message ?? err}` };
|
|
7463
|
-
}
|
|
7464
|
-
return { ok: true, truncatedAfter: index, totalBefore: lines.length };
|
|
7465
|
-
}
|
|
7466
|
-
function saveUndoSnapshot(undoDir, transcriptFile, messagesFile, meta) {
|
|
7467
|
-
mkdirSync$1(undoDir, { recursive: true });
|
|
7468
|
-
if (existsSync$1(transcriptFile)) copyFileSync(transcriptFile, join$1(undoDir, "transcript.jsonl"));
|
|
7469
|
-
if (existsSync$1(messagesFile)) copyFileSync(messagesFile, join$1(undoDir, "messages.jsonl"));
|
|
7470
|
-
writeFileSync$1(join$1(undoDir, "meta.json"), JSON.stringify(meta));
|
|
7471
|
-
}
|
|
7472
|
-
function readUndoMeta(undoDir) {
|
|
7473
|
-
const p = join$1(undoDir, "meta.json");
|
|
7474
|
-
if (!existsSync$1(p)) return null;
|
|
7475
|
-
try {
|
|
7476
|
-
return JSON.parse(readFileSync$1(p, "utf-8"));
|
|
7477
|
-
} catch {
|
|
7478
|
-
return null;
|
|
7479
|
-
}
|
|
7480
|
-
}
|
|
7481
|
-
function restoreTranscriptFromUndo(undoDir, targetFile) {
|
|
7482
|
-
const backup = join$1(undoDir, "transcript.jsonl");
|
|
7483
|
-
if (!existsSync$1(backup)) return false;
|
|
7484
|
-
const tmp = `${targetFile}.tmp-${process.pid}`;
|
|
7485
|
-
writeFileSync$1(tmp, readFileSync$1(backup));
|
|
7486
|
-
renameSync$1(tmp, targetFile);
|
|
7487
|
-
return true;
|
|
7488
|
-
}
|
|
7489
|
-
function clearUndoSnapshot(undoDir) {
|
|
7490
|
-
try {
|
|
7491
|
-
rmSync$1(undoDir, { recursive: true, force: true });
|
|
7492
|
-
} catch {
|
|
7493
|
-
}
|
|
7494
|
-
}
|
|
7495
|
-
function transcriptSessionIdMatches(file, expectedSessionId) {
|
|
7496
|
-
if (!existsSync$1(file)) return false;
|
|
7497
|
-
const lines = parseTranscript(file);
|
|
7498
|
-
for (const { obj } of lines) {
|
|
7499
|
-
if (obj && typeof obj.sessionId === "string") {
|
|
7500
|
-
return obj.sessionId === expectedSessionId;
|
|
7501
|
-
}
|
|
7502
|
-
}
|
|
7503
|
-
return false;
|
|
7504
|
-
}
|
|
7505
|
-
|
|
7506
7192
|
const execFileAsync = promisify$1(execFile$1);
|
|
7507
7193
|
const SVAMP_TOOLS_DIR = join(homedir(), ".svamp", "tools");
|
|
7508
7194
|
const SVAMP_BIN_DIR = join(SVAMP_TOOLS_DIR, "bin");
|
|
@@ -10234,7 +9920,15 @@ function deactivateLoop(directory, sessionId) {
|
|
|
10234
9920
|
} catch {
|
|
10235
9921
|
}
|
|
10236
9922
|
}
|
|
10237
|
-
function
|
|
9923
|
+
function readLoopJson(directory, sessionId, file) {
|
|
9924
|
+
try {
|
|
9925
|
+
const p = join$1(getLoopDir(directory, sessionId), file);
|
|
9926
|
+
return existsSync$1(p) ? JSON.parse(readFileSync$1(p, "utf-8")) : null;
|
|
9927
|
+
} catch {
|
|
9928
|
+
return null;
|
|
9929
|
+
}
|
|
9930
|
+
}
|
|
9931
|
+
function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata, sessionService, logger, onLoopActivated, onSupervisionVerdict) {
|
|
10238
9932
|
const configPath = getSvampConfigPath(directory, sessionId);
|
|
10239
9933
|
let lastConfigContent = "";
|
|
10240
9934
|
if (existsSync$1(configPath)) {
|
|
@@ -10323,6 +10017,27 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
10323
10017
|
"event"
|
|
10324
10018
|
);
|
|
10325
10019
|
logger.log(`[svampConfig] ${s.phase === "done" ? "Loop complete" : "Loop gave up"} (iter ${iter})`);
|
|
10020
|
+
try {
|
|
10021
|
+
const cfg = readLoopJson(directory, sessionId, "loop.config.json") || {};
|
|
10022
|
+
const ev = readLoopJson(directory, sessionId, "evaluator-verdict.json") || {};
|
|
10023
|
+
const verdict = {
|
|
10024
|
+
type: "supervision:verdict",
|
|
10025
|
+
sessionId,
|
|
10026
|
+
round: iter,
|
|
10027
|
+
verdict: s.phase === "done" ? "approved" : "rework",
|
|
10028
|
+
criteria: typeof cfg.criteria === "string" ? cfg.criteria : "",
|
|
10029
|
+
judge: cfg.evaluator?.enabled !== false ? "agent" : cfg.oracle ? "oracle" : "agent",
|
|
10030
|
+
...s.phase === "gave_up" ? { guidance: s.gave_up_reason || "gave up before meeting the criteria" } : ev.guidance ? { guidance: String(ev.guidance) } : {},
|
|
10031
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
10032
|
+
};
|
|
10033
|
+
try {
|
|
10034
|
+
const vp = join$1(getLoopDir(directory, sessionId), "supervisor-verdict.json");
|
|
10035
|
+
writeFileSync$1(vp, JSON.stringify(verdict, null, 2));
|
|
10036
|
+
} catch {
|
|
10037
|
+
}
|
|
10038
|
+
onSupervisionVerdict?.(verdict);
|
|
10039
|
+
} catch {
|
|
10040
|
+
}
|
|
10326
10041
|
} catch {
|
|
10327
10042
|
}
|
|
10328
10043
|
};
|
|
@@ -10378,6 +10093,53 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
10378
10093
|
const { loop: _, ...restPatch } = patch;
|
|
10379
10094
|
patch = restPatch;
|
|
10380
10095
|
}
|
|
10096
|
+
if ("supervisor" in patch) {
|
|
10097
|
+
const sup = patch.supervisor;
|
|
10098
|
+
if (sup && typeof sup === "object" && typeof sup.criteria === "string" && sup.criteria.trim()) {
|
|
10099
|
+
const criteria = sup.criteria.trim();
|
|
10100
|
+
const judges = Array.isArray(sup.judges) ? sup.judges : [];
|
|
10101
|
+
const oracleJudge = judges.find((j) => j?.type === "oracle");
|
|
10102
|
+
const oracle = oracleJudge && typeof oracleJudge.cmd === "string" && oracleJudge.cmd.trim() ? oracleJudge.cmd.trim() : typeof sup.oracle === "string" && sup.oracle.trim() ? sup.oracle.trim() : void 0;
|
|
10103
|
+
const hasAgentJudge = judges.length === 0 || judges.some((j) => j?.type === "agent");
|
|
10104
|
+
const maxRounds = typeof sup.max_rounds === "number" ? sup.max_rounds : 20;
|
|
10105
|
+
const ok = initLoop(directory, {
|
|
10106
|
+
task: criteria,
|
|
10107
|
+
// P1: criteria seeds LOOP.md as the goal (skill carries it as config.criteria in #30)
|
|
10108
|
+
criteria,
|
|
10109
|
+
oracle,
|
|
10110
|
+
maxIterations: maxRounds,
|
|
10111
|
+
evaluator: hasAgentJudge,
|
|
10112
|
+
sessionId
|
|
10113
|
+
});
|
|
10114
|
+
if (ok) {
|
|
10115
|
+
const judgeLabel = judges.length ? judges.map((j) => j.type).join("\u2192") : "agent";
|
|
10116
|
+
const idle = getMetadata().lifecycleState === "idle";
|
|
10117
|
+
if (idle) {
|
|
10118
|
+
const existingQueue = getMetadata().messageQueue || [];
|
|
10119
|
+
const nudge = `You are now under supervision. Success criteria: ${criteria}
|
|
10120
|
+
Continue working until they are met, or verify and finish \u2014 an independent Stop gate will re-check before you can stop.`;
|
|
10121
|
+
setMetadata((m) => ({ ...m, messageQueue: [...existingQueue, { id: randomUUID$1(), text: nudge, displayText: `\u{1F441} Supervisor attached`, createdAt: Date.now() }] }));
|
|
10122
|
+
onLoopActivated?.();
|
|
10123
|
+
}
|
|
10124
|
+
sessionService.pushMessage(
|
|
10125
|
+
{ type: "message", message: `\u{1F441} Supervisor attached \u2014 judges: ${judgeLabel}, max ${maxRounds}. Criteria: ${criteria.slice(0, 120)}${criteria.length > 120 ? "\u2026" : ""}` },
|
|
10126
|
+
"event"
|
|
10127
|
+
);
|
|
10128
|
+
logger.log(`[svampConfig] Supervisor attached (judges: ${judgeLabel}, max ${maxRounds})`);
|
|
10129
|
+
} else {
|
|
10130
|
+
sessionService.pushMessage(
|
|
10131
|
+
{ type: "message", message: "Failed to attach supervisor \u2014 the loop skill could not be located. Reinstall with: svamp skills install loop --force", level: "error" },
|
|
10132
|
+
"event"
|
|
10133
|
+
);
|
|
10134
|
+
}
|
|
10135
|
+
} else {
|
|
10136
|
+
deactivateLoop(directory, sessionId);
|
|
10137
|
+
sessionService.pushMessage({ type: "message", message: "Supervisor detached." }, "event");
|
|
10138
|
+
logger.log(`[svampConfig] Supervisor detached`);
|
|
10139
|
+
}
|
|
10140
|
+
const { supervisor: _s, ...restPatch } = patch;
|
|
10141
|
+
patch = restPatch;
|
|
10142
|
+
}
|
|
10381
10143
|
if (Object.keys(patch).length > 0) {
|
|
10382
10144
|
const config = readSvampConfig(configPath);
|
|
10383
10145
|
for (const [key, value] of Object.entries(patch)) {
|
|
@@ -10805,7 +10567,7 @@ async function startDaemon(options) {
|
|
|
10805
10567
|
const list = loadExposedTunnels().filter((t) => t.name !== name);
|
|
10806
10568
|
saveExposedTunnels(list);
|
|
10807
10569
|
}
|
|
10808
|
-
const { ServeManager } = await import('./serveManager-
|
|
10570
|
+
const { ServeManager } = await import('./serveManager-B19qVJeZ.mjs');
|
|
10809
10571
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
10810
10572
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
10811
10573
|
});
|
|
@@ -10835,6 +10597,31 @@ async function startDaemon(options) {
|
|
|
10835
10597
|
logger.log(`Hypha connection permanently lost: ${reason}. Daemon continues running \u2014 restart manually to reconnect.`);
|
|
10836
10598
|
});
|
|
10837
10599
|
const pidToTrackedSession = /* @__PURE__ */ new Map();
|
|
10600
|
+
const routeSupervisionVerdict = (parentId, v) => {
|
|
10601
|
+
try {
|
|
10602
|
+
if (!parentId) return;
|
|
10603
|
+
const parent = Array.from(pidToTrackedSession.values()).find((t) => t.svampSessionId === parentId);
|
|
10604
|
+
const send = parent?.sessionRPCHandlers?.sendInboxMessage;
|
|
10605
|
+
if (!send) return;
|
|
10606
|
+
Promise.resolve(send({
|
|
10607
|
+
messageId: randomUUID$1(),
|
|
10608
|
+
from: `agent:${v.sessionId}`,
|
|
10609
|
+
fromSession: v.sessionId,
|
|
10610
|
+
to: parentId,
|
|
10611
|
+
subject: `supervision: ${v.verdict} (child ${v.sessionId.slice(0, 8)})`,
|
|
10612
|
+
body: `<supervision-verdict child="${v.sessionId}" verdict="${v.verdict}" round="${v.round}" judge="${v.judge}">
|
|
10613
|
+
${v.guidance ? v.guidance + "\n" : ""}Criteria: ${v.criteria || "(none)"}
|
|
10614
|
+
</supervision-verdict>`,
|
|
10615
|
+
timestamp: Date.now(),
|
|
10616
|
+
read: false,
|
|
10617
|
+
urgency: "normal",
|
|
10618
|
+
hopCount: 1
|
|
10619
|
+
})).catch(() => {
|
|
10620
|
+
});
|
|
10621
|
+
logger.log(`[supervision] verdict '${v.verdict}' for ${v.sessionId.slice(0, 8)} \u2192 parent ${parentId.slice(0, 8)}`);
|
|
10622
|
+
} catch {
|
|
10623
|
+
}
|
|
10624
|
+
};
|
|
10838
10625
|
server.on("services_registered", () => {
|
|
10839
10626
|
if (consecutiveHeartbeatFailures > 0) {
|
|
10840
10627
|
logger.log(`Hypha reconnection successful \u2014 services re-registered (resetting ${consecutiveHeartbeatFailures} failures)`);
|
|
@@ -11857,13 +11644,12 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11857
11644
|
}
|
|
11858
11645
|
return child;
|
|
11859
11646
|
};
|
|
11860
|
-
const restartClaudeHandler = async (
|
|
11647
|
+
const restartClaudeHandler = async () => {
|
|
11861
11648
|
logger.log(`[Session ${sessionId}] Restart Claude requested`);
|
|
11862
11649
|
if (isRestartingClaude || isSwitchingMode) {
|
|
11863
11650
|
return { success: false, message: "Restart already in progress." };
|
|
11864
11651
|
}
|
|
11865
11652
|
isRestartingClaude = true;
|
|
11866
|
-
let beforeRespawnError;
|
|
11867
11653
|
try {
|
|
11868
11654
|
if (claudeProcess && claudeProcess.exitCode === null) {
|
|
11869
11655
|
isKillingClaude = true;
|
|
@@ -11875,14 +11661,6 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11875
11661
|
if (trackedSession?.stopped) {
|
|
11876
11662
|
return { success: false, message: "Session was stopped during restart." };
|
|
11877
11663
|
}
|
|
11878
|
-
if (opts?.beforeRespawn) {
|
|
11879
|
-
try {
|
|
11880
|
-
await opts.beforeRespawn();
|
|
11881
|
-
} catch (hookErr) {
|
|
11882
|
-
beforeRespawnError = hookErr?.message ?? String(hookErr);
|
|
11883
|
-
logger.log(`[Session ${sessionId}] beforeRespawn hook failed: ${beforeRespawnError}`);
|
|
11884
|
-
}
|
|
11885
|
-
}
|
|
11886
11664
|
if (claudeResumeId) {
|
|
11887
11665
|
if (!stagedCredentials && shouldIsolateSession()) {
|
|
11888
11666
|
try {
|
|
@@ -11895,9 +11673,6 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11895
11673
|
spawnClaude(void 0, { permissionMode: currentPermissionMode });
|
|
11896
11674
|
sessionService.updateMetadata(sessionMetadata);
|
|
11897
11675
|
logger.log(`[Session ${sessionId}] Claude respawned with --resume ${claudeResumeId}`);
|
|
11898
|
-
if (beforeRespawnError) {
|
|
11899
|
-
return { success: false, message: `Edit failed (session restored): ${beforeRespawnError}` };
|
|
11900
|
-
}
|
|
11901
11676
|
return { success: true, message: "Claude process restarted successfully." };
|
|
11902
11677
|
} else {
|
|
11903
11678
|
logger.log(`[Session ${sessionId}] No resume ID \u2014 cannot restart`);
|
|
@@ -11911,26 +11686,6 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11911
11686
|
isRestartingClaude = false;
|
|
11912
11687
|
}
|
|
11913
11688
|
};
|
|
11914
|
-
const editHistoryIdleGuard = () => {
|
|
11915
|
-
const lifecycle = sessionMetadata.lifecycleState;
|
|
11916
|
-
if (lifecycle === "running" || lifecycle === "restarting") {
|
|
11917
|
-
return { success: false, message: "Cannot edit while the agent is working \u2014 wait until it is idle." };
|
|
11918
|
-
}
|
|
11919
|
-
if (isRestartingClaude || isSwitchingMode || isKillingClaude) {
|
|
11920
|
-
return { success: false, message: "Cannot edit during a restart/mode switch \u2014 try again in a moment." };
|
|
11921
|
-
}
|
|
11922
|
-
const queueLen = sessionMetadata.messageQueue?.length ?? 0;
|
|
11923
|
-
if (queueLen > 0) {
|
|
11924
|
-
return { success: false, message: "Cannot edit while messages are queued." };
|
|
11925
|
-
}
|
|
11926
|
-
if (isLoopActiveForSession(directory, sessionId)) {
|
|
11927
|
-
return { success: false, message: "Cannot edit history while a loop is active." };
|
|
11928
|
-
}
|
|
11929
|
-
if (!claudeResumeId) {
|
|
11930
|
-
return { success: false, message: "No Claude transcript to edit yet." };
|
|
11931
|
-
}
|
|
11932
|
-
return null;
|
|
11933
|
-
};
|
|
11934
11689
|
if (shouldIsolateSession()) {
|
|
11935
11690
|
try {
|
|
11936
11691
|
stagedCredentials = await stageCredentialsForSharing(sessionId);
|
|
@@ -11951,7 +11706,6 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11951
11706
|
logger.log(`[Session ${sessionId}] User message received`);
|
|
11952
11707
|
userMessagePending = true;
|
|
11953
11708
|
turnInitiatedByUser = true;
|
|
11954
|
-
clearUndoSnapshot(join$1(getSessionDir(directory, sessionId), "undo"));
|
|
11955
11709
|
let text;
|
|
11956
11710
|
let msgMeta = meta;
|
|
11957
11711
|
try {
|
|
@@ -12171,86 +11925,6 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12171
11925
|
}
|
|
12172
11926
|
},
|
|
12173
11927
|
onRestartClaude: restartClaudeHandler,
|
|
12174
|
-
onEditTranscript: async ({ role, fromEnd, oldText, newText, applyStoreA }) => {
|
|
12175
|
-
const gate = editHistoryIdleGuard();
|
|
12176
|
-
if (gate) return gate;
|
|
12177
|
-
logger.log(`[Session ${sessionId}] Edit history: role=${role} fromEnd=${fromEnd} \u2192 kill + rewrite + resume`);
|
|
12178
|
-
const result = await restartClaudeHandler({
|
|
12179
|
-
beforeRespawn: async () => {
|
|
12180
|
-
const file = resolveTranscriptPath(directory, claudeResumeId);
|
|
12181
|
-
if (!transcriptSessionIdMatches(file, claudeResumeId)) {
|
|
12182
|
-
throw new Error("transcript file id mismatch (session rotated) \u2014 aborting edit");
|
|
12183
|
-
}
|
|
12184
|
-
const lines = parseTranscript(file);
|
|
12185
|
-
const anchor = findAnchorIndex(lines, { role, fromEnd, oldText });
|
|
12186
|
-
if (!anchor.ok) throw new Error(anchor.reason);
|
|
12187
|
-
const sessDir = getSessionDir(directory, sessionId);
|
|
12188
|
-
saveUndoSnapshot(join$1(sessDir, "undo"), file, join$1(sessDir, "messages.jsonl"), {
|
|
12189
|
-
preEditClaudeSessionId: claudeResumeId,
|
|
12190
|
-
createdAt: Date.now(),
|
|
12191
|
-
label: "edit"
|
|
12192
|
-
});
|
|
12193
|
-
const edit = applyTranscriptEdit(file, anchor.index, newText);
|
|
12194
|
-
if (!edit.ok) throw new Error(edit.reason);
|
|
12195
|
-
applyStoreA();
|
|
12196
|
-
logger.log(`[Session ${sessionId}] Edit applied \u2014 transcript truncated after line ${anchor.index}`);
|
|
12197
|
-
}
|
|
12198
|
-
});
|
|
12199
|
-
if (result.success && claudeResumeId && !trackedSession.stopped) {
|
|
12200
|
-
saveSession({
|
|
12201
|
-
sessionId,
|
|
12202
|
-
directory,
|
|
12203
|
-
claudeResumeId,
|
|
12204
|
-
permissionMode: currentPermissionMode,
|
|
12205
|
-
spawnMeta: lastSpawnMeta,
|
|
12206
|
-
metadata: sessionMetadata,
|
|
12207
|
-
createdAt: sessionCreatedAt,
|
|
12208
|
-
machineId,
|
|
12209
|
-
wasProcessing: false
|
|
12210
|
-
});
|
|
12211
|
-
artifactSync.syncSession(sessionId, getSessionDir(directory, sessionId), sessionMetadata, machineId).catch(() => {
|
|
12212
|
-
});
|
|
12213
|
-
}
|
|
12214
|
-
return result;
|
|
12215
|
-
},
|
|
12216
|
-
onUndoEdit: async ({ restoreStoreA }) => {
|
|
12217
|
-
const gate = editHistoryIdleGuard();
|
|
12218
|
-
if (gate) return gate;
|
|
12219
|
-
const undoDir = join$1(getSessionDir(directory, sessionId), "undo");
|
|
12220
|
-
const meta = readUndoMeta(undoDir);
|
|
12221
|
-
if (!meta) return { success: false, message: "Nothing to undo." };
|
|
12222
|
-
logger.log(`[Session ${sessionId}] Undo edit \u2192 restore pre-edit transcript ${meta.preEditClaudeSessionId} + messages, resume`);
|
|
12223
|
-
const result = await restartClaudeHandler({
|
|
12224
|
-
beforeRespawn: async () => {
|
|
12225
|
-
const target = resolveTranscriptPath(directory, meta.preEditClaudeSessionId);
|
|
12226
|
-
if (!restoreTranscriptFromUndo(undoDir, target)) {
|
|
12227
|
-
throw new Error("undo snapshot missing transcript backup");
|
|
12228
|
-
}
|
|
12229
|
-
restoreStoreA();
|
|
12230
|
-
claudeResumeId = meta.preEditClaudeSessionId;
|
|
12231
|
-
sessionMetadata = { ...sessionMetadata, claudeSessionId: claudeResumeId };
|
|
12232
|
-
}
|
|
12233
|
-
});
|
|
12234
|
-
if (result.success) {
|
|
12235
|
-
clearUndoSnapshot(undoDir);
|
|
12236
|
-
if (claudeResumeId && !trackedSession.stopped) {
|
|
12237
|
-
saveSession({
|
|
12238
|
-
sessionId,
|
|
12239
|
-
directory,
|
|
12240
|
-
claudeResumeId,
|
|
12241
|
-
permissionMode: currentPermissionMode,
|
|
12242
|
-
spawnMeta: lastSpawnMeta,
|
|
12243
|
-
metadata: sessionMetadata,
|
|
12244
|
-
createdAt: sessionCreatedAt,
|
|
12245
|
-
machineId,
|
|
12246
|
-
wasProcessing: false
|
|
12247
|
-
});
|
|
12248
|
-
artifactSync.syncSession(sessionId, getSessionDir(directory, sessionId), sessionMetadata, machineId).catch(() => {
|
|
12249
|
-
});
|
|
12250
|
-
}
|
|
12251
|
-
}
|
|
12252
|
-
return result;
|
|
12253
|
-
},
|
|
12254
11928
|
onUpdateSecurityContext: async (newSecurityContext) => {
|
|
12255
11929
|
logger.log(`[Session ${sessionId}] Security context update requested \u2014 restarting agent`);
|
|
12256
11930
|
sessionMetadata = { ...sessionMetadata, securityContext: newSecurityContext };
|
|
@@ -12511,7 +12185,8 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12511
12185
|
logger,
|
|
12512
12186
|
() => {
|
|
12513
12187
|
if (!trackedSession?.stopped) setTimeout(() => processMessageQueueRef?.(), 200);
|
|
12514
|
-
}
|
|
12188
|
+
},
|
|
12189
|
+
(v) => routeSupervisionVerdict(sessionMetadata.parentSessionId, v)
|
|
12515
12190
|
);
|
|
12516
12191
|
checkSvampConfig = svampConfig.check;
|
|
12517
12192
|
cleanupSvampConfig = svampConfig.cleanup;
|
|
@@ -13008,7 +12683,8 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
13008
12683
|
}
|
|
13009
12684
|
});
|
|
13010
12685
|
}
|
|
13011
|
-
}
|
|
12686
|
+
},
|
|
12687
|
+
(v) => routeSupervisionVerdict(sessionMetadata.parentSessionId, v)
|
|
13012
12688
|
);
|
|
13013
12689
|
const checkSvampConfig = svampConfigChecker.check;
|
|
13014
12690
|
const writeSvampConfigPatchAcp = svampConfigChecker.writeConfig;
|
|
@@ -13403,7 +13079,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
13403
13079
|
const specs = loadExposedTunnels();
|
|
13404
13080
|
if (specs.length === 0) return;
|
|
13405
13081
|
logger.log(`[exposed-tunnels] Restoring ${specs.length} tunnel(s) from ${EXPOSED_TUNNELS_FILE}`);
|
|
13406
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
13082
|
+
const { FrpcTunnel } = await import('./frpc-9qgaimIN.mjs');
|
|
13407
13083
|
for (const spec of specs) {
|
|
13408
13084
|
if (tunnels.has(spec.name)) continue;
|
|
13409
13085
|
try {
|