mio-agent-runtime 0.14.1 → 0.16.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.
@@ -20,7 +20,7 @@ const { createSubscriptionStore } = require('../subscription-store.js')
20
20
  const { createDigest } = require('../digest.js')
21
21
  const { createEvolutionReport } = require('../evolution-report.js')
22
22
 
23
- const { CreativityEngine, sourcesFromInsights } = require('../creativity-engine.js')
23
+ const { CreativityEngine, sourcesFromInsights, adoptHypothesis } = require('../creativity-engine.js')
24
24
  const { buildAutoSources, topUpSources } = require('../creativity-sources.js')
25
25
  const { runIdeaGenerate, groundingFrom } = require('../idea-generate.js')
26
26
  // The observer and insight engines are optional dependencies. Their stores own
@@ -29,6 +29,7 @@ const { runIdeaGenerate, groundingFrom } = require('../idea-generate.js')
29
29
  // one implementation (see ../observer-store.js and ../insight-store.js).
30
30
  const { createObserverStore, isObserverAvailable } = require('../observer-store.js')
31
31
  const { createInsightStore, isInsightAvailable } = require('../insight-store.js')
32
+ const { mechanismRegistry, mechanismPromote, mechanismRollback } = require('../mechanism-registry.js')
32
33
 
33
34
  const SERVER_INFO = { name: 'mio-intelligence-mcp', version: '0.1.0' }
34
35
  const PROTOCOL_VERSION = '2024-11-05'
@@ -39,10 +40,7 @@ const PROTOCOL_VERSION = '2024-11-05'
39
40
 
40
41
  const { chatJson } = require('../llm-client.js')
41
42
 
42
-
43
- const dataDir = path.resolve(
44
- process.env.MIO_DATA_DIR || path.join(process.cwd(), '.mio-intelligence')
45
- )
43
+ const dataDir = path.resolve(process.env.MIO_DATA_DIR || path.join(process.cwd(), '.mio-intelligence'))
46
44
  const memoryPath = path.join(dataDir, 'memory.jsonl')
47
45
  const tracePath = path.join(dataDir, 'traces.jsonl')
48
46
  const experienceReusePath = path.join(dataDir, 'experience_reuse.jsonl')
@@ -91,11 +89,7 @@ function appendJsonl(file, value) {
91
89
 
92
90
  function writeJsonl(file, values) {
93
91
  fs.mkdirSync(path.dirname(file), { recursive: true })
94
- fs.writeFileSync(
95
- file,
96
- values.length > 0 ? values.map((value) => JSON.stringify(value)).join('\n') + '\n' : '',
97
- 'utf8'
98
- )
92
+ fs.writeFileSync(file, values.length > 0 ? values.map((value) => JSON.stringify(value)).join('\n') + '\n' : '', 'utf8')
99
93
  }
100
94
 
101
95
  function readJsonl(file) {
@@ -128,15 +122,11 @@ function projectName() {
128
122
  return lastProjectName
129
123
  }
130
124
  try {
131
- const commonDir = execFileSync(
132
- 'git',
133
- ['rev-parse', '--path-format=absolute', '--git-common-dir'],
134
- {
135
- cwd: currentCwd,
136
- encoding: 'utf8',
137
- stdio: ['ignore', 'pipe', 'ignore'],
138
- }
139
- ).trim()
125
+ const commonDir = execFileSync('git', ['rev-parse', '--path-format=absolute', '--git-common-dir'], {
126
+ cwd: currentCwd,
127
+ encoding: 'utf8',
128
+ stdio: ['ignore', 'pipe', 'ignore'],
129
+ }).trim()
140
130
  if (commonDir) {
141
131
  const repoRoot = path.dirname(commonDir)
142
132
  const name = path.basename(repoRoot)
@@ -161,11 +151,7 @@ const experienceStore = createExperienceStore({
161
151
  // Preserves the phase0 summary these tools have always returned.
162
152
  phase0Summary: (project) => summarizePhase0(loadPhase0(dataDir, project)),
163
153
  })
164
- const {
165
- listReuse: listExperienceReuse,
166
- confirmReuse: confirmExperienceReuse,
167
- recordReuse: recordExperienceReuse,
168
- } = experienceStore
154
+ const { listReuse: listExperienceReuse, confirmReuse: confirmExperienceReuse, recordReuse: recordExperienceReuse } = experienceStore
169
155
 
170
156
  // One query-log instance shared by the memory store (which records queries) and
171
157
  // the task store (which consumes them for auto-claim). This is what removes the
@@ -255,7 +241,9 @@ const { subscribe: subscribeObserver, digest: observerDigest } = subscriptionSto
255
241
  function phase0Report(args = {}) {
256
242
  const project = args.project || projectName()
257
243
  const report = loadPhase0(dataDir, project)
258
- const format = String(args.format || 'json').trim().toLowerCase()
244
+ const format = String(args.format || 'json')
245
+ .trim()
246
+ .toLowerCase()
259
247
  if (format === 'markdown') {
260
248
  return {
261
249
  project,
@@ -302,7 +290,8 @@ const TOOLS = [
302
290
  },
303
291
  {
304
292
  name: 'mio.memory.forget',
305
- description: 'Permanently delete memory records by id. This is the only irreversible memory operation: archive is reversible, and forget removes the record outright. An audit entry (id, project, timestamp, reason, kind, length, excerpt) is written to memory-forget-audit.jsonl before the delete, so a removal is always reconstructable. Prefer archive unless the record must actually be gone.',
293
+ description:
294
+ 'Permanently delete memory records by id. This is the only irreversible memory operation: archive is reversible, and forget removes the record outright. An audit entry (id, project, timestamp, reason, kind, length, excerpt) is written to memory-forget-audit.jsonl before the delete, so a removal is always reconstructable. Prefer archive unless the record must actually be gone.',
306
295
  inputSchema: {
307
296
  type: 'object',
308
297
  properties: {
@@ -315,14 +304,18 @@ const TOOLS = [
315
304
  },
316
305
  {
317
306
  name: 'mio.memory.merge',
318
- description: 'Merge near-duplicate memory records into one survivor: the survivor gains a supersedes list and the others are archived. Refuses to merge records whose content differs unless keep and allowDivergent are both given, because concatenating divergent bodies corrupts them. Reversible via mio.memory.archive with restore.',
307
+ description:
308
+ 'Merge near-duplicate memory records into one survivor: the survivor gains a supersedes list and the others are archived. Refuses to merge records whose content differs unless keep and allowDivergent are both given, because concatenating divergent bodies corrupts them. Reversible via mio.memory.archive with restore.',
319
309
  inputSchema: {
320
310
  type: 'object',
321
311
  properties: {
322
312
  ids: { type: 'array', items: { type: 'string' }, description: 'Memory record ids to merge (at least 2).' },
323
313
  project: { type: 'string', description: 'Project name filter. Defaults to current repository.' },
324
314
  keep: { type: 'string', description: 'Id of the record whose content survives. Defaults to the newest record in the set.' },
325
- allowDivergent: { type: 'boolean', description: 'When true, allow merging records whose content differs. Requires keep so the surviving body is explicit.' },
315
+ allowDivergent: {
316
+ type: 'boolean',
317
+ description: 'When true, allow merging records whose content differs. Requires keep so the surviving body is explicit.',
318
+ },
326
319
  reason: { type: 'string', description: 'Optional merge reason.' },
327
320
  },
328
321
  required: ['ids'],
@@ -330,7 +323,8 @@ const TOOLS = [
330
323
  },
331
324
  {
332
325
  name: 'mio.memory.migrate',
333
- description: 'Migrate memory records between project and global layers (P3). Moving to global makes the record reusable across projects (project set to null); moving to project restores project scoping.',
326
+ description:
327
+ 'Migrate memory records between project and global layers (P3). Moving to global makes the record reusable across projects (project set to null); moving to project restores project scoping.',
334
328
  inputSchema: {
335
329
  type: 'object',
336
330
  properties: {
@@ -351,6 +345,11 @@ const TOOLS = [
351
345
  content: { type: 'string', description: 'Memory content.' },
352
346
  kind: { type: 'string', description: 'Memory kind, such as decision, context, or problem.' },
353
347
  tags: { type: 'array', items: { type: 'string' }, description: 'Optional tags.' },
348
+ hypothesisId: {
349
+ type: 'string',
350
+ description:
351
+ 'Optional creativity hypothesis UUID. Injects the tag "hypothesis:<id>" so this record joins that hypothesis (the tag mio.creativity.adopt enforces); search it back via memory.query tags.',
352
+ },
354
353
  project: { type: 'string', description: 'Project name. Defaults to current directory name.' },
355
354
  scope: { type: 'string', description: 'Record scope: project (default) or global.' },
356
355
  source: { type: 'string', description: 'Recording source. Defaults to mcp.' },
@@ -377,13 +376,17 @@ const TOOLS = [
377
376
  },
378
377
  {
379
378
  name: 'mio.trace.query',
380
- description: 'Query the observer trace log (traces.jsonl): task outcomes, tool errors, and ingested events. Newest first, with counts by event_type and outcome over the full filtered set.',
379
+ description:
380
+ 'Query the observer trace log (traces.jsonl): task outcomes, tool errors, and ingested events. Newest first, with counts by event_type and outcome over the full filtered set.',
381
381
  inputSchema: {
382
382
  type: 'object',
383
383
  properties: {
384
384
  event_type: { type: 'string', description: 'Filter by event type (e.g. task_outcome, error, tool_call).' },
385
385
  outcome: { type: 'string', description: 'Filter by outcome (success, failure, error, aborted).' },
386
- project: { type: 'string', description: 'Project name filter. Defaults to current repository; pass empty string for all projects.' },
386
+ project: {
387
+ type: 'string',
388
+ description: 'Project name filter. Defaults to current repository; pass empty string for all projects.',
389
+ },
387
390
  agent: { type: 'string', description: 'Filter by agent identifier (e.g. codex, claude-code, cli).' },
388
391
  host: { type: 'string', description: 'Filter by host identifier.' },
389
392
  since: { type: 'string', description: 'Only traces at or after this time (ISO 8601 or epoch ms).' },
@@ -395,7 +398,8 @@ const TOOLS = [
395
398
  },
396
399
  {
397
400
  name: 'mio.digest.generate',
398
- description: 'Generate an actionable digest from recent traces/memory/reuse data: task throughput and success rates per agent, project activity, error hotspots, reuse evidence, and rule-based suggestions. Returns the report object with a markdown field.',
401
+ description:
402
+ 'Generate an actionable digest from recent traces/memory/reuse data: task throughput and success rates per agent, project activity, error hotspots, reuse evidence, and rule-based suggestions. Returns the report object with a markdown field.',
399
403
  inputSchema: {
400
404
  type: 'object',
401
405
  properties: {
@@ -406,11 +410,16 @@ const TOOLS = [
406
410
  },
407
411
  {
408
412
  name: 'mio.observer.subscribe',
409
- description: 'Subscribe to observer events (project, event types, optional topic). New matching events are delivered via mio.observer.digest.',
413
+ description:
414
+ 'Subscribe to observer events (project, event types, optional topic). New matching events are delivered via mio.observer.digest.',
410
415
  inputSchema: {
411
416
  type: 'object',
412
417
  properties: {
413
- eventTypes: { type: 'array', items: { type: 'string' }, description: 'Event types to watch, e.g. error, retry, task_outcome. Empty means all.' },
418
+ eventTypes: {
419
+ type: 'array',
420
+ items: { type: 'string' },
421
+ description: 'Event types to watch, e.g. error, retry, task_outcome. Empty means all.',
422
+ },
414
423
  project: { type: 'string', description: 'Project name. Defaults to current repository.' },
415
424
  topic: { type: 'string', description: 'Optional keyword; only events whose payload mentions it match.' },
416
425
  ttlDays: { type: 'number', description: 'Subscription lifetime in days. Defaults to 30.' },
@@ -431,7 +440,8 @@ const TOOLS = [
431
440
  },
432
441
  {
433
442
  name: 'mio.policy.check',
434
- description: 'Check historical risk for an action before executing it, returning risk level, outcome counts, and recent failure examples.',
443
+ description:
444
+ 'Check historical risk for an action before executing it, returning risk level, outcome counts, and recent failure examples.',
435
445
  inputSchema: {
436
446
  type: 'object',
437
447
  properties: {
@@ -479,7 +489,10 @@ const TOOLS = [
479
489
  properties: {
480
490
  id: { type: 'string', description: 'Id of the auto-claimed reuse record returned by mio.observer.ingest.' },
481
491
  confirmedBy: { type: 'string', description: 'Who confirmed the reuse: agent or human. Defaults to agent.' },
482
- outcomeImproved: { type: 'boolean', description: 'Override: true when the reused experience improved the task outcome. Defaults to the auto-claim value.' },
492
+ outcomeImproved: {
493
+ type: 'boolean',
494
+ description: 'Override: true when the reused experience improved the task outcome. Defaults to the auto-claim value.',
495
+ },
483
496
  notes: { type: 'string', description: 'Optional confirmation notes.' },
484
497
  },
485
498
  required: ['id'],
@@ -492,7 +505,11 @@ const TOOLS = [
492
505
  type: 'object',
493
506
  properties: {
494
507
  project: { type: 'string', description: 'Project name filter. Defaults to current repository.' },
495
- status: { type: 'string', description: 'Filter: pending (auto-claim awaiting confirmation), confirmed, verified, auto_claim, agent_report, all. Defaults to all.' },
508
+ status: {
509
+ type: 'string',
510
+ description:
511
+ 'Filter: pending (auto-claim awaiting confirmation), confirmed, verified, auto_claim, agent_report, all. Defaults to all.',
512
+ },
496
513
  targetAgent: { type: 'string', description: 'Filter records by target agent.' },
497
514
  limit: { type: 'number', description: 'Maximum records to return, 1-100. Defaults to 20.' },
498
515
  },
@@ -511,11 +528,12 @@ const TOOLS = [
511
528
  },
512
529
  {
513
530
  name: 'mio.task.route',
514
- description: 'Route an incoming task to the most relevant verified experiences and related memories, and include an agent-health signal (from the latest digest) saying which agents to prefer or avoid. Turns recall plus observed performance into decision influence (P1).',
531
+ description:
532
+ 'Route an incoming task to the most relevant verified experiences and related memories, and include an agent-health signal (from the latest digest) saying which agents to prefer or avoid. Turns recall plus observed performance into decision influence (P1).',
515
533
  inputSchema: {
516
534
  type: 'object',
517
535
  properties: {
518
- task: { type: 'string', description: 'Task description to route, e.g. \'fix IPC communication bug\'.' },
536
+ task: { type: 'string', description: "Task description to route, e.g. 'fix IPC communication bug'." },
519
537
  project: { type: 'string', description: 'Project name filter. Defaults to current repository.' },
520
538
  scope: { type: 'string', description: 'Search scope: project (default), global, or all.' },
521
539
  limit: { type: 'number', description: 'Maximum routes, between 1 and 10. Defaults to 5.' },
@@ -572,7 +590,8 @@ const TOOLS = [
572
590
  },
573
591
  {
574
592
  name: 'mio.evolution.report',
575
- description: 'Generate cross-agent evolution report: ecosystem summary, agent performance comparison, cross-agent patterns, memory health, and optimization suggestions (ADR-017 Evolution plane).',
593
+ description:
594
+ 'Generate cross-agent evolution report: ecosystem summary, agent performance comparison, cross-agent patterns, memory health, and optimization suggestions (ADR-017 Evolution plane).',
576
595
  inputSchema: {
577
596
  type: 'object',
578
597
  properties: {
@@ -679,7 +698,8 @@ const TOOLS = [
679
698
  },
680
699
  {
681
700
  name: 'mio.task.record_outcome',
682
- description: 'Record task outcome with agent registry update and auto-claim experience reuse in one atomic call. Combines observer.ingest + agent stats + auto-claim for a complete task lifecycle closure.',
701
+ description:
702
+ 'Record task outcome with agent registry update and auto-claim experience reuse in one atomic call. Combines observer.ingest + agent stats + auto-claim for a complete task lifecycle closure.',
683
703
  inputSchema: {
684
704
  type: 'object',
685
705
  properties: {
@@ -696,24 +716,31 @@ const TOOLS = [
696
716
  },
697
717
  {
698
718
  name: 'mio.creativity.status',
699
- description: 'Show creativity engine status: hypothesis counts, active/validated/rejected/draft, recent top ideas.',
719
+ description:
720
+ 'Show creativity engine status: hypothesis counts, active/validated/rejected/draft, recent top ideas, plus the adoption evidence join — adopted/claimed/evidenced counts with sources (informational, NOT a metric; derived from creativity.adopt trace events and hypothesis:<id> memory tags).',
700
721
  inputSchema: { type: 'object', properties: {} },
701
722
  },
702
723
  {
703
724
  name: 'mio.creativity.list',
704
- description: 'List creativity hypotheses with optional status filter. Newest 20 by default; pass limit: 0 for everything.',
725
+ description:
726
+ 'List creativity hypotheses with optional status filter. Newest 20 by default; pass limit: 0 for everything. Each row carries adopted: true when a creativity.adopt event or a hypothesis:<id> memory tag exists for its id (read-side evidence join, informational).',
705
727
  inputSchema: {
706
728
  type: 'object',
707
729
  properties: {
708
730
  status: { type: 'string', description: 'Filter by status: active, validated, rejected, draft.' },
709
731
  limit: { type: 'number', description: 'Max results. Default 20.' },
710
- sort: { type: 'string', description: "Sort order: 'novelty' ranks stored hypotheses by relative noveltyScore (a dedup signal, NOT a quality score), newest 200 candidates capped. Default: newest." },
732
+ sort: {
733
+ type: 'string',
734
+ description:
735
+ "Sort order: 'novelty' ranks stored hypotheses by relative noveltyScore (a dedup signal, NOT a quality score), newest 200 candidates capped. Default: newest.",
736
+ },
711
737
  },
712
738
  },
713
739
  },
714
740
  {
715
741
  name: 'mio.creativity.generate',
716
- description: 'Generate new creative hypotheses by combining concept sources via LLM. Returns ideas with novelty/feasibility/impact scores. Pass sources, or set fromInsights to seed them from stored insights.',
742
+ description:
743
+ 'Generate new creative hypotheses by combining concept sources via LLM. Returns ideas with novelty/feasibility/impact scores. Pass sources, or set fromInsights to seed them from stored insights.',
717
744
  inputSchema: {
718
745
  type: 'object',
719
746
  properties: {
@@ -733,9 +760,14 @@ const TOOLS = [
733
760
  },
734
761
  fromInsights: {
735
762
  type: 'boolean',
736
- description: 'Seed concept sources from stored insights (highest score first), merged with any explicit sources. Needs @akemi-mio/insight and at least one stored insight.',
763
+ description:
764
+ 'Seed concept sources from stored insights (highest score first), merged with any explicit sources. Needs @akemi-mio/insight and at least one stored insight.',
765
+ },
766
+ strategy: {
767
+ type: 'string',
768
+ description:
769
+ 'Generation strategy: explore (cross-domain), signal (provocative), stable (conservative). Auto-selected if omitted. Pair dedup is per strategy: the same source pair may be tried once under each strategy.',
737
770
  },
738
- strategy: { type: 'string', description: 'Generation strategy: explore (cross-domain), signal (provocative), stable (conservative). Auto-selected if omitted. Pair dedup is per strategy: the same source pair may be tried once under each strategy.' },
739
771
  },
740
772
  },
741
773
  },
@@ -750,182 +782,266 @@ const TOOLS = [
750
782
  },
751
783
  },
752
784
  {
753
- name: 'mio.idea.generate',
785
+ name: 'mio.creativity.adopt',
754
786
  description:
755
- 'Generate up to 3 concrete, experiment-backed hypotheses from one goal. Sources are assembled locally (goal, context/constraints, Mio memory grounding, local memory/traces/stored hypotheses) and every idea is persisted to the CreativityStore with provenance — Mio memory is never written. Returns { ideas, generatedAt, groundedWith, persistedIds }, or { reason } when fewer than two sources are available.',
756
- inputSchema: {
757
- type: 'object',
758
- properties: {
759
- goal: { type: 'string', description: 'The one-line goal the ideas should pursue (required).' },
760
- context: { type: 'string', description: 'Optional free-form background.' },
761
- constraints: { type: 'array', items: { type: 'string' }, description: 'Optional hard constraints.' },
762
- numIdeas: { type: 'number', description: 'How many ideas to return; clamped to 1..3, default 3.' },
763
- },
764
- required: ['goal'],
765
- },
766
- },
767
-
768
- // ═══ Observer Research Pipeline ═══
769
-
770
- // ═══ Insight Self-Observation ═══
771
-
772
- ...(isInsightAvailable() ? [{
773
- name: 'mio.insight.status',
774
- description: 'Get insight engine status: total insights, unreported count, cooldown.',
775
- inputSchema: { type: 'object', properties: {} },
776
- },
777
- {
778
- name: 'mio.insight.list',
779
- description: 'List stored insights with optional filters.',
780
- inputSchema: {
781
- type: 'object',
782
- properties: {
783
- unreported: { type: 'boolean', description: 'Only unreported insights' },
784
- minScore: { type: 'number', description: 'Minimum score filter' },
785
- detector: { type: 'string', description: 'Filter by detector name' },
786
- limit: { type: 'number', description: 'Max results (default 20)' },
787
- },
788
- },
789
- },
790
- {
791
- name: 'mio.insight.mark_reported',
792
- description: 'Mark insights as reported (acknowledged).',
793
- inputSchema: {
794
- type: 'object',
795
- properties: {
796
- ids: { type: 'array', items: { type: 'string' }, description: 'Insight IDs to mark' },
797
- },
798
- required: ['ids'],
799
- },
800
- },
801
- {
802
- name: 'mio.insight.generate',
803
- description: 'Run LLM-based insight generation from provided context data.',
804
- inputSchema: {
805
- type: 'object',
806
- properties: {
807
- memories: { type: 'array', items: { type: 'object' }, description: 'Memory entries to analyze' },
808
- summaries: { type: 'array', items: { type: 'string' }, description: 'Recent summaries' },
809
- plans: { type: 'array', items: { type: 'object' }, description: 'Active plans' },
810
- },
811
- },
812
- }] : []),
813
-
814
- // ═══ Observer Research Pipeline ═══
815
-
816
- ...(isObserverAvailable() ? [{
817
- name: 'mio.observer.world_model',
818
- description: 'Query the Observer world model: entities, events, trends, narratives, uncertainties, and relations.',
819
- inputSchema: {
820
- type: 'object',
821
- properties: {
822
- baseDir: { type: 'string', description: 'Observer data directory (default: .local/observer)' },
823
- },
824
- },
825
- },
826
- {
827
- name: 'mio.observer.trends',
828
- description: 'Get recent trend reports from the Observer pipeline.',
829
- inputSchema: {
830
- type: 'object',
831
- properties: {
832
- date: { type: 'string', description: 'Specific date (YYYY-MM-DD) or null for latest' },
833
- limit: { type: 'number', description: 'Max reports to return (default 7)' },
834
- baseDir: { type: 'string', description: 'Observer data directory' },
835
- },
836
- },
837
- },
838
- {
839
- name: 'mio.observer.research',
840
- description: 'List recent deep research results from the Observer pipeline.',
841
- inputSchema: {
842
- type: 'object',
843
- properties: {
844
- limit: { type: 'number', description: 'Max results (default 10)' },
845
- baseDir: { type: 'string', description: 'Observer data directory' },
846
- },
847
- },
848
- },
849
- {
850
- name: 'mio.observer.insights',
851
- description: 'List Observer-generated insights (5-section structured articles).',
852
- inputSchema: {
853
- type: 'object',
854
- properties: {
855
- limit: { type: 'number', description: 'Max insights (default 20)' },
856
- baseDir: { type: 'string', description: 'Observer data directory' },
857
- },
858
- },
859
- },
860
- {
861
- name: 'mio.observer.status',
862
- description: 'Get Observer pipeline status: counts of observations, trends, topics, research, insights, essays, and world model entities.',
787
+ 'Record that a stored creativity hypothesis was adopted: validates the hypothesis id, then appends a creativity.adopt trace event (who/when/what). Optionally enforces that the referenced memory record carries the hypothesis:<id> tag. Writes traces only — never Mio memory, never hypothesis status.',
863
788
  inputSchema: {
864
789
  type: 'object',
865
790
  properties: {
866
- baseDir: { type: 'string', description: 'Observer data directory' },
791
+ hypothesisId: {
792
+ type: 'string',
793
+ description: 'UUID of a stored hypothesis (creativity-hypotheses.jsonl). Must exist or the call fails.',
794
+ },
795
+ memoryId: {
796
+ type: 'string',
797
+ description:
798
+ 'Optional memory record id. If given, the record must exist and carry the tag "hypothesis:<hypothesisId>" or the call fails.',
799
+ },
800
+ taskId: { type: 'string', description: 'Optional task id this adoption feeds.' },
801
+ note: { type: 'string', description: 'Optional short rationale for the adoption.' },
867
802
  },
803
+ required: ['hypothesisId'],
868
804
  },
869
805
  },
870
806
  {
871
- name: 'mio.observer.collect',
872
- description: 'Force data collection from all configured sources (RSS, Bilibili, HackerNews, GitHub, Douyin). Returns collected observations.',
807
+ name: 'mio.mechanism.registry',
808
+ description:
809
+ 'Query the mechanism registry: active/rolled_back MechanismVersion entries and PromotionRequest list. Thin JSONL store backed by mechanisms.jsonl.',
873
810
  inputSchema: {
874
811
  type: 'object',
875
812
  properties: {
876
- sources: { type: 'array', items: { type: 'string' }, description: 'Specific sources to collect (default: all)' },
877
- keywords: { type: 'array', items: { type: 'string' }, description: 'Keyword filter (OR match)' },
878
- limit: { type: 'number', description: 'Max items per source (default 20)' },
879
- baseDir: { type: 'string', description: 'Observer data directory' },
813
+ mechanismId: { type: 'string', description: 'Optional mechanism id filter. Omit for all versions.' },
814
+ status: { type: 'string', description: 'Filter promotion requests by approvalStatus: pending | approved | rejected.' },
880
815
  },
881
816
  },
882
817
  },
883
818
  {
884
- name: 'mio.observer.ferment',
885
- description: 'Run fermentation engine: find recurring themes across recent observations and optionally write an essay.',
819
+ name: 'mio.mechanism.promote',
820
+ description:
821
+ 'PromotionRequest flow: submit (gate-checked against experiment runs), approve (A5: approver must differ from submitter — enforced in code), or reject. Approve activates the new MechanismVersion and marks the previous active as rolled_back.',
886
822
  inputSchema: {
887
823
  type: 'object',
888
824
  properties: {
889
- session: { type: 'string', description: 'Session label: morning/afternoon/night (default: afternoon)' },
890
- baseDir: { type: 'string', description: 'Observer data directory' },
825
+ action: { type: 'string', description: 'submit | approve | reject' },
826
+ mechanismId: { type: 'string', description: 'Mechanism id (required for submit).' },
827
+ version: { type: 'number', description: 'Version number to promote (required for submit).' },
828
+ hypothesisId: { type: 'string', description: 'Optional hypothesis UUID this promotion is derived from.' },
829
+ experimentRunIds: { type: 'array', items: { type: 'string' }, description: 'Experiment run ids supporting this promotion.' },
830
+ submittedBy: { type: 'string', description: 'Submitter agent id (required for submit).' },
831
+ requestId: { type: 'string', description: 'Promotion request id (required for approve/reject).' },
832
+ approvedBy: { type: 'string', description: 'Approver agent id (required for approve/reject; must differ from submitter — A5).' },
833
+ reason: { type: 'string', description: 'Rejection reason (for reject).' },
834
+ gateOverride: {
835
+ type: 'object',
836
+ description: 'Optional { allowed, reason } to bypass the default checkSubmitGate computation (for thin CLI surface).',
837
+ },
891
838
  },
839
+ required: ['action'],
892
840
  },
893
841
  },
894
842
  {
895
- name: 'mio.observer.pipeline',
896
- description: 'Run the Observer research DAG (collect -> trend -> tension -> research -> multi-brain -> compose -> world model -> publish). Heavy: network plus several LLM calls. Previews by default; pass run:true to execute.',
843
+ name: 'mio.mechanism.rollback',
844
+ description:
845
+ 'Rollback the active version of a mechanism to its rollbackTo target. The current active is marked rolled_back; the previous version is restored to active.',
897
846
  inputSchema: {
898
847
  type: 'object',
899
848
  properties: {
900
- mode: { type: 'string', description: 'Writing mode: neutral | analytical | creative (default: analytical)' },
901
- run: { type: 'boolean', description: 'Execute the pipeline. Default false returns a dry-run plan without network or LLM calls.' },
902
- baseDir: { type: 'string', description: 'Observer data directory' },
849
+ mechanismId: { type: 'string', description: 'Mechanism id to roll back.' },
903
850
  },
851
+ required: ['mechanismId'],
904
852
  },
905
853
  },
906
854
  {
907
- name: 'mio.observer.essays',
908
- description: 'List published or draft essays from the Observer.',
855
+ name: 'mio.idea.generate',
856
+ description:
857
+ 'Generate up to 3 concrete, experiment-backed hypotheses from one goal. Sources are assembled locally (goal, context/constraints, Mio memory grounding, local memory/traces/stored hypotheses) and every idea is persisted to the CreativityStore with provenance — Mio memory is never written. Returns { ideas, generatedAt, groundedWith, persistedIds }, or { reason } when fewer than two sources are available.',
909
858
  inputSchema: {
910
859
  type: 'object',
911
860
  properties: {
912
- type: { type: 'string', description: 'published or draft (default: published)' },
913
- limit: { type: 'number', description: 'Max essays (default 10)' },
914
- baseDir: { type: 'string', description: 'Observer data directory' },
861
+ goal: { type: 'string', description: 'The one-line goal the ideas should pursue (required).' },
862
+ context: { type: 'string', description: 'Optional free-form background.' },
863
+ constraints: { type: 'array', items: { type: 'string' }, description: 'Optional hard constraints.' },
864
+ numIdeas: { type: 'number', description: 'How many ideas to return; clamped to 1..3, default 3.' },
915
865
  },
866
+ required: ['goal'],
916
867
  },
917
868
  },
918
- {
919
- name: 'mio.observer.dag',
920
- description: 'Get DAG state machine status: today\'s task, recent summaries, retryable tasks.',
921
- inputSchema: {
922
- type: 'object',
923
- properties: {
924
- days: { type: 'number', description: 'Days to look back (default 7)' },
925
- baseDir: { type: 'string', description: 'Observer data directory' },
926
- },
927
- },
928
- }] : []),
869
+
870
+ // ═══ Observer Research Pipeline ═══
871
+
872
+ // ═══ Insight Self-Observation ═══
873
+
874
+ ...(isInsightAvailable()
875
+ ? [
876
+ {
877
+ name: 'mio.insight.status',
878
+ description: 'Get insight engine status: total insights, unreported count, cooldown.',
879
+ inputSchema: { type: 'object', properties: {} },
880
+ },
881
+ {
882
+ name: 'mio.insight.list',
883
+ description: 'List stored insights with optional filters.',
884
+ inputSchema: {
885
+ type: 'object',
886
+ properties: {
887
+ unreported: { type: 'boolean', description: 'Only unreported insights' },
888
+ minScore: { type: 'number', description: 'Minimum score filter' },
889
+ detector: { type: 'string', description: 'Filter by detector name' },
890
+ limit: { type: 'number', description: 'Max results (default 20)' },
891
+ },
892
+ },
893
+ },
894
+ {
895
+ name: 'mio.insight.mark_reported',
896
+ description: 'Mark insights as reported (acknowledged).',
897
+ inputSchema: {
898
+ type: 'object',
899
+ properties: {
900
+ ids: { type: 'array', items: { type: 'string' }, description: 'Insight IDs to mark' },
901
+ },
902
+ required: ['ids'],
903
+ },
904
+ },
905
+ {
906
+ name: 'mio.insight.generate',
907
+ description: 'Run LLM-based insight generation from provided context data.',
908
+ inputSchema: {
909
+ type: 'object',
910
+ properties: {
911
+ memories: { type: 'array', items: { type: 'object' }, description: 'Memory entries to analyze' },
912
+ summaries: { type: 'array', items: { type: 'string' }, description: 'Recent summaries' },
913
+ plans: { type: 'array', items: { type: 'object' }, description: 'Active plans' },
914
+ },
915
+ },
916
+ },
917
+ ]
918
+ : []),
919
+
920
+ // ═══ Observer Research Pipeline ═══
921
+
922
+ ...(isObserverAvailable()
923
+ ? [
924
+ {
925
+ name: 'mio.observer.world_model',
926
+ description: 'Query the Observer world model: entities, events, trends, narratives, uncertainties, and relations.',
927
+ inputSchema: {
928
+ type: 'object',
929
+ properties: {
930
+ baseDir: { type: 'string', description: 'Observer data directory (default: .local/observer)' },
931
+ },
932
+ },
933
+ },
934
+ {
935
+ name: 'mio.observer.trends',
936
+ description: 'Get recent trend reports from the Observer pipeline.',
937
+ inputSchema: {
938
+ type: 'object',
939
+ properties: {
940
+ date: { type: 'string', description: 'Specific date (YYYY-MM-DD) or null for latest' },
941
+ limit: { type: 'number', description: 'Max reports to return (default 7)' },
942
+ baseDir: { type: 'string', description: 'Observer data directory' },
943
+ },
944
+ },
945
+ },
946
+ {
947
+ name: 'mio.observer.research',
948
+ description: 'List recent deep research results from the Observer pipeline.',
949
+ inputSchema: {
950
+ type: 'object',
951
+ properties: {
952
+ limit: { type: 'number', description: 'Max results (default 10)' },
953
+ baseDir: { type: 'string', description: 'Observer data directory' },
954
+ },
955
+ },
956
+ },
957
+ {
958
+ name: 'mio.observer.insights',
959
+ description: 'List Observer-generated insights (5-section structured articles).',
960
+ inputSchema: {
961
+ type: 'object',
962
+ properties: {
963
+ limit: { type: 'number', description: 'Max insights (default 20)' },
964
+ baseDir: { type: 'string', description: 'Observer data directory' },
965
+ },
966
+ },
967
+ },
968
+ {
969
+ name: 'mio.observer.status',
970
+ description:
971
+ 'Get Observer pipeline status: counts of observations, trends, topics, research, insights, essays, and world model entities.',
972
+ inputSchema: {
973
+ type: 'object',
974
+ properties: {
975
+ baseDir: { type: 'string', description: 'Observer data directory' },
976
+ },
977
+ },
978
+ },
979
+ {
980
+ name: 'mio.observer.collect',
981
+ description:
982
+ 'Force data collection from all configured sources (RSS, Bilibili, HackerNews, GitHub, Douyin). Returns collected observations.',
983
+ inputSchema: {
984
+ type: 'object',
985
+ properties: {
986
+ sources: { type: 'array', items: { type: 'string' }, description: 'Specific sources to collect (default: all)' },
987
+ keywords: { type: 'array', items: { type: 'string' }, description: 'Keyword filter (OR match)' },
988
+ limit: { type: 'number', description: 'Max items per source (default 20)' },
989
+ baseDir: { type: 'string', description: 'Observer data directory' },
990
+ },
991
+ },
992
+ },
993
+ {
994
+ name: 'mio.observer.ferment',
995
+ description: 'Run fermentation engine: find recurring themes across recent observations and optionally write an essay.',
996
+ inputSchema: {
997
+ type: 'object',
998
+ properties: {
999
+ session: { type: 'string', description: 'Session label: morning/afternoon/night (default: afternoon)' },
1000
+ baseDir: { type: 'string', description: 'Observer data directory' },
1001
+ },
1002
+ },
1003
+ },
1004
+ {
1005
+ name: 'mio.observer.pipeline',
1006
+ description:
1007
+ 'Run the Observer research DAG (collect -> trend -> tension -> research -> multi-brain -> compose -> world model -> publish). Heavy: network plus several LLM calls. Previews by default; pass run:true to execute.',
1008
+ inputSchema: {
1009
+ type: 'object',
1010
+ properties: {
1011
+ mode: { type: 'string', description: 'Writing mode: neutral | analytical | creative (default: analytical)' },
1012
+ run: {
1013
+ type: 'boolean',
1014
+ description: 'Execute the pipeline. Default false returns a dry-run plan without network or LLM calls.',
1015
+ },
1016
+ baseDir: { type: 'string', description: 'Observer data directory' },
1017
+ },
1018
+ },
1019
+ },
1020
+ {
1021
+ name: 'mio.observer.essays',
1022
+ description: 'List published or draft essays from the Observer.',
1023
+ inputSchema: {
1024
+ type: 'object',
1025
+ properties: {
1026
+ type: { type: 'string', description: 'published or draft (default: published)' },
1027
+ limit: { type: 'number', description: 'Max essays (default 10)' },
1028
+ baseDir: { type: 'string', description: 'Observer data directory' },
1029
+ },
1030
+ },
1031
+ },
1032
+ {
1033
+ name: 'mio.observer.dag',
1034
+ description: "Get DAG state machine status: today's task, recent summaries, retryable tasks.",
1035
+ inputSchema: {
1036
+ type: 'object',
1037
+ properties: {
1038
+ days: { type: 'number', description: 'Days to look back (default 7)' },
1039
+ baseDir: { type: 'string', description: 'Observer data directory' },
1040
+ },
1041
+ },
1042
+ },
1043
+ ]
1044
+ : []),
929
1045
  ]
930
1046
 
931
1047
  // ═══ Insight Self-Observation Handlers ═══
@@ -1116,6 +1232,14 @@ async function callTool(name, args = {}) {
1116
1232
  return creativityEngine.generate(generationSources(args), args.strategy)
1117
1233
  case 'mio.creativity.ferment':
1118
1234
  return creativityEngine.ferment(args.limit)
1235
+ case 'mio.creativity.adopt':
1236
+ return adoptHypothesis(creativityEngine, args, ingestObservation, dataDir)
1237
+ case 'mio.mechanism.registry':
1238
+ return mechanismRegistry(args, dataDir)
1239
+ case 'mio.mechanism.promote':
1240
+ return mechanismPromote(args, dataDir)
1241
+ case 'mio.mechanism.rollback':
1242
+ return mechanismRollback(args, dataDir)
1119
1243
  case 'mio.idea.generate': {
1120
1244
  const goal = typeof args.goal === 'string' ? args.goal.trim() : ''
1121
1245
  if (!goal) throw new Error('mio.idea.generate requires goal: a non-empty string')
@@ -1195,7 +1319,7 @@ async function handleMessage(message) {
1195
1319
  protocolVersion: PROTOCOL_VERSION,
1196
1320
  capabilities: { tools: {} },
1197
1321
  serverInfo: SERVER_INFO,
1198
- })
1322
+ }),
1199
1323
  )
1200
1324
  return
1201
1325
  }
@@ -1215,7 +1339,7 @@ async function handleMessage(message) {
1215
1339
  jsonRpcResult(message.id, {
1216
1340
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1217
1341
  isError: false,
1218
- })
1342
+ }),
1219
1343
  )
1220
1344
  return
1221
1345
  }
@@ -1245,13 +1369,10 @@ rl.on('close', () => {
1245
1369
  }
1246
1370
  })
1247
1371
 
1248
-
1249
1372
  const evolutionReportStore = createEvolutionReport({ dataDir, projectName })
1250
1373
 
1251
1374
  function evolutionReport(args = {}) {
1252
1375
  return evolutionReportStore.report(args)
1253
1376
  }
1254
1377
 
1255
-
1256
-
1257
1378
  module.exports = { TOOLS, callTool, handleMessage, readJsonl, appendJsonl, rl }