svamp-cli 0.2.176 → 0.2.178

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.
Files changed (31) hide show
  1. package/bin/skills/loop/SKILL.md +1 -1
  2. package/dist/{agentCommands-kchQlyIS.mjs → agentCommands-BIn9btot.mjs} +5 -5
  3. package/dist/{auth-oH2upjHc.mjs → auth-DY_tcgLY.mjs} +1 -1
  4. package/dist/cli.mjs +60 -64
  5. package/dist/{commands-CbrJ16x7.mjs → commands-BTyp-k7w.mjs} +5 -5
  6. package/dist/{commands-Cu6nPOsD.mjs → commands-BfdkS7Iy.mjs} +10 -10
  7. package/dist/{commands-USG2Z4XP.mjs → commands-CdMEMD_c.mjs} +1 -1
  8. package/dist/{commands-CXtkDntj.mjs → commands-J9WOCzDG.mjs} +2 -2
  9. package/dist/{commands-DGkX7Kcr.mjs → commands-MjIkmrmC.mjs} +1 -1
  10. package/dist/{commands-DHbv5qMP.mjs → commands-WgUO8QeK.mjs} +1 -1
  11. package/dist/{commands-9LT5p45A.mjs → commands-kepmDOXD.mjs} +2 -2
  12. package/dist/{fleet-BcS8xNkk.mjs → fleet-CDllUpMj.mjs} +1 -1
  13. package/dist/{frpc-Bm7BrLan.mjs → frpc-D4x74mG6.mjs} +1 -1
  14. package/dist/{headlessCli-B4CVNI7S.mjs → headlessCli-CmBCGmNV.mjs} +2 -2
  15. package/dist/index.mjs +1 -1
  16. package/dist/{package-DndVS0ln.mjs → package-B-yM7SvY.mjs} +2 -2
  17. package/dist/{rpc-BbaS0cLh.mjs → rpc-2TJ6ipXZ.mjs} +7 -7
  18. package/dist/{rpc-DVNDMfl8.mjs → rpc-DBH2bo8s.mjs} +1 -1
  19. package/dist/{run-BmpyfykC.mjs → run-B-1WDymr.mjs} +31 -409
  20. package/dist/{run-Cr1yt_tm.mjs → run-BVNBqt1C.mjs} +1 -1
  21. package/dist/{scheduler-BusuolPD.mjs → scheduler-C3j8Tghm.mjs} +1 -1
  22. package/dist/{serveCommands-Crb8ENkv.mjs → serveCommands-BFWBxK8L.mjs} +5 -5
  23. package/dist/{serveManager-DQByExEO.mjs → serveManager-DXm1bdoU.mjs} +2 -2
  24. package/dist/{sideband-B4L2zR2U.mjs → sideband-Da1VG2iq.mjs} +1 -1
  25. package/package.json +2 -2
  26. package/bin/skills/loop/bin/routine-cli.mjs +0 -121
  27. package/bin/skills/loop/bin/routine-runner.mjs +0 -125
  28. package/bin/skills/loop/bin/routine-store.mjs +0 -49
  29. package/bin/skills/loop/routines.process.yaml +0 -20
  30. package/bin/skills/loop/test/test-routine-engine.mjs +0 -122
  31. package/dist/commands-DBi7fxGX.mjs +0 -223
@@ -436,33 +436,8 @@ function buildTools(deps, skills) {
436
436
  return "(handed off to the deep agent \u2014 it works asynchronously and will report the result back; call get_context to poll progress)";
437
437
  }
438
438
  },
439
- // Create routine / loop / channel. ONLY call after the caller confirmed the
440
- // proposed config (see the propose-then-confirm instruction in context.ts).
441
- {
442
- name: "create_routine",
443
- readOnly: false,
444
- description: "Create a session-scoped routine (cron schedule or webhook \u2192 message or loop). ONLY after the caller confirmed the proposal.",
445
- parameters: { type: "object", properties: {
446
- name: { type: "string", description: "Short human name." },
447
- cron: { type: "string", description: 'Cron for a schedule trigger, e.g. "0 9 * * 1-5". Omit for a webhook.' },
448
- tz: { type: "string", description: "IANA timezone for the schedule." },
449
- action_kind: { type: "string", enum: ["message", "loop"], description: '"message" sends text to this session; "loop" starts a self-verifying loop.' },
450
- message: { type: "string", description: "For message action: the text/template to deliver." },
451
- task: { type: "string", description: "For loop action: the loop task." },
452
- oracle: { type: "string", description: "For loop action: optional pass/fail command." }
453
- }, required: ["name", "action_kind"], additionalProperties: false },
454
- run: async (a) => {
455
- const cron = a?.cron ? str$1(a.cron) : "";
456
- const routine = {
457
- name: str$1(a?.name),
458
- enabled: true,
459
- trigger: cron ? { type: "schedule", cron, ...a?.tz ? { tz: str$1(a.tz) } : {} } : { type: "webhook" },
460
- action: str$1(a?.action_kind) === "loop" ? { kind: "loop", task_template: str$1(a?.task), loop: { task: str$1(a?.task), ...a?.oracle ? { oracle: str$1(a.oracle) } : {} } } : { kind: "message", template: str$1(a?.message) }
461
- };
462
- const r = await deps.saveRoutine(routine);
463
- return r.success ? `Created routine "${str$1(a?.name)}" (${r.routine?.id || "saved"}).` : `Could not create routine: ${r.error || "unknown error"}.`;
464
- }
465
- },
439
+ // Create loop / channel. ONLY call after the caller confirmed the proposed
440
+ // config (see the propose-then-confirm instruction in context.ts).
466
441
  {
467
442
  name: "create_loop",
468
443
  readOnly: false,
@@ -664,13 +639,13 @@ You are WISE Agent, a fast, text-mode companion to the deep coding agent (Claude
664
639
  - use_skill \u2014 load a project skill's full steps by name, then carry them out with run_bash.
665
640
  - run_bash \u2014 run a shell command on the session's machine (when granted).
666
641
  - send_to_session \u2014 hand a clear, reformulated instruction to the deep coding agent (when granted); pass wait=true to block for its reply.
667
- - 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).
642
+ - create_loop / create_channel \u2014 start a self-verifying loop, or set up an inbound channel for this session (when granted). For SCHEDULED automation, hand the deep agent a "svamp workflow add \u2026 --on schedule --cron ..." instruction via send_to_session. ALWAYS propose first and confirm before calling these (see below).
668
643
  - set_checklist / stop_loop \u2014 turn the caller's goals into tracked ISSUES (the backlog = the loop's success criteria) and start the self-verifying loop, or stop a running loop (when granted). Each item becomes one concrete, checkable issue (optional per-item oracle command \u2192 its verify-cmd). ALWAYS propose the items first and confirm before calling.
669
644
 
670
645
  # Instructions
671
646
  - Answer general questions and questions about yourself directly. Use tools only to act on the machine/session.
672
647
  - Take the cheap path: read state directly; delegate anything LONG to summarize_session \u2014 keep your own context small.
673
- - 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.
648
+ - To create a 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_loop / create_channel / set_checklist / stop_loop after they confirm in a follow-up message. Never create or stop without confirmation.
674
649
  - For destructive actions (deleting, stopping, killing), require a verified caller and confirm intent; for safe reads, just do it.
675
650
  - If a tool fails or returns nothing useful, say so plainly \u2014 never fabricate a result.
676
651
  - Report the outcome in one line.`;
@@ -1059,9 +1034,6 @@ function buildSessionDeps(rpc, opts = {}) {
1059
1034
  latestMessage: latestText
1060
1035
  };
1061
1036
  },
1062
- async saveRoutine(routine) {
1063
- return await rpc.saveRoutine(routine, ctx);
1064
- },
1065
1037
  async startLoop(cfg) {
1066
1038
  await rpc.updateConfig({
1067
1039
  loop: {
@@ -1234,37 +1206,6 @@ function cronMatches(expr, date) {
1234
1206
  if (c.domRestricted && c.dowRestricted) return domOk || dowOk;
1235
1207
  return domOk && dowOk;
1236
1208
  }
1237
- function inZone(date, tz) {
1238
- if (!tz) return date;
1239
- try {
1240
- const p = new Intl.DateTimeFormat("en-US", {
1241
- timeZone: tz,
1242
- hour12: false,
1243
- year: "numeric",
1244
- month: "2-digit",
1245
- day: "2-digit",
1246
- hour: "2-digit",
1247
- minute: "2-digit"
1248
- }).formatToParts(date).reduce((o, x) => {
1249
- o[x.type] = x.value;
1250
- return o;
1251
- }, {});
1252
- return new Date(+p.year, +p.month - 1, +p.day, +(p.hour === "24" ? 0 : p.hour), +p.minute);
1253
- } catch {
1254
- return date;
1255
- }
1256
- }
1257
- function nextFire(expr, from, tz) {
1258
- const c = parseCron(expr);
1259
- const d = new Date(from.getTime());
1260
- d.setSeconds(0, 0);
1261
- d.setMinutes(d.getMinutes() + 1);
1262
- for (let i = 0; i < 366 * 24 * 60; i++) {
1263
- if (cronMatches(c, tz ? inZone(d, tz) : d)) return new Date(d.getTime());
1264
- d.setMinutes(d.getMinutes() + 1);
1265
- }
1266
- return null;
1267
- }
1268
1209
  function resolvePath(ctx, path) {
1269
1210
  return path.split(".").reduce((o, k) => o == null ? void 0 : o[k], ctx);
1270
1211
  }
@@ -1274,42 +1215,9 @@ function renderTemplate(template, ctx) {
1274
1215
  return v == null ? "" : typeof v === "object" ? JSON.stringify(v) : String(v);
1275
1216
  });
1276
1217
  }
1277
- const TRIGGER_TYPES = ["manual", "schedule", "webhook", "api"];
1278
- const ACTION_KINDS = ["message", "loop", "verify"];
1279
- const OVERLAP = ["queue", "skip", "replace"];
1280
- function validateRoutine(r) {
1281
- const errs = [];
1282
- if (!r || typeof r !== "object") return ["routine must be an object"];
1283
- if (!r.session_id) errs.push("session_id required");
1284
- if (!r.name) errs.push("name required");
1285
- const t = r.trigger;
1286
- if (!t || !TRIGGER_TYPES.includes(t.type)) errs.push(`trigger.type must be one of ${TRIGGER_TYPES.join("|")}`);
1287
- if (t?.type === "schedule") {
1288
- try {
1289
- parseCron(t.cron);
1290
- } catch (e) {
1291
- errs.push(`trigger.cron: ${e.message}`);
1292
- }
1293
- if (t.missed && !["catchup", "skip"].includes(t.missed)) errs.push("trigger.missed must be catchup|skip");
1294
- }
1295
- const a = r.action;
1296
- if (!a || !ACTION_KINDS.includes(a.kind)) errs.push(`action.kind must be one of ${ACTION_KINDS.join("|")}`);
1297
- if (a?.kind === "message" && !a.template) errs.push("action.template required for message action");
1298
- if (a?.kind === "loop" && !a.loop && !a.task_template) errs.push("action.loop or action.task_template required for loop action");
1299
- if (a?.kind === "verify" && !r.dir) errs.push("a verify (backlog watchdog) action requires a dir (the project root holding .svamp/issues)");
1300
- if (r.overlap && !OVERLAP.includes(r.overlap)) errs.push(`overlap must be one of ${OVERLAP.join("|")}`);
1301
- if (r.bind && r.bind !== "stateful" && r.bind !== "stateless") errs.push('bind must be "stateful" or "stateless"');
1302
- if (r.bind === "stateless") {
1303
- if (!r.dir) errs.push("a stateless routine requires a dir (the project folder to spawn the one-shot in)");
1304
- if (a?.kind === "loop") errs.push('a stateless routine cannot use a loop action (needs a persistent session); use a message action or bind "stateful"');
1305
- }
1306
- if ((t?.type === "webhook" || t?.type === "api") && t.public && a?.kind === "loop")
1307
- errs.push("a public webhook/api may not use a loop action (unauthenticated task injection) \u2014 use a message action or require a key");
1308
- return errs;
1309
- }
1310
1218
 
1311
- const genId$1 = () => "c_" + randomBytes(5).toString("hex");
1312
- const genKey$1 = () => "ck_" + randomBytes(18).toString("base64url");
1219
+ const genId = () => "c_" + randomBytes(5).toString("hex");
1220
+ const genKey = () => "ck_" + randomBytes(18).toString("base64url");
1313
1221
  const DEFAULT_TEMPLATE = `<inbound-message from="\${sender.name}" sender-type="\${sender.kind}" verified="\${sender.verified}" channel="\${channel.name}" call-id="\${call.id}" at="\${now}">
1314
1222
  \${body.message}
1315
1223
  </inbound-message>`;
@@ -1392,7 +1300,7 @@ class ChannelStore {
1392
1300
  }
1393
1301
  save(channel) {
1394
1302
  const c = { enabled: true, bind: "dynamic", template: DEFAULT_TEMPLATE, last_calls: [], ...channel };
1395
- if (!c.id) c.id = genId$1();
1303
+ if (!c.id) c.id = genId();
1396
1304
  const errs = validateChannel(c);
1397
1305
  if (errs.length) throw new Error("invalid channel: " + errs.join("; "));
1398
1306
  mkdirSync(this.dir, { recursive: true });
@@ -1427,7 +1335,7 @@ class ChannelStore {
1427
1335
  const c = this.get(id);
1428
1336
  if (!c) return null;
1429
1337
  c.identity.callers = c.identity.callers || [];
1430
- const caller = { name, kind, key: genKey$1() };
1338
+ const caller = { name, kind, key: genKey() };
1431
1339
  c.identity.callers.push(caller);
1432
1340
  this.save(c);
1433
1341
  return caller;
@@ -2791,7 +2699,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
2791
2699
  const tunnels = handlers.tunnels;
2792
2700
  if (!tunnels) throw new Error("Tunnel management not available");
2793
2701
  if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
2794
- const { FrpcTunnel } = await import('./frpc-Bm7BrLan.mjs');
2702
+ const { FrpcTunnel } = await import('./frpc-D4x74mG6.mjs');
2795
2703
  const tunnel = new FrpcTunnel({
2796
2704
  name: params.name,
2797
2705
  ports: params.ports,
@@ -3238,7 +3146,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3238
3146
  }
3239
3147
  const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
3240
3148
  const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
3241
- const { toolsForRole } = await import('./sideband-B4L2zR2U.mjs');
3149
+ const { toolsForRole } = await import('./sideband-Da1VG2iq.mjs');
3242
3150
  const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
3243
3151
  return fmt(r2);
3244
3152
  }
@@ -3337,7 +3245,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3337
3245
  if (r.error || !r.sender) return { error: r.error || "unauthorized" };
3338
3246
  const callId = "call_" + Math.random().toString(16).slice(2, 12);
3339
3247
  const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
3340
- const { queryCore } = await import('./commands-DGkX7Kcr.mjs');
3248
+ const { queryCore } = await import('./commands-MjIkmrmC.mjs');
3341
3249
  const timeout = c.reply?.timeout_sec || 120;
3342
3250
  let result;
3343
3251
  try {
@@ -3481,209 +3389,6 @@ ${d?.error || "not found"}`;
3481
3389
  };
3482
3390
  }
3483
3391
 
3484
- function defaultRoutinesDir() {
3485
- return process.env.SVAMP_ROUTINES_DIR || join(homedir(), ".svamp", "routines");
3486
- }
3487
- const genId = () => "rt_" + randomBytes(5).toString("hex");
3488
- const genKey = () => randomBytes(18).toString("base64url");
3489
- class RoutineStore {
3490
- dir;
3491
- constructor(dir = defaultRoutinesDir()) {
3492
- this.dir = dir;
3493
- mkdirSync(dir, { recursive: true });
3494
- }
3495
- _path(id) {
3496
- return join(this.dir, `${id}.json`);
3497
- }
3498
- list(sessionId) {
3499
- const all = readdirSync(this.dir).filter((f) => f.endsWith(".json")).map((f) => {
3500
- try {
3501
- return JSON.parse(readFileSync(join(this.dir, f), "utf8"));
3502
- } catch {
3503
- return null;
3504
- }
3505
- }).filter((r) => !!r);
3506
- return sessionId ? all.filter((r) => r.session_id === sessionId) : all;
3507
- }
3508
- get(id) {
3509
- try {
3510
- return JSON.parse(readFileSync(this._path(id), "utf8"));
3511
- } catch {
3512
- return null;
3513
- }
3514
- }
3515
- save(routine) {
3516
- const r = { overlap: "queue", enabled: true, last_runs: [], ...routine };
3517
- if (!r.id) r.id = genId();
3518
- if ((r.trigger?.type === "webhook" || r.trigger?.type === "api") && !r.trigger.key) r.trigger.key = genKey();
3519
- const errs = validateRoutine(r);
3520
- if (errs.length) throw new Error("invalid routine: " + errs.join("; "));
3521
- const tmp = this._path(r.id) + ".tmp";
3522
- writeFileSync(tmp, JSON.stringify(r, null, 2));
3523
- renameSync(tmp, this._path(r.id));
3524
- return r;
3525
- }
3526
- remove(id) {
3527
- const p = this._path(id);
3528
- if (existsSync(p)) {
3529
- rmSync(p);
3530
- return true;
3531
- }
3532
- return false;
3533
- }
3534
- setEnabled(id, enabled) {
3535
- const r = this.get(id);
3536
- if (!r) return null;
3537
- r.enabled = enabled;
3538
- return this.save(r);
3539
- }
3540
- recordRun(id, entry) {
3541
- const r = this.get(id);
3542
- if (!r) return null;
3543
- r.last_runs = [{ firedAt: (/* @__PURE__ */ new Date()).toISOString(), via: "manual", delivered: "message", outcome: "ok", ...entry }, ...r.last_runs || []].slice(0, 20);
3544
- return this.save(r);
3545
- }
3546
- }
3547
-
3548
- const minuteKey = (d) => `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}-${d.getHours()}-${d.getMinutes()}`;
3549
- class RoutineRunner {
3550
- store;
3551
- deliver;
3552
- now;
3553
- onReplace;
3554
- log;
3555
- active = /* @__PURE__ */ new Set();
3556
- _firedMinute = /* @__PURE__ */ new Map();
3557
- constructor(o) {
3558
- this.store = o.store;
3559
- this.deliver = o.deliver;
3560
- this.now = o.now || (() => /* @__PURE__ */ new Date());
3561
- this.onReplace = o.onReplace;
3562
- this.log = o.log || (() => {
3563
- });
3564
- }
3565
- resolveAction(routine, payload = {}) {
3566
- const ctx = { ...payload, now: this.now().toISOString(), routine: { id: routine.id, name: routine.name } };
3567
- const a = routine.action;
3568
- if (a.kind === "message") return { kind: "message", text: renderTemplate(a.template || "", ctx) };
3569
- if (a.kind === "verify") return { kind: "verify" };
3570
- const task = renderTemplate(a.task_template || a.loop?.task || "", ctx);
3571
- return { kind: "loop", task, loop: a.loop };
3572
- }
3573
- markDone(id) {
3574
- this.active.delete(id);
3575
- }
3576
- // Deliveries counted today via a dedicated counter (NOT derived from the
3577
- // capped last_runs audit, which would undercount past 20 runs/day).
3578
- _deliveredToday(routine) {
3579
- const today = this.now().toDateString();
3580
- return routine.daily && routine.daily.date === today ? routine.daily.n : 0;
3581
- }
3582
- // `active` guards genuinely CONCURRENT in-flight deliveries only — the runner
3583
- // cannot observe a spawned loop's full duration (fire-and-forget), so it is
3584
- // cleared once delivery returns. `replace` calls onReplace (best-effort) then proceeds.
3585
- async fire(routine, payload = {}, via = "manual") {
3586
- if (!routine.enabled) return { skipped: "disabled" };
3587
- if (routine.action?.kind === "loop" && (routine.trigger?.type === "webhook" || routine.trigger?.type === "api") && routine.trigger?.public)
3588
- return { skipped: "forbidden (public webhook + loop)" };
3589
- const today = this.now().toDateString();
3590
- const r0 = this.store.get(routine.id) || routine;
3591
- const usedToday = r0.daily && r0.daily.date === today ? r0.daily.n : 0;
3592
- if (routine.daily_cap && usedToday >= routine.daily_cap) {
3593
- this.store.recordRun(routine.id, { via, delivered: routine.action.kind, outcome: "skipped (daily cap)" });
3594
- return { skipped: "daily_cap" };
3595
- }
3596
- if (this.active.has(routine.id)) {
3597
- if (routine.overlap === "skip") {
3598
- this.store.recordRun(routine.id, { via, delivered: routine.action.kind, outcome: "skipped (busy)" });
3599
- return { skipped: "busy" };
3600
- }
3601
- if (routine.overlap === "replace") {
3602
- try {
3603
- this.onReplace?.(routine.id);
3604
- } catch {
3605
- }
3606
- }
3607
- }
3608
- this.active.add(routine.id);
3609
- if (routine.daily_cap) {
3610
- r0.daily = { date: today, n: usedToday + 1 };
3611
- this.store.save(r0);
3612
- }
3613
- const resolved = this.resolveAction(routine, payload);
3614
- let outcome = "delivered";
3615
- const refundDailyCap = () => {
3616
- if (!routine.daily_cap) return;
3617
- const rb = this.store.get(routine.id);
3618
- if (rb?.daily?.date === today && rb.daily.n > 0) {
3619
- rb.daily.n -= 1;
3620
- this.store.save(rb);
3621
- }
3622
- };
3623
- try {
3624
- const res = await this.deliver({ routine, action: routine.action, resolved, payload, via });
3625
- if (res && res.skipped) {
3626
- outcome = `skipped (${res.skipped})`;
3627
- this.log(`[routine] ${routine.id || routine.name}: ${outcome}`);
3628
- refundDailyCap();
3629
- }
3630
- } catch (e) {
3631
- outcome = "error: " + (e?.message || e);
3632
- refundDailyCap();
3633
- } finally {
3634
- this.active.delete(routine.id);
3635
- }
3636
- const r = this.store.get(routine.id) || routine;
3637
- r.last_fired_at = this.now().toISOString();
3638
- r.last_runs = [{ firedAt: r.last_fired_at, via, delivered: resolved.kind, outcome }, ...r.last_runs || []].slice(0, 20);
3639
- this.store.save(r);
3640
- return { fired: true, via, resolved, outcome };
3641
- }
3642
- async tick(date = this.now()) {
3643
- const results = [];
3644
- for (const r of this.store.list()) {
3645
- if (!r.enabled || r.trigger?.type !== "schedule") continue;
3646
- if (!cronMatches(r.trigger.cron, inZone(date, r.trigger.tz))) continue;
3647
- const mk = minuteKey(date);
3648
- if (this._firedMinute.get(r.id) === mk) continue;
3649
- this._firedMinute.set(r.id, mk);
3650
- results.push({ id: r.id, ...await this.fire(r, { tick: date.toISOString() }, "schedule") });
3651
- }
3652
- return results;
3653
- }
3654
- async webhook(id, opts = {}) {
3655
- const { key, method = "POST", body = {}, query = {} } = opts;
3656
- const r = this.store.get(id);
3657
- if (!r) return { status: 404, error: "routine not found" };
3658
- if (r.trigger?.type !== "webhook" && r.trigger?.type !== "api") return { status: 400, error: "not a webhook routine" };
3659
- if (!r.enabled) return { status: 409, error: "routine disabled" };
3660
- if (!r.trigger.public && r.trigger.key && key !== r.trigger.key) return { status: 401, error: "bad key" };
3661
- const methods = r.trigger.methods || ["GET", "POST"];
3662
- if (!methods.includes(method)) return { status: 405, error: "method not allowed" };
3663
- const res = await this.fire(r, { body, query }, r.trigger.type);
3664
- return { status: 200, ...res };
3665
- }
3666
- async runNow(id, payload = {}) {
3667
- const r = this.store.get(id);
3668
- if (!r) return { error: "not found" };
3669
- return this.fire(r, payload, "manual");
3670
- }
3671
- async catchUp(sinceDate, nowDate = this.now()) {
3672
- const results = [];
3673
- for (const r of this.store.list()) {
3674
- if (!r.enabled || r.trigger?.type !== "schedule" || r.trigger.missed !== "catchup") continue;
3675
- const deadlineMs = (r.trigger.deadline_sec || 3600) * 1e3;
3676
- const since = new Date(Math.max(sinceDate.getTime(), nowDate.getTime() - deadlineMs));
3677
- const due = nextFire(r.trigger.cron, since, r.trigger.tz);
3678
- if (due && due <= nowDate) {
3679
- const last = r.last_fired_at ? new Date(r.last_fired_at) : /* @__PURE__ */ new Date(0);
3680
- if (last < due) results.push({ id: r.id, ...await this.fire(r, { catchup: due.toISOString() }, "schedule") });
3681
- }
3682
- }
3683
- return results;
3684
- }
3685
- }
3686
-
3687
3392
  const PARTICIPANTS_CHANNEL_ID = "sys-participants";
3688
3393
  function channelPublicView(c) {
3689
3394
  return { id: c.id, name: c.name, description: c.description, identity: { mode: c.identity?.mode }, action: c.action?.kind, bind: c.bind };
@@ -3961,13 +3666,6 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
3961
3666
  });
3962
3667
  return msg;
3963
3668
  };
3964
- const routineStore = new RoutineStore();
3965
- const syncRoutinesToMetadata = () => {
3966
- metadata.routines = routineStore.list(sessionId);
3967
- metadataVersion++;
3968
- notifyListeners({ type: "update-session", sessionId, metadata: { value: metadata, version: metadataVersion } });
3969
- callbacks.onMetadataUpdate?.(metadata);
3970
- };
3971
3669
  const channelStore = new ChannelStore(initialMetadata.path);
3972
3670
  const meetingActivity = [];
3973
3671
  const channelOutbox = new ChannelOutbox(initialMetadata.path);
@@ -4242,70 +3940,6 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
4242
3940
  callbacks.onUpdateConfig?.(patch);
4243
3941
  return { success: true };
4244
3942
  },
4245
- // ── Routines (session-scoped triggers -> message/loop). CRUD + metadata
4246
- // sync; actual schedule/webhook firing is handled by the supervised
4247
- // routine server (`svamp routine serve`) reading the shared store. ──
4248
- listRoutines: async (context) => {
4249
- authorizeRequest(context, metadata.sharing, "view");
4250
- return { routines: routineStore.list(sessionId) };
4251
- },
4252
- saveRoutine: async (routine, context) => {
4253
- authorizeRequest(context, metadata.sharing, "admin");
4254
- try {
4255
- const saved = routineStore.save({ ...routine, session_id: sessionId, dir: routine.dir || metadata.path });
4256
- syncRoutinesToMetadata();
4257
- return { success: true, routine: saved };
4258
- } catch (e) {
4259
- return { success: false, error: e?.message || String(e) };
4260
- }
4261
- },
4262
- removeRoutine: async (id, context) => {
4263
- authorizeRequest(context, metadata.sharing, "admin");
4264
- const r = routineStore.get(id);
4265
- if (r && r.session_id !== sessionId) return { success: false };
4266
- const ok = routineStore.remove(id);
4267
- syncRoutinesToMetadata();
4268
- return { success: ok };
4269
- },
4270
- setRoutineEnabled: async (id, enabled, context) => {
4271
- authorizeRequest(context, metadata.sharing, "admin");
4272
- const r = routineStore.get(id);
4273
- if (r && r.session_id !== sessionId) return { success: false };
4274
- routineStore.setEnabled(id, enabled);
4275
- syncRoutinesToMetadata();
4276
- return { success: true };
4277
- },
4278
- runRoutineNow: async (id, context) => {
4279
- authorizeRequest(context, metadata.sharing, "interact");
4280
- const r = routineStore.get(id);
4281
- if (!r || r.session_id !== sessionId) return { error: "not found" };
4282
- const resolved = new RoutineRunner({ store: routineStore, deliver: async () => {
4283
- } }).resolveAction(r, {});
4284
- let outcome = "delivered";
4285
- try {
4286
- if (r.bind === "stateless") {
4287
- if (!r.dir || !callbacks.onRunStatelessRoutine) {
4288
- outcome = "stateless run-now unavailable (no dir/handler)";
4289
- } else {
4290
- callbacks.onRunStatelessRoutine(r.dir, resolved.text || resolved.task || "run");
4291
- outcome = "spawned stateless one-shot";
4292
- }
4293
- } else {
4294
- const text = resolved.kind === "message" ? resolved.text || "" : `Begin the loop (LOOP MODE). Task: ${resolved.task}. Iterate until the gate lets you stop.`;
4295
- await rpcHandlers.sendMessage(
4296
- JSON.stringify({ role: "user", content: { type: "text", text }, meta: { sentFrom: "routine-run-now" } }),
4297
- void 0,
4298
- { sentFrom: "routine-run-now" },
4299
- context
4300
- );
4301
- }
4302
- } catch (e) {
4303
- outcome = "error: " + (e?.message || e);
4304
- }
4305
- routineStore.recordRun(id, { via: "manual", delivered: resolved.kind, outcome });
4306
- syncRoutinesToMetadata();
4307
- return { success: !/error|unavailable/.test(outcome), resolved, outcome };
4308
- },
4309
3943
  // ── Channels (project-folder config; served by the channel server) ──
4310
3944
  listChannels: async (context) => {
4311
3945
  authorizeRequest(context, metadata.sharing, "view");
@@ -10178,6 +9812,11 @@ class ProcessSupervisor {
10178
9812
  }
10179
9813
  }
10180
9814
 
9815
+ function normalizeStatus(s) {
9816
+ if (s === "backlog") return "ready";
9817
+ if (s === "done") return "archived";
9818
+ return s === "ready" || s === "in_progress" || s === "archived" ? s : "ready";
9819
+ }
10181
9820
  const FIELD_ORDER = ["id", "title", "status", "scope", "labels", "verify", "disposition", "triaged", "branch", "session", "original", "created", "closed"];
10182
9821
  function resolveProjectRoot(start = process.cwd()) {
10183
9822
  let dir = start;
@@ -10227,7 +9866,7 @@ function parseIssue(content) {
10227
9866
  return {
10228
9867
  id: String(fm.id),
10229
9868
  title: String(fm.title ?? ""),
10230
- status: fm.status ?? "backlog",
9869
+ status: normalizeStatus(fm.status),
10231
9870
  scope: fm.scope ?? "session",
10232
9871
  labels: Array.isArray(fm.labels) ? fm.labels.map(String) : [],
10233
9872
  verify: fm.verify ?? null,
@@ -10290,7 +9929,7 @@ function addIssue(projectRoot, fields) {
10290
9929
  const issue = {
10291
9930
  id: nextId(projectRoot),
10292
9931
  title: fields.title.trim(),
10293
- status: fields.status ?? "backlog",
9932
+ status: fields.status ?? "ready",
10294
9933
  scope: fields.scope ?? "session",
10295
9934
  labels: fields.labels ?? [],
10296
9935
  verify: fields.verify ?? null,
@@ -10311,9 +9950,10 @@ function updateIssue(projectRoot, id, patch) {
10311
9950
  if (!cur) return null;
10312
9951
  const wasArchived = cur.status === "archived";
10313
9952
  const next = { ...cur, ...patch, id: cur.id };
9953
+ if (patch.status) next.status = normalizeStatus(patch.status);
10314
9954
  const nowArchived = next.status === "archived";
10315
- if ((next.status === "done" || nowArchived) && !next.closed) next.closed = (/* @__PURE__ */ new Date()).toISOString();
10316
- if (next.status !== "done" && !nowArchived) next.closed = null;
9955
+ if (nowArchived && !next.closed) next.closed = (/* @__PURE__ */ new Date()).toISOString();
9956
+ if (!nowArchived) next.closed = null;
10317
9957
  atomicWrite(issuePath(projectRoot, next.id, nowArchived), serializeIssue(next));
10318
9958
  if (wasArchived !== nowArchived) {
10319
9959
  const oldPath = issuePath(projectRoot, cur.id, wasArchived);
@@ -10341,7 +9981,7 @@ function routeCrewVerdict(projectRoot, childSessionId, v) {
10341
9981
  if (!linked) return null;
10342
9982
  const meta = [v.round != null ? `round ${v.round}` : "", v.judge ? `judge:${v.judge}` : ""].filter(Boolean).join(", ");
10343
9983
  addComment(projectRoot, linked.id, `Crew child ${childSessionId.slice(0, 8)} \u2192 ${v.verdict}${meta ? ` (${meta})` : ""}.${v.guidance ? " " + v.guidance : ""}`);
10344
- return v.verdict === "approved" ? updateIssue(projectRoot, linked.id, { status: "done" }) ?? null : getIssue(projectRoot, linked.id);
9984
+ return v.verdict === "approved" ? updateIssue(projectRoot, linked.id, { status: "archived" }) ?? null : getIssue(projectRoot, linked.id);
10345
9985
  }
10346
9986
  function searchIssues(projectRoot, query) {
10347
9987
  const q = query.trim();
@@ -10365,7 +10005,7 @@ function searchIssues(projectRoot, query) {
10365
10005
  }
10366
10006
  function summarize(issues) {
10367
10007
  const by = (s) => issues.filter((i) => i.status === s).length;
10368
- return { total: issues.length, backlog: by("backlog"), ready: by("ready"), in_progress: by("in_progress"), done: by("done"), archived: by("archived") };
10008
+ return { total: issues.length, ready: by("ready"), in_progress: by("in_progress"), archived: by("archived") };
10369
10009
  }
10370
10010
 
10371
10011
  const OAUTH_TOKEN_ENDPOINT = "https://platform.claude.com/v1/oauth/token";
@@ -11907,7 +11547,7 @@ async function startDaemon(options) {
11907
11547
  saveExposedTunnels(list);
11908
11548
  }
11909
11549
  async function createExposedTunnel(spec) {
11910
- const { FrpcTunnel } = await import('./frpc-Bm7BrLan.mjs');
11550
+ const { FrpcTunnel } = await import('./frpc-D4x74mG6.mjs');
11911
11551
  const tunnel = new FrpcTunnel({
11912
11552
  name: spec.name,
11913
11553
  ports: spec.ports,
@@ -11927,7 +11567,7 @@ async function startDaemon(options) {
11927
11567
  return tunnel;
11928
11568
  }
11929
11569
  const tunnelRecreateState = /* @__PURE__ */ new Map();
11930
- const { ServeManager } = await import('./serveManager-DQByExEO.mjs');
11570
+ const { ServeManager } = await import('./serveManager-DXm1bdoU.mjs');
11931
11571
  const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
11932
11572
  ensureAutoInstalledSkills(logger).catch(() => {
11933
11573
  });
@@ -13662,15 +13302,6 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
13662
13302
  }
13663
13303
  }
13664
13304
  },
13665
- onRunStatelessRoutine: (dir, prompt) => {
13666
- try {
13667
- const child = spawn$1("svamp", ["session", "query", dir, prompt], { detached: true, stdio: "ignore" });
13668
- child.unref();
13669
- logger.log(`[routine] run-now: spawned stateless one-shot in ${dir}`);
13670
- } catch (e) {
13671
- logger.log(`[routine] run-now stateless spawn failed: ${e?.message || e}`);
13672
- }
13673
- },
13674
13305
  onMetadataUpdate: (newMeta) => {
13675
13306
  const oldTitle = sessionMetadata.summary?.text;
13676
13307
  const newTitle = newMeta.summary?.text;
@@ -13727,11 +13358,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
13727
13358
  });
13728
13359
  },
13729
13360
  onIssue: async (params) => {
13730
- const { issueRpc } = await import('./rpc-BbaS0cLh.mjs');
13361
+ const { issueRpc } = await import('./rpc-2TJ6ipXZ.mjs');
13731
13362
  return issueRpc(params?.cwd || directory, params || {});
13732
13363
  },
13733
13364
  onWorkflow: async (params) => {
13734
- const { workflowRpc } = await import('./rpc-DVNDMfl8.mjs');
13365
+ const { workflowRpc } = await import('./rpc-DBH2bo8s.mjs');
13735
13366
  return workflowRpc(params?.cwd || directory, params || {});
13736
13367
  },
13737
13368
  onRipgrep: async (args, cwd) => {
@@ -14172,15 +13803,6 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
14172
13803
  }
14173
13804
  }
14174
13805
  },
14175
- onRunStatelessRoutine: (dir, prompt) => {
14176
- try {
14177
- const child = spawn$1("svamp", ["session", "query", dir, prompt], { detached: true, stdio: "ignore" });
14178
- child.unref();
14179
- logger.log(`[routine] run-now: spawned stateless one-shot in ${dir}`);
14180
- } catch (e) {
14181
- logger.log(`[routine] run-now stateless spawn failed: ${e?.message || e}`);
14182
- }
14183
- },
14184
13806
  onMetadataUpdate: (newMeta) => {
14185
13807
  const oldTitleAcp = sessionMetadata.summary?.text;
14186
13808
  const newTitleAcp = newMeta.summary?.text;
@@ -14246,11 +13868,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
14246
13868
  });
14247
13869
  },
14248
13870
  onIssue: async (params) => {
14249
- const { issueRpc } = await import('./rpc-BbaS0cLh.mjs');
13871
+ const { issueRpc } = await import('./rpc-2TJ6ipXZ.mjs');
14250
13872
  return issueRpc(params?.cwd || directory, params || {});
14251
13873
  },
14252
13874
  onWorkflow: async (params) => {
14253
- const { workflowRpc } = await import('./rpc-DVNDMfl8.mjs');
13875
+ const { workflowRpc } = await import('./rpc-DBH2bo8s.mjs');
14254
13876
  return workflowRpc(params?.cwd || directory, params || {});
14255
13877
  },
14256
13878
  onRipgrep: async (args, cwd) => {
@@ -15003,7 +14625,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
15003
14625
  const PING_TIMEOUT_MS = 15e3;
15004
14626
  const POST_RECONNECT_GRACE_MS = 2e4;
15005
14627
  const RECONNECT_JITTER_MS = 2500;
15006
- const { WorkflowScheduler } = await import('./scheduler-BusuolPD.mjs');
14628
+ const { WorkflowScheduler } = await import('./scheduler-C3j8Tghm.mjs');
15007
14629
  const workflowScheduler = new WorkflowScheduler({
15008
14630
  projectRoots: () => {
15009
14631
  const dirs = /* @__PURE__ */ new Set();
@@ -15582,4 +15204,4 @@ var run = /*#__PURE__*/Object.freeze({
15582
15204
  writeStopMarker: writeStopMarker
15583
15205
  });
15584
15206
 
15585
- export { handleMatchesMetadata as $, loadMachineContext as A, buildMachineInstructions as B, machineToolsForRole as C, buildMachineTools as D, parseFrontmatter as E, getSkillsServer as F, getSkillsWorkspaceName as G, getSkillsCollectionName as H, fetchWithTimeout as I, searchSkills as J, SKILLS_DIR as K, getSkillInfo as L, downloadSkillFile as M, listSkillFiles as N, resolveModel as O, formatHandle as P, normalizeAllowedUser as Q, RoutineStore as R, ServeAuth as S, loadSecurityContextConfig as T, resolveSecurityContext as U, buildSecurityContextFromFlags as V, mergeSecurityContexts as W, buildSessionShareUrl as X, computeOutboundHop as Y, buildMachineShareUrl as Z, parseHandle as _, createSessionStore as a, describeMisconfiguration as a0, buildMachineDeps as a1, composeSessionId as a2, generateFriendlyName as a3, generateHookSettings as a4, projectInfo as a5, DefaultTransport$1 as a6, acpBackend as a7, acpAgentConfig as a8, codexMcpBackend as a9, GeminiTransport$1 as aa, claudeAuth as ab, instanceConfig as ac, api as ad, run as ae, 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, addComment as o, addIssue as p, listIssues as q, registerMachineService as r, startDaemon as s, searchIssues as t, updateIssue as u, cronMatches as v, summarize as w, RoutineRunner as x, shortId as y, READ_ONLY_TOOLS as z };
15207
+ export { buildMachineDeps as $, machineToolsForRole as A, buildMachineTools as B, parseFrontmatter as C, getSkillsServer as D, getSkillsWorkspaceName as E, getSkillsCollectionName as F, fetchWithTimeout as G, searchSkills as H, SKILLS_DIR as I, getSkillInfo as J, downloadSkillFile as K, listSkillFiles as L, resolveModel as M, formatHandle as N, normalizeAllowedUser as O, loadSecurityContextConfig as P, resolveSecurityContext as Q, READ_ONLY_TOOLS as R, ServeAuth as S, buildSecurityContextFromFlags as T, mergeSecurityContexts as U, buildSessionShareUrl as V, computeOutboundHop as W, buildMachineShareUrl as X, parseHandle as Y, handleMatchesMetadata as Z, describeMisconfiguration as _, createSessionStore as a, composeSessionId as a0, generateFriendlyName as a1, generateHookSettings as a2, projectInfo as a3, DefaultTransport$1 as a4, acpBackend as a5, acpAgentConfig as a6, codexMcpBackend as a7, GeminiTransport$1 as a8, claudeAuth as a9, instanceConfig as aa, api as ab, run as ac, 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, addComment as o, addIssue as p, listIssues as q, registerMachineService as r, startDaemon as s, searchIssues as t, updateIssue as u, cronMatches as v, summarize as w, shortId as x, loadMachineContext as y, buildMachineInstructions as z };
@@ -1,4 +1,4 @@
1
- import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { a2 as composeSessionId, a3 as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a4 as generateHookSettings } from './run-BmpyfykC.mjs';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { a0 as composeSessionId, a1 as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a2 as generateHookSettings } from './run-B-1WDymr.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';
@@ -1,5 +1,5 @@
1
1
  import { spawn } from 'node:child_process';
2
- import { m as resolveProjectRoot, v as cronMatches } from './run-BmpyfykC.mjs';
2
+ import { m as resolveProjectRoot, v as cronMatches } from './run-B-1WDymr.mjs';
3
3
  import { l as listWorkflows, b as workflowCrons, w as workflowSteps } from './store-BPL__e4D.mjs';
4
4
  import 'os';
5
5
  import 'fs/promises';