@tea-agent/loop-agent 0.31.1 → 0.32.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.
Files changed (85) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +46 -0
  3. package/README.md +1 -1
  4. package/dist/shared/operator/capabilities.js +22 -1
  5. package/dist/shared/operator/command-lifecycle.js +94 -0
  6. package/dist/shared/operator/index.js +1 -0
  7. package/dist/worker/cli.js +10 -24
  8. package/dist/worker/console/doctor.js +11 -4
  9. package/dist/worker/console/observe-health-match.js +18 -15
  10. package/dist/worker/console/operator-actions.js +2 -1
  11. package/dist/worker/console/static/assets/index-Bpa2qrc-.js +29 -0
  12. package/dist/worker/console/static/assets/index-BqfFDdnG.css +1 -0
  13. package/dist/worker/console/static/index.html +2 -2
  14. package/dist/worker/console/static-src/app/console-types.js +140 -0
  15. package/dist/worker/console/static-src/app/useConsoleShell.js +101 -0
  16. package/dist/worker/console/static-src/app/useOperatorActions.js +304 -0
  17. package/dist/worker/console/static-src/app/usePrdImport.js +171 -0
  18. package/dist/worker/console/static-src/app/useRecoveryActions.js +257 -0
  19. package/dist/worker/console/static-src/app/useRecoveryConsole.js +334 -0
  20. package/dist/worker/console/static-src/app/useTaskWizard.js +229 -0
  21. package/dist/worker/console/static-src/chat-view-types.js +2 -0
  22. package/dist/worker/console/static-src/night/night-types.js +24 -0
  23. package/dist/worker/console/static-src/night/useNightBoard.js +125 -0
  24. package/dist/worker/console/static-src/night/useNightWizard.js +171 -0
  25. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +253 -0
  26. package/dist/worker/console/static-src/operator-chat/format.js +71 -0
  27. package/dist/worker/console/static-src/operator-chat/refs.js +47 -0
  28. package/dist/worker/console/static-src/operator-chat/tools-catalog.js +77 -0
  29. package/dist/worker/console/static-src/operator-chat/types.js +1 -0
  30. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +320 -0
  31. package/dist/worker/console/static-src/operator-chat/useChatStream.js +209 -0
  32. package/dist/worker/console/static-src/operator-chat/useChatThread.js +218 -0
  33. package/dist/worker/console/static-src/operator-chat/useComposer.js +125 -0
  34. package/dist/worker/console/static-src/operator-chat/useInterview.js +108 -0
  35. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +123 -0
  36. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +207 -0
  37. package/dist/worker/feature/acceptance-policy.js +227 -0
  38. package/dist/worker/feature/decision-loader.js +1 -1
  39. package/dist/worker/feature/next-action.js +56 -6
  40. package/dist/worker/feature/profile-schema.js +3 -0
  41. package/dist/worker/feature/reducer.js +1 -0
  42. package/dist/worker/feature/review.js +72 -8
  43. package/dist/worker/feature/scaffold.js +14 -0
  44. package/dist/worker/loop-agent/controller-protocol.js +143 -0
  45. package/dist/worker/materialize/harness-task-lifecycle-probe.js +126 -0
  46. package/dist/worker/materialize/harness-task-lineage.js +220 -0
  47. package/dist/worker/materialize/harness-task-materializer.js +350 -80
  48. package/dist/worker/observability/progress-composite.js +1 -0
  49. package/dist/worker/observability/read-model.js +66 -19
  50. package/dist/worker/pool/attempt-identity.js +41 -0
  51. package/dist/worker/pool/attempt-lease.js +184 -0
  52. package/dist/worker/pool/attempt-transition.js +210 -0
  53. package/dist/worker/pool/begin-attempt-with-lease.js +26 -0
  54. package/dist/worker/pool/begin-attempt.js +35 -0
  55. package/dist/worker/pool/failure-routing.js +49 -0
  56. package/dist/worker/pool/recovery-decision.js +163 -0
  57. package/dist/worker/pool/run-owner-store.js +126 -0
  58. package/dist/worker/pool/run-store.js +32 -46
  59. package/dist/worker/pool/runtime-reconcile-inventory.js +127 -0
  60. package/dist/worker/pool/state-projection.js +57 -0
  61. package/dist/worker/run-task/run-task.js +31 -4
  62. package/dist/worker/runner/run-ready.js +64 -14
  63. package/dist/worker/runner/single-task-attempt.js +42 -13
  64. package/dist/worker/task-graph/acceptance-schema.js +3 -0
  65. package/docs/README.md +3 -1
  66. package/docs/architecture/evolution.md +1 -1
  67. package/docs/operations/README.md +1 -0
  68. package/docs/templates/README.md +1 -0
  69. package/docs/templates/agent-worker-production-readiness-checklist.md +47 -0
  70. package/docs/templates/evaluation/agents-map-slim-v1.md +1 -1
  71. package/docs/templates/evaluation/agents-map-verbose-v0.md +1 -1
  72. package/docs/templates/init-managed-agents.md +1 -1
  73. package/docs/templates/product-line/scaffold-samples/backend-only/acceptance.yaml +3 -0
  74. package/docs/templates/product-line/scaffold-samples/backend-only/feature.yaml +11 -0
  75. package/docs/templates/product-line/scaffold-samples/fe-with-api/acceptance.yaml +3 -0
  76. package/docs/templates/product-line/scaffold-samples/fe-with-api/feature.yaml +11 -0
  77. package/docs/templates/product-line/scaffold-samples/frontend-only/acceptance.yaml +3 -0
  78. package/docs/templates/product-line/scaffold-samples/frontend-only/feature.yaml +11 -0
  79. package/package.json +5 -4
  80. package/skills/agent-worker/SKILL.md +1 -1
  81. package/skills/agent-worker/references/agent-worker-operator.md +1 -1
  82. package/skills/loop-agent/references/command-reference.md +2 -2
  83. package/skills/loop-agent/references/harness-policy.md +1 -1
  84. package/dist/worker/console/static/assets/index-D9gnJn_l.js +0 -29
  85. package/dist/worker/console/static/assets/index-rajoXwkM.css +0 -1
package/AGENTS.md CHANGED
@@ -38,7 +38,7 @@
38
38
  4. 涉及测试纪律/验证声明/调试时继续读:`docs/governance/harness-methodology-*.md`。
39
39
  5. 查看最近提交、相关 plan/progress/report;`git status --short --branch`;跑最小基线验证。
40
40
  6. 后端/接口/pytest → `taskKind: "backend-test"`(不是 `--profile`);知识回写 `knowledge-sync`;图谱开荒 `knowledge-graph-bootstrap`。`--profile` 仅 `auto|minimal|standard|reviewed|supervised`。
41
- 7. 看板/observe → `agent-worker console`(默认 repo=当前目录、port=8790;兼容入口 `agent-worker console serve --repo . --port 8790`)(`/inspect/` 只读);`observe serve` 仅为兼容入口。
41
+ 7. 看板/observe → `agent-worker console`(默认 repo=当前目录、port=8790;兼容入口 `agent-worker console serve --repo . --port 8790`)(`/inspect/` 只读);`observe serve` 已下线(`OBSERVE_SERVE_REMOVED` + exit 2);`observe snapshot` 仍可用。
42
42
  8. 分支合并 → 先读 `docs/operations/branch-merge-guideline.md`。
43
43
 
44
44
  ## 会话协议
package/CHANGELOG.md CHANGED
@@ -2,6 +2,52 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.32.1] - 2026-08-09
6
+
7
+ ### 改进
8
+
9
+ - Operator Console 前端重构视觉层级与代码结构:`OperatorChat.tsx`(3656→407 行)、`App.tsx`(3044→182 行)、`NightJobsWorkbench.tsx`(852→90 行)按职责拆为 `operator-chat/`、`app/`、`pages/`、`shell/`、`night/` 模块,消息流 / 工具卡 / gate 卡 / composer / 侧栏各自独立;样式统一为单一语义 token 层,人工决策 gate 卡以决策卡规格(橙色左边条 + 浮起阴影 + 状态徽标 + 未决时吸附 composer 上方)突出,工具过程卡默认退后
10
+ - Agent Worker Production Readiness v1 Phase 8:在已发布 `@0.32.0` 上完成 published-tarball deterministic campaign 与 burn-in day 0 开窗;scorecard 仍为 **`NOT_READY`**(待 burn-in 累计、live Pi canary、live Closeout、C4/C7 进程 crash)
11
+
12
+ ### 修复
13
+
14
+ - Operator Console:补齐 `--accent` / `--danger` / `--surface-raised` 等 14 个被引用但从未定义的 CSS 变量(pi-web 遗留命名与 warm canvas 命名两代并存),此前 HumanGateCard 的橙色左边条、confirmed 态、红色错误文本实际不渲染;设置页仓库指纹截断为短哈希展示
15
+ - Operator Console:新增 `console:typecheck` 门禁并修复拆分后 `.ts/.tsx` 未纳入类型检查的盲区;`ChatComposer` 错误从 `ToolsCatalog` 导入未导出的 `ToolCatalogView`;将 46 处旧式 `JSX.Element` 迁移为 React 19 兼容的 `React.JSX.Element`
16
+ - Operator Chat 会话切换硬化:activate / create / restore 改为版本化提交,慢响应的旧会话选择不再覆盖较新的用户选择;新建 / 删除 / 归档会话完整清理 messages、tool calls、operation / gate / artifact cards、usage、streaming、unread、composer draft、图片附件与文件预览,createSession 改为服务端创建成功后再重置
17
+ - Operator Console:新增 `styles.css` 自定义属性完整性契约与会话选择一致性源码契约;测试中以 `URL.parse` 替换会抛错的 `new URL` 构造
18
+
19
+ ## [0.32.0] - 2026-08-09
20
+
21
+ ### 重点更新
22
+
23
+ - 启动 Agent Worker Production Readiness v1 分阶段落地:Running Attempt 归属不再依赖 terminal ledger;safe `run-error` 可直接 `task retry`,不再要求 emergency `mark-failed`
24
+ - Harness Task 新 identity 改为 lineage/revision digest,同日 retry 不再依赖修改 title
25
+ - 新增 Task-scoped attempt lease、controller protocol compatibility 评估,以及 agent-worker production readiness 操作合同/checklist
26
+ - Attempt 终态释放 lease;写入前评估 controllerProtocol;scaffold 强制 ADR 0007 acceptance_policy;reconcile dry-run inventory 给出唯一恢复命令
27
+ - 硬下线 `agent-worker observe serve`,只读检视统一走 Console `/inspect/`
28
+
29
+ ### 新增
30
+
31
+ - `WorkerAttemptIdentity` / immutable run-owner store / `beginWorkerAttempt`(含 lease 路径)
32
+ - `WorkerAttemptFailureV2`、`resolveWorkerRecoveryDecision`、Attempt transition reducer
33
+ - Harness Task lineage/revision digests 与 `MaterializeOutcome` 元数据
34
+ - `docs/operations/agent-worker-production-readiness.md` 与 checklist 模板
35
+ - operator capabilities `controllerProtocol` envelope;`buildRuntimeReconcileInventoryDryRun`;command lifecycle official 命令清单
36
+ - Feature scaffold `acceptance_policy` 与 AC `verification.authority` / `required_evidence_kinds`
37
+
38
+ ### 改进
39
+
40
+ - Feature Review / Observe 对 Running 与 terminal 分层投影,ownership 告警按 `workerRunId` 聚合
41
+ - Task-scoped WorkerEvent 携带 `featureId`
42
+ - `batch run-ready` / `runSingleTaskAttempt` 在 terminal 后 CAS 释放 lease;有 controller identity 时先做 protocol preflight
43
+ - Feature next-action 优先消费 acceptanceGaps.nextCommand
44
+
45
+ ### 修复
46
+
47
+ - 硬下线 `agent-worker observe serve`:调用后不再监听端口,stderr 输出稳定标记 `OBSERVE_SERVE_REMOVED` 与替代入口,进程以退出码 2 结束
48
+ - 只读检视统一走 `agent-worker console`(默认 `127.0.0.1:8790`)的 `/inspect/`;`observe snapshot` 仍输出 GlobalSnapshot JSON
49
+ - Console doctor / health-match 不再以外部 8787 Observe 进程为健康前提
50
+
5
51
  ## [0.31.1] - 2026-08-09
6
52
 
7
53
  ### 重点更新
package/README.md CHANGED
@@ -100,7 +100,7 @@ loop-agent dag report --run-id <run-id>
100
100
  | --- | --- | --- |
101
101
  | 受治理 CLI | `loop-agent …` / `agent-worker …` | 权威写入与诊断 |
102
102
  | Loop Operator Console | `agent-worker console` | Official 本地控制面(默认 repo=当前目录、`127.0.0.1:8790`,含 Operate 与 Inspect) |
103
- | Observe | `agent-worker observe serve --repo . --port 8787` | 兼容期独立只读入口;不再作为推荐路径 |
103
+ | Inspect(只读) | `agent-worker console` → `/inspect/`;`observe snapshot` | 统一 Console 内只读检视;`observe serve` 已下线(REMOVED / exit 2) |
104
104
 
105
105
  ```bash
106
106
  # 终端 A:Console(任务/运行操作面;canonical mutation 只经 sibling loop-agent)
@@ -1,4 +1,18 @@
1
+ import { createRequire } from "node:module";
1
2
  import { OPERATOR_ERROR_CODES_V1, OPERATOR_OUTCOMES_V1 } from "./registry.js";
3
+ import { WORKER_ADAPTER_PROTOCOL_RANGE } from "../../worker/loop-agent/controller-protocol.js";
4
+ const require = createRequire(import.meta.url);
5
+ const PACKAGE_VERSION = require("../../../package.json").version ?? "0.0.0";
6
+ /** Keep aligned with WORKER_CONTROLLER_REQUIREMENTS_V1 in controller-protocol.ts */
7
+ const CONTROLLER_PROTOCOL_CAPABILITIES = [
8
+ "task-advance-managed-contract-v2",
9
+ "worker-association-v1",
10
+ "events-jsonl-v1",
11
+ "document-index-closure-v1",
12
+ "worker-attempt-owner-v1",
13
+ "worker-attempt-revision-v1",
14
+ "recovery-decision-v2",
15
+ ];
2
16
  const officialAction = (action, cli, kind, coverage) => ({
3
17
  action,
4
18
  cli,
@@ -1348,6 +1362,13 @@ export function buildOperatorCapabilitiesDocument() {
1348
1362
  canonicalizerVersion: 1,
1349
1363
  taskConfigSchemaVersion: 1,
1350
1364
  },
1365
+ controllerProtocol: {
1366
+ schemaVersion: 1,
1367
+ controllerProtocolVersion: 1,
1368
+ workerAdapterProtocolRange: WORKER_ADAPTER_PROTOCOL_RANGE,
1369
+ capabilities: CONTROLLER_PROTOCOL_CAPABILITIES,
1370
+ packageVersion: PACKAGE_VERSION,
1371
+ },
1351
1372
  actions,
1352
1373
  };
1353
1374
  }
@@ -2215,7 +2236,7 @@ export const OPERATOR_COMMAND_COVERAGE = Object.freeze([
2215
2236
  coverage: "excluded",
2216
2237
  action: null,
2217
2238
  source: "agent-worker",
2218
- exclusionReason: "Not exposed to Operator Chat in M5.",
2239
+ exclusionReason: "Removed (Wave 5): OBSERVE_SERVE_REMOVED + exit 2; use agent-worker console + /inspect/.",
2219
2240
  },
2220
2241
  {
2221
2242
  command: "agent-worker observe snapshot",
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Public command lifecycle machine facts (Phase 7 Track B).
3
+ * Implementation remains in CLI modules; this registry is the shared tombstone /
4
+ * deprecation source for capabilities, drift checks, and operator envelopes.
5
+ */
6
+ const COMMAND_LIFECYCLE_REGISTRY = {
7
+ "agent-worker observe serve": {
8
+ command: "agent-worker observe serve",
9
+ product: "agent-worker",
10
+ lifecycle: "removed",
11
+ replacement: "agent-worker console",
12
+ removedVersion: "0.19.0",
13
+ diagnosticCode: "OBSERVE_SERVE_REMOVED",
14
+ removedExitCode: 2,
15
+ docsSummary: "Independent observe HTTP server removed; use unified Operator Console /inspect/.",
16
+ operatorCategory: "observe",
17
+ },
18
+ "agent-worker console": {
19
+ command: "agent-worker console",
20
+ product: "agent-worker",
21
+ lifecycle: "official",
22
+ introducedVersion: "0.19.0",
23
+ docsSummary: "Official unified Operator Console (Operate + Inspect on default loopback :8790).",
24
+ operatorCategory: "console",
25
+ },
26
+ "agent-worker feature review": {
27
+ command: "agent-worker feature review",
28
+ product: "agent-worker",
29
+ lifecycle: "official",
30
+ docsSummary: "Project Feature readiness, acceptance gaps, and next action.",
31
+ operatorCategory: "feature",
32
+ },
33
+ "agent-worker feature advance": {
34
+ command: "agent-worker feature advance",
35
+ product: "agent-worker",
36
+ lifecycle: "official",
37
+ docsSummary: "ADR 0007 orchestration: verify-final → delivery → closeout.",
38
+ operatorCategory: "feature",
39
+ },
40
+ "agent-worker feature verify-final": {
41
+ command: "agent-worker feature verify-final",
42
+ product: "agent-worker",
43
+ lifecycle: "official",
44
+ docsSummary: "Canonical Final Verification writer (ADR 0007); only authority for coverage facts.",
45
+ operatorCategory: "feature",
46
+ },
47
+ "agent-worker batch run-ready": {
48
+ command: "agent-worker batch run-ready",
49
+ product: "agent-worker",
50
+ lifecycle: "official",
51
+ docsSummary: "Execute Ready Feature tasks with Task Pool attempt lease.",
52
+ operatorCategory: "batch",
53
+ },
54
+ "agent-worker task reconcile": {
55
+ command: "agent-worker task reconcile",
56
+ product: "agent-worker",
57
+ lifecycle: "official",
58
+ docsSummary: "Reconcile partial/orphan Worker attempt facts without deleting evidence.",
59
+ operatorCategory: "task",
60
+ },
61
+ "agent-worker pool doctor": {
62
+ command: "agent-worker pool doctor",
63
+ product: "agent-worker",
64
+ lifecycle: "official",
65
+ docsSummary: "Diagnose Task Pool / lease / ownership consistency.",
66
+ operatorCategory: "pool",
67
+ },
68
+ "loop-agent operator capabilities": {
69
+ command: "loop-agent operator capabilities",
70
+ product: "loop-agent",
71
+ lifecycle: "official",
72
+ docsSummary: "Publish operator + controllerProtocol envelope for Console/Worker negotiation.",
73
+ operatorCategory: "operator",
74
+ },
75
+ };
76
+ export function listCommandLifecycleEntries() {
77
+ return Object.values(COMMAND_LIFECYCLE_REGISTRY);
78
+ }
79
+ export function normalizeCommandLifecycleKey(command) {
80
+ return command.trim().replace(/\s+/g, " ");
81
+ }
82
+ export function resolveCommandLifecycle(command) {
83
+ const key = normalizeCommandLifecycleKey(command);
84
+ return COMMAND_LIFECYCLE_REGISTRY[key];
85
+ }
86
+ export function isRemovedCommand(command) {
87
+ return resolveCommandLifecycle(command)?.lifecycle === "removed";
88
+ }
89
+ export function listOfficialCommands(product) {
90
+ return listCommandLifecycleEntries()
91
+ .filter((entry) => entry.lifecycle === "official" &&
92
+ (product === undefined || entry.product === product))
93
+ .map((entry) => entry.command);
94
+ }
@@ -2,3 +2,4 @@ export * from "./types.js";
2
2
  export * from "./registry.js";
3
3
  export * from "./envelope.js";
4
4
  export * from "./capabilities.js";
5
+ export * from "./command-lifecycle.js";
@@ -13,7 +13,6 @@ import { createProgressReporter } from "./progress-reporter.js";
13
13
  import { createCompositeProgressReporter } from "./observability/progress-composite.js";
14
14
  import { createRoutedWorkerEventStore } from "./observability/event-store.js";
15
15
  import { buildGlobalSnapshot } from "./observability/read-model.js";
16
- import { createObserveServer } from "./observe/server.js";
17
16
  import { diagnoseTaskPoolStates, formatDoctorHuman, loadOperatorMapping, } from "./pool/doctor.js";
18
17
  import { migrateTaskPoolStates } from "./pool/migrate-state.js";
19
18
  import { markTaskPoolFailed, reconcileTaskPoolAbandon, } from "./pool/reconcile.js";
@@ -690,29 +689,16 @@ export function buildAgentWorkerProgram() {
690
689
  });
691
690
  observe
692
691
  .command("serve")
693
- .requiredOption("--repo <repo-root>", "Target repo root")
694
- .option("--port <port>", "HTTP port", "8787")
695
- .option("--host <host>", "Bind host (default 127.0.0.1; use 0.0.0.0 for LAN, no network auth)", "127.0.0.1")
696
- .option("--debug", "Log request source/method/path/status to stderr")
697
- .description("Start read-only observe HTTP server (compat entry; prefer console serve)")
698
- .action(async (options) => {
699
- const repoRoot = path.resolve(options.repo);
700
- const server = await createObserveServer({
701
- repoRoot,
702
- host: options.host,
703
- port: Number.parseInt(options.port, 10),
704
- debug: options.debug === true,
705
- });
706
- process.stderr.write("[OBSERVE_SERVE_DEPRECATED] agent-worker observe serve 作为独立看板入口将在兼容窗口后下线。\n" +
707
- "请改用:agent-worker console serve --repo <repo> --port 8790\n" +
708
- "统一 Operator Console 已包含原 Observe 的只读检视能力:http://127.0.0.1:8790/\n");
709
- process.stdout.write(`${server.url}\n`);
710
- await new Promise((resolve) => {
711
- const shutdown = () => resolve();
712
- process.once("SIGINT", shutdown);
713
- process.once("SIGTERM", shutdown);
714
- });
715
- await server.close();
692
+ .option("--repo <repo-root>", "Target repo root (ignored; command removed)")
693
+ .option("--port <port>", "HTTP port (ignored; command removed)", "8787")
694
+ .option("--host <host>", "Bind host (ignored; command removed)", "127.0.0.1")
695
+ .option("--debug", "Ignored; command removed")
696
+ .description("Removed: independent observe HTTP server (use agent-worker console + /inspect/)")
697
+ .action(() => {
698
+ process.stderr.write("[OBSERVE_SERVE_REMOVED] agent-worker observe serve 已下线。\n" +
699
+ "请改用:agent-worker console --repo <repo> --port 8790\n" +
700
+ "只读检视:http://127.0.0.1:8790/inspect/\n");
701
+ process.exitCode = 2;
716
702
  });
717
703
  observe
718
704
  .command("snapshot")
@@ -161,12 +161,19 @@ export async function runConsoleDoctor(options) {
161
161
  };
162
162
  }
163
163
  const inspect = await probeInspect(options.consoleBaseUrl, options.fetchImpl ?? fetch);
164
- const observe = options.observeBaseUrl
165
- ? await probeObserve(options.observeBaseUrl, fingerprint, options.fetchImpl ?? fetch)
164
+ // Wave 5: do not probe retired independent observe serve (8787).
165
+ // Optional observeBaseUrl only accepted when it is the same origin as Console.
166
+ const consoleOrigin = options.consoleBaseUrl?.replace(/\/+$/, "");
167
+ const observeOrigin = options.observeBaseUrl?.replace(/\/+$/, "");
168
+ const observe = observeOrigin &&
169
+ consoleOrigin &&
170
+ observeOrigin === consoleOrigin
171
+ ? await probeObserve(observeOrigin, fingerprint, options.fetchImpl ?? fetch)
166
172
  : {
167
173
  status: "unavailable",
168
174
  url: `${DEFAULT_OBSERVE_BASE_URL}/api/health`,
169
- message: "未探测兼容 Observe;统一 Inspect 不依赖 8787",
175
+ message: "独立 observe serve 已下线;健康以统一 Operator Console / Inspect 为准",
176
+ startCommand: OBSERVE_START_COMMAND,
170
177
  };
171
178
  const pi = await probePiReadiness({
172
179
  override: options.piReadinessProbe,
@@ -204,7 +211,7 @@ export function formatConsoleDoctorHuman(report) {
204
211
  ];
205
212
  if (report.observe.status === "unavailable" ||
206
213
  report.observe.status === "mismatch") {
207
- lines.push(`observe.startCommand: ${report.observe.startCommand ?? OBSERVE_START_COMMAND}`);
214
+ lines.push(`observe.startCommand: ${report.observe.startCommand ?? OBSERVE_START_COMMAND} (use console + /inspect/)`);
208
215
  }
209
216
  if (report.siblingLoopAgentBin) {
210
217
  lines.push(`siblingLoopAgentBin: ${report.siblingLoopAgentBin}`);
@@ -1,9 +1,12 @@
1
1
  /**
2
- * Observe health match classification (Phase 3).
3
- * Fail-closed deep links: only same-repo + schema + required capability.
2
+ * Observe link capability mapping + retired cross-process health-match stubs.
3
+ * Wave 5: independent observe serve (8787) is removed; doctor uses Console surface.
4
+ * capabilityForObserveTarget remains for Inspect deep-links / night-jobs.
4
5
  */
5
- export const OBSERVE_START_COMMAND = "agent-worker observe serve --repo . --port 8787";
6
- export const DEFAULT_OBSERVE_BASE_URL = "http://127.0.0.1:8787";
6
+ /** @deprecated Wave 5 observe serve removed; use agent-worker console */
7
+ export const OBSERVE_START_COMMAND = "agent-worker console --repo . --port 8790";
8
+ /** @deprecated Wave 5 — default unified Console origin (not independent Observe) */
9
+ export const DEFAULT_OBSERVE_BASE_URL = "http://127.0.0.1:8790";
7
10
  export function capabilityForObserveTarget(target) {
8
11
  switch (target.kind) {
9
12
  case "dag":
@@ -25,8 +28,8 @@ export function capabilityForObserveTarget(target) {
25
28
  }
26
29
  }
27
30
  /**
28
- * Classify Observe /api/health body against local repo fingerprint + required capability.
29
- * Pure function — no I/O.
31
+ * Classify Console/Inspect /api/health body against local repo fingerprint + capability.
32
+ * Pure function — no I/O. Prefer unified Console origin (8790), not retired 8787 Observe.
30
33
  */
31
34
  export function classifyObserveHealth(input) {
32
35
  const url = input.healthUrl ?? `${DEFAULT_OBSERVE_BASE_URL}/api/health`;
@@ -36,7 +39,7 @@ export function classifyObserveHealth(input) {
36
39
  return {
37
40
  status: "unavailable",
38
41
  url,
39
- message: "Observe health body missing or not JSON object",
42
+ message: "Operator surface health body missing or not JSON object",
40
43
  startCommand,
41
44
  body,
42
45
  localFingerprint: input.localFingerprint,
@@ -48,7 +51,7 @@ export function classifyObserveHealth(input) {
48
51
  return {
49
52
  status: "unavailable",
50
53
  url,
51
- message: "Observe health ok !== true",
54
+ message: "Operator surface health ok !== true",
52
55
  startCommand,
53
56
  body,
54
57
  localFingerprint: input.localFingerprint,
@@ -60,7 +63,7 @@ export function classifyObserveHealth(input) {
60
63
  return {
61
64
  status: "mismatch",
62
65
  url,
63
- message: `Observe schemaVersion mismatch (expected 1, got ${String(health.schemaVersion)})`,
66
+ message: `Operator surface schemaVersion mismatch (expected 1, got ${String(health.schemaVersion)})`,
64
67
  startCommand,
65
68
  body,
66
69
  localFingerprint: input.localFingerprint,
@@ -75,7 +78,7 @@ export function classifyObserveHealth(input) {
75
78
  return {
76
79
  status: "mismatch",
77
80
  url,
78
- message: "Observe repoFingerprint does not match local worktree",
81
+ message: "Operator surface repoFingerprint does not match local worktree",
79
82
  startCommand,
80
83
  body,
81
84
  localFingerprint: input.localFingerprint,
@@ -91,7 +94,7 @@ export function classifyObserveHealth(input) {
91
94
  return {
92
95
  status: "mismatch",
93
96
  url,
94
- message: `Observe missing route capability: ${input.requiredCapability}`,
97
+ message: `Operator surface missing route capability: ${input.requiredCapability}`,
95
98
  startCommand,
96
99
  body,
97
100
  localFingerprint: input.localFingerprint,
@@ -104,7 +107,7 @@ export function classifyObserveHealth(input) {
104
107
  return {
105
108
  status: "match",
106
109
  url,
107
- message: "Observe health matches local repo and required capability",
110
+ message: "Operator surface health matches local repo and required capability",
108
111
  startCommand,
109
112
  body,
110
113
  localFingerprint: input.localFingerprint,
@@ -117,7 +120,7 @@ export function classifyObserveHealth(input) {
117
120
  return {
118
121
  status: "match",
119
122
  url,
120
- message: "Observe health matches local repo",
123
+ message: "Operator surface health matches local repo",
121
124
  startCommand,
122
125
  body,
123
126
  localFingerprint: input.localFingerprint,
@@ -126,8 +129,8 @@ export function classifyObserveHealth(input) {
126
129
  };
127
130
  }
128
131
  /**
129
- * Probe Observe /api/health and classify against local fingerprint.
130
- * Network failure → unavailable (non-blocking for Console doctor).
132
+ * Probe /api/health and classify against local fingerprint.
133
+ * Network failure → unavailable. Prefer Console origin; independent Observe serve is removed.
131
134
  */
132
135
  export async function probeAndClassifyObserve(input) {
133
136
  const base = input.baseUrl.replace(/\/+$/, "");
@@ -537,7 +537,8 @@ export async function dispatchOperatorAction(ctx, req) {
537
537
  }
538
538
  case "observeLink": {
539
539
  const consoleOrigin = ctx.getConsoleOrigin?.();
540
- const baseUrl = ctx.observeBaseUrl ?? consoleOrigin ?? DEFAULT_OBSERVE_BASE_URL;
540
+ // Wave 5: prefer unified Console origin; do not fall back to retired 8787 Observe.
541
+ const baseUrl = consoleOrigin ?? ctx.observeBaseUrl ?? DEFAULT_OBSERVE_BASE_URL;
541
542
  const dagRunId = str(p.dagRunId);
542
543
  const taskId = str(p.taskId);
543
544
  const featureId = str(p.featureId);