svamp-cli 0.2.107 → 0.2.109
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/inject-loop.mjs +20 -6
- package/bin/skills/loop/bin/loop-init.mjs +21 -8
- package/bin/skills/loop/bin/loop-status.mjs +17 -4
- package/bin/skills/loop/bin/precompact.mjs +5 -2
- package/bin/skills/loop/bin/state-fp.mjs +5 -7
- package/bin/skills/loop/bin/stop-gate.mjs +17 -4
- package/bin/skills/loop/test/test-loop-gate.mjs +55 -18
- package/dist/{agentCommands-CduKZKhS.mjs → agentCommands-CoGlvh8y.mjs} +37 -7
- package/dist/{auth-Df9Vbe1o.mjs → auth-DMERa7I8.mjs} +2 -2
- package/dist/cli.mjs +60 -52
- package/dist/{commands-DkDAm71w.mjs → commands-BhkiEmV8.mjs} +5 -5
- package/dist/{commands-Db5oGiGF.mjs → commands-D2kGC5mL.mjs} +3 -3
- package/dist/{commands-DMfzyn8l.mjs → commands-DJgTzFWk.mjs} +38 -17
- package/dist/{commands-B6uZpAXr.mjs → commands-Dy6X_MM5.mjs} +2 -2
- package/dist/{commands-Bq2anCn7.mjs → commands-j38M6llT.mjs} +2 -2
- package/dist/{fleet-kxxGXRSB.mjs → fleet-Pg9X2izv.mjs} +2 -2
- package/dist/{frpc-C9lPF2nK.mjs → frpc-BmpNco2u.mjs} +2 -2
- package/dist/{headlessCli-BgWGL3_l.mjs → headlessCli-6WuIXZ9F.mjs} +3 -3
- package/dist/index.mjs +2 -2
- package/dist/{package-VWSohGLN.mjs → package-Bz0dbAvV.mjs} +2 -2
- package/dist/{run-CvT9o581.mjs → run-B3epzMIw.mjs} +3 -3
- package/dist/{run-CCsUvTEL.mjs → run-CvXWD1x2.mjs} +313 -58
- package/dist/{serveCommands-tid4kA9J.mjs → serveCommands-J44oCE_D.mjs} +5 -5
- package/dist/{serveManager-CeM1exTU.mjs → serveManager-DU4OuM57.mjs} +3 -3
- package/dist/{sideband-fu9NMI64.mjs → sideband-Dt9N8vh6.mjs} +2 -2
- package/package.json +2 -2
|
@@ -1,15 +1,15 @@
|
|
|
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, copyFileSync,
|
|
4
|
-
import path__default, { join as join$1, dirname, basename, resolve } from 'path';
|
|
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';
|
|
8
|
+
import { randomBytes, randomUUID, createHash } from 'node:crypto';
|
|
8
9
|
import { existsSync, readFileSync, mkdirSync, readdirSync, writeFileSync, renameSync, rmSync, appendFileSync, unlinkSync } from 'node:fs';
|
|
9
|
-
import { exec,
|
|
10
|
+
import { exec, execSync, spawn, execFile as execFile$1, execFileSync } from 'node:child_process';
|
|
10
11
|
import { promisify } from 'util';
|
|
11
|
-
import { join } from 'node:path';
|
|
12
|
-
import { randomBytes, randomUUID, createHash } from 'node:crypto';
|
|
12
|
+
import { join, basename, dirname } from 'node:path';
|
|
13
13
|
import os, { homedir, platform } from 'node:os';
|
|
14
14
|
import { EventEmitter } from 'node:events';
|
|
15
15
|
import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk';
|
|
@@ -2527,7 +2527,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2527
2527
|
const tunnels = handlers.tunnels;
|
|
2528
2528
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
2529
2529
|
if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
|
|
2530
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
2530
|
+
const { FrpcTunnel } = await import('./frpc-BmpNco2u.mjs');
|
|
2531
2531
|
const tunnel = new FrpcTunnel({
|
|
2532
2532
|
name: params.name,
|
|
2533
2533
|
ports: params.ports,
|
|
@@ -2788,7 +2788,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
2788
2788
|
}
|
|
2789
2789
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
2790
2790
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
2791
|
-
const { toolsForRole } = await import('./sideband-
|
|
2791
|
+
const { toolsForRole } = await import('./sideband-Dt9N8vh6.mjs');
|
|
2792
2792
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
2793
2793
|
return fmt(r2);
|
|
2794
2794
|
}
|
|
@@ -2887,7 +2887,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
2887
2887
|
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
2888
2888
|
const callId = "call_" + Math.random().toString(16).slice(2, 12);
|
|
2889
2889
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
2890
|
-
const { queryCore } = await import('./commands-
|
|
2890
|
+
const { queryCore } = await import('./commands-DJgTzFWk.mjs');
|
|
2891
2891
|
const timeout = c.reply?.timeout_sec || 120;
|
|
2892
2892
|
let result;
|
|
2893
2893
|
try {
|
|
@@ -3349,7 +3349,6 @@ function formatInboxMessageXml(msg) {
|
|
|
3349
3349
|
if (msg.from) attrs.push(`from="${escapeXml(msg.from)}"`);
|
|
3350
3350
|
if (msg.channel) attrs.push(`channel="${escapeXml(msg.channel)}"`);
|
|
3351
3351
|
if (msg.verified !== void 0) attrs.push(`verified="${msg.verified === true}"`);
|
|
3352
|
-
if (msg.fromSession) attrs.push(`from-session="${escapeXml(msg.fromSession)}"`);
|
|
3353
3352
|
if (msg.to) attrs.push(`to="${escapeXml(msg.to)}"`);
|
|
3354
3353
|
if (msg.subject) attrs.push(`subject="${escapeXml(msg.subject)}"`);
|
|
3355
3354
|
if (msg.urgency) attrs.push(`urgency="${msg.urgency}"`);
|
|
@@ -3989,6 +3988,16 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
3989
3988
|
callbacks.onArchiveSession();
|
|
3990
3989
|
return { success: true };
|
|
3991
3990
|
},
|
|
3991
|
+
// Manual ✨ trigger from the UI: regenerate the session topic + shared project
|
|
3992
|
+
// description via a forked btw (Claude sessions only).
|
|
3993
|
+
regenerateSummary: async (context) => {
|
|
3994
|
+
authorizeRequest(context, metadata.sharing, "interact");
|
|
3995
|
+
if (!callbacks.onRegenerateSummary) {
|
|
3996
|
+
return { success: false, error: "Summary regeneration is not supported for this agent." };
|
|
3997
|
+
}
|
|
3998
|
+
const result = await callbacks.onRegenerateSummary();
|
|
3999
|
+
return { success: true, ...result };
|
|
4000
|
+
},
|
|
3992
4001
|
// ── Activity ──
|
|
3993
4002
|
keepAlive: async (thinking, mode, context) => {
|
|
3994
4003
|
authorizeRequest(context, metadata.sharing, "interact");
|
|
@@ -4384,6 +4393,20 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
4384
4393
|
return { store, rpcHandlers };
|
|
4385
4394
|
}
|
|
4386
4395
|
|
|
4396
|
+
const ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
4397
|
+
const MAX_UNBIASED = Math.floor(256 / ALPHABET.length) * ALPHABET.length;
|
|
4398
|
+
function shortId(length = 10) {
|
|
4399
|
+
let out = "";
|
|
4400
|
+
while (out.length < length) {
|
|
4401
|
+
const buf = randomBytes(length - out.length);
|
|
4402
|
+
for (let i = 0; i < buf.length && out.length < length; i++) {
|
|
4403
|
+
const b = buf[i];
|
|
4404
|
+
if (b < MAX_UNBIASED) out += ALPHABET[b % ALPHABET.length];
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
return out;
|
|
4408
|
+
}
|
|
4409
|
+
|
|
4387
4410
|
const SVAMP_HOME$2 = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
|
|
4388
4411
|
const num = (key, def) => {
|
|
4389
4412
|
const v = Number(process.env[key]);
|
|
@@ -4503,6 +4526,97 @@ function classifyInbound(message, now = Date.now()) {
|
|
|
4503
4526
|
return { action: "wake", reason: "urgent", breakerTripped: tripped };
|
|
4504
4527
|
}
|
|
4505
4528
|
|
|
4529
|
+
function resolveProjectRoot(directory) {
|
|
4530
|
+
try {
|
|
4531
|
+
const top = execSync("git rev-parse --show-toplevel", {
|
|
4532
|
+
cwd: directory,
|
|
4533
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
4534
|
+
}).toString().trim();
|
|
4535
|
+
if (top) return top;
|
|
4536
|
+
} catch {
|
|
4537
|
+
}
|
|
4538
|
+
return directory.replace(/[/\\]+$/, "") || directory;
|
|
4539
|
+
}
|
|
4540
|
+
function projectInfoPath(directory) {
|
|
4541
|
+
return join(resolveProjectRoot(directory), ".svamp", "project.json");
|
|
4542
|
+
}
|
|
4543
|
+
function projectName(directory) {
|
|
4544
|
+
return basename(resolveProjectRoot(directory).replace(/[/\\]+$/, "")) || "project";
|
|
4545
|
+
}
|
|
4546
|
+
function readProjectInfo(directory) {
|
|
4547
|
+
try {
|
|
4548
|
+
const p = projectInfoPath(directory);
|
|
4549
|
+
if (!existsSync(p)) return null;
|
|
4550
|
+
const info = JSON.parse(readFileSync(p, "utf-8"));
|
|
4551
|
+
if (info && typeof info === "object") return info;
|
|
4552
|
+
} catch {
|
|
4553
|
+
}
|
|
4554
|
+
return null;
|
|
4555
|
+
}
|
|
4556
|
+
function writeProjectInfo(directory, info) {
|
|
4557
|
+
const p = projectInfoPath(directory);
|
|
4558
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
4559
|
+
const tmp = p + ".tmp";
|
|
4560
|
+
writeFileSync(tmp, JSON.stringify(info, null, 2));
|
|
4561
|
+
renameSync(tmp, p);
|
|
4562
|
+
}
|
|
4563
|
+
function sanitizeDescription(raw, maxLen = 240) {
|
|
4564
|
+
if (!raw) return null;
|
|
4565
|
+
let t = String(raw).replace(/\s+/g, " ").trim();
|
|
4566
|
+
t = t.replace(/^["'`*\s]+|["'`*\s]+$/g, "").trim();
|
|
4567
|
+
if (t.length > maxLen) t = t.slice(0, maxLen - 1).trimEnd() + "\u2026";
|
|
4568
|
+
return t || null;
|
|
4569
|
+
}
|
|
4570
|
+
function extractDescriptionFromDocs(directory) {
|
|
4571
|
+
const root = resolveProjectRoot(directory);
|
|
4572
|
+
const candidates = [
|
|
4573
|
+
[join(root, "CLAUDE.md"), "claude.md"],
|
|
4574
|
+
[join(root, "README.md"), "readme"],
|
|
4575
|
+
[join(root, "readme.md"), "readme"]
|
|
4576
|
+
];
|
|
4577
|
+
for (const [file, source] of candidates) {
|
|
4578
|
+
try {
|
|
4579
|
+
if (!existsSync(file)) continue;
|
|
4580
|
+
const desc = sanitizeDescription(leadParagraph(readFileSync(file, "utf-8")));
|
|
4581
|
+
if (desc) return { description: desc, source };
|
|
4582
|
+
} catch {
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4585
|
+
return null;
|
|
4586
|
+
}
|
|
4587
|
+
function leadParagraph(md) {
|
|
4588
|
+
const buf = [];
|
|
4589
|
+
for (const raw of md.split(/\r?\n/)) {
|
|
4590
|
+
const line = raw.trim();
|
|
4591
|
+
if (!line) {
|
|
4592
|
+
if (buf.length) break;
|
|
4593
|
+
else continue;
|
|
4594
|
+
}
|
|
4595
|
+
if (/^#{1,6}\s/.test(line)) {
|
|
4596
|
+
if (buf.length) break;
|
|
4597
|
+
else continue;
|
|
4598
|
+
}
|
|
4599
|
+
if (/^([![]|<|\||>)/.test(line)) continue;
|
|
4600
|
+
if (/^[-*_=]{3,}$/.test(line)) continue;
|
|
4601
|
+
buf.push(line);
|
|
4602
|
+
if (buf.join(" ").length > 240) break;
|
|
4603
|
+
}
|
|
4604
|
+
const d = buf.join(" ").replace(/\s+/g, " ").trim();
|
|
4605
|
+
return d || null;
|
|
4606
|
+
}
|
|
4607
|
+
|
|
4608
|
+
var projectInfo = /*#__PURE__*/Object.freeze({
|
|
4609
|
+
__proto__: null,
|
|
4610
|
+
extractDescriptionFromDocs: extractDescriptionFromDocs,
|
|
4611
|
+
leadParagraph: leadParagraph,
|
|
4612
|
+
projectInfoPath: projectInfoPath,
|
|
4613
|
+
projectName: projectName,
|
|
4614
|
+
readProjectInfo: readProjectInfo,
|
|
4615
|
+
resolveProjectRoot: resolveProjectRoot,
|
|
4616
|
+
sanitizeDescription: sanitizeDescription,
|
|
4617
|
+
writeProjectInfo: writeProjectInfo
|
|
4618
|
+
});
|
|
4619
|
+
|
|
4506
4620
|
async function registerDebugService(server, machineId, deps) {
|
|
4507
4621
|
const serviceInfo = await server.registerService(
|
|
4508
4622
|
{
|
|
@@ -9014,7 +9128,7 @@ function buildBaselineSystemPrompt(sessionId) {
|
|
|
9014
9128
|
You are running inside a Svamp session (id: ${sessionId}) on Hypha Cloud. 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.
|
|
9015
9129
|
|
|
9016
9130
|
**Session state:**
|
|
9017
|
-
- \`svamp session set-title "<
|
|
9131
|
+
- \`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).
|
|
9018
9132
|
- \`svamp session set-link "<url>" "<label>"\` \u2014 surface any viewable artifact as a button
|
|
9019
9133
|
- \`svamp session notify "<msg>" [--level info|warning|error]\` \u2014 send a user notification
|
|
9020
9134
|
|
|
@@ -9024,7 +9138,7 @@ You are running inside a Svamp session (id: ${sessionId}) on Hypha Cloud. Use th
|
|
|
9024
9138
|
|
|
9025
9139
|
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.
|
|
9026
9140
|
|
|
9027
|
-
**Inbox messages from other agents** arrive wrapped as \`<svamp-message message-id="\u2026" from="agent:\u2026"
|
|
9141
|
+
**Inbox messages from other agents** arrive wrapped as \`<svamp-message message-id="\u2026" from="agent:\u2026" \u2026>BODY</svamp-message>\` (a plain user turn has no wrapper). 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.
|
|
9028
9142
|
`;
|
|
9029
9143
|
}
|
|
9030
9144
|
|
|
@@ -9312,7 +9426,7 @@ async function readSessionFileBase64(resolvedPath) {
|
|
|
9312
9426
|
}
|
|
9313
9427
|
|
|
9314
9428
|
const __filename$1 = fileURLToPath(import.meta.url);
|
|
9315
|
-
const __dirname$1 = dirname(__filename$1);
|
|
9429
|
+
const __dirname$1 = dirname$1(__filename$1);
|
|
9316
9430
|
const CLAUDE_SKILLS_DIR = join$1(os$1.homedir(), ".claude", "skills");
|
|
9317
9431
|
function looksLikeClaudeError(line) {
|
|
9318
9432
|
const l = line.toLowerCase();
|
|
@@ -9389,7 +9503,7 @@ async function installSkillFromEndpoint(name, baseUrl) {
|
|
|
9389
9503
|
const content = await fileResp.text();
|
|
9390
9504
|
const localPath = join$1(targetDir, filePath);
|
|
9391
9505
|
if (!localPath.startsWith(targetDir + "/")) continue;
|
|
9392
|
-
mkdirSync$1(dirname(localPath), { recursive: true });
|
|
9506
|
+
mkdirSync$1(dirname$1(localPath), { recursive: true });
|
|
9393
9507
|
writeFileSync$1(localPath, content, "utf-8");
|
|
9394
9508
|
}
|
|
9395
9509
|
}
|
|
@@ -9423,7 +9537,7 @@ async function installSkillFromMarketplace(name) {
|
|
|
9423
9537
|
const content = await resp.text();
|
|
9424
9538
|
const localPath = join$1(targetDir, filePath);
|
|
9425
9539
|
if (!localPath.startsWith(targetDir + "/")) continue;
|
|
9426
|
-
mkdirSync$1(dirname(localPath), { recursive: true });
|
|
9540
|
+
mkdirSync$1(dirname$1(localPath), { recursive: true });
|
|
9427
9541
|
writeFileSync$1(localPath, content, "utf-8");
|
|
9428
9542
|
}
|
|
9429
9543
|
}
|
|
@@ -9431,9 +9545,9 @@ function getBundledSkillsDir() {
|
|
|
9431
9545
|
try {
|
|
9432
9546
|
const here = fileURLToPath(import.meta.url);
|
|
9433
9547
|
const candidates = [
|
|
9434
|
-
join$1(dirname(here), "..", "bin", "skills"),
|
|
9548
|
+
join$1(dirname$1(here), "..", "bin", "skills"),
|
|
9435
9549
|
// built dist/ layout
|
|
9436
|
-
join$1(dirname(here), "..", "..", "bin", "skills")
|
|
9550
|
+
join$1(dirname$1(here), "..", "..", "bin", "skills")
|
|
9437
9551
|
// src/daemon → bin layout via tsx
|
|
9438
9552
|
];
|
|
9439
9553
|
for (const c of candidates) {
|
|
@@ -9691,36 +9805,43 @@ function readSvampConfig(configPath) {
|
|
|
9691
9805
|
return {};
|
|
9692
9806
|
}
|
|
9693
9807
|
function writeSvampConfig(configPath, config) {
|
|
9694
|
-
mkdirSync$1(dirname(configPath), { recursive: true });
|
|
9808
|
+
mkdirSync$1(dirname$1(configPath), { recursive: true });
|
|
9695
9809
|
const content = JSON.stringify(config, null, 2);
|
|
9696
9810
|
const tmpPath = configPath + ".tmp";
|
|
9697
9811
|
writeFileSync$1(tmpPath, content);
|
|
9698
9812
|
renameSync$1(tmpPath, configPath);
|
|
9699
9813
|
return content;
|
|
9700
9814
|
}
|
|
9701
|
-
function getLoopDir(directory) {
|
|
9815
|
+
function getLoopDir(directory, sessionId) {
|
|
9816
|
+
if (sessionId) {
|
|
9817
|
+
const scoped = join$1(directory, ".svamp", sessionId, "loop");
|
|
9818
|
+
if (existsSync$1(join$1(scoped, "loop-state.json"))) return scoped;
|
|
9819
|
+
const legacy = join$1(directory, ".claude", "loop");
|
|
9820
|
+
if (existsSync$1(join$1(legacy, "loop-state.json"))) return legacy;
|
|
9821
|
+
return scoped;
|
|
9822
|
+
}
|
|
9702
9823
|
return join$1(directory, ".claude", "loop");
|
|
9703
9824
|
}
|
|
9704
|
-
function readLoopState(directory) {
|
|
9825
|
+
function readLoopState(directory, sessionId) {
|
|
9705
9826
|
try {
|
|
9706
|
-
const p = join$1(getLoopDir(directory), "loop-state.json");
|
|
9827
|
+
const p = join$1(getLoopDir(directory, sessionId), "loop-state.json");
|
|
9707
9828
|
if (!existsSync$1(p)) return null;
|
|
9708
9829
|
return JSON.parse(readFileSync$1(p, "utf-8"));
|
|
9709
9830
|
} catch {
|
|
9710
9831
|
return null;
|
|
9711
9832
|
}
|
|
9712
9833
|
}
|
|
9713
|
-
function isLoopActive(directory) {
|
|
9714
|
-
const s = readLoopState(directory);
|
|
9834
|
+
function isLoopActive(directory, sessionId) {
|
|
9835
|
+
const s = readLoopState(directory, sessionId);
|
|
9715
9836
|
return !!s && s.active !== false && s.phase !== "done" && s.phase !== "gave_up" && s.phase !== "cancelled";
|
|
9716
9837
|
}
|
|
9717
|
-
function loopOwnerSession(directory) {
|
|
9718
|
-
const s = readLoopState(directory);
|
|
9838
|
+
function loopOwnerSession(directory, sessionId) {
|
|
9839
|
+
const s = readLoopState(directory, sessionId);
|
|
9719
9840
|
if (!s || s.active === false || s.phase === "done" || s.phase === "gave_up" || s.phase === "cancelled") return null;
|
|
9720
9841
|
return typeof s.session_id === "string" ? s.session_id : null;
|
|
9721
9842
|
}
|
|
9722
9843
|
function isLoopActiveForSession(directory, sessionId) {
|
|
9723
|
-
const s = readLoopState(directory);
|
|
9844
|
+
const s = readLoopState(directory, sessionId);
|
|
9724
9845
|
if (!s || s.active === false || s.phase === "done" || s.phase === "gave_up" || s.phase === "cancelled") return false;
|
|
9725
9846
|
if (typeof s.session_id !== "string") return true;
|
|
9726
9847
|
return s.session_id === sessionId;
|
|
@@ -9746,9 +9867,9 @@ function initLoop(directory, cfg) {
|
|
|
9746
9867
|
const res = spawnSync(process.execPath, args, { encoding: "utf-8", timeout: 3e4 });
|
|
9747
9868
|
return res.status === 0;
|
|
9748
9869
|
}
|
|
9749
|
-
function deactivateLoop(directory) {
|
|
9870
|
+
function deactivateLoop(directory, sessionId) {
|
|
9750
9871
|
try {
|
|
9751
|
-
const p = join$1(getLoopDir(directory), "loop-state.json");
|
|
9872
|
+
const p = join$1(getLoopDir(directory, sessionId), "loop-state.json");
|
|
9752
9873
|
if (!existsSync$1(p)) return;
|
|
9753
9874
|
const s = JSON.parse(readFileSync$1(p, "utf-8"));
|
|
9754
9875
|
s.active = false;
|
|
@@ -9873,7 +9994,7 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
9873
9994
|
logger.log(`[svampConfig] Loop init failed \u2014 loop-init.mjs not found`);
|
|
9874
9995
|
}
|
|
9875
9996
|
} else {
|
|
9876
|
-
deactivateLoop(directory);
|
|
9997
|
+
deactivateLoop(directory, sessionId);
|
|
9877
9998
|
sessionService.pushMessage({ type: "message", message: "Loop cancelled." }, "event");
|
|
9878
9999
|
logger.log(`[svampConfig] Loop cancelled`);
|
|
9879
10000
|
}
|
|
@@ -9892,7 +10013,7 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
9892
10013
|
};
|
|
9893
10014
|
let watcher = null;
|
|
9894
10015
|
try {
|
|
9895
|
-
const configDir = dirname(configPath);
|
|
10016
|
+
const configDir = dirname$1(configPath);
|
|
9896
10017
|
mkdirSync$1(configDir, { recursive: true });
|
|
9897
10018
|
watcher = watch(configDir, (eventType, filename) => {
|
|
9898
10019
|
if (filename === "config.json") configChecker();
|
|
@@ -10307,7 +10428,7 @@ async function startDaemon(options) {
|
|
|
10307
10428
|
const list = loadExposedTunnels().filter((t) => t.name !== name);
|
|
10308
10429
|
saveExposedTunnels(list);
|
|
10309
10430
|
}
|
|
10310
|
-
const { ServeManager } = await import('./serveManager-
|
|
10431
|
+
const { ServeManager } = await import('./serveManager-DU4OuM57.mjs');
|
|
10311
10432
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
10312
10433
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
10313
10434
|
});
|
|
@@ -10399,7 +10520,7 @@ async function startDaemon(options) {
|
|
|
10399
10520
|
return { type: "error", errorMessage: `Failed to create directory: ${err.message}` };
|
|
10400
10521
|
}
|
|
10401
10522
|
}
|
|
10402
|
-
const sessionId = options2.sessionId ||
|
|
10523
|
+
const sessionId = options2.sessionId || shortId();
|
|
10403
10524
|
const agentName = options2.agent || agentConfig.agent_type || "claude";
|
|
10404
10525
|
if (agentName !== "claude" && (KNOWN_ACP_AGENTS[agentName] || KNOWN_MCP_AGENTS[agentName])) {
|
|
10405
10526
|
return await spawnAgentSession(sessionId, directory, agentName, options2, resumeSessionId);
|
|
@@ -10524,6 +10645,135 @@ async function startDaemon(options) {
|
|
|
10524
10645
|
let lastSpawnMeta = persisted?.spawnMeta || {};
|
|
10525
10646
|
let sessionWasProcessing = !!options2.wasProcessing;
|
|
10526
10647
|
let lastMainModel;
|
|
10648
|
+
const AUTO_TOPIC_DISABLED = process.env.SVAMP_AUTO_TOPIC === "0";
|
|
10649
|
+
const projName = projectName(directory);
|
|
10650
|
+
let bootstrapAttempted = false;
|
|
10651
|
+
let topicBtwInFlight = false;
|
|
10652
|
+
const sanitizeTopic = (raw) => sanitizeDescription(raw == null ? null : String(raw).split(/\r?\n/).find((l) => l.trim()) || "", 140);
|
|
10653
|
+
const applyTopic = (topic) => {
|
|
10654
|
+
sessionMetadata = { ...sessionMetadata, summary: { text: topic, updatedAt: Date.now() } };
|
|
10655
|
+
sessionService.updateMetadata(sessionMetadata);
|
|
10656
|
+
sessionService.pushMessage({ type: "summary", summary: topic }, "session");
|
|
10657
|
+
};
|
|
10658
|
+
const applyProjectToMeta = (name, description) => {
|
|
10659
|
+
if (sessionMetadata.projectName === name && sessionMetadata.projectDescription === description) return;
|
|
10660
|
+
sessionMetadata = { ...sessionMetadata, projectName: name, projectDescription: description };
|
|
10661
|
+
sessionService.updateMetadata(sessionMetadata);
|
|
10662
|
+
};
|
|
10663
|
+
const forkBtw = (question) => new Promise((resolve2) => {
|
|
10664
|
+
const claudeSessionId = sessionMetadata.claudeSessionId;
|
|
10665
|
+
if (!claudeSessionId) {
|
|
10666
|
+
resolve2(null);
|
|
10667
|
+
return;
|
|
10668
|
+
}
|
|
10669
|
+
const cwd = sessionMetadata.path || directory;
|
|
10670
|
+
const model = process.env.SVAMP_TOPIC_MODEL;
|
|
10671
|
+
try {
|
|
10672
|
+
const child = spawn$1("claude", [
|
|
10673
|
+
"--print",
|
|
10674
|
+
question,
|
|
10675
|
+
"--resume",
|
|
10676
|
+
claudeSessionId,
|
|
10677
|
+
"--fork-session",
|
|
10678
|
+
"--no-session-persistence",
|
|
10679
|
+
"--permission-mode",
|
|
10680
|
+
"bypassPermissions",
|
|
10681
|
+
...model ? ["--model", model] : [],
|
|
10682
|
+
"--output-format",
|
|
10683
|
+
"json",
|
|
10684
|
+
"--max-turns",
|
|
10685
|
+
"6"
|
|
10686
|
+
], { cwd, timeout: 6e4, stdio: ["ignore", "pipe", "pipe"], env: { ...process.env, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" } });
|
|
10687
|
+
let stdout = "";
|
|
10688
|
+
child.stdout?.on("data", (d) => {
|
|
10689
|
+
stdout += d.toString();
|
|
10690
|
+
});
|
|
10691
|
+
child.on("close", () => {
|
|
10692
|
+
try {
|
|
10693
|
+
const r = JSON.parse(stdout);
|
|
10694
|
+
resolve2(r.result || r.text || null);
|
|
10695
|
+
} catch {
|
|
10696
|
+
resolve2(null);
|
|
10697
|
+
}
|
|
10698
|
+
});
|
|
10699
|
+
child.on("error", () => resolve2(null));
|
|
10700
|
+
} catch {
|
|
10701
|
+
resolve2(null);
|
|
10702
|
+
}
|
|
10703
|
+
});
|
|
10704
|
+
const parseLabeled = (raw, label) => {
|
|
10705
|
+
if (!raw) return null;
|
|
10706
|
+
const m = raw.match(new RegExp(`${label}\\s*:\\s*(.+)`, "i"));
|
|
10707
|
+
return m ? m[1].trim() : null;
|
|
10708
|
+
};
|
|
10709
|
+
const generateSummary = async (force) => {
|
|
10710
|
+
if (topicBtwInFlight) return {};
|
|
10711
|
+
const existing = readProjectInfo(directory);
|
|
10712
|
+
applyProjectToMeta(existing?.name || projName, existing?.description);
|
|
10713
|
+
let projectDescription = existing?.description;
|
|
10714
|
+
let haveProject = !!projectDescription;
|
|
10715
|
+
if (force || !haveProject) {
|
|
10716
|
+
const docs = extractDescriptionFromDocs(directory);
|
|
10717
|
+
if (docs) {
|
|
10718
|
+
writeProjectInfo(directory, { name: projName, description: docs.description, source: docs.source, updatedAt: Date.now() });
|
|
10719
|
+
applyProjectToMeta(projName, docs.description);
|
|
10720
|
+
projectDescription = docs.description;
|
|
10721
|
+
haveProject = true;
|
|
10722
|
+
}
|
|
10723
|
+
}
|
|
10724
|
+
const needTopic = force || !sessionMetadata.summary?.text?.trim();
|
|
10725
|
+
const needProjectBtw = !haveProject;
|
|
10726
|
+
let topic = sessionMetadata.summary?.text;
|
|
10727
|
+
if (!needTopic && !needProjectBtw || trackedSession?.stopped) return { topic, projectDescription };
|
|
10728
|
+
topicBtwInFlight = true;
|
|
10729
|
+
try {
|
|
10730
|
+
const parts = [];
|
|
10731
|
+
if (needTopic) parts.push("TOPIC: <one sentence (10-18 words) on what THIS session is currently working on>");
|
|
10732
|
+
if (needProjectBtw) parts.push("PROJECT: <one sentence on what this project/folder is about overall \u2014 its purpose, not this session's task>");
|
|
10733
|
+
const raw = await forkBtw(`Based only on the conversation you can already see (do NOT use any tools), output EXACTLY the following line(s), nothing else:
|
|
10734
|
+
${parts.join("\n")}`);
|
|
10735
|
+
if (needTopic) {
|
|
10736
|
+
const t = sanitizeTopic(needProjectBtw ? parseLabeled(raw, "TOPIC") : parseLabeled(raw, "TOPIC") || raw);
|
|
10737
|
+
if (t) {
|
|
10738
|
+
applyTopic(t);
|
|
10739
|
+
topic = t;
|
|
10740
|
+
logger.log(`[Session ${sessionId}] ${force ? "Regenerated" : "Auto"}-topic \u2192 "${t}"`);
|
|
10741
|
+
} else if (!sessionMetadata.summary?.text?.trim()) {
|
|
10742
|
+
const b = basename$1(directory.replace(/[/\\]+$/, "")) || "session";
|
|
10743
|
+
applyTopic(b);
|
|
10744
|
+
topic = b;
|
|
10745
|
+
}
|
|
10746
|
+
}
|
|
10747
|
+
if (needProjectBtw) {
|
|
10748
|
+
const d = sanitizeDescription(parseLabeled(raw, "PROJECT") || (!needTopic ? raw : null));
|
|
10749
|
+
if (d) {
|
|
10750
|
+
writeProjectInfo(directory, { name: projName, description: d, source: "btw", updatedAt: Date.now() });
|
|
10751
|
+
applyProjectToMeta(projName, d);
|
|
10752
|
+
projectDescription = d;
|
|
10753
|
+
logger.log(`[Session ${sessionId}] Project description (btw) \u2192 "${d}"`);
|
|
10754
|
+
}
|
|
10755
|
+
}
|
|
10756
|
+
} catch {
|
|
10757
|
+
} finally {
|
|
10758
|
+
topicBtwInFlight = false;
|
|
10759
|
+
}
|
|
10760
|
+
return { topic, projectDescription };
|
|
10761
|
+
};
|
|
10762
|
+
const maybeBootstrap = () => {
|
|
10763
|
+
try {
|
|
10764
|
+
const existing = readProjectInfo(directory);
|
|
10765
|
+
applyProjectToMeta(existing?.name || projName, existing?.description);
|
|
10766
|
+
if (bootstrapAttempted || topicBtwInFlight) return;
|
|
10767
|
+
if (AUTO_TOPIC_DISABLED || trackedSession?.stopped || isLoopActive(directory)) return;
|
|
10768
|
+
bootstrapAttempted = true;
|
|
10769
|
+
void generateSummary(false);
|
|
10770
|
+
} catch {
|
|
10771
|
+
}
|
|
10772
|
+
};
|
|
10773
|
+
const regenerateSummaryNow = async () => {
|
|
10774
|
+
bootstrapAttempted = true;
|
|
10775
|
+
return await generateSummary(true);
|
|
10776
|
+
};
|
|
10527
10777
|
let spawnHasReceivedInit = false;
|
|
10528
10778
|
let startupFailureRetryPending = false;
|
|
10529
10779
|
let startupRetryMessage;
|
|
@@ -10546,7 +10796,7 @@ async function startDaemon(options) {
|
|
|
10546
10796
|
stuckWatchdogTimer = setInterval(() => {
|
|
10547
10797
|
if (!claudeProcess || claudeProcess.exitCode !== null) return;
|
|
10548
10798
|
if (!sessionWasProcessing) return;
|
|
10549
|
-
if (!isLoopActive(directory)) return;
|
|
10799
|
+
if (!isLoopActive(directory, sessionId)) return;
|
|
10550
10800
|
if (claudeProcess.pid && hasActiveChildren(claudeProcess.pid)) {
|
|
10551
10801
|
lastOutputTime = Date.now();
|
|
10552
10802
|
return;
|
|
@@ -10560,7 +10810,7 @@ async function startDaemon(options) {
|
|
|
10560
10810
|
);
|
|
10561
10811
|
claudeProcess.kill("SIGTERM");
|
|
10562
10812
|
setTimeout(() => {
|
|
10563
|
-
if (!trackedSession.stopped && isLoopActive(directory)) {
|
|
10813
|
+
if (!trackedSession.stopped && isLoopActive(directory, sessionId)) {
|
|
10564
10814
|
logger.log(`[Session ${sessionId}] Stuck watchdog: nudging loop to resume`);
|
|
10565
10815
|
enqueueLoopContinue();
|
|
10566
10816
|
processMessageQueueRef?.();
|
|
@@ -10668,7 +10918,7 @@ async function startDaemon(options) {
|
|
|
10668
10918
|
if (options2.forceIsolation || sessionMetadata.sharing?.enabled) {
|
|
10669
10919
|
rawPermissionMode = rawPermissionMode === "default" ? "auto-approve-all" : rawPermissionMode;
|
|
10670
10920
|
}
|
|
10671
|
-
if (
|
|
10921
|
+
if (isLoopActiveForSession(directory, sessionId)) {
|
|
10672
10922
|
rawPermissionMode = "bypassPermissions";
|
|
10673
10923
|
}
|
|
10674
10924
|
const permissionMode = toClaudePermissionMode(rawPermissionMode);
|
|
@@ -10941,7 +11191,7 @@ async function startDaemon(options) {
|
|
|
10941
11191
|
}
|
|
10942
11192
|
}
|
|
10943
11193
|
if (msg.type === "result") {
|
|
10944
|
-
const loopActive =
|
|
11194
|
+
const loopActive = isLoopActiveForSession(directory, sessionId);
|
|
10945
11195
|
if (!turnInitiatedByUser && !loopActive) {
|
|
10946
11196
|
logger.log(`[Session ${sessionId}] Skipping stale result from SDK-initiated turn`);
|
|
10947
11197
|
const hasBackgroundTasks = backgroundTaskCount > 0;
|
|
@@ -11005,17 +11255,7 @@ async function startDaemon(options) {
|
|
|
11005
11255
|
}
|
|
11006
11256
|
checkSvampConfig?.();
|
|
11007
11257
|
clearInboundContext(sessionId);
|
|
11008
|
-
|
|
11009
|
-
const hasTitle = !!sessionMetadata.summary?.text?.trim() || !!sessionMetadata.customTitle?.toString().trim();
|
|
11010
|
-
if (!hasTitle) {
|
|
11011
|
-
const base = basename(directory.replace(/[/\\]+$/, "")) || "session";
|
|
11012
|
-
sessionMetadata = { ...sessionMetadata, summary: { text: base, updatedAt: Date.now() } };
|
|
11013
|
-
sessionService.updateMetadata(sessionMetadata);
|
|
11014
|
-
sessionService.pushMessage({ type: "summary", summary: base }, "session");
|
|
11015
|
-
logger.log(`[Session ${sessionId}] Auto-title fallback \u2192 "${base}"`);
|
|
11016
|
-
}
|
|
11017
|
-
} catch {
|
|
11018
|
-
}
|
|
11258
|
+
maybeBootstrap();
|
|
11019
11259
|
if (backgroundTaskCount > 0) {
|
|
11020
11260
|
const taskInfo = `Background tasks still running (${backgroundTaskCount}): ${backgroundTaskNames.join(", ")}`;
|
|
11021
11261
|
logger.log(`[Session ${sessionId}] ${taskInfo}`);
|
|
@@ -11582,6 +11822,10 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11582
11822
|
logger.log(`[Session ${sessionId}] Archive session requested`);
|
|
11583
11823
|
archiveSession(sessionId);
|
|
11584
11824
|
},
|
|
11825
|
+
onRegenerateSummary: async () => {
|
|
11826
|
+
logger.log(`[Session ${sessionId}] Manual summary regeneration requested`);
|
|
11827
|
+
return await regenerateSummaryNow();
|
|
11828
|
+
},
|
|
11585
11829
|
onInboxMessage: (message) => {
|
|
11586
11830
|
if (trackedSession?.stopped) return;
|
|
11587
11831
|
const decision = classifyInbound(message);
|
|
@@ -11712,7 +11956,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11712
11956
|
if (sessionMetadata.securityContext && resolvedPath !== resolve(directory) && !resolvedPath.startsWith(resolve(directory) + "/")) {
|
|
11713
11957
|
throw new Error("Path outside working directory");
|
|
11714
11958
|
}
|
|
11715
|
-
await fs.mkdir(dirname(resolvedPath), { recursive: true });
|
|
11959
|
+
await fs.mkdir(dirname$1(resolvedPath), { recursive: true });
|
|
11716
11960
|
await fs.writeFile(resolvedPath, Buffer.from(content, "base64"));
|
|
11717
11961
|
},
|
|
11718
11962
|
onListDirectory: async (path) => {
|
|
@@ -11759,7 +12003,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11759
12003
|
if (sessionMetadata.securityContext && resolvedPath !== resolve(directory) && !resolvedPath.startsWith(resolve(directory) + "/")) {
|
|
11760
12004
|
throw new Error("Path outside working directory");
|
|
11761
12005
|
}
|
|
11762
|
-
const tree = await buildTree(resolvedPath, basename(resolvedPath), 0);
|
|
12006
|
+
const tree = await buildTree(resolvedPath, basename$1(resolvedPath), 0);
|
|
11763
12007
|
return { success: !!tree, tree };
|
|
11764
12008
|
}
|
|
11765
12009
|
},
|
|
@@ -12190,7 +12434,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12190
12434
|
if (sessionMetadata.securityContext && resolvedPath !== resolve(directory) && !resolvedPath.startsWith(resolve(directory) + "/")) {
|
|
12191
12435
|
throw new Error("Path outside working directory");
|
|
12192
12436
|
}
|
|
12193
|
-
await fs.mkdir(dirname(resolvedPath), { recursive: true });
|
|
12437
|
+
await fs.mkdir(dirname$1(resolvedPath), { recursive: true });
|
|
12194
12438
|
await fs.writeFile(resolvedPath, Buffer.from(content, "base64"));
|
|
12195
12439
|
},
|
|
12196
12440
|
onListDirectory: async (path) => {
|
|
@@ -12237,7 +12481,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12237
12481
|
if (sessionMetadata.securityContext && resolvedPath !== resolve(directory) && !resolvedPath.startsWith(resolve(directory) + "/")) {
|
|
12238
12482
|
throw new Error("Path outside working directory");
|
|
12239
12483
|
}
|
|
12240
|
-
const tree = await buildTree(resolvedPath, basename(resolvedPath), 0);
|
|
12484
|
+
const tree = await buildTree(resolvedPath, basename$1(resolvedPath), 0);
|
|
12241
12485
|
return { success: !!tree, tree };
|
|
12242
12486
|
}
|
|
12243
12487
|
},
|
|
@@ -12337,7 +12581,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12337
12581
|
try {
|
|
12338
12582
|
const hasTitle = !!sessionMetadata.summary?.text?.trim() || !!sessionMetadata.customTitle?.toString().trim();
|
|
12339
12583
|
if (!hasTitle) {
|
|
12340
|
-
const base = basename(directory.replace(/[/\\]+$/, "")) || "session";
|
|
12584
|
+
const base = basename$1(directory.replace(/[/\\]+$/, "")) || "session";
|
|
12341
12585
|
sessionMetadata = { ...sessionMetadata, summary: { text: base, updatedAt: Date.now() } };
|
|
12342
12586
|
sessionService.updateMetadata(sessionMetadata);
|
|
12343
12587
|
sessionService.pushMessage({ type: "summary", summary: base }, "session");
|
|
@@ -12464,7 +12708,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12464
12708
|
const wasInMemory = teardownTrackedSession(sessionId);
|
|
12465
12709
|
const markedArchived = markSessionAsArchived(sessionId);
|
|
12466
12710
|
if (loopDir && isLoopActiveForSession(loopDir, sessionId)) {
|
|
12467
|
-
deactivateLoop(loopDir);
|
|
12711
|
+
deactivateLoop(loopDir, sessionId);
|
|
12468
12712
|
logger.log(`Deactivated loop for archived session ${sessionId}`);
|
|
12469
12713
|
}
|
|
12470
12714
|
if (wasInMemory || markedArchived) {
|
|
@@ -12521,7 +12765,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12521
12765
|
teardownTrackedSession(sessionId);
|
|
12522
12766
|
deletePersistedSession(sessionId);
|
|
12523
12767
|
if (loopDir && isLoopActiveForSession(loopDir, sessionId)) {
|
|
12524
|
-
deactivateLoop(loopDir);
|
|
12768
|
+
deactivateLoop(loopDir, sessionId);
|
|
12525
12769
|
}
|
|
12526
12770
|
logger.log(`Session ${sessionId} deleted`);
|
|
12527
12771
|
return true;
|
|
@@ -12669,7 +12913,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12669
12913
|
const specs = loadExposedTunnels();
|
|
12670
12914
|
if (specs.length === 0) return;
|
|
12671
12915
|
logger.log(`[exposed-tunnels] Restoring ${specs.length} tunnel(s) from ${EXPOSED_TUNNELS_FILE}`);
|
|
12672
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
12916
|
+
const { FrpcTunnel } = await import('./frpc-BmpNco2u.mjs');
|
|
12673
12917
|
for (const spec of specs) {
|
|
12674
12918
|
if (tunnels.has(spec.name)) continue;
|
|
12675
12919
|
try {
|
|
@@ -12757,10 +13001,21 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12757
13001
|
for (const p of persistedSessions) {
|
|
12758
13002
|
if (sweptDirs.has(p.directory)) continue;
|
|
12759
13003
|
sweptDirs.add(p.directory);
|
|
12760
|
-
|
|
12761
|
-
|
|
13004
|
+
try {
|
|
13005
|
+
for (const ent of readdirSync$1(join$1(p.directory, ".svamp"), { withFileTypes: true })) {
|
|
13006
|
+
if (!ent.isDirectory() || knownSessionIds.has(ent.name)) continue;
|
|
13007
|
+
const owner = loopOwnerSession(p.directory, ent.name);
|
|
13008
|
+
if (owner && !knownSessionIds.has(owner)) {
|
|
13009
|
+
deactivateLoop(p.directory, ent.name);
|
|
13010
|
+
logger.log(`[loop] Deactivated stale loop-state for ${ent.name} in ${p.directory} (owner no longer known)`);
|
|
13011
|
+
}
|
|
13012
|
+
}
|
|
13013
|
+
} catch {
|
|
13014
|
+
}
|
|
13015
|
+
const legacyOwner = loopOwnerSession(p.directory);
|
|
13016
|
+
if (legacyOwner && !knownSessionIds.has(legacyOwner)) {
|
|
12762
13017
|
deactivateLoop(p.directory);
|
|
12763
|
-
logger.log(`[loop] Deactivated stale loop-state in ${p.directory} (owner session ${
|
|
13018
|
+
logger.log(`[loop] Deactivated stale legacy loop-state in ${p.directory} (owner session ${legacyOwner} no longer known)`);
|
|
12764
13019
|
}
|
|
12765
13020
|
}
|
|
12766
13021
|
}
|
|
@@ -13431,4 +13686,4 @@ var run = /*#__PURE__*/Object.freeze({
|
|
|
13431
13686
|
writeStopMarker: writeStopMarker
|
|
13432
13687
|
});
|
|
13433
13688
|
|
|
13434
|
-
export {
|
|
13689
|
+
export { api as $, READ_ONLY_TOOLS as A, loadMachineContext as B, buildMachineInstructions as C, machineToolsForRole as D, buildMachineTools as E, resolveModel as F, normalizeAllowedUser as G, loadSecurityContextConfig as H, resolveSecurityContext as I, buildSecurityContextFromFlags as J, mergeSecurityContexts as K, buildSessionShareUrl as L, computeOutboundHop as M, buildMachineShareUrl as N, describeMisconfiguration as O, buildMachineDeps as P, generateHookSettings as Q, RoutineStore as R, ServeAuth as S, projectInfo as T, DefaultTransport$1 as U, acpBackend as V, acpAgentConfig as W, codexMcpBackend as X, GeminiTransport$1 as Y, claudeAuth as Z, instanceConfig as _, createSessionStore as a, run as a0, 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 };
|