feat: upgrade investigation and presentation runtime
This commit is contained in:
+48
-87
@@ -1,106 +1,67 @@
|
||||
# Agent UI Protocol
|
||||
|
||||
## Summary
|
||||
## Controlled content and display commands
|
||||
|
||||
`agent-ui@1` is the experimental frontend protocol for Agent-driven UI. The
|
||||
Agent never returns executable frontend code. It invokes trusted opencode tools;
|
||||
the service maps those tool calls to `UIEnvelope` payloads; the frontend validates
|
||||
and renders known components, charts, and map actions.
|
||||
`answer-ui@1` describes retained answer content rendered by the frontend component catalog. Stable block_id, process/final phase and standalone `[[ui:block_id]]` anchors govern placement. Publishing or reading answer content never replays a map action.
|
||||
|
||||
## SSE Event
|
||||
Map, chart, overlay and SCADA display use a single flat command object: `{action, ...typed fields}`. Chat calls `present_view({command, reason?})` with an optional concise purpose; tasks call `publish_task_presentation({request_id, command})`; Dev JSONL wraps the same command in `{record_type:"presentation", command}`. No executable JS, JSX, HTML, CSS, URL or arbitrary request is accepted. The former chat name/params action shape and frontend-action@1 are rejected; no migration adapter is provided.
|
||||
|
||||
生成接口为 `POST /api/v1/responses`。下方示例是会话状态订阅中的 UIEnvelope;Responses 生成流将同一业务数据放入 `tjwater.ui_envelope` 事件的 `data` 中,并携带 `response_id` 和 `sequence_number`。
|
||||
The ten commands are locate_features, zoom_to_map, highlight_features, clear_highlights, open_chart, close_chart, set_overlay, clear_overlay, render_scada_analysis and clear_scada_analysis. Historical data must first be queried through an authorized MCP tool and then supplied to open_chart. Command definitions, including bounded GeoJSON, charts and target lists, are generated from the supply frontend's `src/features/agent/presentation/command-schema.ts`; the exported `src/frontendAction/command.ts` is also used with OpenCode's native Zod instance. Regenerate using `node scripts/export-view-command-contract.mjs` in the supply frontend; add `--check` to verify copies.
|
||||
|
||||
运行中追加文本使用同一生成地址,`stream=false`,携带 `conversation`、`tjwater.delivery="steer" | "queue"` 和稳定的 `tjwater.input_id`。HTTP 202 的 `tjwater.response_input` 收件回执不是新的 Response 或处理成功证明。当前 Responses SSE 保持连接,用 `tjwater.user_input` 的 `pending` / `delivered` 状态同步输入,`delivered` 表示原生收件箱已取走而非任务完成。`GET /api/v1/session/:session_id/inputs` 返回待处理消息;PATCH 其 `/:input_id` 的 `delivery` 可在处理前切换模式,处理后返回409。恢复会话流使用同样的 `user_input` 业务事件和原生待处理列表。
|
||||
## Chat transport and receipts
|
||||
|
||||
```text
|
||||
event: ui_envelope
|
||||
data: {
|
||||
"session_id": "session-id",
|
||||
"envelope_id": "env-id",
|
||||
"created_at": 1710000000000,
|
||||
"envelope": {}
|
||||
The frontend advertises `frontend-action@2`, and discovers supported actions through `GET /api/v1/frontend-action-registry`, schema_version `frontend-action-registry@2`.
|
||||
|
||||
Generation uses `POST /api/v1/responses`. A `tjwater.frontend_action` SSE event carries response_id, sequence_number and data containing:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "frontend-action@2",
|
||||
"actionId": "host-action-id",
|
||||
"toolCallId": "native-call-id",
|
||||
"sessionId": "session-id",
|
||||
"command": {"action":"highlight_features","layer":"pipes","feature_ids":["533793"],"effect":"pulse"},
|
||||
"risk": "view",
|
||||
"replayPolicy": "never",
|
||||
"issuedAt": 1710000000000,
|
||||
"expiresAt": 1710000015000
|
||||
}
|
||||
```
|
||||
|
||||
## Envelope Types
|
||||
Session subscriptions carry the same data in a `frontend_action` event. The browser validates the active session and command, executes in stream order, and submits `frontend-action-result@2` through `POST /api/v1/frontend-actions/:action_id/result`, with the scoped session header. Results contain actionId, status, output or error, and completedAt. Status is succeeded, failed, rejected, cancelled or expired. Locate/highlight output reports actual matched and missing IDs; publishing a tool call is not proof of display success. Duplicate delivery reuses the terminal receipt, not the map side effect. Session switches cancel running and queued work; expiry prevents later execution.
|
||||
|
||||
Current `agent-ui@1` support is intentionally limited to three trusted shapes:
|
||||
## Task and preview lifetimes
|
||||
|
||||
- `registered_component` for frontend-registered business panels.
|
||||
- `chart` for the `echarts-safe-subset` chart grammar.
|
||||
- `map_action` for the WebGIS map adapter.
|
||||
Task display is asynchronous: Server verifies the task/run/generation, active lease, source session, evidence ownership and idempotency key before recording `operational.presentation.requested`. A recorded result does not mean a browser displayed it; tasks progress without any connected browser. New subscriptions take a fresh event baseline and do not replay historical display commands.
|
||||
|
||||
The Agent must not return executable JS, JSX, HTML, CSS, or arbitrary component
|
||||
source. The frontend renders only allowlisted components, chart specs, and map
|
||||
actions.
|
||||
Task open_chart requires persisted evidence references. Task set_overlay can omit data; Server resolves immutable evidence into bounded GeoJSON. Supplied data must match that resolution and never overrides the stored snapshot. Chat and Dev supply resolved data for structural validation before rendering.
|
||||
|
||||
```ts
|
||||
type UISurface = "chat_inline" | "side_panel" | "canvas" | "map_overlay";
|
||||
Chat, task following and Dev previews share the frontend command dispatcher but own independent map sources, SCADA layers and chart state. Clearing or switching one owner does not clear another. Dev JSONL is an explicit local preview/import/export container, not an alternative online business protocol. Plain data records and ui_block records do not execute embedded actions. The earlier agent-ui@1 registry has no active tool mappings and is not an action entry point.
|
||||
|
||||
type UIEnvelope =
|
||||
| {
|
||||
kind: "registered_component";
|
||||
schemaVersion: "agent-ui@1";
|
||||
component: string;
|
||||
surface: UISurface;
|
||||
props: unknown;
|
||||
data?: unknown;
|
||||
fallbackText?: string;
|
||||
}
|
||||
| {
|
||||
kind: "chart";
|
||||
schemaVersion: "agent-ui@1";
|
||||
grammar: "echarts-safe-subset";
|
||||
surface: "chat_inline" | "side_panel" | "canvas";
|
||||
spec: unknown;
|
||||
data: unknown;
|
||||
fallbackText?: string;
|
||||
}
|
||||
| {
|
||||
kind: "map_action";
|
||||
schemaVersion: "agent-ui@1";
|
||||
action: string;
|
||||
surface: "map_overlay";
|
||||
params: unknown;
|
||||
fallbackText?: string;
|
||||
};
|
||||
```
|
||||
## Running and waiting input delivery
|
||||
|
||||
## Registry
|
||||
Text output has separate append, flush and close operations. Smoothing releases
|
||||
English words and bounded Chinese chunks at the configured interval (20 ms by
|
||||
default). A short tail that cannot form a regular chunk waits at most 100 ms
|
||||
after input starts or the last visible release; this deadline never dumps a
|
||||
whole queued paragraph. Text segment completion, tool transitions and
|
||||
permission/question waits drain preceding text at the same pace before the
|
||||
next event. Publishing `present_answer` also drains preceding text but leaves
|
||||
output open for later narration. Only terminal cleanup closes output.
|
||||
Cancellation immediately drains already-received text before native interruption;
|
||||
it does not manufacture a final answer. The first-token log records emission
|
||||
from this buffer, rather than input admission into it.
|
||||
|
||||
```text
|
||||
GET /api/v1/ui-registry
|
||||
```
|
||||
Ordinary chat and task collaboration share one final-answer handoff. A completed
|
||||
`final_answer` is held until all observed tools are terminal and the inbox close
|
||||
guard permits completion. The host then emits `tjwater.final_answer` exactly
|
||||
once before interrupting further native rewrites and waiting for native idle.
|
||||
Plain-text and component answers follow the same rule. Tool arguments are not
|
||||
published as partial JSON; answer content and component references must pass
|
||||
their existing validation before publication. Background `task_execution` keeps
|
||||
its native idle and Server completion checks; a chat answer never completes a
|
||||
business task.
|
||||
|
||||
The registry returns the allowed chart grammar, registered components, and map
|
||||
actions. Unknown components/actions should be rejected by the frontend.
|
||||
Running text additions use `POST /api/v1/responses` with stream=false, conversation, `tjwater.delivery="steer" | "queue"` and a stable tjwater.input_id. HTTP 202/tjwater.response_input is an inbox receipt, not execution completion. The existing SSE connection carries tjwater.user_input pending/delivered states; delivered means the native inbox took the input. `GET /api/v1/session/:session_id/inputs` lists pending input; PATCH of its /:input_id delivery can change its mode before processing and returns 409 afterward. Restored session streams retain these semantics.
|
||||
|
||||
`map_action` is a WebGIS-specific extension, not a general command channel. It
|
||||
is valid only on `map_overlay`; every action and parameter set must be validated
|
||||
by the frontend before it can affect MapLibre state. Invalid map actions should
|
||||
fall back to `fallbackText`.
|
||||
|
||||
There are currently no Agent tool mappings for charts, components, or map
|
||||
actions. The protocol types remain documented for compatibility and future
|
||||
allowlisted actions.
|
||||
|
||||
Analysis evidence must come from validated request context or allowlisted
|
||||
tools. This service does not bundle a local historical business dataset.
|
||||
|
||||
## State Persistence
|
||||
|
||||
Assistant messages may contain:
|
||||
|
||||
```ts
|
||||
uiEnvelopes?: Array<{
|
||||
envelopeId: string;
|
||||
createdAt: number;
|
||||
envelope: UIEnvelope;
|
||||
renderStatus: "pending" | "rendered" | "fallback" | "error";
|
||||
error?: string;
|
||||
}>;
|
||||
```
|
||||
|
||||
`tool_call` and `artifacts` remain deprecated compatibility/debug surfaces. New
|
||||
frontends should consume `ui_envelope` and `uiEnvelopes`.
|
||||
A pending host question or failed continuation does not disable input admission. With no active host turn, steer/queue messages enter the native inbox with resume=false and remain available for the next owned continuation. This does not answer a host question or launch inference. A lost delivery acknowledgement can be reconciled only after verifying both native consumption and completed related output. HTTP 409 with code RESPONSE_FINISHED means the input was rejected before admission because the turn has ended; the client may refresh state and send it as a new response. Other failures do not authorize automatic resubmission.
|
||||
|
||||
@@ -66,6 +66,7 @@ Agent:`bun run check`,`bun test tests/routes/executionPlan.test.ts tests/rou
|
||||
|
||||
报告采用固定字段、增量扩展,不增加格式版本标签。完整规则见
|
||||
[Server 调查报告契约](../../next-tjwater-server/docs/investigation-report-contract.md)。
|
||||
数据缺失不再保持 waiting:关键缺口导致 final/insufficient_data 正常退出;
|
||||
动作提议前先保存本轮 interim/concluded 报告,动作提议后再保存 final 报告。
|
||||
事件调查中可由用户补齐的关键输入通过 `ask_task_user` 保存问题,右侧调查报告直接展示固定问答,无需预先发布报告或生成表单。问题为 pending 时当前 run 正常结束、任务进入 waiting_human;用户在报告内提交后原任务排队新 generation,继续读取 questions 中的回答。
|
||||
只有查询与适用的人工补充均无法解决关键缺口、且无法形成必要判断或具体建议时,才以 final/insufficient_data 正常退出。计算等待、人工等待与执行故障按调查指令分别处理。
|
||||
动作提议前先保存本轮 interim/concluded 报告,动作提议后继续必要分析,满足停止条件再保存 final 报告。
|
||||
数据授权由系统配置,本场景不请求人工数据授权。
|
||||
|
||||
Reference in New Issue
Block a user