pi-onlyne 1.1.2 → 1.2.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/README.md +66 -34
- package/README.zh.md +47 -26
- package/package.json +1 -1
- package/src/agent.mjs +175 -49
- package/src/agent.test.mjs +245 -31
- package/src/config.mjs +27 -5
- package/src/index.ts +35 -3
- package/src/protocol.mjs +41 -49
- package/src/protocol.test.mjs +58 -8
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ length-prefixed JSON codec, and the runtime has no npm dependencies.
|
|
|
9
9
|
|
|
10
10
|
Outside an onlyne session the extension is inert. The client injects `ONLYNE_ROLE`,
|
|
11
11
|
`ONLYNE_SESSION_ID` and `ONLYNE_TASK_ID` into every process it spawns
|
|
12
|
-
(`crates/onlyne-client/src/dispatch.rs`). With any of the three missing, this is an
|
|
12
|
+
(`crates/onlyne-client/src/session/dispatch.rs`). With any of the three missing, this is an
|
|
13
13
|
ordinary pi session: the plugin registers nothing and opens nothing.
|
|
14
14
|
|
|
15
15
|
```
|
|
@@ -25,8 +25,10 @@ hello{protocol:1, plugin:"pi-onlyne", kind:"agent", capabilities:[…], mount:{r
|
|
|
25
25
|
├─ task text (+ image path) ──► pi user message (deliverAs:"followUp")
|
|
26
26
|
├─ assign_ack{accepted:true}
|
|
27
27
|
├─ report.heartbeat{running|idle} — per turn, and every 10s while a task is live
|
|
28
|
+
├─ a turn that ends without `onlyne_complete` ──► the idle ladder: the same
|
|
29
|
+
│ message again (at most `idleReminders` times), then `failed` and out
|
|
28
30
|
├─ report.complete{outcome, head} — the ledger's terminal fact
|
|
29
|
-
│ └─ then one report.heartbeat{agent:"idle"}
|
|
31
|
+
│ └─ then one report.heartbeat{agent:"idle"} stating the agent only
|
|
30
32
|
│ └─ the client's answer is the handover: pi is asked to shut down, then detaches
|
|
31
33
|
├─ probe ──► one heartbeat
|
|
32
34
|
◀── recycle ──► complete (if unsettled) → stop → pi exits
|
|
@@ -102,9 +104,10 @@ pi --session-id <id> -e /abs/path/to/plugins/onlyne-agent-pi -ns -nc
|
|
|
102
104
|
| --- | --- | --- |
|
|
103
105
|
| `enabled` | `true` | `false` turns the extension off for this workspace |
|
|
104
106
|
| `watch.autoStart` | `true` | `false` registers the tools but opens no socket until `/onlyne connect` |
|
|
107
|
+
| `idleReminders` | `2` | how many times an idle turn end re-sends the assignment before the task fails (§4); 0 means the first idle without a completion fails it |
|
|
105
108
|
|
|
106
|
-
A missing file means
|
|
107
|
-
keeps
|
|
109
|
+
A missing file means every default. A malformed file prints one warning on stderr and
|
|
110
|
+
keeps the defaults: a typo must not silently disable a role. The client does not read
|
|
108
111
|
this file (§11 of the plan downgraded the old readiness gates to generate-time template
|
|
109
112
|
advice), so only this extension consumes it; the key shape stays the one the templates
|
|
110
113
|
carry.
|
|
@@ -154,7 +157,8 @@ png/jpeg/gif/webp file: the plugin reads it, base64-encodes it and attaches it a
|
|
|
154
157
|
|
|
155
158
|
### `onlyne_complete{outcome?, text?, force?, reason?}`
|
|
156
159
|
|
|
157
|
-
Ends the current task with an explicit outcome (`done` by default, or `failed`).
|
|
160
|
+
Ends the current task with an explicit outcome (`done` by default, or `failed`). It is the
|
|
161
|
+
only path to `done`: a turn that ends without it is reminded and then failed (§4). A
|
|
158
162
|
non-empty `text` becomes the ledger `head` verbatim: whitespace collapses to one line and
|
|
159
163
|
the text stops at 200 characters. An absent or blank `text` carries no summary, so the
|
|
160
164
|
completion falls back to the last assistant text. The call also ends the session's
|
|
@@ -163,28 +167,52 @@ pi to shut down through `ctx.shutdown()`. pi 0.85.1 has no tool-result `terminat
|
|
|
163
167
|
handling. When the workspace carries a relay policy (§5), `force: true` with a non-empty
|
|
164
168
|
`reason` is the deliberate way past a handoff the session still owes.
|
|
165
169
|
|
|
170
|
+
### `onlyne_handoff{to, text, image?}`
|
|
171
|
+
|
|
172
|
+
Hands this session's task on to the next hop of its family. The plugin sends one `handoff`
|
|
173
|
+
frame naming the task the session currently holds, and the host mints one child task for
|
|
174
|
+
`to` under it: the child names this task as its `parent_task`, sits one hop further along,
|
|
175
|
+
and carries the same family id, hop budget, origin, deadline and labels. The tool's result
|
|
176
|
+
names the child task id and its hop. A client refusal comes back as the tool's error,
|
|
177
|
+
verbatim. `image` is the same absolute png/jpeg/gif/webp path the send tool takes. An
|
|
178
|
+
assignment whose causality names a hop budget states the hop and the budget in its
|
|
179
|
+
injected header line. `onlyne_send{kind: "task"}` is the other way to reach a role: that
|
|
180
|
+
envelope starts a family of its own at hop 0.
|
|
181
|
+
|
|
166
182
|
## 4. Outcome rules
|
|
167
183
|
|
|
168
|
-
The plugin sends one completion per task,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
184
|
+
`onlyne_complete` is the only path to `done`. The plugin sends one completion per task,
|
|
185
|
+
at the first of these events:
|
|
186
|
+
|
|
187
|
+
1. **`onlyne_complete`** — the model gives an explicit outcome (`done` by default, or
|
|
188
|
+
`failed` / `cancelled`). A later completion for the same task is refused (not
|
|
189
|
+
re-reported). Its non-empty `text` is the head.
|
|
190
|
+
2. **An errored turn** — the turn ended with a provider error (`stopReason: "error"`).
|
|
191
|
+
That is proof on its own, so the plugin reports `failed` at once, with the error as
|
|
192
|
+
the head.
|
|
193
|
+
3. **The idle ladder** — the turn ended cleanly without a completion, and the task is
|
|
194
|
+
still open. The plugin re-sends the assignment and counts the rung. The idle that
|
|
195
|
+
finds the bound `idleReminders` names already spent reports `failed` — head
|
|
196
|
+
`no completion after <n> idle reminders` — and the session exits the way any
|
|
197
|
+
completion makes it exit.
|
|
198
|
+
4. **`recycle{outcome}`** — the host is tearing the session down. The plugin settles an
|
|
181
199
|
unsettled task with the host's outcome first, then stops and exits pi.
|
|
182
200
|
|
|
201
|
+
Two things settle nothing: a task that was assigned but whose turn has not run yet (the
|
|
202
|
+
injected message has not executed, so completing now would claim work that never
|
|
203
|
+
happened), and an idle the ladder still has a rung for. The ladder re-sends the
|
|
204
|
+
assignment the task arrived with — the same header, task text and attachment paths the
|
|
205
|
+
injection carried, under one line saying the previous turn ended without a completion —
|
|
206
|
+
and never the role prose, which is already in the session's context. Its images are not
|
|
207
|
+
re-attached: the paths travel as text, so the same bytes are not put into the context
|
|
208
|
+
twice. A new envelope for the same task restarts the count.
|
|
209
|
+
|
|
183
210
|
`head` is a single line, capped at 200 characters; it matches what the client puts in
|
|
184
211
|
`out_head` and what the receipt carries. Each task has one source for it: the `text` of
|
|
185
|
-
the explicit `onlyne_complete` call when that call carried one,
|
|
186
|
-
|
|
187
|
-
|
|
212
|
+
the explicit `onlyne_complete` call when that call carried one, the error a failed turn
|
|
213
|
+
reported, or the ladder's own line. The last assistant text is the fallback for an
|
|
214
|
+
`onlyne_complete` call that carried no text at all — a sentence spoken after such a call
|
|
215
|
+
cannot replace what the call handed over, and nothing else reads it.
|
|
188
216
|
|
|
189
217
|
A reported completion ends the session's process. `report.complete` goes out as a request,
|
|
190
218
|
and the client answers it only after it has settled the session row, acked the delivery
|
|
@@ -193,7 +221,7 @@ outcome the socket could not carry is queued and flushed after the next `hello`,
|
|
|
193
221
|
flush's answer is the handover that ends the process. A completion the host refused leaves
|
|
194
222
|
the process running, so an exit never loses the task.
|
|
195
223
|
|
|
196
|
-
The last report is one observation with `agent: "idle"
|
|
224
|
+
The last report is one observation with `agent: "idle"`, sent
|
|
197
225
|
after the completion is acknowledged and before the process leaves. The completion settles
|
|
198
226
|
the row from the tuple the client holds, and that tuple still reads `running` when the
|
|
199
227
|
finishing turn was the last heartbeat. Nothing observes the process afterwards, so without
|
|
@@ -253,7 +281,7 @@ file its turn.
|
|
|
253
281
|
| scope | this session's own sends, in process memory: a reconnect keeps them, a restarted session starts empty rather than guessing at what an earlier process sent |
|
|
254
282
|
| waiver | `force: true` with a non-empty `reason`; it only matters when the guard refuses |
|
|
255
283
|
| audit | a waived completion's ledger head starts with `relay-guard-forced: <reason>`, followed by the model's `text` when the call carried one |
|
|
256
|
-
| not guarded | the
|
|
284
|
+
| not guarded | outcomes the plugin reports without the model: an errored turn, the idle ladder's failure, and `recycle{outcome}` |
|
|
257
285
|
|
|
258
286
|
`relay.toml` is a closed subset of TOML: flat `key = value` lines, the two keys above,
|
|
259
287
|
one-line arrays of double-quoted strings, `#` comments. Anything outside that warns on
|
|
@@ -271,18 +299,22 @@ the shipped client.
|
|
|
271
299
|
- **Report sequence base.** The plugin's own `report` sequence starts at 1000, not 1. The
|
|
272
300
|
client stamps its own dispatch events (`created`, resource attach, `ready`) into the
|
|
273
301
|
same `(generation, seq)` watermark, and the reducer silently drops any report at or
|
|
274
|
-
below it (`crates/onlyne-session/src/reconcile
|
|
302
|
+
below it (`crates/onlyne-session/src/reconcile/`). A plugin sequence starting at 1
|
|
275
303
|
would lose its first observations. Everything else about the versioning is per spec.
|
|
276
|
-
- **`observed` is a full `Observation`.** `report.heartbeat` carries the
|
|
304
|
+
- **`observed` is a full `Observation`.** `report.heartbeat` carries the state
|
|
277
305
|
tuple (`version`, `generation_live`, `isolate_after`, `terminate_after`,
|
|
278
|
-
`mismatch_count`, `agent`, `delivery`, `resource`, `recovery
|
|
279
|
-
a `{"state": "running"}` shorthand: the host deserialises it
|
|
280
|
-
`is_legal`
|
|
281
|
-
`
|
|
282
|
-
|
|
283
|
-
|
|
306
|
+
`mismatch_count`, `agent`, `delivery`, `resource`, `recovery`), not
|
|
307
|
+
a `{"state": "running"}` shorthand: the host deserialises it, overwrites the six
|
|
308
|
+
keys the client owns, and applies only a tuple `is_legal` accepts. This plugin owns the `agent` dimension (turn hooks), the
|
|
309
|
+
`resource` claim — its process is live in the pane the attach was recorded on —
|
|
310
|
+
and the `host` binding. It has no witness for `delivery`, `recovery`,
|
|
311
|
+
`generation_live`, `isolate_after`, `terminate_after` or `mismatch_count`: the
|
|
312
|
+
client rewrites all six from its own intent queue, reducer history and role
|
|
313
|
+
config before the tuple is applied, so whatever this plugin sends there is
|
|
314
|
+
never read. Neither a
|
|
315
|
+
task outcome nor a public view travels in a tuple at all.
|
|
284
316
|
- **`ready` is reported once per connection.** The host's own hand-off path
|
|
285
|
-
(`crates/onlyne-client/src/dispatch.rs::hand_session`) already reports `ready` when the
|
|
317
|
+
(`crates/onlyne-client/src/session/dispatch/delivery.rs::hand_session`) already reports `ready` when the
|
|
286
318
|
client stages the session for a mounting plugin, so a second report from the plugin is a
|
|
287
319
|
no-op at the host. The plugin sends it anyway: a plugin that mounts *before* any work
|
|
288
320
|
exists is the case the ready barrier names, and it costs one frame.
|
|
@@ -354,12 +386,12 @@ path the client's daemon bound, read when the environment carried none, §8).
|
|
|
354
386
|
| `reconnecting in 4000ms` in a loop | the client is down or the socket was replaced | `onlyne --server-root … roles` |
|
|
355
387
|
| `ready refused: internal: unknown session for …` | the plugin mounted and reported for a task the client never staged (normal when pi is started by hand outside a task) | start pi under the client, not by hand |
|
|
356
388
|
| `assign` never arrives | the client's `session_command` did not spawn pi, or `inject` was dropped | the client log for the spawn line; `/onlyne status` for the capability set |
|
|
357
|
-
| ledger stays `in_flight` | no completion
|
|
389
|
+
| ledger stays `in_flight` | no completion yet: no turn has run (the injected message has not executed), or the ladder is still reminding it (`idleReminders`) | the pi session file for the `onlyne-assign` entry and the reminders injected after it; the `onlyne` panel for `reminder n of m`; `/onlyne status` for the task and phase |
|
|
358
390
|
| `onlyne_complete` answers `relay guard: missing handoff to: …` | the workspace's spec (or a `relay.toml` standing in for it) names a role this session never sent to | routine notices appear in the `onlyne` panel; stderr keeps refusals such as `relay guard from …`, socket errors, timeouts and framing faults; `required=…` names the policy; `relay guard: missing handoff …` names the delivered set |
|
|
359
391
|
| `hello … forbidden` / connection closed right after `hello` | the mount role does not match the client's role | `hello.args.mount.role` vs the workspace's role |
|
|
360
392
|
| `frame_too_large` | a body above 8 MiB | only reachable through an oversize outbound image; the ceiling is the core's |
|
|
361
393
|
| tools missing | `pi.registerTool` is absent in that pi version | `/onlyne status`; the capability table above |
|
|
362
|
-
| session reads `idle` again after `exited` | a heartbeat
|
|
394
|
+
| session reads `idle` again after `exited` | a turn-end heartbeat landed after the completion, moving the agent dimension back | the session log for the report order after `completion`; the plugin stops reporting for a completed task, and the client's own `delivery` survives either way |
|
|
363
395
|
| the supervisor board lists no tabs | no live session reported a pane: the adapter predates the report, or this pi is not inside an Orca pane | `onlyne --server-root … sessions --json` for `projection.observed.host.orca.pane_key`; `env \| grep ORCA_` inside the pane |
|
|
364
396
|
|
|
365
397
|
`/onlyne status` prints the live state (`connected`, `socket`, `role`, `sessionId`,
|
package/README.zh.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
`node:net` 上重写,四字节大端长度前缀加 UTF-8 JSON 的编解码是手写的,运行时零 npm 依赖。
|
|
7
7
|
|
|
8
8
|
扩展在 onlyne 之外完全静默。客户端 spawn 进程时会注入 `ONLYNE_ROLE`、`ONLYNE_SESSION_ID`、
|
|
9
|
-
`ONLYNE_TASK_ID`(`crates/onlyne-client/src/dispatch.rs`);三者缺一,就是普通 pi session,
|
|
9
|
+
`ONLYNE_TASK_ID`(`crates/onlyne-client/src/session/dispatch.rs`);三者缺一,就是普通 pi session,
|
|
10
10
|
插件不注册任何工具、不打开任何 socket。
|
|
11
11
|
|
|
12
12
|
```
|
|
@@ -22,6 +22,8 @@ hello{protocol:1, plugin:"pi-onlyne", kind:"agent", capabilities:[…], mount:{r
|
|
|
22
22
|
├─ 任务文本(含图片路径)──► pi user message(deliverAs:"followUp")
|
|
23
23
|
├─ assign_ack{accepted:true}
|
|
24
24
|
├─ report.heartbeat{running|idle} —— 每个 turn,以及任务存续期间每 10 秒
|
|
25
|
+
├─ turn 结束却没有 `onlyne_complete` ──► idle 阶梯:同一条消息再注入
|
|
26
|
+
│ (最多 `idleReminders` 次),之后 `failed` 并退出
|
|
25
27
|
├─ report.complete{outcome, head} —— ledger 的终态事实
|
|
26
28
|
│ └─ client 的应答就是交接点:插件据此让 pi 退出,随后 detach
|
|
27
29
|
├─ probe ──► 一条 heartbeat
|
|
@@ -95,8 +97,9 @@ pi --session-id <id> -e /abs/path/to/plugins/onlyne-agent-pi -ns -nc
|
|
|
95
97
|
| --- | --- | --- |
|
|
96
98
|
| `enabled` | `true` | `false` 时该工作区禁用扩展 |
|
|
97
99
|
| `watch.autoStart` | `true` | `false` 时注册工具但不建连接,需 `/onlyne connect` |
|
|
100
|
+
| `idleReminders` | `2` | turn 结束却空闲时重发任务的次数上限(§4);`0` 表示第一次空闲就判失败 |
|
|
98
101
|
|
|
99
|
-
|
|
102
|
+
文件缺失即取各自默认值。文件格式错误时打印一行警告,并保留默认值:一个笔误不该静默关掉一个
|
|
100
103
|
role。client 不读这个文件(计划 §11 已把旧 readiness 门降级为 generate 期模板提示),所以
|
|
101
104
|
只有本扩展消费它;键名沿用模板里既有的形状。
|
|
102
105
|
|
|
@@ -143,37 +146,53 @@ png/jpeg/gif/webp 的绝对路径:插件读出内容,base64 编码后挂成
|
|
|
143
146
|
|
|
144
147
|
### `onlyne_complete{outcome?, text?, force?, reason?}`
|
|
145
148
|
|
|
146
|
-
显式结束当前任务,`outcome` 缺省 `done`,也可 `failed
|
|
149
|
+
显式结束当前任务,`outcome` 缺省 `done`,也可 `failed`。它是通向 `done` 的唯一路径:turn 结束时
|
|
150
|
+
没有这次调用,任务会被重发提醒,之后判失败(§4)。`text` 非空时就是 ledger 的 `head`,
|
|
147
151
|
原样写出:空白折叠成单行,截到 200 字符。`text` 缺失或全空白时不带摘要,completion 退回
|
|
148
152
|
最后一段 assistant 文本。这一调用同时结束所在 session 的进程。client 应答完 completion
|
|
149
153
|
报告(见 §4)之后,插件通过 `ctx.shutdown()` 让 pi 退出。pi 0.85.1 没有 tool-result
|
|
150
154
|
`terminate` 处理。工作区带接力策略(§5)时,`force: true` 加非空 `reason` 是绕过一个仍欠着的
|
|
151
155
|
接力的正规通道。
|
|
152
156
|
|
|
157
|
+
### `onlyne_handoff{to, text, image?}`
|
|
158
|
+
|
|
159
|
+
把本会话手上的任务交给家族的下一跳。插件发一个 `handoff` 帧,帧里点名本会话当前持有的任务,
|
|
160
|
+
宿主据此为 `to` 铸一个该家族的子任务:子任务把本任务记为 `parent_task`,hop 加一,家族 id、
|
|
161
|
+
hop 预算、origin、deadline 与 labels 一并随行。工具结果给出子任务 id 与它的 hop。client 拒绝时
|
|
162
|
+
以工具错误原样抛出。`image` 与 send 工具同一含义:png/jpeg/gif/webp 图片的绝对路径。家族带
|
|
163
|
+
hop 预算时,注入的标题行写明 hop 与预算。`onlyne_send{kind: "task"}` 是触达 role 的另一条路:
|
|
164
|
+
那条 envelope 开一个新家族,hop 从 0 起。
|
|
165
|
+
|
|
153
166
|
## 4. outcome 判定规则
|
|
154
167
|
|
|
155
|
-
|
|
168
|
+
`onlyne_complete` 是通向 `done` 的唯一路径。插件每个任务只发一次 completion,取以下四者的先到者:
|
|
156
169
|
|
|
157
|
-
1. **`onlyne_complete`** —— 模型给显式 outcome
|
|
158
|
-
|
|
159
|
-
2.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
170
|
+
1. **`onlyne_complete`** —— 模型给显式 outcome(缺省 `done`,也可 `failed` / `cancelled`)。同一
|
|
171
|
+
任务的第二次 completion 被拒(不重报)。`text` 非空时即 head,原样写出。
|
|
172
|
+
2. **turn 出错** —— turn 以 provider 错误告终(`stopReason: "error"`)。这本身就是证据,插件立即
|
|
173
|
+
报 `failed`,错误信息当 head。
|
|
174
|
+
3. **idle 阶梯** —— turn 干净结束却没有 completion,任务还开着。插件重发任务并记一次;当空闲发现
|
|
175
|
+
`idleReminders` 给的次数已经用完,就报 `failed`(head 为 `no completion after <n> idle
|
|
176
|
+
reminders`),并像任何一次 completion 一样退出 session。
|
|
177
|
+
4. **`recycle{outcome}`** —— 宿主拆 session。插件先按宿主给的 outcome 结算未终态的任务,再停
|
|
164
178
|
插件并退出 pi。
|
|
165
179
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
180
|
+
两种情况都不结算:任务已投递但还没跑过任何 turn(注入的消息尚未执行,这时报终态就是撒谎),
|
|
181
|
+
以及阶梯还有余额的那次空闲。阶梯重发的是任务到手时的那条消息——同样的头部、任务文本和附件路径,
|
|
182
|
+
外加一行说明上一轮没有 completion——但不重发 role prose,它已经在上下文里;图片也不重挂,
|
|
183
|
+
路径以文本出现,同样的字节不会进上下文两次。同一任务收到新 envelope 时计数重来。
|
|
184
|
+
|
|
185
|
+
`head` 恒为单行、上限 200 字符,与 client 写入 `out_head` 和回执携带的内容一致。每个任务的 head
|
|
186
|
+
只有一个来源:显式 `onlyne_complete` 带的 `text`(有则原样采用)、出错 turn 报的错误,或阶梯自己
|
|
187
|
+
的那一行。最后一段 assistant 文本只是 `text` 完全缺失的 `onlyne_complete` 的退路——调用之后再说
|
|
188
|
+
的话顶不掉调用交出的内容,此外没有任何东西读它。
|
|
170
189
|
|
|
171
190
|
报出去的 completion 会结束所在 session 的进程。`report.complete` 以请求形式发出,client 只有
|
|
172
191
|
在结算 session 行、ack 掉投递、并写好 `Completion` envelope 之后才应答,插件就在这个应答处
|
|
173
192
|
让 pi 退出。socket 当时送不出去的 outcome 会被记住,并在下一次 `hello` 后补发,那次补发的
|
|
174
193
|
应答就是结束进程的交接点。被宿主拒掉的 completion 不会让进程退出,任务不会因为退出而丢失。
|
|
175
194
|
|
|
176
|
-
|
|
195
|
+
最后一条上报只说 agent 这一维:`agent: "idle"` 的观测,发在 completion 被
|
|
177
196
|
ack 之后、进程退出之前。completion 是按 client 手里的元组结算 session 行的,而收尾那一轮
|
|
178
197
|
就是最后一次 heartbeat 时,这个元组读到的仍是 `running`;此后没有任何东西再观测这个进程,
|
|
179
198
|
所以缺了这条上报,已退出的 session 会一直说 `running`。最后一次心跳本来就是 idle 时,插件
|
|
@@ -224,7 +243,7 @@ stderr 告警并忽略,把机会让回文件。
|
|
|
224
243
|
| 作用域 | 本会话自己的投递,仅进程内存:重连不丢,会话重启从空开始,不去猜上一个进程发过什么 |
|
|
225
244
|
| 豁免 | `force: true` 加非空 `reason`;只在守卫拒绝时才起作用 |
|
|
226
245
|
| 审计 | 被豁免的 completion,ledger head 以 `relay-guard-forced: <reason>` 开头;调用带了 `text` 时紧接其后 |
|
|
227
|
-
| 不管的路 |
|
|
246
|
+
| 不管的路 | 插件不经过模型就报出的终态:turn 出错、idle 阶梯用尽,以及 `recycle{outcome}` |
|
|
228
247
|
|
|
229
248
|
`relay.toml` 是 TOML 的封闭子集:扁平的 `key = value` 行、上面两个键、单行双引号字符串数组、
|
|
230
249
|
`#` 注释。子集之外一律 stderr 告警并忽略。它刻意不放 `.onlyne/config.toml`:client 以
|
|
@@ -238,17 +257,19 @@ stderr 告警并忽略,把机会让回文件。
|
|
|
238
257
|
|
|
239
258
|
- **report 序号基址。** 插件自己的 `report` 序号从 1000 起,不是 1。client 把自身的派发事件
|
|
240
259
|
(`created`、资源 attach、`ready`)写进同一个 `(generation, seq)` 水位,reducer 会静默丢弃
|
|
241
|
-
水位及以下的报告(`crates/onlyne-session/src/reconcile
|
|
260
|
+
水位及以下的报告(`crates/onlyne-session/src/reconcile/`),所以从 1 起会丢掉最初的观测。
|
|
242
261
|
其余版本语义与规范一致。
|
|
243
262
|
- **`observed` 是完整的 `Observation`。** `report.heartbeat` 携带整个合法状态元组
|
|
244
263
|
(`version`、`generation_live`、`isolate_after`、`terminate_after`、`mismatch_count`、
|
|
245
|
-
`agent`、`delivery`、`resource`、`recovery
|
|
246
|
-
`{"state": "running"}`
|
|
247
|
-
`agent
|
|
248
|
-
|
|
249
|
-
|
|
264
|
+
`agent`、`delivery`、`resource`、`recovery`),不是
|
|
265
|
+
`{"state": "running"}` 这种简写。宿主会反序列化它,先改写 client 主张的那六项,再套用 `is_legal`。本插件主张的
|
|
266
|
+
是 `agent`(turn hooks)、`resource`(进程还活在那块 pane 里,attach 早被宿主派发路径记过)
|
|
267
|
+
与 `host` 绑定这三件事。`delivery`、`recovery`、`generation_live`、`isolate_after`、
|
|
268
|
+
`terminate_after`、`mismatch_count` 它一个见证都没有:client 在归约前会用自己
|
|
269
|
+
的 intent 队列、reducer 历史与角色配置重写这六项,插件填什么都不会被读。任务的 outcome 与公开视图
|
|
270
|
+
更已经不在元组里,所以也就不再上报。
|
|
250
271
|
- **`ready` 每连接报一次。** 宿主的 hand-off 路径
|
|
251
|
-
(`crates/onlyne-client/src/dispatch.rs::hand_session`)在把 session 交给挂载的插件时已经报过
|
|
272
|
+
(`crates/onlyne-client/src/session/dispatch/delivery.rs::hand_session`)在把 session 交给挂载的插件时已经报过
|
|
252
273
|
`ready`,所以插件再报一次在宿主侧是 no-op。插件仍然发送:先挂载、后有活正是 ready barrier
|
|
253
274
|
描述的情形,而且只花一帧。
|
|
254
275
|
- **从不发 `cluster_ref`。** 本插件代表本地 role 说话,从不代表 aggregate;Rust 侧出于同样的
|
|
@@ -309,12 +330,12 @@ stderr 告警并忽略,把机会让回文件。
|
|
|
309
330
|
| 反复 `reconnecting in 4000ms` | client 已停或 socket 被替换 | `onlyne --server-root … roles` |
|
|
310
331
|
| `ready refused: internal: unknown session for …` | 插件为 client 从未暂存的任务报了 ready(手工起 pi 时的正常现象) | 让 client 拉起 pi,而不是手工起 |
|
|
311
332
|
| `assign` 一直不来 | client 的 `session_command` 没能拉起 pi,或 `inject` 被降级 | client 日志里的 spawn 行;`/onlyne status` 看能力集 |
|
|
312
|
-
| ledger 停在 `in_flight` |
|
|
333
|
+
| ledger 停在 `in_flight` | 还没有 completion:没跑过 turn(注入的消息尚未执行),或阶梯还在提醒(`idleReminders`) | pi session 文件里的 `onlyne-assign` 条目和其后注入的提醒;`onlyne` 面板里的 `reminder n of m`;`/onlyne status` 看任务与阶段 |
|
|
313
334
|
| `onlyne_complete` 回答 `relay guard: missing handoff to: …` | 工作区的 spec(或顶替它的 `relay.toml`)点名了一个本会话从未触达的 role | 日常通知显示在 `onlyne` 面板;stderr 保留 `relay guard from …` 等拒绝、socket 错误、超时与帧错误;`required=…` 说明策略;`relay guard: missing handoff …` 列出已投递集合 |
|
|
314
335
|
| `hello` 后立刻 `forbidden` / 断连 | mount role 与 client 的 role 不一致 | `hello.args.mount.role` 对该工作区的 role |
|
|
315
336
|
| `frame_too_large` | 正文超过 8 MiB | 只会由超限的出站图片触发;上限来自核心 |
|
|
316
337
|
| 工具缺失 | 该 pi 版本没有 `pi.registerTool` | `/onlyne status`;对照上面的能力表 |
|
|
317
|
-
| 会话在 `exited` 之后又回到 `idle` | completion
|
|
338
|
+
| 会话在 `exited` 之后又回到 `idle` | completion 之后又落进一条 turn-end heartbeat,把 agent 维搬了回去 | 看 session 日志里 `completion` 之后的 report 顺序;插件对已完成任务不再上报,而 client 自己的 `delivery` 两条都会保住 |
|
|
318
339
|
| supervisor 看板一个 tab 都不列 | 没有 live session 上报过 pane:适配器版本早于这条上报,或这个 pi 不在 Orca pane 里 | `onlyne --server-root … sessions --json` 看 `projection.observed.host.orca.pane_key`;在 pane 里跑 `env \| grep ORCA_` |
|
|
319
340
|
|
|
320
341
|
`/onlyne status` 打印实时状态(`connected`、`socket`、`role`、`sessionId`、`generation`、
|