mio-agent-runtime 0.3.0 → 0.3.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.
- package/README.md +78 -72
- package/observe/observer.js +34 -5
- package/package.json +1 -1
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
|
@@ -178,6 +178,7 @@ function createTurn(meta) {
|
|
|
178
178
|
ts: null,
|
|
179
179
|
project: meta.project,
|
|
180
180
|
sessionId: meta.sessionId,
|
|
181
|
+
cwd: meta.cwd || null,
|
|
181
182
|
toolCount: 0,
|
|
182
183
|
errors: [],
|
|
183
184
|
lastAssistant: null,
|
|
@@ -187,12 +188,14 @@ function createTurn(meta) {
|
|
|
187
188
|
function feedWorkbuddyLine(turn, obj) {
|
|
188
189
|
if (!obj || typeof obj !== 'object') return { finalized: null, next: turn }
|
|
189
190
|
let finalized = null
|
|
191
|
+
if (obj.cwd) turn.cwd = obj.cwd
|
|
190
192
|
if (obj.type === 'message') {
|
|
191
193
|
if (obj.role === 'user') {
|
|
192
194
|
finalized = turn
|
|
193
195
|
const next = createTurn(turn)
|
|
194
196
|
next.id = obj.id
|
|
195
197
|
next.ts = obj.timestamp
|
|
198
|
+
next.cwd = obj.cwd || turn.cwd
|
|
196
199
|
return { finalized, next: next }
|
|
197
200
|
}
|
|
198
201
|
if (obj.role === 'assistant') {
|
|
@@ -218,9 +221,35 @@ function feedWorkbuddyLine(turn, obj) {
|
|
|
218
221
|
return { finalized: finalized, next: turn }
|
|
219
222
|
}
|
|
220
223
|
|
|
224
|
+
function localDateKey(ts) {
|
|
225
|
+
const d = new Date(Number(ts) || Date.now())
|
|
226
|
+
const y = d.getFullYear()
|
|
227
|
+
const m = String(d.getMonth() + 1).padStart(2, '0')
|
|
228
|
+
const day = String(d.getDate()).padStart(2, '0')
|
|
229
|
+
return y + '-' + m + '-' + day
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Write a compact Mio digest back into WorkBuddy's own memory file
|
|
233
|
+
// (<workspace>/.workbuddy/memory/YYYY-MM-DD.md). WorkBuddy instructs its model
|
|
234
|
+
// to maintain and read these files every session, so the digest is
|
|
235
|
+
// 'retrieved' next session without any Mio tool call by the model.
|
|
236
|
+
function writeWorkbuddyDigest(turn, outcome, summary) {
|
|
237
|
+
if (!turn.cwd || !summary) return
|
|
238
|
+
const dir = path.join(turn.cwd, '.workbuddy', 'memory')
|
|
239
|
+
const file = path.join(dir, localDateKey(turn.ts) + '.md')
|
|
240
|
+
try {
|
|
241
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
242
|
+
const time = new Date(Number(turn.ts) || Date.now()).toLocaleString('zh-CN', { hour12: false })
|
|
243
|
+
const entry =
|
|
244
|
+
'\n## Mio 自动摘要 (' + time + ')\n' +
|
|
245
|
+
'- outcome: ' + outcome + ' | tools: ' + turn.toolCount + ' | ' + summary.slice(0, 160) + '\n'
|
|
246
|
+
fs.appendFileSync(file, entry, 'utf8')
|
|
247
|
+
} catch (_) {}
|
|
248
|
+
}
|
|
249
|
+
|
|
221
250
|
function emitWorkbuddyOutcome(turn, sinks) {
|
|
222
251
|
const la = turn.lastAssistant
|
|
223
|
-
if (!la) return
|
|
252
|
+
if (!la) return false
|
|
224
253
|
let outcome
|
|
225
254
|
if (la.status === 'completed') outcome = 'success'
|
|
226
255
|
else if (la.error) outcome = 'failure'
|
|
@@ -256,6 +285,7 @@ function emitWorkbuddyOutcome(turn, sinks) {
|
|
|
256
285
|
project: turn.project,
|
|
257
286
|
source: 'workbuddy-observer',
|
|
258
287
|
})
|
|
288
|
+
writeWorkbuddyDigest(turn, outcome, summary)
|
|
259
289
|
}
|
|
260
290
|
|
|
261
291
|
for (const err of turn.errors) {
|
|
@@ -276,6 +306,7 @@ function emitWorkbuddyOutcome(turn, sinks) {
|
|
|
276
306
|
project: turn.project,
|
|
277
307
|
})
|
|
278
308
|
}
|
|
309
|
+
return true
|
|
279
310
|
}
|
|
280
311
|
|
|
281
312
|
// ---------------------------------------------------------------------------
|
|
@@ -338,8 +369,7 @@ function runCycle(home, state, sinks, opts) {
|
|
|
338
369
|
}
|
|
339
370
|
const result = feedWorkbuddyLine(turn, obj)
|
|
340
371
|
if (result.finalized) {
|
|
341
|
-
emitWorkbuddyOutcome(result.finalized, sinks)
|
|
342
|
-
out.outcomes += 1
|
|
372
|
+
if (emitWorkbuddyOutcome(result.finalized, sinks)) out.outcomes += 1
|
|
343
373
|
}
|
|
344
374
|
turn = result.next
|
|
345
375
|
}
|
|
@@ -347,8 +377,7 @@ function runCycle(home, state, sinks, opts) {
|
|
|
347
377
|
}
|
|
348
378
|
|
|
349
379
|
if (turn.lastAssistant && now - st.mtimeMs > IDLE_MS) {
|
|
350
|
-
emitWorkbuddyOutcome(turn, sinks)
|
|
351
|
-
out.outcomes += 1
|
|
380
|
+
if (emitWorkbuddyOutcome(turn, sinks)) out.outcomes += 1
|
|
352
381
|
state.openTurns[file] = null
|
|
353
382
|
} else if (turn.id) {
|
|
354
383
|
state.openTurns[file] = turn
|