svamp-cli 0.2.223-canary.2 → 0.2.224
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/dist/{agentCommands-MGRUIvWB.mjs → agentCommands-BignK4hn.mjs} +5 -5
- package/dist/{auth-FvyDIAch.mjs → auth-tP-DHfsI.mjs} +1 -1
- package/dist/cli.mjs +61 -61
- package/dist/{commands-Cmjb9Je1.mjs → commands-1IupY9qh.mjs} +6 -6
- package/dist/{commands-Dm3EQICq.mjs → commands-Bpq8_9OU.mjs} +2 -2
- package/dist/{commands-BhFr0kUL.mjs → commands-C1VIwqOH.mjs} +1 -1
- package/dist/{commands-_RXnsA1v.mjs → commands-CA8AMvq-.mjs} +1 -1
- package/dist/{commands-BiIkCdGB.mjs → commands-DMoXgLUh.mjs} +1 -1
- package/dist/{commands-BuecUAk8.mjs → commands-DqvzIDMK.mjs} +13 -1
- package/dist/{commands-nlra_-D0.mjs → commands-ez1xdcH5.mjs} +2 -2
- package/dist/{fleet-Bi3KIev7.mjs → fleet-B6Rh4mje.mjs} +1 -1
- package/dist/{frpc-BpQyeWvI.mjs → frpc-vpwKk0kV.mjs} +1 -1
- package/dist/{headlessCli-Be0bYOUB.mjs → headlessCli-C51w8i7T.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/package-CzzAMi8h.mjs +63 -0
- package/dist/{rpc-CRZWx5YY.mjs → rpc-CcQQrbBO.mjs} +1 -1
- package/dist/{rpc-WS0yDVqB.mjs → rpc-D7QFTk_g.mjs} +1 -1
- package/dist/{run-8_mnLg6R.mjs → run-BZrPFG_w.mjs} +1 -1
- package/dist/{run-DVdqRhtp.mjs → run-C-9ZtEi4.mjs} +77 -33
- package/dist/{scheduler-Dx-QrQfP.mjs → scheduler-jlX6Mkxf.mjs} +1 -1
- package/dist/{serveCommands-CI2P6buK.mjs → serveCommands-Dhrv-HAD.mjs} +5 -5
- package/dist/{serveManager-B-keYGE9.mjs → serveManager-DtBN8E9D.mjs} +2 -2
- package/dist/{sideband-BdYb3ezu.mjs → sideband-DxH9bMhv.mjs} +1 -1
- package/package.json +3 -3
- package/bin/skills/loop/IMPLEMENTATION_PROGRESS.md +0 -49
- package/bin/skills/loop/SKILL.md +0 -100
- package/bin/skills/loop/bin/channel-core.mjs +0 -161
- package/bin/skills/loop/bin/channel-server.mjs +0 -151
- package/bin/skills/loop/bin/inject-loop.mjs +0 -90
- package/bin/skills/loop/bin/loop-init.mjs +0 -194
- package/bin/skills/loop/bin/loop-status.mjs +0 -51
- package/bin/skills/loop/bin/precompact.mjs +0 -30
- package/bin/skills/loop/bin/routine-core.mjs +0 -126
- package/bin/skills/loop/bin/state-fp.mjs +0 -111
- package/bin/skills/loop/bin/stop-gate.mjs +0 -349
- package/bin/skills/loop/test/test-channel-core.mjs +0 -86
- package/bin/skills/loop/test/test-loop-gate.mjs +0 -489
- package/bin/skills/loop/test/test-routine-core.mjs +0 -54
- package/dist/package-CwGmpH_-.mjs +0 -63
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
3
|
import { basename, resolve, join, isAbsolute } from 'node:path';
|
|
4
4
|
import os from 'node:os';
|
|
5
|
-
import { Q as formatHandle, T as normalizeAllowedUser, U as loadSecurityContextConfig, V as resolveSecurityContext, W as buildSecurityContextFromFlags, X as mergeSecurityContexts, c as connectToHypha, Y as buildSessionShareUrl, Z as computeOutboundHop, A as shortId, _ as
|
|
5
|
+
import { Q as formatHandle, T as normalizeAllowedUser, U as loadSecurityContextConfig, V as resolveSecurityContext, W as buildSecurityContextFromFlags, X as mergeSecurityContexts, c as connectToHypha, Y as buildSessionShareUrl, Z as computeOutboundHop, A as shortId, _ as registerAwaitingReply, $ as buildMachineShareUrl, a0 as parseHandle, a1 as handleMatchesMetadata } from './run-C-9ZtEi4.mjs';
|
|
6
6
|
import 'os';
|
|
7
7
|
import 'fs/promises';
|
|
8
8
|
import 'fs';
|
|
@@ -1834,6 +1834,12 @@ async function sendCore(machine, fullId, message, opts) {
|
|
|
1834
1834
|
...hop.threadId ? { threadId: hop.threadId } : {}
|
|
1835
1835
|
};
|
|
1836
1836
|
const result = await machine.sessionRPC(fullId, "sendInboxMessage", { message: inboxMessage });
|
|
1837
|
+
if (callerSessionId) {
|
|
1838
|
+
try {
|
|
1839
|
+
registerAwaitingReply(callerSessionId, hop.threadId || inboxMessage.messageId);
|
|
1840
|
+
} catch {
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1837
1843
|
let waitResult;
|
|
1838
1844
|
if (shouldWait) {
|
|
1839
1845
|
const timeoutMs = (opts?.timeout || 300) * 1e3;
|
|
@@ -2614,6 +2620,12 @@ async function sessionInboxSend(sessionIdPartial, body, machineId, opts) {
|
|
|
2614
2620
|
threadId: opts?.threadId || hop.threadId
|
|
2615
2621
|
};
|
|
2616
2622
|
const result = await machine.sessionRPC(fullId, "sendInboxMessage", { message });
|
|
2623
|
+
if (callerSessionId) {
|
|
2624
|
+
try {
|
|
2625
|
+
registerAwaitingReply(callerSessionId, opts?.threadId || hop.threadId || message.messageId);
|
|
2626
|
+
} catch {
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2617
2629
|
if (opts?.json) {
|
|
2618
2630
|
console.log(formatJson({ sessionId: fullId, messageId: result.messageId, sent: true }));
|
|
2619
2631
|
} else {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
|
-
import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-
|
|
3
|
+
import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-DqvzIDMK.mjs';
|
|
4
4
|
import { execSync } from 'node:child_process';
|
|
5
|
-
import { u as updateIssue, q as addComment, t as addIssue, A as shortId } from './run-
|
|
5
|
+
import { u as updateIssue, q as addComment, t as addIssue, A as shortId } from './run-C-9ZtEi4.mjs';
|
|
6
6
|
import 'node:os';
|
|
7
7
|
import 'os';
|
|
8
8
|
import 'fs/promises';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
|
-
import { c as connectToHypha } from './run-
|
|
4
|
+
import { c as connectToHypha } from './run-C-9ZtEi4.mjs';
|
|
5
5
|
import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-VIupR1NK.mjs';
|
|
6
6
|
import 'os';
|
|
7
7
|
import 'fs/promises';
|
|
@@ -4,7 +4,7 @@ import { mkdirSync, writeFileSync, unlinkSync, existsSync, chmodSync, readFileSy
|
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import { homedir, platform, arch } from 'os';
|
|
6
6
|
import { randomUUID, createHash } from 'crypto';
|
|
7
|
-
import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-
|
|
7
|
+
import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-C-9ZtEi4.mjs';
|
|
8
8
|
import 'fs/promises';
|
|
9
9
|
import 'url';
|
|
10
10
|
import 'node:crypto';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as resolveModel,
|
|
2
|
-
import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-
|
|
1
|
+
import { P as resolveModel, a2 as describeMisconfiguration, a3 as buildMachineDeps } from './run-C-9ZtEi4.mjs';
|
|
2
|
+
import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-DxH9bMhv.mjs';
|
|
3
3
|
import { WebSocket } from 'ws';
|
|
4
4
|
import { execSync, spawn } from 'child_process';
|
|
5
5
|
import 'os';
|
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-C-9ZtEi4.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var name = "svamp-cli";
|
|
2
|
+
var version = "0.2.224";
|
|
3
|
+
var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
|
|
4
|
+
var author = "Amun AI AB";
|
|
5
|
+
var license = "SEE LICENSE IN LICENSE";
|
|
6
|
+
var type = "module";
|
|
7
|
+
var bin = {
|
|
8
|
+
svamp: "./bin/svamp.mjs"
|
|
9
|
+
};
|
|
10
|
+
var files = [
|
|
11
|
+
"dist",
|
|
12
|
+
"bin"
|
|
13
|
+
];
|
|
14
|
+
var main = "./dist/index.mjs";
|
|
15
|
+
var exports$1 = {
|
|
16
|
+
".": "./dist/index.mjs",
|
|
17
|
+
"./cli": "./dist/cli.mjs"
|
|
18
|
+
};
|
|
19
|
+
var scripts = {
|
|
20
|
+
build: "rm -rf dist bin/skills bin/commands && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/crew bin/skills/crew && cp -r ../../commands bin/commands && tsc --noEmit && pkgroll",
|
|
21
|
+
typecheck: "tsc --noEmit",
|
|
22
|
+
test: "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.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-sharing-notify-sync.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-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-btw-proxy-env.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.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-store.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-inbox-cross-machine.mjs && npx tsx test/test-issue-store.mjs && npx tsx test/test-issue-close-gate.mjs && npx tsx test/test-issue-rpc.mjs && npx tsx test/test-issue-pause.mjs && npx tsx test/test-workflow-store.mjs && npx tsx test/test-workflow-rpc.mjs && npx tsx test/test-workflow-scheduler.mjs && npx tsx test/test-workflow-runs.mjs && npx tsx test/test-serve-link-subdomain.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.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 && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.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-hotreload-seam.mjs && npx tsx test/test-hot-reload.mjs && npx tsx test/test-hot-reload-live.mjs && npx tsx test/test-session-core.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-shared-session-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-channel-upload.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs && npx tsx test/test-crew-verdict-routing.mjs && npx tsx test/test-crew-standalone.mjs && npx tsx test/test-session-links.mjs && npx tsx test/test-issue-loop-pause-guard.mjs",
|
|
23
|
+
"test:hypha": "node --no-warnings test/test-hypha-service.mjs",
|
|
24
|
+
dev: "tsx src/cli.ts",
|
|
25
|
+
"dev:daemon": "tsx src/cli.ts daemon start-sync",
|
|
26
|
+
"test:e2e": "node --no-warnings test/e2e-session-tests.mjs",
|
|
27
|
+
"test:frpc": "npx tsx test/test-frpc-e2e.mjs"
|
|
28
|
+
};
|
|
29
|
+
var dependencies = {
|
|
30
|
+
"@agentclientprotocol/sdk": "^0.14.1",
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
32
|
+
"hypha-rpc": "0.21.42",
|
|
33
|
+
"node-pty": "1.2.0-beta.11",
|
|
34
|
+
ws: "^8.18.0",
|
|
35
|
+
yaml: "^2.8.2",
|
|
36
|
+
zod: "^3.24.4"
|
|
37
|
+
};
|
|
38
|
+
var devDependencies = {
|
|
39
|
+
"@types/node": ">=20",
|
|
40
|
+
"@types/ws": "^8.5.14",
|
|
41
|
+
pkgroll: "^2.14.2",
|
|
42
|
+
tsx: "^4.20.6",
|
|
43
|
+
typescript: "5.9.3"
|
|
44
|
+
};
|
|
45
|
+
var packageManager = "yarn@1.22.22";
|
|
46
|
+
var _package = {
|
|
47
|
+
name: name,
|
|
48
|
+
version: version,
|
|
49
|
+
description: description,
|
|
50
|
+
author: author,
|
|
51
|
+
license: license,
|
|
52
|
+
type: type,
|
|
53
|
+
bin: bin,
|
|
54
|
+
files: files,
|
|
55
|
+
main: main,
|
|
56
|
+
exports: exports$1,
|
|
57
|
+
scripts: scripts,
|
|
58
|
+
dependencies: dependencies,
|
|
59
|
+
devDependencies: devDependencies,
|
|
60
|
+
packageManager: packageManager
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export { author, bin, _package as default, dependencies, description, devDependencies, exports$1 as exports, files, license, main, name, packageManager, scripts, type, version };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { m as resolveProjectRoot } from './run-
|
|
1
|
+
import { m as resolveProjectRoot } from './run-C-9ZtEi4.mjs';
|
|
2
2
|
import { g as getWorkflow, s as setWorkflowEnabled, r as removeWorkflow, a as saveWorkflow, b as rawWorkflow, l as listWorkflows } from './store-BTs0H_y0.mjs';
|
|
3
3
|
import { g as getRun, l as listRuns, r as runWorkflow } from './runStore-CtptN7US.mjs';
|
|
4
4
|
import 'os';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { m as resolveProjectRoot, u as updateIssue, n as getIssue, o as resumeIssue, p as pauseIssue, q as addComment, t as addIssue, v as listIssues, w as searchIssues, x as isVisibleTo } from './run-
|
|
1
|
+
import { m as resolveProjectRoot, u as updateIssue, n as getIssue, o as resumeIssue, p as pauseIssue, q as addComment, t as addIssue, v as listIssues, w as searchIssues, x as isVisibleTo } from './run-C-9ZtEi4.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import {
|
|
1
|
+
import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { a4 as applyClaudeProxyEnv, a5 as composeSessionId, a6 as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a7 as generateHookSettings } from './run-C-9ZtEi4.mjs';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import { resolve, join } from 'node:path';
|
|
4
4
|
import { existsSync, readFileSync, watch } from 'node:fs';
|
|
@@ -2912,7 +2912,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2912
2912
|
const tunnels = handlers.tunnels;
|
|
2913
2913
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
2914
2914
|
if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
|
|
2915
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
2915
|
+
const { FrpcTunnel } = await import('./frpc-vpwKk0kV.mjs');
|
|
2916
2916
|
const tunnel = new FrpcTunnel({
|
|
2917
2917
|
name: params.name,
|
|
2918
2918
|
ports: params.ports,
|
|
@@ -3359,7 +3359,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3359
3359
|
}
|
|
3360
3360
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
3361
3361
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
3362
|
-
const { toolsForRole } = await import('./sideband-
|
|
3362
|
+
const { toolsForRole } = await import('./sideband-DxH9bMhv.mjs');
|
|
3363
3363
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
3364
3364
|
return fmt(r2);
|
|
3365
3365
|
}
|
|
@@ -3458,7 +3458,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3458
3458
|
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
3459
3459
|
const callId = "call_" + Math.random().toString(16).slice(2, 12);
|
|
3460
3460
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
3461
|
-
const { queryCore } = await import('./commands-
|
|
3461
|
+
const { queryCore } = await import('./commands-DqvzIDMK.mjs');
|
|
3462
3462
|
const timeout = c.reply?.timeout_sec || 120;
|
|
3463
3463
|
let result;
|
|
3464
3464
|
try {
|
|
@@ -3694,6 +3694,48 @@ function readInboundContext(sessionId) {
|
|
|
3694
3694
|
return null;
|
|
3695
3695
|
}
|
|
3696
3696
|
}
|
|
3697
|
+
const AWAIT_TTL_MS = num("SVAMP_INBOX_AWAIT_TTL_MS", 60 * 6e4);
|
|
3698
|
+
const AWAIT_MAX = num("SVAMP_INBOX_AWAIT_MAX", 200);
|
|
3699
|
+
function awaitPath(sessionId) {
|
|
3700
|
+
return join(SVAMP_HOME$2, "awaiting", `${sessionId}.json`);
|
|
3701
|
+
}
|
|
3702
|
+
function readAwaiting(sessionId) {
|
|
3703
|
+
try {
|
|
3704
|
+
const p = awaitPath(sessionId);
|
|
3705
|
+
if (!existsSync(p)) return {};
|
|
3706
|
+
const m = JSON.parse(readFileSync(p, "utf-8"));
|
|
3707
|
+
return m && typeof m === "object" ? m : {};
|
|
3708
|
+
} catch {
|
|
3709
|
+
return {};
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
function writeAwaiting(sessionId, map) {
|
|
3713
|
+
try {
|
|
3714
|
+
mkdirSync(join(SVAMP_HOME$2, "awaiting"), { recursive: true });
|
|
3715
|
+
writeFileSync(awaitPath(sessionId), JSON.stringify(map));
|
|
3716
|
+
} catch {
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
function registerAwaitingReply(sessionId, threadId, now = Date.now()) {
|
|
3720
|
+
if (!sessionId || !threadId) return;
|
|
3721
|
+
const map = readAwaiting(sessionId);
|
|
3722
|
+
for (const k of Object.keys(map)) if (now - map[k] > AWAIT_TTL_MS) delete map[k];
|
|
3723
|
+
map[threadId] = now;
|
|
3724
|
+
const keys = Object.keys(map);
|
|
3725
|
+
if (keys.length > AWAIT_MAX) {
|
|
3726
|
+
keys.sort((a, b) => map[a] - map[b]).slice(0, keys.length - AWAIT_MAX).forEach((k) => delete map[k]);
|
|
3727
|
+
}
|
|
3728
|
+
writeAwaiting(sessionId, map);
|
|
3729
|
+
}
|
|
3730
|
+
function takeAwaitingReply(sessionId, threadId, now = Date.now()) {
|
|
3731
|
+
if (!sessionId || !threadId) return false;
|
|
3732
|
+
const map = readAwaiting(sessionId);
|
|
3733
|
+
const at = map[threadId];
|
|
3734
|
+
if (typeof at !== "number") return false;
|
|
3735
|
+
delete map[threadId];
|
|
3736
|
+
writeAwaiting(sessionId, map);
|
|
3737
|
+
return now - at <= AWAIT_TTL_MS;
|
|
3738
|
+
}
|
|
3697
3739
|
function computeOutboundHop(sessionId) {
|
|
3698
3740
|
if (!sessionId) return { hopCount: 0, fromInboxTurn: false };
|
|
3699
3741
|
const ctx = readInboundContext(sessionId);
|
|
@@ -3739,18 +3781,27 @@ function senderIsHuman(message) {
|
|
|
3739
3781
|
function senderKey(message) {
|
|
3740
3782
|
return message.fromSession || message.from || "unknown";
|
|
3741
3783
|
}
|
|
3742
|
-
function classifyInbound(message, now = Date.now()) {
|
|
3784
|
+
function classifyInbound(message, now = Date.now(), opts) {
|
|
3743
3785
|
const hop = message.hopCount ?? 0;
|
|
3744
3786
|
const human = senderIsHuman(message);
|
|
3745
|
-
|
|
3787
|
+
const self = !!opts?.self;
|
|
3788
|
+
const openThreadReply = !!opts?.openThreadReply;
|
|
3789
|
+
if (!human && !self && !message.channel && hop > MAX_AGENT_HOPS) {
|
|
3746
3790
|
return { action: "silent", reason: `loop-guard: hop ${hop} > ${MAX_AGENT_HOPS}` };
|
|
3747
3791
|
}
|
|
3748
|
-
if (
|
|
3749
|
-
|
|
3792
|
+
if (human || self) {
|
|
3793
|
+
const tripped2 = recordWake(now);
|
|
3794
|
+
return { action: "wake", reason: self ? "always-wake (self)" : "always-wake (human)", breakerTripped: tripped2 };
|
|
3750
3795
|
}
|
|
3751
|
-
if (
|
|
3796
|
+
if (openThreadReply) {
|
|
3797
|
+
if (isBreakerOpenAt(now)) {
|
|
3798
|
+
return { action: "queue", reason: "open-thread reply (breaker open)" };
|
|
3799
|
+
}
|
|
3752
3800
|
const tripped2 = recordWake(now);
|
|
3753
|
-
return { action: "wake", reason: "
|
|
3801
|
+
return { action: "wake", reason: "open-thread reply", breakerTripped: tripped2 };
|
|
3802
|
+
}
|
|
3803
|
+
if (message.urgency !== "urgent") {
|
|
3804
|
+
return { action: "queue", reason: "non-urgent" };
|
|
3754
3805
|
}
|
|
3755
3806
|
if (isBreakerOpenAt(now)) {
|
|
3756
3807
|
return { action: "queue", reason: "circuit-breaker-open" };
|
|
@@ -11295,20 +11346,6 @@ async function ensureAutoInstalledSkills(logger) {
|
|
|
11295
11346
|
},
|
|
11296
11347
|
marketplaceVersion: async () => readBundledSkillVersion("artifact")
|
|
11297
11348
|
},
|
|
11298
|
-
{
|
|
11299
|
-
// The self-verifying `loop` skill drives loop mode (Stop-hook gate +
|
|
11300
|
-
// LOOP.md injection). Bundled in the npm package (bin/skills/loop/) so the
|
|
11301
|
-
// daemon can run loop-init.mjs even offline / before marketplace publish.
|
|
11302
|
-
name: "loop",
|
|
11303
|
-
install: async () => {
|
|
11304
|
-
try {
|
|
11305
|
-
installBundledSkill("loop");
|
|
11306
|
-
} catch {
|
|
11307
|
-
await installSkillFromMarketplace("loop");
|
|
11308
|
-
}
|
|
11309
|
-
},
|
|
11310
|
-
marketplaceVersion: async () => readBundledSkillVersion("loop")
|
|
11311
|
-
},
|
|
11312
11349
|
{
|
|
11313
11350
|
// The `crew` skill: a lead session delegates features to managed worktree
|
|
11314
11351
|
// children (svamp feature start/list/done/merge) and merges them back.
|
|
@@ -11496,6 +11533,13 @@ function parseLoopBudget(raw) {
|
|
|
11496
11533
|
if (Number.isFinite(tph) && tph > 0) out.max_tokens_per_hour = tph;
|
|
11497
11534
|
return out.max_runtime_sec || out.max_tokens || out.max_tokens_per_hour ? out : void 0;
|
|
11498
11535
|
}
|
|
11536
|
+
function classifyOptsForMessage(sessionId, message, ownerEmail) {
|
|
11537
|
+
const fromVal = message.from || "";
|
|
11538
|
+
const senderEmail = message.verified && fromVal.includes("@") ? fromVal.replace(/^user:/, "").toLowerCase() : void 0;
|
|
11539
|
+
const self = !!(ownerEmail && senderEmail && senderEmail === String(ownerEmail).toLowerCase());
|
|
11540
|
+
const openThreadReply = takeAwaitingReply(sessionId, message.threadId);
|
|
11541
|
+
return { self, openThreadReply };
|
|
11542
|
+
}
|
|
11499
11543
|
function buildGoalCondition(o) {
|
|
11500
11544
|
const parts = [];
|
|
11501
11545
|
if (o.task) parts.push(o.task.trim());
|
|
@@ -12188,7 +12232,7 @@ async function startDaemon(options) {
|
|
|
12188
12232
|
saveExposedTunnels(list);
|
|
12189
12233
|
}
|
|
12190
12234
|
async function createExposedTunnel(spec) {
|
|
12191
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
12235
|
+
const { FrpcTunnel } = await import('./frpc-vpwKk0kV.mjs');
|
|
12192
12236
|
const tunnel = new FrpcTunnel({
|
|
12193
12237
|
name: spec.name,
|
|
12194
12238
|
ports: spec.ports,
|
|
@@ -12208,7 +12252,7 @@ async function startDaemon(options) {
|
|
|
12208
12252
|
return tunnel;
|
|
12209
12253
|
}
|
|
12210
12254
|
const tunnelRecreateState = /* @__PURE__ */ new Map();
|
|
12211
|
-
const { ServeManager } = await import('./serveManager-
|
|
12255
|
+
const { ServeManager } = await import('./serveManager-DtBN8E9D.mjs');
|
|
12212
12256
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
12213
12257
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
12214
12258
|
});
|
|
@@ -14013,7 +14057,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14013
14057
|
},
|
|
14014
14058
|
onInboxMessage: (message) => {
|
|
14015
14059
|
if (trackedSession?.stopped) return;
|
|
14016
|
-
const decision = classifyInbound(message);
|
|
14060
|
+
const decision = classifyInbound(message, Date.now(), classifyOptsForMessage(sessionId, message, sessionMetadata?.sharing?.owner));
|
|
14017
14061
|
logger.log(`[Session ${sessionId}] Inbox message (urgency: ${message.urgency || "normal"}, hop: ${message.hopCount ?? 0}, from: ${message.from || "unknown"}) \u2192 ${decision.action} (${decision.reason})`);
|
|
14018
14062
|
if (decision.breakerTripped) {
|
|
14019
14063
|
logger.error(`[Session ${sessionId}] Inbox circuit breaker TRIPPED \u2014 cross-agent wake rate too high; auto-waking paused.`);
|
|
@@ -14109,11 +14153,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14109
14153
|
});
|
|
14110
14154
|
},
|
|
14111
14155
|
onIssue: async (params) => {
|
|
14112
|
-
const { issueRpc } = await import('./rpc-
|
|
14156
|
+
const { issueRpc } = await import('./rpc-D7QFTk_g.mjs');
|
|
14113
14157
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
14114
14158
|
},
|
|
14115
14159
|
onWorkflow: async (params) => {
|
|
14116
|
-
const { workflowRpc } = await import('./rpc-
|
|
14160
|
+
const { workflowRpc } = await import('./rpc-CcQQrbBO.mjs');
|
|
14117
14161
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
14118
14162
|
},
|
|
14119
14163
|
onRipgrep: async (args, cwd) => {
|
|
@@ -14519,7 +14563,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14519
14563
|
},
|
|
14520
14564
|
onInboxMessage: (message) => {
|
|
14521
14565
|
if (acpStopped) return;
|
|
14522
|
-
const decision = classifyInbound(message);
|
|
14566
|
+
const decision = classifyInbound(message, Date.now(), classifyOptsForMessage(sessionId, message, sessionMetadata?.sharing?.owner));
|
|
14523
14567
|
logger.log(`[${agentName} Session ${sessionId}] Inbox message (urgency: ${message.urgency || "normal"}, hop: ${message.hopCount ?? 0}, from: ${message.from || "unknown"}) \u2192 ${decision.action} (${decision.reason})`);
|
|
14524
14568
|
if (decision.breakerTripped) {
|
|
14525
14569
|
logger.error(`[${agentName} Session ${sessionId}] Inbox circuit breaker TRIPPED \u2014 auto-waking paused.`);
|
|
@@ -14646,11 +14690,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14646
14690
|
});
|
|
14647
14691
|
},
|
|
14648
14692
|
onIssue: async (params) => {
|
|
14649
|
-
const { issueRpc } = await import('./rpc-
|
|
14693
|
+
const { issueRpc } = await import('./rpc-D7QFTk_g.mjs');
|
|
14650
14694
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
14651
14695
|
},
|
|
14652
14696
|
onWorkflow: async (params) => {
|
|
14653
|
-
const { workflowRpc } = await import('./rpc-
|
|
14697
|
+
const { workflowRpc } = await import('./rpc-CcQQrbBO.mjs');
|
|
14654
14698
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
14655
14699
|
},
|
|
14656
14700
|
onRipgrep: async (args, cwd) => {
|
|
@@ -15424,7 +15468,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
15424
15468
|
const PING_TIMEOUT_MS = 15e3;
|
|
15425
15469
|
const POST_RECONNECT_GRACE_MS = 2e4;
|
|
15426
15470
|
const RECONNECT_JITTER_MS = 2500;
|
|
15427
|
-
const { WorkflowScheduler } = await import('./scheduler-
|
|
15471
|
+
const { WorkflowScheduler } = await import('./scheduler-jlX6Mkxf.mjs');
|
|
15428
15472
|
const workflowScheduler = new WorkflowScheduler({
|
|
15429
15473
|
projectRoots: () => {
|
|
15430
15474
|
const dirs = /* @__PURE__ */ new Set();
|
|
@@ -16003,4 +16047,4 @@ var run = /*#__PURE__*/Object.freeze({
|
|
|
16003
16047
|
writeStopMarker: writeStopMarker
|
|
16004
16048
|
});
|
|
16005
16049
|
|
|
16006
|
-
export {
|
|
16050
|
+
export { buildMachineShareUrl as $, shortId as A, loadMachineContext as B, buildMachineInstructions as C, machineToolsForRole as D, buildMachineTools as E, parseFrontmatter as F, getSkillsServer as G, getSkillsWorkspaceName as H, getSkillsCollectionName as I, fetchWithTimeout as J, searchSkills as K, SKILLS_DIR as L, getSkillInfo as M, downloadSkillFile as N, listSkillFiles as O, resolveModel as P, formatHandle as Q, READ_ONLY_TOOLS as R, ServeAuth as S, normalizeAllowedUser as T, loadSecurityContextConfig as U, resolveSecurityContext as V, buildSecurityContextFromFlags as W, mergeSecurityContexts as X, buildSessionShareUrl as Y, computeOutboundHop as Z, registerAwaitingReply as _, createSessionStore as a, parseHandle as a0, handleMatchesMetadata as a1, describeMisconfiguration as a2, buildMachineDeps as a3, applyClaudeProxyEnv as a4, composeSessionId as a5, generateFriendlyName as a6, generateHookSettings as a7, claudeAuth as a8, projectInfo as a9, DefaultTransport$1 as aa, acpBackend as ab, acpAgentConfig as ac, codexMcpBackend as ad, GeminiTransport$1 as ae, instanceConfig as af, api as ag, run as ah, 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, resolveProjectRoot as m, getIssue as n, resumeIssue as o, pauseIssue as p, addComment as q, registerMachineService as r, startDaemon as s, addIssue as t, updateIssue as u, listIssues as v, searchIssues as w, isVisibleTo as x, cronMatches as y, summarize as z };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { m as resolveProjectRoot, y as cronMatches } from './run-
|
|
1
|
+
import { m as resolveProjectRoot, y as cronMatches } from './run-C-9ZtEi4.mjs';
|
|
2
2
|
import { l as listWorkflows, i as isWorkflowEnabled, w as workflowCrons } from './store-BTs0H_y0.mjs';
|
|
3
3
|
import { r as runWorkflow } from './runStore-CtptN7US.mjs';
|
|
4
4
|
import 'os';
|
|
@@ -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-DqvzIDMK.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-DqvzIDMK.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-DqvzIDMK.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-DqvzIDMK.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-DqvzIDMK.mjs');
|
|
239
239
|
const { machine, server } = await connectAndGetMachine(machineId);
|
|
240
240
|
try {
|
|
241
241
|
const info = await machine.serveInfo();
|
|
@@ -4,7 +4,7 @@ import * as fs from 'fs';
|
|
|
4
4
|
import * as http from 'http';
|
|
5
5
|
import * as net from 'net';
|
|
6
6
|
import * as path from 'path';
|
|
7
|
-
import { k as getHyphaServerUrl, S as ServeAuth, l as hasCookieToken } from './run-
|
|
7
|
+
import { k as getHyphaServerUrl, S as ServeAuth, l as hasCookieToken } from './run-C-9ZtEi4.mjs';
|
|
8
8
|
import 'os';
|
|
9
9
|
import 'fs/promises';
|
|
10
10
|
import 'url';
|
|
@@ -733,7 +733,7 @@ class ServeManager {
|
|
|
733
733
|
const mount = this.mounts.get(mountName);
|
|
734
734
|
const subdomainOverride = mount?.access === "link" && mount.linkToken ? /* @__PURE__ */ new Map([[this.port, buildLinkSubdomain(subdomainSafe, mount.linkToken)]]) : void 0;
|
|
735
735
|
try {
|
|
736
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
736
|
+
const { FrpcTunnel } = await import('./frpc-vpwKk0kV.mjs');
|
|
737
737
|
let tunnel;
|
|
738
738
|
tunnel = new FrpcTunnel({
|
|
739
739
|
name: tunnelName,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as READ_ONLY_TOOLS, B as loadMachineContext, C as buildMachineInstructions, D as machineToolsForRole, E as buildMachineTools } from './run-
|
|
1
|
+
import { R as READ_ONLY_TOOLS, B as loadMachineContext, C as buildMachineInstructions, D as machineToolsForRole, E as buildMachineTools } from './run-C-9ZtEi4.mjs';
|
|
2
2
|
import 'node:child_process';
|
|
3
3
|
import 'os';
|
|
4
4
|
import 'fs/promises';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svamp-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.224",
|
|
4
4
|
"description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
|
|
5
5
|
"author": "Amun AI AB",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
"./cli": "./dist/cli.mjs"
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
|
-
"build": "rm -rf dist bin/skills bin/commands && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/
|
|
21
|
+
"build": "rm -rf dist bin/skills bin/commands && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/crew bin/skills/crew && cp -r ../../commands bin/commands && tsc --noEmit && pkgroll",
|
|
22
22
|
"typecheck": "tsc --noEmit",
|
|
23
|
-
"test": "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.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-sharing-notify-sync.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-
|
|
23
|
+
"test": "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.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-sharing-notify-sync.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-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-btw-proxy-env.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.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-store.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-inbox-cross-machine.mjs && npx tsx test/test-issue-store.mjs && npx tsx test/test-issue-close-gate.mjs && npx tsx test/test-issue-rpc.mjs && npx tsx test/test-issue-pause.mjs && npx tsx test/test-workflow-store.mjs && npx tsx test/test-workflow-rpc.mjs && npx tsx test/test-workflow-scheduler.mjs && npx tsx test/test-workflow-runs.mjs && npx tsx test/test-serve-link-subdomain.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.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 && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.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-hotreload-seam.mjs && npx tsx test/test-hot-reload.mjs && npx tsx test/test-hot-reload-live.mjs && npx tsx test/test-session-core.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-shared-session-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-channel-upload.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs && npx tsx test/test-crew-verdict-routing.mjs && npx tsx test/test-crew-standalone.mjs && npx tsx test/test-session-links.mjs && npx tsx test/test-issue-loop-pause-guard.mjs",
|
|
24
24
|
"test:hypha": "node --no-warnings test/test-hypha-service.mjs",
|
|
25
25
|
"dev": "tsx src/cli.ts",
|
|
26
26
|
"dev:daemon": "tsx src/cli.ts daemon start-sync",
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# Loops, Routines & Channels — implementation status
|
|
2
|
-
|
|
3
|
-
Branch: `loop-engineering-phase1`. Design docs: `docs/loops-and-routines-design.md`, `docs/channels-design.md`.
|
|
4
|
-
|
|
5
|
-
This consolidates the work into one accurate picture: what's built, how it was verified, and the single remaining step (deploy + UI QA).
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 1. Loop engine — DONE (tested + live-E2E + 2 adversarial reviews)
|
|
10
|
-
Skill-first loop with a harness-enforced completion gate. Files in `skills/loop/`.
|
|
11
|
-
- **`SKILL.md`** — loop-engineering protocol (actor ≠ judge; oracle + isolated evaluator).
|
|
12
|
-
- **`bin/stop-gate.mjs`** — Claude Code `Stop` hook: blocks the turn ending unless the **oracle exits 0 AND a fingerprint-fresh evaluator verdict says "done."** Bounded by clamped `max_iterations` (hard ceiling 200), runtime budget, and token budget (summed from the transcript). Appends a per-iteration audit trail.
|
|
13
|
-
- **`bin/state-fp.mjs`** — working-tree fingerprint (git diff, or filesystem walk incl. symlink targets for non-git dirs) tying a verdict to the exact code it judged.
|
|
14
|
-
- **`bin/inject-loop.mjs`** (SessionStart/UserPromptSubmit) · **`bin/precompact.mjs`** (PreCompact handoff) · **`bin/loop-init.mjs`** (generates LOOP.md + `.claude/settings.json` hooks + optional `loop-evaluator` agent) · **`bin/loop-status.mjs`** (timeline reader).
|
|
15
|
-
- **Tests:** `test/test-loop-gate.mjs` (29) — all pass. **Live-verified** in real svamp sessions: happy path, **early-quit blocked**, isolated evaluator subagent, full regression.
|
|
16
|
-
- Skill↔gate↔inject **contract verified consistent** (verdict path/fields/`state_fp` command all agree).
|
|
17
|
-
|
|
18
|
-
## 2. Routines — DONE (tested + live-E2E)
|
|
19
|
-
Session-scoped triggers → message/loop actions. `skills/loop/bin/routine-*.mjs` (JS engine + supervised server) and `packages/svamp-cli/src/routine/*.ts` (daemon).
|
|
20
|
-
- Triggers: schedule (cron, tz-aware tick **and** catch-up), webhook (capability URL, GET/POST), manual. Actions: message | loop. Overlap (queue/skip/replace concurrent-guard), daily_cap (reserve-before-deliver), missed-run catch-up.
|
|
21
|
-
- `svamp routine add/list/remove/enable/disable/run-now/serve`; daemon RPC (`hyphaSessionService`, 10 runtime tests); frontend **Routines tab**.
|
|
22
|
-
- **Tests:** routine-core, routine-engine (26), cli routine (18), cli routine-rpc (10) — all pass. **Live-verified**: webhook → templated message delivered into a real session.
|
|
23
|
-
|
|
24
|
-
## 3. Channels — DONE (tested + live-E2E both transports + 2 security reviews)
|
|
25
|
-
External / agent-to-agent inbound endpoints. `skills/loop/bin/channel-*.mjs` + `packages/svamp-cli/src/channel/store.ts`.
|
|
26
|
-
- Identity: per-key (verified) / caller-supplied (unverified) / fixed / Hypha `context.user` (opt-in `hypha_allow`, deny-by-default, anonymous rejected). Default **XML provenance template** (all values XML-escaped + control-chars stripped → no forgery/tag-breakout). Auto-generated **skill** per channel.
|
|
27
|
-
- Transports: **Hypha `svamp-channels` service** (verified identity, no key) **and** HTTP `/channel/<id>` (+ `/skill.md`, bearer key). Delivers as a **plain single-tag message**. Actions: **message and loop** (loop seeds `loop-init` + kickoff).
|
|
28
|
-
- `hypha channel list/describe/send` CLI client; daemon RPC (10 runtime tests); frontend **Channels tab**.
|
|
29
|
-
- **Tests:** channel-core (32), cli channel-rpc (10) — all pass. **Live-verified**: HTTP per-key (`from=alice verified`), Hypha (`from=<hypha-user> verified`, no key), loop-action (loop seeded + work completed).
|
|
30
|
-
|
|
31
|
-
## Verification summary
|
|
32
|
-
All feature suites green; **svamp-cli + svamp-app + hypha-cli typecheck 0 errors**. Standalone/skill parts live-E2E-verified. Daemon RPC runtime-verified via `createSessionStore`. Two adversarial review rounds each on the loop gate and channels (caught + fixed 3 CRITICAL block-forever bugs and a CRITICAL provenance-forgery hole); daemon-code review confirmed no destabilization. Review has **converged** (latest pass found nothing).
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## The ONE remaining step — deploy + UI QA (must be done from a SEPARATE session)
|
|
37
|
-
The daemon RPC + the three frontend tabs (Routines / Channels / Loop-monitor) are typecheck-clean and production-bundle clean, but their **live rendering + RPC round-trip have not been visually confirmed** — and this repo has no component-render test infra (UI is verified by deploy/manual). Activating them restarts the daemon hosting this loop session, so it is **deliberately not done here**. To finish:
|
|
38
|
-
```
|
|
39
|
-
yarn workspace svamp-cli build && svamp daemon restart # activates routine/channel RPC
|
|
40
|
-
./deploy/quick-deploy.sh # ships the web UI
|
|
41
|
-
```
|
|
42
|
-
Then open a session → input → settings and confirm the **Routines / Channels / Loop-monitor** tabs render and round-trip. The standalone engines need no deploy — `node skills/loop/bin/loop-init.mjs …`, `svamp routine …`, and the supervised channel/routine servers already work today.
|
|
43
|
-
|
|
44
|
-
## Deferred (by design or low-risk-to-leave)
|
|
45
|
-
- **await-reply (channels v1.1)** — request/response; v1 is async (two channels), per the design decision.
|
|
46
|
-
- **Loop gate on an already-running session** — hooks load at Claude startup, so looping a live session is best-effort (self-contained kickoff); full enforcement needs a fresh/restarted session.
|
|
47
|
-
- **#4/#6/#11 concurrency edges** — cross-process lost-update / git↔walk fp flip / store last-writer-wins. Low-likelihood for the single-session loop; a forced fix needs locking that could itself cause block-forever, so documented rather than fixed.
|
|
48
|
-
- **Token/cost budget in USD** — token budget (from transcript) is implemented; USD cost would need daemon usage accounting.
|
|
49
|
-
</content>
|