mio-agent-runtime 0.3.0 → 0.3.2

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/README.md CHANGED
@@ -1,73 +1,79 @@
1
- # mio-agent-runtime
2
-
3
- Mio Agent Runtime 的可发布 CLI 包。
4
-
5
- 安装一次,所有 Agent 自动接入 Mio。
6
-
7
- ## Install
8
-
9
- ```bash
10
- npm install -g mio-agent-runtime
11
- ```
12
-
13
- ## Commands
14
-
15
- ```text
16
- mio init Initialize MIO_HOME
17
- mio mcp Start Mio MCP server (stdio)
18
- mio install <host> Install Mio into a host (codex|opencode|workbuddy)
19
- mio status Show runtime and adapter status
20
- mio agents List installed agents
21
- mio observe Watch WorkBuddy transcripts and auto-ingest task outcomes (foreground)
22
- mio observe --start|--stop|--status|--once Manage the background observer daemon
23
- mio --json status Machine-readable status
24
- mio --json agents Machine-readable agents
25
- ```
26
-
27
- ## Host notes
28
-
29
- - OpenCode uses `~/.config/opencode/opencode.json` for MCP registration and
30
- `~/.config/opencode/AGENTS.md` for global instructions. `mio install opencode`
31
- writes both files and injects Mio usage rules into `AGENTS.md` so OpenCode
32
- calls Mio memory/observer/policy automatically. If OpenCode is already
33
- running, restart it once so it reloads both files.
34
- - WorkBuddy uses `~/.workbuddy/mcp.json` and requires each custom MCP server to
35
- be approved in `~/.workbuddy/mcp-approvals.json`. `mio install workbuddy`
36
- writes both files and injects Mio usage rules into
37
- `~/.workbuddy/CODEBUDDY.md` (user-level memory rules, auto-loaded by
38
- WorkBuddy into every session; the CLI hardcodes the CodeBuddy product name, so the file is CODEBUDDY.md not WORKBUDDY.md) plus `<workspace>/AGENTS.md` (project-level).
39
- If WorkBuddy is already running, restart it once so it reloads the approval
40
- and memory rule files.
41
-
42
- ## Passive observer (`mio observe`)
43
-
44
- WorkBuddy 的默认模型不一定会主动调用 Mio MCP 工具,即使规则已注入
45
- (`CODEBUDDY.md` / `AGENTS.md`)。`mio observe` 是一个不依赖模型自觉的
46
- 兜底:它持续监听 `~/.workbuddy/projects/**/*.jsonl` 会话记录,把每个任务的
47
- 结果(`task_outcome`)、工具调用错误(`error`)以及完成任务的摘要自动写入
48
- `MIO_HOME/traces.jsonl` 和 `MIO_HOME/memory.jsonl`(与 MCP 服务同一套
49
- schema,`source=workbuddy-observer`)。
50
-
51
- - `mio install <host>` 会自动执行一次回填并启动后台观察器。
52
- - 已处理的文件偏移记录在 `MIO_HOME/observe-state.json`,不会重复写入。
53
- - 只回填最近 24h 内更新的会话,避免把历史记录灌入记忆库。
54
-
55
- ## MIO_HOME
56
-
57
- 默认 `~/.mio-intelligence`,可通过环境变量 `MIO_HOME` 覆盖。
58
-
59
- ## Package layout
60
-
61
- ```text
62
- packages/mio-cli
63
- ├── bin/mio.js
64
- ├── adapters/
65
- │ ├── codex.js
66
- │ ├── opencode.js
67
- │ └── workbuddy.js
68
- ├── observe/
69
- │ └── observer.js
70
- ├── server/
71
- │ └── mio-intelligence-mcp/index.js
72
- └── package.json
1
+ # mio-agent-runtime
2
+
3
+ Mio Agent Runtime 的可发布 CLI 包。
4
+
5
+ 安装一次,所有 Agent 自动接入 Mio。
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g mio-agent-runtime
11
+ ```
12
+
13
+ ## Commands
14
+
15
+ ```text
16
+ mio init Initialize MIO_HOME
17
+ mio mcp Start Mio MCP server (stdio)
18
+ mio install <host> Install Mio into a host (codex|opencode|workbuddy)
19
+ mio status Show runtime and adapter status
20
+ mio agents List installed agents
21
+ mio observe Watch WorkBuddy transcripts and auto-ingest task outcomes (foreground)
22
+ mio observe --start|--stop|--status|--once Manage the background observer daemon
23
+ mio --json status Machine-readable status
24
+ mio --json agents Machine-readable agents
25
+ ```
26
+
27
+ ## Host notes
28
+
29
+ - OpenCode uses `~/.config/opencode/opencode.json` for MCP registration and
30
+ `~/.config/opencode/AGENTS.md` for global instructions. `mio install opencode`
31
+ writes both files and injects Mio usage rules into `AGENTS.md` so OpenCode
32
+ calls Mio memory/observer/policy automatically. If OpenCode is already
33
+ running, restart it once so it reloads both files.
34
+ - WorkBuddy uses `~/.workbuddy/mcp.json` and requires each custom MCP server to
35
+ be approved in `~/.workbuddy/mcp-approvals.json`. `mio install workbuddy`
36
+ writes both files and injects Mio usage rules into
37
+ `~/.workbuddy/CODEBUDDY.md` (user-level memory rules, auto-loaded by
38
+ WorkBuddy into every session; the CLI hardcodes the CodeBuddy product name, so the file is CODEBUDDY.md not WORKBUDDY.md) plus `<workspace>/AGENTS.md` (project-level).
39
+ If WorkBuddy is already running, restart it once so it reloads the approval
40
+ and memory rule files.
41
+
42
+ ## Passive observer (`mio observe`)
43
+
44
+ WorkBuddy 的默认模型不一定会主动调用 Mio MCP 工具,即使规则已注入
45
+ (`CODEBUDDY.md` / `AGENTS.md`)。`mio observe` 是一个不依赖模型自觉的
46
+ 兜底:它持续监听 `~/.workbuddy/projects/**/*.jsonl` 会话记录,把每个任务的
47
+ 结果(`task_outcome`)、工具调用错误(`error`)以及完成任务的摘要自动写入
48
+ `MIO_HOME/traces.jsonl` 和 `MIO_HOME/memory.jsonl`(与 MCP 服务同一套
49
+ schema,`source=workbuddy-observer`)。
50
+
51
+ - `mio install <host>` 会自动执行一次回填并启动后台观察器。
52
+
53
+ - 除了写入 `MIO_HOME`,观察器还会把任务摘要**写回 WorkBuddy 自己的记忆文件**
54
+ (`<workspace>/.workbuddy/memory/YYYY-MM-DD.md`,标为 `## Mio 自动摘要`)。
55
+ WorkBuddy 的系统提示要求模型每次会话维护并阅读这些文件,所以下一次会话
56
+ WorkBuddy 会**被动取到** Mio 记录的内容——单 Agent 的「存 + 取」闭环不依赖
57
+ 模型调用 Mio 工具。
58
+ - 已处理的文件偏移记录在 `MIO_HOME/observe-state.json`,不会重复写入。
59
+ - 只回填最近 24h 内更新的会话,避免把历史记录灌入记忆库。
60
+
61
+ ## MIO_HOME
62
+
63
+ 默认 `~/.mio-intelligence`,可通过环境变量 `MIO_HOME` 覆盖。
64
+
65
+ ## Package layout
66
+
67
+ ```text
68
+ packages/mio-cli
69
+ ├── bin/mio.js
70
+ ├── adapters/
71
+ │ ├── codex.js
72
+ │ ├── opencode.js
73
+ │ └── workbuddy.js
74
+ ├── observe/
75
+ │ └── observer.js
76
+ ├── server/
77
+ │ └── mio-intelligence-mcp/index.js
78
+ └── package.json
73
79
  ```
@@ -74,10 +74,11 @@ function loadState(home) {
74
74
  files: parsed.files || {},
75
75
  buffers: parsed.buffers || {},
76
76
  openTurns: parsed.openTurns || {},
77
+ contexts: parsed.contexts || {},
77
78
  }
78
79
  }
79
80
  } catch (_) {}
80
- return { version: 1, files: {}, buffers: {}, openTurns: {} }
81
+ return { version: 1, files: {}, buffers: {}, openTurns: {}, contexts: {} }
81
82
  }
82
83
 
83
84
  function saveState(home, state) {
@@ -178,6 +179,7 @@ function createTurn(meta) {
178
179
  ts: null,
179
180
  project: meta.project,
180
181
  sessionId: meta.sessionId,
182
+ cwd: meta.cwd || null,
181
183
  toolCount: 0,
182
184
  errors: [],
183
185
  lastAssistant: null,
@@ -187,12 +189,14 @@ function createTurn(meta) {
187
189
  function feedWorkbuddyLine(turn, obj) {
188
190
  if (!obj || typeof obj !== 'object') return { finalized: null, next: turn }
189
191
  let finalized = null
192
+ if (obj.cwd) turn.cwd = obj.cwd
190
193
  if (obj.type === 'message') {
191
194
  if (obj.role === 'user') {
192
195
  finalized = turn
193
196
  const next = createTurn(turn)
194
197
  next.id = obj.id
195
198
  next.ts = obj.timestamp
199
+ next.cwd = obj.cwd || turn.cwd
196
200
  return { finalized, next: next }
197
201
  }
198
202
  if (obj.role === 'assistant') {
@@ -218,9 +222,63 @@ function feedWorkbuddyLine(turn, obj) {
218
222
  return { finalized: finalized, next: turn }
219
223
  }
220
224
 
221
- function emitWorkbuddyOutcome(turn, sinks) {
225
+ const CONTEXT_BEGIN = '<!-- MIO_CONTEXT_BEGIN -->'
226
+ const CONTEXT_END = '<!-- MIO_CONTEXT_END -->'
227
+ const CONTEXT_MAX = 6
228
+
229
+ // Maintain a compact 'recent Mio context' block inside <workspace>/AGENTS.md,
230
+ // which WorkBuddy's MemoryLoader auto-injects into every session. This makes
231
+ // retrieval guaranteed (not dependent on the model deciding to read memory).
232
+ function updateProjectContext(state, cwd, line) {
233
+ if (!cwd || !line) return
234
+ const target = path.join(cwd, 'AGENTS.md')
235
+ const list = state.contexts[cwd] || []
236
+ list.unshift(line)
237
+ if (list.length > CONTEXT_MAX) list.pop()
238
+ state.contexts[cwd] = list
239
+ try {
240
+ let content = fs.existsSync(target) ? fs.readFileSync(target, 'utf8') : ''
241
+ const block = CONTEXT_BEGIN + '\n## Mio 最近上下文(自动注入)\n' + list.map(function (l) { return '- ' + l }).join('\n') + '\n' + CONTEXT_END
242
+ const start = content.indexOf(CONTEXT_BEGIN)
243
+ if (start !== -1) {
244
+ const end = content.indexOf(CONTEXT_END, start)
245
+ content = end === -1 ? content.slice(0, start) : content.slice(0, start) + content.slice(end + CONTEXT_END.length)
246
+ }
247
+ content = content.trimEnd() + '\n\n' + block + '\n'
248
+ fs.mkdirSync(path.dirname(target), { recursive: true })
249
+ fs.writeFileSync(target, content, 'utf8')
250
+ } catch (_) {}
251
+ }
252
+
253
+ function localDateKey(ts) {
254
+ const d = new Date(Number(ts) || Date.now())
255
+ const y = d.getFullYear()
256
+ const m = String(d.getMonth() + 1).padStart(2, '0')
257
+ const day = String(d.getDate()).padStart(2, '0')
258
+ return y + '-' + m + '-' + day
259
+ }
260
+
261
+ // Write a compact Mio digest back into WorkBuddy's own memory file
262
+ // (<workspace>/.workbuddy/memory/YYYY-MM-DD.md). WorkBuddy instructs its model
263
+ // to maintain and read these files every session, so the digest is
264
+ // 'retrieved' next session without any Mio tool call by the model.
265
+ function writeWorkbuddyDigest(turn, outcome, summary) {
266
+ if (!turn.cwd || !summary) return
267
+ const dir = path.join(turn.cwd, '.workbuddy', 'memory')
268
+ const file = path.join(dir, localDateKey(turn.ts) + '.md')
269
+ try {
270
+ fs.mkdirSync(dir, { recursive: true })
271
+ const time = new Date(Number(turn.ts) || Date.now()).toLocaleString('zh-CN', { hour12: false })
272
+ const entry =
273
+ '\n## Mio 自动摘要 (' + time + ')\n' +
274
+ '- outcome: ' + outcome + ' | tools: ' + turn.toolCount + ' | ' + summary.slice(0, 160) + '\n'
275
+ fs.appendFileSync(file, entry, 'utf8')
276
+ } catch (_) {}
277
+ }
278
+
279
+ function emitWorkbuddyOutcome(turn, sinks, state) {
222
280
  const la = turn.lastAssistant
223
- if (!la) return
281
+ if (!la) return false
224
282
  let outcome
225
283
  if (la.status === 'completed') outcome = 'success'
226
284
  else if (la.error) outcome = 'failure'
@@ -248,7 +306,10 @@ function emitWorkbuddyOutcome(turn, sinks) {
248
306
  project: turn.project,
249
307
  })
250
308
 
251
- if (outcome === 'success' && summary.length >= 40) {
309
+ // Low threshold: even a short but real completion summary must reach the
310
+ // passive retrieval paths (memory + daily digest + AGENTS.md context block).
311
+ // 40 was too strict and silently dropped short summaries from retrieval.
312
+ if (outcome === 'success' && summary.length >= 10) {
252
313
  sinks.memory({
253
314
  kind: 'note',
254
315
  content: '[workbuddy] 任务完成: ' + summary.slice(0, 160),
@@ -256,6 +317,9 @@ function emitWorkbuddyOutcome(turn, sinks) {
256
317
  project: turn.project,
257
318
  source: 'workbuddy-observer',
258
319
  })
320
+ writeWorkbuddyDigest(turn, outcome, summary)
321
+ var ctxTime = new Date(Number(turn.ts) || Date.now()).toLocaleString('zh-CN', { hour12: false })
322
+ updateProjectContext(state, turn.cwd, ctxTime + ' | ' + outcome + ' | ' + summary.slice(0, 80))
259
323
  }
260
324
 
261
325
  for (const err of turn.errors) {
@@ -276,6 +340,7 @@ function emitWorkbuddyOutcome(turn, sinks) {
276
340
  project: turn.project,
277
341
  })
278
342
  }
343
+ return true
279
344
  }
280
345
 
281
346
  // ---------------------------------------------------------------------------
@@ -338,8 +403,7 @@ function runCycle(home, state, sinks, opts) {
338
403
  }
339
404
  const result = feedWorkbuddyLine(turn, obj)
340
405
  if (result.finalized) {
341
- emitWorkbuddyOutcome(result.finalized, sinks)
342
- out.outcomes += 1
406
+ if (emitWorkbuddyOutcome(result.finalized, sinks, state)) out.outcomes += 1
343
407
  }
344
408
  turn = result.next
345
409
  }
@@ -347,8 +411,7 @@ function runCycle(home, state, sinks, opts) {
347
411
  }
348
412
 
349
413
  if (turn.lastAssistant && now - st.mtimeMs > IDLE_MS) {
350
- emitWorkbuddyOutcome(turn, sinks)
351
- out.outcomes += 1
414
+ if (emitWorkbuddyOutcome(turn, sinks, state)) out.outcomes += 1
352
415
  state.openTurns[file] = null
353
416
  } else if (turn.id) {
354
417
  state.openTurns[file] = turn
package/package.json CHANGED
@@ -1,24 +1,24 @@
1
- {
2
- "name": "mio-agent-runtime",
3
- "version": "0.3.0",
4
- "description": "Mio Agent Runtime installer and MCP intelligence layer",
5
- "license": "MIT",
6
- "type": "commonjs",
7
- "bin": {
8
- "mio": "bin/mio.js"
9
- },
10
- "files": [
11
- "bin/",
12
- "server/",
13
- "adapters/",
14
- "observe/",
15
- "README.md"
16
- ],
17
- "scripts": {
18
- "check": "node --check bin/mio.js && node --check adapters/codex.js && node --check adapters/opencode.js && node --check adapters/workbuddy.js && node --check server/mio-intelligence-mcp/index.js && node --check observe/observer.js",
19
- "prepack": "npm run check"
20
- },
21
- "engines": {
22
- "node": ">=18"
23
- }
24
- }
1
+ {
2
+ "name": "mio-agent-runtime",
3
+ "version": "0.3.2",
4
+ "description": "Mio Agent Runtime installer and MCP intelligence layer",
5
+ "license": "MIT",
6
+ "type": "commonjs",
7
+ "bin": {
8
+ "mio": "bin/mio.js"
9
+ },
10
+ "files": [
11
+ "bin/",
12
+ "server/",
13
+ "adapters/",
14
+ "observe/",
15
+ "README.md"
16
+ ],
17
+ "scripts": {
18
+ "check": "node --check bin/mio.js && node --check adapters/codex.js && node --check adapters/opencode.js && node --check adapters/workbuddy.js && node --check server/mio-intelligence-mcp/index.js && node --check observe/observer.js",
19
+ "prepack": "npm run check"
20
+ },
21
+ "engines": {
22
+ "node": ">=18"
23
+ }
24
+ }