svamp-cli 0.2.127 → 0.2.129

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.
@@ -58,7 +58,7 @@ async function serviceExpose(args) {
58
58
  process.exit(1);
59
59
  }
60
60
  if (foreground) {
61
- const { runFrpcTunnel } = await import('./frpc-BElGm4ku.mjs');
61
+ const { runFrpcTunnel } = await import('./frpc-BpBOWRbL.mjs');
62
62
  await runFrpcTunnel(name, ports, void 0, {
63
63
  group,
64
64
  groupKey,
@@ -68,7 +68,7 @@ async function serviceExpose(args) {
68
68
  });
69
69
  return;
70
70
  }
71
- const { connectAndGetMachine } = await import('./commands-jr6pFEEu.mjs');
71
+ const { connectAndGetMachine } = await import('./commands-BV30A1zt.mjs');
72
72
  const { server, machine } = await connectAndGetMachine();
73
73
  try {
74
74
  const status = await machine.tunnelStart({
@@ -123,7 +123,7 @@ async function serviceServe(args) {
123
123
  };
124
124
  process.on("SIGINT", cleanup);
125
125
  process.on("SIGTERM", cleanup);
126
- const { runFrpcTunnel } = await import('./frpc-BElGm4ku.mjs');
126
+ const { runFrpcTunnel } = await import('./frpc-BpBOWRbL.mjs');
127
127
  await runFrpcTunnel(name, [caddyPort]);
128
128
  } catch (err) {
129
129
  console.error(`Error serving directory: ${err.message}`);
@@ -132,7 +132,7 @@ async function serviceServe(args) {
132
132
  }
133
133
  async function serviceList(_args) {
134
134
  try {
135
- const { connectAndGetMachine } = await import('./commands-jr6pFEEu.mjs');
135
+ const { connectAndGetMachine } = await import('./commands-BV30A1zt.mjs');
136
136
  const { server, machine } = await connectAndGetMachine();
137
137
  try {
138
138
  const tunnels = await machine.tunnelList({});
@@ -161,7 +161,7 @@ async function serviceDelete(args) {
161
161
  process.exit(1);
162
162
  }
163
163
  try {
164
- const { connectAndGetMachine } = await import('./commands-jr6pFEEu.mjs');
164
+ const { connectAndGetMachine } = await import('./commands-BV30A1zt.mjs');
165
165
  const { server, machine } = await connectAndGetMachine();
166
166
  try {
167
167
  await machine.tunnelStop({ name });
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from 'node:fs';
2
- import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-jr6pFEEu.mjs';
2
+ import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-BV30A1zt.mjs';
3
3
  import { execSync } from 'node:child_process';
4
- import { m as shortId } from './run-BlfxwBzW.mjs';
4
+ import { m as shortId } from './run-BaTwfE1Q.mjs';
5
5
  import 'node:path';
6
6
  import 'node:os';
7
7
  import 'os';
@@ -204,17 +204,17 @@ async function featureStart(brief, opts = {}) {
204
204
  let judges = [];
205
205
  if (oracle) {
206
206
  judges = [{ type: "oracle", cmd: oracle, on_fail: "escalate" }, { type: "parent", parent: lead.sessionId }];
207
- patch.supervisor = {
208
- criteria: brief.trim(),
209
- judges,
210
- action: opts.action || "nudge",
211
- max_rounds: opts.maxRounds ?? 20
207
+ patch.loop = {
208
+ until: brief.trim(),
209
+ oracle,
210
+ parent: lead.sessionId,
211
+ max_iterations: opts.maxRounds ?? 20
212
212
  };
213
213
  }
214
214
  try {
215
215
  await rpc(machine, childId, "updateConfig", { patch });
216
216
  } catch (e) {
217
- console.warn(`Note: could not write crew/supervisor config yet (${e.message}).`);
217
+ console.warn(`Note: could not write crew/loop config yet (${e.message}).`);
218
218
  }
219
219
  try {
220
220
  await rpc(machine, childId, "sendMessage", {
@@ -453,7 +453,7 @@ const FEATURE_HELP = `Usage: svamp feature <command>
453
453
 
454
454
  start "<brief>" [--oracle "<cmd>"] [--base <branch>] [--action nudge|block]
455
455
  [--max N] [--dir <path>] [--lead <id>] [-m <machine>]
456
- Spawn a managed worktree child of the lead + attach a supervisor (oracle\u2192parent).
456
+ Spawn a managed worktree child of the lead + attach a loop gate (oracle\u2192parent).
457
457
  list [--json] [--lead <id>] [-m <machine>]
458
458
  The lead's feature children: branch, ahead/behind, status.
459
459
  report "<text>" [--blocker] [-m <machine>] (run by a child) progress/blocker \u2192 lead
@@ -1,7 +1,7 @@
1
1
  import { execSync, execFileSync } from 'node:child_process';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { createServer } from 'node:http';
4
- import { E as readChecklist, F as compileChecklist, G as RoutineStore, H as RoutineRunner } from './run-BlfxwBzW.mjs';
4
+ import { E as readChecklist, F as compileChecklist, G as RoutineStore, H as RoutineRunner } from './run-BaTwfE1Q.mjs';
5
5
  import 'os';
6
6
  import 'fs/promises';
7
7
  import 'fs';
@@ -104,7 +104,7 @@ Criteria: ${res.criteria || "(none)"}
104
104
  urgency: "normal",
105
105
  hopCount: 1
106
106
  };
107
- const { connectAndGetMachine } = await import('./commands-jr6pFEEu.mjs');
107
+ const { connectAndGetMachine } = await import('./commands-BV30A1zt.mjs');
108
108
  const { server, machine } = await connectAndGetMachine();
109
109
  try {
110
110
  await machine.sessionRPC(reportTo, "sendInboxMessage", { message });
@@ -1,11 +1,11 @@
1
1
  import { writeFileSync, readFileSync } from 'fs';
2
2
  import { resolve } from 'path';
3
- import { connectAndGetMachine } from './commands-jr6pFEEu.mjs';
3
+ import { connectAndGetMachine } from './commands-BV30A1zt.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:child_process';
6
6
  import 'node:path';
7
7
  import 'node:os';
8
- import './run-BlfxwBzW.mjs';
8
+ import './run-BaTwfE1Q.mjs';
9
9
  import 'os';
10
10
  import 'fs/promises';
11
11
  import 'url';
@@ -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-BlfxwBzW.mjs';
4
+ import { c as connectToHypha } from './run-BaTwfE1Q.mjs';
5
5
  import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-HydRNEt7.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
@@ -3,7 +3,7 @@ import { mkdirSync, writeFileSync, unlinkSync, existsSync, chmodSync, readFileSy
3
3
  import { join } from 'path';
4
4
  import { homedir, platform, arch } from 'os';
5
5
  import { createHash, randomUUID } from 'crypto';
6
- import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-BlfxwBzW.mjs';
6
+ import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-BaTwfE1Q.mjs';
7
7
  import 'fs/promises';
8
8
  import 'url';
9
9
  import 'node:crypto';
@@ -1,5 +1,5 @@
1
- import { D as resolveModel, V as describeMisconfiguration, W as buildMachineDeps } from './run-BlfxwBzW.mjs';
2
- import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-DDuv8hTP.mjs';
1
+ import { D as resolveModel, V as describeMisconfiguration, W as buildMachineDeps } from './run-BaTwfE1Q.mjs';
2
+ import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-D_Hzijan.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-BlfxwBzW.mjs';
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-BaTwfE1Q.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -1,5 +1,5 @@
1
1
  var name = "svamp-cli";
2
- var version = "0.2.127";
2
+ var version = "0.2.129";
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 && cp -r ../../skills/crew bin/skills/crew && 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-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.mjs && npx tsx test/test-checklist.mjs && npx tsx test/test-short-id.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 && 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-checklist-watchdog.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-channel-binding.mjs && npx tsx test/test-channel-identity.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 && npx tsx test/test-crew-merge.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-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.mjs && npx tsx test/test-checklist.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 && 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-checklist-watchdog.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-channel-binding.mjs && npx tsx test/test-channel-identity.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 && npx tsx test/test-crew-merge.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",
@@ -2677,7 +2677,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
2677
2677
  const tunnels = handlers.tunnels;
2678
2678
  if (!tunnels) throw new Error("Tunnel management not available");
2679
2679
  if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
2680
- const { FrpcTunnel } = await import('./frpc-BElGm4ku.mjs');
2680
+ const { FrpcTunnel } = await import('./frpc-BpBOWRbL.mjs');
2681
2681
  const tunnel = new FrpcTunnel({
2682
2682
  name: params.name,
2683
2683
  ports: params.ports,
@@ -2938,7 +2938,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
2938
2938
  }
2939
2939
  const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
2940
2940
  const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
2941
- const { toolsForRole } = await import('./sideband-DDuv8hTP.mjs');
2941
+ const { toolsForRole } = await import('./sideband-D_Hzijan.mjs');
2942
2942
  const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
2943
2943
  return fmt(r2);
2944
2944
  }
@@ -3037,7 +3037,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3037
3037
  if (r.error || !r.sender) return { error: r.error || "unauthorized" };
3038
3038
  const callId = "call_" + Math.random().toString(16).slice(2, 12);
3039
3039
  const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
3040
- const { queryCore } = await import('./commands-jr6pFEEu.mjs');
3040
+ const { queryCore } = await import('./commands-BV30A1zt.mjs');
3041
3041
  const timeout = c.reply?.timeout_sec || 120;
3042
3042
  let result;
3043
3043
  try {
@@ -3673,7 +3673,7 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
3673
3673
  notifyListeners({ type: "update-session", sessionId, metadata: { value: metadata, version: metadataVersion } });
3674
3674
  callbacks.onMetadataUpdate?.(metadata);
3675
3675
  };
3676
- const forkClaudePrint = async (prompt, claudeSessionId, cwd, maxTurns = 6) => {
3676
+ const forkClaudePrint = async (prompt, claudeSessionId, cwd, maxTurns = 6, timeoutMs = 6e4) => {
3677
3677
  const { spawn } = await import('child_process');
3678
3678
  return new Promise((resolve) => {
3679
3679
  const child = spawn("claude", [
@@ -3691,7 +3691,7 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
3691
3691
  String(maxTurns)
3692
3692
  ], {
3693
3693
  cwd,
3694
- timeout: 6e4,
3694
+ timeout: timeoutMs,
3695
3695
  stdio: ["ignore", "pipe", "pipe"],
3696
3696
  env: { ...process.env, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" }
3697
3697
  });
@@ -4487,7 +4487,7 @@ ${instruction}
4487
4487
  </instruction>
4488
4488
 
4489
4489
  Output ONLY the full revised reply text \u2014 no preamble, no commentary, no surrounding quotes or code fences.`;
4490
- const revised = await forkClaudePrint(prompt, claudeSessionId, metadata.path || process.cwd());
4490
+ const revised = await forkClaudePrint(prompt, claudeSessionId, metadata.path || process.cwd(), 1, 18e4);
4491
4491
  if (!revised.success || !revised.text?.trim()) {
4492
4492
  return { success: false, message: revised.error || "Failed to generate a revised reply." };
4493
4493
  }
@@ -10804,15 +10804,32 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
10804
10804
  loopChecker();
10805
10805
  };
10806
10806
  const writeConfig = (patch) => {
10807
- if ("loop" in patch) {
10808
- const lp = patch.loop;
10809
- if (lp && typeof lp === "object" && typeof lp.task === "string" && lp.task.trim()) {
10810
- const oracle = typeof lp.oracle === "string" && lp.oracle.trim() ? lp.oracle.trim() : void 0;
10811
- const maxIterations = typeof lp.max_iterations === "number" ? lp.max_iterations : 20;
10812
- const evaluator = lp.evaluator !== false;
10807
+ const _gateKey = "loop" in patch ? "loop" : "supervisor" in patch ? "supervisor" : null;
10808
+ if (_gateKey) {
10809
+ let cfg = patch[_gateKey];
10810
+ if (_gateKey === "supervisor" && cfg && typeof cfg === "object") {
10811
+ const j = Array.isArray(cfg.judges) ? cfg.judges : [];
10812
+ const oj = j.find((x) => x?.type === "oracle");
10813
+ const pj = j.find((x) => x?.type === "parent");
10814
+ cfg = {
10815
+ // legacy supervisor = hot-plug (no task); criteria → until.
10816
+ until: cfg.criteria,
10817
+ oracle: (oj && typeof oj.cmd === "string" ? oj.cmd : void 0) || cfg.oracle,
10818
+ evaluator: j.length === 0 || j.some((x) => x?.type === "agent"),
10819
+ parent: pj?.parent || cfg.parent,
10820
+ max_iterations: cfg.max_rounds
10821
+ };
10822
+ }
10823
+ const task = cfg && typeof cfg.task === "string" && cfg.task.trim() ? cfg.task.trim() : void 0;
10824
+ const until = cfg && typeof cfg.until === "string" && cfg.until.trim() ? cfg.until.trim() : cfg && typeof cfg.criteria === "string" && cfg.criteria.trim() ? cfg.criteria.trim() : void 0;
10825
+ if (cfg && typeof cfg === "object" && (task || until)) {
10826
+ const oracle = typeof cfg.oracle === "string" && cfg.oracle.trim() ? cfg.oracle.trim() : void 0;
10827
+ const evaluator = cfg.evaluator !== false;
10828
+ const maxIterations = typeof cfg.max_iterations === "number" ? cfg.max_iterations : typeof cfg.max_rounds === "number" ? cfg.max_rounds : 20;
10813
10829
  const ok = initLoop(directory, {
10814
- task: lp.task.trim(),
10815
- criteria: typeof lp.criteria === "string" && lp.criteria.trim() ? lp.criteria.trim() : void 0,
10830
+ task: task || until,
10831
+ // LOOP.md goal = the task, or the until-criteria when hot-plugging
10832
+ criteria: until,
10816
10833
  oracle,
10817
10834
  maxIterations,
10818
10835
  evaluator,
@@ -10820,24 +10837,23 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
10820
10837
  });
10821
10838
  if (ok) {
10822
10839
  const existingQueue = getMetadata().messageQueue || [];
10823
- const kickoff = "Begin the loop. Read LOOP.md and work on the task until the exit conditions are met. Do not stop early \u2014 an independent Stop gate will re-check before the loop can end.";
10824
- setMetadata((m) => ({
10825
- ...m,
10826
- messageQueue: [...existingQueue, {
10827
- id: randomUUID$1(),
10828
- text: kickoff,
10829
- // Compact chat badge; full task lives in LOOP.md. Show an ellipsis
10830
- // when truncated so it's clear the preview is shortened, not the task.
10831
- displayText: `\u{1F501} Loop started: ${lp.task.trim().slice(0, 100)}${lp.task.trim().length > 100 ? "\u2026" : ""}`,
10832
- createdAt: Date.now()
10833
- }]
10834
- }));
10840
+ if (task) {
10841
+ const kickoff = "Begin the loop. Read LOOP.md and work on the task until the exit conditions are met. Do not stop early \u2014 an independent Stop gate will re-check before the loop can end.";
10842
+ setMetadata((m) => ({ ...m, messageQueue: [...existingQueue, { id: randomUUID$1(), text: kickoff, displayText: `\u{1F501} Loop started: ${task.slice(0, 100)}${task.length > 100 ? "\u2026" : ""}`, createdAt: Date.now() }] }));
10843
+ } else {
10844
+ const idle = getMetadata().lifecycleState === "idle";
10845
+ if (idle) {
10846
+ const nudge = `A loop gate is now active. Keep working until this is met: ${until}
10847
+ Or verify and finish \u2014 an independent Stop gate re-checks before you can stop.`;
10848
+ setMetadata((m) => ({ ...m, messageQueue: [...existingQueue, { id: randomUUID$1(), text: nudge, displayText: `\u{1F501} Loop until: ${until.slice(0, 100)}${until.length > 100 ? "\u2026" : ""}`, createdAt: Date.now() }] }));
10849
+ }
10850
+ }
10851
+ onLoopActivated?.();
10835
10852
  sessionService.pushMessage(
10836
- { type: "message", message: `\u{1F501} Loop started \u2014 iterating until done (oracle: ${oracle || "none"}, evaluator ${evaluator ? "on" : "off"}, max ${maxIterations}).` },
10853
+ { type: "message", message: `\u{1F501} Loop active \u2014 ${task ? "iterating on the task" : "gating current work"} (until: ${until || "done"}, oracle: ${oracle || "none"}, evaluator ${evaluator ? "on" : "off"}, max ${maxIterations}).` },
10837
10854
  "event"
10838
10855
  );
10839
- logger.log(`[svampConfig] Loop started: "${lp.task.trim().slice(0, 50)}..."`);
10840
- onLoopActivated?.();
10856
+ logger.log(`[svampConfig] Loop active (${task ? "kickoff" : "hot-plug"}): until="${(until || task || "").slice(0, 50)}"`);
10841
10857
  } else {
10842
10858
  sessionService.pushMessage(
10843
10859
  { type: "message", message: "Failed to start loop \u2014 the loop skill could not be located. Reinstall with: svamp skills install loop --force", level: "error" },
@@ -10850,54 +10866,7 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
10850
10866
  sessionService.pushMessage({ type: "message", message: "Loop cancelled." }, "event");
10851
10867
  logger.log(`[svampConfig] Loop cancelled`);
10852
10868
  }
10853
- const { loop: _, ...restPatch } = patch;
10854
- patch = restPatch;
10855
- }
10856
- if ("supervisor" in patch) {
10857
- const sup = patch.supervisor;
10858
- if (sup && typeof sup === "object" && typeof sup.criteria === "string" && sup.criteria.trim()) {
10859
- const criteria = sup.criteria.trim();
10860
- const judges = Array.isArray(sup.judges) ? sup.judges : [];
10861
- const oracleJudge = judges.find((j) => j?.type === "oracle");
10862
- const oracle = oracleJudge && typeof oracleJudge.cmd === "string" && oracleJudge.cmd.trim() ? oracleJudge.cmd.trim() : typeof sup.oracle === "string" && sup.oracle.trim() ? sup.oracle.trim() : void 0;
10863
- const hasAgentJudge = judges.length === 0 || judges.some((j) => j?.type === "agent");
10864
- const maxRounds = typeof sup.max_rounds === "number" ? sup.max_rounds : 20;
10865
- const ok = initLoop(directory, {
10866
- task: criteria,
10867
- // P1: criteria seeds LOOP.md as the goal (skill carries it as config.criteria in #30)
10868
- criteria,
10869
- oracle,
10870
- maxIterations: maxRounds,
10871
- evaluator: hasAgentJudge,
10872
- sessionId
10873
- });
10874
- if (ok) {
10875
- const judgeLabel = judges.length ? judges.map((j) => j.type).join("\u2192") : "agent";
10876
- const idle = getMetadata().lifecycleState === "idle";
10877
- if (idle) {
10878
- const existingQueue = getMetadata().messageQueue || [];
10879
- const nudge = `You are now under supervision. Success criteria: ${criteria}
10880
- Continue working until they are met, or verify and finish \u2014 an independent Stop gate will re-check before you can stop.`;
10881
- setMetadata((m) => ({ ...m, messageQueue: [...existingQueue, { id: randomUUID$1(), text: nudge, displayText: `\u{1F441} Supervisor attached`, createdAt: Date.now() }] }));
10882
- onLoopActivated?.();
10883
- }
10884
- sessionService.pushMessage(
10885
- { type: "message", message: `\u{1F441} Supervisor attached \u2014 judges: ${judgeLabel}, max ${maxRounds}. Criteria: ${criteria.slice(0, 120)}${criteria.length > 120 ? "\u2026" : ""}` },
10886
- "event"
10887
- );
10888
- logger.log(`[svampConfig] Supervisor attached (judges: ${judgeLabel}, max ${maxRounds})`);
10889
- } else {
10890
- sessionService.pushMessage(
10891
- { type: "message", message: "Failed to attach supervisor \u2014 the loop skill could not be located. Reinstall with: svamp skills install loop --force", level: "error" },
10892
- "event"
10893
- );
10894
- }
10895
- } else {
10896
- deactivateLoop(directory, sessionId);
10897
- sessionService.pushMessage({ type: "message", message: "Supervisor detached." }, "event");
10898
- logger.log(`[svampConfig] Supervisor detached`);
10899
- }
10900
- const { supervisor: _s, ...restPatch } = patch;
10869
+ const { [_gateKey]: _drop, ...restPatch } = patch;
10901
10870
  patch = restPatch;
10902
10871
  }
10903
10872
  if ("checklist" in patch) {
@@ -11359,7 +11328,7 @@ async function startDaemon(options) {
11359
11328
  const list = loadExposedTunnels().filter((t) => t.name !== name);
11360
11329
  saveExposedTunnels(list);
11361
11330
  }
11362
- const { ServeManager } = await import('./serveManager-CUcXJklY.mjs');
11331
+ const { ServeManager } = await import('./serveManager-cPgahjYE.mjs');
11363
11332
  const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
11364
11333
  ensureAutoInstalledSkills(logger).catch(() => {
11365
11334
  });
@@ -14047,7 +14016,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
14047
14016
  const specs = loadExposedTunnels();
14048
14017
  if (specs.length === 0) return;
14049
14018
  logger.log(`[exposed-tunnels] Restoring ${specs.length} tunnel(s) from ${EXPOSED_TUNNELS_FILE}`);
14050
- const { FrpcTunnel } = await import('./frpc-BElGm4ku.mjs');
14019
+ const { FrpcTunnel } = await import('./frpc-BpBOWRbL.mjs');
14051
14020
  for (const spec of specs) {
14052
14021
  if (tunnels.has(spec.name)) continue;
14053
14022
  try {
@@ -1,4 +1,4 @@
1
- import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { X as generateFriendlyName, m as shortId, c as connectToHypha, a as createSessionStore, r as registerMachineService, Y as generateHookSettings } from './run-BlfxwBzW.mjs';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { X as generateFriendlyName, m as shortId, c as connectToHypha, a as createSessionStore, r as registerMachineService, Y as generateHookSettings } from './run-BaTwfE1Q.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';
@@ -54,7 +54,7 @@ async function handleServeCommand() {
54
54
  }
55
55
  }
56
56
  async function serveAdd(args, machineId) {
57
- const { connectAndGetMachine } = await import('./commands-jr6pFEEu.mjs');
57
+ const { connectAndGetMachine } = await import('./commands-BV30A1zt.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-jr6pFEEu.mjs');
96
+ const { connectAndGetMachine } = await import('./commands-BV30A1zt.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-jr6pFEEu.mjs');
185
+ const { connectAndGetMachine } = await import('./commands-BV30A1zt.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-jr6pFEEu.mjs');
205
+ const { connectAndGetMachine } = await import('./commands-BV30A1zt.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-jr6pFEEu.mjs');
238
+ const { connectAndGetMachine } = await import('./commands-BV30A1zt.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-BlfxwBzW.mjs';
7
+ import { k as getHyphaServerUrl, S as ServeAuth, l as hasCookieToken } from './run-BaTwfE1Q.mjs';
8
8
  import 'os';
9
9
  import 'fs/promises';
10
10
  import 'url';
@@ -713,7 +713,7 @@ class ServeManager {
713
713
  const mount = this.mounts.get(mountName);
714
714
  const subdomainOverride = mount?.access === "link" && mount.linkToken ? /* @__PURE__ */ new Map([[this.port, `static-${subdomainSafe}-${mount.linkToken}`]]) : void 0;
715
715
  try {
716
- const { FrpcTunnel } = await import('./frpc-BElGm4ku.mjs');
716
+ const { FrpcTunnel } = await import('./frpc-BpBOWRbL.mjs');
717
717
  let tunnel;
718
718
  tunnel = new FrpcTunnel({
719
719
  name: tunnelName,
@@ -1,4 +1,4 @@
1
- import { R as READ_ONLY_TOOLS, z as loadMachineContext, A as buildMachineInstructions, B as machineToolsForRole, C as buildMachineTools } from './run-BlfxwBzW.mjs';
1
+ import { R as READ_ONLY_TOOLS, z as loadMachineContext, A as buildMachineInstructions, B as machineToolsForRole, C as buildMachineTools } from './run-BaTwfE1Q.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.127",
3
+ "version": "0.2.129",
4
4
  "description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
5
5
  "author": "Amun AI AB",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -20,7 +20,7 @@
20
20
  "scripts": {
21
21
  "build": "rm -rf dist bin/skills && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/loop bin/skills/loop && cp -r ../../skills/crew bin/skills/crew && tsc --noEmit && pkgroll",
22
22
  "typecheck": "tsc --noEmit",
23
- "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-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.mjs && npx tsx test/test-checklist.mjs && npx tsx test/test-short-id.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 && 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-checklist-watchdog.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-channel-binding.mjs && npx tsx test/test-channel-identity.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 && npx tsx test/test-crew-merge.mjs",
23
+ "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-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.mjs && npx tsx test/test-checklist.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 && 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-checklist-watchdog.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-channel-binding.mjs && npx tsx test/test-channel-identity.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 && npx tsx test/test-crew-merge.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",