@wrongstack/cli 1.0.27 → 1.0.29

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 (46) hide show
  1. package/data/providers.json +22 -0
  2. package/dist/{acp-M4565YUW.js → acp-QWCBMQSO.js} +2 -2
  3. package/dist/{chunk-RGFWGUAB.js → chunk-4UWCMBPG.js} +159 -3
  4. package/dist/{chunk-ENRYW73H.js → chunk-6A7URMWN.js} +79 -8
  5. package/dist/{chunk-HDMFKTT3.js → chunk-7IQWQCBH.js} +5 -2
  6. package/dist/{chunk-5JYDKG5B.js → chunk-BZHT5GPF.js} +6 -1
  7. package/dist/{chunk-HFZH2XJN.js → chunk-IFQ6NQPL.js} +4 -4
  8. package/dist/{chunk-N57IU5FG.js → chunk-KT34LWIS.js} +10 -9
  9. package/dist/{chunk-UZ2TMHNH.js → chunk-UKP7H2U4.js} +10 -2
  10. package/dist/{chunk-EPR42SWM.js → chunk-WRYZLEXP.js} +20 -1
  11. package/dist/{cli-context-C7RBSWHF.js → cli-context-6RONY47P.js} +18 -18
  12. package/dist/{cli-main-TQTH73ZK.js → cli-main-YS3HIOSD.js} +827 -313
  13. package/dist/{execution-3STBD2U7.js → execution-JEYF2L7O.js} +32 -24
  14. package/dist/fleet/host-explore-companion.d.ts +1 -1
  15. package/dist/fleet/host-memory-companion.d.ts +43 -0
  16. package/dist/fleet/host-subagent-factory.d.ts +1 -1
  17. package/dist/fleet/host.d.ts +1 -0
  18. package/dist/fleet/memory-companion-evidence.d.ts +12 -0
  19. package/dist/fleet/memory-companion-policy.d.ts +9 -0
  20. package/dist/goal-commands.d.ts +10 -0
  21. package/dist/{hq-44F4OQH2.js → hq-BNK67HOE.js} +3 -3
  22. package/dist/hq-publisher.d.ts +4 -1
  23. package/dist/hq-server/sage-sync-send.d.ts +5 -0
  24. package/dist/hq-server/server-lifecycle.d.ts +1 -1
  25. package/dist/hq-server/snapshot.d.ts +1 -1
  26. package/dist/{hq-server-UIB24LU6.js → hq-server-PCQACV7Y.js} +2 -2
  27. package/dist/hq-ttl-N4DAIAVX.js +7 -0
  28. package/dist/index.js +3 -3
  29. package/dist/{mailbox-serve-GNGXEQ2R.js → mailbox-serve-FW3JCCPK.js} +2 -2
  30. package/dist/{per-subcommand-help-QDAQASPZ.js → per-subcommand-help-MWQWFIAV.js} +2 -2
  31. package/dist/sage-FFKUOCQL.js +475 -0
  32. package/dist/sage-connect.d.ts +62 -0
  33. package/dist/sage-hq-sync.d.ts +8 -0
  34. package/dist/sage-sync-ZMTHB477.js +69 -0
  35. package/dist/services/run-git.d.ts +3 -1
  36. package/dist/{sessions-config-XHLXJ5C3.js → sessions-config-JGIZRNT6.js} +15 -4
  37. package/dist/{short-circuit-flags-5TNTJPQR.js → short-circuit-flags-5N2LPK4R.js} +3 -3
  38. package/dist/subcommands/handlers/helpers.d.ts +1 -1
  39. package/dist/subcommands/handlers/sage-sync.d.ts +4 -0
  40. package/dist/subcommands/handlers/sage.d.ts +5 -0
  41. package/dist/{subcommands-BHQYLDSD.js → subcommands-WX4GDMWB.js} +2 -2
  42. package/dist/{typesafe-U7YB2RRV.js → typesafe-WA5O3KVS.js} +10 -2
  43. package/dist/{version-help-XJJQ7YSH.js → version-help-HN32QYQN.js} +2 -2
  44. package/dist/{webui-server-GCXIO3JY.js → webui-server-EXJWLB3X.js} +10 -8
  45. package/package.json +30 -29
  46. package/dist/hq-ttl-VJR65OBJ.js +0 -7
@@ -4,6 +4,28 @@
4
4
  "name": "OpenAI Codex (ChatGPT sign-in)",
5
5
  "doc": "https://developers.openai.com/codex/models",
6
6
  "models": {
7
+ "gpt-6.1-sol": {
8
+ "id": "gpt-6.1-sol",
9
+ "name": "GPT-6.1 Sol",
10
+ "description": "Latest workhorse model for coding and everyday work.",
11
+ "reasoning": true,
12
+ "reasoningConfig": {
13
+ "default": "enabled",
14
+ "disableSupported": false,
15
+ "effortSupported": true,
16
+ "effortLevels": ["low", "medium", "high", "xhigh", "max"],
17
+ "preserveThinking": "unsupported"
18
+ },
19
+ "tool_call": true,
20
+ "modalities": {
21
+ "input": ["text", "image"],
22
+ "output": ["text"]
23
+ },
24
+ "limit": {
25
+ "context": 872000,
26
+ "output": 128000
27
+ }
28
+ },
7
29
  "gpt-6-astra": {
8
30
  "id": "gpt-6-astra",
9
31
  "name": "GPT-6 Astra",
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-NRXWOMK7.js";
11
11
  import {
12
12
  startCliHqConnection
13
- } from "./chunk-RGFWGUAB.js";
13
+ } from "./chunk-4UWCMBPG.js";
14
14
  import "./chunk-SDJCLEK6.js";
15
15
  import "./chunk-F5DCFAKI.js";
16
16
  import "./chunk-SIGUWXAA.js";
@@ -1192,4 +1192,4 @@ ${renderAcpBenchText(result)}
1192
1192
  export {
1193
1193
  acpCmd
1194
1194
  };
1195
- //# sourceMappingURL=acp-M4565YUW.js.map
1195
+ //# sourceMappingURL=acp-QWCBMQSO.js.map
@@ -426,6 +426,146 @@ async function writeState(projectRoot, state) {
426
426
  await writeKanbanMetadata(projectRoot, SYNC_STATE_KEY, JSON.stringify(state));
427
427
  }
428
428
 
429
+ // src/sage-hq-sync.ts
430
+ import {
431
+ chunkHqSageRecords,
432
+ compareHqSageRecords,
433
+ isHqSageRecord,
434
+ MAX_HQ_SAGE_RECORD_BYTES
435
+ } from "@wrongstack/core/hq";
436
+ import { toErrorMessage } from "@wrongstack/core/utils";
437
+ import { SageProjectServerConnection } from "@wrongstack/sage";
438
+ function createSageHqSync(projectRoot, publisher, directory) {
439
+ const connection = new SageProjectServerConnection(projectRoot, directory);
440
+ const acknowledged = /* @__PURE__ */ new Map();
441
+ const sent = /* @__PURE__ */ new Map();
442
+ const warned = /* @__PURE__ */ new Map();
443
+ const remote = /* @__PURE__ */ new Map();
444
+ const controller = new AbortController();
445
+ const callOptions = { meta: { clientId: "hq-sage-sync" }, signal: controller.signal };
446
+ let stopped = false;
447
+ let running;
448
+ let lastFailure;
449
+ let lastScanVersion;
450
+ let generation = 0;
451
+ const version = (r) => `${r.revision}:${r.changeId}`;
452
+ const synchronize = async () => {
453
+ const startedGeneration = generation;
454
+ while (remote.size && !stopped && startedGeneration === generation) {
455
+ const batch = chunkHqSageRecords(publisher.project.projectId, remote.values()).next();
456
+ if (batch.done) break;
457
+ const records = batch.value.records;
458
+ for (const r of records) remote.delete(r.id);
459
+ try {
460
+ await connection.call("applyHqSync", { records }, callOptions);
461
+ for (const r of records) {
462
+ if (startedGeneration !== generation) break;
463
+ acknowledged.set(r.id, version(r));
464
+ if (sent.get(r.id) === version(r)) sent.delete(r.id);
465
+ }
466
+ } catch (error) {
467
+ if (stopped || startedGeneration !== generation) return;
468
+ for (const r of records) {
469
+ const pending = remote.get(r.id);
470
+ if (!pending || compareHqSageRecords(r, pending) > 0) remote.set(r.id, r);
471
+ }
472
+ throw error;
473
+ }
474
+ }
475
+ const scanVersion = await connection.call("getHqSyncVersion", {}, callOptions);
476
+ if (stopped || startedGeneration !== generation || !publisher.connected) return;
477
+ if (scanVersion === lastScanVersion && sent.size === 0) return;
478
+ let after = "";
479
+ while (!stopped && publisher.connected) {
480
+ const page = await connection.call("listHqSync", { after }, callOptions);
481
+ if (stopped || startedGeneration !== generation || !publisher.connected) return;
482
+ if (!page.length) {
483
+ lastScanVersion = scanVersion;
484
+ return;
485
+ }
486
+ const outgoing = page.filter((r) => {
487
+ if (acknowledged.get(r.id) === version(r)) {
488
+ sent.delete(r.id);
489
+ return false;
490
+ }
491
+ if (isHqSageRecord(r)) {
492
+ warned.delete(r.id);
493
+ return true;
494
+ }
495
+ if (warned.get(r.id) !== version(r)) {
496
+ process.emitWarning(
497
+ `SAGE memory ${r.id} cannot sync: invalid record or exceeds ${MAX_HQ_SAGE_RECORD_BYTES} bytes. Other memories will continue syncing.`,
498
+ { code: "WRONGSTACK_HQ_SAGE_RECORD_REJECTED" }
499
+ );
500
+ warned.set(r.id, version(r));
501
+ }
502
+ return false;
503
+ });
504
+ for (const payload of chunkHqSageRecords(publisher.project.projectId, outgoing)) {
505
+ for (const r of payload.records) sent.set(r.id, version(r));
506
+ publisher.publishEvent({
507
+ type: "sage.snapshot",
508
+ payload,
509
+ maxSummaryLength: MAX_HQ_SAGE_RECORD_BYTES
510
+ });
511
+ }
512
+ after = page[page.length - 1].id;
513
+ }
514
+ };
515
+ const refresh = () => {
516
+ if (stopped || !publisher.connected) return Promise.resolve();
517
+ if (running) return running;
518
+ running = synchronize().then(() => {
519
+ lastFailure = void 0;
520
+ }).catch((error) => {
521
+ const message = toErrorMessage(error);
522
+ if (!stopped && lastFailure !== message) {
523
+ process.emitWarning(`SAGE HQ sync will retry: ${message}`, {
524
+ code: "WRONGSTACK_HQ_SAGE_SYNC_FAILED"
525
+ });
526
+ lastFailure = message;
527
+ }
528
+ }).finally(() => {
529
+ running = void 0;
530
+ });
531
+ return running;
532
+ };
533
+ const unsubscribe = publisher.onConnected(() => {
534
+ generation++;
535
+ remote.clear();
536
+ acknowledged.clear();
537
+ sent.clear();
538
+ lastScanVersion = void 0;
539
+ void refresh();
540
+ });
541
+ const timer = setInterval(() => void refresh(), 3e3);
542
+ timer.unref?.();
543
+ void refresh();
544
+ return {
545
+ async handleRemote(snapshot) {
546
+ if (stopped || snapshot.projectId !== publisher.project.projectId) return;
547
+ for (const r of snapshot.records) {
548
+ const pending = remote.get(r.id);
549
+ if (!pending || compareHqSageRecords(r, pending) > 0) remote.set(r.id, r);
550
+ }
551
+ await refresh();
552
+ },
553
+ refresh,
554
+ stop() {
555
+ stopped = true;
556
+ controller.abort();
557
+ clearInterval(timer);
558
+ unsubscribe();
559
+ remote.clear();
560
+ acknowledged.clear();
561
+ sent.clear();
562
+ warned.clear();
563
+ connection.close();
564
+ void running?.finally(() => connection.close());
565
+ }
566
+ };
567
+ }
568
+
429
569
  // src/hq-publisher.ts
430
570
  var HQ_DISCOVERY_CONNECTION_KEY = "<discover>";
431
571
  function nodeWsSocketFactory(url) {
@@ -450,15 +590,20 @@ function resolvedHqConnectionKey(options) {
450
590
  const endpoint = config.discover === true ? HQ_DISCOVERY_CONNECTION_KEY : `${config.url}
451
591
  ${config.token ?? ""}`;
452
592
  return `${endpoint}
453
- raw=${String(config.rawContent)}`;
593
+ raw=${String(config.rawContent)}
594
+ sage=${options.appConfig?.Sage?.storage?.directory ?? ""}`;
454
595
  }
455
596
  function startCliHqConnection(options) {
456
597
  const shouldOwnKanbanSync = options.ownKanbanSync !== false && options.onKanbanSnapshot === void 0;
457
598
  let kanbanSync;
599
+ let sageSync;
600
+ const shouldOwnSageSync = (options.ownSageSync ?? options.ownKanbanSync !== false) && options.onSageSnapshot === void 0;
458
601
  const publisherOptions = shouldOwnKanbanSync ? {
459
602
  ...options,
460
603
  onKanbanSnapshot: (snapshot) => kanbanSync?.handleRemote(snapshot)
461
- } : options;
604
+ } : { ...options };
605
+ if (shouldOwnSageSync)
606
+ publisherOptions.onSageSnapshot = (snapshot) => sageSync?.handleRemote(snapshot);
462
607
  let publisher;
463
608
  let publisherKey;
464
609
  let timer;
@@ -467,6 +612,8 @@ function startCliHqConnection(options) {
467
612
  if (publisher === void 0) return;
468
613
  kanbanSync?.stop();
469
614
  kanbanSync = void 0;
615
+ sageSync?.stop();
616
+ sageSync = void 0;
470
617
  publisher.close();
471
618
  publisher = void 0;
472
619
  publisherKey = void 0;
@@ -484,6 +631,8 @@ function startCliHqConnection(options) {
484
631
  `)) publisher.refreshEndpoint();
485
632
  return;
486
633
  }
634
+ sageSync?.stop();
635
+ sageSync = void 0;
487
636
  publisher?.close();
488
637
  publisher = void 0;
489
638
  publisherKey = void 0;
@@ -495,6 +644,12 @@ function startCliHqConnection(options) {
495
644
  kanbanSync?.stop();
496
645
  kanbanSync = createKanbanHqSync(options.projectRoot, next.project.projectId);
497
646
  }
647
+ if (shouldOwnSageSync)
648
+ sageSync = createSageHqSync(
649
+ options.projectRoot,
650
+ next,
651
+ current.appConfig?.Sage?.storage?.directory
652
+ );
498
653
  next.connect();
499
654
  if (kanbanSync !== void 0) {
500
655
  kanbanSync.attachPublisher(next).catch((error) => {
@@ -520,6 +675,7 @@ function startCliHqConnection(options) {
520
675
  timer = void 0;
521
676
  }
522
677
  kanbanSync?.stop();
678
+ sageSync?.stop();
523
679
  publisher?.close();
524
680
  publisher = void 0;
525
681
  publisherKey = void 0;
@@ -530,4 +686,4 @@ function startCliHqConnection(options) {
530
686
  export {
531
687
  startCliHqConnection
532
688
  };
533
- //# sourceMappingURL=chunk-RGFWGUAB.js.map
689
+ //# sourceMappingURL=chunk-4UWCMBPG.js.map
@@ -1689,7 +1689,7 @@ import { WebSocket } from "ws";
1689
1689
  var HQ_SNAPSHOT_BROADCAST_DEBOUNCE_MS = 100;
1690
1690
  var HQ_SNAPSHOT_PERSIST_MIN_INTERVAL_MS = 3e3;
1691
1691
  var HQ_BROWSER_MAX_BUFFERED_BYTES = 32 * 1024 * 1024;
1692
- function sendGuarded(ws, data) {
1692
+ function sendGuarded(ws, data, callback) {
1693
1693
  if (ws.readyState !== WebSocket.OPEN) return false;
1694
1694
  const buffered = Number.isFinite(ws.bufferedAmount) ? ws.bufferedAmount : 0;
1695
1695
  if (buffered + Buffer.byteLength(data, "utf8") > HQ_BROWSER_MAX_BUFFERED_BYTES) {
@@ -1704,7 +1704,8 @@ function sendGuarded(ws, data) {
1704
1704
  return false;
1705
1705
  }
1706
1706
  try {
1707
- ws.send(data);
1707
+ if (callback) ws.send(data, callback);
1708
+ else ws.send(data);
1708
1709
  return true;
1709
1710
  } catch {
1710
1711
  return false;
@@ -3770,7 +3771,8 @@ function createHqServerShutdown(params) {
3770
3771
  params.persistence.eventLog.drain(),
3771
3772
  params.persistence.snapshotStore.drain(),
3772
3773
  params.persistence.timeseries.drain(),
3773
- params.persistence.kanban.drain()
3774
+ params.persistence.kanban.drain(),
3775
+ params.persistence.sage?.drain()
3774
3776
  ]).then(() => clearHqRuntimeMarker(params.dataDir, params.hqUrl)).catch(() => void 0).finally(() => res());
3775
3777
  });
3776
3778
  });
@@ -3849,16 +3851,56 @@ import {
3849
3851
  tightenHqRedactionPolicy as tightenHqRedactionPolicy2,
3850
3852
  tokenHasCapability as tokenHasCapability3
3851
3853
  } from "@wrongstack/core/hq";
3854
+ import { toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils";
3852
3855
  import { WebSocket as WebSocket3 } from "ws";
3853
3856
 
3857
+ // src/hq-server/sage-sync-send.ts
3858
+ import { chunkHqSageRecords } from "@wrongstack/core/hq";
3859
+ async function sendSageSnapshot(ws, snapshot) {
3860
+ for (const part of chunkHqSageRecords(snapshot.projectId, snapshot.records)) {
3861
+ const delivered = await new Promise((resolve) => {
3862
+ let settled = false;
3863
+ const finish = (ok) => {
3864
+ if (settled) return;
3865
+ settled = true;
3866
+ clearTimeout(timer);
3867
+ ws.removeListener("close", onClose);
3868
+ resolve(ok);
3869
+ };
3870
+ const onClose = () => finish(false);
3871
+ const timer = setTimeout(() => {
3872
+ finish(false);
3873
+ try {
3874
+ ws.terminate();
3875
+ } catch {
3876
+ }
3877
+ }, 1e4);
3878
+ timer.unref?.();
3879
+ ws.once("close", onClose);
3880
+ const accepted = sendGuarded(
3881
+ ws,
3882
+ JSON.stringify({ type: "hq.sage_snapshot", payload: part }),
3883
+ (error) => finish(!error)
3884
+ );
3885
+ if (!accepted) finish(false);
3886
+ });
3887
+ if (!delivered) return false;
3888
+ }
3889
+ return true;
3890
+ }
3891
+
3854
3892
  // src/hq-server/ws-client-events.ts
3855
3893
  import {
3894
+ chunkHqSageRecords as chunkHqSageRecords2,
3856
3895
  HQ_TRANSCRIPT_TEXT_CAP,
3896
+ isHqSageSnapshotPayload,
3897
+ MAX_HQ_SAGE_RECORD_BYTES,
3857
3898
  parseHqEventPayload,
3858
3899
  redactHqEvent,
3859
3900
  tightenHqRedactionPolicy,
3860
3901
  tokenHasCapability as tokenHasCapability2
3861
3902
  } from "@wrongstack/core/hq";
3903
+ import { toErrorMessage } from "@wrongstack/core/utils";
3862
3904
  function fanoutKanbanDelta(message, clients, _browsers, projectId) {
3863
3905
  let clientsNotified = 0;
3864
3906
  for (const peer of clients.values()) {
@@ -3908,12 +3950,33 @@ function handleIncomingClientEvent(ws, incomingEvent, clients, browsers, auth, s
3908
3950
  // Chat-transcript events carry full turns — the generic 500-char
3909
3951
  // summary cap would truncate them a second time after the
3910
3952
  // publisher already applied the transcript cap.
3911
- ...incomingEvent.type === "session.transcript" || incomingEvent.type === "agent.message" ? { maxSummaryLength: HQ_TRANSCRIPT_TEXT_CAP } : {}
3953
+ ...incomingEvent.type === "sage.snapshot" ? { maxSummaryLength: MAX_HQ_SAGE_RECORD_BYTES } : incomingEvent.type === "session.transcript" || incomingEvent.type === "agent.message" ? { maxSummaryLength: HQ_TRANSCRIPT_TEXT_CAP } : {}
3912
3954
  }
3913
3955
  ).value;
3914
3956
  if (event.type === "client.heartbeat") {
3915
3957
  return;
3916
3958
  }
3959
+ if (event.type === "sage.snapshot") {
3960
+ const payload = event.payload;
3961
+ if (!isHqSageSnapshotPayload(payload) || payload.projectId !== client.projectId || !persistence?.sage)
3962
+ return;
3963
+ void persistence.sage.merge(payload).then((merged) => {
3964
+ for (const part of chunkHqSageRecords2(client.projectId, merged.records)) {
3965
+ fanoutKanbanDelta(
3966
+ JSON.stringify({ type: "hq.sage_snapshot", payload: part }),
3967
+ clients,
3968
+ browsers,
3969
+ client.projectId
3970
+ );
3971
+ }
3972
+ }).catch(
3973
+ (error) => process.stderr.write(
3974
+ `${JSON.stringify({ level: "warn", event: "hq.sage_sync_failed", projectId: client.projectId, message: toErrorMessage(error) })}
3975
+ `
3976
+ )
3977
+ );
3978
+ return;
3979
+ }
3917
3980
  if (event.type === "kanban.snapshot") {
3918
3981
  const payload = event.payload;
3919
3982
  if (payload.projectId !== client.projectId || persistence === void 0) return;
@@ -3934,7 +3997,7 @@ function handleIncomingClientEvent(ws, incomingEvent, clients, browsers, auth, s
3934
3997
  JSON.stringify({
3935
3998
  level: "warn",
3936
3999
  event: "hq.kanban_merge_failed",
3937
- message: error instanceof Error ? error.message : String(error),
4000
+ message: toErrorMessage(error),
3938
4001
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
3939
4002
  })
3940
4003
  );
@@ -4343,7 +4406,7 @@ function handleBrowser(ws, snapshotBroadcaster, browsers, eventLog) {
4343
4406
  JSON.stringify({
4344
4407
  level: "warn",
4345
4408
  event: "hq.browser_socket_error",
4346
- message: err instanceof Error ? err.message : String(err),
4409
+ message: toErrorMessage2(err),
4347
4410
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
4348
4411
  })
4349
4412
  );
@@ -4424,7 +4487,7 @@ function handleClient(ws, clients, browsers, eventLog, auth, snapshotBroadcaster
4424
4487
  JSON.stringify({
4425
4488
  level: "warn",
4426
4489
  event: "hq.client_socket_error",
4427
- message: err instanceof Error ? err.message : String(err),
4490
+ message: toErrorMessage2(err),
4428
4491
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
4429
4492
  })
4430
4493
  );
@@ -4525,6 +4588,14 @@ function handleClient(ws, clients, browsers, eventLog, auth, snapshotBroadcaster
4525
4588
  sendGuarded(ws, JSON.stringify({ type: "hq.kanban_snapshot", payload: p }));
4526
4589
  }).catch(() => void 0);
4527
4590
  }
4591
+ if (persistence?.sage !== void 0) {
4592
+ void persistence.sage.load(client.projectId).then((snapshot) => sendSageSnapshot(ws, snapshot)).catch(
4593
+ (error) => process.stderr.write(
4594
+ `${JSON.stringify({ level: "warn", event: "hq.sage_restore_failed", projectId: client.projectId, message: toErrorMessage2(error) })}
4595
+ `
4596
+ )
4597
+ );
4598
+ }
4528
4599
  const event = redactHqEvent2(
4529
4600
  {
4530
4601
  id: randomUUID5(),
@@ -5296,4 +5367,4 @@ export {
5296
5367
  startHqServer,
5297
5368
  HqInsecureExposureError2 as HqInsecureExposureError
5298
5369
  };
5299
- //# sourceMappingURL=chunk-ENRYW73H.js.map
5370
+ //# sourceMappingURL=chunk-6A7URMWN.js.map
@@ -112,17 +112,20 @@ function normalizeTuiThinkingWord(value) {
112
112
  // src/services/run-git.ts
113
113
  import { spawn } from "node:child_process";
114
114
  import { toErrorMessage } from "@wrongstack/core/utils";
115
- async function runGit(args, cwd) {
115
+ async function runGit(args, cwd, env) {
116
116
  try {
117
117
  return await new Promise((resolve, reject) => {
118
118
  const child = spawn("git", args, {
119
119
  cwd,
120
+ ...env ? { env: { ...process.env, ...env } } : {},
120
121
  stdio: ["ignore", "pipe", "pipe"],
121
122
  signal: AbortSignal.timeout(3e4),
122
123
  windowsHide: true
123
124
  });
124
125
  let stdout = "";
125
126
  let stderr = "";
127
+ child.stdout?.setEncoding("utf8");
128
+ child.stderr?.setEncoding("utf8");
126
129
  child.stdout?.on("data", (d) => {
127
130
  stdout += d;
128
131
  });
@@ -154,4 +157,4 @@ export {
154
157
  normalizeTuiThinkingWord,
155
158
  runGit
156
159
  };
157
- //# sourceMappingURL=chunk-HDMFKTT3.js.map
160
+ //# sourceMappingURL=chunk-7IQWQCBH.js.map
@@ -56,6 +56,11 @@ var helpCmd = async (_args, deps) => {
56
56
  ["wstack config-export | config-import", "Move behaviour settings via ./wstack-config.json"],
57
57
  ["wstack mcp", "MCP servers: list, add, remove, serve"],
58
58
  ["wstack import-claude-code", "Import MCP servers from Claude Code (preview; --apply)"],
59
+ [
60
+ "wstack sage connect",
61
+ "SAGE memory in Claude Code, Codex, Cursor, Antigravity (MCP + skill)"
62
+ ],
63
+ ["wstack sage sync", "Keep project SAGE memory synchronized through HQ"],
59
64
  ["wstack plugin", "Plugins: list, install, enable, disable, remove"],
60
65
  ["wstack tools | skills", "List registered tools / discovered skills"],
61
66
  ["wstack typesafe", "TypeSafe account: status, login, test"],
@@ -151,4 +156,4 @@ export {
151
156
  versionCmd,
152
157
  helpCmd
153
158
  };
154
- //# sourceMappingURL=chunk-5JYDKG5B.js.map
159
+ //# sourceMappingURL=chunk-BZHT5GPF.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  helpCmd,
3
3
  versionCmd
4
- } from "./chunk-5JYDKG5B.js";
4
+ } from "./chunk-BZHT5GPF.js";
5
5
  import {
6
6
  parseArgs
7
7
  } from "./chunk-Q4INH2DG.js";
@@ -22,11 +22,11 @@ async function handleHelpVersionShortCircuit(argv) {
22
22
  }
23
23
  if (earlyFlags["help"] === true) {
24
24
  if (positional.length > 0) {
25
- const { subcommandNames } = await import("./subcommands-BHQYLDSD.js");
25
+ const { subcommandNames } = await import("./subcommands-WX4GDMWB.js");
26
26
  if (subcommandNames.includes(positional[0])) {
27
27
  const first = positional[0];
28
28
  const deep = positional[1];
29
- const { renderDeepHelp, renderFocusedHelp } = await import("./per-subcommand-help-QDAQASPZ.js");
29
+ const { renderDeepHelp, renderFocusedHelp } = await import("./per-subcommand-help-MWQWFIAV.js");
30
30
  if (deep && renderDeepHelp(`${first}:${deep}`, stubRenderer)) {
31
31
  return 0;
32
32
  }
@@ -43,4 +43,4 @@ async function handleHelpVersionShortCircuit(argv) {
43
43
  export {
44
44
  handleHelpVersionShortCircuit
45
45
  };
46
- //# sourceMappingURL=chunk-HFZH2XJN.js.map
46
+ //# sourceMappingURL=chunk-IFQ6NQPL.js.map
@@ -1,13 +1,13 @@
1
1
 
2
2
  // src/subcommands/index.ts
3
3
  var loaders = {
4
- acp: async () => (await import("./acp-M4565YUW.js")).acpCmd,
4
+ acp: async () => (await import("./acp-QWCBMQSO.js")).acpCmd,
5
5
  init: async () => (await import("./init-DRMMFG2G.js")).initCmd,
6
6
  auth: async () => (await import("./auth-TE55JF4Z.js")).authCmd,
7
7
  update: async () => (await import("./update-TDZX4IL2.js")).updateCmd,
8
8
  remote: async () => (await import("./remote-GB5VXCCT.js")).remoteCmd,
9
- sessions: async () => (await import("./sessions-config-XHLXJ5C3.js")).sessionsCmd,
10
- config: async () => (await import("./sessions-config-XHLXJ5C3.js")).configCmd,
9
+ sessions: async () => (await import("./sessions-config-JGIZRNT6.js")).sessionsCmd,
10
+ config: async () => (await import("./sessions-config-JGIZRNT6.js")).configCmd,
11
11
  "config-export": async () => (await import("./config-transfer-RUWLDCNK.js")).configExportCmd,
12
12
  "config-import": async () => (await import("./config-transfer-RUWLDCNK.js")).configImportCmd,
13
13
  rewind: async () => (await import("./rewind-JA3ZLCNM.js")).rewindCmd,
@@ -16,7 +16,7 @@ var loaders = {
16
16
  tools: async () => (await import("./tools-skills-GH24UCBY.js")).toolsCmd,
17
17
  skills: async () => (await import("./tools-skills-GH24UCBY.js")).skillsCmd,
18
18
  "skill-suggest": async () => (await import("./skill-suggest-L2EVWB6R.js")).skillSuggestCmd,
19
- typesafe: async () => (await import("./typesafe-U7YB2RRV.js")).typesafeCmd,
19
+ typesafe: async () => (await import("./typesafe-WA5O3KVS.js")).typesafeCmd,
20
20
  providers: async () => (await import("./providers-models-5Z2PJTEW.js")).providersCmd,
21
21
  models: async () => (await import("./providers-models-5Z2PJTEW.js")).modelsCmd,
22
22
  mcp: async () => (await import("./mcp-LBS45HWZ.js")).mcpCmd,
@@ -27,17 +27,18 @@ var loaders = {
27
27
  "proxy-status": async () => (await import("./diag-doctor-BIZAFIAT.js")).proxyCmd,
28
28
  export: async () => (await import("./export-VVRIOWFV.js")).exportCmd,
29
29
  usage: async () => (await import("./plugin-usage-CIGOOTX3.js")).usageCmd,
30
- version: async () => (await import("./version-help-XJJQ7YSH.js")).versionCmd,
31
- help: async () => (await import("./version-help-XJJQ7YSH.js")).helpCmd,
30
+ version: async () => (await import("./version-help-HN32QYQN.js")).versionCmd,
31
+ help: async () => (await import("./version-help-HN32QYQN.js")).helpCmd,
32
32
  projects: async () => (await import("./projects-T4CZCA25.js")).projectsCmd,
33
33
  modeldiag: async () => (await import("./modeldiag-W7XAE625.js")).modeldiagCmd,
34
34
  quick: async () => (await import("./quick-CNB4Z3HS.js")).quickCmd,
35
35
  bench: async () => (await import("./bench-GTLPM22B.js")).benchCmd,
36
36
  chronicle: async () => (await import("./chronicle-UTFETHP6.js")).chronicleCmd,
37
- hq: async () => (await import("./hq-44F4OQH2.js")).hqCmd,
38
- mailbox: async () => (await import("./mailbox-serve-GNGXEQ2R.js")).mailboxServeCmd,
37
+ hq: async () => (await import("./hq-BNK67HOE.js")).hqCmd,
38
+ mailbox: async () => (await import("./mailbox-serve-FW3JCCPK.js")).mailboxServeCmd,
39
39
  permissions: async () => (await import("./permissions-PL27UHYD.js")).permissionsCmd,
40
40
  project: async () => (await import("./project-AOQI5XTK.js")).projectCmd,
41
+ sage: async () => (await import("./sage-FFKUOCQL.js")).sageCmd,
41
42
  governance: async () => (await import("./governance-RYSHV675.js")).governanceCmd,
42
43
  "import-claude-code": async () => (await import("./import-claude-code-AYG24RG2.js")).importClaudeCodeCmd
43
44
  };
@@ -55,4 +56,4 @@ export {
55
56
  subcommands,
56
57
  subcommandNames
57
58
  };
58
- //# sourceMappingURL=chunk-N57IU5FG.js.map
59
+ //# sourceMappingURL=chunk-KT34LWIS.js.map
@@ -8,6 +8,7 @@ var TTL_UNIT_MS = {
8
8
  d: 864e5,
9
9
  w: 6048e5
10
10
  };
11
+ var MAX_SAFE_TTL_MS = 864e13;
11
12
  function parseTokenTtlValue(raw) {
12
13
  const trimmed = raw.trim();
13
14
  if (trimmed.length === 0) {
@@ -18,6 +19,9 @@ function parseTokenTtlValue(raw) {
18
19
  if (!Number.isFinite(ms) || ms <= 0) {
19
20
  return { error: `TTL value must be positive (got ${raw})` };
20
21
  }
22
+ if (!Number.isSafeInteger(ms) || ms > MAX_SAFE_TTL_MS) {
23
+ return { error: `TTL value exceeds maximum safe duration (got ${raw})` };
24
+ }
21
25
  return { value: ms };
22
26
  }
23
27
  const match = /^(?<value>\d+)(?<unit>ms|s|m|h|d|w)$/i.exec(trimmed);
@@ -28,7 +32,11 @@ function parseTokenTtlValue(raw) {
28
32
  if (!Number.isFinite(value) || value <= 0 || multiplier === void 0) {
29
33
  return { error: `TTL value must be positive with a valid unit (got ${raw})` };
30
34
  }
31
- return { value: value * multiplier };
35
+ const totalMs = value * multiplier;
36
+ if (!Number.isSafeInteger(value) || !Number.isSafeInteger(totalMs) || totalMs > MAX_SAFE_TTL_MS) {
37
+ return { error: `TTL value exceeds maximum safe duration (got ${raw})` };
38
+ }
39
+ return { value: totalMs };
32
40
  }
33
41
  return {
34
42
  error: `TTL must be a positive integer (milliseconds) or <number><unit> where unit is one of ms, s, m, h, d, w (got ${raw})`
@@ -38,4 +46,4 @@ function parseTokenTtlValue(raw) {
38
46
  export {
39
47
  parseTokenTtlValue
40
48
  };
41
- //# sourceMappingURL=chunk-UZ2TMHNH.js.map
49
+ //# sourceMappingURL=chunk-UKP7H2U4.js.map
@@ -1142,6 +1142,25 @@ var helpTable = {
1142
1142
  ],
1143
1143
  seeAlso: "wstack mcp list; --mcp-config (one-off servers without importing)"
1144
1144
  },
1145
+ sage: {
1146
+ name: "sage",
1147
+ title: "wstack sage \u2014 this project's SAGE memory in other coding agents",
1148
+ description: "Wire SAGE memory into Claude Code, Codex, Cursor or Antigravity over MCP: writes the MCP server entry plus a skill/rule telling the agent when to recall and how to propose. The server attaches to the SAGE daemon while wstack is open in this project and never starts one. Tools are read-only plus memory_candidates (list/propose); a proposal becomes memory only through WrongStack review. Search is lexical: semantic recall runs inside a WrongStack host.",
1149
+ usage: "wstack sage connect <claude-code|codex|cursor|antigravity|all|print> [--dry-run]",
1150
+ subcommands: [
1151
+ { name: "connect <client|all>", description: "Write the MCP entry and skill for a client." },
1152
+ { name: "connect print", description: "Print JSON/TOML config for any other MCP client." },
1153
+ { name: "disconnect <client|all>", description: "Remove what connect wrote." },
1154
+ { name: "mcp [--origin <name>]", description: "The stdio MCP server clients run." },
1155
+ {
1156
+ name: "sync [--restart-service]",
1157
+ description: "Run project memory sync with HQ; optionally restart this project\u2019s SAGE owner."
1158
+ },
1159
+ { name: "--dry-run", description: "Show the file changes without writing." },
1160
+ { name: "--command <path>", description: "Executable to launch instead of wstack." }
1161
+ ],
1162
+ seeAlso: "wstack mcp serve (all WrongStack tools over MCP)"
1163
+ },
1145
1164
  // -- Chronicle ──────────────────────────────────────────────────────
1146
1165
  chronicle: {
1147
1166
  name: "chronicle",
@@ -1243,4 +1262,4 @@ export {
1243
1262
  subcommandsWithFocusedHelp,
1244
1263
  renderGenericHelp
1245
1264
  };
1246
- //# sourceMappingURL=chunk-EPR42SWM.js.map
1265
+ //# sourceMappingURL=chunk-WRYZLEXP.js.map