@sema-agent/client-core 0.74.2 → 0.74.4
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/CHANGELOG.md +31 -0
- package/README.md +4 -1
- package/dist/adapt/arms.js +7 -1
- package/dist/adapt/panelTasks.d.ts +2 -1
- package/dist/adapt/panelTasks.js +8 -6
- package/dist/approvalsStreamLiveCapability.js +1 -1
- package/dist/decideReceipt.d.ts +15 -0
- package/dist/decideReceipt.js +21 -1
- package/dist/deviceExecutorManagementCapability.js +6 -2
- package/dist/engineAgentPanelStore.d.ts +60 -0
- package/dist/engineAgentPanelStore.js +363 -0
- package/dist/executionLaneCapability.js +1 -1
- package/dist/fleet/fleetProjection.d.ts +5 -0
- package/dist/fleet/fleetProjection.js +8 -0
- package/dist/fleetAgentPanelProjection.d.ts +2 -0
- package/dist/fleetAgentPanelProjection.js +7 -0
- package/dist/hitl/hitlBridge.d.ts +9 -0
- package/dist/hitl/hitlBridge.js +11 -2
- package/dist/hitl/suspendedReopen.d.ts +30 -0
- package/dist/hitl/suspendedReopen.js +27 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +6 -0
- package/dist/notifications.js +3 -2
- package/dist/runCancelContext.d.ts +49 -0
- package/dist/runCancelContext.js +91 -0
- package/dist/sqlEngineCapability.js +2 -2
- package/dist/webSearchBackendCapability.js +1 -1
- package/dist/writeProtectionCapability.js +2 -2
- package/docs/INTEGRATION-CLIENTS.md +107 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -49,6 +49,37 @@
|
|
|
49
49
|
> 挡住 ⇒ 本批把它机械化——④a0 对 `pending` 行**要求段头已是日期形**(`(未发布)` 直接红),阶段一
|
|
50
50
|
> commit 漏转在发布前就红,不再靠人记。
|
|
51
51
|
|
|
52
|
+
## 0.74.4(2026-09-19)
|
|
53
|
+
|
|
54
|
+
> 主题:**面板键「先发布的键为准」**(CC-83;test [7702] 证伪 0.74.3「面板桥零改」)+ **前代 UUID 的迟到 tick 丢弃**(CC-84;test [7702] W2 tick 腿)+ `readRunCancelContext` 下钻收紧。**patch**:型面 additive(`fleet-row` 臂 +`wireTaskId?` / `taskIdOrigin?`,`end` 臂 `taskIdOrigin` 多一词 `'wire'`,`EngineAgentPanelAbsence` +`wireTaskId?`;公面导出名零增减 1061;零 wire 新键;peer 不动)。接入面 §72。
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- **面板键「先发布的键为准」(CC-83)**:0.74.3 把 tick / end 归一到 fleet 尾段,但 tick 归不了一而按 UUID 原样发出的三种序(行帧前两条 tick / 缓一拍拍数耗尽 / 绑卡 tick 后来了行帧)里,随后的行帧仍按尾段发 ⇒ 按 `ev.taskId` 落表的宿主仍长成两行,UUID 那行永远收不到 end(test [7702] N-3,本包亲核复现)。现在漏斗记「已按 UUID 发布」的子代(transcriptId 与 parentToolCallId 两把钥匙),之后的 `fleet-row` 与 fleet 侧 `end`(投影 / 通知)改写成那把 UUID —— 原尾段进 `wireTaskId`,`taskIdOrigin:'wire'`;常态序(行帧先到)仍归一到尾段。**消费端在任何到达序上只见一把键**;要 fleet 尾段的宿主读 `wireTaskId` / `taskIdOrigin`。
|
|
59
|
+
- **前代 UUID 的迟到 tick 不进复活行(CC-84)**:`end` 有陈旧判据(0.74.0 / 0.74.3),tick 没有 ⇒ 复活(cycle 1 → 2)后前代 UUID 的迟到 tick 归一成当代尾段,把前代的累计数写进复活行(test 读数 toolUses 0 → 99,做对 end 判据的宿主也躲不开)。现在漏斗按钥匙周期对最近行帧周期判(与 `end` 同一只 `isStaleEngineAgentPanelEnd`),前代 tick 丢弃;判不出不丢。
|
|
60
|
+
- **异源对抗复审(0.74.4 R1)三条**:① 尾段 → UUID 的反向所有权此前不带周期 ⇒ 复活的新周期(新 transcriptId)也被改写到旧 UUID、其 tick 却归一到尾段(同一子代再度两键)⇒ 所有权带登记时周期,行帧 / end 更新的周期到了就退出所有权、新周期按常态登记;② 所有权表按容量 FIFO 会把仍活着的已发布键忘掉 ⇒ 改成按生命周期退役(收到 end 后先让位,活着的键不被顶;上限 8192);③ 缺席上报与通知车道清常驻标仍按尾段 ⇒ 侧信道与主通道两把键 ⇒ 缺席事件按所有者 UUID 发(原尾段进新键 `EngineAgentPanelAbsence.wireTaskId?`),通知车道改走 `clearEnginePanelTaskResidentByWire`(尾段的所有者一起清)。**R2 两条**:④ 新周期行帧退出旧所有权时旧 UUID 未退役 ⇒ 同一张卡的退路键把新周期行帧又拉回旧 UUID ⇒ 退出即退役;⑤ 已发布的 UUID 的后续 tick / end 仍会经退路键归一到尾段 ⇒ 前代迟到 tick 进新周期的行 ⇒ **一把已发布的键永远不改写**(它的 tick 留在它自己的键上、它的 end 落在它自己的行上)。**R3 两条**:⑥ 迟到 tick 把已退役的键重置成未退役 ⇒ 退路键又能拉回它 ⇒ 退役位不被后续 tick 重置;⑦ 通知车道按尾段清常驻标时不带周期 ⇒ 前代通知(seq 更旧)清掉当代 UUID 的常驻标 ⇒ `clearEnginePanelTaskResidentByWire(id, cycle?)` 带通知周期,比所有权旧的不清;陈旧 end 过境让消费端判、不退役当代所有权。
|
|
61
|
+
- 门 `run-engine-identity-test.mjs` E2–E5 重钉:引擎产物的 `instanceId` 铸点扫 `main.js` + `boot/*.js`、`/health` 体扫 `http/*.js`(server 7.88.x 把启动拆成 `dist/boot/stage-*.js`、`/health` 搬进 `http/dispatch.js`;铸点仍无条件、体仍带 `instanceId`;此前只扫 `main.js` / `http/server.js` ⇒ 消费端换钉实跑本门假红)。7.88.2 发布字节与 7.89.0 本机树两形实跑绿。
|
|
62
|
+
- **`readRunCancelContext` / `classifyRunAbortCause` 的 `result` 下钻**只认像 run 结果的对象(带 `terminal` / `status` / `cancelContext` 任一键);`TaskResult` 自己的 `result` 键哪天成了对象不再被误当成结果记录(test [7702] §七 ④)。
|
|
63
|
+
|
|
64
|
+
## 0.74.3(2026-09-19)
|
|
65
|
+
|
|
66
|
+
> 主题:**面板子代身份归一**(CC-70;cli L-407 / test 定谳:同一后台子代在 wire 上两把身份)+ **run 记录取消现场读口与成因闭集**(CC-71;cli L-212 (a))+ **workflow 列表行 run 级 errorCode**(CC-72;core 7.23.5 #932)+ **reopen 族第二臂两件**(CC-73;cli L-56)。**patch**:型面纯 additive(+2 常量 +7 函数,公面 1052 → 1061;测试钩 50 → 51;`EngineAgentPanelEvent` 三臂各加可选键;零 wire 新键;peer 不动)。接入面 §71。
|
|
67
|
+
|
|
68
|
+
### Added
|
|
69
|
+
|
|
70
|
+
- **面板事件的子代身份归一(CC-70)**:`publishEngineAgentPanelEvent` 成为三条 lane 的唯一漏斗 —— `fleet-row` 登记两把钥匙(`transcriptId` 主 / `parentToolCallId` 退路),`tick` / `end` 上的 `task_progress` UUID 查得到就改写成 fleet 行 id 尾段(= 注册表 task_id = 宿主本地 agentId),原词进新键 `wireTaskId`,`taskIdOrigin:'fleet-row'`;查不到:绑卡的 tick(新提示键 `cardBound`,同步委派形)直发 `taskIdOrigin:'wire'`,未绑的 tick **缓一拍**等行帧(同 UUID 的 fleet-row 到 ⇒ 先行帧再放 tick;第二条 tick ⇒ 新的原样发旧的丢;`end` ⇒ 先 tick 后 end;满 64 条 ⇒ 最旧的原样发;别的 fleet-row / end / sweep 过了 `MAX_HELD_WIRE_TICK_BEATS`(16)拍 ⇒ 原样发;`sweep` 本身不放 —— 没行可结)。钥匙带周期身份:归一后的 `end` 自己不带 `cycleSeq` / `startedAt` 时借登记它的那条 fleet-row 的,复活后迟到的 `end{旧 UUID}` 因此带旧代际、`isStaleEngineAgentPanelEnd` 判得出;经退路键归一过的 UUID 记进主表,随后不带 `parentToolCallId` 的 `end{UUID}` 同样归一(三条皆异源对抗复审 R1 所抓)。常驻标随归一从 UUID 迁到尾段(此前 UUID 记、17 位清 ⇒ 永不清)。新读口 `resolveEnginePanelTaskId(wireTaskId, parentToolCallId?)`(带钥匙上的周期身份);`fleet-row` 事件与 `FleetAgentRowLike` +`parentToolCallId?`;tick 臂 +`parentToolCallId?` / `cardBound?`。**宿主零改**:面板按 `ev.taskId` 落表即可,同一子代不再长成两行。
|
|
71
|
+
- **`src/runCancelContext.ts`(CC-71;server ≥7.87.3 `PersistedTaskResult.cancelContext` 四座,sdk 无型 ⇒ 结构读)**:`readRunCancelContext(record)` —— 缺席 ⇒ `not_reported`(**不折成「未取消」**);四座各自可缺席各自防御读(有 age 无 kind 合法;`elapsedMs` 绝不折 0;`lastBrainStatus` 原样带出);坏形 ⇒ `undefined`。`classifyRunAbortCause(record)` 成因闭集四词:`cancelled`(码 `cancelled`,带现场)/ `engine_error`(别的码,原码开集不枚举)/ `run_still_live`(记录仍非终态 —— 断连是客户端侧事实,不是 run 的成因)/ `unknown`(不猜)。常量 `RUN_CANCELLED_CODE`。
|
|
72
|
+
- **`FleetWorkflowView.errorCode?`(CC-72)**:workflow 列表行的 run 级 errorCode 原词逐字过境(开集);缺席 = 无码,**不是成功也不是失败**;sdk 未声明前按结构读(空串 / 非串 ⇒ 缺席)。
|
|
73
|
+
- **`lastFlagValue` 上公面(CC-80;归层)**:argv 末次旗值扫描原语(`--name v` / `--name=v`、裸 `--` 之后不扫、重复旗末次胜、缺值策略 `invalidate` / `latch-previous` 调用方选)—— 包内 limits / sandbox / scenario / headlessPermissionMode 四处早已收编,端侧仍在手写同一循环;现在从 `index` 导出,`FlagValue` / `OnMissingFlagValue` 型同出。
|
|
74
|
+
- **reopen 族第二臂(CC-73)**:① `DecideReceiptView` +`errorCode?` / `retriable?`(`/decide` 200 **终局形**才有;在场逐字,`retriable` 严格布尔)+ `decideReceiptReopen(receipt)` —— 与 `resumeReopenFromError` **同一只**判官(`status === 'failed'` ∧ 码 ∈ `RESUME_REOPEN_CODES` ∧ `retriable === true`,缺一件 ⇒ null);② `suspendedReopenOf(ev)` 读 durable `suspended.reopened` 三态(`reopened{code}` / `not_reopened`(显式 null)/ `unstated`(键缺席 —— 不冒充 null));`HitlBridge.currentGateReopen()` 把它挂在挂起座上,每条 `suspended` 重读、随座清。
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
|
|
78
|
+
- **异源对抗复审两轮抓的六条(CC-70 / CC-71)**:① 经退路键归一的 tick 之后 `end{UUID}` 归不了一(行永不结、报告丢)⇒ 归一时把 UUID → 尾段记进主表;② 复活后迟到的 `end{旧 UUID}` 不带周期 ⇒ 钥匙带周期身份、`end` 借用;③ 缓着的 tick 无时限 ⇒ 节拍上限 `MAX_HELD_WIRE_TICK_BEATS`(别的 tick 不算拍);④ 钥匙表 FIFO 会把仍在飞的子代挤掉 ⇒ LRU 续命、上限 2048;⑤ 第 16 拍恰是自己的行帧(退路键)时先记拍会让它过期 ⇒ 行帧先登记先放再记拍;⑥ 终态 tick / 通知的清标只清 UUID ⇒ 两把键都清;⑦(R3)前代 UUID 的终态清掉复活行的常驻标 ⇒ 下一次 sweep 假结它 ⇒ 清标走 `clearEnginePanelTaskResidentByWire`:归一键只在终态属当代时才清(判据与 `end` 陈旧判据同一只)。`classifyRunAbortCause`:result 已终态而外层 `status` 仍在飞词 = 证据矛盾 ⇒ `unknown`(带终态词),不说「还活着」。
|
|
79
|
+
- **0.74.2 补审(六只能力位读器)**:`projectDeviceExecutorManagementCapability` 外层 `deviceExecutor` 此前 undefined 判与取值各读一次 ⇒ 变化的 getter 把第一次观测到的「车道缺席」丢掉,现在只读一次;六只 doctor 的 `unobserved` 句此前说「还没收到 caps 回体」—— 坏形删格后也这么说 = 把解析失败说成没启动,改成「本进程没有可用的读数(没收到,或最后一次读不懂)」(措辞变化,消费方不接线零影响)。
|
|
80
|
+
- 面板常驻标「UUID 记、17 位清」永不清(CC-70 同根):tick 臂现在把常驻标记在归一后的键下(`noteResidency(taskId, residentKey)`),通知车道按尾段清得掉。
|
|
81
|
+
- `classifyRunAbortCause` 对平面 `running` / `queued` 记录:平面读器对在飞词答 `unknown{word}`,那不是「读不出」而是「还没结」⇒ 先看 `status` 再落 `unknown`。
|
|
82
|
+
|
|
52
83
|
## 0.74.2(2026-09-19)
|
|
53
84
|
|
|
54
85
|
> 主题:**`capabilities.deviceExecutor.management` 四态读口**(CC-69;server 7.88.0 点名本包)。**patch**:型面纯 additive(+6 函数,公面 1046 → 1052;测试钩 49 → 50;零 wire 新键;peer 不动)。
|
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Renamed from **`@sema-agent/wire-cc-adapter`** (0.1.x, deprecated — see *Migra
|
|
|
35
35
|
|
|
36
36
|
## Scope
|
|
37
37
|
|
|
38
|
-
**Version:** 0.74.
|
|
38
|
+
**Version:** 0.74.4
|
|
39
39
|
|
|
40
40
|
- **Today** — the adapter seam, the whole `adapt()` pipeline (all 14 A-layer arms plus the
|
|
41
41
|
B/D/E tool-card layers), the notification/caps/model families, the adapter kernel (stream driver
|
|
@@ -314,6 +314,9 @@ public-surface guard checks that last one).
|
|
|
314
314
|
| `scripts/run-mcp-panel-projection-test.mjs` | The `GET /v1/sessions/:id/mcp` panel reader (`projectMcpPanel`; server >=7.77.0 adds the optional `lastLegMcp` key) and the single wording mint for its "last leg" line. Absence of `lastLegMcp` is one literal sentence that never blames the engine version (a new session, a leg outside the retention window, a leg without a manifest and an older engine all look the same on the wire); a key that is present but unreadable is a different sentence plus a `lastLegMcpUnreadable: true` mark, never folded into absence. The `mcp[]` roster goes through the same reader as the live `wiring_manifest` third section, so a replayed roster and a live one have one shape. The two faces of the panel (`servers[]` and the last-leg roster) may legitimately differ, so the view carries no agreement flag and none of the five sentences mentions `servers`. Required keys are pinned to the SDK `openapi.yaml` component bytes **0.69.0:** `fetchMcpPanel` fetches the panel through the SDK client's own `sessions.mcp` call (same transport and auth as every other read) and projects it; transport failure, an unreadable body and an empty session id all come back as `undefined`, never as a fabricated empty panel 0.71.0 adds section K: `mcpEngineLegPresence(view)` — the engine-side MCP presence tri-state read only off the panel view (`unknown` when the view could not be read, never rendered as "no MCP configured") |
|
|
315
315
|
| `scripts/run-absence-fold-census-test.mjs` | A package-wide census of the "absence folded into a positive outcome" defect shape, so that fixing the six sites this release does not merely move the shape somewhere else. The defect is defined by position, not syntax: a fallback position (the unconditional tail return, the `default:` arm, the literal minted when there is nothing to pass on, the value returned from an error path) may only say `unknown` or stay absent, never a positive word. Detection walks the syntax tree of every source file, so comments, strings and multi-line spellings cannot hide or fake a hit, and covers five forms: the right arm of `??` / `||`, the else arm of a ternary, the first return of an explicit `default:`, a `catch` block or `.catch(() => …)` arrow returning a healthy value, and a function whose last statement returns a positive word after other returns. Every remaining hit must be registered with a written reason, an unregistered hit fails the gate naming the file and line, the registered count must equal the real count so a cleared site cannot leave a spare allowance behind, and the gate proves its own teeth behind a fence (a failed self-proof refuses to report any count): each form injected into an in-memory copy must add exactly one hit, two correct spellings are pinned as non-hits, and samples inside comments or strings do not count. It also pins the headline site: the fleet panel projection no longer mints an `end` with `isError: false` on absence |
|
|
316
316
|
| `scripts/run-device-executor-management-capability-test.mjs` | The engine's device-management self-description (`capabilities.deviceExecutor.management`, engine ≥7.88.0), read the same four-state way as its five sibling capability readers: an absent `management` key is reported as not reported (never folded into `false`; an older engine really ships the lane object without it), an absent `deviceExecutor` key is likewise not reported, `deviceExecutor: false` is the lane being absent, presence is judged by own-property not truthiness, the value must be a strict boolean, the tee never throws and drops stale generations, and the package owns the verdict on whether the `/v1/devices` management verbs are usable (`yes` only when present and true, `no` when present-false or lane-absent, otherwise `unknown`) |
|
|
317
|
+
| `scripts/run-run-cancel-context-test.mjs` | The run record's `cancelContext` side-note (engine ≥7.87.3) read structurally, and the cause of a `turn_aborted{engine_error}` classified from machine-readable evidence only: `cancelled` (code `cancelled`, with the cancel-time context when present) / `engine_error` (any other failure code, passed through verbatim) / `run_still_live` (the record is not terminal — a dropped stream is a client-side fact, not the run's cause) / `unknown` (never guessed). An absent `cancelContext` reads as *not reported*, never as "not cancelled"; `elapsedMs` is never folded to 0. |
|
|
318
|
+
| `scripts/run-suspended-reopen-projection-test.mjs` | The durable `suspended` event's `reopened` key read as three distinct states — `reopened` (with the engine's code, verbatim), `not_reopened` (an explicit `null`), `unstated` (key absent or unreadable) — and carried on the HITL bridge's active gate (`currentGateReopen()`), re-read on every `suspended` and cleared with the gate. |
|
|
319
|
+
| `scripts/run-panel-identity-normalization-test.mjs` | One background subagent has two ids on the wire — the fleet row id tail and the `task_progress` task id (its transcript id). Every panel event goes through one funnel that rewrites the `tick` / `end` task id onto the fleet row's id once a `fleet-row` has registered the key (`transcriptId` first, `parentToolCallId` as the fallback), carrying the original as `wireTaskId` and marking `taskIdOrigin`; an unbound tick whose row has not arrived yet waits one beat (bounded) and is released verbatim on the next tick / `end`, when the buffer is full, or after `MAX_HELD_WIRE_TICK_BEATS` other fleet-row / end / sweep events (a `sweep` itself leaves it alone: there is no row to settle yet); a normalized `end` that carries no cycle identity borrows the registering row's, so a late close of a revived task is recognized as stale; the key table is an LRU (a task that keeps ticking is never evicted by newer registrations); the residency mark migrates with the id and both keys are cleared on settle — except that a stale (previous-cycle) terminal never clears the revived row's mark — so the notification lane can clear it. Once a tick has been delivered verbatim under its UUID, that UUID is the subagent's key: later fleet rows and fleet-side ends are rewritten onto it (the tail kept in `wireTaskId`), so a consumer sees one row in every arrival order; a late tick from a previous cycle is dropped rather than folded into the revived row. |
|
|
317
320
|
| `scripts/run-approval-card-retract-test.mjs` | The approval card's **decision-free retraction** and the in-stream frame leg's **outcome hand-back**: a host that must withdraw a card that no longer has a decision channel (session switch, engine switch, a tracker reporting the ask gone) answers `{ kind: 'retracted' }` and the package sends nothing on any of the three legs (in-stream frame, suspended ask, durable park), reporting `decision: 'unresolved'` with a `retracted` flag; `aborted` / `failed` / `deny` keep their meaning (a real deny is still posted), and `onToolApprovalOutcome` hands every in-stream outcome back to the host exactly once, tolerating a throwing or rejecting callback |
|
|
318
321
|
| `scripts/run-panel-cycle-identity-test.mjs` | The **cycle identity** on agent-panel events and the fleet ledger's **departure read-out**: a background agent may be revived under the same id, so `fleet-row` and `end` events now carry the wire's own `cycleSeq` / `startedAt` when present (absent means the row has no notion of generations, never "generation one"), `isStaleEngineAgentPanelEnd` is the single rule for ignoring a late `end` from a previous cycle (only when both sides carry a comparable identity; absence never drops a real terminal), a changed `cycleSeq` is a new cycle for usage stickiness and buffer coalescing, the notification lane carries `cycleSeq` only when the wire really sent `seq`, and `task_remove` frames reach the host through `onTaskRemoved` with `removeReason` / `cycleSeq` verbatim, a stale previous-generation removal leaving the newer row in place |
|
|
319
322
|
| `scripts/run-workflow-size-warning-test.mjs` | The **workflow size warning** verdict shared by every host footer / panel: a three-state result (`warn` / `ok` / `unknown`) read off the optional fleet view keys, where an unknown size is never reported as a normal one (absent `totalCount` / `tokens` without positive over-cap evidence is `unknown`, naming the missing keys), positive evidence on either axis wins regardless of absent keys, the per-agent denominator uses the engine's started count only when it is not below done+failed (a smaller value is a stale reading), otherwise falls back to the done+failed lower bound only when both keys are present — and a lower-bound denominator only yields an upper bound of the projection, which can prove *within cap* (`ok`, flagged) but never *over cap* (`unknown`, with the upper bound exposed) — and the prior is used only when the engine itself reports zero started agents; cap precedence env > explicit guideline > default, prototype keys never act as a guideline, the env reader is pure and does not fall through to the second name on a bad first value; caps, guideline table, env names and the three copy variants are single-sourced |
|
package/dist/adapt/arms.js
CHANGED
|
@@ -10,6 +10,7 @@ import { readAsyncLaunchedAgentReceipt } from '../toolResult.js';
|
|
|
10
10
|
import { isWorkflowAgentTaskId, recordWorkflowAgentTaskId } from '../workflow.js';
|
|
11
11
|
// L-215+(0.65.0;core [6908]):终局状态词的单铸谓词(三处同形一次改齐)。
|
|
12
12
|
import { isTerminalStatus } from '../runTerminal.js';
|
|
13
|
+
import { resolveEnginePanelTaskId } from '../engineAgentPanelStore.js';
|
|
13
14
|
import { chrome, MAIN, transcript, messageIdentityOf } from './ids.js';
|
|
14
15
|
import { CANCEL_MESSAGE, decisionOf, flattenWireOutput, REJECT_MESSAGE, sanitizeToolUseBlock, shortTaskLabel, TASK_TOOL_NAMES, WORKFLOW_TOOL_NAMES, } from './wireShapes.js';
|
|
15
16
|
// ══ M2/M1 —— assistant(tool_use 半场 + durable 文本/思考半场)══════════════════════════════════
|
|
@@ -831,7 +832,9 @@ const taskProgressArm = function* (m, { ctx, cards, panel, inst }) {
|
|
|
831
832
|
yield* panel.start(taskId, agentType);
|
|
832
833
|
}
|
|
833
834
|
// #6 常驻台账的两个写口整臂让位给 M3(module store 不许两个模块各写各的)。
|
|
834
|
-
|
|
835
|
+
// 0.74.3 CC-70:常驻标记在**归一后的键**下(fleet 尾段 = 通知车道清标用的那把);此前 UUID 记、17 位清 ⇒ 永不清。
|
|
836
|
+
const identity = resolveEnginePanelTaskId(taskId, explicitParent);
|
|
837
|
+
panel.noteResidency(taskId, identity.taskId);
|
|
835
838
|
panel.markLive(taskId);
|
|
836
839
|
yield chrome({
|
|
837
840
|
kind: 'panel_task',
|
|
@@ -843,6 +846,9 @@ const taskProgressArm = function* (m, { ctx, cards, panel, inst }) {
|
|
|
843
846
|
...(description !== undefined ? { description } : {}),
|
|
844
847
|
...(prompt !== undefined ? { prompt } : {}),
|
|
845
848
|
...(currentAction !== undefined ? { currentAction } : {}),
|
|
849
|
+
// 0.74.3 CC-70:身份归一的两把钥匙 —— 退路键 + 发布方提示(绑卡 = 同步委派,漏斗不用等 fleet 行)。
|
|
850
|
+
...(explicitParent !== undefined ? { parentToolCallId: explicitParent } : {}),
|
|
851
|
+
...(panel.isLiveBound(taskId) ? { cardBound: true } : {}),
|
|
846
852
|
// 🔴 这里的 `: 0` 与 `fleetAgentPanelProjection` 那句「绝不在本层 `?? 0`」**不矛盾**,
|
|
847
853
|
// 因为不是同一条 lane 的同一种供给形(两层的分层实情写在 `engineAgentPanelStore.ts`
|
|
848
854
|
// 的 `PANEL_TOOLUSES_LANE_POLICY` 上,两条 lane 的可选性由编译钉锁住):
|
|
@@ -59,7 +59,8 @@ export interface PanelTaskLedger {
|
|
|
59
59
|
/** 登记「本 turn 发布过面板行」。 */
|
|
60
60
|
markLive(taskId: string): void;
|
|
61
61
|
/** #6 常驻台账:live-bound ⇒ 清常驻;inert/unbound ⇒ 记常驻(module store 的**唯一**写口之一)。 */
|
|
62
|
-
|
|
62
|
+
/** 0.74.3 CC-70:`residentKey` = 常驻标该记在哪把键下(归一后的 fleet 尾段);缺省 = taskId。绑卡判定仍按 tick 的 taskId。 */
|
|
63
|
+
noteResidency(taskId: string, residentKey?: string): void;
|
|
63
64
|
/** SubagentStart(类型恒记,fire 一生一次)。 */
|
|
64
65
|
start(taskId: string, agentType: string): Generator<AdapterOutput>;
|
|
65
66
|
/** 关卡 settle(cardId)或 turn 末/abort 防御 sweep(cardId===null)。 */
|
package/dist/adapt/panelTasks.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { clearEnginePanelTaskResident, markEnginePanelTaskResident } from '../engineAgentPanelStore.js';
|
|
1
|
+
import { clearEnginePanelTaskResident, clearEnginePanelTaskResidentByWire, markEnginePanelTaskResident } from '../engineAgentPanelStore.js';
|
|
2
2
|
import { chrome, MAIN } from './ids.js';
|
|
3
3
|
import { SUBAGENT_TOOL_NAMES } from './wireShapes.js';
|
|
4
4
|
export function createPanelTaskLedger(ctx, cards, inst) {
|
|
@@ -16,6 +16,8 @@ export function createPanelTaskLedger(ctx, cards, inst) {
|
|
|
16
16
|
* 与 unbound 行是 **session 常驻**:终态只认 task_notification / bg_notification。
|
|
17
17
|
*/
|
|
18
18
|
const liveBoundPanelTasks = new Set();
|
|
19
|
+
/** 0.74.3 CC-70(异源复审 R2 [medium] / R3 [high]):常驻标可能记在归一键(fleet 尾段)下 —— 两把键都清,但前代终态不清当代的尾段键(判据在 store,单源)。 */
|
|
20
|
+
const clearResidencyBothKeys = (taskId) => clearEnginePanelTaskResidentByWire(taskId);
|
|
19
21
|
/** taskId → 子代类型(Stop hook 查它;cli 同为 turn 局部)。 */
|
|
20
22
|
const subagentTypeById = new Map();
|
|
21
23
|
const laneOf = (taskId) => {
|
|
@@ -98,11 +100,11 @@ export function createPanelTaskLedger(ctx, cards, inst) {
|
|
|
98
100
|
},
|
|
99
101
|
// #6 session 常驻台账:live-bound(卡本 turn 开着)= 本 turn 生命周期,turn 末可 sweep;
|
|
100
102
|
// inert/unbound(跨 turn bg 子代形)= 常驻,两处 sweep 都放行,终态只认通知帧。
|
|
101
|
-
noteResidency: (taskId) => {
|
|
103
|
+
noteResidency: (taskId, residentKey = taskId) => {
|
|
102
104
|
if (liveBoundPanelTasks.has(taskId))
|
|
103
|
-
clearEnginePanelTaskResident(
|
|
105
|
+
clearEnginePanelTaskResident(residentKey);
|
|
104
106
|
else
|
|
105
|
-
markEnginePanelTaskResident(
|
|
107
|
+
markEnginePanelTaskResident(residentKey);
|
|
106
108
|
},
|
|
107
109
|
/** SubagentStart(cli fireSubagentStartHook 的臂化):类型恒记,fire 一生一次。 */
|
|
108
110
|
*start(taskId, agentType) {
|
|
@@ -173,7 +175,7 @@ export function createPanelTaskLedger(ctx, cards, inst) {
|
|
|
173
175
|
*settleFromNotification(taskId, isError) {
|
|
174
176
|
// #6 通知-settle 边(推送半场):session 常驻的 bg 子代行不再被 turn sweep 假结,
|
|
175
177
|
// 真终态在此落行(failed/killed ⇒ isError 真归因)。
|
|
176
|
-
|
|
178
|
+
clearResidencyBothKeys(taskId);
|
|
177
179
|
if (endedPanelTasks.has(taskId))
|
|
178
180
|
return;
|
|
179
181
|
endedPanelTasks.add(taskId);
|
|
@@ -209,7 +211,7 @@ export function createPanelTaskLedger(ctx, cards, inst) {
|
|
|
209
211
|
if (endedPanelTasks.has(taskId))
|
|
210
212
|
return;
|
|
211
213
|
endedPanelTasks.add(taskId);
|
|
212
|
-
|
|
214
|
+
clearResidencyBothKeys(taskId);
|
|
213
215
|
const lane = laneOf(taskId);
|
|
214
216
|
const card = taskCardBinding.get(taskId);
|
|
215
217
|
if (card !== undefined) {
|
|
@@ -74,7 +74,7 @@ export function livePendingNeedsReconcile(reading) {
|
|
|
74
74
|
export function approvalsStreamLiveDoctorDetail(reading) {
|
|
75
75
|
switch (reading.kind) {
|
|
76
76
|
case 'unobserved':
|
|
77
|
-
return 'live approval push not observed — the engine reports it on /v1/capabilities; this process has
|
|
77
|
+
return 'live approval push not observed — the engine reports it on /v1/capabilities; this process has no usable capabilities reading cached for it (none received, or the last one was unreadable), so suspended asks are pulled by the reconcile cadence';
|
|
78
78
|
case 'not_reported':
|
|
79
79
|
return 'live approval push not reported by this engine — only newer engines advertise it; this does not say whether it pushes, so suspended asks are pulled by the reconcile cadence';
|
|
80
80
|
case 'present':
|
package/dist/decideReceipt.d.ts
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
* 消费端渲卡时:**在场**才说「执行时被放行/被否」,缺席一个字都不说。
|
|
33
33
|
*/
|
|
34
34
|
import { type GateOutcomeView } from './gateOutcome.js';
|
|
35
|
+
import { type ResumeReopenDetail } from './resumeRefusalCopy.js';
|
|
35
36
|
/** 一次 `/decide` 200 的读数。**每一位都可能缺席**(四种形没有共同保证的键)。 */
|
|
36
37
|
export interface DecideReceiptView {
|
|
37
38
|
/** 回体上的 `status`(开集透传;受理形与 parked 回执上是 `"resuming"`,回放形上**没有**这一位)。 */
|
|
@@ -60,6 +61,13 @@ export interface DecideReceiptView {
|
|
|
60
61
|
* 🔴 **缺席 = 未知,禁读成 allowed**(见模块顶注的两处上游逐字)。
|
|
61
62
|
*/
|
|
62
63
|
executionOutcome?: GateOutcomeView;
|
|
64
|
+
/**
|
|
65
|
+
* 0.74.3 CC-73:**终局形**才有的两位(契约:`verify:true` / 无 run store 的同步长调用在 200 体上回
|
|
66
|
+
* `{ status:"failed", errorCode, retriable:true }`;受理形刻意不带)。在场逐字,缺席不落键;`retriable` 严格布尔,
|
|
67
|
+
* `false` 是 server 说的照带。判「这是不是 reopen 类失败」走 {@link decideReceiptReopen},别在端上凭码猜。
|
|
68
|
+
*/
|
|
69
|
+
errorCode?: string;
|
|
70
|
+
retriable?: boolean;
|
|
63
71
|
}
|
|
64
72
|
/**
|
|
65
73
|
* `/decide` 的 200 体 → {@link DecideReceiptView};不是一只对象 / 一位都读不出 ⇒ `undefined`,
|
|
@@ -70,6 +78,13 @@ export interface DecideReceiptView {
|
|
|
70
78
|
* 🔴 **不补默认、不折缺席**:读不出 `status` 不等于终局,读不出 `executionOutcome` 不等于 allowed。
|
|
71
79
|
*/
|
|
72
80
|
export declare function readDecideReceipt(result: unknown): DecideReceiptView | undefined;
|
|
81
|
+
/**
|
|
82
|
+
* 0.74.3 CC-73(cli L-56 ①):`/decide` 200 **终局形**是不是 reopen 类失败 —— 三件合取,与 {@link resumeReopenFromError}
|
|
83
|
+
* **同一只**判官(码 ∈ `RESUME_REOPEN_CODES` ∧ `retriable === true`),外加终局形自己的 `status === 'failed'`。
|
|
84
|
+
* 任一件缺 ⇒ `null`:受理形带同码不算(它不是终局)、`retriable` 缺席 / false 不算(凭码不说「卡仍 pending」)。
|
|
85
|
+
* 读数在场 ⇒ 卡仍 pending、决定没被消费、可再决;文案走 `resumeReopenContent(detail)`。
|
|
86
|
+
*/
|
|
87
|
+
export declare function decideReceiptReopen(receipt: DecideReceiptView | undefined): ResumeReopenDetail | null;
|
|
73
88
|
/**
|
|
74
89
|
* 🔴 **这次 decide 的 200 有没有把门解决掉** —— 唯一的诚实答案是「**说不出来**」。
|
|
75
90
|
*
|
package/dist/decideReceipt.js
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
* 消费端渲卡时:**在场**才说「执行时被放行/被否」,缺席一个字都不说。
|
|
33
33
|
*/
|
|
34
34
|
import { gateOutcomeOf } from './gateOutcome.js';
|
|
35
|
+
import { resumeReopenFromError } from './resumeRefusalCopy.js';
|
|
35
36
|
import { DECIDE_WORKFLOW_HOST_UNKNOWN, DECIDE_WORKFLOW_LANE_CODES, DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED, } from './engineErrorCodes.js';
|
|
36
37
|
/** 受理形/parked 回执上那个**唯一**表示「受理了、还没做完」的状态词(sdk README §9.0.0 逐字)。 */
|
|
37
38
|
const STATUS_RESUMING = 'resuming';
|
|
@@ -63,11 +64,15 @@ export function readDecideReceipt(result) {
|
|
|
63
64
|
const executionOutcome = Object.hasOwn(r, 'executionOutcome')
|
|
64
65
|
? gateOutcomeOf({ gate: r.executionOutcome })
|
|
65
66
|
: undefined;
|
|
67
|
+
const errorCode = Object.hasOwn(r, 'errorCode') ? str(r.errorCode) : undefined;
|
|
68
|
+
const retriable = Object.hasOwn(r, 'retriable') && typeof r.retriable === 'boolean' ? r.retriable : undefined;
|
|
66
69
|
if (status === undefined &&
|
|
67
70
|
taskId === undefined &&
|
|
68
71
|
sessionId === undefined &&
|
|
69
72
|
idempotent === undefined &&
|
|
70
|
-
executionOutcome === undefined
|
|
73
|
+
executionOutcome === undefined &&
|
|
74
|
+
errorCode === undefined &&
|
|
75
|
+
retriable === undefined) {
|
|
71
76
|
return undefined;
|
|
72
77
|
}
|
|
73
78
|
return {
|
|
@@ -77,8 +82,23 @@ export function readDecideReceipt(result) {
|
|
|
77
82
|
...(idempotent !== undefined ? { idempotent } : {}),
|
|
78
83
|
...(handoffTaskId !== undefined ? { handoffTaskId } : {}),
|
|
79
84
|
...(executionOutcome !== undefined ? { executionOutcome } : {}),
|
|
85
|
+
...(errorCode !== undefined ? { errorCode } : {}),
|
|
86
|
+
...(retriable !== undefined ? { retriable } : {}),
|
|
80
87
|
};
|
|
81
88
|
}
|
|
89
|
+
/** 200 终局形上「这是不是 reopen 类失败」的判据词(契约:reopen 类失败仍 `suspended` 且锁在手,200 体 `status:"failed"`)。 */
|
|
90
|
+
const STATUS_FAILED = 'failed';
|
|
91
|
+
/**
|
|
92
|
+
* 0.74.3 CC-73(cli L-56 ①):`/decide` 200 **终局形**是不是 reopen 类失败 —— 三件合取,与 {@link resumeReopenFromError}
|
|
93
|
+
* **同一只**判官(码 ∈ `RESUME_REOPEN_CODES` ∧ `retriable === true`),外加终局形自己的 `status === 'failed'`。
|
|
94
|
+
* 任一件缺 ⇒ `null`:受理形带同码不算(它不是终局)、`retriable` 缺席 / false 不算(凭码不说「卡仍 pending」)。
|
|
95
|
+
* 读数在场 ⇒ 卡仍 pending、决定没被消费、可再决;文案走 `resumeReopenContent(detail)`。
|
|
96
|
+
*/
|
|
97
|
+
export function decideReceiptReopen(receipt) {
|
|
98
|
+
if (receipt === undefined || receipt.status !== STATUS_FAILED)
|
|
99
|
+
return null;
|
|
100
|
+
return resumeReopenFromError(receipt);
|
|
101
|
+
}
|
|
82
102
|
/**
|
|
83
103
|
* 🔴 **这次 decide 的 200 有没有把门解决掉** —— 唯一的诚实答案是「**说不出来**」。
|
|
84
104
|
*
|
|
@@ -23,9 +23,13 @@ export function projectDeviceExecutorManagementCapability(caps) {
|
|
|
23
23
|
if (caps === null || typeof caps !== 'object')
|
|
24
24
|
return undefined;
|
|
25
25
|
const c = caps;
|
|
26
|
-
if (!Object.hasOwn(c, 'deviceExecutor')
|
|
26
|
+
if (!Object.hasOwn(c, 'deviceExecutor'))
|
|
27
27
|
return { kind: 'not_reported', why: 'capability_absent' };
|
|
28
|
+
// 🔴 外层也只读一次(0.74.2 补审 [medium]):hasOwn 之后 `deviceExecutor` 读进局部量,undefined / false / 对象三判都对同一次读;
|
|
29
|
+
// 此前 undefined 判与取值各读一次,变化的 getter 会把第一次观测到的「车道缺席」丢掉。
|
|
28
30
|
const d = c.deviceExecutor;
|
|
31
|
+
if (d === undefined)
|
|
32
|
+
return { kind: 'not_reported', why: 'capability_absent' };
|
|
29
33
|
if (d === false)
|
|
30
34
|
return { kind: 'lane_absent' };
|
|
31
35
|
if (d === null || typeof d !== 'object' || Array.isArray(d))
|
|
@@ -92,7 +96,7 @@ export function deviceManagementVerbsAvailable(reading) {
|
|
|
92
96
|
export function deviceExecutorManagementDoctorDetail(reading) {
|
|
93
97
|
switch (reading.kind) {
|
|
94
98
|
case 'unobserved':
|
|
95
|
-
return 'device management face not observed — the engine reports it on /v1/capabilities (deviceExecutor.management); this process has
|
|
99
|
+
return 'device management face not observed — the engine reports it on /v1/capabilities (deviceExecutor.management); this process has no usable capabilities reading cached for it (none received, or the last one was unreadable)';
|
|
96
100
|
case 'not_reported':
|
|
97
101
|
return reading.why === 'management_absent'
|
|
98
102
|
? 'device management face not reported by this engine — the device lane is advertised but only newer engines say whether the /v1/devices management verbs exist; this does not say whether they exist, probe them directly'
|
|
@@ -37,6 +37,27 @@ export type EngineAgentPanelEvent = {
|
|
|
37
37
|
currentAction?: string;
|
|
38
38
|
toolUses: number;
|
|
39
39
|
totalTokens: number;
|
|
40
|
+
/**
|
|
41
|
+
* 0.74.3 CC-70:tick 上带的委派卡 id(wire `task_progress.parentToolCallId`,在场才带)—— 身份归一的**退路键**
|
|
42
|
+
* (`transcriptId` 查不到时按它反查 fleet 尾段)。
|
|
43
|
+
*/
|
|
44
|
+
parentToolCallId?: string;
|
|
45
|
+
/**
|
|
46
|
+
* 0.74.3 CC-70(**发布方提示键**,消费端忽略):`true` = 这条 tick 绑在本 turn 一张仍开着的委派卡上(同步委派形)。
|
|
47
|
+
* 身份归一的漏斗据此决定「查不到 fleet 尾段时直发还是缓一拍」:绑卡的子代不会有 fleet 行要等。
|
|
48
|
+
*/
|
|
49
|
+
cardBound?: true;
|
|
50
|
+
/**
|
|
51
|
+
* 0.74.3 CC-70:**归一后**才有 —— 这条事件在 wire 上原来的 `taskId`(引擎 UUIDv7 = transcriptId);
|
|
52
|
+
* `taskId` 已改写成 fleet 行 id 尾段。在场 = 改写过;缺席 = 原样。
|
|
53
|
+
*/
|
|
54
|
+
wireTaskId?: string;
|
|
55
|
+
/**
|
|
56
|
+
* 0.74.3 CC-70:这条 tick 的 `taskId` 是哪一把身份 —— `'fleet-row'` = 已归一到 fleet 尾段(与 fleet-row / 通知 end /
|
|
57
|
+
* 壳本地 agentId 同键);`'wire'` = 反查不到,原样发(同步委派子代 / 老 server 行帧无 transcriptId / fleet 行还没到)。
|
|
58
|
+
* tick 恒带(它只有一个铸点 = task_progress);`end` 只在归一时带。
|
|
59
|
+
*/
|
|
60
|
+
taskIdOrigin?: 'fleet-row' | 'wire';
|
|
40
61
|
} | {
|
|
41
62
|
/**
|
|
42
63
|
* 行帧 lane 投影(2026-07-26 「后台 Agent 子代不进 Background 面板」案)。
|
|
@@ -100,6 +121,14 @@ export type EngineAgentPanelEvent = {
|
|
|
100
121
|
* 🔴 缺席 = 这一行**没有代际概念**(同步委派子代 / workflow agent / 顶层 run / 老引擎),**不是第一代**。
|
|
101
122
|
*/
|
|
102
123
|
cycleSeq?: number;
|
|
124
|
+
/**
|
|
125
|
+
* 0.74.3 CC-70:行帧上的委派卡 id(sdk `FleetTaskRow.parentToolCallId`,在场才带)—— 与 tick 的同名键是同一把钥匙,
|
|
126
|
+
* 身份归一的退路(`transcriptId` 缺席时凭它把 task_progress 的 UUID 反查到本行)。
|
|
127
|
+
*/
|
|
128
|
+
parentToolCallId?: string;
|
|
129
|
+
/** 0.74.4 CC-83:先发布的键为准 —— 这个子代已按 UUID 上屏时,行帧的 `taskId` 改写成那把 UUID,原尾段进这里;缺席 = 未改写。 */
|
|
130
|
+
wireTaskId?: string;
|
|
131
|
+
taskIdOrigin?: 'wire';
|
|
103
132
|
} | {
|
|
104
133
|
kind: 'end';
|
|
105
134
|
taskId: string;
|
|
@@ -116,6 +145,10 @@ export type EngineAgentPanelEvent = {
|
|
|
116
145
|
* explicit close arm carries it; the defensive sweeps settle without one). The consumer persists it
|
|
117
146
|
* on the row (task.result) + appends it to a live-viewed transcript (拍1 查看态 seed). */
|
|
118
147
|
report?: string;
|
|
148
|
+
/** 0.74.3 CC-70:同 tick 臂 —— 归一后才有(流内 close 臂 / 终态 tick 的 `end` 带的是 UUID,改写成 fleet 尾段时记原词)。 */
|
|
149
|
+
wireTaskId?: string;
|
|
150
|
+
/** 0.74.3 CC-70:归一时带 `'fleet-row'`;0.74.4 CC-83:尾段被改写成先发布的 UUID 时带 `'wire'`(原尾段进 `wireTaskId`);其余不带。 */
|
|
151
|
+
taskIdOrigin?: 'fleet-row' | 'wire';
|
|
119
152
|
} | {
|
|
120
153
|
/** Exception-path safety net (published by seamQuery after every turn, throw or return):
|
|
121
154
|
* the consumer settles ALL rows it owns that are still running. Idempotent. */
|
|
@@ -146,6 +179,31 @@ export interface EngineAgentPanelCycleIdentity {
|
|
|
146
179
|
* 陈旧的 `end`:忽略(不 settle、不写 report、不摘所有权)。
|
|
147
180
|
*/
|
|
148
181
|
export declare function isStaleEngineAgentPanelEnd(end: EngineAgentPanelCycleIdentity | null | undefined, current: EngineAgentPanelCycleIdentity | null | undefined): boolean;
|
|
182
|
+
/** 缓一拍的节拍上限:别的 fleet-row / end / sweep 每过一条算一拍(别的 tick 不算)。 */
|
|
183
|
+
export declare const MAX_HELD_WIRE_TICK_BEATS = 16;
|
|
184
|
+
/** 身份归一的读数:`taskId` = 该用的键;`origin` = 它是哪一把。 */
|
|
185
|
+
export interface EnginePanelTaskIdentity {
|
|
186
|
+
readonly taskId: string;
|
|
187
|
+
readonly origin: 'fleet-row' | 'wire';
|
|
188
|
+
/** 钥匙上记的周期身份(登记它的那条 fleet-row 带的);`origin:'wire'` 时恒缺席。 */
|
|
189
|
+
readonly cycleSeq?: number;
|
|
190
|
+
readonly startedAt?: number;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 0.74.3 CC-70:把 wire 上的子代 id(task_progress 的 UUID)反查成 fleet 行 id 尾段;`transcriptId` 表优先,退 `parentToolCallId` 表;
|
|
194
|
+
* 都查不到 ⇒ 原样(`origin:'wire'`)。fleet 尾段本身传进来也原样(它就是目标键)。
|
|
195
|
+
*/
|
|
196
|
+
export declare function resolveEnginePanelTaskId(wireTaskId: string, parentToolCallId?: string): EnginePanelTaskIdentity;
|
|
197
|
+
/**
|
|
198
|
+
* 0.74.3 CC-70(异源复审 R3 [high]):按 wire 上的子代 id 清常驻标 —— 原键照清;归一到 fleet 尾段的那把键**只在这条终态属当代时**才清:
|
|
199
|
+
* 复活后迟到的前代终态(旧 UUID 的流内 close / 终态 tick)归一后带旧代际,清了尾段键 = 撤掉复活行的 sweep 保护 ⇒ 下一次 sweep 假结它。
|
|
200
|
+
* 判据与 `end` 的陈旧判据同一只(`isStaleEngineAgentPanelEnd`,拿钥匙上的周期对最近一条行帧的周期);判不出 ⇒ 照清(不因缺席留下永久常驻)。
|
|
201
|
+
* `cycle`(0.74.4):调用方(通知车道)带得出这条终态的周期时传进来 —— 尾段归某把 UUID 所有而通知比所有权旧 ⇒ 不清那把 UUID 的常驻标。
|
|
202
|
+
*/
|
|
203
|
+
export declare function clearEnginePanelTaskResidentByWire(wireTaskId: string, cycle?: EngineAgentPanelCycleIdentity): void;
|
|
204
|
+
/** 测试钩:清两张钥匙表、缓一拍与周期水位。 */
|
|
205
|
+
export declare function __resetEngineAgentPanelIdentityForTests(): void;
|
|
206
|
+
/** 宿主的唯一发布口(三条 lane 都经这里):先归一身份,再进主通道(订阅者 / 缓冲)。 */
|
|
149
207
|
export declare function publishEngineAgentPanelEvent(ev: EngineAgentPanelEvent): void;
|
|
150
208
|
/** Single consumer (the panel hook). Replays any buffered events, returns an unsubscribe. */
|
|
151
209
|
export declare function subscribeEngineAgentPanel(fn: Listener): () => void;
|
|
@@ -156,6 +214,8 @@ export interface EngineAgentPanelAbsence {
|
|
|
156
214
|
lastSeenAtMs: number;
|
|
157
215
|
/** 发出这条上报时已缺席多久(ms;> `ABSENT_SETTLE_MS`)。 */
|
|
158
216
|
absentForMs: number;
|
|
217
|
+
/** 0.74.4 CC-83:`taskId` 被改写成先发布的 UUID 时,原尾段在这里;缺席 = 按尾段发。 */
|
|
218
|
+
wireTaskId?: string;
|
|
159
219
|
}
|
|
160
220
|
type AbsenceListener = (ev: EngineAgentPanelAbsence) => void;
|
|
161
221
|
/** 发布一条缺席上报(由 `projectFleetAgentRows` 的缺席路径调;消费者抛错不反噬投影)。 */
|