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.
package/bin/mio.js CHANGED
@@ -23,7 +23,8 @@ const { createEvolutionCutoverTools } = require('../server/evolution-cutover.js'
23
23
  const { createMemoryStore } = require('../server/memory-store.js')
24
24
  const { createExperienceStore } = require('../server/experience-store.js')
25
25
  const { createPolicyStore } = require('../server/policy-store.js')
26
- const { CreativityEngine, sourcesFromInsights } = require('../server/creativity-engine.js')
26
+ const { CreativityEngine, sourcesFromInsights, adoptHypothesis } = require('../server/creativity-engine.js')
27
+ const { mechanismRegistry, mechanismPromote, mechanismRollback } = require('../server/mechanism-registry.js')
27
28
  const { buildAutoSources, topUpSources } = require('../server/creativity-sources.js')
28
29
  const { runIdeaGenerate, groundingFrom } = require('../server/idea-generate.js')
29
30
  const { createAgentStore } = require('../server/agent-store.js')
@@ -71,15 +72,11 @@ function readJsonl(file) {
71
72
 
72
73
  function projectName() {
73
74
  try {
74
- const commonDir = execFileSync(
75
- 'git',
76
- ['rev-parse', '--path-format=absolute', '--git-common-dir'],
77
- {
78
- cwd: process.cwd(),
79
- encoding: 'utf8',
80
- stdio: ['ignore', 'pipe', 'ignore'],
81
- }
82
- ).trim()
75
+ const commonDir = execFileSync('git', ['rev-parse', '--path-format=absolute', '--git-common-dir'], {
76
+ cwd: process.cwd(),
77
+ encoding: 'utf8',
78
+ stdio: ['ignore', 'pipe', 'ignore'],
79
+ }).trim()
83
80
  if (commonDir) return path.basename(path.dirname(commonDir))
84
81
  } catch (_) {}
85
82
  return path.basename(process.cwd())
@@ -193,7 +190,7 @@ function printMigrationPlan(plan, useJson) {
193
190
  if (useJson) return jsonOrText(plan, true)
194
191
  console.log(`Migration plan: ${plan.ready ? 'ready' : 'conflicts'}`)
195
192
  console.log(
196
- `legacy=${plan.summary.legacy} modular=${plan.summary.modular} toCreate=${plan.summary.toCreate} conflicts=${plan.summary.conflicts}`
193
+ `legacy=${plan.summary.legacy} modular=${plan.summary.modular} toCreate=${plan.summary.toCreate} conflicts=${plan.summary.conflicts}`,
197
194
  )
198
195
  }
199
196
 
@@ -457,7 +454,9 @@ function splitTagsOption(args, name) {
457
454
  function recallCommand(args, useJson) {
458
455
  const query = args[1]
459
456
  if (!query) {
460
- console.error('Usage: mio recall "<query>" [--project name] [--scope project|global|all] [--kind kind] [--tags a,b] [--limit n] [--json]')
457
+ console.error(
458
+ 'Usage: mio recall "<query>" [--project name] [--scope project|global|all] [--kind kind] [--tags a,b] [--limit n] [--json]',
459
+ )
461
460
  process.exitCode = 1
462
461
  return
463
462
  }
@@ -479,9 +478,7 @@ function recallCommand(args, useJson) {
479
478
  return
480
479
  }
481
480
  if (useJson) return jsonOrText(result, true)
482
- console.log(
483
- `Mio memory: ${result.count} result(s) for "${result.query}" (project=${result.project}, scope=${result.scope})`
484
- )
481
+ console.log(`Mio memory: ${result.count} result(s) for "${result.query}" (project=${result.project}, scope=${result.scope})`)
485
482
  result.results.forEach((record, index) => {
486
483
  const date = (record.timestamp || '').slice(0, 10)
487
484
  console.log(`${index + 1}. [${record.kind || 'note'}] ${record.content}`)
@@ -523,9 +520,7 @@ function tracesCommand(args, useJson) {
523
520
  .filter(([, value]) => value !== null && value !== undefined)
524
521
  .map(([key, value]) => `${key}=${value}`)
525
522
  .join(', ')
526
- console.log(
527
- `Mio traces: ${result.matched} matched / ${result.total} total${filters ? ' (' + filters + ')' : ''}`
528
- )
523
+ console.log(`Mio traces: ${result.matched} matched / ${result.total} total${filters ? ' (' + filters + ')' : ''}`)
529
524
  const summary = Object.entries(result.summary.byOutcome)
530
525
  .map(([key, value]) => `${key}=${value}`)
531
526
  .join(' ')
@@ -534,7 +529,7 @@ function tracesCommand(args, useJson) {
534
529
  const time = (trace.timestamp || '').replace('T', ' ').slice(0, 19)
535
530
  const brief = trace.payload && trace.payload.summary ? ' | ' + String(trace.payload.summary).slice(0, 100) : ''
536
531
  console.log(
537
- `${index + 1}. ${time} [${trace.event_type || '?'}${trace.outcome ? '/' + trace.outcome : ''}] ${trace.agent || '?'} @ ${trace.project || '?'}${brief}`
532
+ `${index + 1}. ${time} [${trace.event_type || '?'}${trace.outcome ? '/' + trace.outcome : ''}] ${trace.agent || '?'} @ ${trace.project || '?'}${brief}`,
538
533
  )
539
534
  console.log(` ${trace.id} trace_id=${trace.trace_id || '-'}`)
540
535
  })
@@ -587,15 +582,11 @@ function digestCommand(args, useJson) {
587
582
  targetCwds = [explicitCwd]
588
583
  } else if (explicitProject) {
589
584
  const wanted = String(explicitProject).toLowerCase()
590
- targetCwds = Object.keys(state.contexts).filter(
591
- (cwd) => path.basename(cwd).toLowerCase() === wanted,
592
- )
585
+ targetCwds = Object.keys(state.contexts).filter((cwd) => path.basename(cwd).toLowerCase() === wanted)
593
586
  } else {
594
587
  // No explicit target: only write to workspaces this digest has data for.
595
588
  // A workspace with no matching project gets nothing.
596
- targetCwds = Object.keys(state.contexts).filter((cwd) =>
597
- projectLine.has(path.basename(cwd).toLowerCase()),
598
- )
589
+ targetCwds = Object.keys(state.contexts).filter((cwd) => projectLine.has(path.basename(cwd).toLowerCase()))
599
590
  }
600
591
 
601
592
  let written = 0
@@ -633,9 +624,7 @@ function digestCommand(args, useJson) {
633
624
  console.log(report.markdown)
634
625
  console.log(`Report saved: ${reportFile}`)
635
626
  if (report.writeBack) {
636
- console.log(
637
- `Write-back: ${report.writeBack.workspaces} workspace(s), ${report.writeBack.skipped} skipped (no digest data)`,
638
- )
627
+ console.log(`Write-back: ${report.writeBack.workspaces} workspace(s), ${report.writeBack.skipped} skipped (no digest data)`)
639
628
  if (report.writeBack.failures && report.writeBack.failures.length > 0) {
640
629
  console.error(` ${report.writeBack.failures.length} write-back failure(s):`)
641
630
  for (const f of report.writeBack.failures) console.error(` - ${f.workspace}: ${f.error}`)
@@ -665,7 +654,7 @@ function pruneCommand(args, useJson) {
665
654
  }
666
655
  if (useJson) return jsonOrText(result, true)
667
656
  console.log(
668
- `Prune ${dryRun ? 'plan (dry-run)' : 'result'}: cutoff ${result.cutoff}, days=${days}${includeMemory ? ', memory included' : ''}`
657
+ `Prune ${dryRun ? 'plan (dry-run)' : 'result'}: cutoff ${result.cutoff}, days=${days}${includeMemory ? ', memory included' : ''}`,
669
658
  )
670
659
  for (const file of result.files) {
671
660
  console.log(`- ${file.file}: ${file.total} total -> keep ${file.kept}, remove ${file.removed}`)
@@ -678,18 +667,24 @@ function pruneCommand(args, useJson) {
678
667
  function rememberCommand(args, useJson) {
679
668
  const content = args[1]
680
669
  if (!content) {
681
- console.error('Usage: mio remember "<content>" [--kind decision|context|problem|note] [--tags a,b] [--scope project|global] [--project name] [--json]')
670
+ console.error(
671
+ 'Usage: mio remember "<content>" [--kind decision|context|problem|note] [--tags a,b] [--hypothesis-id id] [--scope project|global] [--project name] [--json]',
672
+ )
682
673
  process.exitCode = 1
683
674
  return
684
675
  }
685
676
  const flags = args.slice(2)
686
677
  const store = cliMemoryStore()
678
+ // optionValue returns the next token even if it is another flag; a bare
679
+ // trailing `--hypothesis-id` must not swallow `--json` as a "uuid".
680
+ const hypothesisId = optionValue(flags, '--hypothesis-id')
687
681
  let record
688
682
  try {
689
683
  record = store.recordMemory({
690
684
  content,
691
685
  kind: optionValue(flags, '--kind'),
692
686
  tags: splitTagsOption(flags, '--tags'),
687
+ hypothesisId: hypothesisId && !hypothesisId.startsWith('--') ? hypothesisId : undefined,
693
688
  scope: optionValue(flags, '--scope'),
694
689
  project: optionValue(flags, '--project'),
695
690
  source: 'cli',
@@ -706,8 +701,18 @@ function rememberCommand(args, useJson) {
706
701
  // Every flag that consumes the next token. A positional directly after one of
707
702
  // these is that flag's value, not an id.
708
703
  const VALUE_FLAGS = new Set([
709
- '--ids', '--reason', '--project', '--limit', '--scope', '--keep',
710
- '--by', '--notes', '--source-agent', '--target-agent', '--experience-id', '--status',
704
+ '--ids',
705
+ '--reason',
706
+ '--project',
707
+ '--limit',
708
+ '--scope',
709
+ '--keep',
710
+ '--by',
711
+ '--notes',
712
+ '--source-agent',
713
+ '--target-agent',
714
+ '--experience-id',
715
+ '--status',
711
716
  ])
712
717
 
713
718
  // Accept ids both as `--ids a,b,c` and as bare positionals
@@ -737,9 +742,7 @@ function collectIds(flags) {
737
742
 
738
743
  function printMemoryAnalyze(result) {
739
744
  const project = result.project || 'all'
740
- console.log(
741
- `Memory analyze: ${result.total} active, ${result.archived} archived (project=${project})`,
742
- )
745
+ console.log(`Memory analyze: ${result.total} active, ${result.archived} archived (project=${project})`)
743
746
  console.log(`layers: project=${result.layers.project} global=${result.layers.global}`)
744
747
  const kinds = Object.entries(result.byKind)
745
748
  if (kinds.length > 0) {
@@ -809,9 +812,7 @@ function printMemoryMerge(result) {
809
812
  return
810
813
  }
811
814
 
812
- console.log(
813
- `Merged ${result.archivedCount} record(s) into ${result.survivor.id} (project=${result.project || 'current'})`,
814
- )
815
+ console.log(`Merged ${result.archivedCount} record(s) into ${result.survivor.id} (project=${result.project || 'current'})`)
815
816
  console.log(`survivor: ${result.survivor.contentLength} chars, ${result.survivor.timestamp || 'no timestamp'}`)
816
817
  if (result.divergentContent) console.log('note: content differed across members; the survivor body was chosen explicitly')
817
818
  console.log(`supersedes: ${result.survivor.supersedes.join(', ')}`)
@@ -917,7 +918,11 @@ function memoryCommand(args, useJson) {
917
918
  const matched = all.filter((r) => idSet.has(r.id))
918
919
  console.log(`Forget preview: ${ids.length} id(s) (project=${previewProject})`)
919
920
  for (const record of matched) {
920
- console.log(` ${record.id} ${String(record.content || '').replace(/\s+/g, ' ').slice(0, 80)}`)
921
+ console.log(
922
+ ` ${record.id} ${String(record.content || '')
923
+ .replace(/\s+/g, ' ')
924
+ .slice(0, 80)}`,
925
+ )
921
926
  }
922
927
  for (const id of ids) {
923
928
  if (!matched.some((r) => r.id === id)) console.log(` ${id} (not found)`)
@@ -1071,9 +1076,7 @@ Options:
1071
1076
  }
1072
1077
 
1073
1078
  function printExperienceList(result) {
1074
- console.log(
1075
- `Experience reuse: ${result.count} shown of ${result.total} (project=${result.project || 'all'}, status=${result.status})`,
1076
- )
1079
+ console.log(`Experience reuse: ${result.count} shown of ${result.total} (project=${result.project || 'all'}, status=${result.status})`)
1077
1080
  if (result.count === 0) {
1078
1081
  console.log('No reuse records match.')
1079
1082
  return
@@ -1082,23 +1085,15 @@ function printExperienceList(result) {
1082
1085
  const state = record.confirmed ? 'confirmed' : 'pending'
1083
1086
  const badge = record.reuse && record.behaviorChanged && record.outcomeImproved ? 'verified' : state
1084
1087
  const arrow = `${record.sourceAgent || '?'} -> ${record.targetAgent || '?'}`
1085
- const flags = [
1086
- record.reuse ? 'reused' : null,
1087
- record.behaviorChanged ? 'changed' : null,
1088
- record.outcomeImproved ? 'improved' : null,
1089
- ]
1088
+ const flags = [record.reuse ? 'reused' : null, record.behaviorChanged ? 'changed' : null, record.outcomeImproved ? 'improved' : null]
1090
1089
  .filter(Boolean)
1091
1090
  .join(',')
1092
1091
  console.log(`${index + 1}. [${badge}] ${record.experienceId} ${arrow}`)
1093
- console.log(
1094
- ` ${flags || 'no effect flags'} | ${(record.timestamp || '').slice(0, 19)} | ${record.id}`,
1095
- )
1092
+ console.log(` ${flags || 'no effect flags'} | ${(record.timestamp || '').slice(0, 19)} | ${record.id}`)
1096
1093
  if (record.notes) console.log(` note: ${record.notes}`)
1097
1094
  })
1098
1095
  // Only auto_claims are confirmable — experience.confirm rejects anything else.
1099
- const pending = result.records
1100
- .filter((record) => record.source === 'auto_claim' && !record.confirmed)
1101
- .map((record) => record.id)
1096
+ const pending = result.records.filter((record) => record.source === 'auto_claim' && !record.confirmed).map((record) => record.id)
1102
1097
  if (pending.length > 0) {
1103
1098
  console.log('')
1104
1099
  console.log(
@@ -1154,11 +1149,7 @@ function experienceCommand(args, useJson) {
1154
1149
  return
1155
1150
  }
1156
1151
  // Absent = keep whatever the auto-claim recorded; --no-improved rejects it.
1157
- const improved = flagPresent(flags, '--no-improved')
1158
- ? false
1159
- : flagPresent(flags, '--outcome-improved')
1160
- ? true
1161
- : undefined
1152
+ const improved = flagPresent(flags, '--no-improved') ? false : flagPresent(flags, '--outcome-improved') ? true : undefined
1162
1153
  result = store.confirmReuse({
1163
1154
  ids,
1164
1155
  outcomeImproved: improved,
@@ -1256,9 +1247,7 @@ function printPolicyCheck(result) {
1256
1247
  console.log('Try a more specific action, e.g. mio policy check "npm publish".')
1257
1248
  } else if (diag && diag.lowSample) {
1258
1249
  console.log('')
1259
- console.log(
1260
- `NOTE: only ${diag.sampleSize} matching trace(s); treat the level above as a weak signal.`,
1261
- )
1250
+ console.log(`NOTE: only ${diag.sampleSize} matching trace(s); treat the level above as a weak signal.`)
1262
1251
  }
1263
1252
 
1264
1253
  console.log(`\n${result.suggestion}`)
@@ -1496,9 +1485,7 @@ function insightGenerateCommand(args, useJson) {
1496
1485
  return Promise.resolve(result).then((resolved) => {
1497
1486
  if (useJson) return jsonOrText(resolved, true)
1498
1487
  if (!resolved || resolved.generated === 0) {
1499
- console.log(
1500
- `No insights generated: ${(resolved && resolved.reason) || 'nothing notable in the given context'}`
1501
- )
1488
+ console.log(`No insights generated: ${(resolved && resolved.reason) || 'nothing notable in the given context'}`)
1502
1489
  return
1503
1490
  }
1504
1491
  console.log(`Generated ${resolved.generated} insight(s)`)
@@ -1619,7 +1606,10 @@ function printObserverEssays(essays) {
1619
1606
  }
1620
1607
  for (const essay of essays) {
1621
1608
  console.log(`- ${essay.file} (${essay.type}, ${String(essay.created).slice(0, 10)})`)
1622
- const firstLine = String(essay.content || '').split('\n').find((l) => l.trim()) || ''
1609
+ const firstLine =
1610
+ String(essay.content || '')
1611
+ .split('\n')
1612
+ .find((l) => l.trim()) || ''
1623
1613
  if (firstLine) console.log(` ${firstLine.replace(/\s+/g, ' ').slice(0, 120)}`)
1624
1614
  }
1625
1615
  console.log(`\n${essays.length} essay(s)`)
@@ -1956,7 +1946,9 @@ function printObserverServe(result) {
1956
1946
  }
1957
1947
  console.log(`Observer research scheduler running (base-dir=${result.baseDir})`)
1958
1948
  console.log(` ${result.schedule ? result.schedule.collectors : 'collectors started'}`)
1959
- console.log(` pipeline: first tick ${result.schedule ? result.schedule.pipelineFirstTickMs : 5000}ms, then every ${result.schedule ? result.schedule.pipelineTickMs : 60000}ms`)
1949
+ console.log(
1950
+ ` pipeline: first tick ${result.schedule ? result.schedule.pipelineFirstTickMs : 5000}ms, then every ${result.schedule ? result.schedule.pipelineTickMs : 60000}ms`,
1951
+ )
1960
1952
  console.log(' Stops with Ctrl+C (or `mio observe --stop` when started that way).')
1961
1953
  }
1962
1954
 
@@ -2066,11 +2058,12 @@ function observerCommand(args, useJson) {
2066
2058
 
2067
2059
  function creativityUsage(write = console.log) {
2068
2060
  write(`Usage:
2069
- mio creativity status Show hypothesis counts and recent top ideas
2061
+ mio creativity status Show hypothesis counts, recent top ideas, adoption join
2070
2062
  mio creativity list List hypotheses (--status active|validated|rejected|draft, --sort novelty, --limit N)
2071
2063
 
2072
2064
  mio creativity generate Generate hypotheses by combining sources (needs 2+ --source, calls an LLM)
2073
2065
  mio creativity ferment Review/refine active hypotheses (calls an LLM)
2066
+ mio creativity adopt Record an adoption event for a stored hypothesis (writes traces only)
2074
2067
 
2075
2068
  Options:
2076
2069
  --status name Filter list by status
@@ -2098,6 +2091,10 @@ function printCreativityStatus(result) {
2098
2091
  console.log('Creativity engine:')
2099
2092
  console.log(` hypotheses: ${result.hypotheses} combos: ${result.combos} experiments: ${result.experiments}`)
2100
2093
  console.log(` active: ${result.active} validated: ${result.validated} rejected: ${result.rejected} draft: ${result.draft}`)
2094
+ const adoption = result.adoption || { adopted: 0, claimed: 0, evidenced: 0 }
2095
+ console.log(
2096
+ ` adoption: ${adoption.adopted} adopted (events=${adoption.claimed} tags=${adoption.evidenced}) — derived join, not a metric`,
2097
+ )
2101
2098
  if (result.recentIdeas.length === 0) {
2102
2099
  console.log('No hypotheses yet.')
2103
2100
  return
@@ -2118,7 +2115,9 @@ function printCreativityList(items) {
2118
2115
  items.forEach((h, index) => {
2119
2116
  const when = h.createdAt ? new Date(h.createdAt).toISOString().slice(0, 19).replace('T', ' ') : ''
2120
2117
  const labels = Array.isArray(h.sourceLabels) && h.sourceLabels.length > 0 ? h.sourceLabels.join(' + ') : '—'
2121
- console.log(`${index + 1}. [${h.status}] ${h.title} (N=${h.novelty} F=${h.feasibility} I=${h.impact} score=${h.score}${h.noveltyScore !== undefined ? ` noveltyScore=${h.noveltyScore}` : ''})`)
2118
+ console.log(
2119
+ `${index + 1}. [${h.status}]${h.adopted ? ' [adopted]' : ''} ${h.title} (N=${h.novelty} F=${h.feasibility} I=${h.impact} score=${h.score}${h.noveltyScore !== undefined ? ` noveltyScore=${h.noveltyScore}` : ''})`,
2120
+ )
2122
2121
  console.log(` ${labels} | ${when} | ${h.id}`)
2123
2122
  if (h.fermentCount) console.log(` fermented ${h.fermentCount}x`)
2124
2123
  })
@@ -2161,7 +2160,7 @@ function printCreativityErrors(errors, label) {
2161
2160
  if (!Array.isArray(errors) || errors.length === 0) return
2162
2161
  console.log(`\n${errors.length} ${label}(s) failed:`)
2163
2162
  for (const err of errors) {
2164
- const who = err.pair ? err.pair.join(' + ') : (err.title || err.id)
2163
+ const who = err.pair ? err.pair.join(' + ') : err.title || err.id
2165
2164
  console.log(`- ${who}: ${err.error}`)
2166
2165
  }
2167
2166
  }
@@ -2296,10 +2295,45 @@ function creativityFermentCommand(args, useJson) {
2296
2295
  })
2297
2296
  }
2298
2297
 
2298
+ // mio.creativity.adopt from the terminal: same shared adoptHypothesis the MCP
2299
+ // server calls, same enforcement (id must exist; --memory-id must already
2300
+ // carry the hypothesis:<id> tag), event written to MIO_HOME/traces.jsonl.
2301
+ function creativityAdoptCommand(args, useJson) {
2302
+ const flags = args.slice(2)
2303
+ const hypothesisId = flags[0] && !flags[0].startsWith('--') ? String(flags[0]).trim() : ''
2304
+ if (!hypothesisId) {
2305
+ console.error('mio creativity adopt requires <hypothesisId>')
2306
+ console.error('Usage: mio creativity adopt <hypothesisId> [--memory-id id] [--task-id id] [--note text] [--json]')
2307
+ process.exitCode = 1
2308
+ return
2309
+ }
2310
+
2311
+ let result
2312
+ try {
2313
+ result = adoptHypothesis(
2314
+ cliCreativityEngine(),
2315
+ {
2316
+ hypothesisId,
2317
+ memoryId: optionValue(flags, '--memory-id'),
2318
+ taskId: optionValue(flags, '--task-id'),
2319
+ note: optionValue(flags, '--note'),
2320
+ },
2321
+ cliTaskStore().ingestObservation,
2322
+ MIO_HOME,
2323
+ )
2324
+ } catch (error) {
2325
+ console.error(error.message || error)
2326
+ process.exitCode = 1
2327
+ return
2328
+ }
2329
+ if (useJson) return jsonOrText(result, true)
2330
+ console.log(`Adopted hypothesis ${result.hypothesisId}`)
2331
+ console.log(` event: ${result.event && result.event.trace_id ? result.event.trace_id : 'creativity.adopt'}`)
2332
+ }
2333
+
2299
2334
  function ideaUsage() {
2300
2335
  console.error('usage: mio idea generate --goal "one line" [--context "..."] [--constraint "..."] [--num N] [--json]')
2301
2336
  }
2302
-
2303
2337
  async function ideaGenerateCommand(args, useJson) {
2304
2338
  const flags = args.slice(2)
2305
2339
  const goal = (optionValue(flags, '--goal') || '').trim()
@@ -2366,7 +2400,9 @@ async function ideaGenerateCommand(args, useJson) {
2366
2400
  console.log(`Generated ${result.ideas.length} idea(s) (grounded with ${result.groundedWith} memories)`)
2367
2401
  result.ideas.forEach((idea) => {
2368
2402
  console.log(`- [${idea.hypothesis.status}] ${idea.title}`)
2369
- console.log(` novelty=${idea.hypothesis.novelty} feasibility=${idea.hypothesis.feasibility} impact=${idea.hypothesis.impact} | ${idea.hypothesis.id}`)
2403
+ console.log(
2404
+ ` novelty=${idea.hypothesis.novelty} feasibility=${idea.hypothesis.feasibility} impact=${idea.hypothesis.impact} | ${idea.hypothesis.id}`,
2405
+ )
2370
2406
  if (idea.provenance && idea.provenance.technique) console.log(` technique=${idea.provenance.technique}`)
2371
2407
  if (idea.hypothesis.rejectionReason) console.log(` rejected: ${idea.hypothesis.rejectionReason}`)
2372
2408
  })
@@ -2390,6 +2426,7 @@ function creativityCommand(args, useJson) {
2390
2426
  }
2391
2427
  if (sub === 'generate') return creativityGenerateCommand(args, useJson)
2392
2428
  if (sub === 'ferment') return creativityFermentCommand(args, useJson)
2429
+ if (sub === 'adopt') return creativityAdoptCommand(args, useJson)
2393
2430
 
2394
2431
  if (!['status', 'list'].includes(sub)) {
2395
2432
  console.error(`Unknown creativity subcommand: ${sub}`)
@@ -2429,6 +2466,86 @@ function creativityCommand(args, useJson) {
2429
2466
  else printCreativityList(result)
2430
2467
  }
2431
2468
 
2469
+ function mechanismUsage(write = console.log) {
2470
+ write(`mio mechanism <subcommand> — mechanism registry (promotion/rollback)
2471
+
2472
+ Subcommands:
2473
+ mio mechanism registry List versions and promotion requests (--mechanism-id, --status)
2474
+ mio mechanism promote submit ... Submit a PromotionRequest (gate-checked)
2475
+ mio mechanism promote approve ... Approve a request (approver must differ from submitter)
2476
+ mio mechanism promote reject ... Reject a request
2477
+ mio mechanism rollback <mechanismId> Roll back the active version to its rollbackTo target
2478
+
2479
+ Options:
2480
+ --json Machine-readable output
2481
+
2482
+ Examples:
2483
+ mio mechanism registry
2484
+ mio mechanism promote submit --mechanism-id m-1 --version 1 --submitted-by codex
2485
+ mio mechanism promote approve --request-id pr-1 --approved-by other-agent
2486
+ mio mechanism rollback m-1`)
2487
+ }
2488
+
2489
+ function mechanismCommand(args, useJson) {
2490
+ const sub = args[1]
2491
+
2492
+ if (!sub || sub === 'help' || sub === '--help' || sub === '-h') {
2493
+ mechanismUsage()
2494
+ if (!sub) process.exitCode = 1
2495
+ return
2496
+ }
2497
+
2498
+ const flags = args.slice(2)
2499
+
2500
+ try {
2501
+ if (sub === 'registry') {
2502
+ const result = mechanismRegistry(
2503
+ { mechanismId: optionValue(flags, '--mechanism-id'), status: optionValue(flags, '--status') },
2504
+ MIO_HOME,
2505
+ )
2506
+ return jsonOrText(result, useJson)
2507
+ }
2508
+
2509
+ if (sub === 'promote') {
2510
+ const action = flags[0]
2511
+ const result = mechanismPromote(
2512
+ {
2513
+ action,
2514
+ mechanismId: optionValue(flags, '--mechanism-id'),
2515
+ version: parseNumberOption(flags, '--version'),
2516
+ hypothesisId: optionValue(flags, '--hypothesis-id'),
2517
+ experimentRunIds: optionValues(flags, '--experiment-run-ids'),
2518
+ submittedBy: optionValue(flags, '--submitted-by'),
2519
+ requestId: optionValue(flags, '--request-id'),
2520
+ approvedBy: optionValue(flags, '--approved-by'),
2521
+ reason: optionValue(flags, '--reason'),
2522
+ },
2523
+ MIO_HOME,
2524
+ )
2525
+ return jsonOrText(result, useJson)
2526
+ }
2527
+
2528
+ if (sub === 'rollback') {
2529
+ const mechanismId = args[2]
2530
+ if (!mechanismId) {
2531
+ console.error('mio mechanism rollback requires <mechanismId>')
2532
+ console.error('Usage: mio mechanism rollback <mechanismId> [--json]')
2533
+ process.exitCode = 1
2534
+ return
2535
+ }
2536
+ const result = mechanismRollback({ mechanismId }, MIO_HOME)
2537
+ return jsonOrText(result, useJson)
2538
+ }
2539
+
2540
+ console.error(`Unknown mechanism subcommand: ${sub}`)
2541
+ mechanismUsage(console.error)
2542
+ process.exitCode = 1
2543
+ } catch (error) {
2544
+ console.error(error.message || error)
2545
+ process.exitCode = 1
2546
+ }
2547
+ }
2548
+
2432
2549
  function readConfig() {
2433
2550
  const fallback = { version: 1, home: MIO_HOME, agents: {} }
2434
2551
  if (!fs.existsSync(CONFIG_FILE)) return fallback
@@ -2495,7 +2612,7 @@ function configShowCommand(useJson) {
2495
2612
  configured: isLlmConfigured(),
2496
2613
  },
2497
2614
  },
2498
- true
2615
+ true,
2499
2616
  )
2500
2617
  }
2501
2618
 
@@ -2505,7 +2622,7 @@ function configShowCommand(useJson) {
2505
2622
  console.log('LLM (effective):')
2506
2623
  console.log(` apiUrl: ${effective.apiUrl} [${LLM_SOURCE_LABELS[sources.apiUrl]}]`)
2507
2624
  console.log(
2508
- ` apiKey: ${effective.apiKey ? `${maskSecret(effective.apiKey)} [${LLM_SOURCE_LABELS[sources.apiKey]}]` : '(none / unauthenticated)'}`
2625
+ ` apiKey: ${effective.apiKey ? `${maskSecret(effective.apiKey)} [${LLM_SOURCE_LABELS[sources.apiKey]}]` : '(none / unauthenticated)'}`,
2509
2626
  )
2510
2627
  console.log(` model: ${effective.model} [${LLM_SOURCE_LABELS[sources.model]}]`)
2511
2628
  console.log('')
@@ -2589,7 +2706,7 @@ function configLlmCommand(flags, useJson) {
2589
2706
  },
2590
2707
  configFile: CONFIG_FILE,
2591
2708
  },
2592
- true
2709
+ true,
2593
2710
  )
2594
2711
  }
2595
2712
 
@@ -2695,11 +2812,8 @@ function statusCommand(useJson) {
2695
2812
  dataDir: MIO_HOME,
2696
2813
  projectName: () => null,
2697
2814
  }).listReuse({ status: 'pending', limit: 100 }).records
2698
- return records.filter(
2699
- (record) =>
2700
- String(record.sourceAgent || '').toLowerCase() !==
2701
- String(record.targetAgent || '').toLowerCase()
2702
- ).length
2815
+ return records.filter((record) => String(record.sourceAgent || '').toLowerCase() !== String(record.targetAgent || '').toLowerCase())
2816
+ .length
2703
2817
  } catch {
2704
2818
  return 0
2705
2819
  }
@@ -2746,11 +2860,9 @@ function statusCommand(useJson) {
2746
2860
  console.log(`Observer: ${observer.isRunning(MIO_HOME) ? 'running (pid ' + observer.readPid(MIO_HOME) + ')' : 'not running'}`)
2747
2861
  if (pendingAutoClaims > 0) {
2748
2862
  console.log(
2749
- `Pending auto-claims: ${pendingAutoClaims} (${pendingCrossAgent} cross-agent) -- only cross-agent claims whose target output actually cites the source memory may be confirmed; same-agent / weak / self-reported must NOT be (ADR-017).`
2750
- )
2751
- console.log(
2752
- ` review: \`mio experience list --status pending\` then \`mio experience confirm --ids <id,...>\``
2863
+ `Pending auto-claims: ${pendingAutoClaims} (${pendingCrossAgent} cross-agent) -- only cross-agent claims whose target output actually cites the source memory may be confirmed; same-agent / weak / self-reported must NOT be (ADR-017).`,
2753
2864
  )
2865
+ console.log(` review: \`mio experience list --status pending\` then \`mio experience confirm --ids <id,...>\``)
2754
2866
  }
2755
2867
  }
2756
2868
 
@@ -2885,9 +2997,7 @@ function agentsCommand(args, useJson) {
2885
2997
  }
2886
2998
  if (useJson) return jsonOrText(result, true)
2887
2999
  console.log(`Registered ${result.agentId} (project=${project})`)
2888
- console.log(
2889
- ` ${result.registered ? 'created' : `updated (session ${result.sessionCount})`} | host=${result.hostType}`,
2890
- )
3000
+ console.log(` ${result.registered ? 'created' : `updated (session ${result.sessionCount})`} | host=${result.hostType}`)
2891
3001
  if (result.capabilities && result.capabilities.length > 0) {
2892
3002
  console.log(` capabilities: ${result.capabilities.join(', ')}`)
2893
3003
  }
@@ -2908,9 +3018,7 @@ function printAgentList(result) {
2908
3018
  for (const a of result.agents) {
2909
3019
  const caps = Array.isArray(a.capabilities) && a.capabilities.length > 0 ? ` [${a.capabilities.join(', ')}]` : ''
2910
3020
  console.log(`${a.agentId} (${a.hostType})${caps} project=${a.project || '-'}`)
2911
- console.log(
2912
- ` sessions=${a.sessionCount || 0} tasks=${a.taskCount} success=${a.successCount} failure=${a.failureCount} | ${a.id}`,
2913
- )
3021
+ console.log(` sessions=${a.sessionCount || 0} tasks=${a.taskCount} success=${a.successCount} failure=${a.failureCount} | ${a.id}`)
2914
3022
  }
2915
3023
  }
2916
3024
 
@@ -2924,9 +3032,7 @@ function printAgentReport(result) {
2924
3032
  const o = r.taskOutcomes
2925
3033
  const when = r.lastSeenAt ? r.lastSeenAt.slice(0, 19).replace('T', ' ') : 'unknown'
2926
3034
  console.log(`${r.agentId} (${r.hostType}) ${r.active ? 'active' : 'idle'}`)
2927
- console.log(
2928
- ` tasks: ${o.total} total, ${o.success} success, ${o.failure} failure (${o.successRate}% success)`,
2929
- )
3035
+ console.log(` tasks: ${o.total} total, ${o.success} success, ${o.failure} failure (${o.successRate}% success)`)
2930
3036
  console.log(` memories: ${r.memories} experience reuses: ${r.experienceReuses.total} (verified ${r.experienceReuses.verified})`)
2931
3037
  console.log(` last seen: ${when} | sessions=${r.sessionCount}`)
2932
3038
  }
@@ -2950,12 +3056,8 @@ function printRate(label, metric, numeratorKey, denominatorKey, unit, rateKey =
2950
3056
 
2951
3057
  function printEvaluation(result) {
2952
3058
  const s = result.sample
2953
- console.log(
2954
- `ADR-017 evaluation${result.project ? ` (project=${result.project})` : ''}${result.since ? ` since ${result.since}` : ''}`,
2955
- )
2956
- console.log(
2957
- ` sample: queries=${s.queries} routeHits=${s.routeHits} reuses=${s.reuses} traces=${s.traces} agents=${s.agents}`,
2958
- )
3059
+ console.log(`ADR-017 evaluation${result.project ? ` (project=${result.project})` : ''}${result.since ? ` since ${result.since}` : ''}`)
3060
+ console.log(` sample: queries=${s.queries} routeHits=${s.routeHits} reuses=${s.reuses} traces=${s.traces} agents=${s.agents}`)
2959
3061
  console.log(' 1. route adoption rate (hit and acted on / hits)')
2960
3062
  printRate('adoption', result.routeAdoption, 'adopted', 'routeHits', 'experience hits')
2961
3063
  console.log(' 2. behaviour change rate (confirmed behaviorChanged / reuses)')
@@ -2969,9 +3071,7 @@ function printEvaluation(result) {
2969
3071
  if (base) {
2970
3072
  const now = result.behaviorChange.verified
2971
3073
  const verdict = now >= base.observed ? 'at or above' : now >= base.required ? 'above threshold, below baseline' : 'BELOW BASELINE'
2972
- console.log(
2973
- ` baseline ${result.baseline.date}: ${base.observed} (required ${base.required}) -- now ${now}, ${verdict}`,
2974
- )
3074
+ console.log(` baseline ${result.baseline.date}: ${base.observed} (required ${base.required}) -- now ${now}, ${verdict}`)
2975
3075
  }
2976
3076
  console.log(' 3. recall quality (queries with results that led to a reuse)')
2977
3077
  printRate('yield', result.recallQuality, 'queriesLinkedToReuse', 'queriesWithResults', 'queries', 'yield')
@@ -3350,11 +3450,7 @@ function installCommand(host, useJson) {
3350
3450
  if (!observerCycleOk) {
3351
3451
  console.error('Warning: observer cycle failed during install; run `mio observe --once` manually.')
3352
3452
  }
3353
- console.log(
3354
- observer.isRunning(MIO_HOME)
3355
- ? 'Observer daemon running.'
3356
- : 'Observer daemon not running. Run `mio observe --start`.'
3357
- )
3453
+ console.log(observer.isRunning(MIO_HOME) ? 'Observer daemon running.' : 'Observer daemon not running. Run `mio observe --start`.')
3358
3454
  }
3359
3455
 
3360
3456
  function mcpCommand() {
@@ -3395,7 +3491,8 @@ function observeCommand(args, jsonFlag = false) {
3395
3491
  console.log(result.started ? 'Observer started.' : 'Observer already running.')
3396
3492
  if (researchResult) {
3397
3493
  if (researchResult.skipped) console.log('Research scheduler skipped: ' + researchResult.skipped)
3398
- else if (researchResult.started) console.log('Research scheduler started (pid ' + researchResult.pid + ', base-dir ' + researchResult.baseDir + ')')
3494
+ else if (researchResult.started)
3495
+ console.log('Research scheduler started (pid ' + researchResult.pid + ', base-dir ' + researchResult.baseDir + ')')
3399
3496
  else console.log('Research scheduler already running (pid ' + researchResult.pid + ')')
3400
3497
  }
3401
3498
  return
@@ -3404,10 +3501,7 @@ function observeCommand(args, jsonFlag = false) {
3404
3501
  const result = observer.stopBackground(MIO_HOME)
3405
3502
  const researchResult = observer.stopResearchBackground(MIO_HOME)
3406
3503
  if (useJson) {
3407
- return jsonOrText(
3408
- { ...result, research: { running: false, stopped: researchResult.stopped, pid: researchResult.pid } },
3409
- true
3410
- )
3504
+ return jsonOrText({ ...result, research: { running: false, stopped: researchResult.stopped, pid: researchResult.pid } }, true)
3411
3505
  }
3412
3506
  console.log(result.stopped ? 'Observer stopped.' : 'Observer was not running.')
3413
3507
  console.log(researchResult.stopped ? 'Research scheduler stopped.' : 'Research scheduler was not running.')
@@ -3421,9 +3515,7 @@ function observeCommand(args, jsonFlag = false) {
3421
3515
  const researchInfo = { running: researchRunning, pid: researchPid, logFile: observer.researchLogFile(MIO_HOME) }
3422
3516
  if (useJson) return jsonOrText({ running, pid, research: researchInfo }, true)
3423
3517
  console.log(running ? 'Observer: running (pid ' + pid + ')' : 'Observer: not running')
3424
- console.log(
3425
- researchRunning ? 'Research scheduler: running (pid ' + researchPid + ')' : 'Research scheduler: not running'
3426
- )
3518
+ console.log(researchRunning ? 'Research scheduler: running (pid ' + researchPid + ')' : 'Research scheduler: not running')
3427
3519
  return
3428
3520
  }
3429
3521
  if (flags.includes('--research') && !flags.includes('--start')) {
@@ -3432,11 +3524,7 @@ function observeCommand(args, jsonFlag = false) {
3432
3524
  return
3433
3525
  }
3434
3526
  if (flags.includes('--once')) {
3435
- const res = observer.runCycle(
3436
- MIO_HOME,
3437
- observer.loadState(MIO_HOME),
3438
- observer.createSinks(MIO_HOME)
3439
- )
3527
+ const res = observer.runCycle(MIO_HOME, observer.loadState(MIO_HOME), observer.createSinks(MIO_HOME))
3440
3528
  if (useJson) return jsonOrText(res, true)
3441
3529
  console.log('Observed files=' + res.files + ' lines=' + res.lines + ' outcomes=' + res.outcomes)
3442
3530
  return
@@ -3492,10 +3580,14 @@ Usage:
3492
3580
  mio experience list List experience reuse (--status pending|confirmed|verified)
3493
3581
  mio experience confirm --ids a,b Confirm auto-claimed reuse (bulk supported)
3494
3582
  mio experience reuse --source-agent A --target-agent B --experience-id X Record a reuse
3495
- mio creativity status Show creativity hypothesis counts and recent top ideas
3583
+ mio creativity status Show creativity hypothesis counts, recent top ideas, adoption join
3496
3584
  mio creativity list List creativity hypotheses (--status active|validated|rejected|draft, --sort novelty, --limit N)
3497
3585
  mio creativity generate Generate hypotheses from 2+ --source "name|content" (or --from-insights; calls an LLM)
3498
3586
  mio creativity ferment Review and refine active hypotheses (calls an LLM)
3587
+ mio creativity adopt Record an adoption event for a stored hypothesis (writes traces only)
3588
+ mio mechanism registry List mechanism versions and promotion requests
3589
+ mio mechanism promote Submit/approve/reject a PromotionRequest (approver must differ from submitter)
3590
+ mio mechanism rollback Roll back the active version of a mechanism
3499
3591
  mio idea generate --goal "..." Grounded idea pipeline: auto sources, novelty gate, persisted (calls an LLM)
3500
3592
  mio insight status Insight counts: total, reported, unreported, high-value
3501
3593
  mio insight list List insights (--unreported, --min-score N, --detector X, --limit N)
@@ -3560,6 +3652,8 @@ async function main() {
3560
3652
  return policyCommand(args, useJson)
3561
3653
  case 'creativity':
3562
3654
  return creativityCommand(args, useJson)
3655
+ case 'mechanism':
3656
+ return mechanismCommand(args, useJson)
3563
3657
  case 'idea':
3564
3658
  return ideaCommand(args, useJson)
3565
3659
  case 'insight':