@pcircle/memesh 4.7.2 → 4.8.0

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 (141) 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 +330 -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 +24 -19
  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 +1 -1
  113. package/dist/transports/cli/cli.d.ts.map +1 -1
  114. package/dist/transports/cli/cli.js +376 -12
  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/post-commit.js +13 -1
  139. package/scripts/hooks/session-start.js +12 -2
  140. package/scripts/hooks/session-summary.js +20 -8
  141. 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();
@@ -152,9 +155,11 @@ program
152
155
  console.error('Error: --obs needs some text. An empty or whitespace-only observation is not a memory.');
153
156
  process.exit(1);
154
157
  }
158
+ const supersedes = Array.isArray(opts.supersedes) ? opts.supersedes : [];
159
+ const contradicts = Array.isArray(opts.contradicts) ? opts.contradicts : [];
155
160
  const relations = [
156
- ...(opts.supersedes ?? []).map(to => ({ to, type: 'supersedes' })),
157
- ...(opts.contradicts ?? []).map(to => ({ to, type: 'contradicts' })),
161
+ ...supersedes.map(to => ({ to, type: 'supersedes' })),
162
+ ...contradicts.map(to => ({ to, type: 'contradicts' })),
158
163
  ];
159
164
  await withDatabase(async () => {
160
165
  const result = remember({
@@ -178,13 +183,15 @@ program
178
183
  if (result.superseded?.length) {
179
184
  console.log(` archived as superseded: ${result.superseded.join(', ')}`);
180
185
  }
181
- const contradicted = (result.relationsCreated ?? [])
186
+ const relationsCreated = Array.isArray(result.relationsCreated) ? result.relationsCreated : [];
187
+ const contradicted = relationsCreated
182
188
  .filter(r => r.type === 'contradicts')
183
189
  .map(r => r.to);
184
190
  if (contradicted.length > 0) {
185
191
  console.log(` conflicts stated: ${contradicted.join(', ')}`);
186
192
  }
187
- for (const err of result.relationErrors ?? [])
193
+ const relationErrors = Array.isArray(result.relationErrors) ? result.relationErrors : [];
194
+ for (const err of relationErrors)
188
195
  console.error(` ⚠️ ${err}`);
189
196
  }
190
197
  if (result.relationErrors?.length)
@@ -492,6 +499,334 @@ program
492
499
  }
493
500
  });
494
501
  });
502
+ const messageCmd = program
503
+ .command('message')
504
+ .description('Send, wait for, fetch, and explicitly receipt durable local agent messages');
505
+ function parseCliMessagePayload(raw, contentType) {
506
+ if (contentType !== 'application/json')
507
+ return raw;
508
+ try {
509
+ return JSON.parse(raw);
510
+ }
511
+ catch {
512
+ throw new Error('stdin must contain valid JSON when --content-type is application/json.');
513
+ }
514
+ }
515
+ const MAX_CLI_MESSAGE_STDIN_BYTES = 65_536;
516
+ async function readCliMessagePayloadFromStdin(contentType) {
517
+ let raw = '';
518
+ let bytes = 0;
519
+ for await (const chunk of process.stdin) {
520
+ const text = typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8');
521
+ bytes += Buffer.byteLength(text, 'utf8');
522
+ if (bytes > MAX_CLI_MESSAGE_STDIN_BYTES) {
523
+ throw new Error(`stdin payload exceeds ${MAX_CLI_MESSAGE_STDIN_BYTES} UTF-8 bytes.`);
524
+ }
525
+ raw += text;
526
+ }
527
+ if (bytes === 0) {
528
+ throw new Error('stdin payload is empty.');
529
+ }
530
+ return parseCliMessagePayload(raw, contentType);
531
+ }
532
+ async function runCliMessage(input) {
533
+ await withDatabase(async () => {
534
+ try {
535
+ const result = await executeAgentMessageAction(getDatabase(), input, {
536
+ transport: 'cli',
537
+ sourceHost: 'cli',
538
+ });
539
+ console.log(JSON.stringify(result));
540
+ }
541
+ catch (error) {
542
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
543
+ process.exitCode = 1;
544
+ }
545
+ });
546
+ }
547
+ function isAgentPollResult(value) {
548
+ if (typeof value !== 'object' || value === null)
549
+ return false;
550
+ const candidate = value;
551
+ return Array.isArray(candidate.events) && typeof candidate.next_cursor === 'string';
552
+ }
553
+ messageCmd
554
+ .command('send')
555
+ .description('Durably send one exact-recipient local message (idempotent)')
556
+ .requiredOption('--project <name>', 'Project scope')
557
+ .requiredOption('--sender <id>', 'Stable sender agent/host ID')
558
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
559
+ .option('--target-kind <kind>', 'principal | session', 'principal')
560
+ .requiredOption('--idempotency-key <key>', 'Stable retry key')
561
+ .requiredOption('--payload-stdin', 'Read the complete text or JSON payload from stdin (never argv)')
562
+ .option('--content-type <type>', 'text/plain | application/json', 'text/plain')
563
+ .option('--privacy <scope>', 'private | team', 'private')
564
+ .option('--correlation-id <id>', 'Conversation or task correlation ID')
565
+ .option('--reply-to <message-id>', 'Message ID this replies to')
566
+ .action(async (opts) => {
567
+ requireOneOf(opts.contentType, ['text/plain', 'application/json'], '--content-type');
568
+ requireOneOf(opts.privacy, ['private', 'team'], '--privacy');
569
+ requireOneOf(opts.targetKind, ['principal', 'session'], '--target-kind');
570
+ try {
571
+ await runCliMessage({
572
+ action: 'send',
573
+ project: opts.project,
574
+ sender: opts.sender,
575
+ recipient: opts.recipient,
576
+ target_kind: opts.targetKind,
577
+ idempotency_key: opts.idempotencyKey,
578
+ payload: await readCliMessagePayloadFromStdin(opts.contentType),
579
+ content_type: opts.contentType,
580
+ privacy: opts.privacy,
581
+ correlation_id: opts.correlationId,
582
+ reply_to: opts.replyTo,
583
+ });
584
+ }
585
+ catch (error) {
586
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
587
+ process.exitCode = 1;
588
+ }
589
+ });
590
+ messageCmd
591
+ .command('watch')
592
+ .description('Wait once for an exact-recipient event batch and emit privacy-minimized JSONL')
593
+ .requiredOption('--project <name>', 'Project scope')
594
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
595
+ .option('--cursor <token>', 'Opaque cursor returned by an earlier watch/poll')
596
+ .option('--wait-ms <ms>', 'Bounded wait, 0-30000 milliseconds', wholeNumber('--wait-ms', 0), 30_000)
597
+ .option('--limit <n>', 'Maximum events, 1-100', wholeNumber('--limit'), 20)
598
+ .action(async (opts) => {
599
+ await withDatabase(async () => {
600
+ console.log(JSON.stringify({
601
+ type: 'ready',
602
+ project: opts.project,
603
+ recipient: opts.recipient,
604
+ cursor: opts.cursor ?? null,
605
+ }));
606
+ try {
607
+ const result = await executeAgentMessageAction(getDatabase(), {
608
+ action: 'poll',
609
+ project: opts.project,
610
+ recipient: opts.recipient,
611
+ cursor: opts.cursor,
612
+ wait_ms: opts.waitMs,
613
+ limit: opts.limit,
614
+ }, {
615
+ transport: 'cli',
616
+ sourceHost: 'cli',
617
+ });
618
+ if (!isAgentPollResult(result)) {
619
+ throw new Error('Message watch returned an invalid poll result.');
620
+ }
621
+ console.log(JSON.stringify({
622
+ type: result.events.length > 0 ? 'events' : 'timeout',
623
+ ...result,
624
+ }));
625
+ }
626
+ catch (error) {
627
+ console.error(JSON.stringify({
628
+ type: 'error',
629
+ error: error instanceof Error ? error.message : String(error),
630
+ }));
631
+ process.exitCode = 1;
632
+ }
633
+ });
634
+ });
635
+ messageCmd
636
+ .command('fetch')
637
+ .description('Fetch one payload routed to the exact principal or session without acknowledging it')
638
+ .requiredOption('--project <name>', 'Project scope')
639
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
640
+ .option('--target-kind <kind>', 'principal | session', 'principal')
641
+ .requiredOption('--message-id <id>', 'Message ID')
642
+ .action((opts) => {
643
+ requireOneOf(opts.targetKind, ['principal', 'session'], '--target-kind');
644
+ return runCliMessage({
645
+ action: 'fetch', project: opts.project, recipient: opts.recipient,
646
+ target_kind: opts.targetKind, message_id: opts.messageId,
647
+ });
648
+ });
649
+ messageCmd
650
+ .command('intake')
651
+ .description('Record fetched/ingested state without implying ACK')
652
+ .requiredOption('--project <name>', 'Project scope')
653
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
654
+ .requiredOption('--message-id <id>', 'Message ID')
655
+ .requiredOption('--idempotency-key <key>', 'Stable retry key')
656
+ .requiredOption('--state <state>', 'fetched | ingested')
657
+ .action(async (opts) => {
658
+ requireOneOf(opts.state, ['fetched', 'ingested'], '--state');
659
+ await runCliMessage({
660
+ action: 'intake', project: opts.project, recipient: opts.recipient,
661
+ message_id: opts.messageId, idempotency_key: opts.idempotencyKey, intake_state: opts.state,
662
+ });
663
+ });
664
+ messageCmd
665
+ .command('ack')
666
+ .description('Record explicit recipient acknowledgement')
667
+ .requiredOption('--project <name>', 'Project scope')
668
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
669
+ .requiredOption('--message-id <id>', 'Message ID')
670
+ .requiredOption('--idempotency-key <key>', 'Stable retry key')
671
+ .action((opts) => runCliMessage({
672
+ action: 'ack', project: opts.project, recipient: opts.recipient,
673
+ message_id: opts.messageId, idempotency_key: opts.idempotencyKey,
674
+ }));
675
+ messageCmd
676
+ .command('disposition')
677
+ .description('Record a workflow disposition independently from ACK')
678
+ .requiredOption('--project <name>', 'Project scope')
679
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
680
+ .requiredOption('--message-id <id>', 'Message ID')
681
+ .requiredOption('--idempotency-key <key>', 'Stable retry key')
682
+ .requiredOption('--value <state>', 'accepted | rejected | completed | cancelled | deferred')
683
+ .option('--detail <text>', 'Optional bounded explanation')
684
+ .action(async (opts) => {
685
+ requireOneOf(opts.value, ['accepted', 'rejected', 'completed', 'cancelled', 'deferred'], '--value');
686
+ await runCliMessage({
687
+ action: 'disposition', project: opts.project, recipient: opts.recipient,
688
+ message_id: opts.messageId, idempotency_key: opts.idempotencyKey,
689
+ disposition: opts.value, detail: opts.detail,
690
+ });
691
+ });
692
+ messageCmd
693
+ .command('activation')
694
+ .description('Record host activation outcome independently from ACK/disposition')
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>', 'woken | manual_resume_required | unsupported | failed')
700
+ .option('--detail <text>', 'Optional bounded explanation')
701
+ .action(async (opts) => {
702
+ requireOneOf(opts.value, ['woken', 'manual_resume_required', 'unsupported', 'failed'], '--value');
703
+ await runCliMessage({
704
+ action: 'activation', project: opts.project, recipient: opts.recipient,
705
+ message_id: opts.messageId, idempotency_key: opts.idempotencyKey,
706
+ activation: opts.value, detail: opts.detail,
707
+ });
708
+ });
709
+ messageCmd
710
+ .command('receipts')
711
+ .description('Read receipt facts for one message routed to the logical recipient')
712
+ .requiredOption('--project <name>', 'Project scope')
713
+ .requiredOption('--recipient <id>', 'Stable recipient agent/host ID')
714
+ .requiredOption('--message-id <id>', 'Message ID')
715
+ .action((opts) => runCliMessage({
716
+ action: 'receipts', project: opts.project, recipient: opts.recipient, message_id: opts.messageId,
717
+ }));
718
+ const messageStorageCmd = messageCmd
719
+ .command('storage')
720
+ .description('Inspect or bound durable agent-message payload storage without deleting lifecycle audit facts');
721
+ messageStorageCmd
722
+ .command('report')
723
+ .description('Report logical payload, unresolved protection, reusable pages, and SQLite/WAL size')
724
+ .requiredOption('--cutoff <iso-time>', 'Terminal workflows strictly older than this ISO timestamp are prunable')
725
+ .action(async (opts) => {
726
+ await withDatabase(() => {
727
+ const report = getAgentMessageStorageReport(getDatabase(), {
728
+ cutoff: opts.cutoff,
729
+ databasePath: getDbPath(),
730
+ });
731
+ console.log(JSON.stringify({
732
+ policy: {
733
+ cutoff: new Date(opts.cutoff).toISOString(),
734
+ quota_bytes: process.env.MEMESH_AGENT_MESSAGE_STORAGE_QUOTA_BYTES ?? null,
735
+ automatic_pruning: false,
736
+ },
737
+ ...report,
738
+ }));
739
+ });
740
+ });
741
+ messageStorageCmd
742
+ .command('prune')
743
+ .description('Dry-run one bounded terminal-payload tombstone batch; --apply performs it')
744
+ .requiredOption('--cutoff <iso-time>', 'Terminal workflows strictly older than this ISO timestamp are eligible')
745
+ .option('--batch-size <n>', 'Maximum payloads in this transaction, 1-1000', wholeNumber('--batch-size'), 100)
746
+ .option('--apply', 'Write hash-bound tombstones; without this flag nothing is changed')
747
+ .option('--actor <id>', 'Bounded audit actor', 'local-owner-cli')
748
+ .action(async (opts) => {
749
+ await withDatabase(() => {
750
+ const result = pruneTerminalAgentMessagePayloads(getDatabase(), {
751
+ cutoff: opts.cutoff,
752
+ databasePath: getDbPath(),
753
+ batchSize: opts.batchSize,
754
+ dryRun: !opts.apply,
755
+ actor: opts.actor,
756
+ });
757
+ console.log(JSON.stringify(result));
758
+ });
759
+ });
760
+ const agentCmd = program
761
+ .command('agent')
762
+ .description('Set up reusable owner-private local host configuration');
763
+ agentCmd
764
+ .command('setup')
765
+ .argument('<host>', 'codex-session | codex | claude | gemini')
766
+ .requiredOption('--project <name>', 'Project scope used for exact routing')
767
+ .requiredOption('--principal <id>', 'Stable logical recipient ID')
768
+ .option('--workspace <path>', 'Managed Codex/Gemini workspace', process.cwd())
769
+ .option('--json', 'Output machine-readable setup result')
770
+ .action((host, opts) => {
771
+ requireOneOf(host, ['codex-session', 'codex', 'claude', 'gemini'], '<host>');
772
+ assertSecureLocalHostRuntimeSupported();
773
+ const messageDir = path.dirname(getDbPath());
774
+ const hostsDir = path.join(messageDir, 'hosts');
775
+ fs.mkdirSync(hostsDir, { recursive: true, mode: 0o700 });
776
+ const hostsStat = fs.lstatSync(hostsDir);
777
+ if (!hostsStat.isDirectory() || hostsStat.isSymbolicLink() || (hostsStat.mode & 0o077) !== 0) {
778
+ throw new Error('The managed host config directory must be a real owner-private directory.');
779
+ }
780
+ const filename = host === 'gemini' ? 'gemini-acp.json' : `${host}.json`;
781
+ const configPath = path.join(hostsDir, filename);
782
+ if (fs.existsSync(configPath)) {
783
+ throw new Error(`Managed ${host} config already exists at ${configPath}; it was not overwritten.`);
784
+ }
785
+ const routerTokenFile = path.join(messageDir, 'agent-router.token');
786
+ ensureRouterTokenFile(routerTokenFile);
787
+ const common = {
788
+ router_socket: path.join(messageDir, 'agent-router.sock'),
789
+ token_file: routerTokenFile,
790
+ project: opts.project,
791
+ principal_id: opts.principal,
792
+ };
793
+ const config = host === 'codex-session'
794
+ ? { ...common, workspace: fs.realpathSync(path.resolve(opts.workspace)) }
795
+ : host === 'codex'
796
+ ? { ...common, control_socket: path.join(hostsDir, 'codex-app-server.sock'), workspace: path.resolve(opts.workspace) }
797
+ : host === 'claude'
798
+ ? { ...common, server_name: 'memesh-channel' }
799
+ : { ...common, workspace: path.resolve(opts.workspace), command: 'gemini', args: [] };
800
+ fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
801
+ const launchCommand = host === 'codex-session'
802
+ ? null
803
+ : host === 'codex'
804
+ ? `memesh-host-codex --config ${JSON.stringify(configPath)}`
805
+ : host === 'claude'
806
+ ? 'claude --dangerously-load-development-channels server:memesh-channel'
807
+ : `memesh-host-acp --config ${JSON.stringify(configPath)}`;
808
+ const registrationCommand = host === 'claude'
809
+ ? `claude mcp add --transport stdio --scope user memesh-channel -- memesh-host-claude --config ${JSON.stringify(configPath)}`
810
+ : null;
811
+ const result = {
812
+ host,
813
+ config_path: configPath,
814
+ mode: host === 'codex-session'
815
+ ? 'ordinary-session-native-queue'
816
+ : host === 'claude' ? 'session-owned-channel' : 'memesh-managed-session',
817
+ session_identity: host === 'codex-session' ? 'codex-thread-id-at-session-start' : 'generated-per-process',
818
+ ordinary_sessions: host === 'codex-session' ? 'explicit-workspace-opt-in' : 'presence-only/inbound-unavailable',
819
+ registration_command: registrationCommand,
820
+ launch_command: launchCommand,
821
+ next_command: registrationCommand ?? launchCommand ?? 'Restart Codex in the configured workspace',
822
+ };
823
+ console.log(opts.json ? JSON.stringify(result) : [
824
+ `Created owner-private ${host} config: ${configPath}`,
825
+ 'No active or stopped ordinary session was attached.',
826
+ ...(registrationCommand ? [`Register once: ${registrationCommand}`] : []),
827
+ ...(launchCommand ? [`Launch: ${launchCommand}`] : ['Restart Codex in the configured workspace.']),
828
+ ].join('\n'));
829
+ });
495
830
  program
496
831
  .command('briefing')
497
832
  .description('The assembled work topology for a project — task state, decisions, lessons, knowledge, recent activity')
@@ -639,7 +974,8 @@ program
639
974
  console.log('\nPlanned actions:');
640
975
  for (const st of pending)
641
976
  for (const a of st.actions) {
642
- console.log(` • [${st.title}] ${a.label}${a.cmd ? `\n ${a.cmd} ${(a.args ?? []).join(' ')}` : ''}`);
977
+ const actionArgs = Array.isArray(a.args) ? a.args : [];
978
+ console.log(` • [${st.title}] ${a.label}${a.cmd ? `\n ${a.cmd} ${actionArgs.join(' ')}` : ''}`);
643
979
  }
644
980
  if (!opts.yes && !process.stdin.isTTY) {
645
981
  console.error('\nNot a terminal and --yes not given — nothing was changed. Re-run with: memesh setup --yes');
@@ -665,9 +1001,10 @@ program
665
1001
  console.log(` ✅ ${wireUserHooks()}`);
666
1002
  }
667
1003
  else if (action.cmd) {
668
- const r = runSeam(action.cmd, action.args ?? []);
1004
+ const actionArgs = Array.isArray(action.args) ? action.args : [];
1005
+ const r = runSeam(action.cmd, actionArgs);
669
1006
  if (r.status === 0)
670
- console.log(` ✅ done (${action.cmd} ${(action.args ?? []).join(' ')})`);
1007
+ console.log(` ✅ done (${action.cmd} ${actionArgs.join(' ')})`);
671
1008
  else {
672
1009
  console.error(` ❌ ${action.cmd} exited ${r.status ?? 'without running'}${r.stderr ? `: ${r.stderr.trim()}` : ''}`);
673
1010
  failed = true;
@@ -1598,7 +1935,8 @@ dreamCmd
1598
1935
  console.log('');
1599
1936
  for (const p of proposals) {
1600
1937
  const srcLabel = p.kind === 'relation' ? ' (conflict)'
1601
- : p.source_kind === 'transcript' ? ' (transcript)' : '';
1938
+ : p.kind === 'product_improvement' ? ' (product improvement)'
1939
+ : p.source_kind === 'transcript' ? ' (transcript)' : '';
1602
1940
  console.log(` #${p.id} [${p.project}/${p.cluster_key}]${srcLabel} ${p.source_count} source(s) → "${p.digest_name}"`);
1603
1941
  if (p.digest_observations_preview !== null) {
1604
1942
  console.log(` ${p.digest_observations_preview}`);
@@ -1698,6 +2036,11 @@ dreamCmd
1698
2036
  console.log(`Accept: memesh dream accept ${detail.id} | Reject: memesh dream reject ${detail.id}`);
1699
2037
  return;
1700
2038
  }
2039
+ if (detail.kind === 'product_improvement') {
2040
+ const improvement = detail.digest;
2041
+ console.log(`title: ${improvement.title ?? detail.digest.name}`);
2042
+ console.log('authority: human review required; acceptance does not mean implemented or effective');
2043
+ }
1701
2044
  console.log(`name: ${detail.digest.name}`);
1702
2045
  console.log(`type: ${detail.digest.type}`);
1703
2046
  console.log(`observations (${detail.digest.observations.length}):`);
@@ -1712,7 +2055,7 @@ dreamCmd
1712
2055
  });
1713
2056
  dreamCmd
1714
2057
  .command('accept <id>')
1715
- .description('Accept a pending proposal creates digest entity, soft-archives sources')
2058
+ .description('Apply a reviewed pending proposal (behaviour depends on proposal kind)')
1716
2059
  .action(async (id) => {
1717
2060
  await withDatabase(async () => {
1718
2061
  const { applyProposal } = await import('../../core/dreamer.js');
@@ -1729,8 +2072,15 @@ dreamCmd
1729
2072
  process.exit(1);
1730
2073
  }
1731
2074
  console.log(`Applied proposal #${result.proposalId}`);
1732
- console.log(` digest entity: ${result.digestEntityName}`);
1733
- console.log(` sources archived: ${result.sourcesArchived}`);
2075
+ if (result.kind === 'product_improvement') {
2076
+ console.log(` product improvement: ${result.digestEntityName}`);
2077
+ console.log(` source memories preserved: ${result.sourcesLinked ?? 0}`);
2078
+ console.log(' state: accepted for product work; implementation and outcome remain unverified');
2079
+ }
2080
+ else {
2081
+ console.log(` digest entity: ${result.digestEntityName}`);
2082
+ console.log(` sources archived: ${result.sourcesArchived}`);
2083
+ }
1734
2084
  });
1735
2085
  });
1736
2086
  dreamCmd
@@ -2110,5 +2460,19 @@ program.action(async () => {
2110
2460
  program.outputHelp();
2111
2461
  process.exitCode = 0;
2112
2462
  });
2113
- program.parse();
2463
+ export async function runCli(argv = process.argv) {
2464
+ await program.parseAsync([...argv]);
2465
+ }
2466
+ const cliEntryPath = process.argv[1];
2467
+ if (cliEntryPath && isExecutedModule(cliEntryPath, import.meta.url)) {
2468
+ await runCli();
2469
+ }
2470
+ function isExecutedModule(entryPath, moduleUrl) {
2471
+ try {
2472
+ return fs.realpathSync(entryPath) === fs.realpathSync(fileURLToPath(moduleUrl));
2473
+ }
2474
+ catch {
2475
+ return false;
2476
+ }
2477
+ }
2114
2478
  //# sourceMappingURL=cli.js.map