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 +78 -72
- package/observe/observer.js +71 -8
- package/package.json +24 -24
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
|
-
|
|
53
|
-
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
├──
|
|
71
|
-
│
|
|
72
|
-
|
|
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
|
```
|
package/observe/observer.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
+
}
|