From 28cb4ca596e14cc6a937acd7cf3ab5a3b8fb5f78 Mon Sep 17 00:00:00 2001 From: mindfn Date: Mon, 9 Mar 2026 19:31:26 +0800 Subject: [PATCH 1/2] docs: restore governed message pipeline intent record Carry the docs-only governance baseline from the rich-media message delivery branch into a standalone intent PR. This archives the completed message-related feature records under docs/features/archive and openspec/changes/archive, restores the active feature index to the currently governed entries, and adds a new active rich-media message pipeline aggregation record for PR #204. The new active feature record intentionally stays at the intent stage for now; a follow-up commit will replace the placeholder Intent PR / review links with the real docs-only PR URLs before merge so governance-evidence-truth and governance-intent-gate can validate the implementation PR chain. --- docs/design/Architecture.md | 2 +- docs/design/Interfaces.md | 38 +++- .../modules/agent/DareAgent_Detailed.md | 3 +- docs/design/modules/agent/README.md | 8 +- .../modules/agent/ReactAgent_Detailed.md | 3 +- .../modules/agent/SimpleChatAgent_Detailed.md | 5 +- docs/design/modules/context/README.md | 164 ++++++++++++---- docs/design/modules/model/README.md | 56 +++++- docs/design/modules/transport/README.md | 175 +++++++++++++++--- docs/features/README.md | 8 +- docs/features/archive/README.md | 10 + .../add-anthropic-model-adapter.md | 2 +- .../agentscope-d1-d3-message-pipeline.md | 113 +++++++++++ .../agentscope-d2-d4-thinking-transport.md | 2 +- .../agentscope-d5-safe-compression.md | 2 +- .../agentscope-d7-plan-state-tools.md | 2 +- .../{ => archive}/client-session-resume.md | 2 +- .../archive/message-input-boundary-cleanup.md | 59 ++++++ .../{ => archive}/p0-default-eventlog.md | 2 +- .../{ => archive}/p0-step-driven-execution.md | 2 +- .../transport-typed-payload-cutover.md | 103 +++++++++++ docs/features/rich-media-message-pipeline.md | 85 +++++++++ docs/guides/Tool_Approval_Memory.md | 14 +- .../agentscope_domain_execution_todos.md | 92 +++++---- docs/todos/project_overall_todos.md | 6 +- .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/anthropic-model-adapter/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/core-runtime/spec.md | 0 .../specs/define-trust-boundary/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 2 + .../design.md | 171 +++++++++++++++++ .../proposal.md | 46 +++++ .../specs/chat-runtime/spec.md | 40 ++++ .../specs/interaction-dispatch/spec.md | 27 +++ .../specs/rich-media-message-schema/spec.md | 32 ++++ .../specs/transport-channel/spec.md | 68 +++++++ .../tasks.md | 27 +++ .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../agentscope-thinking-transport/spec.md | 0 .../specs/chat-runtime/spec.md | 0 .../specs/transport-channel/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/agentscope-safe-compression/spec.md | 0 .../specs/chat-runtime/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/chat-runtime/spec.md | 0 .../specs/plan-runtime/spec.md | 0 .../tasks.md | 0 .../design.md | 0 .../proposal.md | 0 .../specs/client-host-orchestration/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 2 + .../design.md | 60 ++++++ .../proposal.md | 28 +++ .../specs/plan-module/spec.md | 14 ++ .../specs/rich-media-message-schema/spec.md | 13 ++ .../specs/session-loop/spec.md | 13 ++ .../specs/transport-channel/spec.md | 13 ++ .../tasks.md | 5 + .../.openspec.yaml | 0 .../2026-03-09-p0-default-eventlog}/design.md | 0 .../proposal.md | 0 .../specs/core-runtime/spec.md | 0 .../specs/default-event-log/spec.md | 0 .../specs/observability/spec.md | 0 .../specs/session-loop/spec.md | 0 .../2026-03-09-p0-default-eventlog}/tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/core-runtime/spec.md | 0 .../specs/define-trust-boundary/spec.md | 0 .../specs/security-policy-gate/spec.md | 0 .../specs/validation/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/core-runtime/spec.md | 0 .../specs/plan-module/spec.md | 0 .../specs/session-loop/spec.md | 0 .../specs/step-driven-execution/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 2 + .../design.md | 107 +++++++++++ .../proposal.md | 43 +++++ .../specs/chat-runtime/spec.md | 19 ++ .../specs/interaction-dispatch/spec.md | 19 ++ .../specs/transport-channel/spec.md | 20 ++ .../specs/typed-transport-replies/spec.md | 24 +++ .../tasks.md | 19 ++ .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/core-runtime/spec.md | 0 .../tasks.md | 0 openspec/specs/chat-runtime/spec.md | 28 ++- openspec/specs/interaction-dispatch/spec.md | 67 ++++++- .../specs/rich-media-message-schema/spec.md | 37 ++++ openspec/specs/transport-channel/spec.md | 50 +++-- .../specs/typed-transport-replies/spec.md | 29 +++ 117 files changed, 1817 insertions(+), 166 deletions(-) rename docs/features/{ => archive}/add-anthropic-model-adapter.md (99%) create mode 100644 docs/features/archive/agentscope-d1-d3-message-pipeline.md rename docs/features/{ => archive}/agentscope-d2-d4-thinking-transport.md (99%) rename docs/features/{ => archive}/agentscope-d5-safe-compression.md (99%) rename docs/features/{ => archive}/agentscope-d7-plan-state-tools.md (99%) rename docs/features/{ => archive}/client-session-resume.md (99%) create mode 100644 docs/features/archive/message-input-boundary-cleanup.md rename docs/features/{ => archive}/p0-default-eventlog.md (99%) rename docs/features/{ => archive}/p0-step-driven-execution.md (99%) create mode 100644 docs/features/archive/transport-typed-payload-cutover.md create mode 100644 docs/features/rich-media-message-pipeline.md rename openspec/changes/{add-anthropic-model-adapter => archive/2026-03-09-add-anthropic-model-adapter}/.openspec.yaml (100%) rename openspec/changes/{add-anthropic-model-adapter => archive/2026-03-09-add-anthropic-model-adapter}/design.md (100%) rename openspec/changes/{add-anthropic-model-adapter => archive/2026-03-09-add-anthropic-model-adapter}/proposal.md (100%) rename openspec/changes/{add-anthropic-model-adapter => archive/2026-03-09-add-anthropic-model-adapter}/specs/anthropic-model-adapter/spec.md (100%) rename openspec/changes/{add-anthropic-model-adapter => archive/2026-03-09-add-anthropic-model-adapter}/tasks.md (100%) rename openspec/changes/{add-security-boundary-policy-gate => archive/2026-03-09-add-security-boundary-policy-gate}/.openspec.yaml (100%) rename openspec/changes/{add-security-boundary-policy-gate => archive/2026-03-09-add-security-boundary-policy-gate}/design.md (100%) rename openspec/changes/{add-security-boundary-policy-gate => archive/2026-03-09-add-security-boundary-policy-gate}/proposal.md (100%) rename openspec/changes/{add-security-boundary-policy-gate => archive/2026-03-09-add-security-boundary-policy-gate}/specs/core-runtime/spec.md (100%) rename openspec/changes/{add-security-boundary-policy-gate => archive/2026-03-09-add-security-boundary-policy-gate}/specs/define-trust-boundary/spec.md (100%) rename openspec/changes/{add-security-boundary-policy-gate => archive/2026-03-09-add-security-boundary-policy-gate}/tasks.md (100%) create mode 100644 openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/.openspec.yaml create mode 100644 openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/design.md create mode 100644 openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/proposal.md create mode 100644 openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/chat-runtime/spec.md create mode 100644 openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/interaction-dispatch/spec.md create mode 100644 openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/rich-media-message-schema/spec.md create mode 100644 openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/transport-channel/spec.md create mode 100644 openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/tasks.md rename openspec/changes/{agentscope-d2-d4-thinking-transport => archive/2026-03-09-agentscope-d2-d4-thinking-transport}/.openspec.yaml (100%) rename openspec/changes/{agentscope-d2-d4-thinking-transport => archive/2026-03-09-agentscope-d2-d4-thinking-transport}/design.md (100%) rename openspec/changes/{agentscope-d2-d4-thinking-transport => archive/2026-03-09-agentscope-d2-d4-thinking-transport}/proposal.md (100%) rename openspec/changes/{agentscope-d2-d4-thinking-transport => archive/2026-03-09-agentscope-d2-d4-thinking-transport}/specs/agentscope-thinking-transport/spec.md (100%) rename openspec/changes/{agentscope-d2-d4-thinking-transport => archive/2026-03-09-agentscope-d2-d4-thinking-transport}/specs/chat-runtime/spec.md (100%) rename openspec/changes/{agentscope-d2-d4-thinking-transport => archive/2026-03-09-agentscope-d2-d4-thinking-transport}/specs/transport-channel/spec.md (100%) rename openspec/changes/{agentscope-d2-d4-thinking-transport => archive/2026-03-09-agentscope-d2-d4-thinking-transport}/tasks.md (100%) rename openspec/changes/{agentscope-d5-safe-compression => archive/2026-03-09-agentscope-d5-safe-compression}/.openspec.yaml (100%) rename openspec/changes/{agentscope-d5-safe-compression => archive/2026-03-09-agentscope-d5-safe-compression}/design.md (100%) rename openspec/changes/{agentscope-d5-safe-compression => archive/2026-03-09-agentscope-d5-safe-compression}/proposal.md (100%) rename openspec/changes/{agentscope-d5-safe-compression => archive/2026-03-09-agentscope-d5-safe-compression}/specs/agentscope-safe-compression/spec.md (100%) rename openspec/changes/{agentscope-d5-safe-compression => archive/2026-03-09-agentscope-d5-safe-compression}/specs/chat-runtime/spec.md (100%) rename openspec/changes/{agentscope-d5-safe-compression => archive/2026-03-09-agentscope-d5-safe-compression}/tasks.md (100%) rename openspec/changes/{agentscope-d7-plan-state-tools => archive/2026-03-09-agentscope-d7-plan-state-tools}/.openspec.yaml (100%) rename openspec/changes/{agentscope-d7-plan-state-tools => archive/2026-03-09-agentscope-d7-plan-state-tools}/design.md (100%) rename openspec/changes/{agentscope-d7-plan-state-tools => archive/2026-03-09-agentscope-d7-plan-state-tools}/proposal.md (100%) rename openspec/changes/{agentscope-d7-plan-state-tools => archive/2026-03-09-agentscope-d7-plan-state-tools}/specs/chat-runtime/spec.md (100%) rename openspec/changes/{agentscope-d7-plan-state-tools => archive/2026-03-09-agentscope-d7-plan-state-tools}/specs/plan-runtime/spec.md (100%) rename openspec/changes/{agentscope-d7-plan-state-tools => archive/2026-03-09-agentscope-d7-plan-state-tools}/tasks.md (100%) rename openspec/changes/{client-session-resume => archive/2026-03-09-client-session-resume}/design.md (100%) rename openspec/changes/{client-session-resume => archive/2026-03-09-client-session-resume}/proposal.md (100%) rename openspec/changes/{client-session-resume => archive/2026-03-09-client-session-resume}/specs/client-host-orchestration/spec.md (100%) rename openspec/changes/{client-session-resume => archive/2026-03-09-client-session-resume}/tasks.md (100%) create mode 100644 openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/.openspec.yaml create mode 100644 openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/design.md create mode 100644 openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/proposal.md create mode 100644 openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/plan-module/spec.md create mode 100644 openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/rich-media-message-schema/spec.md create mode 100644 openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/session-loop/spec.md create mode 100644 openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/transport-channel/spec.md create mode 100644 openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/tasks.md rename openspec/changes/{p0-default-eventlog => archive/2026-03-09-p0-default-eventlog}/.openspec.yaml (100%) rename openspec/changes/{p0-default-eventlog => archive/2026-03-09-p0-default-eventlog}/design.md (100%) rename openspec/changes/{p0-default-eventlog => archive/2026-03-09-p0-default-eventlog}/proposal.md (100%) rename openspec/changes/{p0-default-eventlog => archive/2026-03-09-p0-default-eventlog}/specs/core-runtime/spec.md (100%) rename openspec/changes/{p0-default-eventlog => archive/2026-03-09-p0-default-eventlog}/specs/default-event-log/spec.md (100%) rename openspec/changes/{p0-default-eventlog => archive/2026-03-09-p0-default-eventlog}/specs/observability/spec.md (100%) rename openspec/changes/{p0-default-eventlog => archive/2026-03-09-p0-default-eventlog}/specs/session-loop/spec.md (100%) rename openspec/changes/{p0-default-eventlog => archive/2026-03-09-p0-default-eventlog}/tasks.md (100%) rename openspec/changes/{p0-enforce-security-boundary => archive/2026-03-09-p0-enforce-security-boundary}/.openspec.yaml (100%) rename openspec/changes/{p0-enforce-security-boundary => archive/2026-03-09-p0-enforce-security-boundary}/design.md (100%) rename openspec/changes/{p0-enforce-security-boundary => archive/2026-03-09-p0-enforce-security-boundary}/proposal.md (100%) rename openspec/changes/{p0-enforce-security-boundary => archive/2026-03-09-p0-enforce-security-boundary}/specs/core-runtime/spec.md (100%) rename openspec/changes/{p0-enforce-security-boundary => archive/2026-03-09-p0-enforce-security-boundary}/specs/define-trust-boundary/spec.md (100%) rename openspec/changes/{p0-enforce-security-boundary => archive/2026-03-09-p0-enforce-security-boundary}/specs/security-policy-gate/spec.md (100%) rename openspec/changes/{p0-enforce-security-boundary => archive/2026-03-09-p0-enforce-security-boundary}/specs/validation/spec.md (100%) rename openspec/changes/{p0-enforce-security-boundary => archive/2026-03-09-p0-enforce-security-boundary}/tasks.md (100%) rename openspec/changes/{p0-step-driven-execution => archive/2026-03-09-p0-step-driven-execution}/.openspec.yaml (100%) rename openspec/changes/{p0-step-driven-execution => archive/2026-03-09-p0-step-driven-execution}/design.md (100%) rename openspec/changes/{p0-step-driven-execution => archive/2026-03-09-p0-step-driven-execution}/proposal.md (100%) rename openspec/changes/{p0-step-driven-execution => archive/2026-03-09-p0-step-driven-execution}/specs/core-runtime/spec.md (100%) rename openspec/changes/{p0-step-driven-execution => archive/2026-03-09-p0-step-driven-execution}/specs/plan-module/spec.md (100%) rename openspec/changes/{p0-step-driven-execution => archive/2026-03-09-p0-step-driven-execution}/specs/session-loop/spec.md (100%) rename openspec/changes/{p0-step-driven-execution => archive/2026-03-09-p0-step-driven-execution}/specs/step-driven-execution/spec.md (100%) rename openspec/changes/{p0-step-driven-execution => archive/2026-03-09-p0-step-driven-execution}/tasks.md (100%) create mode 100644 openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/.openspec.yaml create mode 100644 openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/design.md create mode 100644 openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/proposal.md create mode 100644 openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/chat-runtime/spec.md create mode 100644 openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/interaction-dispatch/spec.md create mode 100644 openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/transport-channel/spec.md create mode 100644 openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/typed-transport-replies/spec.md create mode 100644 openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/tasks.md rename openspec/changes/{unify-agent-output-envelope => archive/2026-03-09-unify-agent-output-envelope}/.openspec.yaml (100%) rename openspec/changes/{unify-agent-output-envelope => archive/2026-03-09-unify-agent-output-envelope}/design.md (100%) rename openspec/changes/{unify-agent-output-envelope => archive/2026-03-09-unify-agent-output-envelope}/proposal.md (100%) rename openspec/changes/{unify-agent-output-envelope => archive/2026-03-09-unify-agent-output-envelope}/specs/core-runtime/spec.md (100%) rename openspec/changes/{unify-agent-output-envelope => archive/2026-03-09-unify-agent-output-envelope}/tasks.md (100%) create mode 100644 openspec/specs/rich-media-message-schema/spec.md create mode 100644 openspec/specs/typed-transport-replies/spec.md diff --git a/docs/design/Architecture.md b/docs/design/Architecture.md index a8591be3..8d200f1b 100644 --- a/docs/design/Architecture.md +++ b/docs/design/Architecture.md @@ -412,7 +412,7 @@ sequenceDiagram ```python class IAgent(Protocol): - async def run(self, task: "str | Task", deps: Any | None = None) -> "RunResult": ... + async def __call__(self, task: "str | Message", deps: Any | None = None) -> "RunResult": ... class IContext(Protocol): def stm_add(self, message: "Message") -> None: ... diff --git a/docs/design/Interfaces.md b/docs/design/Interfaces.md index d2e5f479..9e23e3d9 100644 --- a/docs/design/Interfaces.md +++ b/docs/design/Interfaces.md @@ -33,10 +33,22 @@ dare_framework// ### 1.1 上下文契约(context/types.py) - `Message` - - `role: str` - - `content: str` + - framework 内部唯一 canonical message + - `id: str | None` + - `role: MessageRole` + - `kind: MessageKind` + - `text: str | None` + - `attachments: list[AttachmentRef]` + - `data: dict[str, Any] | None` - `name: str | None` - `metadata: dict[str, Any]` + - `mark: MessageMark` +- `AttachmentRef` + - `kind: AttachmentKind`(首版 `IMAGE`) + - `uri: str` + - `mime_type: str | None` + - `filename: str | None` + - `metadata: dict[str, Any]` - `Budget` - limits: `max_tokens/max_cost/max_time_seconds/max_tool_calls` - usage: `used_tokens/used_cost/used_time_seconds/used_tool_calls` @@ -49,6 +61,8 @@ dare_framework// ### 1.2 计划与运行结果契约(plan/types.py) - `Task`: `description/task_id/milestones/metadata/previous_session_summary` + - `input_message: Message | None` + - `description` 作为编排输入保留,不再视为真实消息载体 - `Milestone`: `milestone_id/description/user_input/success_criteria` - `ProposedPlan` vs `ValidatedPlan` - `Envelope`: `allowed_capability_ids/budget/done_predicate/risk_level` @@ -71,7 +85,17 @@ dare_framework// - `TransportEnvelope` - `id/reply_to/kind/payload/meta/stream_id/seq` - `EnvelopeKind` - - `MESSAGE/ACTION/CONTROL` + - `MESSAGE/SELECT/ACTION/CONTROL` +- `EnvelopePayload` + - 公共字段:`id/metadata` +- `MessagePayload` + - `id/metadata/role(MessageRole)/message_kind(MessageKind)/text/attachments/data` +- `SelectPayload` + - `id/metadata/select_kind(SelectKind)/select_domain(SelectDomain)/prompt/options/selected` +- `ActionPayload` + - `id/metadata/resource_action/params` +- `ControlPayload` + - `id/metadata/control_id/params` --- @@ -83,7 +107,7 @@ dare_framework// class IAgent(ABC): async def __call__( self, - message: str | Task, + message: str | Message, deps: Any | None = None, *, transport: AgentChannel | None = None, @@ -104,7 +128,7 @@ class IAgent(ABC): class IAgentOrchestration(ABC): async def execute( self, - task: str | Task, + task: Message, *, transport: AgentChannel | None = None, ) -> RunResult: ... @@ -113,7 +137,8 @@ class IAgentOrchestration(ABC): ### 2.3 关键说明 - 统一入口是 `__call__`,而非历史文档中的 `run(...)`。 -- `Task` 与 `str` 并存:支持 simple/react/five-layer 统一调用面。 +- `Message` 是首选直接输入;`Task` 仅用于编排。 +- Public agent input is `Message` (or plain `str` normalized into `Message`); `Task.input_message` 保存 canonical 首轮用户消息,`Task.description` 仅保留给内部规划/里程碑描述。 --- @@ -158,6 +183,7 @@ class IContext(ABC): def list_tools(self) -> list[CapabilityDescriptor]: ... def assemble(self) -> AssembledContext: ... def compress(self, **options: Any) -> None: ... + ``` --- diff --git a/docs/design/modules/agent/DareAgent_Detailed.md b/docs/design/modules/agent/DareAgent_Detailed.md index c8a58d59..1b22d233 100644 --- a/docs/design/modules/agent/DareAgent_Detailed.md +++ b/docs/design/modules/agent/DareAgent_Detailed.md @@ -40,7 +40,8 @@ 输入: -- `str | Task` +- `str | Message` +- Public input 先归一为 canonical `Message`;`Task.input_message` 保存内部会话首轮用户消息 - `Task.task_id` 缺失时在执行入口补齐 输出: diff --git a/docs/design/modules/agent/README.md b/docs/design/modules/agent/README.md index 98071c84..36c1c9c1 100644 --- a/docs/design/modules/agent/README.md +++ b/docs/design/modules/agent/README.md @@ -30,8 +30,8 @@ ## 4. 对外接口(Public Contract) -- 统一入口:`IAgent.__call__(task, transport=None)` -- 编排入口:`IAgentOrchestration.execute(task, transport)` +- 统一入口:`IAgent.__call__(message, transport=None)`,其中 public input 支持 `str | Message` +- 编排入口:`IAgentOrchestration.execute(message, transport)`,内部统一接收 canonical `Message` - 结果契约:统一输出 `RunResult`(含 `output_text`),其中 `RunResult.output` 使用 envelope 结构: - `content: str` - `metadata: dict` @@ -44,7 +44,9 @@ ## 5. 核心字段(Core Fields) -- 统一任务输入:`Task`(`description/task_id/milestones/metadata`) +- 统一顶层输入:`str | Message` +- `Message`:首选直接用户输入 +- `Task`:内部编排对象(`description/task_id/milestones/metadata/input_message`),不再作为 public agent input - 统一运行输出:`RunResult`(`success/output/output_text/errors/metadata`) - `output.content`:统一可展示文本 - `output_text`:与 `output.content` 对齐,供展示与兼容链路消费 diff --git a/docs/design/modules/agent/ReactAgent_Detailed.md b/docs/design/modules/agent/ReactAgent_Detailed.md index ef4b3c97..37c6e5bd 100644 --- a/docs/design/modules/agent/ReactAgent_Detailed.md +++ b/docs/design/modules/agent/ReactAgent_Detailed.md @@ -33,7 +33,8 @@ 输入: -- `str | Task` +- `str | Message` +- `Message` 是首选直接输入;`Task` 仅作为编排对象 输出: diff --git a/docs/design/modules/agent/SimpleChatAgent_Detailed.md b/docs/design/modules/agent/SimpleChatAgent_Detailed.md index 1157248d..5e4cdcb0 100644 --- a/docs/design/modules/agent/SimpleChatAgent_Detailed.md +++ b/docs/design/modules/agent/SimpleChatAgent_Detailed.md @@ -33,8 +33,9 @@ 输入: -- `str | Task` -- `str` 在入口处转换为 `Task.description` +- `str | Message` +- `Message` 是首选直接输入;`str` 会升级为 canonical `Message` +- `Task` 仅用于编排,真实首轮用户消息来自 `Task.input_message`(若为空则退化为 `description`) 输出: diff --git a/docs/design/modules/context/README.md b/docs/design/modules/context/README.md index 2ec5968d..0122bded 100644 --- a/docs/design/modules/context/README.md +++ b/docs/design/modules/context/README.md @@ -1,12 +1,13 @@ # Module: context -> Status: baseline fusion behavior landed in canonical runtime (2026-02-27). +> Status: canonical message redesign baseline updated for rich-media inputs (2026-03-09). > 本文分两层:先说明当前实现(as-is),再给出目标态详细设计(to-be)。 ## 1. 定位与职责 - Context-centric:`Context` 是会话态中心,负责把多来源信息组装成一次模型调用可消费的输入。 - 统一持有引用:`short_term_memory`、`long_term_memory`、`knowledge`、`budget`、`tool provider`、`sys_prompt`。 +- `Message` 是 framework 内部唯一 canonical message;所有会话语义最终都归一到该类型。 - 输出 `AssembledContext(messages, sys_prompt, tools, metadata)`,作为 Agent 进入 Model 的唯一上下文入口。 ## 2. 当前实现基线(as-is) @@ -26,11 +27,91 @@ ### 2.2 现状限制 +- 旧的 `Message.content: str` 路径已经切除,但仍需确保所有运行时入口使用 canonical `Message` 而非重新退化到 `Task.description`。 - 预算控制依赖轻量 token 估算(字符启发式),与模型真实 tokenizer 仍可能存在偏差。 - 当前默认策略未实现高级检索重排与分级压缩(摘要折叠、去重重排等)。 - `retrieval` metadata 已提供基础字段,但跨模块 taxonomy 仍需统一。 -## 3. 关键对象与分层语义 +## 3. Canonical Message 设计(to-be) + +### 3.1 设计原则 + +- 不重新定义平行的 framework message 类型;直接升级现有 `context.types.Message`。 +- transport `kind=message` 的 payload 应与 canonical `Message` 高度对齐,允许 1:1 归一化。 +- provider-specific message 只存在于 adapter 内部,不进入 context/history。 + +### 3.2 `Message` 建议字段 + +建议将当前: + +- `role` +- `content` +- `name` +- `metadata` +- `mark` +- `id` + +升级为: + +- `id` +- `role` +- - 使用确定性枚举 `MessageRole` +- - `SYSTEM | USER | ASSISTANT | TOOL` +- `kind` + - 使用确定性枚举 `MessageKind` + - `CHAT | THINKING | TOOL_CALL | TOOL_RESULT | SUMMARY` +- `text: str | None` +- `attachments: list[AttachmentRef]` +- `data: dict[str, Any] | None` +- `name: str | None` +- `metadata: dict[str, Any]` +- `mark` + +### 3.3 字段语义 + +- `text` + - 通用文本表达。 +- `attachments` + - 通用附件表达;首版只要求 `image`。 + - 附件项必须是强类型 `AttachmentRef`,不再使用裸 `dict`。 + - 当前字段校验矩阵下,仅 `chat` 与 `tool_result` 允许携带附件;`thinking`、`summary`、`tool_call` 直接拒绝附件。 +- `data` + - 仅承载结构化主语义: + - `tool_call` 参数 + - `tool_result` 结构化结果 + - 后续需要的 summary provenance 等 + - 当前字段校验矩阵下,`tool_call` 与 `tool_result` 都要求 `data` 非空。 + - `tool_call/tool_result` 的结构化语义以 `data` 为唯一真相源;`metadata` 不再承担语义回退职责。 +- `name` + - 兼容 tool/message 现有命名约定;后续可逐步被 `data` 中的结构化 id 替代。 + +### 3.4 组合消息表达 + +当前目标场景“一个用户输入 = 一段文字 + 多张图片”直接使用: + +- `kind=chat` +- `text != None` +- `attachments = [image1, image2, ...]` + +不引入额外的 `combo` / `multimodal_chat` 类型。 + +### 3.5 `AttachmentRef` + +建议最小字段: + +- `kind: AttachmentKind` + - 首版固定 `IMAGE` +- `uri` +- `mime_type?` +- `filename?` +- `metadata?` + +说明: + +- `AttachmentRef` 应作为 framework 内部唯一附件引用结构,同时被 canonical `Message` 与 transport `MessagePayload` 复用。 +- 有限域优先使用枚举;只有 `metadata/data` 这类开放扩展位保留弱结构。 + +## 4. 关键对象与分层语义 - `STM (short_term_memory)`: - 会话工作集,保存最近 user/assistant/tool 交互。 @@ -38,20 +119,21 @@ - `LTM (long_term_memory)`: - 跨会话持久记忆,优先存“可复用且会影响未来决策”的事实和经验。 - 通过 `get(query, top_k, ...)` 按需召回,不应全量塞回上下文。 + - 持久化时必须保存 canonical `Message` 的 `role/kind/text/attachments/data/name/metadata/mark/id`;仅检索索引文本允许退化为占位。 - `Knowledge`: - 外部知识源(文档、索引、向量库),优先存“外部事实依据”。 - 与 LTM 区分:LTM 偏“代理自身历史”,Knowledge 偏“外部知识语料”。 -## 4. 详细设计:组装流水线(to-be) +## 5. 详细设计:组装流水线(to-be) -### 4.1 组装目标 +### 5.1 组装目标 在固定 token 预算内,优先保证: 1. 当前轮任务可执行(最近指令、未完成约束、关键工具结果不丢)。 2. 与当前任务强相关的历史与知识被召回。 3. 可追溯(知道每条上下文来自 STM/LTM/Knowledge,以及如何被压缩)。 -### 4.2 组装流程(建议) +### 5.2 组装流程(建议) ```mermaid flowchart TD @@ -70,9 +152,9 @@ flowchart TD K --> J ``` -### 4.3 统一候选消息模型(设计约定) +### 5.3 统一候选消息模型(设计约定) -建议在 `assemble()` 内部把所有来源先归一化为 `Message + metadata`,metadata 至少包含: +建议在 `assemble()` 内部把所有来源先归一化为 canonical `Message + metadata`,metadata 至少包含: - `source`: `stm|ltm|knowledge|summary` - `score`: 相关性综合分(用于重排和裁剪) @@ -80,9 +162,9 @@ flowchart TD - `retrieval_query`: 本次召回 query(非 STM 可选) - `compression_flags`: 是否经过摘要/裁剪/去重 -这一步不改变 `Message` 主结构,只扩展 metadata,兼容现有接口。 +这一步不再依赖 `Message.content: str`;输入必须先提升到新的 canonical `Message` 结构。 -### 4.4 预算分配(建议) +### 5.4 预算分配(建议) 单次组装可采用“分桶预算”而不是全局抢占: @@ -95,7 +177,7 @@ flowchart TD - 工具执行密集场景:提高 STM 桶; - 问答/RAG 场景:提高 Knowledge 桶。 -### 4.5 融合排序(建议) +### 5.5 融合排序(建议) 融合后按综合分排序,示例公式: @@ -106,9 +188,9 @@ flowchart TD - `source_priority`: 可配置(例如安全策略要求 Knowledge 证据优先)。 - `role_priority`: 系统指令、用户硬约束高于普通 assistant 叙述。 -## 5. 详细设计:压缩策略(to-be) +## 6. 详细设计:压缩策略(to-be) -### 5.1 触发点 +### 6.1 触发点 建议至少在以下时机触发压缩检查: @@ -116,7 +198,7 @@ flowchart TD 2. tool loop 每轮后:工具输出可能导致上下文骤增。 3. milestone 结束后:阶段性清理并抽取摘要,避免跨阶段污染。 -### 5.2 分级压缩管线 +### 6.2 分级压缩管线 压缩不建议一步到位,采用由轻到重的级联策略: @@ -126,7 +208,7 @@ flowchart TD 4. `summary_fold`: 把旧消息折叠为摘要消息(增量摘要优先)。 5. `hard_truncate`: 兜底末尾截断(保证绝不超预算)。 -### 5.3 摘要压缩约定 +### 6.3 摘要压缩约定 - `rolling summary`:维护一条“会话运行摘要”消息,定期合并历史片段。 - `segment summary`:对旧 tool 结果或旧对话片段生成局部摘要并替换原文。 @@ -135,22 +217,22 @@ flowchart TD - `summary_version`: 摘要迭代版本; - `loss_note`: 明确哪些细节被省略。 -### 5.4 与现实现的关系 +### 6.4 与现实现的关系 - 当前实现仅覆盖第 5 级(`hard_truncate` 的简化版,且只作用 STM)。 - 其余级别属于上下文工程增强项,可通过覆盖 `Context.compress()` 分阶段接入。 -## 6. STM/LTM/Knowledge 使用策略(to-be) +## 7. STM/LTM/Knowledge 使用策略(to-be) -### 6.1 STM 使用策略 +### 7.1 STM 使用策略 -- 写入:user、assistant、tool message 全量进入 STM。 +- 写入:user、assistant、tool、thinking、summary 等 canonical `Message` 全量进入 STM。 - 常驻保留(pin): - 当前任务目标与硬约束; - 最近一次失败原因和 remediation 建议; - 最新关键工具输出摘要(非原始大块日志)。 -### 6.2 LTM 使用策略 +### 7.2 LTM 使用策略 - 写入候选(建议由 milestone 完成或 run 结束触发): - 用户偏好(格式、语言、禁忌); @@ -160,14 +242,14 @@ flowchart TD - 用当前任务 + 最近用户输入构造 query; - `top_k` 小批量召回(通常 3~10),经重排后再进入上下文。 -### 6.3 Knowledge 使用策略 +### 7.3 Knowledge 使用策略 - 写入:仅通过显式知识摄入路径(如 `knowledge_add` 工具),避免无约束污染。 - 召回: - 面向“事实查询/文档依据”; - 与 LTM 分开打分,避免个人历史记忆覆盖外部事实证据。 -## 7. 审计与可观测性(to-be) +## 8. 审计与可观测性(to-be) 建议扩展 `AssembledContext.metadata` 最小字段: @@ -186,7 +268,7 @@ flowchart TD 这些字段可直接支撑 EventLog 与 Hook 的上下文审计。 -## 8. 与 Agent 循环的落点 +## 9. 与 Agent 循环的落点 - `SimpleChatAgent` / `ReactAgent` / `DareAgent` 当前都直接调用 `context.assemble()`。 - 推荐统一落点:在每次模型调用前执行: @@ -196,20 +278,21 @@ flowchart TD 这样压缩策略可在 Context 域集中治理,不分散到各 Agent。 -## 9. 关键接口与实现 +## 10. 关键接口与实现 - Kernel:`dare_framework/context/kernel.py` - `IRetrievalContext` - `IContext` - Types:`dare_framework/context/types.py` - `Message` + - `AttachmentRef` - `Budget` - `AssembledContext` - Default Impl:`dare_framework/context/context.py` - `Context.assemble()` - `Context.compress()` -## 10. 扩展点 +## 11. 扩展点 - 自定义 Context: - 覆盖 `assemble()` 实现多源检索融合; @@ -219,22 +302,30 @@ flowchart TD - 自定义压缩策略: - 保持 `compress(**options)` 入口稳定,按 `strategy`/`budget` 扩展行为。 -## 11. TODO / 未决问题 +## 12. TODO / 未决问题 - TODO: 为 `assemble()` 定义标准化 options(`query/top_k/budget_alloc/compression_hint`)。 - TODO: 规范 `AssembledContext.metadata` 审计字段与哈希计算口径。 - TODO: 在默认 Agent 调用链接入“预算检查 -> 压缩 -> 组装”的统一顺序。 - TODO: 明确 LTM 与 Knowledge 的冲突消解与证据优先级规则。 +- 已落地最小校验矩阵: + - `chat`: `text/attachments/data?` + - `thinking`: `text` only + - `summary`: `text` only + - `tool_call`: `text + data` + - `tool_result`: `text + data + attachments?` -## 12. Design Clarifications (2026-02-05) +## 13. Design Clarifications (2026-02-05 / 2026-03-09) - `Context.config` 语义是只读快照,不提供运行时增量更新接口。 - Skill 注入路径是 `assemble()` 时动态 enrich `sys_prompt`,不是单独消息注入。 - 目前 `IContext` 已暴露 `compress()`,但默认实现仍是 STM 截断,属于最小实现而非完整压缩框架。 +- `Message` 是 framework 内部唯一 canonical message;`TransportEnvelope(kind=message)` 的 payload 只是其协议承载形式。 +- rich-media 首版只要求 `chat` 消息支持 `text + images[]`,不支持图文混排顺序表达。 -## 13. Memory / Knowledge 集成实现细节(merged) +## 14. Memory / Knowledge 集成实现细节(merged) -### 13.1 当前实现能力(as-is) +### 14.1 当前实现能力(as-is) - STM: - 默认 `InMemorySTM`; @@ -249,7 +340,7 @@ flowchart TD - 支持 `rawdata` / `vector`; - 存储支持 `in_memory` / `sqlite` / `chromadb`(vector)。 -### 13.2 组装时的检索参数约定(to-be) +### 14.2 组装时的检索参数约定(to-be) `Context.assemble()` 建议向 LTM/Knowledge 统一透传: @@ -260,13 +351,13 @@ flowchart TD 目标是让 `IRetrievalContext.get(query="", **kwargs)` 在跨实现时保持可预测行为。 -### 13.3 写入策略约定(to-be) +### 14.3 写入策略约定(to-be) - STM:每轮 user/assistant/tool 在线写入。 - LTM:里程碑完成、run 结束或显式记忆操作时批量持久化。 - Knowledge:仅允许显式摄入(如 `knowledge_add`)避免隐式污染知识库。 -### 13.4 Metadata 打标约定(to-be) +### 14.4 Metadata 打标约定(to-be) 融合后建议在 `Message.metadata` 统一记录: @@ -275,14 +366,14 @@ flowchart TD - `memory_class`: `working|episodic|factual` - `session_id` / `timestamp`(可用时) -### 13.5 增补 TODO(memory/knowledge 视角) +### 14.5 增补 TODO(memory/knowledge 视角) - TODO: 统一 `get(**kwargs)` 参数协议(`top_k/min_similarity/filters`)。 - TODO: 给 LTM/Knowledge 定义统一去重 key 与冲突消解规则。 - TODO: 明确知识写入权限、审计与成本计量策略。 - TODO: 增加“召回质量 + 压缩损失”评估指标并接入观测。 -## 14. 对外接口汇总(Public Contract Snapshot) +## 15. 对外接口汇总(Public Contract Snapshot) - `IRetrievalContext.get(query="", **kwargs) -> list[Message]` - `IContext` @@ -293,9 +384,10 @@ flowchart TD - `compress(**options) -> None` - `set_tool_gateway(tool_gateway)` -## 15. 核心字段汇总(Core Fields Snapshot) +## 16. 核心字段汇总(Core Fields Snapshot) -- `Message`: `role`, `content`, `name`, `metadata` +- `Message`: `id`, `role`, `kind`, `text`, `attachments`, `data`, `name`, `metadata`, `mark` +- `Session/Checkpoint persistence`: 必须以 canonical `Message` 字段为事实源,不得只持久化 `content: str` - `Budget`: `max_tokens/max_cost/max_time_seconds/max_tool_calls` + `used_*` - `AssembledContext`: `messages`, `sys_prompt`, `tools`, `metadata` - 推荐 metadata 最小审计字段: @@ -303,7 +395,7 @@ flowchart TD - `retrieval.query/ltm_count/knowledge_count` - `compression.trigger/strategies_applied/before_token_estimate/after_token_estimate` -## 16. 关键流程汇总(Flow Snapshot) +## 17. 关键流程汇总(Flow Snapshot) ```mermaid flowchart TD diff --git a/docs/design/modules/model/README.md b/docs/design/modules/model/README.md index 79889b55..82c93840 100644 --- a/docs/design/modules/model/README.md +++ b/docs/design/modules/model/README.md @@ -1,11 +1,12 @@ # Module: model -> Status: detailed design aligned to `dare_framework/model` (2026-03-05). +> Status: message-codec boundary updated for canonical rich-media messages (2026-03-09). ## 1. 定位与职责 - 提供统一模型调用抽象:`IModelAdapter.generate`。 - 定义模型输入/输出结构:`ModelInput`、`ModelResponse`。 +- 负责把 context domain 的 canonical `Message` 序列化为 provider message。 - 提供 prompt 装载与分层解析能力(store + loader)。 ## 2. 依赖与边界 @@ -15,6 +16,8 @@ - 类型:`Prompt`, `ModelInput`, `ModelResponse`, `GenerateOptions` - 边界约束: - model domain 负责“调用与格式适配”,不负责执行循环与工具决策。 + - provider message 仅存在于 adapter 内部,不进入 context/history。 + - adapter 必须消费 canonical `Message`,不得要求上游直接构造 provider-native payload。 ## 3. 对外接口(Public Contract) @@ -39,24 +42,54 @@ - `GenerateOptions` - `temperature`, `max_tokens`, `top_p`, `stop`, `metadata` +canonical `Message` 由 context domain 提供,建议字段口径: + +- `id` +- `role` + - `MessageRole` +- `kind` + - `MessageKind` +- `text: str | None` +- `attachments: list[AttachmentRef]` +- `data: dict[str, Any] | None` +- `name` +- `metadata` + +其中: + +- `attachments` 中的附件项必须是强类型 `AttachmentRef`。 +- adapter 必须以 `Message.data` 作为 `tool_call/tool_result` 的结构化主语义来源;`metadata` 仅保留非语义附加信息。 +- adapter 可以对 `data/metadata` 做 provider-specific codec,但不得把附件重新退化为无约束字典再回传上游。 +- 非法 canonical message(例如 `tool_call/tool_result` 缺少 `data`)必须在进入 adapter 前失败,而不是在 adapter 内兜底补语义。 + ## 5. 关键流程(Runtime Flow) ```mermaid flowchart TD A["Agent execute loop"] --> B["Context.assemble -> ModelInput"] - B --> C["IModelAdapter.generate"] - C --> D["ModelResponse(content, tool_calls, usage)"] - D --> E{"tool_calls empty?"} - E -- yes --> F["Write assistant message"] - E -- no --> G["Tool loop invoke"] + B --> C["adapter codec: Message -> provider message"] + C --> D["IModelAdapter.generate"] + D --> E["ModelResponse(content, tool_calls, usage)"] + E --> F{"tool_calls empty?"} + F -- yes --> G["Write assistant message"] + F -- no --> H["Tool loop invoke"] ``` + ## 6. 与其他模块的交互 -- **Context**:提供 messages/tools。 -- **Tool**:通过 `tool_calls` 触发 `IToolGateway.invoke`。 -- **Config**:`Config.llm` 决定 adapter 类型与连接参数。 -- **Observability**:从 `usage` 提取 token 指标。 +- **Context** + - 提供 canonical `Message` / tools。 +- **Tool** + - 通过 `tool_calls` 触发 `IToolGateway.invoke`。 +- **Transport** + - transport 不直接参与 provider 序列化;只负责 message/select/action/control envelope。 +- **Config** + - `Config.llm` 决定 adapter 类型与连接参数。 +- **Observability** + - 从 `usage` 提取 token 指标。 +- **Session/Memory persistence** + - adapter 不持久化 provider-native message;resume/replay 必须基于 canonical `Message` 重新序列化。 ## 6.1 默认 Adapter 能力矩阵 @@ -68,12 +101,15 @@ flowchart TD - 当前流式输出和增量 tool-call 仍是待补齐项。 - tool defs 仍以 OpenAI function-call schema 为主。 +- 首版 rich-media 只要求 `chat` 支持 `text + image attachments`;不要求图文混排顺序表达。 +- 首版字段约束中,`thinking`、`summary`、`tool_call` 不允许携带附件;`tool_result` 允许附件以支持后续工具富媒体输出。 ## 8. TODO / 未决问题 - TODO: 增加 streaming 与多模型路由策略。 - TODO: 明确跨 adapter 的 tool schema 归一化规范。 - TODO: 收敛 adapter client typing,减少 `Any`。 +- TODO: 为 `Message.kind=text/attachments/data` 定义跨 adapter 能力矩阵与降级规则。 ## 能力状态(landed / partial / planned) diff --git a/docs/design/modules/transport/README.md b/docs/design/modules/transport/README.md index 104b3a7b..06a942a7 100644 --- a/docs/design/modules/transport/README.md +++ b/docs/design/modules/transport/README.md @@ -1,22 +1,24 @@ # Module: transport -> Status: detailed design aligned to `dare_framework/transport` (2026-02-25). +> Status: message-format redesign baseline updated for typed envelope payloads (2026-03-09). ## 1. 定位与职责 -- 提供 Agent 与外部客户端(CLI/Web/API)之间的统一信封通信层。 +- 提供 Agent 与外部客户端(CLI/Web/API)之间的统一 envelope 通信层。 - 通过 `TransportEnvelope` + `AgentChannel` 屏蔽具体连接协议差异。 -- 管理 action/control/message 三类交互路径与错误回执。 +- transport 只负责“帧路由与交互分派”,不直接承载 framework message 语义。 +- `TransportEnvelope.kind` 只区分协议大类;具体业务语义下沉到 typed payload。 ## 2. 依赖与边界 - kernel:`AgentChannel`, `ClientChannel` -- types:`EnvelopeKind`, `TransportEnvelope` +- types:`EnvelopeKind`, `TransportEnvelope`, `EnvelopePayload` - 默认实现:`DefaultAgentChannel` - interaction 子域:`ActionHandlerDispatcher`, `AgentControlHandler`, payload builders - 边界约束: - - transport 负责消息路由与回压,不负责任务业务语义。 - - action/control 的业务执行由 dispatcher/agent handler 提供。 + - transport 负责 envelope 路由、回压、reply 关联,不负责模型/provider 消息格式。 + - `Message` 属于 context domain 的 canonical framework message;transport 不重新定义一套平行 message。 + - action/control/select 的业务执行由 dispatcher/agent handler/交互层提供。 ## 3. 对外接口(Public Contract) @@ -31,16 +33,127 @@ - `attach_agent_envelope_sender(sender)` - `agent_envelope_receiver() -> Receiver` -## 4. 关键字段(Core Fields) +## 4. 核心数据结构(Core Data Structures) -- `EnvelopeKind` - - `MESSAGE`, `ACTION`, `CONTROL` -- `TransportEnvelope` - - `id`, `reply_to`, `kind`, `payload`, `meta`, `stream_id`, `seq` +### 4.1 `EnvelopeKind` -统一返回 payload(interaction/payloads): -- success: `{type:"result", kind, target, ok:true, resp}` -- error: `{type:"error", kind, target, ok:false, code, reason, resp}` +- 保留现有 4 个协议大类: + - `MESSAGE` + - `SELECT` + - `ACTION` + - `CONTROL` +- `EnvelopeKind` 不承载细粒度业务语义;`chat/thinking/tool_call/tool_result` 不平铺到这里。 + +### 4.2 `TransportEnvelope` + +建议稳定字段: + +- `id` + - transport frame id;用于 reply/ack/重试/链路追踪。 +- `reply_to` + - 关联上游 frame。 +- `kind` + - envelope 协议大类。 +- `payload` + - typed payload;根据 `kind` 选择对应 payload 家族。 +- `meta` + - transport/runtime 元数据。 +- `stream_id` + - 流式/多帧关联标识。 +- `seq` + - 流式帧序号。 + +设计收敛结论: + +- `event_type` 退出主设计。 +- 原先通过 `event_type` 传递的业务语义,应转入 typed payload。 +- 如果实施时需要过渡,可短期保留 `event_type` 作为兼容字段,但只能由 payload 单向推导,不允许形成第二真相源。 + +### 4.3 `EnvelopePayload` 抽象接口 + +所有 payload 家族共享以下公共字段: + +- `id` + - 语义对象 id;区别于 envelope frame id。 +- `metadata` + - 业务元数据。 + +### 4.4 Payload 家族 + +#### `MessagePayload` + +- `id` +- `metadata` +- `role` +- `message_kind` + - 使用确定性枚举 `MessageKind` + - `CHAT | THINKING | TOOL_CALL | TOOL_RESULT | SUMMARY` +- `text: str | None` +- `attachments: list[AttachmentRef]` +- `data: dict[str, Any] | None` + +说明: + +- `chat` 使用 `text + attachments` 表达;不再单独引入 `ChatBody`。 +- `thinking/summary` 通常只使用 `text`。 +- `tool_call/tool_result` 通过 `data` 承载结构化字段,`text` 作为可选人类可读补充。 +- `MessagePayload` 可稳定转换为 context domain 的 canonical `Message`。 + +#### `SelectPayload` + +- `id` +- `metadata` +- `select_kind` + - 使用确定性枚举 `SelectKind` + - `ASK | ANSWERED` +- `select_domain` + - 使用确定性枚举 `SelectDomain` + - `APPROVAL | CHOICE | FORM` +- `prompt` +- `options` +- `selected` + +说明: + +- `approval.pending` / `approval.resolved` 不再属于 message event;统一回收到 `kind=select`。 +- approval 的两阶段语义分别映射为: + - `ask` + - `answered` + +#### `ActionPayload` + +- `id` +- `metadata` +- `resource_action` +- `params` + +说明: + +- `resource_action` 就是 action 主语义,不再额外引入 `action_kind`/`message_kind`。 +- 该字段继续与 `ResourceAction` 对齐解析;是否在 payload 上直接强制为 enum,取决于后续是否允许外部扩展 action id。 + +#### `ControlPayload` + +- `id` +- `metadata` +- `control_id` +- `params` + +说明: + +- `control_id` 语义上应与 `AgentControl` 一致;当前实现阶段仍保留字符串承载,但后续不应在框架逻辑中散落裸字符串常量。 + +### 4.5 `AttachmentRef` + +首版只要求图片: + +- `kind: image` +- `uri` +- `mime_type?` +- `filename?` +- `metadata?` + +该结构既可用于 `MessagePayload.attachments`,也可用于 canonical `Message.attachments`。 ## 5. 关键流程(Runtime Flow) @@ -48,32 +161,44 @@ flowchart TD A["Client sends TransportEnvelope"] --> B["DefaultAgentChannel._enqueue_inbox"] B --> C{"kind"} - C -- MESSAGE --> D["put inbox -> Agent poll"] - C -- ACTION --> E["ActionHandlerDispatcher.handle_action"] - C -- CONTROL --> F["AgentControlHandler.invoke"] - E --> G["build success/error payload"] - F --> G + C -- MESSAGE --> D["parse MessagePayload -> canonical Message"] + C -- SELECT --> E["dispatch selection / approval interaction"] + C -- ACTION --> F["ActionHandlerDispatcher.handle_action"] + C -- CONTROL --> G["AgentControlHandler.invoke"] D --> H["Agent business loop"] - H --> I["channel.send -> outbox"] - I --> J["pump_outbox_to_receiver"] + F --> I["build typed action reply payload"] + G --> J["build typed control reply payload"] + H --> K["channel.send -> outbox"] + I --> K + J --> K + K --> L["pump_outbox_to_receiver"] ``` ## 6. 与其他模块的交互 -- **Agent**:通过 `poll/send` 进入 transport loop 或 direct-call 通道。 -- **Hook/Observability**:可通过 transport 发送事件 envelope 到外部 UI。 -- **Tool/HITL**:审批状态可经 transport payload 回传客户端。 +- **Context** + - `kind=message` 的 payload 会被归一化为 canonical `Message`。 +- **Agent** + - 通过 `poll/send` 进入 transport loop 或 direct-call 通道。 +- **Model** + - transport 不接触 provider message;provider 序列化只在 adapter 内部发生。 +- **Hook/Observability** + - 通过 envelope/meta 追踪 transport 级事件,不污染 canonical message 结构。 +- **Tool/HITL** + - 审批/选择状态通过 `kind=select` 回传客户端,而不是伪装成 message event。 ## 7. 约束与限制 - 默认 channel 为阻塞回压模型,未提供优先级队列。 - 未提供持久化队列与断线恢复机制。 +- 本文只定义消息格式与 envelope 结构;`pending delta / active model state / resume rebuild` 不在 transport 设计内处理。 ## 8. TODO / 未决问题 - TODO: 支持 reconnect/resume 与 envelope replay。 - TODO: 定义 streaming chunk 的标准 envelope 协议。 -- TODO: 增加 action/control schema 校验与版本化。 +- TODO: 为各 payload 家族补齐 schema 校验与版本化。 +- TODO: 清理 `TransportEventType` 历史语义,尤其是 `approval.pending/resolved` 向 `SelectPayload` 归位。 ## 9. 相关文档 diff --git a/docs/features/README.md b/docs/features/README.md index 71dfc904..22f2689f 100644 --- a/docs/features/README.md +++ b/docs/features/README.md @@ -37,15 +37,9 @@ ## Active Entries -- `docs/features/add-anthropic-model-adapter.md` -- `docs/features/agentscope-d2-d4-thinking-transport.md` -- `docs/features/agentscope-d5-safe-compression.md` -- `docs/features/agentscope-d7-plan-state-tools.md` - `docs/features/enhance-doc-governance-traceability.md` -- `docs/features/client-session-resume.md` - `docs/features/p0-conformance-gate.md` -- `docs/features/p0-default-eventlog.md` -- `docs/features/p0-step-driven-execution.md` +- `docs/features/rich-media-message-pipeline.md` ## Archive Index diff --git a/docs/features/archive/README.md b/docs/features/archive/README.md index 7f06be38..e9b1954f 100644 --- a/docs/features/archive/README.md +++ b/docs/features/archive/README.md @@ -9,6 +9,16 @@ - `docs/features/archive/client-headless-event-envelope-v1.md` - `docs/features/archive/client-host-orchestration-doc-baseline.md` - `docs/features/archive/refactor-dare-agent-structure-split.md` +- `docs/features/archive/agentscope-d1-d3-message-pipeline.md` +- `docs/features/archive/transport-typed-payload-cutover.md` +- `docs/features/archive/add-anthropic-model-adapter.md` +- `docs/features/archive/agentscope-d2-d4-thinking-transport.md` +- `docs/features/archive/agentscope-d5-safe-compression.md` +- `docs/features/archive/agentscope-d7-plan-state-tools.md` +- `docs/features/archive/client-session-resume.md` +- `docs/features/archive/message-input-boundary-cleanup.md` +- `docs/features/archive/p0-default-eventlog.md` +- `docs/features/archive/p0-step-driven-execution.md` ## Archive Migration Rules diff --git a/docs/features/add-anthropic-model-adapter.md b/docs/features/archive/add-anthropic-model-adapter.md similarity index 99% rename from docs/features/add-anthropic-model-adapter.md rename to docs/features/archive/add-anthropic-model-adapter.md index 7793f6a6..a2ce5e8f 100644 --- a/docs/features/add-anthropic-model-adapter.md +++ b/docs/features/archive/add-anthropic-model-adapter.md @@ -4,7 +4,7 @@ doc_kind: feature topics: ["model", "anthropic", "adapter", "cli-doctor"] created: 2026-03-05 updated: 2026-03-05 -status: active +status: archived mode: openspec --- diff --git a/docs/features/archive/agentscope-d1-d3-message-pipeline.md b/docs/features/archive/agentscope-d1-d3-message-pipeline.md new file mode 100644 index 00000000..e0e6abe8 --- /dev/null +++ b/docs/features/archive/agentscope-d1-d3-message-pipeline.md @@ -0,0 +1,113 @@ +--- +change_ids: ["agentscope-d1-d3-message-pipeline"] +doc_kind: feature +topics: ["agentscope", "message-schema", "transport", "context", "model", "rich-media"] +todo_ids: ["D1-1", "D1-2", "D1-3", "D1-4", "D2-1", "D2-2", "D2-3", "D2-4", "D3-1", "D3-2", "D3-3", "D3-4"] +created: 2026-03-09 +updated: 2026-03-09 +status: archived +mode: openspec +--- + +# Feature: agentscope-d1-d3-message-pipeline + +## Scope +冻结消息格式基线,统一 transport typed payload、context canonical `Message` 与 model adapter codec 边界,首版支持单条 `chat` 消息携带“一段文字 + 多张图片”。 + +本特性只覆盖消息格式契约,不覆盖消息投递/缓存/压缩/resume/分发运行时设计。 + +## OpenSpec Artifacts +- Proposal: `openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/proposal.md` +- Design: `openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/design.md` +- Specs: + - `openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/rich-media-message-schema/spec.md` + - `openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/transport-channel/spec.md` + - `openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/chat-runtime/spec.md` + - `openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/interaction-dispatch/spec.md` +- Tasks: `openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/tasks.md` + +## Progress +- 已完成:design docs 基线更新(transport/context/model/interfaces/TODO)。 +- 已完成:canonical `Message` schema、typed payload families、rich-media adapter codec 与回归测试。 +- 已完成:主 specs 同步与 OpenSpec 归档。 + +## Evidence + +### Commands +- `openspec list --json` +- `openspec new change "agentscope-d1-d3-message-pipeline"` +- `openspec status --change "agentscope-d1-d3-message-pipeline" --json` +- `openspec instructions proposal --change "agentscope-d1-d3-message-pipeline" --json` +- `openspec instructions design --change "agentscope-d1-d3-message-pipeline" --json` +- `openspec instructions specs --change "agentscope-d1-d3-message-pipeline" --json` +- `openspec instructions tasks --change "agentscope-d1-d3-message-pipeline" --json` +- `./.venv/bin/pytest -q tests/unit/test_context_message_types.py tests/unit/test_context_implementation.py tests/unit/test_transport_typed_payloads.py tests/unit/test_transport_types.py tests/unit/test_transport_channel.py tests/unit/test_transport_adapters.py tests/unit/test_base_agent_transport_contract.py tests/unit/test_five_layer_agent.py tests/unit/test_openrouter_adapter.py tests/unit/test_anthropic_model_adapter.py tests/unit/test_openai_model_adapter.py` +- `./.venv/bin/pytest -q tests/unit/test_context_message_types.py tests/unit/test_context_implementation.py tests/unit/test_context_compression.py tests/unit/test_transport_typed_payloads.py tests/unit/test_transport_types.py tests/unit/test_transport_channel.py tests/unit/test_transport_adapters.py tests/unit/test_base_agent_transport_contract.py tests/unit/test_five_layer_agent.py tests/unit/test_openrouter_adapter.py tests/unit/test_anthropic_model_adapter.py tests/unit/test_openai_model_adapter.py tests/unit/test_checkpoint_message_schema.py tests/unit/test_example_10_agentscope_compat.py tests/integration/test_hook_governance_flow.py` +- `git diff --check` +- `openspec validate --changes "agentscope-d1-d3-message-pipeline"` + +### Results +- OpenSpec change 创建成功并完成归档(schema: `spec-driven`)。 +- canonical `Message(role/kind/text/attachments/data)` 已在 context/model/session/memory/transport 主链路落地。 +- rich-media `chat(text + images[])` 已进入 adapter serializer 与回归测试。 +- focused/unit/integration regression suites passed。 +- 文档与 specs 已同步,`git diff --check` 和 `openspec validate` 通过。 + +### Contract Delta +- `schema`: changed + - transport 从 `payload: Any + event_type` 转为 typed payload。 + - canonical `Message` 从 `content: str` 转为 `role(MessageRole) + kind(MessageKind) + text + attachments(AttachmentRef[]) + data`。 +- `error semantics`: changed + - invalid typed payload / unsupported attachment-message_kind 组合改为 deterministic fail。 +- `retry`: none + - 本轮不调整运行时投递/重试机制。 + +### Golden Cases +- 新增:`openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/**` +- 更新:`docs/design/modules/transport/README.md` +- 更新:`docs/design/modules/context/README.md` +- 更新:`docs/design/modules/model/README.md` +- 更新:`docs/design/Interfaces.md` +- 更新:`docs/todos/agentscope_domain_execution_todos.md` +- 更新:`docs/todos/project_overall_todos.md` + +### Regression Summary +- rich-media/context/transport/model focused suite passed。 +- compatibility/example/integration suites covering canonical message migration passed。 + +### Observability and Failure Localization +- `start`: typed `MessagePayload(chat|thinking|tool_call|tool_result|summary)` enters context normalization +- `tool_call`: structured tool semantics come from `Message.data` +- `end`: adapters render canonical message into provider-native request blocks +- `fail`: none + +### Structured Review Report +- module boundary: + - transport 只负责 envelope 与 typed payload,不再承载 message 子语义。 + - context `Message` 作为唯一 canonical message。 + - model adapter 只消费 canonical `Message` 并输出 provider message。 +- state: + - 本 change 只完成 message schema,不引入 pending delta / prompt state / render cache。 +- concurrency: + - 主要风险在 `Message.content` 切除后的大面积调用点迁移。 +- side-effect: + - 属于明确 breaking change:移除 `event_type`、移除 `Message.content`、移除 raw transport payload compatibility。 +- coverage: + - transport/context/model/session/memory/adapter/example/integration 覆盖已补齐。 + +### Behavior Verification +- Happy path: + - 单条 `chat` 消息可携带一段文字 + 多张图片,并保持为一个 logical user turn。 + - `tool_call/tool_result` 结构化语义从 `Message.data` 序列化到 adapter request。 +- Error branch: + - invalid typed payload / unsupported attachment-message kind 组合会在 schema validation 分支 deterministic fail。 + +### Risks and Rollback +- 风险:实现阶段需要同步改 transport/context/model/session/compression 多个模块。 +- 风险:现有大量测试直接依赖 `Message.content` 与 `event_type`。 +- 回滚:整体回退到旧消息契约;但当前工作区已按非兼容 cutover 前进,不再保留兼容层。 + +### Review and Merge Gate Links +- Intent PR: 待创建。 +- Review request: 待创建实现 PR 后补充。 +- Merge gate: 待实现与 CI 完成后补充。 diff --git a/docs/features/agentscope-d2-d4-thinking-transport.md b/docs/features/archive/agentscope-d2-d4-thinking-transport.md similarity index 99% rename from docs/features/agentscope-d2-d4-thinking-transport.md rename to docs/features/archive/agentscope-d2-d4-thinking-transport.md index 386de938..388cf530 100644 --- a/docs/features/agentscope-d2-d4-thinking-transport.md +++ b/docs/features/archive/agentscope-d2-d4-thinking-transport.md @@ -5,7 +5,7 @@ topics: ["agentscope", "transport", "thinking", "tool-events", "model-response"] todo_ids: ["D2-1", "D2-2", "D2-3", "D2-4", "D4-1", "D4-2", "D4-3", "D4-4"] created: 2026-03-02 updated: 2026-03-03 -status: draft +status: archived mode: openspec --- diff --git a/docs/features/agentscope-d5-safe-compression.md b/docs/features/archive/agentscope-d5-safe-compression.md similarity index 99% rename from docs/features/agentscope-d5-safe-compression.md rename to docs/features/archive/agentscope-d5-safe-compression.md index 97db392f..3205af2e 100644 --- a/docs/features/agentscope-d5-safe-compression.md +++ b/docs/features/archive/agentscope-d5-safe-compression.md @@ -4,7 +4,7 @@ doc_kind: feature topics: ["agentscope", "compression", "context", "react-agent", "budget"] created: 2026-03-02 updated: 2026-03-09 -status: in_review +status: archived mode: openspec --- diff --git a/docs/features/agentscope-d7-plan-state-tools.md b/docs/features/archive/agentscope-d7-plan-state-tools.md similarity index 99% rename from docs/features/agentscope-d7-plan-state-tools.md rename to docs/features/archive/agentscope-d7-plan-state-tools.md index ddab3d9c..390f58d2 100644 --- a/docs/features/agentscope-d7-plan-state-tools.md +++ b/docs/features/archive/agentscope-d7-plan-state-tools.md @@ -4,7 +4,7 @@ doc_kind: feature topics: ["agentscope", "plan_v2", "state-machine", "critical-block"] created: 2026-03-02 updated: 2026-03-04 -status: done +status: archived mode: openspec --- diff --git a/docs/features/client-session-resume.md b/docs/features/archive/client-session-resume.md similarity index 99% rename from docs/features/client-session-resume.md rename to docs/features/archive/client-session-resume.md index f855a5b6..0d9db506 100644 --- a/docs/features/client-session-resume.md +++ b/docs/features/archive/client-session-resume.md @@ -5,7 +5,7 @@ topics: ["client-cli", "session-resume", "conversation-history", "t5-1"] todo_ids: ["CRES-001", "CRES-002", "CRES-003", "CRES-004", "CRES-005", "CRES-006", "CRES-007"] created: 2026-03-04 updated: 2026-03-05 -status: draft +status: archived mode: openspec --- diff --git a/docs/features/archive/message-input-boundary-cleanup.md b/docs/features/archive/message-input-boundary-cleanup.md new file mode 100644 index 00000000..6269b188 --- /dev/null +++ b/docs/features/archive/message-input-boundary-cleanup.md @@ -0,0 +1,59 @@ +--- +change_ids: ["message-input-boundary-cleanup"] +doc_kind: feature +topics: ["message-schema", "agent-input", "transport", "session-loop", "examples"] +created: 2026-03-09 +updated: 2026-03-09 +status: archived +mode: openspec +--- + +# Feature: message-input-boundary-cleanup + +## Scope +收口 canonical message 的剩余输入边界问题:agent 顶层直接接受 `Message`,`Task` 退回编排对象并通过 `input_message` 保存真实用户消息,transport poll loop 不再把 `MessagePayload` 压扁成字符串。 + +本特性仍然只覆盖 message 本身相关改动,不包含消息投递/缓存/压缩/resume/分发设计。 + +## OpenSpec Artifacts +- Proposal: `openspec/changes/message-input-boundary-cleanup/proposal.md` +- Design: `openspec/changes/message-input-boundary-cleanup/design.md` +- Specs: + - `openspec/changes/message-input-boundary-cleanup/specs/rich-media-message-schema/spec.md` + - `openspec/changes/message-input-boundary-cleanup/specs/transport-channel/spec.md` + - `openspec/changes/message-input-boundary-cleanup/specs/session-loop/spec.md` + - `openspec/changes/message-input-boundary-cleanup/specs/plan-module/spec.md` +- Tasks: `openspec/changes/message-input-boundary-cleanup/tasks.md` + +## Evidence + +### Commands +- `./.venv/bin/pytest -q tests/unit/test_base_agent_transport_contract.py -k canonical_message_payload` +- `./.venv/bin/pytest -q tests/unit/test_session_orchestrator_message_input.py tests/unit/test_context_message_types.py -k 'input_message or requires_structured_data_for_tool'` +- `./.venv/bin/pytest -q tests/unit/test_base_agent_transport_contract.py tests/unit/test_session_orchestrator_message_input.py tests/unit/test_context_message_types.py tests/unit/test_examples_04_cli.py tests/unit/test_examples_cli.py tests/unit/test_examples_cli_mcp.py tests/unit/test_client_cli.py tests/unit/test_dare_agent_hook_governance.py tests/unit/test_react_agent_gateway_injection.py tests/unit/test_five_layer_agent.py` +- `./.venv/bin/pytest -q tests/unit/test_transport_typed_payloads.py tests/unit/test_transport_types.py tests/unit/test_transport_channel.py tests/unit/test_transport_adapters.py tests/unit/test_openai_model_adapter.py tests/unit/test_openrouter_adapter.py tests/unit/test_anthropic_model_adapter.py tests/unit/test_context_implementation.py tests/unit/test_context_compression.py tests/unit/test_checkpoint_message_schema.py` +- `./.venv/bin/pytest -q tests/unit/test_client_cli.py tests/integration/test_client_cli_flow.py -k 'conversation_id or mode_plan or execute or run_task'` +- `./.venv/bin/pytest -q tests/unit/test_dare_agent_hook_governance.py tests/unit/test_react_agent_gateway_injection.py tests/unit/test_five_layer_agent.py` + +### Results +- transport loop now passes canonical `Message` into agent execution without dropping attachments. +- session loop now prefers `Task.input_message` over `Task.description`. +- canonical schema now rejects `tool_call/tool_result` messages without `data`. +- active client/example direct prompt entry points now send `Message`. + +### Contract Delta +- `schema`: changed + - `IAgent.__call__` accepts `str | Message`; internal execute/orchestration paths consume canonical `Message`. + - `Task` adds `input_message`. +- `error semantics`: changed + - `tool_call/tool_result` without `data` now fail at canonical message construction. +- `retry`: none + - no runtime retry semantics changed. + +### Regression Summary +- focused unit and example suites passed. +- client CLI focused integration slice passed. + +### Risks and Rollback +- remaining docs/spec references to `str | Task` outside the touched scope still need broader cleanup. +- rollback would require reverting the public `str | Message` boundary normalization and the `Task.input_message` internal projection. diff --git a/docs/features/p0-default-eventlog.md b/docs/features/archive/p0-default-eventlog.md similarity index 99% rename from docs/features/p0-default-eventlog.md rename to docs/features/archive/p0-default-eventlog.md index 45c7dd14..aa2b4cde 100644 --- a/docs/features/p0-default-eventlog.md +++ b/docs/features/archive/p0-default-eventlog.md @@ -4,7 +4,7 @@ doc_kind: feature topics: ["event-log", "sqlite", "hash-chain", "builder-defaults"] created: 2026-03-01 updated: 2026-03-01 -status: draft +status: archived mode: openspec --- diff --git a/docs/features/p0-step-driven-execution.md b/docs/features/archive/p0-step-driven-execution.md similarity index 99% rename from docs/features/p0-step-driven-execution.md rename to docs/features/archive/p0-step-driven-execution.md index a671bd51..d32c7523 100644 --- a/docs/features/p0-step-driven-execution.md +++ b/docs/features/archive/p0-step-driven-execution.md @@ -4,7 +4,7 @@ doc_kind: feature topics: ["execution-mode", "step-driven", "plan-execute-verify"] created: 2026-03-01 updated: 2026-03-01 -status: done +status: archived mode: openspec --- diff --git a/docs/features/archive/transport-typed-payload-cutover.md b/docs/features/archive/transport-typed-payload-cutover.md new file mode 100644 index 00000000..24e60efc --- /dev/null +++ b/docs/features/archive/transport-typed-payload-cutover.md @@ -0,0 +1,103 @@ +--- +change_ids: ["transport-typed-payload-cutover"] +doc_kind: feature +topics: ["transport", "typed-payload", "agent-runtime", "client", "protocol"] +created: 2026-03-09 +updated: 2026-03-09 +status: archived +mode: openspec +--- + +# Feature: transport-typed-payload-cutover + +## Scope +移除 transport 运行时剩余的 legacy 兼容层:删除 `TransportEnvelope.event_type` 与 `message/action/control` 的 raw payload 兼容,把 request/reply 全面收敛到 typed payload families。 + +## OpenSpec Artifacts +- Proposal: `openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/proposal.md` +- Design: `openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/design.md` +- Specs: + - `openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/typed-transport-replies/spec.md` + - `openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/transport-channel/spec.md` + - `openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/interaction-dispatch/spec.md` + - `openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/chat-runtime/spec.md` +- Tasks: `openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/tasks.md` + +## Progress +- 已完成:transport/runtime/client/example transport typed payload cutover。 +- 已完成:OpenSpec tasks 1.1-4.2 与对应回归验证。 + +## Evidence + +### Commands +- `openspec new change "transport-typed-payload-cutover"` +- `openspec status --change "transport-typed-payload-cutover" --json` +- `openspec instructions proposal --change "transport-typed-payload-cutover" --json` +- `openspec instructions design --change "transport-typed-payload-cutover" --json` +- `openspec instructions specs --change "transport-typed-payload-cutover" --json` +- `openspec instructions tasks --change "transport-typed-payload-cutover" --json` +- `./.venv/bin/pytest -q tests/unit/test_transport_types.py tests/unit/test_transport_channel.py tests/unit/test_transport_adapters.py tests/unit/test_interaction_dispatcher.py tests/unit/test_base_agent_transport_contract.py tests/unit/test_agent_event_transport_hook.py tests/unit/test_react_agent_gateway_injection.py tests/unit/test_example_10_agentscope_compat.py` +- `./.venv/bin/pytest -q tests/unit/test_examples_cli.py tests/unit/test_examples_cli_mcp.py tests/unit/test_dare_agent_hook_transport_boundary.py tests/unit/test_client_cli.py -k 'approvals or transport or approval_pending or hook or event'` +- `./.venv/bin/pytest -q tests/unit/test_transport_types.py tests/unit/test_transport_channel.py tests/unit/test_transport_adapters.py tests/unit/test_interaction_dispatcher.py tests/unit/test_base_agent_transport_contract.py tests/unit/test_agent_event_transport_hook.py tests/unit/test_react_agent_gateway_injection.py tests/unit/test_example_10_agentscope_compat.py tests/unit/test_examples_cli.py tests/unit/test_examples_cli_mcp.py tests/unit/test_client_cli.py tests/unit/test_dare_agent_hook_transport_boundary.py tests/integration/test_client_cli_flow.py` +- `git diff --check` +- `openspec validate --changes "transport-typed-payload-cutover"` + +### Results +- transport request/reply 已全面收敛到 typed payload families。 +- `pytest` focused transport/runtime/example/client suite: `178 passed, 1 warning` +- `git diff --check`: passed +- `openspec validate --changes "transport-typed-payload-cutover"`: `14 passed, 0 failed` + +### Contract Delta +- `schema`: changed + - 移除 `event_type` + - 移除 raw string/dict transport payload + - request/reply 均收敛到 typed payload families +- `error semantics`: changed + - message/action/control 的错误回复改由对应 typed payload 自身字段表达 +- `retry`: none + - 本切片不调整 retry/timeout 策略 + +### Golden Cases +- 更新:typed transport/unit/example/client regression fixtures +- 新增:`openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/**` + +### Regression Summary +- transport/runtime/example/client targeted regression suite passed with typed request/reply contract. + +### Observability and Failure Localization +- `start`: typed payload request enters `TransportEnvelope(kind, payload)` +- `tool_call`: ReactAgent emits `MessagePayload(message_kind=tool_call/tool_result)` +- `end`: BaseAgent/default channel reply with `MessagePayload/ActionPayload/ControlPayload` +- `fail`: invalid typed payload family now fails at envelope construction or deterministic payload validation branch + +### Structured Review Report +- module boundary: + - transport reply 语义回收到 payload family,不再分叉到 `event_type` +- state: + - 不涉及 session/resume/cache +- concurrency: + - 主要风险在 request/reply 契约切换造成 client/test 同步修改 +- side-effect: + - 会破坏 legacy transport consumers,属于明确 breaking change +- coverage: + - transport/runtime/example/client/unit+integration 回归已覆盖 + +### Behavior Verification +- Happy path: + - `message` request -> `MessagePayload(chat)` reply + - `action` request -> `ActionPayload(ok/result)` reply + - approval pending -> `SelectPayload(ask, approval)` +- Error branch: + - invalid payload family rejected deterministically + - action failure returns `ActionPayload(ok=False, code, reason)` + - agent execution failure returns `MessagePayload(summary, data.reason/code)` + +### Risks and Rollback +- 风险:大量 tests/examples 依赖 `event_type + dict payload` +- 回滚:整体回退到当前 transport 兼容契约 + +### Review and Merge Gate Links +- Intent PR: 待补 +- Review request: 待补 +- Merge gate: 待补 diff --git a/docs/features/rich-media-message-pipeline.md b/docs/features/rich-media-message-pipeline.md new file mode 100644 index 00000000..8f7f7a77 --- /dev/null +++ b/docs/features/rich-media-message-pipeline.md @@ -0,0 +1,85 @@ +--- +change_ids: ["agentscope-d1-d3-message-pipeline", "transport-typed-payload-cutover", "message-input-boundary-cleanup"] +doc_kind: feature +topics: ["message-schema", "rich-media", "transport", "agent-input", "a2a"] +created: 2026-03-09 +updated: 2026-03-09 +status: active +mode: openspec +--- + +# Feature: rich-media-message-pipeline + +## Scope + +Land the rich-media message pipeline as a single governed delivery topic: canonical `Message` +schema cutover, typed transport payloads, public agent input normalization, and A2A/example +entrypoint migration to `text + attachments + data`. + +This topic intentionally excludes the later runtime-lifecycle redesign for message delivery, +caching, compression, resume, and dispatch orchestration. + +## OpenSpec Artifacts + +- Proposal: + - `openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/proposal.md` + - `openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/proposal.md` + - `openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/proposal.md` +- Design: + - `openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/design.md` + - `openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/design.md` + - `openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/design.md` +- Specs: + - `openspec/specs/rich-media-message-schema/spec.md` + - `openspec/specs/transport-channel/spec.md` + - `openspec/specs/interaction-dispatch/spec.md` + - `openspec/specs/chat-runtime/spec.md` + - `openspec/specs/typed-transport-replies/spec.md` +- Tasks: + - `openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/tasks.md` + - `openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/tasks.md` + - `openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/tasks.md` + +## Governance Anchors + +- `docs/guides/Development_Constraints.md` +- `docs/guides/Documentation_First_Development_SOP.md` +- `docs/design/Interfaces.md` +- `docs/design/modules/context/README.md` +- `docs/design/modules/transport/README.md` +- `docs/design/modules/model/README.md` +- `docs/design/modules/agent/README.md` + +## Evidence + +### Commands + +- `openspec validate --spec-dir openspec/specs` +- `git diff --check` + +### Results + +- `openspec validate --spec-dir openspec/specs`: baseline spec tree is valid before the + implementation PR updates runtime code and evidence. +- `git diff --check`: docs-only intent branch stays formatting-clean and preserves the governed + feature-doc template contract. + +### Behavior Verification + +- Happy path: this intent record establishes the active governed feature topic that the later + implementation PR will update while landing the message-schema cutover. +- Error/fallback path: runtime delivery/caching/resume redesign remains explicitly out of scope for + this feature topic and must land through a separate follow-up intent/change. + +### Risks and Rollback + +- Risk: the implementation PR still needs to append concrete runtime verification evidence and + update the active feature record before merge. +- Rollback: if the topic scope changes materially, replace this intent record before merging the + implementation PR instead of silently repurposing it. + +### Review and Merge Gate Links + +- Intent PR: `TBD` +- Implementation PR: `https://github.com/zts212653/Deterministic-Agent-Runtime-Engine/pull/204` +- Review request: `TBD` diff --git a/docs/guides/Tool_Approval_Memory.md b/docs/guides/Tool_Approval_Memory.md index 42548b14..2a59a357 100644 --- a/docs/guides/Tool_Approval_Memory.md +++ b/docs/guides/Tool_Approval_Memory.md @@ -131,14 +131,18 @@ 若超时未出现 pending,则 `request=null`。 -## 5. Transport 审批事件(推荐) +## 5. Transport 审批协议(推荐) -当 `requires_approval=true` 的工具调用进入 pending 时,runtime 会通过 transport 主动发出: +当 `requires_approval=true` 的工具调用进入 pending 时,runtime 会通过 transport 主动发出 `kind="select"` 的 typed payload: -- `event_type="approval.pending"`:包含 pending request 详情 -- `event_type="approval.resolved"`:包含 request_id 与最终 decision(allow/deny) +- `SelectPayload(select_kind="ask", select_domain="approval")`:包含 pending request 详情 +- `SelectPayload(select_kind="answered", select_domain="approval")`:包含 request_id 与最终 decision(allow/deny) -协议说明:客户端应仅使用 `event_type` 做分流;`payload.type` 已移除。 +协议说明: + +- transport 层应基于 `TransportEnvelope.kind="select"` 与 `SelectPayload.select_kind/select_domain` 做分流 +- `event_type="approval.pending|approval.resolved"` 不再是 transport 主协议 +- `client --output json` 的 headless 事件流仍会向外发出 `approval.pending` / `approval.resolved` 作为 CLI 事件名,这是独立于 transport 的输出契约 这允许客户端实现 Codex/Claude Code 风格的“消息流里出现审批卡片”,同时再用 `approvals:grant|deny` 完成决策。 diff --git a/docs/todos/agentscope_domain_execution_todos.md b/docs/todos/agentscope_domain_execution_todos.md index efc0c9cf..913fb209 100644 --- a/docs/todos/agentscope_domain_execution_todos.md +++ b/docs/todos/agentscope_domain_execution_todos.md @@ -11,8 +11,8 @@ - 不包含 Session 体系(并行开发中,不在本清单执行范围)。 - 当前阶段不要求历史兼容(允许直接调整接口,不做迁移层)。 - 不做 HITL 闭环;本轮关注完整权限模型与工具调用权限治理。 -- `client send` 升级为:`content: string` + `uris: list` + `metadata`。 -- `context.assemble` 负责把 `content + uris` 归一化为内部 message 结构。 +- transport 输入升级为 typed payload:保留 `EnvelopeKind = message/select/action/control`,`kind=message` 时使用 `MessagePayload(text + attachments + data)`。 +- `context.assemble` 负责把 `TransportEnvelope(kind=message)` 的 `MessagePayload` 归一化为内部 canonical `Message`。 - 当前并行约束:最多 3 个并行 active claim(TODO 级);拆分主轴为依赖关系、重要度、复杂度;切片仅在对应 TODO 详细位置维护。 ## 0.1 认领声明(Claim Ledger) @@ -30,16 +30,38 @@ | CLM-20260302-D7 | D7-1~D7-4 | lang | done | 2026-03-02 | 2026-03-03 | `agentscope-d7-plan-state-tools` | `CLM-20260302-AG3` | PR #138 已合入主干,D7 代码/回归/评审闭环完成。 | | ~~CLM-20260302-D1D3~~ | ~~D1 + D3(历史聚合)~~ | ~~lang~~ | ~~deprecated~~ | ~~2026-03-02~~ | ~~2026-03-04~~ | ~~`agentscope-d1-d3-message-pipeline`~~ | ~~`CLM-20260302-AG4`~~ | 历史聚合认领已废弃;后续以 D1/D3 的 TODO 级 claim 为准。 | | ~~CLM-20260303-D6D8~~ | ~~D6 + D8(历史聚合)~~ | ~~N/A~~ | ~~deprecated~~ | ~~2026-03-03~~ | ~~2026-03-04~~ | ~~`pending`~~ | ~~`CLM-20260303-AG5`~~ | 聚合占位 claim 已废弃;后续以 D6/D8 的 TODO 级 claim 为准。 | -| CLM-20260304-D1 | D1 | | planned | 2026-03-04 | 2026-03-11 | `agentscope-d1-d3-message-pipeline` | `CLM-20260304-AG6` | 已拆分未分配;切片仅在 `5.2` 维护。 | -| CLM-20260304-D3 | D3 | | planned | 2026-03-04 | 2026-03-11 | `agentscope-d1-d3-message-pipeline` | `CLM-20260304-AG6` | 已拆分未分配;切片仅在 `5.2` 维护。 | +| CLM-20260304-D1 | D1 | lang | done | 2026-03-04 | 2026-03-11 | `archive/2026-03-09-agentscope-d1-d3-message-pipeline` | `CLM-20260304-AG6` | canonical rich-media message schema、typed payload families、adapter codec 基线已完成并归档。 | +| CLM-20260304-D3 | D3 | lang | done | 2026-03-04 | 2026-03-11 | `archive/2026-03-09-agentscope-d1-d3-message-pipeline` | `CLM-20260304-AG6` | context assemble/message normalization、attachment strategy 与 rich-media adapter 接口已完成并归档。 | | CLM-20260304-D6 | D6 | | planned | 2026-03-04 | 2026-03-11 | `pending` | `CLM-20260304-AG7` | 已拆分未分配;切片仅在 `5.2` 维护。 | | CLM-20260304-D8 | D8 | | planned | 2026-03-04 | 2026-03-11 | `pending` | `CLM-20260304-AG7` | 已拆分未分配;切片仅在 `5.2` 维护。 | +| CLM-20260309-D2CUT | D2 transport cutover follow-up | lang | done | 2026-03-09 | 2026-03-12 | `archive/2026-03-09-transport-typed-payload-cutover` | `CLM-20260304-AG6` | transport legacy raw payload / `event_type` 兼容已清理完毕,reply typed payload 契约已归档。 | +| CLM-20260309-D1INPUT | D1/D3 message input boundary cleanup | lang | done | 2026-03-09 | 2026-03-12 | `archive/2026-03-09-message-input-boundary-cleanup` | `CLM-20260304-AG6` | 收口 canonical `Message` 输入边界:agent 直收 `Message`、`Task.input_message`、transport poll 保真、不再把用户输入重新退化成 `Task.description`,并已归档。 | 对账快照(2026-03-04): - 未完成域 `D1/D3/D6/D8` 均已存在 `planned` claim(TODO 级)。 - 已完成域 `D2/D4/D5/D7` 均已有 `done` claim 并与项目级聚合 claim 对账。 ---- +## 0.2 消息管线重构拆分约束(2026-03-07) + +- 针对当前富媒体与模型消息能力补齐,`D1 + D3` 后续设计明确拆分为两个独立主题,禁止继续在同一轮设计里混合讨论: + - 主题 A:`文本/富媒体/模型消息 format` 设计。 + - 主题 B:`消息投递/缓存/压缩/resume/分发` 设计。 +- 当前优先级:先完成主题 A 的设计收敛,再以单独设计文档/切片推进主题 B。 +- 主题 B 当前仅作为 TODO 记录,不在本轮消息模型设计里直接落实现有 runtime 机制调整。 +- 主题 B 设计必须显式覆盖: + - `stm_add`、历史消息、pending delta、模型侧缓存状态之间的边界。 + - `assemble(incremental/rebuild)` 的触发条件与失效策略。 + - 压缩 checkpoint、resume 重建、切模型降级、分发/transport 对 runtime state 的影响。 + +## 0.3 Transport Cutover Follow-up(2026-03-09) + +- `agentscope-d1-d3-message-pipeline` 已完成消息 schema 基线,但 transport runtime 仍残留两层历史兼容: + - `TransportEnvelope.event_type` + - `message/action/control` raw `str` / raw `dict` payload +- 后续独立切片 `transport-typed-payload-cutover` 专门负责: + - 删除上述 legacy compatibility + - 把 transport request/reply 统一收敛到 typed payload families + - 同步更新 client/example/test 契约 ## 1. 能力/GAP 覆盖总览(用于确认“每个 TODO 支持什么能力”) @@ -66,14 +88,15 @@ ## D1 `protocol/message`(输入协议与内部消息模型) **主要改动** -- 新定义 `ClientSend` 输入协议(`content`, `uris`, `metadata`)。 -- 新定义 `UriAttachment`(最小必须字段 + 可选 metadata)。 -- 内部 `Message` 支持“文本 + 附件引用块”的统一表达(供 assemble 消费)。 -- 明确输入约束:URI 数量/长度、metadata 大小、空文本处理。 +- 将 transport `payload: Any + event_type` 收敛为 typed payload 体系。 +- 保留 `EnvelopeKind = message/select/action/control`,不再平铺 message 子类型。 +- `MessagePayload` 与 canonical `Message` 高度对齐,支持 `text + attachments + data`。 +- 新定义 `AttachmentRef`(最小必须字段 + 可选 metadata),首版只要求图片。 +- 明确输入约束:附件数量/长度、metadata 大小、空文本处理、字段校验矩阵。 **支撑能力** -- Msg/TextBlock 基础能力。 -- 为多模态(图片/音频/视频)在协议层预留统一入口。 +- canonical message 基础能力。 +- 为多模态(图片/音频/视频)在消息结构层预留统一入口。 **建议改动位置** - `dare_framework/transport/types.py`(客户端输入 envelope 相关类型) @@ -82,22 +105,23 @@ | ID | 任务 | 主要代码改动 | 支持能力 | 依赖 | 状态 | 输出证据 | |---|---|---|---|---|---|---| -| D1-1 | 定义 `ClientSend` | 新类型 + schema 校验 | Msg 输入标准化 | 无 | todo | 单测:合法/非法输入 | -| D1-2 | 定义 `UriAttachment` | URI 字段规范 + 校验 | 多模态入口 | 无 | todo | 单测:URI 错误码稳定 | -| D1-3 | 定义内部 `Message` 块结构 | message 内容块结构 | TextBlock/附件表达 | D1-1/2 | todo | assemble 可消费 | -| D1-4 | 约束与限制策略 | 限额/空值/超限错误模型 | 稳定输入治理 | D1-1/2/3 | todo | 压测与边界测试 | +| D1-1 | 定义 `EnvelopePayload` 抽象与四类 payload | typed payload + schema 校验 | transport/message 契约统一 | 无 | todo | 单测:合法/非法输入 | +| D1-2 | 定义 `AttachmentRef` | URI 字段规范 + 校验 | 富媒体入口 | 无 | todo | 单测:URI 错误码稳定 | +| D1-3 | 升级 canonical `Message` | `text/attachments/data` 结构 | chat/图片/tool 统一表达 | D1-1/2 | todo | assemble 可消费 | +| D1-4 | 约束与限制策略 | 字段矩阵/空值/超限错误模型 | 稳定输入治理 | D1-1/2/3 | todo | 压测与边界测试 | --- ## D2 `transport`(消息类型与payload协议) **主要改动** -- 定义消息类型枚举:`message/tool_call/tool_result/thinking/error/status`。 -- 统一 transport payload envelope 与错误模型。 -- sender/receiver 对消息类型和错误码使用同一协议层定义。 +- 保留 `EnvelopeKind` 大类:`message/select/action/control`。 +- 移除 `event_type` 作为主设计,业务语义转入 typed payload。 +- `approval.pending/resolved` 从 message event 迁回 `select` payload。 +- sender/receiver 按 `kind -> payload family` 做确定性解析。 **支撑能力** -- Tool/ChatModelBase 执行中间态传输。 +- Tool/ChatModelBase/approval 交互协议统一。 - 后续 observability 的事件采集基础。 **建议改动位置** @@ -107,17 +131,17 @@ | ID | 任务 | 主要代码改动 | 支持能力 | 依赖 | 状态 | 输出证据 | |---|---|---|---|---|---|---| -| D2-1 | 定义消息类型枚举 | enum + 常量统一 | thinking/tool 事件标准化 | D1-1 | done | `tests/unit/test_transport_types.py` | -| D2-2 | 定义 payload 协议 | envelope schema + serializer | transport 语义一致 | D2-1 | done | `tests/unit/test_transport_adapters.py` | -| D2-3 | 错误码标准化 | error payload model | 可观测错误治理 | D2-2 | done | `tests/unit/test_transport_channel.py` | -| D2-4 | 协议测试矩阵 | e2e + contract test | 端到端稳定性 | D2-1/2/3 | done | `tests/unit/test_transport_types.py`, `tests/unit/test_transport_adapters.py` | +| D2-1 | 固化 `EnvelopeKind` 与 payload 家族分派 | enum + typed routing | transport 大类稳定 | D1-1 | todo | `tests/unit/test_transport_types.py` | +| D2-2 | 定义 typed payload 序列化协议 | envelope schema + serializer | transport 语义一致 | D2-1 | todo | `tests/unit/test_transport_adapters.py` | +| D2-3 | 错误码标准化 | error payload model | 可观测错误治理 | D2-2 | todo | `tests/unit/test_transport_channel.py` | +| D2-4 | 审批/选择协议回归 | `select ask/answered` contract test | approval 语义归位 | D2-1/2/3 | todo | contract + e2e 测试 | --- ## D3 `context`(assemble 归一化与URI策略) **主要改动** -- `assemble` 增加 `ClientSend -> Message` 归一化步骤。 +- `assemble` 增加 `TransportEnvelope(kind=message) -> MessagePayload -> Message` 归一化步骤。 - URI 解析策略:仅解析元信息 or 预取内容(本轮建议“元信息优先”)。 - 当模型不支持相关模态时,定义 deterministic 降级策略。 @@ -132,7 +156,7 @@ | ID | 任务 | 主要代码改动 | 支持能力 | 依赖 | 状态 | 输出证据 | |---|---|---|---|---|---|---| -| D3-1 | 接入归一化管线 | assemble 前置 normalize | Msg 统一进入模型 | D1-3 + D2-2 | todo | 输入到模型链路测试 | +| D3-1 | 接入归一化管线 | assemble 前置 normalize | typed message 统一进入模型 | D1-3 + D2-2 | todo | 输入到模型链路测试 | | D3-2 | URI 构建策略 | resolver + policy | 附件/富媒体引用处理 | D1-2 | todo | URI 成功/失败路径测试 | | D3-3 | 降级策略 | text-only fallback 规则 | 多模型兼容执行 | D3-2 | todo | 不支持模态时行为稳定 | | D3-4 | 组装链路回归 | context 集成测试 | 旧文本流程不回归 | D3-1/2/3 | todo | 回归全绿 | @@ -268,9 +292,9 @@ | 接口项 | 生产方 | 消费方 | 冲突风险 | 冻结时点 | |---|---|---|---|---| -| `ClientSend` schema | D1 | D3/D2 | 字段名变动导致 assemble/transport 断裂 | Gate-1 | +| `EnvelopePayload` schema | D1 | D3/D2 | payload 字段名变动导致 assemble/transport 断裂 | Gate-1 | | `Message` 内容块结构 | D1/D3 | D4/D5 | 模型输入与压缩算法理解不一致 | Gate-2 | -| transport 消息类型枚举 | D2 | D4/D8/CLI | 事件类型命名漂移 | Gate-1 | +| transport kind/payload 家族 | D2 | D4/D8/CLI | kind/payload 语义漂移 | Gate-1 | | tool_call/tool_result payload 结构 | D2 | D4/D5/D8 | tool pair 匹配失败 | Gate-2 | | 错误码模型 | D2 | D6/D8 | deny/error 无法统一观测 | Gate-3 | | policy decision 结构 | D6 | D8 | 审计日志字段不一致 | Gate-3 | @@ -314,9 +338,9 @@ | Domain | Slice | 范围 | Depends On | 复杂度 | |---|---|---|---|---| -| `D1` | `D1_a` | `ClientSend`/`UriAttachment` schema 定义 | 无 | 中 | +| `D1` | `D1_a` | `EnvelopePayload`/`AttachmentRef` schema 定义 | 无 | 中 | | `D1` | `D1_b` | 输入约束与错误码主集合 | `D1_a` | 高 | -| `D1` | `D1_c` | Message 块结构与兼容适配 | `D1_a` | 高 | +| `D1` | `D1_c` | canonical `Message` 结构与 typed normalize 适配 | `D1_a` | 高 | | `D3` | `D3_a` | assemble normalize 主流程接入 | `D1_c` + D2-2 | 高 | | `D3` | `D3_b` | URI resolver/policy(元信息优先) | `D1_b` | 中-高 | | `D3` | `D3_c` | 多模型降级策略 + D3 回归 | `D3_a` + `D3_b` | 高 | @@ -342,7 +366,7 @@ | 第一阶段(基线与协议冻结) | `b` | `D1_a` | 输入 schema 基线定义 | | 第一阶段(基线与协议冻结) | `c1` | `D1_b` | 输入约束与错误码冻结 | | 第一阶段(基线与协议冻结) | `c2` | `D1_c` | Message 块结构与兼容适配冻结 | -| 第一阶段(基线与协议冻结) | `c3` | Gate-1 验收 | 完成 `ClientSend/UriAttachment/error-code` 冻结验收 | +| 第一阶段(基线与协议冻结) | `c3` | Gate-1 验收 | 完成 `EnvelopePayload/AttachmentRef/error-code` 冻结验收 | | 第二阶段(核心链路实现) | `d` | `D3_a` | assemble normalize 主流程接入 | | 第二阶段(核心链路实现) | `e1` | `D3_b` + `D3_c` | URI resolver 与降级策略闭环 | | 第二阶段(核心链路实现) | `e2` | `D6_a` | policy model/evaluator 接口落地 | @@ -355,20 +379,20 @@ ## 6. 交接 Gate 定义 -1. **Gate-1(协议冻结)** - 冻结:`ClientSend`、`UriAttachment`、transport 消息类型枚举、错误码主集合。 +1. **Gate-1(协议冻结)** + 冻结:`EnvelopePayload`、`AttachmentRef`、transport kind/payload 家族、错误码主集合。 2. **Gate-2(组装冻结)** 冻结:`assemble` 归一化输入输出结构、tool 事件 payload 结构。 3. **Gate-3(治理冻结)** 冻结:policy decision 结构、plan 状态字段语义、日志主字段键。 4. **Gate-4(联调验收)** - `client send -> assemble -> model/tool loop -> policy gate -> transport -> logs` 全链路通过。 + `typed payload -> assemble -> model/tool loop -> policy gate -> transport -> logs` 全链路通过。 --- ## 7. 联调验收(跨域) -- `ClientSend(content+uris)` 能稳定进入模型调用链路。 +- `MessagePayload(text+attachments+data)` 能稳定进入模型调用链路。 - transport 连续输出 `thinking -> tool_call -> tool_result -> final message`。 - 未授权工具被 deterministic 拒绝,且有可检索审计证据。 - 压缩不破坏 tool pair,且预算超限自动触发。 diff --git a/docs/todos/project_overall_todos.md b/docs/todos/project_overall_todos.md index 92518ae8..910ac661 100644 --- a/docs/todos/project_overall_todos.md +++ b/docs/todos/project_overall_todos.md @@ -240,9 +240,9 @@ AgentScope 补齐详细 TODO 入口:`docs/todos/agentscope_domain_execution_to Evidence:`openspec/changes/archive/2026-03-03-agentscope-d2-d4-thinking-transport/`,`docs/features/agentscope-d2-d4-thinking-transport.md`,`.venv/bin/pytest -q`(`528 passed, 12 skipped, 1 warning`) Last Updated: `2026-03-03` -- [ ] T5-3 图片/音频/视频富媒体消息格式支持 - Status: `planned` - 范围:定义并落地多模态 message schema(文本 + 图片 + 音频 + 视频),覆盖模型输入、上下文存储、transport 传输与适配器能力探测;统一替代“图片/富媒体一等支持”的原 T4-4 范围(含 A2A 附件链路规范化)。 +- [ ] T5-3 图片/音频/视频富媒体消息格式支持 + Status: `planned` + 范围:定义并落地多模态消息格式基线。保留 `TransportEnvelope.kind = message/select/action/control`,以 typed payload 替代 `payload: Any + event_type`;升级 canonical `Message` 为 `text + attachments + data` 结构,覆盖模型输入、上下文存储、transport 传输与适配器能力探测;统一替代“图片/富媒体一等支持”的原 T4-4 范围(含 A2A 附件链路规范化)。 交付:跨适配器能力矩阵 + 不支持能力时的降级策略 + 示例用例。 - [ ] T5-4 全链路日志输出整理(模块分层与规范化) diff --git a/openspec/changes/add-anthropic-model-adapter/.openspec.yaml b/openspec/changes/archive/2026-03-09-add-anthropic-model-adapter/.openspec.yaml similarity index 100% rename from openspec/changes/add-anthropic-model-adapter/.openspec.yaml rename to openspec/changes/archive/2026-03-09-add-anthropic-model-adapter/.openspec.yaml diff --git a/openspec/changes/add-anthropic-model-adapter/design.md b/openspec/changes/archive/2026-03-09-add-anthropic-model-adapter/design.md similarity index 100% rename from openspec/changes/add-anthropic-model-adapter/design.md rename to openspec/changes/archive/2026-03-09-add-anthropic-model-adapter/design.md diff --git a/openspec/changes/add-anthropic-model-adapter/proposal.md b/openspec/changes/archive/2026-03-09-add-anthropic-model-adapter/proposal.md similarity index 100% rename from openspec/changes/add-anthropic-model-adapter/proposal.md rename to openspec/changes/archive/2026-03-09-add-anthropic-model-adapter/proposal.md diff --git a/openspec/changes/add-anthropic-model-adapter/specs/anthropic-model-adapter/spec.md b/openspec/changes/archive/2026-03-09-add-anthropic-model-adapter/specs/anthropic-model-adapter/spec.md similarity index 100% rename from openspec/changes/add-anthropic-model-adapter/specs/anthropic-model-adapter/spec.md rename to openspec/changes/archive/2026-03-09-add-anthropic-model-adapter/specs/anthropic-model-adapter/spec.md diff --git a/openspec/changes/add-anthropic-model-adapter/tasks.md b/openspec/changes/archive/2026-03-09-add-anthropic-model-adapter/tasks.md similarity index 100% rename from openspec/changes/add-anthropic-model-adapter/tasks.md rename to openspec/changes/archive/2026-03-09-add-anthropic-model-adapter/tasks.md diff --git a/openspec/changes/add-security-boundary-policy-gate/.openspec.yaml b/openspec/changes/archive/2026-03-09-add-security-boundary-policy-gate/.openspec.yaml similarity index 100% rename from openspec/changes/add-security-boundary-policy-gate/.openspec.yaml rename to openspec/changes/archive/2026-03-09-add-security-boundary-policy-gate/.openspec.yaml diff --git a/openspec/changes/add-security-boundary-policy-gate/design.md b/openspec/changes/archive/2026-03-09-add-security-boundary-policy-gate/design.md similarity index 100% rename from openspec/changes/add-security-boundary-policy-gate/design.md rename to openspec/changes/archive/2026-03-09-add-security-boundary-policy-gate/design.md diff --git a/openspec/changes/add-security-boundary-policy-gate/proposal.md b/openspec/changes/archive/2026-03-09-add-security-boundary-policy-gate/proposal.md similarity index 100% rename from openspec/changes/add-security-boundary-policy-gate/proposal.md rename to openspec/changes/archive/2026-03-09-add-security-boundary-policy-gate/proposal.md diff --git a/openspec/changes/add-security-boundary-policy-gate/specs/core-runtime/spec.md b/openspec/changes/archive/2026-03-09-add-security-boundary-policy-gate/specs/core-runtime/spec.md similarity index 100% rename from openspec/changes/add-security-boundary-policy-gate/specs/core-runtime/spec.md rename to openspec/changes/archive/2026-03-09-add-security-boundary-policy-gate/specs/core-runtime/spec.md diff --git a/openspec/changes/add-security-boundary-policy-gate/specs/define-trust-boundary/spec.md b/openspec/changes/archive/2026-03-09-add-security-boundary-policy-gate/specs/define-trust-boundary/spec.md similarity index 100% rename from openspec/changes/add-security-boundary-policy-gate/specs/define-trust-boundary/spec.md rename to openspec/changes/archive/2026-03-09-add-security-boundary-policy-gate/specs/define-trust-boundary/spec.md diff --git a/openspec/changes/add-security-boundary-policy-gate/tasks.md b/openspec/changes/archive/2026-03-09-add-security-boundary-policy-gate/tasks.md similarity index 100% rename from openspec/changes/add-security-boundary-policy-gate/tasks.md rename to openspec/changes/archive/2026-03-09-add-security-boundary-policy-gate/tasks.md diff --git a/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/.openspec.yaml b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/.openspec.yaml new file mode 100644 index 00000000..5cb9e8f6 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-03-09 diff --git a/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/design.md b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/design.md new file mode 100644 index 00000000..12403d60 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/design.md @@ -0,0 +1,171 @@ +## Context + +当前消息主链路存在三处结构性问题: + +1. transport `kind=message` 仍要求 `payload` 为纯字符串,导致协议层无法稳定承载富媒体输入或结构化消息。 +2. context domain 的 `Message` 仍以 `content: str` 为中心,`thinking/tool_call/tool_result` 的结构化语义被迫散落在 `metadata`、`name` 和 ad-hoc 字符串编码中。 +3. provider adapter 虽然各自已经有不同的消息序列化逻辑,但上游没有稳定的 canonical message contract,导致 transport、context、model 三层之间的边界持续模糊。 + +本次变更只解决“消息格式”问题,不解决运行时缓存、resume、增量投递和压缩状态机。运行时设计会在后续独立主题中处理。 + +## Goals / Non-Goals + +**Goals:** + +- 将 transport 从 `payload: Any + event_type` 收敛为 typed payload 体系。 +- 保留现有 `EnvelopeKind = message/select/action/control`,避免 envelope 大类继续膨胀。 +- 升级当前 `context.types.Message`,使其成为 framework 内部唯一 canonical message。 +- 支持首版 `chat(text + images[])` 组合消息输入。 +- 让 adapter 统一从 canonical `Message` 做 provider 序列化。 +- 将 approval 交互从 message event 变回 `select` 语义。 + +**Non-Goals:** + +- 不在本次设计中定义 `pending_delta / active_model_state / render cache`。 +- 不处理富媒体完整模态矩阵;首版只要求图片附件。 +- 不做兼容层或迁移适配器;本轮允许 breaking change。 +- 不支持图文混排顺序表达。 + +## Decisions + +### Decision: 保留 3 层,不新增平行 FrameworkMessage 类型 + +- 采用 3 层: + - `TransportEnvelope` + - typed `EnvelopePayload` + - canonical `Message` +- 不新增 `FrameworkMessage` 新类型,直接升级现有 `Message`。 + +原因: + +- 当前 `Message` 已经是 context/model/input 的核心类型,继续平行新增只会制造双重真相源。 +- 文档和代码里大量 `Message(...)` 构造点都以它为 framework canonical message 使用,直接升级成本更可控。 + +备选方案: + +- 方案 A:新增 `FrameworkMessage`,旧 `Message` 保持不动。 + - 不采用;会引入一轮无意义的双类型迁移。 +- 方案 B:继续维持 `content: str`,把结构放到 `metadata`。 + - 不采用;会继续模糊 schema 和边界。 + +### Decision: `EnvelopeKind` 只保留协议大类,细粒度语义进入 payload + +- 保留: + - `message` + - `select` + - `action` + - `control` +- `chat/thinking/tool_call/tool_result` 不进入 `EnvelopeKind`,只在 `MessagePayload.message_kind` 中表达。 + +原因: + +- envelope.kind 负责 transport 路由,不负责会话语义。 +- 这样既能保持 inbound routing 简洁,又不丢 message 子类型。 +- 对于有限且确定性的 payload 子语义,优先使用强类型枚举而不是裸字符串,避免框架内部扩散硬编码常量。 + +备选方案: + +- 方案 A:把 `chat/thinking/tool_call/tool_result` 平铺到 `EnvelopeKind`。 + - 不采用;会把协议层和消息语义层混在一起。 + +### Decision: 废弃 `event_type` 主设计,typed payload 成为唯一真相源 + +- `TransportEnvelope.event_type` 不再作为长期主设计。 +- typed payload 上来后,消息/选择/动作/控制的业务语义只能由 payload 表达。 + +原因: + +- `event_type` 与 payload subtype 并存会形成双重真相源。 +- 当前 `approval.pending/resolved` 被塞进 `TransportEventType`,已经导致 approval 语义错误挂靠在 message/status 线上。 + +备选方案: + +- 方案 A:继续保留 `event_type`,只把 payload 补强。 + - 不采用;长期会留下不可验证的一致性问题。 + +### Decision: `Message` 使用 `text + attachments + data`,不引入 `ChatBody` + +canonical `Message` 字段收敛为: + +- `id` +- `role` +- `kind` +- `text` +- `attachments` +- `data` +- `name` +- `metadata` +- `mark` + +原因: + +- `chat`、`thinking`、`summary` 都能直接复用 `text`。 +- `chat`、后续可能的 `tool_result` 都能复用 `attachments`。 +- `tool_call/tool_result` 通过 `data` 承载结构化字段。 +- 不再需要专门的 `ChatBody`,结构更平。 + +备选方案: + +- 方案 A:`Message.body` 做统一联合体。 + - 不采用;对当前目标来说抽象过重。 +- 方案 B:仅使用 `text + attachments`,完全不要结构化字段。 + - 不采用;无法稳定表达 tool_call/tool_result。 + +### Decision: approval 归位到 `SelectPayload` + +`SelectPayload` 负责: + +- `select_kind = SelectKind.ASK | SelectKind.ANSWERED` +- `select_domain = SelectDomain.APPROVAL | SelectDomain.CHOICE | SelectDomain.FORM` + +approval 的两个阶段分别映射: + +- `approval.pending` -> `kind=select`, `select_kind=ask`, `select_domain=approval` +- `approval.resolved` -> `kind=select`, `select_kind=answered`, `select_domain=approval` + +原因: + +- approval 不是 message event,而是交互选择的一种。 +- 现有 `EnvelopeKind.SELECT` 已经存在,语义上正好匹配。 + +补充约束: + +- `MessagePayload.message_kind` 使用 `MessageKind` 枚举。 +- `SelectPayload.select_kind` / `SelectPayload.select_domain` 使用 `SelectKind` / `SelectDomain` 枚举。 +- `resource_action` 继续与 `ResourceAction` 对齐解析;是否强制为 enum 保留给后续扩展性评估。 +- `control_id` 语义上与 `AgentControl` 对齐,后续实现不应继续散落硬编码字符串。 + +## Risks / Trade-offs + +- [Breaking schema surface] transport / context / model 多处类型会同时改动 + → Mitigation:先改 docs 和 OpenSpec,再按 D1 -> D2 -> D3 切片实施。 + +- [测试面广] 当前大量测试直接断言 `Message.content`、`payload=str`、`event_type` + → Mitigation:按模块迁移,优先修正 transport/context/model 三组 contract tests。 + +- [tool message 历史字段冲突] `name` 与 `data.tool_call_id` 可能并存一段时间 + → Mitigation:本次保留 `name`,但新结构化主语义以 `data` 为准。 + +- [adapter 能力差异] 不同 provider 对图片/tool blocks 的要求不同 + → Mitigation:provider message 仍留在 adapter 内部;当前 change 只冻结 canonical contract。 + +## Migration Plan + +1. 先冻结 docs 设计基线与 OpenSpec 规格。 +2. 在 transport domain 引入 typed payload,并把 approval 从 `event_type` 迁回 `select`。 +3. 升级 canonical `Message` 与 `AttachmentRef`。 +4. 调整 context `assemble()` 和 agent 输入链路,使 `kind=message` 的 payload 能归一化为 canonical `Message`。 +5. 调整 model adapter 从 canonical `Message` 做 provider 序列化。 +6. 同步更新 session/checkpoint/compression/test 契约。 +7. 完成后再进入独立的“消息投递/缓存/压缩/resume/分发”设计切片。 + +Rollback: + +- 由于本次允许 breaking change,不保留运行时双写兼容。 +- 回滚以整体回退到旧 `TransportEnvelope + Message.content: str` 契约为准。 + +## Open Questions + +- `Message.kind=tool_call/tool_result` 的 `data` 最小字段集是否需要在本 change 内完全冻结,还是只冻结承载位,细节留给后续工具链路切片。 +- `SelectPayload.options/selected` 的最小标准化字段是否需要直接兼容当前 ask-user 工具输出。 +- `TransportEnvelope.event_type` 是立即删除,还是实施阶段短暂保留为 deprecated 派生字段。 diff --git a/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/proposal.md b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/proposal.md new file mode 100644 index 00000000..4b4711ac --- /dev/null +++ b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/proposal.md @@ -0,0 +1,46 @@ +## Why + +当前 runtime 的消息主链路仍以纯字符串为中心:transport `kind=message` payload 要求是 `str`,context `Message` 也只支持 `content: str`。这导致“单条用户消息 = 一段文字 + 多张图片”无法以稳定 schema 进入模型调用链路,也让 `thinking/tool_call/tool_result/approval` 等语义继续分散在 event_type、metadata 和 ad-hoc payload 中。 + +现在需要先把消息格式基线收敛,形成 transport、context、model 三层一致的 canonical contract,为后续富媒体输入、adapter 能力探测和运行时缓存/resume 设计提供稳定前提。 + +## What Changes + +- 将 transport 从 `payload: Any + event_type` 收敛为 typed payload 体系。 +- 保留 `EnvelopeKind = message/select/action/control`,不再把消息子类型平铺到 envelope kind。 +- 为 transport 定义统一 `EnvelopePayload` 抽象以及 `MessagePayload / SelectPayload / ActionPayload / ControlPayload` 四类 payload。 +- **BREAKING**:升级 canonical `Message`,从 `content: str` 改为 `kind + text + attachments + data` 结构。 +- 在 `Message(kind=chat)` 中支持 `text + image attachments[]`,覆盖“文字 + 多图片”组合消息。 +- 将 `approval.pending / approval.resolved` 从 message event 迁回 `kind=select` 的 `ask / answered` 语义。 +- **BREAKING**:移除 `TransportEventType` 作为主设计,业务语义改由 typed payload 承载。 +- 明确 adapter 只消费 canonical `Message`,provider message 仅存在于 adapter 内部。 + +## Capabilities + +### New Capabilities +- `rich-media-message-schema`: 定义 canonical `Message`、`AttachmentRef` 和 `kind=message` 的 typed payload 契约,支撑 `text + images[]` 组合消息进入 runtime/model 链路。 + +### Modified Capabilities +- `transport-channel`: transport envelope 从 content-agnostic Any payload 演进为 typed payload 家族,并把 approval/selection 语义归位到 `kind=select`。 +- `chat-runtime`: runtime/context/model 调用链从 `Message.content: str` 演进为 canonical rich-media message,并要求 assemble/adapter 消费统一 message 契约。 +- `interaction-dispatch`: deterministic interaction contract 需要显式纳入 `select` 路径与 approval ask/answered 语义,避免继续依赖 message event_type 变体。 + +## Impact + +- Affected docs: + - `docs/design/modules/transport/README.md` + - `docs/design/modules/context/README.md` + - `docs/design/modules/model/README.md` + - `docs/design/Interfaces.md` + - `docs/todos/agentscope_domain_execution_todos.md` +- Affected code: + - `dare_framework/transport/*` + - `dare_framework/context/*` + - `dare_framework/model/*` + - `dare_framework/agent/*` + - `client/runtime/*` + - `client/session_store.py` +- Breaking surface: + - `TransportEnvelope` payload parsing + - `context.Message` structure + - adapter serialization contract diff --git a/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/chat-runtime/spec.md b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/chat-runtime/spec.md new file mode 100644 index 00000000..ab7331d7 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/chat-runtime/spec.md @@ -0,0 +1,40 @@ +## MODIFIED Requirements + +### Requirement: LLM-driven execute loop +The runtime SHALL invoke the configured `IModelAdapter` during the execute loop, provide the assembled canonical message history and available tool definitions, and iterate over tool calls until the model returns a final response. + +- `Context.assemble()` MUST produce canonical `Message` objects rather than plain text-only entries. +- `IModelAdapter` MUST serialize canonical `Message` into provider-native request messages internally. +- The execute loop MUST append tool calls and tool results back into canonical message history before continuing. + +#### Scenario: Model returns a final response +- **WHEN** the model response contains no tool calls +- **THEN** the execute loop returns success and exposes the response content in the run output + +#### Scenario: Model requests a tool call +- **WHEN** the model response includes a tool call +- **THEN** the runtime executes the tool via `ToolRuntime`, appends the result to the canonical message history, and continues + +### Requirement: Retrieval query derives from current user intent +The default assembly strategy SHALL derive retrieval query from the latest user-intent message in STM. + +For `Message(kind="chat")`, retrieval query derivation MUST use the latest non-empty `text` field rather than transport envelope content. + +#### Scenario: Latest user chat text drives retrieval query +- **GIVEN** STM contains multiple turns including a latest user `Message(kind="chat")` with text +- **WHEN** `Context.assemble()` is called +- **THEN** LTM and Knowledge retrieval are invoked with that latest user text as query + +## ADDED Requirements + +### Requirement: Chat messages support text and image attachments in one turn +The runtime SHALL support a single chat message that contains both text and multiple image attachments. + +- `Message(kind="chat")` MUST allow non-empty `text` together with one or more image attachment references. +- The assembled message history MUST preserve the association as one logical user turn. +- Model adapters MAY degrade unsupported image history, but the canonical chat message structure MUST remain intact before adapter-specific conversion. + +#### Scenario: User sends one text and multiple images +- **WHEN** the runtime receives one user chat message with text and two image attachments +- **THEN** the message history contains one canonical `Message(kind="chat")` +- **AND** both image attachments remain associated with that same user turn diff --git a/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/interaction-dispatch/spec.md b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/interaction-dispatch/spec.md new file mode 100644 index 00000000..554090d3 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/interaction-dispatch/spec.md @@ -0,0 +1,27 @@ +## ADDED Requirements + +### Requirement: Select dispatch is isolated from action, control, and prompt execution +The system SHALL provide a deterministic dispatch path for `select` envelopes that is separate from prompt execution, action dispatch, and runtime control handling. + +- `select` dispatch MUST NOT enter the LLM prompt execution path. +- `select` dispatch MUST NOT be routed through `ActionHandlerDispatcher`. +- `select` dispatch MUST preserve `reply_to` correlation semantics for answered selections. + +#### Scenario: Select answer does not execute as a prompt +- **GIVEN** an inbound `TransportEnvelope(kind="select")` +- **WHEN** the interaction layer processes the envelope +- **THEN** it does not invoke the LLM-driven agent execution path +- **AND** it does not route the envelope through `ActionHandlerDispatcher` + +### Requirement: Approval interactions use select ask and answered semantics +The deterministic interaction layer SHALL represent approval interactions as `select` payloads. + +- Pending approval MUST use `SelectPayload(select_domain="approval", select_kind="ask")`. +- Resolved approval MUST use `SelectPayload(select_domain="approval", select_kind="answered")`. +- The runtime MUST NOT rely on message event aliases such as `approval.pending` or `approval.resolved` as the primary approval contract. + +#### Scenario: Approval resolution replies to the pending request +- **GIVEN** an approval request was emitted as `kind="select"` with `select_kind="ask"` +- **WHEN** the user resolves the approval +- **THEN** the resolution is expressed as `kind="select"` with `select_kind="answered"` +- **AND** the reply references the originating request envelope when available diff --git a/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/rich-media-message-schema/spec.md b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/rich-media-message-schema/spec.md new file mode 100644 index 00000000..d028b498 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/rich-media-message-schema/spec.md @@ -0,0 +1,32 @@ +## ADDED Requirements + +### Requirement: Canonical message schema supports text, attachments, and structured data +The system SHALL define a canonical framework `Message` schema that is independent from provider-native message formats and can represent chat text, image attachments, and structured tool-related data. + +- `Message` MUST remain the single canonical message type inside the framework runtime. +- `Message` MUST expose `id`, `role`, `kind`, `text`, `attachments`, `data`, `name`, `metadata`, and `mark`. +- `Message.role` MUST use a deterministic finite type rather than unconstrained strings. +- `Message.kind` MUST distinguish at least `chat`, `thinking`, `tool_call`, `tool_result`, and `summary`. +- `Message.kind` SHOULD be represented by a deterministic finite type rather than unconstrained strings. +- `Message.attachments` MUST support image attachment references via a typed `AttachmentRef` structure rather than raw dictionaries. +- `Message.data` MUST be available for structured message kinds such as `tool_call` and `tool_result`. +- `Message.kind="thinking" | "summary" | "tool_call"` MUST reject attachments at schema validation time. +- `Message.data` MUST be the primary structured source for `tool_call/tool_result`; `metadata` MUST NOT be required as a semantic fallback. + +#### Scenario: Chat message carries text and multiple images +- **WHEN** a user message is represented as `Message(kind="chat")` with non-empty `text` and multiple image attachments +- **THEN** the runtime preserves both the text and all attachment references in a single canonical message + +### Requirement: Message payload normalizes losslessly into canonical message +The system SHALL define a `MessagePayload` transport contract that can be losslessly normalized into canonical `Message`. + +- `MessagePayload` MUST include `id`, `metadata`, `role`, `message_kind`, `text`, `attachments`, and `data`. +- `MessagePayload.role` and `MessagePayload.message_kind` SHOULD use deterministic finite types rather than unconstrained strings. +- `MessagePayload.message_kind` MUST map deterministically to `Message.kind`. +- For `message_kind="chat"`, the payload MUST support `text + attachments` without requiring a separate chat-specific body type. +- `MessagePayload` MUST reject unsupported attachment/message-kind combinations during payload validation. + +#### Scenario: Message payload becomes canonical message +- **WHEN** the runtime receives `TransportEnvelope(kind="message", payload=)` +- **THEN** the payload is normalized into one canonical `Message` +- **AND** no message semantics are taken from transport-only fields diff --git a/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/transport-channel/spec.md b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/transport-channel/spec.md new file mode 100644 index 00000000..7258f0ce --- /dev/null +++ b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/specs/transport-channel/spec.md @@ -0,0 +1,68 @@ +## MODIFIED Requirements + +### Requirement: Transport envelope is content-agnostic and distinct from context messages +The system SHALL define a `TransportEnvelope` type that remains distinct from `context.Message` while carrying typed payload families rather than ad-hoc arbitrary payloads. + +- The envelope MUST provide a primary strong-typed `kind` categorization that distinguishes `message|select|action|control`. +- The envelope MUST carry a typed payload family selected by `kind`. +- For `kind="action"`, the payload MUST carry a deterministic `resource_action` request. +- For `kind="control"`, the payload MUST carry a deterministic runtime control id from `AgentControl` (e.g. `interrupt|pause|retry|reverse`). +- The envelope model MUST NOT require a second routing subtype field in addition to `kind` in order to route inbound envelopes. + +#### Scenario: Envelope carries a control interrupt +- **WHEN** a client sends a `TransportEnvelope` with `kind="control"` and a payload whose `control_id="interrupt"` +- **THEN** the channel can route it to control handling without relying on prompt parsing + +### Requirement: Envelope kind supports message, select, action, and control categories +The transport envelope model SHALL provide a primary categorization field for inbound/outbound envelopes that can distinguish: +- `message` (canonical message payloads) +- `select` (approval/choice/form interactions) +- `action` (deterministic resource actions) +- `control` (interrupt/pause/retry/reverse) + +The envelope model MUST NOT require a separate subtype field in order to route inbound envelopes. + +#### Scenario: Select envelope is distinguishable without prompt parsing +- **GIVEN** a client sends `TransportEnvelope(kind="select", payload=)` +- **WHEN** the channel receives it +- **THEN** the channel can route the request deterministically without inspecting prompt text + +### Requirement: Agent loop consumes prompt messages only +The runtime integration with `AgentChannel.poll()` SHALL treat polled envelopes as prompt message workload only. + +- Agent execution loop MUST NOT be responsible for routing `select`, `action`, or `control`. +- For `kind="message"`, the agent loop MUST only accept typed `MessagePayload` that can normalize to canonical `Message`. +- Any non-message envelope reaching the agent loop MUST be treated as routing error. + +#### Scenario: Non-message envelope is rejected in agent loop +- **GIVEN** a non-message envelope is observed in the agent loop +- **WHEN** runtime validation executes +- **THEN** runtime returns an error response +- **AND** does not invoke prompt execution + +### Requirement: Entry adapters provide deterministic envelope kinds +Client entry adapters SHALL normalize input into explicit envelope kinds and typed payloads before transport routing. + +- `stdio` adapters MAY map slash commands to structured `ACTION/CONTROL/SELECT` envelopes. +- `websocket` and `A2A` adapters MUST send explicit `kind` and typed `payload`. +- Entry adapters MUST NOT rely on transport runtime parsing free-form prompt text to infer action/control/select semantics. + +#### Scenario: Websocket sends explicit action envelope +- **GIVEN** a websocket client wants tool introspection +- **WHEN** it sends `kind="action"` with an `ActionPayload` that targets `tools:list` +- **THEN** channel routes the request through action path without text parsing + +## ADDED Requirements + +### Requirement: Select envelopes carry approval and choice interactions +The transport channel SHALL support `kind="select"` for deterministic selection interactions. + +- `SelectPayload` MUST support `select_kind="ask|answered"`. +- `SelectPayload` MUST support `select_domain="approval|choice|form"`. +- Approval interactions MUST use `kind="select"` rather than message event aliases. + +#### Scenario: Approval pending is emitted as select ask +- **GIVEN** a tool invocation requires human approval +- **WHEN** the runtime emits the interaction to the client +- **THEN** it sends `TransportEnvelope(kind="select")` +- **AND** the payload uses `select_domain="approval"` and `select_kind="ask"` diff --git a/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/tasks.md b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/tasks.md new file mode 100644 index 00000000..cfb128a4 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-agentscope-d1-d3-message-pipeline/tasks.md @@ -0,0 +1,27 @@ +## 1. Transport typed payload baseline + +- [x] 1.1 Replace `TransportEnvelope.payload: Any + event_type` with typed payload families in `dare_framework/transport/types.py` +- [x] 1.2 Keep `EnvelopeKind = message/select/action/control` and route inbound envelopes by payload family in transport channel code +- [x] 1.3 Move approval pending/resolved semantics from message event aliases into `SelectPayload(select_kind=ask|answered, select_domain=approval)` +- [x] 1.4 Update transport payload builders, dispatcher contracts, and transport-facing tests to validate typed payload parsing and reply correlation + +## 2. Canonical message upgrade + +- [x] 2.1 Upgrade `dare_framework/context/types.py::Message` to `kind + text + attachments + data` and add `AttachmentRef` +- [x] 2.2 Update context assemble/STM/query logic to consume canonical `Message` instead of `content: str` +- [x] 2.3 Replace task-description-as-message usage so agent/runtime input paths construct canonical messages directly +- [x] 2.4 Update session/checkpoint/memory persistence code to serialize and restore the upgraded `Message` + +## 3. Model adapter serialization + +- [x] 3.1 Update `ModelInput` consumers so adapters serialize canonical `Message` instead of plain text content +- [x] 3.2 Implement `chat(text + image attachments[])` serialization for supported adapters and deterministic fallback hooks for unsupported history +- [x] 3.3 Move `tool_call/tool_result` structured semantics from ad-hoc metadata parsing to canonical `Message.data` +- [x] 3.4 Update adapter unit tests to cover chat rich-media messages and structured tool message serialization + +## 4. Contract and regression verification + +- [x] 4.1 Update transport contract tests that currently assert `payload=str` and `event_type` +- [x] 4.2 Update context/model/integration tests that currently assert `Message.content` +- [x] 4.3 Add golden-path tests for one chat message containing text plus multiple image attachments +- [x] 4.4 Add error-path tests for invalid typed payloads and unsupported attachment/message-kind combinations diff --git a/openspec/changes/agentscope-d2-d4-thinking-transport/.openspec.yaml b/openspec/changes/archive/2026-03-09-agentscope-d2-d4-thinking-transport/.openspec.yaml similarity index 100% rename from openspec/changes/agentscope-d2-d4-thinking-transport/.openspec.yaml rename to openspec/changes/archive/2026-03-09-agentscope-d2-d4-thinking-transport/.openspec.yaml diff --git a/openspec/changes/agentscope-d2-d4-thinking-transport/design.md b/openspec/changes/archive/2026-03-09-agentscope-d2-d4-thinking-transport/design.md similarity index 100% rename from openspec/changes/agentscope-d2-d4-thinking-transport/design.md rename to openspec/changes/archive/2026-03-09-agentscope-d2-d4-thinking-transport/design.md diff --git a/openspec/changes/agentscope-d2-d4-thinking-transport/proposal.md b/openspec/changes/archive/2026-03-09-agentscope-d2-d4-thinking-transport/proposal.md similarity index 100% rename from openspec/changes/agentscope-d2-d4-thinking-transport/proposal.md rename to openspec/changes/archive/2026-03-09-agentscope-d2-d4-thinking-transport/proposal.md diff --git a/openspec/changes/agentscope-d2-d4-thinking-transport/specs/agentscope-thinking-transport/spec.md b/openspec/changes/archive/2026-03-09-agentscope-d2-d4-thinking-transport/specs/agentscope-thinking-transport/spec.md similarity index 100% rename from openspec/changes/agentscope-d2-d4-thinking-transport/specs/agentscope-thinking-transport/spec.md rename to openspec/changes/archive/2026-03-09-agentscope-d2-d4-thinking-transport/specs/agentscope-thinking-transport/spec.md diff --git a/openspec/changes/agentscope-d2-d4-thinking-transport/specs/chat-runtime/spec.md b/openspec/changes/archive/2026-03-09-agentscope-d2-d4-thinking-transport/specs/chat-runtime/spec.md similarity index 100% rename from openspec/changes/agentscope-d2-d4-thinking-transport/specs/chat-runtime/spec.md rename to openspec/changes/archive/2026-03-09-agentscope-d2-d4-thinking-transport/specs/chat-runtime/spec.md diff --git a/openspec/changes/agentscope-d2-d4-thinking-transport/specs/transport-channel/spec.md b/openspec/changes/archive/2026-03-09-agentscope-d2-d4-thinking-transport/specs/transport-channel/spec.md similarity index 100% rename from openspec/changes/agentscope-d2-d4-thinking-transport/specs/transport-channel/spec.md rename to openspec/changes/archive/2026-03-09-agentscope-d2-d4-thinking-transport/specs/transport-channel/spec.md diff --git a/openspec/changes/agentscope-d2-d4-thinking-transport/tasks.md b/openspec/changes/archive/2026-03-09-agentscope-d2-d4-thinking-transport/tasks.md similarity index 100% rename from openspec/changes/agentscope-d2-d4-thinking-transport/tasks.md rename to openspec/changes/archive/2026-03-09-agentscope-d2-d4-thinking-transport/tasks.md diff --git a/openspec/changes/agentscope-d5-safe-compression/.openspec.yaml b/openspec/changes/archive/2026-03-09-agentscope-d5-safe-compression/.openspec.yaml similarity index 100% rename from openspec/changes/agentscope-d5-safe-compression/.openspec.yaml rename to openspec/changes/archive/2026-03-09-agentscope-d5-safe-compression/.openspec.yaml diff --git a/openspec/changes/agentscope-d5-safe-compression/design.md b/openspec/changes/archive/2026-03-09-agentscope-d5-safe-compression/design.md similarity index 100% rename from openspec/changes/agentscope-d5-safe-compression/design.md rename to openspec/changes/archive/2026-03-09-agentscope-d5-safe-compression/design.md diff --git a/openspec/changes/agentscope-d5-safe-compression/proposal.md b/openspec/changes/archive/2026-03-09-agentscope-d5-safe-compression/proposal.md similarity index 100% rename from openspec/changes/agentscope-d5-safe-compression/proposal.md rename to openspec/changes/archive/2026-03-09-agentscope-d5-safe-compression/proposal.md diff --git a/openspec/changes/agentscope-d5-safe-compression/specs/agentscope-safe-compression/spec.md b/openspec/changes/archive/2026-03-09-agentscope-d5-safe-compression/specs/agentscope-safe-compression/spec.md similarity index 100% rename from openspec/changes/agentscope-d5-safe-compression/specs/agentscope-safe-compression/spec.md rename to openspec/changes/archive/2026-03-09-agentscope-d5-safe-compression/specs/agentscope-safe-compression/spec.md diff --git a/openspec/changes/agentscope-d5-safe-compression/specs/chat-runtime/spec.md b/openspec/changes/archive/2026-03-09-agentscope-d5-safe-compression/specs/chat-runtime/spec.md similarity index 100% rename from openspec/changes/agentscope-d5-safe-compression/specs/chat-runtime/spec.md rename to openspec/changes/archive/2026-03-09-agentscope-d5-safe-compression/specs/chat-runtime/spec.md diff --git a/openspec/changes/agentscope-d5-safe-compression/tasks.md b/openspec/changes/archive/2026-03-09-agentscope-d5-safe-compression/tasks.md similarity index 100% rename from openspec/changes/agentscope-d5-safe-compression/tasks.md rename to openspec/changes/archive/2026-03-09-agentscope-d5-safe-compression/tasks.md diff --git a/openspec/changes/agentscope-d7-plan-state-tools/.openspec.yaml b/openspec/changes/archive/2026-03-09-agentscope-d7-plan-state-tools/.openspec.yaml similarity index 100% rename from openspec/changes/agentscope-d7-plan-state-tools/.openspec.yaml rename to openspec/changes/archive/2026-03-09-agentscope-d7-plan-state-tools/.openspec.yaml diff --git a/openspec/changes/agentscope-d7-plan-state-tools/design.md b/openspec/changes/archive/2026-03-09-agentscope-d7-plan-state-tools/design.md similarity index 100% rename from openspec/changes/agentscope-d7-plan-state-tools/design.md rename to openspec/changes/archive/2026-03-09-agentscope-d7-plan-state-tools/design.md diff --git a/openspec/changes/agentscope-d7-plan-state-tools/proposal.md b/openspec/changes/archive/2026-03-09-agentscope-d7-plan-state-tools/proposal.md similarity index 100% rename from openspec/changes/agentscope-d7-plan-state-tools/proposal.md rename to openspec/changes/archive/2026-03-09-agentscope-d7-plan-state-tools/proposal.md diff --git a/openspec/changes/agentscope-d7-plan-state-tools/specs/chat-runtime/spec.md b/openspec/changes/archive/2026-03-09-agentscope-d7-plan-state-tools/specs/chat-runtime/spec.md similarity index 100% rename from openspec/changes/agentscope-d7-plan-state-tools/specs/chat-runtime/spec.md rename to openspec/changes/archive/2026-03-09-agentscope-d7-plan-state-tools/specs/chat-runtime/spec.md diff --git a/openspec/changes/agentscope-d7-plan-state-tools/specs/plan-runtime/spec.md b/openspec/changes/archive/2026-03-09-agentscope-d7-plan-state-tools/specs/plan-runtime/spec.md similarity index 100% rename from openspec/changes/agentscope-d7-plan-state-tools/specs/plan-runtime/spec.md rename to openspec/changes/archive/2026-03-09-agentscope-d7-plan-state-tools/specs/plan-runtime/spec.md diff --git a/openspec/changes/agentscope-d7-plan-state-tools/tasks.md b/openspec/changes/archive/2026-03-09-agentscope-d7-plan-state-tools/tasks.md similarity index 100% rename from openspec/changes/agentscope-d7-plan-state-tools/tasks.md rename to openspec/changes/archive/2026-03-09-agentscope-d7-plan-state-tools/tasks.md diff --git a/openspec/changes/client-session-resume/design.md b/openspec/changes/archive/2026-03-09-client-session-resume/design.md similarity index 100% rename from openspec/changes/client-session-resume/design.md rename to openspec/changes/archive/2026-03-09-client-session-resume/design.md diff --git a/openspec/changes/client-session-resume/proposal.md b/openspec/changes/archive/2026-03-09-client-session-resume/proposal.md similarity index 100% rename from openspec/changes/client-session-resume/proposal.md rename to openspec/changes/archive/2026-03-09-client-session-resume/proposal.md diff --git a/openspec/changes/client-session-resume/specs/client-host-orchestration/spec.md b/openspec/changes/archive/2026-03-09-client-session-resume/specs/client-host-orchestration/spec.md similarity index 100% rename from openspec/changes/client-session-resume/specs/client-host-orchestration/spec.md rename to openspec/changes/archive/2026-03-09-client-session-resume/specs/client-host-orchestration/spec.md diff --git a/openspec/changes/client-session-resume/tasks.md b/openspec/changes/archive/2026-03-09-client-session-resume/tasks.md similarity index 100% rename from openspec/changes/client-session-resume/tasks.md rename to openspec/changes/archive/2026-03-09-client-session-resume/tasks.md diff --git a/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/.openspec.yaml b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/.openspec.yaml new file mode 100644 index 00000000..5cb9e8f6 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-03-09 diff --git a/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/design.md b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/design.md new file mode 100644 index 00000000..6144fc9c --- /dev/null +++ b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/design.md @@ -0,0 +1,60 @@ +## Overview + +This change closes the remaining gap between the canonical message schema and the runtime execution boundary. The framework already stores and serializes canonical `Message(role, kind, text, attachments, data)`, but direct execution and transport polling still flatten top-level input to strings. The design goal is to make canonical `Message` the first-class user input while keeping `Task` as an orchestration object. + +## Architecture + +### Entry boundary + +- `IAgent.__call__` accepts `str | Message`. +- `IAgentOrchestration.execute` receives canonical `Message`. +- `Message` is the preferred direct prompt input. +- `Task` remains the five-layer orchestration carrier and may optionally hold `input_message`. + +### Orchestration boundary + +- `Task.description` remains a planning/execution description. +- `Task.input_message` becomes the source of truth for the canonical first user turn. +- When `Task.input_message` is absent, runtime derives a default `Message(role=user, kind=chat, text=description)`. + +### Transport boundary + +- `TransportEnvelope(kind=message, payload=MessagePayload)` normalizes into canonical `Message` before agent execution. +- The transport loop must not drop `attachments` or `data`. + +## Data Structures + +### Task + +```text +Task +- description: str +- input_message: Message | None +- milestones / metadata / previous_session_summary / resume_from_checkpoint +``` + +### Validation + +- `Message.kind in {tool_call, tool_result}` requires non-empty `data`. +- `thinking/summary/tool_call` reject attachments. + +## Workflow + +1. Direct caller or transport provides a top-level prompt. +2. Agent normalizes the input: + - `Message` passes through unchanged. + - `str` is upgraded at the public agent boundary to `Message(role=user, kind=chat, text=...)`. +3. Session loop adds the normalized canonical user message to STM. +4. Model adapters receive assembled canonical `Message` objects without earlier text flattening. + +## Error Handling + +- Invalid top-level transport message payload remains a deterministic transport error. +- Invalid canonical messages (for example `tool_call` without `data`) fail at schema construction time. + +## Testing + +- Transport loop preserves attachments when polling `MessagePayload`. +- Session loop prefers `Task.input_message`. +- Canonical schema rejects `tool_call/tool_result` without `data`. +- Example/client helpers send `Message` rather than `Task(description=...)`. diff --git a/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/proposal.md b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/proposal.md new file mode 100644 index 00000000..a446ae55 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/proposal.md @@ -0,0 +1,28 @@ +## Why + +The previous message-schema cutover established canonical `Message(text, attachments, data)`, but the runtime entry boundary still treated top-level input as `str`/`Task.description`. That leaves a contract hole: transport can carry `MessagePayload(chat + images[])`, yet the agent loop still collapses it to plain text before execution. + +## What Changes + +- **BREAKING** Promote canonical `Message` to a first-class agent input alongside `Task`. +- Add `Task.input_message` so orchestration keeps a separate execution description while preserving the original canonical user message. +- Convert transport-loop `MessagePayload` into canonical `Message` before agent execution instead of collapsing to `str`. +- Tighten canonical schema validation so `tool_call/tool_result` require structured `data`. +- Update client/example entry points to send `Message` rather than `Task(description=...)` for direct user prompts. + +## Capabilities + +### New Capabilities +- None. + +### Modified Capabilities +- `rich-media-message-schema`: top-level agent/runtime input must preserve canonical `Message` rather than flattening to text. +- `transport-channel`: message envelopes reaching the agent loop must normalize into canonical `Message`. +- `session-loop`: session initialization must prefer `Task.input_message` over `Task.description` when constructing the first user turn. +- `plan-module`: `Task` gains canonical `input_message` for orchestration/message separation. + +## Impact + +- Affected code: `agent/*`, `plan/types.py`, `transport/*`, `client/runtime/task_runner.py`, active examples, message-schema tests. +- Affected APIs: `IAgent.__call__`, `IAgentOrchestration.execute`, `Task`. +- Systems: direct runtime entry, transport poll loop, session initialization, example/client CLIs. diff --git a/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/plan-module/spec.md b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/plan-module/spec.md new file mode 100644 index 00000000..f5fd6559 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/plan-module/spec.md @@ -0,0 +1,14 @@ +## MODIFIED Requirements + +### Requirement: Task carries orchestration description separately from canonical input +The planning module SHALL allow `Task` to retain orchestration metadata without becoming the sole carrier of user prompt semantics. + +- `Task` MUST expose `input_message: Message | None`. +- `Task.description` remains available for plan/milestone descriptions. +- `Task.to_milestones()` SHOULD prefer `input_message.text` for `Milestone.user_input` when present. + +#### Scenario: Milestone user input follows canonical task message +- **GIVEN** a `Task` with `description="refactor auth"` and `input_message.text="please refactor auth and review these screenshots"` +- **WHEN** default milestones are derived +- **THEN** the milestone description remains `"refactor auth"` +- **AND** `Milestone.user_input` uses the canonical message text diff --git a/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/rich-media-message-schema/spec.md b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/rich-media-message-schema/spec.md new file mode 100644 index 00000000..44a218f4 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/rich-media-message-schema/spec.md @@ -0,0 +1,13 @@ +## MODIFIED Requirements + +### Requirement: Canonical message schema supports text, attachments, and structured data +The framework SHALL preserve canonical `Message` semantics at the runtime input boundary instead of flattening top-level input to raw text. + +- Top-level agent execution MUST accept canonical `Message` input directly. +- Direct user-prompt helpers MUST prefer canonical `Message` over `Task.description`. +- `Message.kind="tool_call" | "tool_result"` MUST reject construction without structured `data`. + +#### Scenario: Direct execution keeps a composite user message intact +- **WHEN** a caller invokes an agent with `Message(kind="chat", text=, attachments=[image...])` +- **THEN** the runtime uses that canonical message as the first user turn +- **AND** `attachments` remain available to context assembly and model serialization diff --git a/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/session-loop/spec.md b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/session-loop/spec.md new file mode 100644 index 00000000..202c6858 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/session-loop/spec.md @@ -0,0 +1,13 @@ +## MODIFIED Requirements + +### Requirement: Previous session summary handoff +The runtime SHALL keep the session-loop user-turn source distinct from orchestration description text. + +- `Task` MAY carry `input_message: Message`. +- Session initialization MUST prefer `Task.input_message` over synthesizing a user message from `Task.description`. + +#### Scenario: Session loop uses canonical task input message +- **GIVEN** a `Task` whose `description` is orchestration text and whose `input_message` is `Message(kind="chat", text=, attachments=[image])` +- **WHEN** the session loop initializes +- **THEN** STM receives `Task.input_message` as the first user turn +- **AND** the runtime does not replace it with a plain-text message from `description` diff --git a/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/transport-channel/spec.md b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/transport-channel/spec.md new file mode 100644 index 00000000..8d47a1dc --- /dev/null +++ b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/specs/transport-channel/spec.md @@ -0,0 +1,13 @@ +## MODIFIED Requirements + +### Requirement: Agent loop consumes prompt messages only +The runtime integration with `AgentChannel.poll()` SHALL normalize inbound `MessagePayload` into canonical `Message` before invoking agent execution. + +- The transport loop MUST NOT flatten `MessagePayload` into plain text. +- `text`, `attachments`, and `data` from `MessagePayload` MUST be preserved in the canonical `Message` passed into agent execution. + +#### Scenario: Polled composite chat message reaches agent intact +- **GIVEN** `AgentChannel.poll()` returns `TransportEnvelope(kind="message", payload=)` +- **WHEN** the base agent transport loop executes that input +- **THEN** the concrete agent receives a canonical `Message` +- **AND** the message still contains the image attachment reference diff --git a/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/tasks.md b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/tasks.md new file mode 100644 index 00000000..fb1d27a4 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-message-input-boundary-cleanup/tasks.md @@ -0,0 +1,5 @@ +- [x] Update design docs to separate canonical `Message` input from orchestration `Task.description`. +- [x] Add failing tests for transport canonical-message preservation, `Task.input_message`, and stricter `tool_call/tool_result` schema validation. +- [x] Add `Task.input_message`, normalize public `str` input at the agent boundary, and keep internal execution on canonical `Message`. +- [x] Update active client/example entry points to send canonical `Message` instead of `Task(description=...)` for direct prompts. +- [x] Refresh focused tests and replace metadata-only tool-call compatibility assertions with the new canonical schema contract. diff --git a/openspec/changes/p0-default-eventlog/.openspec.yaml b/openspec/changes/archive/2026-03-09-p0-default-eventlog/.openspec.yaml similarity index 100% rename from openspec/changes/p0-default-eventlog/.openspec.yaml rename to openspec/changes/archive/2026-03-09-p0-default-eventlog/.openspec.yaml diff --git a/openspec/changes/p0-default-eventlog/design.md b/openspec/changes/archive/2026-03-09-p0-default-eventlog/design.md similarity index 100% rename from openspec/changes/p0-default-eventlog/design.md rename to openspec/changes/archive/2026-03-09-p0-default-eventlog/design.md diff --git a/openspec/changes/p0-default-eventlog/proposal.md b/openspec/changes/archive/2026-03-09-p0-default-eventlog/proposal.md similarity index 100% rename from openspec/changes/p0-default-eventlog/proposal.md rename to openspec/changes/archive/2026-03-09-p0-default-eventlog/proposal.md diff --git a/openspec/changes/p0-default-eventlog/specs/core-runtime/spec.md b/openspec/changes/archive/2026-03-09-p0-default-eventlog/specs/core-runtime/spec.md similarity index 100% rename from openspec/changes/p0-default-eventlog/specs/core-runtime/spec.md rename to openspec/changes/archive/2026-03-09-p0-default-eventlog/specs/core-runtime/spec.md diff --git a/openspec/changes/p0-default-eventlog/specs/default-event-log/spec.md b/openspec/changes/archive/2026-03-09-p0-default-eventlog/specs/default-event-log/spec.md similarity index 100% rename from openspec/changes/p0-default-eventlog/specs/default-event-log/spec.md rename to openspec/changes/archive/2026-03-09-p0-default-eventlog/specs/default-event-log/spec.md diff --git a/openspec/changes/p0-default-eventlog/specs/observability/spec.md b/openspec/changes/archive/2026-03-09-p0-default-eventlog/specs/observability/spec.md similarity index 100% rename from openspec/changes/p0-default-eventlog/specs/observability/spec.md rename to openspec/changes/archive/2026-03-09-p0-default-eventlog/specs/observability/spec.md diff --git a/openspec/changes/p0-default-eventlog/specs/session-loop/spec.md b/openspec/changes/archive/2026-03-09-p0-default-eventlog/specs/session-loop/spec.md similarity index 100% rename from openspec/changes/p0-default-eventlog/specs/session-loop/spec.md rename to openspec/changes/archive/2026-03-09-p0-default-eventlog/specs/session-loop/spec.md diff --git a/openspec/changes/p0-default-eventlog/tasks.md b/openspec/changes/archive/2026-03-09-p0-default-eventlog/tasks.md similarity index 100% rename from openspec/changes/p0-default-eventlog/tasks.md rename to openspec/changes/archive/2026-03-09-p0-default-eventlog/tasks.md diff --git a/openspec/changes/p0-enforce-security-boundary/.openspec.yaml b/openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/.openspec.yaml similarity index 100% rename from openspec/changes/p0-enforce-security-boundary/.openspec.yaml rename to openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/.openspec.yaml diff --git a/openspec/changes/p0-enforce-security-boundary/design.md b/openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/design.md similarity index 100% rename from openspec/changes/p0-enforce-security-boundary/design.md rename to openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/design.md diff --git a/openspec/changes/p0-enforce-security-boundary/proposal.md b/openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/proposal.md similarity index 100% rename from openspec/changes/p0-enforce-security-boundary/proposal.md rename to openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/proposal.md diff --git a/openspec/changes/p0-enforce-security-boundary/specs/core-runtime/spec.md b/openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/specs/core-runtime/spec.md similarity index 100% rename from openspec/changes/p0-enforce-security-boundary/specs/core-runtime/spec.md rename to openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/specs/core-runtime/spec.md diff --git a/openspec/changes/p0-enforce-security-boundary/specs/define-trust-boundary/spec.md b/openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/specs/define-trust-boundary/spec.md similarity index 100% rename from openspec/changes/p0-enforce-security-boundary/specs/define-trust-boundary/spec.md rename to openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/specs/define-trust-boundary/spec.md diff --git a/openspec/changes/p0-enforce-security-boundary/specs/security-policy-gate/spec.md b/openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/specs/security-policy-gate/spec.md similarity index 100% rename from openspec/changes/p0-enforce-security-boundary/specs/security-policy-gate/spec.md rename to openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/specs/security-policy-gate/spec.md diff --git a/openspec/changes/p0-enforce-security-boundary/specs/validation/spec.md b/openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/specs/validation/spec.md similarity index 100% rename from openspec/changes/p0-enforce-security-boundary/specs/validation/spec.md rename to openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/specs/validation/spec.md diff --git a/openspec/changes/p0-enforce-security-boundary/tasks.md b/openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/tasks.md similarity index 100% rename from openspec/changes/p0-enforce-security-boundary/tasks.md rename to openspec/changes/archive/2026-03-09-p0-enforce-security-boundary/tasks.md diff --git a/openspec/changes/p0-step-driven-execution/.openspec.yaml b/openspec/changes/archive/2026-03-09-p0-step-driven-execution/.openspec.yaml similarity index 100% rename from openspec/changes/p0-step-driven-execution/.openspec.yaml rename to openspec/changes/archive/2026-03-09-p0-step-driven-execution/.openspec.yaml diff --git a/openspec/changes/p0-step-driven-execution/design.md b/openspec/changes/archive/2026-03-09-p0-step-driven-execution/design.md similarity index 100% rename from openspec/changes/p0-step-driven-execution/design.md rename to openspec/changes/archive/2026-03-09-p0-step-driven-execution/design.md diff --git a/openspec/changes/p0-step-driven-execution/proposal.md b/openspec/changes/archive/2026-03-09-p0-step-driven-execution/proposal.md similarity index 100% rename from openspec/changes/p0-step-driven-execution/proposal.md rename to openspec/changes/archive/2026-03-09-p0-step-driven-execution/proposal.md diff --git a/openspec/changes/p0-step-driven-execution/specs/core-runtime/spec.md b/openspec/changes/archive/2026-03-09-p0-step-driven-execution/specs/core-runtime/spec.md similarity index 100% rename from openspec/changes/p0-step-driven-execution/specs/core-runtime/spec.md rename to openspec/changes/archive/2026-03-09-p0-step-driven-execution/specs/core-runtime/spec.md diff --git a/openspec/changes/p0-step-driven-execution/specs/plan-module/spec.md b/openspec/changes/archive/2026-03-09-p0-step-driven-execution/specs/plan-module/spec.md similarity index 100% rename from openspec/changes/p0-step-driven-execution/specs/plan-module/spec.md rename to openspec/changes/archive/2026-03-09-p0-step-driven-execution/specs/plan-module/spec.md diff --git a/openspec/changes/p0-step-driven-execution/specs/session-loop/spec.md b/openspec/changes/archive/2026-03-09-p0-step-driven-execution/specs/session-loop/spec.md similarity index 100% rename from openspec/changes/p0-step-driven-execution/specs/session-loop/spec.md rename to openspec/changes/archive/2026-03-09-p0-step-driven-execution/specs/session-loop/spec.md diff --git a/openspec/changes/p0-step-driven-execution/specs/step-driven-execution/spec.md b/openspec/changes/archive/2026-03-09-p0-step-driven-execution/specs/step-driven-execution/spec.md similarity index 100% rename from openspec/changes/p0-step-driven-execution/specs/step-driven-execution/spec.md rename to openspec/changes/archive/2026-03-09-p0-step-driven-execution/specs/step-driven-execution/spec.md diff --git a/openspec/changes/p0-step-driven-execution/tasks.md b/openspec/changes/archive/2026-03-09-p0-step-driven-execution/tasks.md similarity index 100% rename from openspec/changes/p0-step-driven-execution/tasks.md rename to openspec/changes/archive/2026-03-09-p0-step-driven-execution/tasks.md diff --git a/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/.openspec.yaml b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/.openspec.yaml new file mode 100644 index 00000000..5cb9e8f6 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-03-09 diff --git a/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/design.md b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/design.md new file mode 100644 index 00000000..8bbfd7e3 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/design.md @@ -0,0 +1,107 @@ +## Context + +`agentscope-d1-d3-message-pipeline` 已经完成 canonical `Message` 与 transport typed payload 基线,但 transport 运行时仍保留三层历史兼容: + +1. `TransportEnvelope.event_type` 仍承担 reply 语义分派,和 typed payload 并行存在。 +2. `EnvelopeKind.MESSAGE/ACTION/CONTROL` 仍允许裸 `str` / 裸 `dict` payload 进入 runtime。 +3. client/example/test 仍大量围绕 `dict payload + event_type` 解析结果,而不是按 payload family 处理。 + +这让 envelope `kind`、payload schema、reply semantics 三者没有真正收敛,也继续放大维护成本。 + +## Goals / Non-Goals + +**Goals:** +- 删除 transport 主设计中的 `event_type`,不再把 reply 语义挂在 envelope 附加字符串上。 +- 删除 message/action/control 的 legacy raw payload 兼容,只接受 typed payload。 +- 让 success/error/result 语义回收到 `MessagePayload/ActionPayload/ControlPayload/SelectPayload` 自身。 +- 更新 runtime/client/example/tests,使 typed payload 成为唯一 transport 契约。 + +**Non-Goals:** +- 不修改 canonical `Message` schema。 +- 不处理 pending delta / cache / resume 等运行时状态设计。 +- 不新增 `status` / `error` 之类的 envelope kind。 + +## Decisions + +### Decision: `TransportEnvelope` 只保留 `kind + payload` + +删除 `event_type` 字段。reply 分派完全由 `EnvelopeKind` 与 payload 家族决定。 + +原因: +- `kind + event_type + payload` 是三套并行语义源。 +- typed payload 已经足够表达请求和响应的结构化语义。 + +替代方案: +- 保留 `event_type` 作为 deprecated 字段。 + - 不采用;仍会继续扩散分支判断和旧测试口径。 + +### Decision: typed payload 自身承接请求/响应双态 + +- `MessagePayload` + - 请求:`role=user`, `message_kind=chat` + - 响应:`role=assistant`, `message_kind=chat|thinking|tool_call|tool_result|summary` + - 错误:使用 `text + data{success/code/reason/...}` 表达 +- `ActionPayload` + - 请求:`resource_action + params` + - 响应:`resource_action + ok/result/code/reason` +- `ControlPayload` + - 请求:`control_id + params` + - 响应:`control_id + ok/result/code/reason` +- `SelectPayload` + - 保持 `ask|answered` + +原因: +- 不引入新的 envelope kind。 +- 请求和响应都保留在同一协议家族中,client 侧处理更稳定。 + +替代方案: +- 用 `kind=message` 包一个统一 dict reply。 + - 不采用;会重新引入 dict payload 弱结构。 + +### Decision: message request error 也回到 `MessagePayload` + +message 请求失败时返回: +- `kind=message` +- `payload=MessagePayload(role=assistant, message_kind=summary, text=, data={success:false, code, reason, target})` + +原因: +- 保持“消息请求得到消息回复”的对称性。 +- 不需要单独的 error envelope kind。 + +替代方案: +- message error 走 `control` 或 `action` reply。 + - 不采用;语义错位。 + +### Decision: transport JSON adapters 只做 typed payload 序列化/反序列化 + +WebSocket/stdio/direct adapters 保留“dataclass <-> JSON dict”的转换,但不再接受语义上的 raw string payload。 + +原因: +- JSON over-the-wire 和 runtime typed payload 并不冲突。 +- 需要保留网络边界的反序列化能力,但不保留 runtime 的 legacy schema。 + +## Risks / Trade-offs + +- [Breaking transport consumers] 现有 example/测试/CLI 解析将失效 + → Mitigation:同一 change 内同步更新 adapters、client helpers、tests。 + +- [Error semantics drift] message error 不再通过 `event_type=error` 判断 + → Mitigation:统一 `MessagePayload.data.success/code/reason` 结构,并增加 error-path tests。 + +- [Hook observability coupling] 现有 hook transport 事件依赖 `event_type` + → Mitigation:改为 `kind=message + MessagePayload.message_kind/data`,hook tests 同步切换。 + +## Migration Plan + +1. 更新 transport/context/model 文档,冻结 typed-reply cutover 设计。 +2. 修改 `TransportEnvelope` 与 default channel/dispatcher,移除 raw payload/event_type 兼容。 +3. 修改 agent transport loop 与 hook transport emitter,改发 typed replies。 +4. 修改 client/example/adapters/tests,切换为 typed payload 解析。 +5. 跑 transport/runtime/example 回归并完成 OpenSpec 证据回写。 + +Rollback: +- 该 change 明确允许 breaking change;如需回退,整体回退到引入 typed replies 前的 `event_type + dict payload` 契约。 + +## Open Questions + +- `MessagePayload.data` 的最小 reply 字段集是否需要进一步抽为共享 helper,还是先在 transport/runtime 内按约定使用。 diff --git a/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/proposal.md b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/proposal.md new file mode 100644 index 00000000..257d927b --- /dev/null +++ b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/proposal.md @@ -0,0 +1,43 @@ +## Why + +当前 transport 主链路虽然已经引入 typed payload,但运行时仍保留多层 legacy 兼容:`EnvelopeKind.MESSAGE/ACTION/CONTROL` 仍接受裸字符串或裸字典,reply 侧仍依赖 `event_type + dict payload` 包装,导致协议层存在双重真相源,client/example/test 也持续分叉。 + +消息格式切片已经完成,这个兼容壳现在反而阻碍 transport、agent loop、client 消费方彻底收敛到 typed payload 契约,因此需要单独做一次 non-compatible cutover。 + +## What Changes + +- **BREAKING** 删除 `TransportEnvelope.event_type` 作为 transport 主设计字段,reply 语义改由 typed payload 家族自身表达。 +- **BREAKING** 删除 `EnvelopeKind.MESSAGE/ACTION/CONTROL` 对裸 `str` / 裸 `dict` payload 的接收与分派兼容,只保留 typed payload。 +- 将 agent transport loop 的成功/失败回复改为 typed payload: + - `message` 请求回复 `MessagePayload` + - `action` 请求回复 `ActionPayload` + - `control` 请求回复 `ControlPayload` + - `select` 保持 `SelectPayload` +- 更新 stdio/websocket/direct client adapters、dispatcher、example CLI 和相关测试,统一消费 typed payload。 + +## Capabilities + +### New Capabilities +- `typed-transport-replies`: transport reply envelopes use typed payloads instead of `event_type + dict payload` + +### Modified Capabilities +- `transport-channel`: inbound/outbound envelope validation and routing become typed-payload-only +- `interaction-dispatch`: action/control dispatch contracts stop accepting legacy string payloads +- `chat-runtime`: agent transport loop returns canonical assistant message payloads and typed error/result envelopes + +## Impact + +- Affected code: + - `dare_framework/transport/types.py` + - `dare_framework/transport/_internal/default_channel.py` + - `dare_framework/transport/_internal/adapters.py` + - `dare_framework/transport/interaction/dispatcher.py` + - `dare_framework/transport/interaction/payloads.py` + - `dare_framework/agent/base_agent.py` + - `dare_framework/agent/react_agent.py` + - `dare_framework/hook/_internal/agent_event_transport_hook.py` + - client/example consumers using `DirectClientChannel.ask()` or raw transport events +- Affected APIs: + - transport envelope schema + - transport reply parsing in client/example code + - action/control request construction in tests/examples diff --git a/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/chat-runtime/spec.md b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/chat-runtime/spec.md new file mode 100644 index 00000000..374dd1a3 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/chat-runtime/spec.md @@ -0,0 +1,19 @@ +## MODIFIED Requirements + +### Requirement: LLM-driven execute loop +The runtime SHALL invoke the configured `IModelAdapter` during the execute loop, provide the assembled canonical message history and available tool definitions, and iterate over tool calls until the model returns a final response. + +- `Context.assemble()` MUST produce canonical `Message` objects rather than plain text-only entries. +- `IModelAdapter` MUST serialize canonical `Message` into provider-native request messages internally. +- The execute loop MUST append tool calls and tool results back into canonical message history before continuing. +- The transport-driven execute loop MUST return typed reply envelopes without using transport `event_type`. + +#### Scenario: Model returns a final response +- **WHEN** the model response contains no tool calls +- **THEN** the execute loop returns success and exposes the response content in the run output +- **AND** the transport reply is `EnvelopeKind.MESSAGE` carrying one assistant `MessagePayload` + +#### Scenario: Model requests a tool call +- **WHEN** the model response includes a tool call +- **THEN** the runtime executes the tool via `ToolRuntime`, appends the result to the canonical message history, and continues +- **AND** transport intermediate events are emitted as typed message payloads instead of `event_type` aliases diff --git a/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/interaction-dispatch/spec.md b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/interaction-dispatch/spec.md new file mode 100644 index 00000000..1bbffe78 --- /dev/null +++ b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/interaction-dispatch/spec.md @@ -0,0 +1,19 @@ +## MODIFIED Requirements + +### Requirement: Action dispatcher routes only typed action payloads +The action dispatch subsystem SHALL route only `ActionPayload` requests and SHALL return structured action results using typed payloads. + +- `ActionHandlerDispatcher.handle_action()` MUST read `resource_action` from `ActionPayload`. +- The dispatcher MUST NOT accept raw string action ids as request payloads. +- Successful action results MUST populate `ActionPayload.ok=True` and `ActionPayload.result`. +- Failed action results MUST populate `ActionPayload.ok=False` with deterministic `code/reason`. + +#### Scenario: Dispatcher routes supported action payload +- **WHEN** `handle_action()` receives `TransportEnvelope(kind="action", payload=)` +- **THEN** the matching handler is invoked +- **AND** the result is returned as a typed action payload + +#### Scenario: Dispatcher rejects unsupported action id +- **WHEN** `handle_action()` receives `ActionPayload` with an unknown `resource_action` +- **THEN** the dispatcher returns `ok=False` +- **AND** the error response includes a deterministic `code` and `reason` diff --git a/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/transport-channel/spec.md b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/transport-channel/spec.md new file mode 100644 index 00000000..322dff8a --- /dev/null +++ b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/transport-channel/spec.md @@ -0,0 +1,20 @@ +## MODIFIED Requirements + +### Requirement: AgentChannel rejects unsupported inbound envelope payloads deterministically +The runtime SHALL validate inbound transport envelopes at the channel boundary and reject unsupported payload families with deterministic error semantics before they reach agent execution. + +- `EnvelopeKind.MESSAGE` MUST accept only `MessagePayload`. +- `EnvelopeKind.ACTION` MUST accept only `ActionPayload`. +- `EnvelopeKind.CONTROL` MUST accept only `ControlPayload`. +- `EnvelopeKind.SELECT` MUST accept only `SelectPayload`. +- The runtime MUST NOT accept legacy raw `str` or weakly-typed `dict` payloads as transport request payloads. + +#### Scenario: Message envelope with invalid payload is rejected before execution +- **WHEN** the runtime receives `TransportEnvelope(kind="message")` whose payload is not a `MessagePayload` +- **THEN** the envelope is rejected before agent execution begins +- **AND** the requester receives a deterministic typed error reply + +#### Scenario: Action envelope with invalid payload is rejected before dispatch +- **WHEN** the runtime receives `TransportEnvelope(kind="action")` whose payload is not an `ActionPayload` +- **THEN** the dispatcher is not invoked +- **AND** the requester receives a deterministic typed error reply diff --git a/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/typed-transport-replies/spec.md b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/typed-transport-replies/spec.md new file mode 100644 index 00000000..36c6e03d --- /dev/null +++ b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/specs/typed-transport-replies/spec.md @@ -0,0 +1,24 @@ +## ADDED Requirements + +### Requirement: Transport replies use typed payload families +The transport layer SHALL return reply envelopes using the same typed payload families as requests, without relying on `event_type` or weakly-typed dictionary wrappers. + +- `EnvelopeKind.MESSAGE` replies MUST carry `MessagePayload`. +- `EnvelopeKind.ACTION` replies MUST carry `ActionPayload`. +- `EnvelopeKind.CONTROL` replies MUST carry `ControlPayload`. +- `EnvelopeKind.SELECT` replies MUST carry `SelectPayload`. + +#### Scenario: Agent message request returns assistant message payload +- **WHEN** an agent finishes handling a `TransportEnvelope(kind="message", payload=)` +- **THEN** the reply envelope uses `kind="message"` +- **AND** the reply payload is one `MessagePayload` representing the assistant-visible result + +#### Scenario: Action request returns typed action payload +- **WHEN** the runtime completes one `TransportEnvelope(kind="action", payload=)` +- **THEN** the reply envelope uses `kind="action"` +- **AND** the reply payload is one `ActionPayload` containing `ok/result/code/reason` + +#### Scenario: Control request returns typed control payload +- **WHEN** the runtime completes one `TransportEnvelope(kind="control", payload=)` +- **THEN** the reply envelope uses `kind="control"` +- **AND** the reply payload is one `ControlPayload` containing `ok/result/code/reason` diff --git a/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/tasks.md b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/tasks.md new file mode 100644 index 00000000..aec2a3dd --- /dev/null +++ b/openspec/changes/archive/2026-03-09-transport-typed-payload-cutover/tasks.md @@ -0,0 +1,19 @@ +## 1. Transport envelope cutover + +- [x] 1.1 Remove `TransportEnvelope.event_type` and legacy raw payload acceptance from `dare_framework/transport/types.py` +- [x] 1.2 Update default channel and dispatcher so `message/action/control/select` accept only typed payload families and reply with typed payloads + +## 2. Agent/runtime reply cutover + +- [x] 2.1 Update `BaseAgent` transport loop and `ReactAgent` transport emitters to return typed message/action/control replies without `event_type` +- [x] 2.2 Remove `build_success_payload/build_error_payload` dict-wrapper usage from transport/runtime paths + +## 3. Client/adapters/examples + +- [x] 3.1 Update transport adapters (`stdio`/`websocket`/`direct`) and client event pumps to parse/render typed replies +- [x] 3.2 Update examples and CLI helpers to send typed request payloads and consume typed replies + +## 4. Verification + +- [x] 4.1 Update transport/runtime/example tests that still assert `event_type` or legacy raw payloads +- [x] 4.2 Run focused transport/runtime regressions and broader regression suite; sync evidence/docs/tasks diff --git a/openspec/changes/unify-agent-output-envelope/.openspec.yaml b/openspec/changes/archive/2026-03-09-unify-agent-output-envelope/.openspec.yaml similarity index 100% rename from openspec/changes/unify-agent-output-envelope/.openspec.yaml rename to openspec/changes/archive/2026-03-09-unify-agent-output-envelope/.openspec.yaml diff --git a/openspec/changes/unify-agent-output-envelope/design.md b/openspec/changes/archive/2026-03-09-unify-agent-output-envelope/design.md similarity index 100% rename from openspec/changes/unify-agent-output-envelope/design.md rename to openspec/changes/archive/2026-03-09-unify-agent-output-envelope/design.md diff --git a/openspec/changes/unify-agent-output-envelope/proposal.md b/openspec/changes/archive/2026-03-09-unify-agent-output-envelope/proposal.md similarity index 100% rename from openspec/changes/unify-agent-output-envelope/proposal.md rename to openspec/changes/archive/2026-03-09-unify-agent-output-envelope/proposal.md diff --git a/openspec/changes/unify-agent-output-envelope/specs/core-runtime/spec.md b/openspec/changes/archive/2026-03-09-unify-agent-output-envelope/specs/core-runtime/spec.md similarity index 100% rename from openspec/changes/unify-agent-output-envelope/specs/core-runtime/spec.md rename to openspec/changes/archive/2026-03-09-unify-agent-output-envelope/specs/core-runtime/spec.md diff --git a/openspec/changes/unify-agent-output-envelope/tasks.md b/openspec/changes/archive/2026-03-09-unify-agent-output-envelope/tasks.md similarity index 100% rename from openspec/changes/unify-agent-output-envelope/tasks.md rename to openspec/changes/archive/2026-03-09-unify-agent-output-envelope/tasks.md diff --git a/openspec/specs/chat-runtime/spec.md b/openspec/specs/chat-runtime/spec.md index ab86bf73..0652c797 100644 --- a/openspec/specs/chat-runtime/spec.md +++ b/openspec/specs/chat-runtime/spec.md @@ -4,15 +4,22 @@ TBD - created by archiving change add-basic-chat-flow. Update Purpose after archive. ## Requirements ### Requirement: LLM-driven execute loop -The runtime SHALL invoke the configured `IModelAdapter` during the execute loop, provide the assembled prompt and available tool definitions, and iterate over tool calls until the model returns a final response. +The runtime SHALL invoke the configured `IModelAdapter` during the execute loop, provide the assembled canonical message history and available tool definitions, and iterate over tool calls until the model returns a final response. + +- `Context.assemble()` MUST produce canonical `Message` objects rather than plain text-only entries. +- `IModelAdapter` MUST serialize canonical `Message` into provider-native request messages internally. +- The execute loop MUST append tool calls and tool results back into canonical message history before continuing. +- The transport-driven execute loop MUST return typed reply envelopes without using transport `event_type`. #### Scenario: Model returns a final response - **WHEN** the model response contains no tool calls - **THEN** the execute loop returns success and exposes the response content in the run output +- **AND** the transport reply is `EnvelopeKind.MESSAGE` carrying one assistant `MessagePayload` #### Scenario: Model requests a tool call - **WHEN** the model response includes a tool call - **THEN** the runtime executes the tool via `ToolRuntime`, appends the result to the message history, and continues +- **AND** transport intermediate events are emitted as typed message payloads instead of `event_type` aliases ### Requirement: Interactive stdin/stdout chat example The system SHALL provide a stdin/stdout example that wires the OpenAI adapter, base prompt store, and local command tool into an agent to perform interactive dialogue. @@ -48,11 +55,18 @@ The canonical default `Context.assemble()` path SHALL include retrieval results ### Requirement: Retrieval query derives from current user intent The default assembly strategy SHALL derive retrieval query from the latest user-intent message in STM. +For `Message(kind="chat")`, retrieval query derivation MUST use the latest non-empty `text` field rather than transport envelope content. + #### Scenario: Latest user message drives retrieval query - **GIVEN** STM contains multiple turns including at least one user message - **WHEN** `Context.assemble()` is called - **THEN** LTM and Knowledge retrieval are invoked with the latest user message content as query +#### Scenario: Latest user chat text drives retrieval query +- **GIVEN** STM contains multiple turns including a latest user `Message(kind="chat")` with text +- **WHEN** `Context.assemble()` is called +- **THEN** LTM and Knowledge retrieval are invoked with that latest user text as query + ### Requirement: Budget-aware degradation for retrieval sources The default assembly strategy SHALL degrade retrieval under low remaining token budget before model invocation. @@ -67,3 +81,15 @@ The default assembly strategy SHALL degrade retrieval under low remaining token - **WHEN** `Context.assemble()` is called - **THEN** assemble still succeeds with available sources - **AND** metadata records degraded source reason + +### Requirement: Chat messages support text and image attachments in one turn +The runtime SHALL support a single chat message that contains both text and multiple image attachments. + +- `Message(kind="chat")` MUST allow non-empty `text` together with one or more image attachment references. +- The assembled message history MUST preserve the association as one logical user turn. +- Model adapters MAY degrade unsupported image history, but the canonical chat message structure MUST remain intact before adapter-specific conversion. + +#### Scenario: User sends one text and multiple images +- **WHEN** the runtime receives one user chat message with text and two image attachments +- **THEN** the message history contains one canonical `Message(kind="chat")` +- **AND** both image attachments remain associated with that same user turn diff --git a/openspec/specs/interaction-dispatch/spec.md b/openspec/specs/interaction-dispatch/spec.md index 19f35331..6a87337d 100644 --- a/openspec/specs/interaction-dispatch/spec.md +++ b/openspec/specs/interaction-dispatch/spec.md @@ -40,17 +40,20 @@ For deterministic action/control handling, the system SHALL emit responses corre - **WHEN** the handler completes successfully - **THEN** the system sends a response envelope with `reply_to="req-123"` -### Requirement: Deterministic handler errors use a unified payload schema -The system SHALL return deterministic action/control/message errors using a unified payload structure aligned with `TransportEnvelope`. +### Requirement: Deterministic handler errors use typed payload replies +The system SHALL return deterministic action/control/message errors using typed payload replies aligned with `TransportEnvelope.kind`. -- Successful payloads MUST include `kind`, `target`, `ok=true`, and `resp` (dict). -- Error payloads MUST include `kind`, `target`, `ok=false`, and `resp` with `code` and `reason`. -- Unsupported action operations MUST use `resp.code="UNSUPPORTED_OPERATION"`. +- Successful action results MUST populate `ActionPayload.ok=True` and `ActionPayload.result`. +- Failed action results MUST populate `ActionPayload.ok=False` with deterministic `code` and `reason`. +- Successful control results MUST populate `ControlPayload.ok=True` and `ControlPayload.result`. +- Failed control results MUST populate `ControlPayload.ok=False` with deterministic `code` and `reason`. +- Unsupported action operations MUST use `code="UNSUPPORTED_OPERATION"`. -#### Scenario: Action timeout returns structured error +#### Scenario: Action timeout returns typed error - **GIVEN** an action request exceeds configured timeout - **WHEN** the runtime returns an error response -- **THEN** the payload includes `code="ACTION_TIMEOUT"` +- **THEN** the payload is an `ActionPayload` +- **AND** it includes `code="ACTION_TIMEOUT"` - **AND** `reply_to` points to the request id ### Requirement: Standard runtime control signals are provided and dispatchable @@ -64,7 +67,7 @@ The interaction layer MUST NOT invoke the agent LLM execution path for control s Runtime control signals MUST be routed to a deterministic control handling path (e.g. `ControlHandler`). #### Scenario: Pause is not executed as a prompt -- **GIVEN** an inbound control envelope with `kind="control"` and `payload="pause"` +- **GIVEN** an inbound control envelope with `kind="control"` and `payload=` - **WHEN** the interaction layer processes the envelope - **THEN** it does not invoke the LLM-driven agent execution path @@ -76,7 +79,7 @@ The system SHALL provide a dedicated `AgentControlHandler` for runtime controls. - `interrupt` MUST cancel the current execution operation owned by agent runtime. #### Scenario: Interrupt maps to agent control path -- **GIVEN** an inbound control envelope with `payload="interrupt"` +- **GIVEN** an inbound control envelope with `payload=` - **WHEN** control handling executes - **THEN** the runtime invokes the agent interrupt method - **AND** the current execution operation is cancelled @@ -91,7 +94,7 @@ The system SHALL enforce a timeout for action handler execution in a single sess - **GIVEN** an action handler execution exceeds timeout - **WHEN** timeout is reached - **THEN** runtime terminates the action attempt -- **AND** runtime returns `type="error"` with timeout code +- **AND** runtime returns `ActionPayload(ok=False, code="ACTION_TIMEOUT")` ### Requirement: Standard action identifiers are provided for core interaction domains The system SHALL provide a stable set of standard action identifiers for deterministic interaction handling in the following initial domains: @@ -120,3 +123,47 @@ The system SHALL provide a deterministic action discovery operation. - **GIVEN** a stdio client sends `/` - **WHEN** adapter maps it to action `actions:list` - **THEN** the runtime returns a deterministic list of supported action ids + +### Requirement: Action dispatcher routes only typed action payloads +The action dispatch subsystem SHALL route only `ActionPayload` requests and SHALL return structured action results using typed payloads. + +- `ActionHandlerDispatcher.handle_action()` MUST read `resource_action` from `ActionPayload`. +- The dispatcher MUST NOT accept raw string action ids as request payloads. +- Successful action results MUST populate `ActionPayload.ok=True` and `ActionPayload.result`. +- Failed action results MUST populate `ActionPayload.ok=False` with deterministic `code/reason`. + +#### Scenario: Dispatcher routes supported action payload +- **WHEN** `handle_action()` receives `TransportEnvelope(kind="action", payload=)` +- **THEN** the matching handler is invoked +- **AND** the result is returned as a typed action payload + +#### Scenario: Dispatcher rejects unsupported action id +- **WHEN** `handle_action()` receives `ActionPayload` with an unknown `resource_action` +- **THEN** the dispatcher returns `ok=False` +- **AND** the error response includes a deterministic `code` and `reason` + +### Requirement: Select dispatch is isolated from action, control, and prompt execution +The system SHALL provide a deterministic dispatch path for `select` envelopes that is separate from prompt execution, action dispatch, and runtime control handling. + +- `select` dispatch MUST NOT enter the LLM prompt execution path. +- `select` dispatch MUST NOT be routed through `ActionHandlerDispatcher`. +- `select` dispatch MUST preserve `reply_to` correlation semantics for answered selections. + +#### Scenario: Select answer does not execute as a prompt +- **GIVEN** an inbound `TransportEnvelope(kind="select")` +- **WHEN** the interaction layer processes the envelope +- **THEN** it does not invoke the LLM-driven agent execution path +- **AND** it does not route the envelope through `ActionHandlerDispatcher` + +### Requirement: Approval interactions use select ask and answered semantics +The deterministic interaction layer SHALL represent approval interactions as `select` payloads. + +- Pending approval MUST use `SelectPayload(select_domain="approval", select_kind="ask")`. +- Resolved approval MUST use `SelectPayload(select_domain="approval", select_kind="answered")`. +- The runtime MUST NOT rely on message event aliases such as `approval.pending` or `approval.resolved` as the primary approval contract. + +#### Scenario: Approval resolution replies to the pending request +- **GIVEN** an approval request was emitted as `kind="select"` with `select_kind="ask"` +- **WHEN** the user resolves the approval +- **THEN** the resolution is expressed as `kind="select"` with `select_kind="answered"` +- **AND** the reply references the originating request envelope when available diff --git a/openspec/specs/rich-media-message-schema/spec.md b/openspec/specs/rich-media-message-schema/spec.md new file mode 100644 index 00000000..756deb5d --- /dev/null +++ b/openspec/specs/rich-media-message-schema/spec.md @@ -0,0 +1,37 @@ +# rich-media-message-schema Specification + +## Purpose +Define the canonical rich-media message schema used across transport, context, persistence, and model adapters. + +## Requirements + +### Requirement: Canonical message schema supports text, attachments, and structured data +The system SHALL define a canonical framework `Message` schema that is independent from provider-native message formats and can represent chat text, image attachments, and structured tool-related data. + +- `Message` MUST remain the single canonical message type inside the framework runtime. +- `Message` MUST expose `id`, `role`, `kind`, `text`, `attachments`, `data`, `name`, `metadata`, and `mark`. +- `Message.role` MUST use a deterministic finite type rather than unconstrained strings. +- `Message.kind` MUST distinguish at least `chat`, `thinking`, `tool_call`, `tool_result`, and `summary`. +- `Message.kind` SHOULD be represented by a deterministic finite type rather than unconstrained strings. +- `Message.attachments` MUST support image attachment references via a typed `AttachmentRef` structure rather than raw dictionaries. +- `Message.data` MUST be available for structured message kinds such as `tool_call` and `tool_result`. +- `Message.kind="thinking" | "summary" | "tool_call"` MUST reject attachments at schema validation time. +- `Message.data` MUST be the primary structured source for `tool_call/tool_result`; `metadata` MUST NOT be required as a semantic fallback. + +#### Scenario: Chat message carries text and multiple images +- **WHEN** a user message is represented as `Message(kind="chat")` with non-empty `text` and multiple image attachments +- **THEN** the runtime preserves both the text and all attachment references in a single canonical message + +### Requirement: Message payload normalizes losslessly into canonical message +The system SHALL define a `MessagePayload` transport contract that can be losslessly normalized into canonical `Message`. + +- `MessagePayload` MUST include `id`, `metadata`, `role`, `message_kind`, `text`, `attachments`, and `data`. +- `MessagePayload.role` and `MessagePayload.message_kind` SHOULD use deterministic finite types rather than unconstrained strings. +- `MessagePayload.message_kind` MUST map deterministically to `Message.kind`. +- For `message_kind="chat"`, the payload MUST support `text + attachments` without requiring a separate chat-specific body type. +- `MessagePayload` MUST reject unsupported attachment/message-kind combinations during payload validation. + +#### Scenario: Message payload becomes canonical message +- **WHEN** the runtime receives `TransportEnvelope(kind="message", payload=)` +- **THEN** the payload is normalized into one canonical `Message` +- **AND** no message semantics are taken from transport-only fields diff --git a/openspec/specs/transport-channel/spec.md b/openspec/specs/transport-channel/spec.md index efa69654..9279bf72 100644 --- a/openspec/specs/transport-channel/spec.md +++ b/openspec/specs/transport-channel/spec.md @@ -10,16 +10,18 @@ The system SHALL define a transport domain that exposes a client-facing adapter - **WHEN** a developer provides a `ClientChannel` - **THEN** the system can construct an `AgentChannel` that the agent uses for `poll`/`send` -### Requirement: Transport envelope is content-agnostic and distinct from context messages -The system SHALL define a `TransportEnvelope` type that is independent from `context.Message` and can carry arbitrary payloads with minimal metadata. +### Requirement: Transport envelope is independent from context messages and carries typed payload families +The system SHALL define a `TransportEnvelope` type that is independent from `context.Message` and carries one typed payload family selected by `kind`. -- The envelope MUST provide a primary strong-typed `kind` categorization that can distinguish `message|action|control`. -- For `kind="action"`, the payload MUST be a deterministic action id string in `resource:action` form (e.g. `tools:list`). -- For `kind="control"`, the payload MUST be a deterministic runtime control id string from `AgentControl` (e.g. `interrupt|pause|retry|reverse`). -- The envelope model MUST NOT require a separate subtype field (e.g. `type`) in order to route inbound envelopes. +- The envelope MUST provide a primary strong-typed `kind` categorization that can distinguish `message|select|action|control`. +- `kind="message"` MUST carry `MessagePayload`. +- `kind="select"` MUST carry `SelectPayload`. +- `kind="action"` MUST carry `ActionPayload`. +- `kind="control"` MUST carry `ControlPayload`. +- The envelope model MUST NOT require a separate subtype field (e.g. `event_type` or `type`) in order to route inbound envelopes. #### Scenario: Envelope carries a control interrupt -- **WHEN** a client sends a `TransportEnvelope` with `kind="control"` and `payload="interrupt"` +- **WHEN** a client sends a `TransportEnvelope` with `kind="control"` and `payload=` - **THEN** the channel can route it to control handling without relying on prompt parsing ### Requirement: Channel routes inbound envelopes by kind before agent consumption @@ -31,7 +33,7 @@ The system SHALL route inbound envelopes by `kind` in the channel implementation - If action/control handler bindings are missing at startup, runtime MUST fail fast with configuration error. #### Scenario: Action envelope does not enter message inbox -- **GIVEN** a channel receives `TransportEnvelope(kind="action", payload="tools:list")` +- **GIVEN** a channel receives `TransportEnvelope(kind="action", payload=)` - **WHEN** inbound routing is applied - **THEN** the channel invokes action handling path - **AND** `AgentChannel.poll()` does not return that action envelope @@ -116,15 +118,16 @@ These operations SHALL be handled without entering the model prompt execution pa ### Requirement: Envelope kind supports message, action, and control categories The transport envelope model SHALL provide a primary categorization field for inbound/outbound envelopes that can distinguish: -- `message` (prompt/result/hook style messages) +- `message` (canonical chat/thinking/tool/summary payloads) +- `select` (approval/choice/form interactions) - `action` (deterministic resource actions) - `control` (interrupt/pause/retry/reverse) -For `kind="control"`, implementations SHALL represent control variants via the payload value (e.g. `payload="interrupt"`). -The envelope model MUST NOT require a separate subtype field (e.g. `type`) in order to route inbound envelopes. +For `kind="control"`, implementations SHALL represent control variants via `ControlPayload.control_id`. +The envelope model MUST NOT require a separate subtype field (e.g. `event_type`) in order to route inbound envelopes. #### Scenario: Action envelope is distinguishable without slash parsing -- **GIVEN** a client sends `TransportEnvelope(kind="action", payload="tools:list")` +- **GIVEN** a client sends `TransportEnvelope(kind="action", payload=)` - **WHEN** the channel receives it - **THEN** the channel can route it deterministically without inspecting prompt text @@ -144,9 +147,28 @@ The runtime integration with `AgentChannel.poll()` SHALL treat polled envelopes Client entry adapters SHALL normalize input into explicit envelope kinds before transport routing. - `stdio` adapters MAY map slash commands to structured `ACTION/CONTROL` envelopes. -- `websocket` and `A2A` adapters MUST send explicit `kind` and structured `payload`; they MUST NOT rely on slash text inference inside transport runtime. +- `websocket` and `A2A` adapters MUST send explicit `kind` and typed `payload`; they MUST NOT rely on slash text inference or `event_type` aliases inside transport runtime. #### Scenario: Websocket sends explicit action envelope - **GIVEN** a websocket client wants tool introspection -- **WHEN** it sends `kind="action"` and `payload="tools:list"` +- **WHEN** it sends `kind="action"` and `payload=` - **THEN** channel routes the request through action path without text parsing + +### Requirement: AgentChannel rejects unsupported inbound envelope payloads deterministically +The runtime SHALL validate inbound transport envelopes at the channel boundary and reject unsupported payload families with deterministic error semantics before they reach agent execution. + +- `EnvelopeKind.MESSAGE` MUST accept only `MessagePayload`. +- `EnvelopeKind.ACTION` MUST accept only `ActionPayload`. +- `EnvelopeKind.CONTROL` MUST accept only `ControlPayload`. +- `EnvelopeKind.SELECT` MUST accept only `SelectPayload`. +- The runtime MUST NOT accept legacy raw `str` or weakly-typed `dict` payloads as transport request payloads. + +#### Scenario: Message envelope with invalid payload is rejected before execution +- **WHEN** the runtime receives `TransportEnvelope(kind="message")` whose payload is not a `MessagePayload` +- **THEN** the envelope is rejected before agent execution begins +- **AND** the requester receives a deterministic typed error reply + +#### Scenario: Action envelope with invalid payload is rejected before dispatch +- **WHEN** the runtime receives `TransportEnvelope(kind="action")` whose payload is not an `ActionPayload` +- **THEN** the dispatcher is not invoked +- **AND** the requester receives a deterministic typed error reply diff --git a/openspec/specs/typed-transport-replies/spec.md b/openspec/specs/typed-transport-replies/spec.md new file mode 100644 index 00000000..a3f2ad2d --- /dev/null +++ b/openspec/specs/typed-transport-replies/spec.md @@ -0,0 +1,29 @@ +# typed-transport-replies Specification + +## Purpose +Define the canonical transport reply contract so request and reply envelopes use the same typed payload families without envelope-level subtype aliases. + +## Requirements + +### Requirement: Transport replies use typed payload families +The transport layer SHALL return reply envelopes using the same typed payload families as requests, without relying on `event_type` or weakly-typed dictionary wrappers. + +- `EnvelopeKind.MESSAGE` replies MUST carry `MessagePayload`. +- `EnvelopeKind.ACTION` replies MUST carry `ActionPayload`. +- `EnvelopeKind.CONTROL` replies MUST carry `ControlPayload`. +- `EnvelopeKind.SELECT` replies MUST carry `SelectPayload`. + +#### Scenario: Agent message request returns assistant message payload +- **WHEN** an agent finishes handling a `TransportEnvelope(kind="message", payload=)` +- **THEN** the reply envelope uses `kind="message"` +- **AND** the reply payload is one `MessagePayload` representing the assistant-visible result + +#### Scenario: Action request returns typed action payload +- **WHEN** the runtime completes one `TransportEnvelope(kind="action", payload=)` +- **THEN** the reply envelope uses `kind="action"` +- **AND** the reply payload is one `ActionPayload` containing `ok/result/code/reason` + +#### Scenario: Control request returns typed control payload +- **WHEN** the runtime completes one `TransportEnvelope(kind="control", payload=)` +- **THEN** the reply envelope uses `kind="control"` +- **AND** the reply payload is one `ControlPayload` containing `ok/result/code/reason` From fd72bdb9909ed241ee28e39ad1a5448c7925d5bb Mon Sep 17 00:00:00 2001 From: mindfn Date: Mon, 9 Mar 2026 19:32:43 +0800 Subject: [PATCH 2/2] docs: wire rich media intent PR evidence Replace the placeholder governance links in the active rich-media message pipeline feature record with the real intent PR and review anchor URLs for PR #206. This makes the docs-only intent branch pass the governance evidence-truth gate and gives PR #204 a merged intent target it can reference for governance-intent-gate. --- docs/features/rich-media-message-pipeline.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/features/rich-media-message-pipeline.md b/docs/features/rich-media-message-pipeline.md index 8f7f7a77..d484d669 100644 --- a/docs/features/rich-media-message-pipeline.md +++ b/docs/features/rich-media-message-pipeline.md @@ -80,6 +80,6 @@ caching, compression, resume, and dispatch orchestration. ### Review and Merge Gate Links -- Intent PR: `TBD` +- Intent PR: `https://github.com/zts212653/Deterministic-Agent-Runtime-Engine/pull/206` - Implementation PR: `https://github.com/zts212653/Deterministic-Agent-Runtime-Engine/pull/204` -- Review request: `TBD` +- Review request: `https://github.com/zts212653/Deterministic-Agent-Runtime-Engine/pull/206#issuecomment-4023104454`