svamp-cli 0.2.101 → 0.2.103
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 +5 -0
- package/dist/{agentCommands-CAqLhLOH.mjs → agentCommands-oHt9FQJt.mjs} +14 -3
- package/dist/{auth-CYA0e4mT.mjs → auth-Dg0s5H5y.mjs} +2 -1
- package/dist/cli.mjs +51 -50
- package/dist/{commands-Bxn_4u7d.mjs → commands-BGE6zFa1.mjs} +2 -1
- package/dist/{commands-B2uNdsyR.mjs → commands-BXFukv2v.mjs} +3 -2
- package/dist/{commands-D-3h8H0C.mjs → commands-BvJ_Dl1l.mjs} +5 -5
- package/dist/{commands-DRQUzw4j.mjs → commands-C_B8GNUT.mjs} +2 -1
- package/dist/{commands-CdxEOPUt.mjs → commands-C_DlMpl7.mjs} +12 -2
- package/dist/{fleet-CNF84yJV.mjs → fleet-BlrT4zSC.mjs} +2 -1
- package/dist/{frpc-WVnBbyjf.mjs → frpc-Dn5pmk_f.mjs} +2 -1
- package/dist/{headlessCli-DcP8eawK.mjs → headlessCli-D7NXB73S.mjs} +3 -2
- package/dist/{httpServer-wwHHk1EM.mjs → httpServer-D9qLS8ed.mjs} +56 -0
- package/dist/index.mjs +2 -1
- package/dist/{package-DHxiXJ3N.mjs → package-fu3Jsw1q.mjs} +2 -2
- package/dist/{run-CsMTSngP.mjs → run-BmZjAEob.mjs} +256 -16
- package/dist/{run-C7WSV8zx.mjs → run-DTOSfKSH.mjs} +2 -1
- package/dist/{serveCommands-Can8WtLI.mjs → serveCommands-zFOjNs-0.mjs} +5 -5
- package/dist/{serveManager-DfETVSOb.mjs → serveManager-D6lGn8jh.mjs} +3 -2
- package/dist/{sideband-C10Ni7p_.mjs → sideband-CNyGVxRy.mjs} +2 -1
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { E as resolveModel, N as describeMisconfiguration, O as buildMachineDeps } from './run-
|
|
2
|
-
import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-
|
|
1
|
+
import { E as resolveModel, N as describeMisconfiguration, O as buildMachineDeps } from './run-BmZjAEob.mjs';
|
|
2
|
+
import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-CNyGVxRy.mjs';
|
|
3
3
|
import { WebSocket } from 'ws';
|
|
4
4
|
import { execSync, spawn } from 'child_process';
|
|
5
5
|
import 'os';
|
|
@@ -14,6 +14,7 @@ import 'util';
|
|
|
14
14
|
import 'node:crypto';
|
|
15
15
|
import 'node:path';
|
|
16
16
|
import 'node:os';
|
|
17
|
+
import 'node:events';
|
|
17
18
|
import '@agentclientprotocol/sdk';
|
|
18
19
|
import '@modelcontextprotocol/sdk/client/index.js';
|
|
19
20
|
import '@modelcontextprotocol/sdk/client/stdio.js';
|
|
@@ -52,6 +52,62 @@ function createChannelHttpServer(deps) {
|
|
|
52
52
|
res.writeHead(200, { "content-type": "text/markdown" }).end(d.skill?.body || "");
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
|
+
const keyOf = () => (req.headers.authorization || "").replace(/^Bearer\s+/i, "") || u.searchParams.get("key") || void 0;
|
|
56
|
+
m = u.pathname.match(/^\/channel\/([\w.-]+)\/receive$/);
|
|
57
|
+
if (m) {
|
|
58
|
+
const rpc2 = await findOwner(deps, m[1]);
|
|
59
|
+
if (!rpc2?.channelReceive) {
|
|
60
|
+
json(404, { error: "channel not found" });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const out = await rpc2.channelReceive({
|
|
64
|
+
channel: m[1],
|
|
65
|
+
key: keyOf(),
|
|
66
|
+
from: u.searchParams.get("from") || void 0,
|
|
67
|
+
cursor: Number(u.searchParams.get("cursor") || 0),
|
|
68
|
+
correlationId: u.searchParams.get("correlationId") || void 0,
|
|
69
|
+
wait: u.searchParams.get("wait") != null ? Number(u.searchParams.get("wait")) : void 0
|
|
70
|
+
});
|
|
71
|
+
json(out?.error ? out.error === "channel not found" ? 404 : 401 : 200, out);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
m = u.pathname.match(/^\/channel\/([\w.-]+)\/events$/);
|
|
75
|
+
if (m) {
|
|
76
|
+
const rpc2 = await findOwner(deps, m[1]);
|
|
77
|
+
if (!rpc2?.channelReceive) {
|
|
78
|
+
res.writeHead(404).end("not found");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const key = keyOf();
|
|
82
|
+
const from = u.searchParams.get("from") || void 0;
|
|
83
|
+
let cursor = Number(u.searchParams.get("cursor") || 0);
|
|
84
|
+
res.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive", "x-accel-buffering": "no" });
|
|
85
|
+
res.write(": connected\n\n");
|
|
86
|
+
let closed = false;
|
|
87
|
+
req.on("close", () => {
|
|
88
|
+
closed = true;
|
|
89
|
+
});
|
|
90
|
+
while (!closed) {
|
|
91
|
+
const out = await rpc2.channelReceive({ channel: m[1], key, from, cursor, wait: 25 });
|
|
92
|
+
if (out?.error) {
|
|
93
|
+
res.write(`event: error
|
|
94
|
+
data: ${JSON.stringify({ error: out.error })}
|
|
95
|
+
|
|
96
|
+
`);
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
for (const reply of out.replies || []) res.write(`data: ${JSON.stringify(reply)}
|
|
100
|
+
|
|
101
|
+
`);
|
|
102
|
+
cursor = out.cursor ?? cursor;
|
|
103
|
+
if (!(out.replies || []).length) res.write(": keepalive\n\n");
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
res.end();
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
55
111
|
m = u.pathname.match(/^\/channel\/([\w.-]+)$/);
|
|
56
112
|
if (!m) {
|
|
57
113
|
json(404, { error: "not found" });
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-
|
|
1
|
+
export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-BmZjAEob.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -12,6 +12,7 @@ import 'util';
|
|
|
12
12
|
import 'node:crypto';
|
|
13
13
|
import 'node:path';
|
|
14
14
|
import 'node:os';
|
|
15
|
+
import 'node:events';
|
|
15
16
|
import '@agentclientprotocol/sdk';
|
|
16
17
|
import '@modelcontextprotocol/sdk/client/index.js';
|
|
17
18
|
import '@modelcontextprotocol/sdk/client/stdio.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var name = "svamp-cli";
|
|
2
|
-
var version = "0.2.
|
|
2
|
+
var version = "0.2.103";
|
|
3
3
|
var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
|
|
4
4
|
var author = "Amun AI AB";
|
|
5
5
|
var license = "SEE LICENSE IN LICENSE";
|
|
@@ -19,7 +19,7 @@ var exports$1 = {
|
|
|
19
19
|
var scripts = {
|
|
20
20
|
build: "rm -rf dist bin/skills && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/loop bin/skills/loop && tsc --noEmit && pkgroll",
|
|
21
21
|
typecheck: "tsc --noEmit",
|
|
22
|
-
test: "npx tsx test/test-context-window.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-loop-activation.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-routine.mjs && npx tsx test/test-routine-rpc.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs",
|
|
22
|
+
test: "npx tsx test/test-context-window.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-loop-activation.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-routine.mjs && npx tsx test/test-routine-rpc.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs",
|
|
23
23
|
"test:hypha": "node --no-warnings test/test-hypha-service.mjs",
|
|
24
24
|
dev: "tsx src/cli.ts",
|
|
25
25
|
"dev:daemon": "tsx src/cli.ts daemon start-sync",
|
|
@@ -11,6 +11,7 @@ import { promisify } from 'util';
|
|
|
11
11
|
import { randomBytes, randomUUID, createHash } from 'node:crypto';
|
|
12
12
|
import { join as join$1 } from 'node:path';
|
|
13
13
|
import os, { homedir, platform } from 'node:os';
|
|
14
|
+
import { EventEmitter } from 'node:events';
|
|
14
15
|
import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk';
|
|
15
16
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
16
17
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
@@ -2165,7 +2166,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2165
2166
|
const tunnels = handlers.tunnels;
|
|
2166
2167
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
2167
2168
|
if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
|
|
2168
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
2169
|
+
const { FrpcTunnel } = await import('./frpc-Dn5pmk_f.mjs');
|
|
2169
2170
|
const tunnel = new FrpcTunnel({
|
|
2170
2171
|
name: params.name,
|
|
2171
2172
|
ports: params.ports,
|
|
@@ -2426,7 +2427,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
2426
2427
|
}
|
|
2427
2428
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
2428
2429
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
2429
|
-
const { toolsForRole } = await import('./sideband-
|
|
2430
|
+
const { toolsForRole } = await import('./sideband-CNyGVxRy.mjs');
|
|
2430
2431
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
2431
2432
|
return fmt(r2);
|
|
2432
2433
|
}
|
|
@@ -2502,7 +2503,15 @@ ${d?.error || "not found"}`;
|
|
|
2502
2503
|
trackInbound();
|
|
2503
2504
|
const rpc = await findChannelOwner(kwargs.channel);
|
|
2504
2505
|
if (!rpc?.channelSend) return { error: "channel not found" };
|
|
2505
|
-
return rpc.channelSend({ channel: kwargs.channel, message: kwargs.message, from: kwargs.from, key: kwargs.key }, context);
|
|
2506
|
+
return rpc.channelSend({ channel: kwargs.channel, message: kwargs.message, from: kwargs.from, key: kwargs.key, reply_to: kwargs.reply_to }, context);
|
|
2507
|
+
},
|
|
2508
|
+
// Async reply retrieval for queue-mode channels — long-poll the channel outbox
|
|
2509
|
+
// for replies addressed to the caller. Channel-identity auth (key/from).
|
|
2510
|
+
receive: async (kwargs = {}, context) => {
|
|
2511
|
+
trackInbound();
|
|
2512
|
+
const rpc = await findChannelOwner(kwargs.channel);
|
|
2513
|
+
if (!rpc?.channelReceive) return { error: "channel not found" };
|
|
2514
|
+
return rpc.channelReceive({ channel: kwargs.channel, key: kwargs.key, from: kwargs.from, cursor: kwargs.cursor, correlationId: kwargs.correlationId, wait: kwargs.wait }, context);
|
|
2506
2515
|
}
|
|
2507
2516
|
},
|
|
2508
2517
|
{ overwrite: true }
|
|
@@ -2960,10 +2969,27 @@ function generateSkillBody(channel, ctx) {
|
|
|
2960
2969
|
const sendUrl = `${gw}/send`;
|
|
2961
2970
|
const key = ctx?.key || "<your-key>";
|
|
2962
2971
|
const isAgent = channel.action?.kind === "agent";
|
|
2972
|
+
const isQueue = channel.reply?.mode === "queue";
|
|
2973
|
+
const recvUrl = `${gw}/receive`;
|
|
2963
2974
|
const hyphaOpen = (channel.identity?.hypha_allow || []).length > 0;
|
|
2964
2975
|
const name = channel.skill?.name || channel.name;
|
|
2965
2976
|
const desc = channel.skill?.description || channel.description || `Send a message to the "${channel.name}" channel.`;
|
|
2966
|
-
const replyNote = isAgent ? `This is a **WISE Agent** channel: \`send()\` runs a fast assistant against the session's tools/skills and returns its answer synchronously in the result \`reply\`.` : `Delivery is fire-and-forget \u2014 the message lands in the agent's inbox, tagged with your verified identity (\`from\`).`;
|
|
2977
|
+
const replyNote = isAgent ? `This is a **WISE Agent** channel: \`send()\` runs a fast assistant against the session's tools/skills and returns its answer synchronously in the result \`reply\`.` : isQueue ? `This is an **async** channel: \`send()\` returns a \`correlationId\` and the agent replies later \u2014 poll \`receive()\` (or GET /receive \xB7 /events) for replies addressed to you.` : `Delivery is fire-and-forget \u2014 the message lands in the agent's inbox, tagged with your verified identity (\`from\`).`;
|
|
2978
|
+
const queueSection = isQueue ? `
|
|
2979
|
+
|
|
2980
|
+
## Getting the reply (async)
|
|
2981
|
+
\`send()\` returns \`{ correlationId }\`. The agent answers later; retrieve replies addressed
|
|
2982
|
+
to you by **long-polling** \`receive\` with a cursor you advance each call:
|
|
2983
|
+
\`\`\`js
|
|
2984
|
+
const { correlationId } = await get_service("${svc}").send({ channel: "${channel.id}", message: "\u2026", from: "your-name" });
|
|
2985
|
+
let cursor = 0;
|
|
2986
|
+
while (true) {
|
|
2987
|
+
const r = await get_service("${svc}").receive({ channel: "${channel.id}", key: "${key}", cursor, wait: 25 });
|
|
2988
|
+
cursor = r.cursor;
|
|
2989
|
+
for (const reply of r.replies) if (reply.correlationId === correlationId) return reply.body;
|
|
2990
|
+
}
|
|
2991
|
+
\`\`\`
|
|
2992
|
+
**HTTP:** \`POST ${recvUrl}\` with \`{"kwargs": {"channel": "${channel.id}", "key": "${key}", "cursor": 0, "wait": 25}}\` (long-poll), or stream \`GET <channel-http>/channel/${channel.id}/events?key=${key}\` (SSE).` : "";
|
|
2967
2993
|
const rpcLine = hyphaOpen ? `**Hypha RPC** \u2014 preferred. Your verified Hypha identity is accepted, no key needed:` : `**Hypha RPC** \u2014 verified identity:`;
|
|
2968
2994
|
return `---
|
|
2969
2995
|
name: ${name}
|
|
@@ -2991,7 +3017,139 @@ POST ${sendUrl}
|
|
|
2991
3017
|
Content-Type: application/json
|
|
2992
3018
|
|
|
2993
3019
|
{"kwargs": {"channel": "${channel.id}", "message": "your message here", "from": "your-name", "key": "${key}"}}
|
|
2994
|
-
|
|
3020
|
+
\`\`\`${queueSection}`;
|
|
3021
|
+
}
|
|
3022
|
+
|
|
3023
|
+
const MAX_PER_CHANNEL = 200;
|
|
3024
|
+
const TTL_MS = 60 * 60 * 1e3;
|
|
3025
|
+
class ChannelOutbox {
|
|
3026
|
+
file;
|
|
3027
|
+
byChannel = /* @__PURE__ */ new Map();
|
|
3028
|
+
seqByChannel = /* @__PURE__ */ new Map();
|
|
3029
|
+
emitter = new EventEmitter();
|
|
3030
|
+
constructor(projectDir) {
|
|
3031
|
+
const dir = join$1(projectDir, ".svamp", "channels");
|
|
3032
|
+
this.file = join$1(dir, "_outbox.jsonl");
|
|
3033
|
+
this.emitter.setMaxListeners(0);
|
|
3034
|
+
try {
|
|
3035
|
+
mkdirSync$1(dir, { recursive: true });
|
|
3036
|
+
} catch {
|
|
3037
|
+
}
|
|
3038
|
+
this._load();
|
|
3039
|
+
}
|
|
3040
|
+
_load() {
|
|
3041
|
+
if (!existsSync(this.file)) return;
|
|
3042
|
+
const cutoff = Date.now() - TTL_MS;
|
|
3043
|
+
try {
|
|
3044
|
+
for (const line of readFileSync(this.file, "utf8").split("\n")) {
|
|
3045
|
+
if (!line.trim()) continue;
|
|
3046
|
+
let rec = null;
|
|
3047
|
+
try {
|
|
3048
|
+
rec = JSON.parse(line);
|
|
3049
|
+
} catch {
|
|
3050
|
+
continue;
|
|
3051
|
+
}
|
|
3052
|
+
if (!rec || rec.ts < cutoff) continue;
|
|
3053
|
+
const { channelId, ...reply } = rec;
|
|
3054
|
+
const arr = this.byChannel.get(channelId) || [];
|
|
3055
|
+
arr.push(reply);
|
|
3056
|
+
this.byChannel.set(channelId, arr);
|
|
3057
|
+
this.seqByChannel.set(channelId, Math.max(this.seqByChannel.get(channelId) || 0, reply.seq));
|
|
3058
|
+
}
|
|
3059
|
+
for (const [ch, arr] of this.byChannel) if (arr.length > MAX_PER_CHANNEL) this.byChannel.set(ch, arr.slice(-MAX_PER_CHANNEL));
|
|
3060
|
+
} catch {
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
_evict(channelId) {
|
|
3064
|
+
const cutoff = Date.now() - TTL_MS;
|
|
3065
|
+
let arr = this.byChannel.get(channelId);
|
|
3066
|
+
if (!arr) return;
|
|
3067
|
+
if (arr.some((r) => r.ts < cutoff)) arr = arr.filter((r) => r.ts >= cutoff);
|
|
3068
|
+
if (arr.length > MAX_PER_CHANNEL) arr = arr.slice(-MAX_PER_CHANNEL);
|
|
3069
|
+
this.byChannel.set(channelId, arr);
|
|
3070
|
+
}
|
|
3071
|
+
/** Append a reply addressed to `to`. Assigns seq + ts, persists, and wakes waiters. */
|
|
3072
|
+
append(channelId, r) {
|
|
3073
|
+
const seq = (this.seqByChannel.get(channelId) || 0) + 1;
|
|
3074
|
+
this.seqByChannel.set(channelId, seq);
|
|
3075
|
+
const reply = { seq, ts: Date.now(), to: r.to, body: r.body, ...r.correlationId ? { correlationId: r.correlationId } : {} };
|
|
3076
|
+
const arr = this.byChannel.get(channelId) || [];
|
|
3077
|
+
arr.push(reply);
|
|
3078
|
+
this.byChannel.set(channelId, arr);
|
|
3079
|
+
this._evict(channelId);
|
|
3080
|
+
try {
|
|
3081
|
+
appendFileSync(this.file, JSON.stringify({ channelId, ...reply }) + "\n");
|
|
3082
|
+
} catch {
|
|
3083
|
+
try {
|
|
3084
|
+
mkdirSync$1(join$1(this.file, ".."), { recursive: true });
|
|
3085
|
+
appendFileSync(this.file, JSON.stringify({ channelId, ...reply }) + "\n");
|
|
3086
|
+
} catch {
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
this.emitter.emit(channelId, reply);
|
|
3090
|
+
return reply;
|
|
3091
|
+
}
|
|
3092
|
+
/** Replies for `to` on `channelId` with seq > cursor (optionally one correlationId). */
|
|
3093
|
+
since(channelId, cursor, to, correlationId) {
|
|
3094
|
+
this._evict(channelId);
|
|
3095
|
+
return (this.byChannel.get(channelId) || []).filter((r) => r.seq > cursor && r.to === to && (!correlationId || r.correlationId === correlationId));
|
|
3096
|
+
}
|
|
3097
|
+
/** Highest seq on a channel (the cursor a caller gets back). */
|
|
3098
|
+
cursor(channelId) {
|
|
3099
|
+
return this.seqByChannel.get(channelId) || 0;
|
|
3100
|
+
}
|
|
3101
|
+
/**
|
|
3102
|
+
* Long-poll: resolve immediately if there are replies after `cursor`, else wait for
|
|
3103
|
+
* the next append (filtered to this channel + `to`) or `timeoutMs`, then return.
|
|
3104
|
+
*/
|
|
3105
|
+
wait(channelId, cursor, to, timeoutMs, correlationId) {
|
|
3106
|
+
const ready = this.since(channelId, cursor, to, correlationId);
|
|
3107
|
+
if (ready.length) return Promise.resolve({ replies: ready, cursor: this.cursor(channelId) });
|
|
3108
|
+
return new Promise((resolve) => {
|
|
3109
|
+
const onReply = (r) => {
|
|
3110
|
+
if (r.to !== to || correlationId && r.correlationId !== correlationId) return;
|
|
3111
|
+
cleanup();
|
|
3112
|
+
resolve({ replies: this.since(channelId, cursor, to, correlationId), cursor: this.cursor(channelId) });
|
|
3113
|
+
};
|
|
3114
|
+
const timer = setTimeout(() => {
|
|
3115
|
+
cleanup();
|
|
3116
|
+
resolve({ replies: [], cursor: this.cursor(channelId) });
|
|
3117
|
+
}, Math.max(0, timeoutMs));
|
|
3118
|
+
const cleanup = () => {
|
|
3119
|
+
clearTimeout(timer);
|
|
3120
|
+
this.emitter.off(channelId, onReply);
|
|
3121
|
+
};
|
|
3122
|
+
this.emitter.on(channelId, onReply);
|
|
3123
|
+
});
|
|
3124
|
+
}
|
|
3125
|
+
/** Push subscription for SSE: calls onReply for each new reply addressed to `to`. */
|
|
3126
|
+
subscribe(channelId, to, onReply) {
|
|
3127
|
+
const handler = (r) => {
|
|
3128
|
+
if (r.to === to) onReply(r);
|
|
3129
|
+
};
|
|
3130
|
+
this.emitter.on(channelId, handler);
|
|
3131
|
+
return () => this.emitter.off(channelId, handler);
|
|
3132
|
+
}
|
|
3133
|
+
/** Drop a channel's outbox (on channel delete) — best-effort rewrite of the log. */
|
|
3134
|
+
purge(channelId) {
|
|
3135
|
+
this.byChannel.delete(channelId);
|
|
3136
|
+
this.seqByChannel.delete(channelId);
|
|
3137
|
+
if (!existsSync(this.file)) return;
|
|
3138
|
+
try {
|
|
3139
|
+
const kept = readFileSync(this.file, "utf8").split("\n").filter((l) => {
|
|
3140
|
+
if (!l.trim()) return false;
|
|
3141
|
+
try {
|
|
3142
|
+
return JSON.parse(l).channelId !== channelId;
|
|
3143
|
+
} catch {
|
|
3144
|
+
return false;
|
|
3145
|
+
}
|
|
3146
|
+
});
|
|
3147
|
+
const tmp = this.file + ".tmp";
|
|
3148
|
+
writeFileSync$1(tmp, kept.join("\n") + (kept.length ? "\n" : ""));
|
|
3149
|
+
renameSync$1(tmp, this.file);
|
|
3150
|
+
} catch {
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
2995
3153
|
}
|
|
2996
3154
|
|
|
2997
3155
|
function resolveSender(channel, input = {}) {
|
|
@@ -3269,6 +3427,7 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
3269
3427
|
callbacks.onMetadataUpdate?.(metadata);
|
|
3270
3428
|
};
|
|
3271
3429
|
const channelStore = new ChannelStore(initialMetadata.path);
|
|
3430
|
+
const channelOutbox = new ChannelOutbox(initialMetadata.path);
|
|
3272
3431
|
const cfg = server?.config || {};
|
|
3273
3432
|
const channelsServiceId = cfg.workspace && cfg.client_id ? `${cfg.workspace}/${cfg.client_id}:channels` : void 0;
|
|
3274
3433
|
const channelsBaseUrl = cfg.public_base_url || process.env.HYPHA_SERVER_URL || "https://hypha.aicell.io";
|
|
@@ -3476,6 +3635,7 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
3476
3635
|
removeChannel: async (id, context) => {
|
|
3477
3636
|
authorizeRequest(context, metadata.sharing, "admin");
|
|
3478
3637
|
const ok = channelStore.remove(id);
|
|
3638
|
+
channelOutbox.purge(id);
|
|
3479
3639
|
syncChannelsToMetadata();
|
|
3480
3640
|
return { success: ok };
|
|
3481
3641
|
},
|
|
@@ -3546,6 +3706,8 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
3546
3706
|
return { ok: result.status === "completed", call_id: callId, status: result.status, reply: result.reply, tool_calls: result.toolCalls, error: result.error };
|
|
3547
3707
|
}
|
|
3548
3708
|
if (c.action?.kind === "loop") return { error: "loop channels are served by the channel server, not channelSend" };
|
|
3709
|
+
const queue = c.reply?.mode === "queue";
|
|
3710
|
+
const replySession = queue ? params.reply_to?.session : void 0;
|
|
3549
3711
|
const inboxMsg = {
|
|
3550
3712
|
messageId: callId,
|
|
3551
3713
|
body: xmlEscape(String(params.message ?? "").slice(0, 16 * 1024)),
|
|
@@ -3554,17 +3716,56 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
3554
3716
|
from: r.sender.name,
|
|
3555
3717
|
verified: r.sender.verified,
|
|
3556
3718
|
channel: c.name,
|
|
3557
|
-
subject: c.name
|
|
3719
|
+
subject: c.name,
|
|
3720
|
+
...replySession ? { fromSession: replySession, threadId: callId } : {},
|
|
3721
|
+
...queue && !replySession ? { channelId: c.id, correlationId: callId } : {}
|
|
3558
3722
|
};
|
|
3559
3723
|
await rpcHandlers.sendInboxMessage(inboxMsg, ownerCtx);
|
|
3560
3724
|
channelStore.recordCall(c.id, { sender: r.sender.name, verified: r.sender.verified, callId, outcome: "delivered" });
|
|
3561
3725
|
syncChannelsToMetadata();
|
|
3562
|
-
return { ok: true, call_id: callId, status: "accepted" };
|
|
3726
|
+
return { ok: true, call_id: callId, status: queue ? "queued" : "accepted", ...queue ? { correlationId: callId } : {} };
|
|
3563
3727
|
} catch (e) {
|
|
3564
3728
|
channelStore.recordCall(c.id, { sender: r.sender.name, verified: r.sender.verified, callId, outcome: "error" });
|
|
3565
3729
|
return { ok: false, call_id: callId, status: "error", error: e?.message || String(e) };
|
|
3566
3730
|
}
|
|
3567
3731
|
},
|
|
3732
|
+
// Agent/owner answers a queued (async) channel message → channel outbox, addressed
|
|
3733
|
+
// to the original external caller. Routed here by `inbox reply` for channel-origin
|
|
3734
|
+
// inbox messages (which carry channelId + correlationId + from). Owner-gated since
|
|
3735
|
+
// it's the session speaking on its own behalf.
|
|
3736
|
+
channelReply: async (params, context) => {
|
|
3737
|
+
authorizeRequest(context, metadata.sharing, "interact");
|
|
3738
|
+
const c = channelStore.get(params.channel);
|
|
3739
|
+
if (!c) return { ok: false, error: "channel not found" };
|
|
3740
|
+
if (!params.to || !params.body) return { ok: false, error: "to and body are required" };
|
|
3741
|
+
const reply = channelOutbox.append(c.id, {
|
|
3742
|
+
correlationId: params.correlationId,
|
|
3743
|
+
to: String(params.to),
|
|
3744
|
+
body: String(params.body).slice(0, 16 * 1024)
|
|
3745
|
+
});
|
|
3746
|
+
channelStore.recordCall(c.id, { sender: String(params.to), verified: true, callId: params.correlationId || `seq_${reply.seq}`, outcome: "replied" });
|
|
3747
|
+
return { ok: true, seq: reply.seq };
|
|
3748
|
+
},
|
|
3749
|
+
// External caller retrieves async replies addressed to it. Channel-identity auth
|
|
3750
|
+
// (key/from) — NOT session sharing. Long-polls up to `wait` seconds for new replies
|
|
3751
|
+
// after `cursor`; the caller advances `cursor` each call (ack-by-cursor).
|
|
3752
|
+
channelReceive: async (params, context) => {
|
|
3753
|
+
const c = channelStore.get(params.channel);
|
|
3754
|
+
if (!c || c.enabled === false) return { error: "channel not found" };
|
|
3755
|
+
const u = context?.user;
|
|
3756
|
+
const r = resolveSender(c, {
|
|
3757
|
+
key: params.key,
|
|
3758
|
+
from: params.from,
|
|
3759
|
+
hyphaUser: u && u.is_anonymous !== true ? u.email || u.id : void 0,
|
|
3760
|
+
hyphaAnonymous: u?.is_anonymous === true,
|
|
3761
|
+
hyphaWorkspace: u?.scope?.current_workspace
|
|
3762
|
+
});
|
|
3763
|
+
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
3764
|
+
const cursor = Math.max(0, Number(params.cursor) || 0);
|
|
3765
|
+
const waitMs = Math.min(Math.max(0, Number(params.wait ?? 25) * 1e3), 6e4);
|
|
3766
|
+
const out = await channelOutbox.wait(c.id, cursor, r.sender.name, waitMs, params.correlationId);
|
|
3767
|
+
return { ok: true, replies: out.replies, cursor: out.cursor };
|
|
3768
|
+
},
|
|
3568
3769
|
// ── Agent State ──
|
|
3569
3770
|
getAgentState: async (context) => {
|
|
3570
3771
|
authorizeRequest(context, metadata.sharing, "view");
|
|
@@ -3909,6 +4110,9 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
3909
4110
|
const child = spawn("claude", args, {
|
|
3910
4111
|
cwd,
|
|
3911
4112
|
timeout: 6e4,
|
|
4113
|
+
// Ignore stdin: --print otherwise waits ~3s for piped input ("no stdin
|
|
4114
|
+
// data received in 3s, proceeding without it") — pure latency per /btw.
|
|
4115
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3912
4116
|
env: { ...process.env, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" }
|
|
3913
4117
|
});
|
|
3914
4118
|
let stdout = "";
|
|
@@ -9342,6 +9546,17 @@ function isLoopActive(directory) {
|
|
|
9342
9546
|
const s = readLoopState(directory);
|
|
9343
9547
|
return !!s && s.active !== false && s.phase !== "done" && s.phase !== "gave_up" && s.phase !== "cancelled";
|
|
9344
9548
|
}
|
|
9549
|
+
function loopOwnerSession(directory) {
|
|
9550
|
+
const s = readLoopState(directory);
|
|
9551
|
+
if (!s || s.active === false || s.phase === "done" || s.phase === "gave_up" || s.phase === "cancelled") return null;
|
|
9552
|
+
return typeof s.session_id === "string" ? s.session_id : null;
|
|
9553
|
+
}
|
|
9554
|
+
function isLoopActiveForSession(directory, sessionId) {
|
|
9555
|
+
const s = readLoopState(directory);
|
|
9556
|
+
if (!s || s.active === false || s.phase === "done" || s.phase === "gave_up" || s.phase === "cancelled") return false;
|
|
9557
|
+
if (typeof s.session_id !== "string") return true;
|
|
9558
|
+
return s.session_id === sessionId;
|
|
9559
|
+
}
|
|
9345
9560
|
function resolveLoopInit() {
|
|
9346
9561
|
const candidates = [
|
|
9347
9562
|
join(CLAUDE_SKILLS_DIR, "loop", "bin", "loop-init.mjs"),
|
|
@@ -9359,6 +9574,7 @@ function initLoop(directory, cfg) {
|
|
|
9359
9574
|
if (typeof cfg.maxIterations === "number") args.push("--max", String(cfg.maxIterations));
|
|
9360
9575
|
args.push("--evaluator", cfg.evaluator === false ? "off" : "on");
|
|
9361
9576
|
if (cfg.model) args.push("--model", cfg.model);
|
|
9577
|
+
if (cfg.sessionId) args.push("--session", cfg.sessionId);
|
|
9362
9578
|
const res = spawnSync(process.execPath, args, { encoding: "utf-8", timeout: 3e4 });
|
|
9363
9579
|
return res.status === 0;
|
|
9364
9580
|
}
|
|
@@ -9460,7 +9676,8 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
9460
9676
|
criteria: typeof lp.criteria === "string" && lp.criteria.trim() ? lp.criteria.trim() : void 0,
|
|
9461
9677
|
oracle,
|
|
9462
9678
|
maxIterations,
|
|
9463
|
-
evaluator
|
|
9679
|
+
evaluator,
|
|
9680
|
+
sessionId
|
|
9464
9681
|
});
|
|
9465
9682
|
if (ok) {
|
|
9466
9683
|
const existingQueue = getMetadata().messageQueue || [];
|
|
@@ -9922,7 +10139,7 @@ async function startDaemon(options) {
|
|
|
9922
10139
|
const list = loadExposedTunnels().filter((t) => t.name !== name);
|
|
9923
10140
|
saveExposedTunnels(list);
|
|
9924
10141
|
}
|
|
9925
|
-
const { ServeManager } = await import('./serveManager-
|
|
10142
|
+
const { ServeManager } = await import('./serveManager-D6lGn8jh.mjs');
|
|
9926
10143
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
9927
10144
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
9928
10145
|
});
|
|
@@ -10034,7 +10251,7 @@ async function startDaemon(options) {
|
|
|
10034
10251
|
}
|
|
10035
10252
|
}, shouldAutoAllow2 = function(toolName, toolInput) {
|
|
10036
10253
|
if (toolName === "AskUserQuestion") {
|
|
10037
|
-
return
|
|
10254
|
+
return isLoopActiveForSession(directory, sessionId);
|
|
10038
10255
|
}
|
|
10039
10256
|
if (toolName === "Bash") {
|
|
10040
10257
|
const inputObj = toolInput;
|
|
@@ -10047,7 +10264,7 @@ async function startDaemon(options) {
|
|
|
10047
10264
|
} else if (allowedTools.has(toolName)) {
|
|
10048
10265
|
return true;
|
|
10049
10266
|
}
|
|
10050
|
-
if (
|
|
10267
|
+
if (isLoopActiveForSession(directory, sessionId)) return true;
|
|
10051
10268
|
if (currentPermissionMode === "bypassPermissions" || currentPermissionMode === "yolo") return true;
|
|
10052
10269
|
if ((currentPermissionMode === "acceptEdits" || currentPermissionMode === "safe-yolo") && EDIT_TOOLS.has(toolName)) return true;
|
|
10053
10270
|
return false;
|
|
@@ -10412,6 +10629,12 @@ async function startDaemon(options) {
|
|
|
10412
10629
|
logger.log(`[Session ${sessionId}] Permission request: ${requestId} (corr=${correlationId}) tool=${toolName}`);
|
|
10413
10630
|
if (shouldAutoAllow2(toolName, toolInput)) {
|
|
10414
10631
|
logger.log(`[Session ${sessionId}] Auto-allowing ${toolName} (mode=${currentPermissionMode})`);
|
|
10632
|
+
if (toolName === "AskUserQuestion") {
|
|
10633
|
+
sessionService.pushMessage(
|
|
10634
|
+
{ type: "message", message: "\u{1F501} Question auto-dismissed \u2014 a loop is running, so there is no human to prompt. The agent will proceed as if the questions were skipped.", level: "warning" },
|
|
10635
|
+
"event"
|
|
10636
|
+
);
|
|
10637
|
+
}
|
|
10415
10638
|
if (claudeProcess && !claudeProcess.killed && claudeProcess.stdin) {
|
|
10416
10639
|
const controlResponse = JSON.stringify({
|
|
10417
10640
|
type: "control_response",
|
|
@@ -10582,6 +10805,10 @@ async function startDaemon(options) {
|
|
|
10582
10805
|
}
|
|
10583
10806
|
if (msg.session_id) {
|
|
10584
10807
|
claudeResumeId = msg.session_id;
|
|
10808
|
+
if (sessionMetadata.claudeSessionId !== msg.session_id) {
|
|
10809
|
+
sessionMetadata = { ...sessionMetadata, claudeSessionId: msg.session_id };
|
|
10810
|
+
sessionService.updateMetadata(sessionMetadata);
|
|
10811
|
+
}
|
|
10585
10812
|
}
|
|
10586
10813
|
signalProcessing(false);
|
|
10587
10814
|
sessionWasProcessing = false;
|
|
@@ -11485,7 +11712,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
11485
11712
|
} else if (allowedTools.has(toolName)) {
|
|
11486
11713
|
return true;
|
|
11487
11714
|
}
|
|
11488
|
-
if (
|
|
11715
|
+
if (isLoopActiveForSession(directory, sessionId)) return true;
|
|
11489
11716
|
if (currentPermissionMode === "bypassPermissions" || currentPermissionMode === "yolo") return true;
|
|
11490
11717
|
if ((currentPermissionMode === "acceptEdits" || currentPermissionMode === "safe-yolo") && EDIT_TOOLS.has(toolName)) return true;
|
|
11491
11718
|
return false;
|
|
@@ -12212,7 +12439,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12212
12439
|
const channelHttpPort = Number(process.env.SVAMP_CHANNEL_HTTP_PORT) || 0;
|
|
12213
12440
|
if (channelHttpPort > 0) {
|
|
12214
12441
|
try {
|
|
12215
|
-
const { createChannelHttpServer } = await import('./httpServer-
|
|
12442
|
+
const { createChannelHttpServer } = await import('./httpServer-D9qLS8ed.mjs');
|
|
12216
12443
|
const channelHttpServer = createChannelHttpServer({
|
|
12217
12444
|
getSessionIds: () => {
|
|
12218
12445
|
const ids = [];
|
|
@@ -12233,7 +12460,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12233
12460
|
const specs = loadExposedTunnels();
|
|
12234
12461
|
if (specs.length === 0) return;
|
|
12235
12462
|
logger.log(`[exposed-tunnels] Restoring ${specs.length} tunnel(s) from ${EXPOSED_TUNNELS_FILE}`);
|
|
12236
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
12463
|
+
const { FrpcTunnel } = await import('./frpc-Dn5pmk_f.mjs');
|
|
12237
12464
|
for (const spec of specs) {
|
|
12238
12465
|
if (tunnels.has(spec.name)) continue;
|
|
12239
12466
|
try {
|
|
@@ -12315,6 +12542,19 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12315
12542
|
}
|
|
12316
12543
|
const sessionsToAutoContinue = [];
|
|
12317
12544
|
const sessionsToLoopResume = [];
|
|
12545
|
+
{
|
|
12546
|
+
const knownSessionIds = new Set(persistedSessions.map((p) => p.sessionId));
|
|
12547
|
+
const sweptDirs = /* @__PURE__ */ new Set();
|
|
12548
|
+
for (const p of persistedSessions) {
|
|
12549
|
+
if (sweptDirs.has(p.directory)) continue;
|
|
12550
|
+
sweptDirs.add(p.directory);
|
|
12551
|
+
const owner = loopOwnerSession(p.directory);
|
|
12552
|
+
if (owner && !knownSessionIds.has(owner)) {
|
|
12553
|
+
deactivateLoop(p.directory);
|
|
12554
|
+
logger.log(`[loop] Deactivated stale loop-state in ${p.directory} (owner session ${owner} no longer known)`);
|
|
12555
|
+
}
|
|
12556
|
+
}
|
|
12557
|
+
}
|
|
12318
12558
|
if (persistedSessions.length > 0) {
|
|
12319
12559
|
logger.log(`Restoring ${persistedSessions.length} persisted session(s)...`);
|
|
12320
12560
|
for (const persisted of persistedSessions) {
|
|
@@ -12359,7 +12599,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12359
12599
|
if (persisted.wasProcessing && persisted.claudeResumeId && !isOrphaned) {
|
|
12360
12600
|
sessionsToAutoContinue.push(persisted.sessionId);
|
|
12361
12601
|
}
|
|
12362
|
-
if (!isOrphaned && !persisted.wasProcessing &&
|
|
12602
|
+
if (!isOrphaned && !persisted.wasProcessing && isLoopActiveForSession(persisted.directory, persisted.sessionId)) {
|
|
12363
12603
|
sessionsToLoopResume.push({ sessionId: persisted.sessionId, directory: persisted.directory });
|
|
12364
12604
|
}
|
|
12365
12605
|
} else {
|
|
@@ -12406,7 +12646,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
12406
12646
|
}
|
|
12407
12647
|
setTimeout(async () => {
|
|
12408
12648
|
try {
|
|
12409
|
-
if (!
|
|
12649
|
+
if (!isLoopActiveForSession(sessDir, sessionId)) return;
|
|
12410
12650
|
const prompt = "Continue the loop. Read LOOP.md and keep working toward the exit conditions until the Stop gate confirms completion.";
|
|
12411
12651
|
await rpc.sendMessage(
|
|
12412
12652
|
JSON.stringify({
|
|
@@ -2,7 +2,7 @@ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(im
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import { resolve, join } from 'node:path';
|
|
4
4
|
import { existsSync, readFileSync, watch } from 'node:fs';
|
|
5
|
-
import { c as connectToHypha, a as createSessionStore, r as registerMachineService, P as generateHookSettings } from './run-
|
|
5
|
+
import { c as connectToHypha, a as createSessionStore, r as registerMachineService, P as generateHookSettings } from './run-BmZjAEob.mjs';
|
|
6
6
|
import { createServer } from 'node:http';
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
8
|
import { createInterface } from 'node:readline';
|
|
@@ -14,6 +14,7 @@ import 'url';
|
|
|
14
14
|
import 'child_process';
|
|
15
15
|
import 'crypto';
|
|
16
16
|
import 'util';
|
|
17
|
+
import 'node:events';
|
|
17
18
|
import '@agentclientprotocol/sdk';
|
|
18
19
|
import '@modelcontextprotocol/sdk/client/index.js';
|
|
19
20
|
import '@modelcontextprotocol/sdk/client/stdio.js';
|
|
@@ -54,7 +54,7 @@ async function handleServeCommand() {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
async function serveAdd(args, machineId) {
|
|
57
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
57
|
+
const { connectAndGetMachine } = await import('./commands-C_DlMpl7.mjs');
|
|
58
58
|
const pos = positionalArgs(args);
|
|
59
59
|
const name = pos[0];
|
|
60
60
|
if (!name) {
|
|
@@ -93,7 +93,7 @@ async function serveAdd(args, machineId) {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
async function serveApply(args, machineId) {
|
|
96
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
96
|
+
const { connectAndGetMachine } = await import('./commands-C_DlMpl7.mjs');
|
|
97
97
|
const fs = await import('fs');
|
|
98
98
|
const yaml = await import('yaml');
|
|
99
99
|
const file = positionalArgs(args)[0];
|
|
@@ -182,7 +182,7 @@ async function serveApply(args, machineId) {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
async function serveRemove(args, machineId) {
|
|
185
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
185
|
+
const { connectAndGetMachine } = await import('./commands-C_DlMpl7.mjs');
|
|
186
186
|
const pos = positionalArgs(args);
|
|
187
187
|
const name = pos[0];
|
|
188
188
|
if (!name) {
|
|
@@ -202,7 +202,7 @@ async function serveRemove(args, machineId) {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
async function serveList(args, machineId) {
|
|
205
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
205
|
+
const { connectAndGetMachine } = await import('./commands-C_DlMpl7.mjs');
|
|
206
206
|
const all = hasFlag(args, "--all", "-a");
|
|
207
207
|
const json = hasFlag(args, "--json");
|
|
208
208
|
const sessionId = getFlag(args, "--session");
|
|
@@ -235,7 +235,7 @@ async function serveList(args, machineId) {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
async function serveInfo(machineId) {
|
|
238
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
238
|
+
const { connectAndGetMachine } = await import('./commands-C_DlMpl7.mjs');
|
|
239
239
|
const { machine, server } = await connectAndGetMachine(machineId);
|
|
240
240
|
try {
|
|
241
241
|
const info = await machine.serveInfo();
|