svamp-cli 0.2.143 → 0.2.145

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.
@@ -482,6 +482,39 @@ function buildTools(deps, skills) {
482
482
  return `Started a loop: "${str$1(a?.task).slice(0, 80)}".`;
483
483
  }
484
484
  },
485
+ {
486
+ name: "set_checklist",
487
+ readOnly: false,
488
+ description: "Create or replace the bound session's checklist (the loop's success criteria) and start the self-verifying loop. Each item is a concrete, checkable requirement. ONLY after the caller confirmed the proposal.",
489
+ parameters: { type: "object", properties: {
490
+ items: { type: "array", description: "The checklist items (success criteria).", items: { type: "object", properties: {
491
+ text: { type: "string", description: "A concrete, checkable requirement." },
492
+ oracle: { type: "string", description: "Optional pass/fail command verifying this item." }
493
+ }, required: ["text"], additionalProperties: false } },
494
+ prompt: { type: "string", description: "Optional overall goal/instruction for the loop." },
495
+ max_iterations: { type: "number", description: "Iteration ceiling (default 20)." }
496
+ }, required: ["items"], additionalProperties: false },
497
+ run: async (a) => {
498
+ const rawItems = Array.isArray(a?.items) ? a.items : [];
499
+ const items = rawItems.map((i) => ({ text: str$1(i?.text), oracle: i?.oracle ? str$1(i.oracle) : void 0 })).filter((i) => i.text.trim());
500
+ if (!items.length) return "No checklist items were provided.";
501
+ const r = await deps.applyChecklist(items, {
502
+ prompt: a?.prompt ? str$1(a.prompt) : void 0,
503
+ maxIterations: typeof a?.max_iterations === "number" ? a.max_iterations : void 0
504
+ });
505
+ return r.ok ? `Set a ${items.length}-item checklist and started the loop.` : `Could not set the checklist: ${r.error || "unknown error"}.`;
506
+ }
507
+ },
508
+ {
509
+ name: "stop_loop",
510
+ readOnly: false,
511
+ description: "Stop/cancel the running loop in the bound session. ONLY after the caller asked to stop.",
512
+ parameters: { type: "object", properties: {}, additionalProperties: false },
513
+ run: async () => {
514
+ await deps.stopLoop();
515
+ return "Stopped the loop.";
516
+ }
517
+ },
485
518
  {
486
519
  name: "create_channel",
487
520
  readOnly: false,
@@ -631,11 +664,12 @@ You are WISE Agent, a fast, text-mode companion to the deep coding agent (Claude
631
664
  - run_bash \u2014 run a shell command on the session's machine (when granted).
632
665
  - send_to_session \u2014 hand a clear, reformulated instruction to the deep coding agent (when granted); pass wait=true to block for its reply.
633
666
  - create_routine / create_loop / create_channel \u2014 set up a scheduled/triggered routine, a self-verifying loop, or an inbound channel for this session (when granted). ALWAYS propose first and confirm before calling these (see below).
667
+ - set_checklist / stop_loop \u2014 turn the caller's goals into a tracked checklist (the loop's success criteria) and start the self-verifying loop, or stop a running loop (when granted). Each item is one concrete, checkable requirement (optional per-item oracle command). ALWAYS propose the items first and confirm before calling.
634
668
 
635
669
  # Instructions
636
670
  - Answer general questions and questions about yourself directly. Use tools only to act on the machine/session.
637
671
  - Take the cheap path: read state directly; delegate anything LONG to summarize_session \u2014 keep your own context small.
638
- - To create a routine, loop, or channel: first restate the resolved config in one line and ask the caller to reply "confirm" to proceed. Only call create_routine / create_loop / create_channel after they confirm in a follow-up message. Never create without confirmation.
672
+ - To create a routine, loop, channel, or checklist: first restate the resolved config (for set_checklist, list the items) in one line and ask the caller to reply "confirm" to proceed. Only call create_routine / create_loop / create_channel / set_checklist / stop_loop after they confirm in a follow-up message. Never create or stop without confirmation.
639
673
  - For destructive actions (deleting, stopping, killing), require a verified caller and confirm intent; for safe reads, just do it.
640
674
  - If a tool fails or returns nothing useful, say so plainly \u2014 never fabricate a result.
641
675
  - Report the outcome in one line.`;
@@ -1023,6 +1057,28 @@ function buildSessionDeps(rpc, opts = {}) {
1023
1057
  }
1024
1058
  }, ctx);
1025
1059
  },
1060
+ async applyChecklist(items, opts2) {
1061
+ const built = (items || []).filter((i) => i?.text && i.text.trim()).map((i, idx) => ({
1062
+ id: `wise-${Date.now().toString(36)}-${idx}`,
1063
+ text: i.text.trim(),
1064
+ status: "todo",
1065
+ disposition: "inline",
1066
+ scope: "session",
1067
+ ...i.oracle && i.oracle.trim() ? { verify: { type: "command", text: i.oracle.trim() }, oracle: i.oracle.trim() } : {}
1068
+ }));
1069
+ await rpc.updateConfig({
1070
+ checklist: built,
1071
+ checklistConfig: {
1072
+ ...opts2?.prompt ? { prompt: opts2.prompt } : {},
1073
+ maxIterations: opts2?.maxIterations ?? 20,
1074
+ evaluator: true
1075
+ }
1076
+ }, ctx);
1077
+ return { ok: true };
1078
+ },
1079
+ async stopLoop() {
1080
+ await rpc.updateConfig({ loop: null }, ctx);
1081
+ },
1026
1082
  async saveChannel(channel) {
1027
1083
  return await rpc.saveChannel(channel, ctx);
1028
1084
  }
@@ -1405,14 +1461,15 @@ const res = await get_service("${svc}").send({ channel: "${channel.id}", message
1405
1461
  if (res.status === "completed") return res.reply; // answered now \u2014 done
1406
1462
  let cursor = 0; // else poll for the async reply
1407
1463
  while (true) {
1408
- const r = await get_service("${svc}").receive({ channel: "${channel.id}", key: "${key}", cursor, wait: 25 });
1464
+ const r = await get_service("${svc}").receive({ channel: "${channel.id}", key: "${key}", correlationId: res.correlationId, cursor, wait: 25 });
1409
1465
  cursor = r.cursor;
1410
- for (const reply of r.replies) if (reply.correlationId === res.correlationId) return reply.body;
1466
+ if (r.replies.length) return r.replies[0].body; // server matched it by correlationId
1411
1467
  }
1412
1468
  \`\`\`
1413
- **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).
1469
+ **HTTP:** \`POST ${recvUrl}\` with \`{"kwargs": {"channel": "${channel.id}", "key": "${key}", "correlationId": "<from send>", "cursor": 0, "wait": 25}}\` (long-poll), or stream \`GET <channel-http>/channel/${channel.id}/events?key=${key}\` (SSE).
1470
+ > Tip: always pass the \`correlationId\` from \`send\` to \`receive\` \u2014 it is the reply key, so the reply finds you with no identity matching. Omit it and \`receive\` falls back to matching your \`from\`, which you must then supply identically on every call.
1414
1471
 
1415
- ### Agent-to-agent: get the reply in your own inbox
1472
+ ### Agent-to-agent: also get the reply in your own inbox
1416
1473
  If **you are a Svamp agent session**, pass \`reply_to: { session: "<your-session-id>" }\`
1417
1474
  to \`send()\`. The reply is then delivered straight to **your session's inbox**
1418
1475
  (\`inbox reply\`-able, persistent) \u2014 symmetric agent\u2194agent messaging. Without
@@ -1577,10 +1634,19 @@ class ChannelOutbox {
1577
1634
  this.emitter.emit(channelId, reply);
1578
1635
  return reply;
1579
1636
  }
1580
- /** Replies for `to` on `channelId` with seq > cursor (optionally one correlationId). */
1637
+ /**
1638
+ * Replies on `channelId` with seq > cursor.
1639
+ *
1640
+ * The `correlationId` is the reply KEY — the capability `send` returns to the caller.
1641
+ * When given, we match on it ALONE: knowing the unguessable id proves the caller owns
1642
+ * that conversation, so we skip the fragile sender-identity match (which required the
1643
+ * caller to pass the exact same `from` on send and receive — the #1 source of
1644
+ * "outbox is empty" confusion). Without a correlationId, fall back to "every reply
1645
+ * addressed to my identity" (for unsolicited pushes / polling all my mail).
1646
+ */
1581
1647
  since(channelId, cursor, to, correlationId) {
1582
1648
  this._evict(channelId);
1583
- return (this.byChannel.get(channelId) || []).filter((r) => r.seq > cursor && r.to === to && (!correlationId || r.correlationId === correlationId));
1649
+ return (this.byChannel.get(channelId) || []).filter((r) => r.seq > cursor && (correlationId ? r.correlationId === correlationId : r.to === to));
1584
1650
  }
1585
1651
  /** Highest seq on a channel (the cursor a caller gets back). */
1586
1652
  cursor(channelId) {
@@ -1595,7 +1661,7 @@ class ChannelOutbox {
1595
1661
  if (ready.length) return Promise.resolve({ replies: ready, cursor: this.cursor(channelId) });
1596
1662
  return new Promise((resolve) => {
1597
1663
  const onReply = (r) => {
1598
- if (r.to !== to || correlationId && r.correlationId !== correlationId) return;
1664
+ if (correlationId ? r.correlationId !== correlationId : r.to !== to) return;
1599
1665
  cleanup();
1600
1666
  resolve({ replies: this.since(channelId, cursor, to, correlationId), cursor: this.cursor(channelId) });
1601
1667
  };
@@ -2705,7 +2771,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
2705
2771
  const tunnels = handlers.tunnels;
2706
2772
  if (!tunnels) throw new Error("Tunnel management not available");
2707
2773
  if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
2708
- const { FrpcTunnel } = await import('./frpc-Co_GG6Hr.mjs');
2774
+ const { FrpcTunnel } = await import('./frpc-MevmUvAf.mjs');
2709
2775
  const tunnel = new FrpcTunnel({
2710
2776
  name: params.name,
2711
2777
  ports: params.ports,
@@ -3055,7 +3121,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3055
3121
  }
3056
3122
  const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
3057
3123
  const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
3058
- const { toolsForRole } = await import('./sideband-C2OWGzaU.mjs');
3124
+ const { toolsForRole } = await import('./sideband-xxUkkrOq.mjs');
3059
3125
  const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
3060
3126
  return fmt(r2);
3061
3127
  }
@@ -3154,7 +3220,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3154
3220
  if (r.error || !r.sender) return { error: r.error || "unauthorized" };
3155
3221
  const callId = "call_" + Math.random().toString(16).slice(2, 12);
3156
3222
  const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
3157
- const { queryCore } = await import('./commands-DNTaAEcq.mjs');
3223
+ const { queryCore } = await import('./commands-Ch1QWOw5.mjs');
3158
3224
  const timeout = c.reply?.timeout_sec || 120;
3159
3225
  let result;
3160
3226
  try {
@@ -4249,7 +4315,7 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
4249
4315
  channel: c.name,
4250
4316
  subject: c.name,
4251
4317
  ...replySession ? { fromSession: replySession, threadId: callId } : {},
4252
- ...queue && !replySession ? { channelId: c.id, correlationId: callId } : {}
4318
+ ...queue ? { channelId: c.id, correlationId: callId } : {}
4253
4319
  };
4254
4320
  await rpcHandlers.sendInboxMessage(inboxMsg, ownerCtx);
4255
4321
  channelStore.recordCall(c.id, { sender: r.sender.name, verified: r.sender.verified, callId, outcome: "delivered" });
@@ -11615,7 +11681,7 @@ async function startDaemon(options) {
11615
11681
  saveExposedTunnels(list);
11616
11682
  }
11617
11683
  async function createExposedTunnel(spec) {
11618
- const { FrpcTunnel } = await import('./frpc-Co_GG6Hr.mjs');
11684
+ const { FrpcTunnel } = await import('./frpc-MevmUvAf.mjs');
11619
11685
  const tunnel = new FrpcTunnel({
11620
11686
  name: spec.name,
11621
11687
  ports: spec.ports,
@@ -11635,7 +11701,7 @@ async function startDaemon(options) {
11635
11701
  return tunnel;
11636
11702
  }
11637
11703
  const tunnelRecreateState = /* @__PURE__ */ new Map();
11638
- const { ServeManager } = await import('./serveManager-CKpIYSav.mjs');
11704
+ const { ServeManager } = await import('./serveManager-CK5APPYi.mjs');
11639
11705
  const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
11640
11706
  ensureAutoInstalledSkills(logger).catch(() => {
11641
11707
  });
@@ -1,4 +1,4 @@
1
- import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { _ as composeSessionId, $ as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a0 as generateHookSettings } from './run-DsuM6onp.mjs';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { _ as composeSessionId, $ as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a0 as generateHookSettings } from './run-C_xwiN7C.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-DNTaAEcq.mjs');
57
+ const { connectAndGetMachine } = await import('./commands-Ch1QWOw5.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-DNTaAEcq.mjs');
96
+ const { connectAndGetMachine } = await import('./commands-Ch1QWOw5.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-DNTaAEcq.mjs');
185
+ const { connectAndGetMachine } = await import('./commands-Ch1QWOw5.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-DNTaAEcq.mjs');
205
+ const { connectAndGetMachine } = await import('./commands-Ch1QWOw5.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-DNTaAEcq.mjs');
238
+ const { connectAndGetMachine } = await import('./commands-Ch1QWOw5.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-DsuM6onp.mjs';
7
+ import { k as getHyphaServerUrl, S as ServeAuth, l as hasCookieToken } from './run-C_xwiN7C.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-Co_GG6Hr.mjs');
736
+ const { FrpcTunnel } = await import('./frpc-MevmUvAf.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, z as loadMachineContext, A as buildMachineInstructions, B as machineToolsForRole, C as buildMachineTools } from './run-DsuM6onp.mjs';
1
+ import { R as READ_ONLY_TOOLS, z as loadMachineContext, A as buildMachineInstructions, B as machineToolsForRole, C as buildMachineTools } from './run-C_xwiN7C.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.143",
3
+ "version": "0.2.145",
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-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-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-inbox-cross-machine.mjs && npx tsx test/test-checklist.mjs && npx tsx test/test-checklist-cli.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-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-shared-session-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-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-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-inbox-cross-machine.mjs && npx tsx test/test-checklist.mjs && npx tsx test/test-checklist-cli.mjs && npx tsx test/test-issue-store.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-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-shared-session-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",