diff --git a/.agents/skills/unidesk-otel/references/full.md b/.agents/skills/unidesk-otel/references/full.md index 5e6e01f0..4970e181 100644 --- a/.agents/skills/unidesk-otel/references/full.md +++ b/.agents/skills/unidesk-otel/references/full.md @@ -88,12 +88,12 @@ OTel trace 内常见业务关联属性: OpenCode 对话长时间显示 `Thinking`、用户发消息后无 assistant 文本、或怀疑 iframe/live state 没吃到事件时,先分别证明 provider、Cloud Web 代理和浏览器事件流三层状态。provider span 可能已经 200,但 UI 仍可能因 `/global/event` 目录、ticket 或 live state 不一致而不收敛。 -优先用明确 service 的 TraceQL 查询 provider,避免普通 grep 漏掉新 span 名或属性: +优先用增强后的 `--grep` 查询 provider/service 线索;当前 grep 会先推断合适的 TraceQL 候选查询,再在扫描到的 trace 内匹配 raw body、span name、route、status message 和 full span attributes: ```bash bun scripts/cli.ts platform-infra observability search \ --target \ - --query '{ resource.service.name = "opencode-provider-proxy" }' \ + --grep opencode-provider-proxy \ --lookback-minutes 30 \ --candidate-limit 100 \ --limit 20 @@ -106,7 +106,18 @@ Cloud Web 侧查 `/global/event` 长连接的 start span;长连接可能在调 ```bash bun scripts/cli.ts platform-infra observability search \ --target \ - --query '{ resource.service.name = "hwlab-cloud-web" && span.http.route = "/global/event" }' \ + --grep opencode.proxy.stream.start \ + --lookback-minutes 30 \ + --candidate-limit 300 \ + --limit 20 +``` + +属性 drill-down 可直接查 key/value;例如: + +```bash +bun scripts/cli.ts platform-infra observability search \ + --target \ + --grep opencode.proxy.sse.directory_rewrite_enabled=true \ --lookback-minutes 30 \ --candidate-limit 300 \ --limit 20 @@ -114,7 +125,7 @@ bun scripts/cli.ts platform-infra observability search \ 期望看到 `opencode.proxy.stream.start`,并检查 `opencode.proxy.sse.directory_rewrite_enabled`、`opencode.proxy.sse.directory_rewrite_from`、`opencode.proxy.sse.directory_rewrite_to`、`opencode.proxy.ticket_accepted`、`span.http.route=/global/event` 和 streaming 标记。`from=/workspace`、`to=/` 代表 HWLAB iframe/public route 与 OpenCode server workspace route 已对齐;如果该 rewrite 不存在或 ticket 未接受,provider 正常也不能说明 UI 正常。 -关闭 OpenCode UI 卡住类 issue 时,OTel 只能证明链路状态;最终还要用 web-probe DOM/事件证据确认浏览器看到 assistant 文本、没有残留 `Thinking`,并且 EventSource 收到 `message.part.updated`、`step-finish`、`session.idle` 等事件。若 `--grep` 没搜到新属性或 span 名,改用上述 TraceQL query,再只对小范围 trace 使用 `trace --raw` 做 bounded attr drill-down。 +关闭 OpenCode UI 卡住类 issue 时,OTel 只能证明链路状态;最终还要用 `web-probe opencode-smoke` 或等价 DOM/事件证据确认浏览器看到 assistant 文本、没有残留 `Thinking`,并且 EventSource 收到 `message.part.updated`、`step-finish`、`session.idle` 等事件。若 `--grep` 没搜到新属性或 span 名,先阅读输出中的 `grepCoverage`、`grepQueryInference` 和 no-match Next;必要时再改用显式 TraceQL,例如 `{ resource.service.name = "hwlab-cloud-web" && .http.route = "/global/event" }`,并只对小范围 trace 使用 `trace --full|--raw` 做 bounded attr drill-down。 ## Code Agent / AgentRun 排障 diff --git a/.agents/skills/unidesk-webdev/references/full.md b/.agents/skills/unidesk-webdev/references/full.md index 4be56054..efad2936 100644 --- a/.agents/skills/unidesk-webdev/references/full.md +++ b/.agents/skills/unidesk-webdev/references/full.md @@ -170,7 +170,8 @@ MDTODO 或 Project Management 的 Web 重写/布局 closeout 不能只引用组 - issue closeout 优先引用 `web-probe script` 输出的顶层 `issueEvidence` 或 `summary.issueEvidence`;只有需要展开调查时才粘贴 `probe.script.result`、`probe.steps` 或完整 `reportPath`,避免 stdout、summary 和 report 多层重复同一证据。 - stdin heredoc 与 `--script-file` 都按 ES module 加载,脚本必须导出 `export default async ({ page, gotoStable, recordStep, ... }) => { ... }`;不要在模块顶层直接写 `return`。失败为 `Illegal return statement`、`does not provide an export named default` 或 finalUrl 仍是 `about:blank` 且 stepCount=0 时,先按 probe 脚本入口误用处理,不要归因成 Cloud Web 行为失败。 - 自定义脚本需要主动失败时,优先返回 `{ ok: false, failedCondition: "..." }` 或抛出错误;兼容返回 `{ pass: false }`、`{ success: false }`,且 `{ ok: true, pass: false }` 仍按失败处理。`failedCondition`、`errorMessage`、`message`、`error`、`reason` 或 `summary` 会进入失败摘要;不要只把失败埋在普通业务字段里。 -- OpenCode iframe 或 direct-host smoke 必须等到 OpenCode frame/direct origin 可用后再操作 composer;填入 prompt 后优先点击可见的 `[data-action='prompt-submit']`,不要把 Enter-only 当成稳定提交路径。关闭 OpenCode 对话类 issue 时,`web-probe script` 顶层 `ok` 不能只是“脚本没抛错”;必须断言 DOM body 出现最终 assistant 文本、没有残留 `Thinking`,并记录 EventSource 或等价事件探针看到 `message.part.updated`、`step-finish`、`session.idle`。手工 EventSource 探针可以证明 `/global/event` 消息到达,但不能替代 UI DOM 终态证据。 +- OpenCode iframe 或 direct-host smoke 优先使用 typed `web-probe opencode-smoke --node --lane `,不要重复手写一次性 Playwright 片段。该命令负责登录 HWLAB public origin、打开 `/opencode`、定位 iframe/direct origin、打开 project/composer、点击可见 `[data-action='prompt-submit']`、采集 EventSource,并断言 DOM assistant 文本与 update/finish/idle 终态。只有 typed 命令覆盖不到的额外 API 或 UI 细节才退回 `web-probe script`。 +- 自定义 OpenCode smoke 必须等到 OpenCode frame/direct origin 可用后再操作 composer;填入 prompt 后优先点击可见的 `[data-action='prompt-submit']`,不要把 Enter-only 当成稳定提交路径。关闭 OpenCode 对话类 issue 时,`web-probe script` 顶层 `ok` 不能只是“脚本没抛错”;必须断言 DOM body 出现最终 assistant 文本、没有残留 `Thinking`,并记录 EventSource 或等价事件探针看到 `message.part.updated`、`step-finish`、`session.idle`。手工 EventSource 探针可以证明 `/global/event` 消息到达,但不能替代 UI DOM 终态证据。 - web-probe 由 UniDesk CLI 从 YAML 声明的 bootstrap admin sourceRef 读取凭据并建立同源 `hwlab_session`;脚本不得自行读取、打印或复制 Web 登录凭据、cookie、token 或完整 API key。 - 需要禁用 `EventSource`、mock `Date`/clock、注入 preload hook 或修改浏览器启动前全局对象时,`page.addInitScript()` 必须在目标页面整页加载前注册,并用 `page.goto(new URL(path, origin).toString(), { waitUntil: "domcontentloaded" })` 进入目标 deep link;不要依赖 `gotoStable()` 复用当前 SPA 页面后再期待 init script 生效。若只在已加载页面上 route/block 请求,既有 SSE 或 store 状态可能继续更新,不能作为“无后端刷新依赖”的验收。 - 脚本构造 URL 时使用 `new URL(path, baseUrl).toString()`;不要拼出 `//v1/...`。 diff --git a/docs/reference/hwlab.md b/docs/reference/hwlab.md index ee4e722d..bf9fb8d8 100644 --- a/docs/reference/hwlab.md +++ b/docs/reference/hwlab.md @@ -117,9 +117,9 @@ Workbench prompt、TraceTimeline、final response、详情弹窗、session 切 HWLAB 接入 OpenCode 时,默认采用独立 `opencode-server` Pod 和独立 public hostname,通过 Cloud Web 的 `/opencode` 导航与 iframe 集成;不要把 OpenCode UI 与 HWLAB Cloud Web 合并到同一个 Pod,也不要让 public OpenCode hostname 直接暴露 OpenCode Basic Auth。Cloud Web 继续拥有 HWLAB 登录态和同源 session,OpenCode public hostname 应先经过 Cloud Web 鉴权代理;未登录访问 OpenCode host 的健康或 API 路径时,预期是 Cloud Web 的鉴权失败响应,而不是浏览器 Basic Auth 弹窗。 -OpenCode provider/model、public hostname、SecretRef 和 extra FRP proxy 都必须从 issue/CLI 选中的 node/lane YAML 与目标 HWLAB repo render 读取;长期参考不硬编码当前模型或端口。关闭 OpenCode 集成、provider profile 或微前端入口类 issue 时,最小证据应来自选中 node/lane 的 `web-probe script`:登录 HWLAB public origin,打开 `/opencode`,确认 iframe origin、OpenCode `/global/health`、`/config` 中的 provider/model、`/session` 创建,以及 `/session/:id/message` 返回的 providerID、modelID、terminal finish 和最终 assistant text。只看到 `opencode-server` Pod ready、Caddy hostname 200 或 Secret 存在,不能替代这个浏览器入口 smoke。 +OpenCode provider/model、public hostname、SecretRef 和 extra FRP proxy 都必须从 issue/CLI 选中的 node/lane YAML 与目标 HWLAB repo render 读取;长期参考不硬编码当前模型或端口。关闭 OpenCode 集成、provider profile 或微前端入口类 issue 时,最小浏览器证据优先使用选中 node/lane 的 `web-probe opencode-smoke`:登录 HWLAB public origin,打开 `/opencode`,定位 iframe/direct OpenCode origin,打开 project/composer,点击可见 submit,确认最终 assistant 文本和 EventSource 终态事件。只有需要额外核对 `/global/health`、`/config` 或底层 `/session/:id/message` payload 时,才补 `web-probe script` 做 bounded API drill-down。只看到 `opencode-server` Pod ready、Caddy hostname 200 或 Secret 存在,不能替代浏览器入口 smoke。 -排查 OpenCode 对话长时间停在 `Thinking` 或无 assistant 文本时,先拆分 provider 完成、Cloud Web 代理长连接和 UI live state 三层事实。provider 侧必须按 `opencode-provider-proxy` service 明确查 OTel,确认 `/v1/chat/completions` 状态、耗时、content chunk、reasoning-only drop 和 done line;Cloud Web 侧必须能看到 `/global/event` 的 `opencode.proxy.stream.start` span,以及 `opencode.proxy.sse.directory_rewrite_enabled`、`from=/workspace`、`to=/`、`ticket_accepted=true` 等属性。关闭证据必须包含浏览器 DOM 最终 assistant 文本、没有残留 `Thinking`,以及 EventSource 收到 `message.part.updated`、`step-finish`、`session.idle` 等事件;provider 200 或 `/session/:id/message` 返回 terminal 不能单独证明 UI 已收敛。OpenCode composer smoke 应优先点击可见的 `[data-action='prompt-submit']` 提交按钮;只依赖 Enter 在 contenteditable 状态下不稳定。 +排查 OpenCode 对话长时间停在 `Thinking` 或无 assistant 文本时,先拆分 provider 完成、Cloud Web 代理长连接和 UI live state 三层事实。provider 侧必须按 `opencode-provider-proxy` service 明确查 OTel,确认 `/v1/chat/completions` 状态、耗时、content chunk、reasoning-only drop 和 done line;Cloud Web 侧必须能看到 `/global/event` 的 `opencode.proxy.stream.start` span,以及 `opencode.proxy.sse.directory_rewrite_enabled`、`from=/workspace`、`to=/`、`ticket_accepted=true` 等属性。关闭证据必须包含 `web-probe opencode-smoke` 或等价浏览器 DOM 的最终 assistant 文本、没有残留 `Thinking`,以及 EventSource 收到 `message.part.updated`、`step-finish`、`session.idle` 等事件;provider 200 或 `/session/:id/message` 返回 terminal 不能单独证明 UI 已收敛。OpenCode composer smoke 应优先点击可见的 `[data-action='prompt-submit']` 提交按钮;只依赖 Enter 在 contenteditable 状态下不稳定。 ## HWLAB FRP 维护 diff --git a/docs/reference/observability.md b/docs/reference/observability.md index f78fff65..fe0f4e92 100644 --- a/docs/reference/observability.md +++ b/docs/reference/observability.md @@ -61,6 +61,12 @@ OA Event Flow 的高频 trace 统计不得把每个 `trace-stats-updated` 投影 CLI 写 stdout/stderr 遇到下游 pipe 关闭的 `EPIPE` 必须安静退出,不能打印 Bun stack trace。常见验证命令是 `set -o pipefail; bun scripts/cli.ts server status | head -1`,应只看到第一行 JSON 而无额外错误噪声。 +## OpenCode Trace Search + +HWLAB OpenCode 对话排障时,`platform-infra observability search --grep` 是首选入口,而不是默认手写宽泛 TraceQL。`--grep opencode.proxy.stream.start` 应推断为 span name 查询,`--grep /global/event` 应推断为 route 查询,`--grep opencode.proxy.sse.directory_rewrite_enabled=true` 应推断为属性 bool 查询;候选 trace 取回后还必须在 raw body、span name、status message、route 和 full span attributes 中做二次匹配。默认输出必须披露 `grepCoverage` 和 `grepQueryInference`,无命中时给出显式 TraceQL 与扩大 candidate/window 的下一步。 + +OpenCode provider 侧至少保留 `opencode.provider.sse.content_chunks`、`content_chars`、`output_data_lines`、`done_lines`、`json_errors` 和 `reasoning_only_choices_dropped` 等属性摘要;Cloud Web `/global/event` 侧至少保留 `opencode.proxy.stream.start`、`opencode.proxy.sse.directory_rewrite_enabled`、directory rewrite from/to、`opencode.proxy.ticket_accepted` 和 streaming 标记。OTel 只能证明链路状态,关闭 UI 卡住类问题仍必须回到 `web-probe opencode-smoke` 或等价浏览器 DOM/EventSource 终态证据。 + ## Task Liveness backend-core 必须把 queued、dispatched、running 视为待处理任务,并通过 `TASK_PENDING_TIMEOUT_MS` 对长时间没有 provider 终态回报的任务做超时处理。超时任务转为 failed,result 中保留 timeout、previousStatus 和 previousResult 摘要,避免 `态势总览` 的待处理数量长期卡住且无法解释。 diff --git a/scripts/src/hwlab-node/web-probe.ts b/scripts/src/hwlab-node/web-probe.ts index c6699dda..367239a8 100644 --- a/scripts/src/hwlab-node/web-probe.ts +++ b/scripts/src/hwlab-node/web-probe.ts @@ -2019,7 +2019,6 @@ export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions { "Passing means the browser DOM reached assistant text and the OpenCode EventSource produced update, finish and idle evidence.", ], generatedPreferredCommands: { - rerun: commandLabel, withExpectedText: `${commandLabel} --expect-text `, }, scriptSource: {