@pcircle/memesh 4.7.3 → 4.8.1

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 (140) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/AGENTS.md +3 -1
  4. package/README.de.md +42 -16
  5. package/README.md +46 -19
  6. package/README.zh-TW.md +42 -17
  7. package/dashboard/dist/index.html +1 -1
  8. package/dist/core/agent-message-storage.d.ts +76 -0
  9. package/dist/core/agent-message-storage.d.ts.map +1 -0
  10. package/dist/core/agent-message-storage.js +359 -0
  11. package/dist/core/agent-message-storage.js.map +1 -0
  12. package/dist/core/agent-messaging.d.ts +224 -0
  13. package/dist/core/agent-messaging.d.ts.map +1 -0
  14. package/dist/core/agent-messaging.js +881 -0
  15. package/dist/core/agent-messaging.js.map +1 -0
  16. package/dist/core/agent-router.d.ts +194 -0
  17. package/dist/core/agent-router.d.ts.map +1 -0
  18. package/dist/core/agent-router.js +1138 -0
  19. package/dist/core/agent-router.js.map +1 -0
  20. package/dist/core/briefing.d.ts.map +1 -1
  21. package/dist/core/briefing.js +8 -1
  22. package/dist/core/briefing.js.map +1 -1
  23. package/dist/core/config.d.ts.map +1 -1
  24. package/dist/core/config.js.map +1 -1
  25. package/dist/core/doctor.d.ts +19 -0
  26. package/dist/core/doctor.d.ts.map +1 -1
  27. package/dist/core/doctor.js +176 -2
  28. package/dist/core/doctor.js.map +1 -1
  29. package/dist/core/dreamer.d.ts +8 -5
  30. package/dist/core/dreamer.d.ts.map +1 -1
  31. package/dist/core/dreamer.js +88 -3
  32. package/dist/core/dreamer.js.map +1 -1
  33. package/dist/core/product-improvements.d.ts +61 -0
  34. package/dist/core/product-improvements.d.ts.map +1 -0
  35. package/dist/core/product-improvements.js +216 -0
  36. package/dist/core/product-improvements.js.map +1 -0
  37. package/dist/core/repo-state.d.ts +11 -0
  38. package/dist/core/repo-state.d.ts.map +1 -0
  39. package/dist/core/repo-state.js +70 -0
  40. package/dist/core/repo-state.js.map +1 -0
  41. package/dist/core/schema-export.d.ts.map +1 -1
  42. package/dist/core/schema-export.js +70 -0
  43. package/dist/core/schema-export.js.map +1 -1
  44. package/dist/core/task-state.d.ts.map +1 -1
  45. package/dist/core/task-state.js +3 -3
  46. package/dist/core/task-state.js.map +1 -1
  47. package/dist/core/updater.d.ts +2 -0
  48. package/dist/core/updater.d.ts.map +1 -1
  49. package/dist/core/updater.js +11 -5
  50. package/dist/core/updater.js.map +1 -1
  51. package/dist/core/work-topology.d.ts.map +1 -1
  52. package/dist/core/work-topology.js +1 -0
  53. package/dist/core/work-topology.js.map +1 -1
  54. package/dist/host-adapters/acp-client.d.ts +147 -0
  55. package/dist/host-adapters/acp-client.d.ts.map +1 -0
  56. package/dist/host-adapters/acp-client.js +793 -0
  57. package/dist/host-adapters/acp-client.js.map +1 -0
  58. package/dist/host-adapters/claude-channel.d.ts +75 -0
  59. package/dist/host-adapters/claude-channel.d.ts.map +1 -0
  60. package/dist/host-adapters/claude-channel.js +255 -0
  61. package/dist/host-adapters/claude-channel.js.map +1 -0
  62. package/dist/host-adapters/codex-app-server.d.ts +83 -0
  63. package/dist/host-adapters/codex-app-server.d.ts.map +1 -0
  64. package/dist/host-adapters/codex-app-server.js +360 -0
  65. package/dist/host-adapters/codex-app-server.js.map +1 -0
  66. package/dist/host-adapters/codex-cli-queue.d.ts +17 -0
  67. package/dist/host-adapters/codex-cli-queue.d.ts.map +1 -0
  68. package/dist/host-adapters/codex-cli-queue.js +85 -0
  69. package/dist/host-adapters/codex-cli-queue.js.map +1 -0
  70. package/dist/host-runtime/acp.d.ts +57 -0
  71. package/dist/host-runtime/acp.d.ts.map +1 -0
  72. package/dist/host-runtime/acp.js +338 -0
  73. package/dist/host-runtime/acp.js.map +1 -0
  74. package/dist/host-runtime/claude.d.ts +41 -0
  75. package/dist/host-runtime/claude.d.ts.map +1 -0
  76. package/dist/host-runtime/claude.js +213 -0
  77. package/dist/host-runtime/claude.js.map +1 -0
  78. package/dist/host-runtime/codex-session.d.ts +24 -0
  79. package/dist/host-runtime/codex-session.d.ts.map +1 -0
  80. package/dist/host-runtime/codex-session.js +98 -0
  81. package/dist/host-runtime/codex-session.js.map +1 -0
  82. package/dist/host-runtime/codex.d.ts +33 -0
  83. package/dist/host-runtime/codex.d.ts.map +1 -0
  84. package/dist/host-runtime/codex.js +215 -0
  85. package/dist/host-runtime/codex.js.map +1 -0
  86. package/dist/host-runtime/config.d.ts +9 -0
  87. package/dist/host-runtime/config.d.ts.map +1 -0
  88. package/dist/host-runtime/config.js +91 -0
  89. package/dist/host-runtime/config.js.map +1 -0
  90. package/dist/host-runtime/router-client.d.ts +38 -0
  91. package/dist/host-runtime/router-client.d.ts.map +1 -0
  92. package/dist/host-runtime/router-client.js +463 -0
  93. package/dist/host-runtime/router-client.js.map +1 -0
  94. package/dist/host-runtime/router.d.ts +3 -0
  95. package/dist/host-runtime/router.d.ts.map +1 -0
  96. package/dist/host-runtime/router.js +43 -0
  97. package/dist/host-runtime/router.js.map +1 -0
  98. package/dist/mcp/server.js +2 -2
  99. package/dist/mcp/server.js.map +1 -1
  100. package/dist/skills-manifest.json +22 -17
  101. package/dist/storage/conflicts.d.ts.map +1 -1
  102. package/dist/storage/conflicts.js +8 -1
  103. package/dist/storage/conflicts.js.map +1 -1
  104. package/dist/storage/schema.d.ts +1 -1
  105. package/dist/storage/schema.d.ts.map +1 -1
  106. package/dist/storage/schema.js +255 -0
  107. package/dist/storage/schema.js.map +1 -1
  108. package/dist/transports/agent-messaging.d.ts +11 -0
  109. package/dist/transports/agent-messaging.d.ts.map +1 -0
  110. package/dist/transports/agent-messaging.js +273 -0
  111. package/dist/transports/agent-messaging.js.map +1 -0
  112. package/dist/transports/cli/cli.d.ts +6 -1
  113. package/dist/transports/cli/cli.d.ts.map +1 -1
  114. package/dist/transports/cli/cli.js +392 -17
  115. package/dist/transports/cli/cli.js.map +1 -1
  116. package/dist/transports/http/server.d.ts.map +1 -1
  117. package/dist/transports/http/server.js +31 -2
  118. package/dist/transports/http/server.js.map +1 -1
  119. package/dist/transports/mcp/handlers.d.ts +152 -1
  120. package/dist/transports/mcp/handlers.d.ts.map +1 -1
  121. package/dist/transports/mcp/handlers.js +108 -3
  122. package/dist/transports/mcp/handlers.js.map +1 -1
  123. package/dist/transports/schemas.d.ts +106 -1
  124. package/dist/transports/schemas.d.ts.map +1 -1
  125. package/dist/transports/schemas.js +86 -0
  126. package/dist/transports/schemas.js.map +1 -1
  127. package/docs/platforms/README.md +167 -0
  128. package/docs/platforms/agent-messaging.md +290 -0
  129. package/hooks/hooks.json +11 -0
  130. package/llms-install.md +101 -25
  131. package/package.json +14 -5
  132. package/scripts/hooks/_generated/repo-state.js +77 -0
  133. package/scripts/hooks/_generated/schema.js +255 -0
  134. package/scripts/hooks/_generated/task-state.js +3 -3
  135. package/scripts/hooks/_generated/work-topology.js +1 -0
  136. package/scripts/hooks/_shared.js +77 -97
  137. package/scripts/hooks/auto-update-runner.mjs +243 -0
  138. package/scripts/hooks/session-start.js +12 -2
  139. package/scripts/hooks/session-summary.js +7 -6
  140. package/skills/memesh/SKILL.md +40 -1
@@ -16,6 +16,9 @@ import { inspectHosts, allWired } from '../../core/setup.js';
16
16
  import { installHooks } from '../../core/install-hooks.js';
17
17
  import { getTaskState, setTaskState } from '../../core/task-state-store.js';
18
18
  import { TASK_STATE_FIELDS, taskStateLines } from '../../core/task-state.js';
19
+ import { executeAgentMessageAction } from '../agent-messaging.js';
20
+ import { getAgentMessageStorageReport, pruneTerminalAgentMessagePayloads, } from '../../core/agent-message-storage.js';
21
+ import { assertSecureLocalHostRuntimeSupported, ensureRouterTokenFile, } from '../../host-runtime/config.js';
19
22
  async function withDatabase(fn) {
20
23
  try {
21
24
  openDatabase();
@@ -90,6 +93,23 @@ function isOnPath(tool) {
90
93
  }
91
94
  return false;
92
95
  }
96
+ export function createHostConfigAtomically(host, configPath, config) {
97
+ try {
98
+ fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
99
+ }
100
+ catch (error) {
101
+ if (error.code === 'EEXIST') {
102
+ throw new Error(`Managed ${host} config already exists at ${configPath}; it was not overwritten.`, { cause: error });
103
+ }
104
+ throw error;
105
+ }
106
+ }
107
+ export function feedbackBrowserOpenCommand(platform, url) {
108
+ const command = platform === 'darwin' ? 'open'
109
+ : platform === 'win32' ? 'explorer.exe'
110
+ : 'xdg-open';
111
+ return { command, args: [url] };
112
+ }
93
113
  function wireUserHooks() {
94
114
  const r = installHooks({ pluginRoot: packageRoot, pluginVersion: pkg.version, scope: 'user' });
95
115
  return `hooks: added ${r.added}, skipped ${r.skipped} already-installed${r.backupPath ? ` (backup: ${r.backupPath})` : ''}`;
@@ -152,9 +172,11 @@ program
152
172
  console.error('Error: --obs needs some text. An empty or whitespace-only observation is not a memory.');
153
173
  process.exit(1);
154
174
  }
175
+ const supersedes = Array.isArray(opts.supersedes) ? opts.supersedes : [];
176
+ const contradicts = Array.isArray(opts.contradicts) ? opts.contradicts : [];
155
177
  const relations = [
156
- ...(opts.supersedes ?? []).map(to => ({ to, type: 'supersedes' })),
157
- ...(opts.contradicts ?? []).map(to => ({ to, type: 'contradicts' })),
178
+ ...supersedes.map(to => ({ to, type: 'supersedes' })),
179
+ ...contradicts.map(to => ({ to, type: 'contradicts' })),
158
180
  ];
159
181
  await withDatabase(async () => {
160
182
  const result = remember({
@@ -178,13 +200,15 @@ program
178
200
  if (result.superseded?.length) {
179
201
  console.log(` archived as superseded: ${result.superseded.join(', ')}`);
180
202
  }
181
- const contradicted = (result.relationsCreated ?? [])
203
+ const relationsCreated = Array.isArray(result.relationsCreated) ? result.relationsCreated : [];
204
+ const contradicted = relationsCreated
182
205
  .filter(r => r.type === 'contradicts')
183
206
  .map(r => r.to);
184
207
  if (contradicted.length > 0) {
185
208
  console.log(` conflicts stated: ${contradicted.join(', ')}`);
186
209
  }
187
- for (const err of result.relationErrors ?? [])
210
+ const relationErrors = Array.isArray(result.relationErrors) ? result.relationErrors : [];
211
+ for (const err of relationErrors)
188
212
  console.error(` ⚠️ ${err}`);
189
213
  }
190
214
  if (result.relationErrors?.length)
@@ -492,6 +516,331 @@ program
492
516
  }
493
517
  });
494
518
  });
519
+ const messageCmd = program
520
+ .command('message')
521
+ .description('Send, wait for, fetch, and explicitly receipt durable local agent messages');
522
+ function parseCliMessagePayload(raw, contentType) {
523
+ if (contentType !== 'application/json')
524
+ return raw;
525
+ try {
526
+ return JSON.parse(raw);
527
+ }
528
+ catch {
529
+ throw new Error('stdin must contain valid JSON when --content-type is application/json.');
530
+ }
531
+ }
532
+ const MAX_CLI_MESSAGE_STDIN_BYTES = 65_536;
533
+ async function readCliMessagePayloadFromStdin(contentType) {
534
+ let raw = '';
535
+ let bytes = 0;
536
+ for await (const chunk of process.stdin) {
537
+ const text = typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8');
538
+ bytes += Buffer.byteLength(text, 'utf8');
539
+ if (bytes > MAX_CLI_MESSAGE_STDIN_BYTES) {
540
+ throw new Error(`stdin payload exceeds ${MAX_CLI_MESSAGE_STDIN_BYTES} UTF-8 bytes.`);
541
+ }
542
+ raw += text;
543
+ }
544
+ if (bytes === 0) {
545
+ throw new Error('stdin payload is empty.');
546
+ }
547
+ return parseCliMessagePayload(raw, contentType);
548
+ }
549
+ async function runCliMessage(input) {
550
+ await withDatabase(async () => {
551
+ try {
552
+ const result = await executeAgentMessageAction(getDatabase(), input, {
553
+ transport: 'cli',
554
+ sourceHost: 'cli',
555
+ });
556
+ console.log(JSON.stringify(result));
557
+ }
558
+ catch (error) {
559
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
560
+ process.exitCode = 1;
561
+ }
562
+ });
563
+ }
564
+ function isAgentPollResult(value) {
565
+ if (typeof value !== 'object' || value === null)
566
+ return false;
567
+ const candidate = value;
568
+ return Array.isArray(candidate.events) && typeof candidate.next_cursor === 'string';
569
+ }
570
+ messageCmd
571
+ .command('send')
572
+ .description('Durably send one exact-recipient local message (idempotent)')
573
+ .requiredOption('--project <name>', 'Project scope')
574
+ .requiredOption('--sender <id>', 'Stable sender agent/host ID')
575
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
576
+ .option('--target-kind <kind>', 'principal | session', 'principal')
577
+ .requiredOption('--idempotency-key <key>', 'Stable retry key')
578
+ .requiredOption('--payload-stdin', 'Read the complete text or JSON payload from stdin (never argv)')
579
+ .option('--content-type <type>', 'text/plain | application/json', 'text/plain')
580
+ .option('--privacy <scope>', 'private | team', 'private')
581
+ .option('--correlation-id <id>', 'Conversation or task correlation ID')
582
+ .option('--reply-to <message-id>', 'Message ID this replies to')
583
+ .action(async (opts) => {
584
+ requireOneOf(opts.contentType, ['text/plain', 'application/json'], '--content-type');
585
+ requireOneOf(opts.privacy, ['private', 'team'], '--privacy');
586
+ requireOneOf(opts.targetKind, ['principal', 'session'], '--target-kind');
587
+ try {
588
+ await runCliMessage({
589
+ action: 'send',
590
+ project: opts.project,
591
+ sender: opts.sender,
592
+ recipient: opts.recipient,
593
+ target_kind: opts.targetKind,
594
+ idempotency_key: opts.idempotencyKey,
595
+ payload: await readCliMessagePayloadFromStdin(opts.contentType),
596
+ content_type: opts.contentType,
597
+ privacy: opts.privacy,
598
+ correlation_id: opts.correlationId,
599
+ reply_to: opts.replyTo,
600
+ });
601
+ }
602
+ catch (error) {
603
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
604
+ process.exitCode = 1;
605
+ }
606
+ });
607
+ messageCmd
608
+ .command('watch')
609
+ .description('Wait once for an exact-recipient event batch and emit privacy-minimized JSONL')
610
+ .requiredOption('--project <name>', 'Project scope')
611
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
612
+ .option('--cursor <token>', 'Opaque cursor returned by an earlier watch/poll')
613
+ .option('--wait-ms <ms>', 'Bounded wait, 0-30000 milliseconds', wholeNumber('--wait-ms', 0), 30_000)
614
+ .option('--limit <n>', 'Maximum events, 1-100', wholeNumber('--limit'), 20)
615
+ .action(async (opts) => {
616
+ await withDatabase(async () => {
617
+ console.log(JSON.stringify({
618
+ type: 'ready',
619
+ project: opts.project,
620
+ recipient: opts.recipient,
621
+ cursor: opts.cursor ?? null,
622
+ }));
623
+ try {
624
+ const result = await executeAgentMessageAction(getDatabase(), {
625
+ action: 'poll',
626
+ project: opts.project,
627
+ recipient: opts.recipient,
628
+ cursor: opts.cursor,
629
+ wait_ms: opts.waitMs,
630
+ limit: opts.limit,
631
+ }, {
632
+ transport: 'cli',
633
+ sourceHost: 'cli',
634
+ });
635
+ if (!isAgentPollResult(result)) {
636
+ throw new Error('Message watch returned an invalid poll result.');
637
+ }
638
+ console.log(JSON.stringify({
639
+ type: result.events.length > 0 ? 'events' : 'timeout',
640
+ ...result,
641
+ }));
642
+ }
643
+ catch (error) {
644
+ console.error(JSON.stringify({
645
+ type: 'error',
646
+ error: error instanceof Error ? error.message : String(error),
647
+ }));
648
+ process.exitCode = 1;
649
+ }
650
+ });
651
+ });
652
+ messageCmd
653
+ .command('fetch')
654
+ .description('Fetch one payload routed to the exact principal or session without acknowledging it')
655
+ .requiredOption('--project <name>', 'Project scope')
656
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
657
+ .option('--target-kind <kind>', 'principal | session', 'principal')
658
+ .requiredOption('--message-id <id>', 'Message ID')
659
+ .action((opts) => {
660
+ requireOneOf(opts.targetKind, ['principal', 'session'], '--target-kind');
661
+ return runCliMessage({
662
+ action: 'fetch', project: opts.project, recipient: opts.recipient,
663
+ target_kind: opts.targetKind, message_id: opts.messageId,
664
+ });
665
+ });
666
+ messageCmd
667
+ .command('intake')
668
+ .description('Record fetched/ingested state without implying ACK')
669
+ .requiredOption('--project <name>', 'Project scope')
670
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
671
+ .requiredOption('--message-id <id>', 'Message ID')
672
+ .requiredOption('--idempotency-key <key>', 'Stable retry key')
673
+ .requiredOption('--state <state>', 'fetched | ingested')
674
+ .action(async (opts) => {
675
+ requireOneOf(opts.state, ['fetched', 'ingested'], '--state');
676
+ await runCliMessage({
677
+ action: 'intake', project: opts.project, recipient: opts.recipient,
678
+ message_id: opts.messageId, idempotency_key: opts.idempotencyKey, intake_state: opts.state,
679
+ });
680
+ });
681
+ messageCmd
682
+ .command('ack')
683
+ .description('Record explicit recipient acknowledgement')
684
+ .requiredOption('--project <name>', 'Project scope')
685
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
686
+ .requiredOption('--message-id <id>', 'Message ID')
687
+ .requiredOption('--idempotency-key <key>', 'Stable retry key')
688
+ .action((opts) => runCliMessage({
689
+ action: 'ack', project: opts.project, recipient: opts.recipient,
690
+ message_id: opts.messageId, idempotency_key: opts.idempotencyKey,
691
+ }));
692
+ messageCmd
693
+ .command('disposition')
694
+ .description('Record a workflow disposition independently from ACK')
695
+ .requiredOption('--project <name>', 'Project scope')
696
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
697
+ .requiredOption('--message-id <id>', 'Message ID')
698
+ .requiredOption('--idempotency-key <key>', 'Stable retry key')
699
+ .requiredOption('--value <state>', 'accepted | rejected | completed | cancelled | deferred')
700
+ .option('--detail <text>', 'Optional bounded explanation')
701
+ .action(async (opts) => {
702
+ requireOneOf(opts.value, ['accepted', 'rejected', 'completed', 'cancelled', 'deferred'], '--value');
703
+ await runCliMessage({
704
+ action: 'disposition', project: opts.project, recipient: opts.recipient,
705
+ message_id: opts.messageId, idempotency_key: opts.idempotencyKey,
706
+ disposition: opts.value, detail: opts.detail,
707
+ });
708
+ });
709
+ messageCmd
710
+ .command('activation')
711
+ .description('Record host activation outcome independently from ACK/disposition')
712
+ .requiredOption('--project <name>', 'Project scope')
713
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
714
+ .requiredOption('--message-id <id>', 'Message ID')
715
+ .requiredOption('--idempotency-key <key>', 'Stable retry key')
716
+ .requiredOption('--value <state>', 'woken | manual_resume_required | unsupported | failed')
717
+ .option('--detail <text>', 'Optional bounded explanation')
718
+ .action(async (opts) => {
719
+ requireOneOf(opts.value, ['woken', 'manual_resume_required', 'unsupported', 'failed'], '--value');
720
+ await runCliMessage({
721
+ action: 'activation', project: opts.project, recipient: opts.recipient,
722
+ message_id: opts.messageId, idempotency_key: opts.idempotencyKey,
723
+ activation: opts.value, detail: opts.detail,
724
+ });
725
+ });
726
+ messageCmd
727
+ .command('receipts')
728
+ .description('Read receipt facts for one message routed to the logical recipient')
729
+ .requiredOption('--project <name>', 'Project scope')
730
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
731
+ .requiredOption('--message-id <id>', 'Message ID')
732
+ .action((opts) => runCliMessage({
733
+ action: 'receipts', project: opts.project, recipient: opts.recipient, message_id: opts.messageId,
734
+ }));
735
+ const messageStorageCmd = messageCmd
736
+ .command('storage')
737
+ .description('Inspect or bound durable agent-message payload storage without deleting lifecycle audit facts');
738
+ messageStorageCmd
739
+ .command('report')
740
+ .description('Report logical payload, unresolved protection, reusable pages, and SQLite/WAL size')
741
+ .requiredOption('--cutoff <iso-time>', 'Terminal workflows strictly older than this ISO timestamp are prunable')
742
+ .action(async (opts) => {
743
+ await withDatabase(() => {
744
+ const report = getAgentMessageStorageReport(getDatabase(), {
745
+ cutoff: opts.cutoff,
746
+ databasePath: getDbPath(),
747
+ });
748
+ console.log(JSON.stringify({
749
+ policy: {
750
+ cutoff: new Date(opts.cutoff).toISOString(),
751
+ quota_bytes: process.env.MEMESH_AGENT_MESSAGE_STORAGE_QUOTA_BYTES ?? null,
752
+ automatic_pruning: false,
753
+ },
754
+ ...report,
755
+ }));
756
+ });
757
+ });
758
+ messageStorageCmd
759
+ .command('prune')
760
+ .description('Dry-run one bounded terminal-payload tombstone batch; --apply performs it')
761
+ .requiredOption('--cutoff <iso-time>', 'Terminal workflows strictly older than this ISO timestamp are eligible')
762
+ .option('--batch-size <n>', 'Maximum payloads in this transaction, 1-1000', wholeNumber('--batch-size'), 100)
763
+ .option('--apply', 'Write hash-bound tombstones; without this flag nothing is changed')
764
+ .option('--actor <id>', 'Bounded audit actor', 'local-owner-cli')
765
+ .action(async (opts) => {
766
+ await withDatabase(() => {
767
+ const result = pruneTerminalAgentMessagePayloads(getDatabase(), {
768
+ cutoff: opts.cutoff,
769
+ databasePath: getDbPath(),
770
+ batchSize: opts.batchSize,
771
+ dryRun: !opts.apply,
772
+ actor: opts.actor,
773
+ });
774
+ console.log(JSON.stringify(result));
775
+ });
776
+ });
777
+ const agentCmd = program
778
+ .command('agent')
779
+ .description('Set up reusable owner-private local host configuration');
780
+ agentCmd
781
+ .command('setup')
782
+ .argument('<host>', 'codex-session | codex | claude | gemini')
783
+ .requiredOption('--project <name>', 'Project scope used for exact routing')
784
+ .requiredOption('--principal <id>', 'Stable logical recipient ID')
785
+ .option('--workspace <path>', 'Managed Codex/Gemini workspace', process.cwd())
786
+ .option('--json', 'Output machine-readable setup result')
787
+ .action((host, opts) => {
788
+ requireOneOf(host, ['codex-session', 'codex', 'claude', 'gemini'], '<host>');
789
+ assertSecureLocalHostRuntimeSupported();
790
+ const messageDir = path.dirname(getDbPath());
791
+ const hostsDir = path.join(messageDir, 'hosts');
792
+ fs.mkdirSync(hostsDir, { recursive: true, mode: 0o700 });
793
+ const hostsStat = fs.lstatSync(hostsDir);
794
+ if (!hostsStat.isDirectory() || hostsStat.isSymbolicLink() || (hostsStat.mode & 0o077) !== 0) {
795
+ throw new Error('The managed host config directory must be a real owner-private directory.');
796
+ }
797
+ const filename = host === 'gemini' ? 'gemini-acp.json' : `${host}.json`;
798
+ const configPath = path.join(hostsDir, filename);
799
+ const routerTokenFile = path.join(messageDir, 'agent-router.token');
800
+ ensureRouterTokenFile(routerTokenFile);
801
+ const common = {
802
+ router_socket: path.join(messageDir, 'agent-router.sock'),
803
+ token_file: routerTokenFile,
804
+ project: opts.project,
805
+ principal_id: opts.principal,
806
+ };
807
+ const config = host === 'codex-session'
808
+ ? { ...common, workspace: fs.realpathSync(path.resolve(opts.workspace)) }
809
+ : host === 'codex'
810
+ ? { ...common, control_socket: path.join(hostsDir, 'codex-app-server.sock'), workspace: path.resolve(opts.workspace) }
811
+ : host === 'claude'
812
+ ? { ...common, server_name: 'memesh-channel' }
813
+ : { ...common, workspace: path.resolve(opts.workspace), command: 'gemini', args: [] };
814
+ createHostConfigAtomically(host, configPath, config);
815
+ const launchCommand = host === 'codex-session'
816
+ ? null
817
+ : host === 'codex'
818
+ ? `memesh-host-codex --config ${JSON.stringify(configPath)}`
819
+ : host === 'claude'
820
+ ? 'claude --dangerously-load-development-channels server:memesh-channel'
821
+ : `memesh-host-acp --config ${JSON.stringify(configPath)}`;
822
+ const registrationCommand = host === 'claude'
823
+ ? `claude mcp add --transport stdio --scope user memesh-channel -- memesh-host-claude --config ${JSON.stringify(configPath)}`
824
+ : null;
825
+ const result = {
826
+ host,
827
+ config_path: configPath,
828
+ mode: host === 'codex-session'
829
+ ? 'ordinary-session-native-queue'
830
+ : host === 'claude' ? 'session-owned-channel' : 'memesh-managed-session',
831
+ session_identity: host === 'codex-session' ? 'codex-thread-id-at-session-start' : 'generated-per-process',
832
+ ordinary_sessions: host === 'codex-session' ? 'explicit-workspace-opt-in' : 'presence-only/inbound-unavailable',
833
+ registration_command: registrationCommand,
834
+ launch_command: launchCommand,
835
+ next_command: registrationCommand ?? launchCommand ?? 'Restart Codex in the configured workspace',
836
+ };
837
+ console.log(opts.json ? JSON.stringify(result) : [
838
+ `Created owner-private ${host} config: ${configPath}`,
839
+ 'No active or stopped ordinary session was attached.',
840
+ ...(registrationCommand ? [`Register once: ${registrationCommand}`] : []),
841
+ ...(launchCommand ? [`Launch: ${launchCommand}`] : ['Restart Codex in the configured workspace.']),
842
+ ].join('\n'));
843
+ });
495
844
  program
496
845
  .command('briefing')
497
846
  .description('The assembled work topology for a project — task state, decisions, lessons, knowledge, recent activity')
@@ -639,7 +988,8 @@ program
639
988
  console.log('\nPlanned actions:');
640
989
  for (const st of pending)
641
990
  for (const a of st.actions) {
642
- console.log(` • [${st.title}] ${a.label}${a.cmd ? `\n ${a.cmd} ${(a.args ?? []).join(' ')}` : ''}`);
991
+ const actionArgs = Array.isArray(a.args) ? a.args : [];
992
+ console.log(` • [${st.title}] ${a.label}${a.cmd ? `\n ${a.cmd} ${actionArgs.join(' ')}` : ''}`);
643
993
  }
644
994
  if (!opts.yes && !process.stdin.isTTY) {
645
995
  console.error('\nNot a terminal and --yes not given — nothing was changed. Re-run with: memesh setup --yes');
@@ -665,9 +1015,10 @@ program
665
1015
  console.log(` ✅ ${wireUserHooks()}`);
666
1016
  }
667
1017
  else if (action.cmd) {
668
- const r = runSeam(action.cmd, action.args ?? []);
1018
+ const actionArgs = Array.isArray(action.args) ? action.args : [];
1019
+ const r = runSeam(action.cmd, actionArgs);
669
1020
  if (r.status === 0)
670
- console.log(` ✅ done (${action.cmd} ${(action.args ?? []).join(' ')})`);
1021
+ console.log(` ✅ done (${action.cmd} ${actionArgs.join(' ')})`);
671
1022
  else {
672
1023
  console.error(` ❌ ${action.cmd} exited ${r.status ?? 'without running'}${r.stderr ? `: ${r.stderr.trim()}` : ''}`);
673
1024
  failed = true;
@@ -1598,7 +1949,8 @@ dreamCmd
1598
1949
  console.log('');
1599
1950
  for (const p of proposals) {
1600
1951
  const srcLabel = p.kind === 'relation' ? ' (conflict)'
1601
- : p.source_kind === 'transcript' ? ' (transcript)' : '';
1952
+ : p.kind === 'product_improvement' ? ' (product improvement)'
1953
+ : p.source_kind === 'transcript' ? ' (transcript)' : '';
1602
1954
  console.log(` #${p.id} [${p.project}/${p.cluster_key}]${srcLabel} ${p.source_count} source(s) → "${p.digest_name}"`);
1603
1955
  if (p.digest_observations_preview !== null) {
1604
1956
  console.log(` ${p.digest_observations_preview}`);
@@ -1698,6 +2050,11 @@ dreamCmd
1698
2050
  console.log(`Accept: memesh dream accept ${detail.id} | Reject: memesh dream reject ${detail.id}`);
1699
2051
  return;
1700
2052
  }
2053
+ if (detail.kind === 'product_improvement') {
2054
+ const improvement = detail.digest;
2055
+ console.log(`title: ${improvement.title ?? detail.digest.name}`);
2056
+ console.log('authority: human review required; acceptance does not mean implemented or effective');
2057
+ }
1701
2058
  console.log(`name: ${detail.digest.name}`);
1702
2059
  console.log(`type: ${detail.digest.type}`);
1703
2060
  console.log(`observations (${detail.digest.observations.length}):`);
@@ -1712,7 +2069,7 @@ dreamCmd
1712
2069
  });
1713
2070
  dreamCmd
1714
2071
  .command('accept <id>')
1715
- .description('Accept a pending proposal creates digest entity, soft-archives sources')
2072
+ .description('Apply a reviewed pending proposal (behaviour depends on proposal kind)')
1716
2073
  .action(async (id) => {
1717
2074
  await withDatabase(async () => {
1718
2075
  const { applyProposal } = await import('../../core/dreamer.js');
@@ -1729,8 +2086,15 @@ dreamCmd
1729
2086
  process.exit(1);
1730
2087
  }
1731
2088
  console.log(`Applied proposal #${result.proposalId}`);
1732
- console.log(` digest entity: ${result.digestEntityName}`);
1733
- console.log(` sources archived: ${result.sourcesArchived}`);
2089
+ if (result.kind === 'product_improvement') {
2090
+ console.log(` product improvement: ${result.digestEntityName}`);
2091
+ console.log(` source memories preserved: ${result.sourcesLinked ?? 0}`);
2092
+ console.log(' state: accepted for product work; implementation and outcome remain unverified');
2093
+ }
2094
+ else {
2095
+ console.log(` digest entity: ${result.digestEntityName}`);
2096
+ console.log(` sources archived: ${result.sourcesArchived}`);
2097
+ }
1734
2098
  });
1735
2099
  });
1736
2100
  dreamCmd
@@ -1898,12 +2262,9 @@ program
1898
2262
  console.log('Re-run with --no-diagnostics to leave out the install ID and the doctor report.');
1899
2263
  }
1900
2264
  const { spawn } = await import('child_process');
1901
- const cmd = process.platform === 'darwin' ? 'open'
1902
- : process.platform === 'win32' ? 'cmd'
1903
- : 'xdg-open';
1904
- const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url];
2265
+ const { command, args } = feedbackBrowserOpenCommand(process.platform, url);
1905
2266
  try {
1906
- const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
2267
+ const child = spawn(command, args, { stdio: 'ignore', detached: true });
1907
2268
  child.unref();
1908
2269
  console.log(`Opened browser to file ${fbType} issue.`);
1909
2270
  console.log('Edit the title + body before submitting.');
@@ -2110,5 +2471,19 @@ program.action(async () => {
2110
2471
  program.outputHelp();
2111
2472
  process.exitCode = 0;
2112
2473
  });
2113
- program.parse();
2474
+ export async function runCli(argv = process.argv) {
2475
+ await program.parseAsync([...argv]);
2476
+ }
2477
+ const cliEntryPath = process.argv[1];
2478
+ if (cliEntryPath && isExecutedModule(cliEntryPath, import.meta.url)) {
2479
+ await runCli();
2480
+ }
2481
+ function isExecutedModule(entryPath, moduleUrl) {
2482
+ try {
2483
+ return fs.realpathSync(entryPath) === fs.realpathSync(fileURLToPath(moduleUrl));
2484
+ }
2485
+ catch {
2486
+ return false;
2487
+ }
2488
+ }
2114
2489
  //# sourceMappingURL=cli.js.map