mio-agent-runtime 0.15.0 → 0.16.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.
@@ -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: {
@@ -382,13 +376,17 @@ const TOOLS = [
382
376
  },
383
377
  {
384
378
  name: 'mio.trace.query',
385
- 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.',
386
381
  inputSchema: {
387
382
  type: 'object',
388
383
  properties: {
389
384
  event_type: { type: 'string', description: 'Filter by event type (e.g. task_outcome, error, tool_call).' },
390
385
  outcome: { type: 'string', description: 'Filter by outcome (success, failure, error, aborted).' },
391
- 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
+ },
392
390
  agent: { type: 'string', description: 'Filter by agent identifier (e.g. codex, claude-code, cli).' },
393
391
  host: { type: 'string', description: 'Filter by host identifier.' },
394
392
  since: { type: 'string', description: 'Only traces at or after this time (ISO 8601 or epoch ms).' },
@@ -400,7 +398,8 @@ const TOOLS = [
400
398
  },
401
399
  {
402
400
  name: 'mio.digest.generate',
403
- 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.',
404
403
  inputSchema: {
405
404
  type: 'object',
406
405
  properties: {
@@ -411,11 +410,16 @@ const TOOLS = [
411
410
  },
412
411
  {
413
412
  name: 'mio.observer.subscribe',
414
- 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.',
415
415
  inputSchema: {
416
416
  type: 'object',
417
417
  properties: {
418
- 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
+ },
419
423
  project: { type: 'string', description: 'Project name. Defaults to current repository.' },
420
424
  topic: { type: 'string', description: 'Optional keyword; only events whose payload mentions it match.' },
421
425
  ttlDays: { type: 'number', description: 'Subscription lifetime in days. Defaults to 30.' },
@@ -436,7 +440,8 @@ const TOOLS = [
436
440
  },
437
441
  {
438
442
  name: 'mio.policy.check',
439
- 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.',
440
445
  inputSchema: {
441
446
  type: 'object',
442
447
  properties: {
@@ -484,7 +489,10 @@ const TOOLS = [
484
489
  properties: {
485
490
  id: { type: 'string', description: 'Id of the auto-claimed reuse record returned by mio.observer.ingest.' },
486
491
  confirmedBy: { type: 'string', description: 'Who confirmed the reuse: agent or human. Defaults to agent.' },
487
- 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
+ },
488
496
  notes: { type: 'string', description: 'Optional confirmation notes.' },
489
497
  },
490
498
  required: ['id'],
@@ -497,7 +505,11 @@ const TOOLS = [
497
505
  type: 'object',
498
506
  properties: {
499
507
  project: { type: 'string', description: 'Project name filter. Defaults to current repository.' },
500
- 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
+ },
501
513
  targetAgent: { type: 'string', description: 'Filter records by target agent.' },
502
514
  limit: { type: 'number', description: 'Maximum records to return, 1-100. Defaults to 20.' },
503
515
  },
@@ -516,11 +528,12 @@ const TOOLS = [
516
528
  },
517
529
  {
518
530
  name: 'mio.task.route',
519
- 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).',
520
533
  inputSchema: {
521
534
  type: 'object',
522
535
  properties: {
523
- 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'." },
524
537
  project: { type: 'string', description: 'Project name filter. Defaults to current repository.' },
525
538
  scope: { type: 'string', description: 'Search scope: project (default), global, or all.' },
526
539
  limit: { type: 'number', description: 'Maximum routes, between 1 and 10. Defaults to 5.' },
@@ -577,7 +590,8 @@ const TOOLS = [
577
590
  },
578
591
  {
579
592
  name: 'mio.evolution.report',
580
- 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).',
581
595
  inputSchema: {
582
596
  type: 'object',
583
597
  properties: {
@@ -684,7 +698,8 @@ const TOOLS = [
684
698
  },
685
699
  {
686
700
  name: 'mio.task.record_outcome',
687
- 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.',
688
703
  inputSchema: {
689
704
  type: 'object',
690
705
  properties: {
@@ -701,24 +716,31 @@ const TOOLS = [
701
716
  },
702
717
  {
703
718
  name: 'mio.creativity.status',
704
- description: '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).',
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).',
705
721
  inputSchema: { type: 'object', properties: {} },
706
722
  },
707
723
  {
708
724
  name: 'mio.creativity.list',
709
- description: '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).',
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).',
710
727
  inputSchema: {
711
728
  type: 'object',
712
729
  properties: {
713
730
  status: { type: 'string', description: 'Filter by status: active, validated, rejected, draft.' },
714
731
  limit: { type: 'number', description: 'Max results. Default 20.' },
715
- 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
+ },
716
737
  },
717
738
  },
718
739
  },
719
740
  {
720
741
  name: 'mio.creativity.generate',
721
- 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.',
722
744
  inputSchema: {
723
745
  type: 'object',
724
746
  properties: {
@@ -738,9 +760,14 @@ const TOOLS = [
738
760
  },
739
761
  fromInsights: {
740
762
  type: 'boolean',
741
- 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.',
742
770
  },
743
- 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.' },
744
771
  },
745
772
  },
746
773
  },
@@ -761,8 +788,15 @@ const TOOLS = [
761
788
  inputSchema: {
762
789
  type: 'object',
763
790
  properties: {
764
- hypothesisId: { type: 'string', description: 'UUID of a stored hypothesis (creativity-hypotheses.jsonl). Must exist or the call fails.' },
765
- memoryId: { type: 'string', description: 'Optional memory record id. If given, the record must exist and carry the tag "hypothesis:<hypothesisId>" or the call fails.' },
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
+ },
766
800
  taskId: { type: 'string', description: 'Optional task id this adoption feeds.' },
767
801
  note: { type: 'string', description: 'Optional short rationale for the adoption.' },
768
802
  },
@@ -770,182 +804,244 @@ const TOOLS = [
770
804
  },
771
805
  },
772
806
  {
773
- name: 'mio.idea.generate',
807
+ name: 'mio.mechanism.registry',
774
808
  description:
775
- '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.',
809
+ 'Query the mechanism registry: active/rolled_back MechanismVersion entries and PromotionRequest list. Thin JSONL store backed by mechanisms.jsonl.',
776
810
  inputSchema: {
777
811
  type: 'object',
778
812
  properties: {
779
- goal: { type: 'string', description: 'The one-line goal the ideas should pursue (required).' },
780
- context: { type: 'string', description: 'Optional free-form background.' },
781
- constraints: { type: 'array', items: { type: 'string' }, description: 'Optional hard constraints.' },
782
- numIdeas: { type: 'number', description: 'How many ideas to return; clamped to 1..3, default 3.' },
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.' },
783
815
  },
784
- required: ['goal'],
785
816
  },
786
817
  },
787
-
788
- // ═══ Observer Research Pipeline ═══
789
-
790
- // ═══ Insight Self-Observation ═══
791
-
792
- ...(isInsightAvailable() ? [{
793
- name: 'mio.insight.status',
794
- description: 'Get insight engine status: total insights, unreported count, cooldown.',
795
- inputSchema: { type: 'object', properties: {} },
796
- },
797
818
  {
798
- name: 'mio.insight.list',
799
- description: 'List stored insights with optional filters.',
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.',
800
822
  inputSchema: {
801
823
  type: 'object',
802
824
  properties: {
803
- unreported: { type: 'boolean', description: 'Only unreported insights' },
804
- minScore: { type: 'number', description: 'Minimum score filter' },
805
- detector: { type: 'string', description: 'Filter by detector name' },
806
- limit: { type: 'number', description: 'Max results (default 20)' },
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
+ },
807
838
  },
839
+ required: ['action'],
808
840
  },
809
841
  },
810
842
  {
811
- name: 'mio.insight.mark_reported',
812
- description: 'Mark insights as reported (acknowledged).',
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.',
813
846
  inputSchema: {
814
847
  type: 'object',
815
848
  properties: {
816
- ids: { type: 'array', items: { type: 'string' }, description: 'Insight IDs to mark' },
849
+ mechanismId: { type: 'string', description: 'Mechanism id to roll back.' },
817
850
  },
818
- required: ['ids'],
851
+ required: ['mechanismId'],
819
852
  },
820
853
  },
821
854
  {
822
- name: 'mio.insight.generate',
823
- description: 'Run LLM-based insight generation from provided context data.',
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.',
824
858
  inputSchema: {
825
859
  type: 'object',
826
860
  properties: {
827
- memories: { type: 'array', items: { type: 'object' }, description: 'Memory entries to analyze' },
828
- summaries: { type: 'array', items: { type: 'string' }, description: 'Recent summaries' },
829
- plans: { type: 'array', items: { type: 'object' }, description: 'Active plans' },
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.' },
830
865
  },
866
+ required: ['goal'],
831
867
  },
832
- }] : []),
868
+ },
833
869
 
834
870
  // ═══ Observer Research Pipeline ═══
835
871
 
836
- ...(isObserverAvailable() ? [{
837
- name: 'mio.observer.world_model',
838
- description: 'Query the Observer world model: entities, events, trends, narratives, uncertainties, and relations.',
839
- inputSchema: {
840
- type: 'object',
841
- properties: {
842
- baseDir: { type: 'string', description: 'Observer data directory (default: .local/observer)' },
843
- },
844
- },
845
- },
846
- {
847
- name: 'mio.observer.trends',
848
- description: 'Get recent trend reports from the Observer pipeline.',
849
- inputSchema: {
850
- type: 'object',
851
- properties: {
852
- date: { type: 'string', description: 'Specific date (YYYY-MM-DD) or null for latest' },
853
- limit: { type: 'number', description: 'Max reports to return (default 7)' },
854
- baseDir: { type: 'string', description: 'Observer data directory' },
855
- },
856
- },
857
- },
858
- {
859
- name: 'mio.observer.research',
860
- description: 'List recent deep research results from the Observer pipeline.',
861
- inputSchema: {
862
- type: 'object',
863
- properties: {
864
- limit: { type: 'number', description: 'Max results (default 10)' },
865
- baseDir: { type: 'string', description: 'Observer data directory' },
866
- },
867
- },
868
- },
869
- {
870
- name: 'mio.observer.insights',
871
- description: 'List Observer-generated insights (5-section structured articles).',
872
- inputSchema: {
873
- type: 'object',
874
- properties: {
875
- limit: { type: 'number', description: 'Max insights (default 20)' },
876
- baseDir: { type: 'string', description: 'Observer data directory' },
877
- },
878
- },
879
- },
880
- {
881
- name: 'mio.observer.status',
882
- description: 'Get Observer pipeline status: counts of observations, trends, topics, research, insights, essays, and world model entities.',
883
- inputSchema: {
884
- type: 'object',
885
- properties: {
886
- baseDir: { type: 'string', description: 'Observer data directory' },
887
- },
888
- },
889
- },
890
- {
891
- name: 'mio.observer.collect',
892
- description: 'Force data collection from all configured sources (RSS, Bilibili, HackerNews, GitHub, Douyin). Returns collected observations.',
893
- inputSchema: {
894
- type: 'object',
895
- properties: {
896
- sources: { type: 'array', items: { type: 'string' }, description: 'Specific sources to collect (default: all)' },
897
- keywords: { type: 'array', items: { type: 'string' }, description: 'Keyword filter (OR match)' },
898
- limit: { type: 'number', description: 'Max items per source (default 20)' },
899
- baseDir: { type: 'string', description: 'Observer data directory' },
900
- },
901
- },
902
- },
903
- {
904
- name: 'mio.observer.ferment',
905
- description: 'Run fermentation engine: find recurring themes across recent observations and optionally write an essay.',
906
- inputSchema: {
907
- type: 'object',
908
- properties: {
909
- session: { type: 'string', description: 'Session label: morning/afternoon/night (default: afternoon)' },
910
- baseDir: { type: 'string', description: 'Observer data directory' },
911
- },
912
- },
913
- },
914
- {
915
- name: 'mio.observer.pipeline',
916
- 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.',
917
- inputSchema: {
918
- type: 'object',
919
- properties: {
920
- mode: { type: 'string', description: 'Writing mode: neutral | analytical | creative (default: analytical)' },
921
- run: { type: 'boolean', description: 'Execute the pipeline. Default false returns a dry-run plan without network or LLM calls.' },
922
- baseDir: { type: 'string', description: 'Observer data directory' },
923
- },
924
- },
925
- },
926
- {
927
- name: 'mio.observer.essays',
928
- description: 'List published or draft essays from the Observer.',
929
- inputSchema: {
930
- type: 'object',
931
- properties: {
932
- type: { type: 'string', description: 'published or draft (default: published)' },
933
- limit: { type: 'number', description: 'Max essays (default 10)' },
934
- baseDir: { type: 'string', description: 'Observer data directory' },
935
- },
936
- },
937
- },
938
- {
939
- name: 'mio.observer.dag',
940
- description: 'Get DAG state machine status: today\'s task, recent summaries, retryable tasks.',
941
- inputSchema: {
942
- type: 'object',
943
- properties: {
944
- days: { type: 'number', description: 'Days to look back (default 7)' },
945
- baseDir: { type: 'string', description: 'Observer data directory' },
946
- },
947
- },
948
- }] : []),
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
+ : []),
949
1045
  ]
950
1046
 
951
1047
  // ═══ Insight Self-Observation Handlers ═══
@@ -1138,6 +1234,12 @@ async function callTool(name, args = {}) {
1138
1234
  return creativityEngine.ferment(args.limit)
1139
1235
  case 'mio.creativity.adopt':
1140
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)
1141
1243
  case 'mio.idea.generate': {
1142
1244
  const goal = typeof args.goal === 'string' ? args.goal.trim() : ''
1143
1245
  if (!goal) throw new Error('mio.idea.generate requires goal: a non-empty string')
@@ -1217,7 +1319,7 @@ async function handleMessage(message) {
1217
1319
  protocolVersion: PROTOCOL_VERSION,
1218
1320
  capabilities: { tools: {} },
1219
1321
  serverInfo: SERVER_INFO,
1220
- })
1322
+ }),
1221
1323
  )
1222
1324
  return
1223
1325
  }
@@ -1237,7 +1339,7 @@ async function handleMessage(message) {
1237
1339
  jsonRpcResult(message.id, {
1238
1340
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1239
1341
  isError: false,
1240
- })
1342
+ }),
1241
1343
  )
1242
1344
  return
1243
1345
  }
@@ -1267,13 +1369,10 @@ rl.on('close', () => {
1267
1369
  }
1268
1370
  })
1269
1371
 
1270
-
1271
1372
  const evolutionReportStore = createEvolutionReport({ dataDir, projectName })
1272
1373
 
1273
1374
  function evolutionReport(args = {}) {
1274
1375
  return evolutionReportStore.report(args)
1275
1376
  }
1276
1377
 
1277
-
1278
-
1279
1378
  module.exports = { TOOLS, callTool, handleMessage, readJsonl, appendJsonl, rl }