DSH / Atlas
2026-08-23implementedarchitecture

Client-Derived Presentation from Raw Session Tool Events

Client 从原始 Session 工具事件派生展示

Session history is a durable journal interface, while tool cards are Client presentation. Computing card views during `page` or `follow` would couple history reads to the Tools registry, Agent presets, restored scopes, presenter execution, and transient UI types. A `tool/result` does not repeat the tool name or arguments. Host-side result presentation therefore requires either a call index or a backward scan by `call

English

Problem

Session history is a durable journal interface, while tool cards are Client presentation. Computing card views during page or follow would couple history reads to the Tools registry, Agent presets, restored scopes, presenter execution, and transient UI types.

A tool/result does not repeat the tool name or arguments. Host-side result presentation therefore requires either a call index or a backward scan by callId; repeated scans over a tool-dense page can approach quadratic work because maxMessages does not directly bound the event count.

Host projection would also duplicate structured data. Read, diff, search, and web results already persist bounded facts in tool/result.data.meta; another view object increases Remote payload size and Client decoding without adding durable meaning.

The Client already owns a complete tool-presentation entry point. ui-chat assembles tool/call, tool/result, and PTC dispatch events into stable ToolCallBlock values. ui-tool owns the recursive call tree, the tool.call.toolview keyed slot dispatched by tool name, the Generic fallback, card models, and details output. A business Client plugin can register a renderer for its own tool names.

Splitting presentation between Host presenters and Client keyed renderers creates two interpretations of the same event. The keyed renderer is the Web extension point, so an intermediate Host view provides no independent Web capability.

ToolDefinition.presentCall and presentResult remain useful Host APIs even though ACP is automation-only and the repository has no production TUI consumer. Removing their definitions is a separate decision from keeping Session reads independent of presentation.

The required result is one raw Session journal and one Client presentation owner without visual degradation or incidental enhancement. Specialized cards, interactions, and PTC dispatch topology remain stable while the transport stops carrying transient views.

Decision

The visual-equivalence requirements below exclude the separately approved nested terminal-card fix, recorded tool details, and background Shell command display; all other presentation and ownership constraints remain.

The Session Remote journal sends only raw, validated, persistable Session events. session.page and session.follow do not parse tool arguments, query the Tools registry, restore a presenter scope, execute presentCall or presentResult, or construct or clone any tool view.

The Client Conversation layer continues to own tool call/result identity, pairing, lifecycle, PTC dispatch topology, and stable Chat Nodes. It does not interpret individual tool names or produce terminal, diff, read, search, or web component props.

Client ui-tool continues to own card models and concrete renderers. Each card model directly reads the tool name, raw arguments, result content, error, durable metadata, Session cwd, and Host home from ToolCallBlock, and produces the same component props as the current page.

The Client has no second presenter registry. Tool-name dispatch uses only the existing tool.call.toolview keyed slot. Pure Client card-model helpers are renderer implementation details, not a Cordis service, public registry, or wire DTO.

The Host ToolDefinition.presentCall, ToolDefinition.presentResult, ToolCallView, ToolResultView, and existing tool presenter implementations remain. The Session Controller does not invoke them, and the Client does not import or consume them. A future non-Client consumer is outside this decision.

ToolOutputDefinition.presentationMeta and durable tool/result.data.meta remain. They carry execution-result facts required by existing specialized cards that the model-visible result text cannot represent losslessly. The Client validates and consumes meta directly rather than requiring the Host to convert it into a view during history reads.

Goals and non-goals

CategoryDecision
AbsentSessionEventEntry.view, SessionToolView, and SessionToolCallView
AbsentviewFor, backscanArgs, parseToolCall, jsonView, and presenter-scope lookup from history.ts
AbsentopenCalls and fallback event scans used only for follow presentation
Absentthe Client Session's parallel views array, Conversation input view, and Tool block callView/resultView
Derivedterminal, diff, read, search, and web card models read raw blocks and metadata
DerivedDeliverables reads successful mutation names and arguments
RetainedHost ToolDefinition.presentCall/presentResult APIs, types, implementations, and direct tests
Retainedoutput.presentationMeta and durable tool/result.data.meta
Retainedthe Session log format, Remote journal lifecycle, and Conversation identity/topology
Retainedthe existing keyed slot, Generic fallback, and Chat, Details, and Trajectory structure
Forbiddena new Client presenter service, parallel registry, or wire renderer id
Forbiddennew cards, visual redesign, interaction redesign, or PTC dispatch rich-card enhancements except the nested terminal-card exception, recorded tool details, and background Shell command display
Forbiddencompatibility dual-writing, version negotiation, or retention of the old view field

Terminology

Raw Session event means a SessionEvent fact from the durable log, including the name and raw arguments string on tool/call, and the content, isError, structured error, and optional meta on tool/result.

Durable metadata means the JSON value produced by ToolOutputDefinition.presentationMeta after a tool succeeds and stored in tool/result.data.meta. It is part of the result facts, not a pre-laid-out React or card DTO.

Host tool view means the ToolCallView or ToolResultView returned by ToolDefinition.presentCall or presentResult. Session Remote does not transport it.

Client card model means the pure props data under ui-tool/src/client/tool/models/ consumed directly by TerminalBlock, DiffBlock, ReadBlock, SearchBlock, WebBlock, or ToolRow.

Specialized card means the structured terminal, diff, read, search, or web body. Titles, summaries, lifecycle styling, business glyphs, and ordinary IN/OUT text remain part of the generic tool row.

Equivalent means that the same supported input produces the user-visible result and interaction pinned by the existing component, assembly, and browser evidence. It does not require the same intermediate TypeScript types or internal calls.

No enhancement means that this decision does not give an input pinned to Generic fallback a new specialized card or expand an existing card's data or interactions.

Architecture and Ownership

Tool execution and persistence

  1. A tool registers output.schema, output.render, and optional output.presentationMeta.
  2. Successful execution produces a canonical JSON value.
  3. The Tools runtime snapshots, schema-validates, and freezes the value.
  4. output.render(args, value) produces model-visible ContentBlock[].
  5. When a top-level call declares output.presentationMeta, the runtime also produces JSON-safe metadata.
  6. The agent loop writes the model-visible result and metadata into a tool/result Session event.
  7. The Session log does not store ToolCallView or ToolResultView.

Host journal reads

  1. session.page obtains attached or persisted events.
  2. paginate() cuts pages on append-origin user/assistant message boundaries.
  3. A tail page obtains its baseline from the registered projection snapshot/restore path.
  4. Every page entry contains only {event}.
  5. session.follow establishes its listener before catch-up reads, emits the opening cursor, and then streams contiguous {event} frames.
  6. Neither path resolves a preset or Tools scope for presentation, parses tool arguments, invokes presenters, or indexes calls.

Client data and presentation

  1. The Client Session stores one contiguous raw event window.
  2. SessionEventSource publishes SessionEventEntry values containing only events.
  3. ui-conversation folds each event without a presentation companion.
  4. The Chat and Trajectory Tool Definitions pair top-level calls and results by callId and assemble PTC dispatch subtrees.
  5. RunningToolCall and ToolResultNode retain raw facts, metadata, and existing parent identity.
  6. ToolCallTree dispatches tool.call.toolview by wire tool name.
  7. ui-tool derives card component props from the block at the render site.

Production consumer audit

ObjectProducerProduction consumerDecision
presentCall/presentResultHost toolsnon-Client callers, if anyretained outside Session Remote
SessionEventEntry.viewnonenoneabsent from the wire
callView/resultViewnonenoneabsent from the Client model
presentationMetaTools runtimetool/result, Client card models, and Host presentersretained durable input
fixture presenter mirrornonenonefixtures send raw metadata

ACP does not consume a Session tool view or map Host render intent. The repository has no production TUI consumer. Host presenters remain available without making Session Remote their transport.

Data Flow

Tool execute
  -> canonical value
  -> output.render(args, value)
  -> model-visible result content
  -> output.presentationMeta(args, value), when declared
  -> durable tool/result event

Session page/follow
  -> raw Session event envelope
  -> no tool lookup
  -> no preset lookup for presentation
  -> no call backscan
  -> no render-intent serialization

Client SessionEventSource
  -> Conversation Tool Definition
  -> root call/result pairing + PTC dispatch topology
  -> ToolCallBlock(name, argsRaw, content, error, meta)
  -> tool.call.toolview keyed dispatch
  -> Client card model
  -> existing React component

This path retains one durable metadata projection because it runs while the canonical result is still in memory. It removes the second presentation projection performed while reading history.

Layer responsibilities

LayerOwnsDoes not own
Tools runtimeexecution, canonical value, model text, replayable metadataWeb card selection and component props
Session logdurable facts, ordering, replaytransient card DTOs
Session Controlleraddressing, authority, cold reads, pagination, follow, projection baselinetool lookup, presenters, presentation scope
Client SessionRemote journal lifecycle and contiguous windowtool meaning and card types
Conversation Tool Definitioncall/result pairing, lifecycle, root/subcall topologymapping a tool name to a component
ui-toolcard models, Generic fallback, Chat/Details presentationSession pagination and the Host registry
Business Client pluginkeyed renderer for its own tool nameroot/subcall assembly and a global registry
ui-deliverablesproduced paths for current first-party mutationsUI cards or Host render intent

Remote and Durable Data Contracts

SessionEventEntry

SessionEventEntry remains the journal-entry envelope and contains only event: SessionWireEvent. This change does not also turn page entries into bare events or refactor the general RemoteJournalStream entry contract.

SessionPage.events remains SessionEventEntry[].

SessionFollowFrame remains either an opening frame or an event frame containing event.

SessionToolCallView, SessionToolView, and SessionEventEntry.view are deleted.

The Client connection stops re-exporting ToolCallView and ToolResultView from dsh-tools/presentation for Session consumers.

Generated catalogs and graphs derive the narrowed Remote types and package dependencies from their owning sources.

Durable log

  • tool/call.data.name remains unchanged.
  • tool/call.data.arguments remains the model-produced raw JSON string.
  • tool/result.data.message.content remains the model-visible result.
  • tool/result.data.error remains the structured failure identity.
  • tool/result.data.meta remains a tool-private JSON value.
  • Client card models do not write to the Session log.
  • Renderer keys and Host tool implementation ids do not enter the Session log.
  • Existing durable Sessions need no migration, and SESSION_FORMAT_VERSION does not change.

presentationMeta

presentationMeta is not a Host tool view. It reads the canonical value when tool execution completes, and that value is not persisted. Removing it would make the following existing presentation impossible to reconstruct losslessly:

  • read path, offset, lines, totalLines, and lang;
  • applied contextual hunks for write/edit;
  • grouped grep/glob results, truncation flag, and total;
  • web_search source fields and provider answer;
  • web_fetch final URL, HTTP status, and effective truncation flag.

The Client narrows meta locally at runtime. Renaming presentationMeta to more neutral result metadata is outside this decision.

Host Design

After obtaining source events, SessionHistoryController.page() performs only pagination and the existing projection-baseline calculation. Attached Sessions use the projection registry snapshot; detached Sessions use its restore path over the inspected log. History does not mount a preset to change the registered projection set.

SessionHistoryController.follow() retains listener-first setup, opening cursors, gap-free replay, live buffering, cancellation, and teardown. It maintains no additional state for tool events.

The controller has no presenterScopeFor(), viewFor(), backscanArgs(), parseToolCall(), or jsonView() path. Page state contains no presenter scope or argument resolver; follow state contains no openCalls, fallbackEvents, or presentation argument resolver. Each page/follow event is wrapped only as {event} while addressing, ownership, cursor, sequence, and projection logic remains intact.

An immutable event-conversion helper may remain narrow or be inlined; its name is irrelevant as long as history performs no presentation work.

Session Controller dependencies remain only when another package responsibility requires them. Manifest and project references contain no presentation-only dependency.

Performance constraints

  • page() performs no tool-specific work.
  • Adding tool results to a page does not cause repeated scans over existing page events.
  • follow() maintains no presentation index.
  • History does not trigger the Cordis tools service proxy.
  • History does not wait for a presenter standing scope.
  • History does not parse tool-argument JSON.
  • History does not perform tool-view JSON clones.
  • The Remote payload does not repeat structured data already expressed by meta.
  • The Client does not scan the complete Session event window to build one card.
  • The Client derives a card model again only when the corresponding immutable Tool block changes.

Client Session and Conversation

The Client Session has no private views array parallel to the raw event window. installWindow(), prependWindow(), and appendLive() handle only event entries, cursor/hasMore state, queues, projection, and notifications.

ConversationEventInput contains only event. The Conversation assembler does not know SessionToolView; its replace/prepend/append behavior, Context identity, Location, and publication cadence remain unchanged.

The Chat and Trajectory Tool Definitions read no views. They derive the following data from events:

  • callId;
  • tool name;
  • raw arguments;
  • turn, step, seq, and time;
  • result content;
  • isError and structured error;
  • result metadata;
  • root/subcall parent-child topology;
  • synthetic interruption results.

RunningToolCall has no callView.

ToolResultNode has no callView or resultView.

ToolCallBlock does not gain a generic view, card, kind, or locations field to replace the deleted fields. Concrete presentation remains the responsibility of ui-tool and keyed renderers.

Root and PTC dispatch subcalls

Host presenter APIs describe top-level calls and results. PTC dispatch subcalls retain Generic, flattened presentation for the diff, read, search, and web models covered here; supported terminal calls use the same eligibility rules as roots.

PTC dispatch start and result events already carry parentCallId. Conversation preserves that existing fact on each child ToolCallBlock; root Session calls omit it. The diff, read, search, and web models accept only blocks without parentCallId; the terminal model and existing renderers that intentionally support nested calls accept child blocks.

Shared card models apply the same terminal eligibility and nonterminal child restrictions wherever a block renders, so no second presentation surface needs a placement field; the details panel that once delegated a selected block was removed with the right-hand details column (decision).

The keyed slot continues dispatching every subcall by its real tool name. parentCallId restricts only the diff, read, search, and web structured models covered by this decision. Existing specialized renderers such as Skill and Cordis, which already read raw blocks, remain unchanged.

Missing call head

When a result node has no matching call in the current window, ToolResultNode.call remains null. The Client does not scan the window, issue another RPC, or infer a tool name from result text.

A specialized derivation that needs the name or arguments uses the current Generic fallback when call === null. A model that could use result metadata alone does not gain new presentation, because the current Host presentResult must first recover the matching call.

If a later older page supplies the call head, the Conversation Context rebuilds under existing replay rules and may then produce the already-supported specialized card.

Argument and metadata narrowing

The Client parses JSON from argsRaw; a parse failure returns the Generic form instead of throwing a React render error.

Chat and Details reuse parsing for the same block through pure helpers. Any future cache must use immutable block identity and must not create cross-Session global state keyed by callId.

Each specialized model checks only the fields it needs. The Client does not copy complete Host tool schemas or invoke a Host defineTool validator.

Valid first-party events must be equivalent to current presenter output. Malformed, old-version, or manually edited logs promise only a crash-free Generic fallback.

Client Card-Model Design

The existing ui-tool/src/client/tool/models/ directory remains the single source of shared derivation for Chat and Details. Helpers return component props directly; they do not return ToolCallView or ToolResultView, and they do not create an isomorphic ClientToolView union.

Branches on tool name exist only in ui-tool card models, existing row-classification tables, or the Client plugin that owns a keyed renderer for that tool. They must not enter the Session Controller, Client Session, Conversation assembler, or generic Slot renderer.

Unknown tools continue to use GenericToolCard with the name, raw arguments, result content, and error.

Generic tool row

toolRowModel() derives the generic row directly from toolName, argsRaw, result content, error, cwd, and home. It preserves:

  • classification into search, read, bash, write, edit, code, and others;
  • existing titles and tool-specific titles;
  • summary-field priority and single-line truncation;
  • comma joining of multiple queries;
  • cwd-relative paths and home abbreviation;
  • file-path clicks;
  • pretty JSON arguments and non-JSON raw-text fallback;
  • flattened result content and structured-error fallback;
  • running, ok, error, and stopped states.

The title, kind, rawInput, content, and locations from Generic Host presentCall do not currently drive an ordinary Web row. Generic presentResult.content also does not drive Web output, so the Client need not copy these unconsumed values.

<a id="terminal-card"></a>

Terminal card

The Client terminal model derives existing TerminalBlock props from the tool name, call arguments, result content, error, and Session cwd, independently of parentCallId. Background Shell command display governs explicit background bash/pwsh launches; the remaining ownership and fallback rules below stay in force.

InputPreserved result
running standard bash/pwsh foreground callterminal prompt, description, cwd, and running state
successful standard foreground callterminal output, exit code/signal, and settled state presentation
explicit background standard bash/pwshoriginal background flag with call running at Start; acknowledgement clears running and retains a static command without output or job status
tool execution errorGeneric IN/OUT and error summary
running persistent bash/pwshterminal prompt
settled persistent bash/pwshGeneric flattened result, with no new exit card
foreground terminal_sendterminal prompt and output
background/error terminal_sendGeneric result
PTC dispatch childsame terminal eligibility and fallback rules as a root call

Foreground standard shell results parse trailing [exit code: N] and [killed by signal: X] markers; explicit background launch acknowledgements do not enter output or exit-status parsing. A final recognized spill-policy notice selects Generic output instead: expandable in shell rows and raw in Details, because the exit marker may be displaced or omitted. A parsed marker is removed from the terminal body; timeout, sandbox denial, and markers without a pill remain in the body.

Call description remains above the card and overrides the collapsed summary. Workdir continues handling absolute, relative, and missing values. Relative paths resolve against the Session cwd while preserving normalization for ., .., drive letters, and UNC roots.

For terminal_send, non-empty input and the session id remain verbatim tool data; the empty-input fallback and session label resolve through the render site's conversation locale.

Standard and persistent providers sharing the same tool name are a special compatibility point. The Client uses currently valid argument and result features to preserve their delivered differences. Input that cannot be identified unambiguously uses a Generic settled result rather than gaining new presentation.

Outside the explicit background command-only presentation, TerminalBlock ANSI handling, cursor replay, wide characters, line limits, expansion, copying, and assistive text remain unchanged.

Diff card

InputPreserved result
running writeintended added-only diff from file_path and content
running editintended replacement diff from file_path, old_string, and new_string
running str_replace_editor createintended added-only diff from path and file_text
running str_replace_editor str_replaceintended replacement diff from path, old_str, and new_str
successful settled write/editapplied contextual hunks from meta.diffs
settled str_replace_editorGeneric, because the tool defines no result presenter
write create or missing/malformed/empty applied metadatacurrent argument fallback
error, malformed arguments, edit with malformed metadata, or PTC dispatch childGeneric

Paths, oldText:null, newText, result-over-call diff precedence, the eight-line Chat limit, full-height Details presentation, and file-opening behavior remain unchanged.

Read card

A running read continues to show only the summary row. A successful settled read reads path, offset, lines, totalLines, and lang from result metadata and confirms that the result is one text block matching the read envelope.

Missing metadata, malformed fields, a mismatched result envelope, an error, a missing call head, or a PTC dispatch child all use Generic. Cwd-relative path labels, home abbreviation, syntax language, total line count, the eight-line Chat limit, and full-height Details presentation remain unchanged.

The Client does not need to construct Host ReadResultView.content; Generic fallback can always read raw result content directly.

Search card

A running grep or glob continues to show only the argument summary. Successful results produce grouped matches or a path list from meta.shape:'matches' and meta.shape:'paths', respectively.

The Client validates path, lineNumber, line, truncated, and total. Empty matches or paths form a valid card. Missing or malformed metadata, an unknown shape, an error, a missing call head, or a PTC dispatch child uses Generic.

When truncated:true, the card continues to show a recovery locator from raw result content. It does not show one when untruncated. The eight-line Chat limit, full-height Details presentation, and expansion behavior remain unchanged.

Web card

A running web_search or web_fetch continues to show only the summary row. A successful search builds the card from meta.sources, meta.answer, and meta.truncated; a successful fetch builds it from meta.url, meta.statusCode, and meta.truncated.

The Client validates every source's url, title, snippet, and publishedAt, and continues rendering only http/https URLs as links. Missing or malformed metadata, an error, a missing call head, or a PTC dispatch child uses Generic.

Search answer text, source ordering, label fallback, and truncation notice remain unchanged. The fetch final URL, status, truncation notice, and raw body below Details remain unchanged.

Renderers already using raw blocks

  • Todo rows continue deriving completed/active summaries from arguments.
  • Question rows continue deriving waiting, answered, cancelled, and interrupted states from result content and errors.
  • Skill rows continue deriving names and states from calls and results.
  • Cordis define/run/action rows continue deriving from calls, results, and their own Client services.
  • These renderers retain their props, slot keys, registration order, and visible results.

Deliverables

ui-deliverables derives mutation business facts independently of presentation intent, so produced-file behavior is not coupled to card screenshots.

The Deliverables Definition observes root tool/call and successful tool/result events by callId and retains a minimal Client-owned mutation candidate without scanning the Session window or depending on a UI renderer.

ToolMutation conditionPath source
writeany successful callfile_path
editany successful callfile_path
str_replace_editorcreate, str_replace, or insertpath
str_replace_editorviewproduces no path
Otherno current first-party mutation semanticsproduces no path

Failures, interruptions, orphan results, missing paths, and malformed arguments produce no deliverable. Paths retain first-seen deduplication, and results settled after the closing Assistant seq remain excluded.

This change does not add a general tool-side-effect registry. The ability for a Host-only third-party presenter to join Deliverables automatically through kind:'edit' or locations is intentionally removed. A future real third-party mutation requirement must use a Client business contribution and cannot restore Session views.

Fixtures and Test Data

The assembled RemoteMock scenario contains no handwritten presentCall(), presentResult(), viewFor(), or tool-view types. It supplies the same raw calls, result content, and result metadata as a real log.

FixtureRaw facts that must remain
terminalarguments and real result status markers
diffarguments and result meta.diffs
readresult metadata path/offset/lines/totalLines/lang
grep/globresult metadata shape/files or paths/truncated/total
webresult metadata sources/answer or url/statusCode/truncated
generic/customname, argsRaw, content, and error

The scenario does not import Host tool packages to compute page presentation and retains no presenter mirror. The same raw scenario drives built Web snapshots under jsdom; real-Host browser cases independently cover the network path.

Presentation-Equivalence Matrix

“Current presentation” is defined by committed component tests, assembly tests, and Web browser expected outputs. A transport or ownership refactor does not justify refreshing snapshots; an approved product change requires separate evidence.

ScenarioRequired presentation
unknown tool, runningGeneric row with tool name and argument summary
unknown tool, settledGeneric row and raw output
malformed argumentssafe Generic fallback
orphan resultcallId title and Generic output
interrupted callwarning/stopped state
foreground bash/pwshcurrent terminal prompt, body, cwd, and state
explicit background bash/pwshterminal Start and static command-only acknowledgement; launch errors retain Generic IN/OUT
persistent shellcurrent running terminal and settled Generic form
terminal_sendcurrent foreground terminal and background/error Generic form
write/editcurrent intended/applied diff and error fallback
readcurrent running summary, settled ReadBlock, and error fallback
grep/globcurrent grouped/path card, truncation, and recovery
web_search/web_fetchcurrent source/summary card and raw body
Todo/Question/Skill/Cordiscurrent specialized rows
PTC dispatch subcallterminal cards when eligible; diff/read/search/web remain Generic/flattened
Chat and Detailsidentical card fields for the same call
Trajectorycurrent identity, tree, selection, and details
Deliverablescurrent successful-mutation chips and links

Client Extension Contract

tool.call.toolview remains the sole tool UI registration mechanism. A tool that needs specialized Client presentation must have a Client plugin register its wire tool name.

The registrant receives the raw ToolCallBlock, Session path information, and host actions, and validates the argument and metadata fields it recognizes. It does not call the Host tool registry, depend on presentCall or presentResult, or require SessionEventEntry.view.

A tool with no Client renderer consistently degrades to Generic. Only one keyed registration for a tool name can be active, and duplicate keys continue to fail loudly.

A Session-scoped slot can express Client-side Session differences, but no renderer variant is inferred from a preset. A Host-only presenter does not grant a Web rich card automatically. This is the explicit boundary between “the Host describes presentation” and “the Client plugin owns presentation.”

Failures and Fallback

  • The Client treats arguments and metadata as wire JSON and narrows them at the consumption site.
  • Argument JSON parse failure uses Generic.
  • A known tool missing required fields uses Generic.
  • Missing or malformed metadata uses Generic, except successful write, whose current presenter preserves its argument-derived whole-file diff.
  • An error result does not show a success card merely because metadata is present.
  • A missing call head does not trigger guesses about the tool name or arguments.
  • Unknown metadata fields are ignored.
  • A new metadata variant uses Generic in an older Client.
  • Card-model helpers catch expected parse failures instead of relying on a React error boundary for ordinary fallback.
  • Unexpected failures inside a keyed renderer remain isolated by existing Slot error handling.

Same-Named Host Providers

The Host registry allows different scopes to provide different definitions under the same tool name. Through presenter scope, a Session view can theoretically select a different render intent by preset. After removing the view, the Client keyed slot observes only the wire name and cannot observe Host definition identity.

The notable current first-party examples are standard and persistent bash and pwsh. Client derivation uses valid argument and result features to preserve their delivered differences without a provider-id wire field. Malformed or custom same-name provider input that cannot be distinguished uses Generic.

This change does not promise to preserve differences expressed only through a Host presenter by third-party same-name providers. If the product later requires distinct Client presentation for same-name providers, it must define a stable, non-presentational Client identity and must not restore per-page Host view computation.

Shipped Scope

Session Controller

  • SessionEventEntry contains only the raw event.
  • Both Session tool-view types are absent.
  • History has no presentation imports, helpers, or page/follow presentation state.
  • Addressing, pagination, follow, and projection logic remain in the Session owner.
  • Host tests assert the raw journal contract.

Session Controller Client

  • Session.views is absent.
  • EventSource replace/prepend/append deltas remain unchanged.
  • Transport, fixture, and test-support types carry raw entries.
  • Event identity and reference stability remain unchanged.

UI Conversation, Chat, and Trajectory

  • Conversation input and Tool blocks contain no view fields.
  • Chat and Trajectory Tool Definitions read raw events.
  • Event pairing, Context replay, trees, and target snapshots remain unchanged.
  • Child Tool blocks preserve the existing PTC dispatch parentCallId; row and Details slot owner props add no separate placement field.

UI Tool and Deliverables

  • Card models derive from raw blocks and metadata.
  • Chat and Details share the same helpers.
  • Generic fallback and keyed dispatch remain unchanged.
  • Deliverables recognizes first-party mutation arguments.

Fixtures, documentation, and generated artifacts

  • Fixtures send only raw events and metadata.
  • Session Controller and Client README/JSDoc contracts describe the raw journal and Client presentation owner.
  • The tool cookbook documents the Web Client integration path.
  • This Agent Note is the decision owner; retained Host presenter notes keep their independent decisions.
  • Authored Remote types, dependencies, READMEs, pairing records, and generated references remain synchronized.

Verification Matrix

Host

  • page returns contiguous raw event entries.
  • follow returns an opening cursor and contiguous raw event entries.
  • page/follow behave identically without the Tools service.
  • A cold page does not resolve or mount a preset.
  • A tail page computes its baseline through the standard projection registry; provider availability follows the projection composition rather than a history-side setup path.
  • Addressing, ownership, message-aligned boundaries, and tail projection remain unchanged.
  • Listener-before-read, reconnect catch-up, and gap repair remain unchanged.
  • Many tool results do not trigger a backscan per result.
  • Wire results contain no view.

session-history-journal.host.spec.ts owns pagination, continuity, and history error behavior without presenter assertions.

Client Conversation

  • replace, prepend, and append accept entries without views.
  • Chat and Trajectory root call/result pairing remains unchanged.
  • The PTC dispatch tree remains unchanged.
  • Result-only fallback remains unchanged.
  • A synthetic interruption result copies no view.
  • Node identity across registry rebuild, older prepend, and live append remains unchanged.

Client card model

  • terminal produces the pinned props from raw arguments/content.
  • diff produces the pinned diffs from arguments/metadata.
  • read produces the pinned lines from metadata/content.
  • search produces the pinned grouped/path card and recovery from metadata/content.
  • web produces the pinned sources/fetch summary from metadata/content.
  • unknown, malformed, error, missing-call, and missing-metadata cases remain Generic.
  • absent and present parentCallId cases prove equal terminal eligibility and preserve Generic fallback for diff, read, search, and web descendants.
  • Chat and Details produce identical card fields for the same block.

Deliverables

  • Successful write/edit calls produce file_path.
  • str_replace_editor create/str_replace/insert calls produce path.
  • str_replace_editor view produces no path.
  • failure, interruption, malformed input, and orphan results produce no path.
  • First-seen deduplication and the closing-seq cutoff remain unchanged.

Assembly and browser

  • terminal, diff, read, search, and web browser expected outputs all pass without refresh.
  • Visible assertions for the tool tree, details, trajectory, and deliverables retain their expected values.
  • The built Client still displays the same cards after obtaining raw events from real Remote page/follow operations.
  • Fixtures and the real Host use the same Client derivation.
  • A minimal preset independently pins persistent-shell behavior.

Static and documentation

  • Production code contains no SessionToolView or SessionToolCallView.
  • Session history does not reference dsh-tools/presentation, ctx.tools, presenterScopeFor, or backscanArgs.
  • Client Conversation does not reference ToolCallView or ToolResultView.
  • Client models do not read callView or resultView.
  • The fixture defines no presenter mirror.
  • Host presentCall, presentResult, and presentationMeta remain.
  • No new Client registry or Host-to-Client presentation hint exists.
  • Affected authored types, READMEs, Agent Notes, catalogs, and graphs are synchronized.

Verification Commands

Changes to this decision use dsh-pre-push-checks to select commands for the final diff. Required evidence includes:

  • focused Session Controller history/transport tests;
  • ui-chat and ui-trajectory Tool Definition tests;
  • ui-tool terminal, diff, read, search, web, row, tree, and details tests;
  • ui-deliverables produced-file tests;
  • assembled RemoteMock and Client runtime tests;
  • affected Host and Client TypeScript faces;
  • lint and duplication;
  • per-file 100% coverage for affected source files;
  • DSH_SNAPSHOT=replay pnpm run test:web, without refreshing existing presentation goldens;
  • authored Remote type and TypeScript checks;
  • pnpm run doc-sync;
  • git diff --check.

Shipped Invariants

  • Session page/follow does not read the Tools registry or a presenter scope.
  • Session history has no callId backscan, presentation cache, or view clone.
  • A Remote Session entry carries no view.
  • The Session log and SESSION_FORMAT_VERSION remain unchanged.
  • Result metadata passes byte-for-byte through the log and Remote to the Client.
  • Conversation assembles ToolCallBlock only from raw events.
  • ToolCallBlock contains no Host render-intent fields.
  • The five structured card models read only raw blocks and Session path facts; only diff, read, search, and web use parentCallId to reject children.
  • Generic, Todo, Question, Skill, and Cordis rows remain unchanged.
  • Deliverables does not depend on render intent and preserves current paths.
  • Text, components, expanded content, states, links, and ordering for all first-party top-level tools remain unchanged.
  • Malformed, missing-metadata, error, orphan, and unknown-tool cases continue to fall back safely.
  • PTC dispatch diff, read, search, and web subcalls remain Generic and flattened; terminal subcalls follow root eligibility.
  • Chat, Details, and Trajectory behavior remains unchanged.
  • Existing Web browser expected outputs pass without refresh.
  • Host presenter APIs, implementations, and direct tests remain unchanged.
  • ACP output remains unchanged.
  • No new downstream presentation field or second Client registry is introduced.
  • Pagination cost no longer grows as the number of results multiplied by page event count.
  • Downstream payloads no longer duplicate result metadata in a card DTO.

Alternatives considered

Optimize only backscanArgs and retain views

Building one callId → {name,args} Map before processing a page would make backscan linear, and live follow already has an openCalls fast path. It would leave Host lookups, preset scopes, presenters, JSON clones, duplicate payloads, and dual ownership intact, so this alternative is rejected.

Add a presenter registry to the Client

Copying the presentCall and presentResult interfaces into the browser would duplicate the registration, lifecycle, fallback, and override semantics of the tool.call.toolview slot. Renderers would still have to convert presenter DTOs into component props, so this alternative is rejected.

Have the Conversation Tool Definition produce one unified view

This would put tool names and UI-card semantics into the target-neutral Conversation owner and recreate an intermediate DTO isomorphic to the Host view, so this alternative is rejected.

Delete presentationMeta

Read line structure, applied diffs, search grouping, web sources, and effective truncation cannot be recovered losslessly from model text. Parsing free-form text would also bind the UI to output wording, so this alternative is rejected.

Persist canonical tool results

This would enlarge the Session log, expose internal result structures, change the durable format, and potentially store objects far larger than presentation requires. Existing metadata is sufficient, so this alternative is rejected.

Delete Host presenter APIs

Deleting them would shrink more code, but the decision preserves Host presentCall and presentResult. Their APIs, implementations, tests, and types remain independent of Session Remote.

Import Host tool implementations into the Client

Tool packages include Node, filesystem, subprocess, or provider dependencies and cannot enter the browser bundle. The Client consumes only raw JSON and maintains narrow parsers inside its own renderers, so this alternative is rejected.

Query presentation from the Host per result

An on-demand RPC would turn one page read into N network calls and would still require Host lookups, scope restoration, callId recovery, and error coordination, so this alternative is rejected.

Allow presentation enhancements

Bundling richer PTC dispatch cards, missing-call-head inference, or other historical presentation enhancements with the ownership change would prevent snapshots from proving equivalence. This decision rejects that coupling; the nested terminal-card exception does not relax nonterminal child restrictions.

Accept temporary Generic degradation

Stopping view delivery before completing Client cards would temporarily degrade terminal, diff, read, search, web, and Deliverables behavior. Client-equivalent derivation and Host removal must land in the same releasable change.

Consequences

The decision removes presentation work, repeated scans, and duplicate view payloads from Session reads. Its cost is that the retained Host presenter and Client card derivation can evolve independently, so both sides require owner-specific tests and Web equivalence remains an explicit product constraint.

Client and Host logic drift

Each tool may have one Host render intent and one Client card derivation. They serve different consumers and do not share a runtime path. Unrefreshed browser expected outputs pin visual equivalence for the first-party Web experience, while Host presenter tests constrain only the Host API.

Same-named providers lack stable identity

A raw event records the tool name but not the specific ToolDefinition. The Client uses valid event fields to preserve differences between standard and persistent shells. Ambiguous custom or malformed input falls back to Generic; the wire has no extra hint for theoretical extensibility.

Metadata is unknown JSON

Old Sessions may lack fields, and manually edited logs may contain malformed values. Each Client model must narrow locally and cannot pass unknown arrays or objects directly into UI primitives.

Preset-owned projection availability

History does not compensate for projection units absent from the current composition. A preset-owned unit that must remain visible across a cold read requires the shared Session preparation/projection composition to make its definition available before restore; history must not regain a preset-mount or presenter setup branch.

Two targets must stay synchronized

Chat and Trajectory have separate Tool Definitions and both carry the raw fields. Card derivation remains only in ui-tool and cannot be copied into either Definition.

Deliverables has a hidden dependency

Deliverables is not a visual component, so its mutation parser must remain synchronized with supported first-party write tools. Dedicated tests pin file chips and Markdown links independently of card screenshots.

Fixtures can create false confidence

Fixtures send raw events and metadata rather than handwritten views. Real-Host assembly coverage remains necessary because fixture-only snapshots cannot prove the transport path.

Incorrectly refreshing snapshots

This change promises unchanged user-visible output. A snapshot difference must be fixed in Client derivation. Expected outputs must not be refreshed unless the owner separately approves a specific visual change.

Documentation drift

The Agent Note, package READMEs, cookbook, root rules, and generated references must change together whenever the raw journal or Client presentation owner changes. Host API documentation remains separate.

Remote protocol narrowing

The absence of optional view is a prerelease wire-type decision shared by all consumers. There is no compatibility shim, dual-writing, or version negotiation.

Relationship to Existing Decisions

The current terminal-card rules permit nested terminal cards, replacing only the terminal child-card prohibition and its visual-equivalence requirement. This note remains active for raw-journal ownership, Client derivation, and the diff/read/search/web child restrictions.

This note partially supersedes the implementation fact in Client tool presentation ownership that “card models receive Host views.” Its core decisions remain: ui-tool owns presentation, business plugins use keyed slots, and Conversation owns only lifecycle and topology.

This note preserves toolview dissolution: the Client still has one slot registration model and does not restore ToolViewRegistry.

This note narrows the consumer scope of the render-intent union. The Host APIs and types remain, while the Session Remote and Web Client do not consume them. This note owns the transport split without rewriting that presenter decision.

This note updates the entry contract from Session history and Remote event transport: the journal transports only raw events plus an independent projection baseline, not transient tool views.

This note follows Conversation Node assembly: the Tool Definition owns event pairing and the call tree, while concrete card models remain in ui-tool.

This note preserves result metadata from the canonical tool output contract, because it is the lossless, replayable input to Client derivation.

Deferred

  • A separate explicit decision may evaluate deleting Host presenters if they remain without production consumers; this decision does not prejudge it.
  • Specialized diff, read, search, and web cards for PTC dispatch subcalls require a separate design and visible-snapshot updates; terminal calls follow the linked current card rules.
  • A third-party mutation tool that joins Deliverables requires a new Client-owned contribution; this decision does not create a registry for an absent consumer.
  • Distinct Client presentation for same-named providers first requires a stable, non-presentational identity; it must not restore per-page Host views.
  • If Client card-model performance needs measurement, an immutable-block microbenchmark can be added; the shipped architecture already prohibits scanning the Session window.

中文

Problem

Session 历史是持久 journal 接口,工具卡片属于 Client 展示。在 page/follow 中计算卡片 view 会让历史读取依赖 Tools registry、Agent preset、恢复后的 scope、presenter 执行和临时 UI 类型。

tool/result 不重复记录工具名称和参数。Host 端结果展示因此需要 call index 或按 callId 回扫;maxMessages 不直接限制事件数量,工具密集页面上的重复扫描可能接近二次方成本。

Host 投影还会重复结构化数据。read、diff、search 与 web 结果已在 tool/result.data.meta 中持久化有界事实;另一份 view 只增加 Remote payload 与 Client 解码成本,不增加持久语义。

Client 已经拥有完整的工具展示入口。ui-chat 将 tool/call、tool/result 与 PTC dispatch 事件组装成稳定的 ToolCallBlock;ui-tool 拥有递归调用树、按工具名称分发的 tool.call.toolview keyed slot、Generic fallback、卡片模型和 details output;业务 Client 插件可以为自己的工具名称注册 renderer。

Host presenter 与 Client keyed renderer 分担展示会形成对同一事件的两套解释。keyed renderer 是 Web 扩展点,因此中间 Host view 不提供独立 Web 能力。

ToolDefinition.presentCall/presentResult 仍是保留的 Host API;ACP 采用 automation-only 协议,仓库也没有生产 TUI consumer。是否删除这些定义与 Session 读取是否独立于展示是两个决定。

所需结果是一条原始 Session journal 和一个 Client 展示 owner,且不发生可见退化或顺带增强。专用卡片、交互和 PTC dispatch 拓扑保持稳定,transport 不再携带临时 view。

Decision

下述展示对等要求不包含已独立批准的嵌套 terminal 卡片修复、记录结果的工具详情和后台 Shell 命令展示;其他展示与所有权约束全部保留。

Session Remote journal 只下发原始、已验证、可持久化的 Session event。session.page 和 session.follow 不解析工具参数,不查询 Tools registry,不恢复 presenter scope,不执行 presentCall/presentResult,也不构造或克隆任何 tool view。

Client Conversation 层继续负责工具调用与结果的 identity、配对、生命周期、PTC dispatch 拓扑和稳定 Chat Node。它不解释具体工具名称,也不生成 terminal、diff、read、search 或 web 组件 props。

Client ui-tool 继续负责 card model 和具体 renderer。每个 card model 改为直接读取 ToolCallBlock 中的工具名称、原始参数、结果内容、错误、持久 metadata、Session cwd 与 Host home,并生成与现有页面相同的组件 props。

Client 不建立第二套 presenter registry。工具名称分发只使用现有 tool.call.toolview keyed slot;Client 中的纯 card-model helper 属于 renderer 实现,不成为 Cordis service、公开 registry 或 wire DTO。

Host 的 ToolDefinition.presentCall、ToolDefinition.presentResult、ToolCallView、ToolResultView 及现有 presenter 实现全部保留。Session Controller 不调用它们,Client 不导入或消费它们;未来非 Client consumer 是否使用它们不属于本决定。

ToolOutputDefinition.presentationMeta 与持久 tool/result.data.meta 保留。它们携带模型可见结果文本无法无损表达、而现有专用卡片需要的执行结果事实。Client 直接校验并消费 meta,不要求 Host 在历史读取时再把它转换成 view。

目标与非目标

类别决定
不存在SessionEventEntry.view、SessionToolView、SessionToolCallView
不存在history.ts 的 viewFor、backscanArgs、parseToolCall、jsonView 与 presenter scope lookup
不存在follow 中只服务 presentation 的 openCalls 与 fallback event scan
不存在Client Session 的平行 views 数组、Conversation input 的 view、Tool block 的 callView/resultView
派生terminal、diff、read、search、web card model 读取 raw block/meta
派生Deliverables 读取成功 mutation 的名称与参数
保留Host ToolDefinition.presentCall/presentResult API、类型、实现与直接测试
保留output.presentationMeta 与持久 tool/result.data.meta
保留Session 日志格式、Remote journal 生命周期与 Conversation identity/topology
保留现有 keyed slot、Generic fallback、Chat、Details 与 Trajectory 结构
禁止新 Client presenter service、平行 registry 或 wire renderer id
禁止新卡片、视觉改版、交互改版或 PTC dispatch rich-card 增强,嵌套 terminal 卡片例外、记录结果的工具详情和后台 Shell 命令展示除外
禁止为兼容保留双写、版本协商或旧 view 字段

术语

原始 Session event指持久日志中的 SessionEvent 事实,包括 tool/call 的 name 与原始 arguments 字符串,以及 tool/result 的 content、isError、结构化错误和可选 meta。

持久 metadata指 ToolOutputDefinition.presentationMeta 在工具成功执行时生成并写入 tool/result.data.meta 的 JSON 值。它是结果事实的一部分,不是预先排版的 React 或 card DTO。

Host tool view指 ToolDefinition.presentCall/presentResult 返回的 ToolCallView/ToolResultView;Session Remote 不运输它。

Client card model指 ui-tool/src/client/tool/models/ 下直接供 TerminalBlock、DiffBlock、ReadBlock、SearchBlock、WebBlock 或 ToolRow 使用的纯 props 数据。

专用卡片指 terminal、diff、read、search 与 web 的结构化正文;标题、摘要、生命周期样式、业务图形和普通 IN/OUT 文本仍属于通用工具行。

对等指同一受支持输入产生由现有组件、组装与浏览器证据固定的用户可见结果和交互,不要求相同的中间 TypeScript 类型或内部函数调用。

无增强指本决定不让被固定为 Generic fallback 的输入获得新专用卡片,也不扩大已有卡片的数据或交互。

架构与所有权

工具执行与持久化

  1. 工具注册 output.schema、output.render 和可选 output.presentationMeta。
  2. 成功执行产生 canonical JSON value。
  3. Tools runtime 对 value 做快照、schema 校验和冻结。
  4. output.render(args, value) 生成模型可见 ContentBlock[]。
  5. 顶层调用若声明 output.presentationMeta,runtime 同时生成 JSON-safe metadata。
  6. Agent loop 把模型可见结果与 metadata 写入 tool/result Session event。
  7. Session log 不保存 ToolCallView 或 ToolResultView。

Host journal 读取

  1. session.page 取得 attached 或 persisted 事件。
  2. paginate() 按 append-origin user/assistant message 边界切页。
  3. tail page 通过已注册 projection 的 snapshot/restore 路径取得 baseline。
  4. 每个 page entry 只包含 {event}。
  5. session.follow 先建立 listener,再执行 catch-up read、发送 opening cursor 并流式下发连续 {event} frame。
  6. 两条路径都不为展示解析 preset/Tools scope、解析工具参数、调用 presenter 或建立 call index。

Client 数据与展示

  1. Client Session 保存一个连续 raw event window。
  2. SessionEventSource 发布只含 event 的 SessionEventEntry。
  3. ui-conversation 在没有 presentation companion 的情况下 fold 每个事件。
  4. Chat 与 Trajectory Tool Definition 按 callId 配对顶层 call/result,并组装 PTC dispatch 子树。
  5. RunningToolCall 与 ToolResultNode 保存 raw facts、metadata 与既有 parent identity。
  6. ToolCallTree 按 wire tool name 分发 tool.call.toolview。
  7. ui-tool 在 render site 从 block 派生 card component props。

生产消费者审计

对象生产者生产消费者决定
presentCall/presentResult各 Host 工具可能存在的非 Client caller保留在 Session Remote 之外
SessionEventEntry.view无无wire 不存在
callView/resultView无无Client model 不存在
presentationMetaTools runtimetool/result、Client card model 与 Host presenter保留的持久输入
fixture presenter mirror无无fixture 下发 raw metadata

ACP 不消费 Session tool view,也不映射 Host render intent。仓库没有生产 TUI consumer;Host presenter 保留,但 Session Remote 不作为其 transport。

数据流

Tool execute
  -> canonical value
  -> output.render(args, value)
  -> model-visible result content
  -> output.presentationMeta(args, value), when declared
  -> durable tool/result event

Session page/follow
  -> raw Session event envelope
  -> no tool lookup
  -> no preset lookup for presentation
  -> no call backscan
  -> no render-intent serialization

Client SessionEventSource
  -> Conversation Tool Definition
  -> root call/result pairing + PTC dispatch topology
  -> ToolCallBlock(name, argsRaw, content, error, meta)
  -> tool.call.toolview keyed dispatch
  -> Client card model
  -> existing React component

这条链路保留一次持久 metadata 投影,因为它发生在 canonical result 尚在内存时;删除的是读取历史时的第二次展示投影。

分层责任

层负责不负责
Tools runtime执行、canonical value、模型文本、可重放 metadataWeb 卡片选择和组件 props
Session log持久事实、顺序、回放临时 card DTO
Session Controller地址、权限、冷读、分页、follow、projection baselinetool lookup、presenter、展示 scope
Client SessionRemote journal 生命周期与连续窗口工具含义、卡片类型
Conversation Tool Definitioncall/result 配对、lifecycle、root/subcall topology工具名到组件的解释
ui-toolcard model、通用 fallback、Chat/Details 展示Session 分页与 Host registry
业务 Client 插件自有 tool name 的 keyed rendererroot/subcall 编排与全局 registry
ui-deliverables当前第一方 mutation 的 produced pathUI card 或 Host render intent

Remote 与持久数据约定

SessionEventEntry

SessionEventEntry 保留为 journal entry envelope,只含 event: SessionWireEvent。本次不顺带把 page entries 改成裸事件,也不重构 RemoteJournalStream 的通用 entry 约定。

SessionPage.events 仍是 SessionEventEntry[]。

SessionFollowFrame 仍是 opening frame 或带 event 的 event frame。

删除 SessionToolCallView、SessionToolView 和 SessionEventEntry.view。

Client connection 不再从 dsh-tools/presentation 转出 ToolCallView/ToolResultView 供 Session 消费。

生成 catalog 与 graph 从各自 source owner 派生已收窄的 Remote 类型和 package dependency。

持久日志

  • tool/call.data.name 保持原样。
  • tool/call.data.arguments 保持模型产生的原始 JSON 字符串。
  • tool/result.data.message.content 保持模型可见结果。
  • tool/result.data.error 保持结构化失败身份。
  • tool/result.data.meta 保持工具私有 JSON 值。
  • Client card model 不写入 Session log。
  • renderer key 与 Host tool implementation id 不写入 Session log。
  • 现有持久 Session 无需迁移,SESSION_FORMAT_VERSION 不变。

presentationMeta

presentationMeta 不是 Host tool view。它在工具执行完成时读取 canonical value,而该 value 不会持久化;删除它会使下列现有展示无法无损恢复:

  • read 的 path、offset、lines、totalLines 与 lang;
  • write/edit 的 applied contextual hunks;
  • grep/glob 的分组结果、截断标志与总数;
  • web_search 的来源字段与 provider answer;
  • web_fetch 的最终 URL、HTTP status 与有效截断标志。

Client 对 meta 做局部运行时收窄。是否把 presentationMeta 改名为更中性的 result metadata 不属于本决定。

Host 端设计

SessionHistoryController.page() 在取得 source events 后只执行分页与现有 projection baseline 计算。attached Session 使用 projection registry snapshot;detached Session 使用该 registry 对 inspected log 的 restore 路径。history 不通过挂载 preset 改变已注册的 projection 集合。

SessionHistoryController.follow() 保留 listener-first、opening cursor、gap-free replay、live buffering、取消和 teardown;它不为工具事件维护额外状态。

Controller 不存在 presenterScopeFor()、viewFor()、backscanArgs()、parseToolCall() 或 jsonView() 路径。page state 不含 presenter scope 或参数 resolver;follow state 不含 openCalls、fallbackEvents 或 presentation 参数 resolver。每个 page/follow event 只包装成 {event},地址、ownership、cursor、seq 与 projection 逻辑保持完整。

不可变 event 转换 helper 可以保持窄实现或内联;只要 history 不执行 presentation 工作,其名称没有语义。

Session Controller dependency 只在其他 package responsibility 需要时保留;manifest 与 project reference 不含 presentation-only dependency。

性能约束

  • page() 的工具相关工作为零。
  • 页面增加 tool result 不增加对既有页面事件的重复扫描。
  • follow() 不维护展示索引。
  • history 不触发 Cordis tools service proxy。
  • history 不等待 presenter standing scope。
  • history 不执行工具参数 JSON parse。
  • history 不执行 tool view JSON clone。
  • Remote payload 不重复携带 meta 已表达的结构化数据。
  • Client 不扫描完整 Session event window 生成单个卡片。
  • Client 只在对应 immutable Tool block 变化时重新派生 card model。

Client Session 与 Conversation

Client Session 不含与 raw event window 平行的私有 views 数组。installWindow()、prependWindow() 和 appendLive() 只处理 event entries、cursor/hasMore、queue、projection 与通知。

ConversationEventInput 只携带 event。Conversation assembler 不认识 SessionToolView,其 replace/prepend/append、Context identity、Location 与 publication cadence 不变。

Chat 和 Trajectory 的 Tool Definition 都不读取 view,而从事件生成以下数据:

  • callId;
  • tool name;
  • raw arguments;
  • turn、step、seq 与 time;
  • result content;
  • isError 与 structured error;
  • result metadata;
  • root/subcall parent-child topology;
  • interruption synthetic result。

RunningToolCall 不含 callView。

ToolResultNode 不含 callView 与 resultView。

ToolCallBlock 不新增通用 view、card、kind 或 locations 字段替代被删除字段。具体展示仍只属于 ui-tool 与 keyed renderer。

Root 与 PTC dispatch 子调用

Host presenter API 描述顶层 call/result。本决定覆盖的 diff、read、search 和 web model 对 PTC dispatch 子调用保留 Generic/flattened 展示;受支持的 terminal 调用使用与根调用相同的适用规则。

PTC dispatch start 与 result event 已经携带 parentCallId。Conversation 在每个 child ToolCallBlock 上保留这项现有事实,root Session call 则不携带它。diff、read、search 和 web model 只接受没有 parentCallId 的 block;terminal model 与原本有意支持嵌套调用的 renderer 接受 child block。

共享的 card model 在 block 渲染到哪里都施加同样的终端资格与非终端子调用限制,因此不需要第二个展示面带 placement 字段;曾经原样委托选中 block 的详情面板已随右侧详情列一并删除(决策)。

keyed slot 仍按每个子调用的真实 tool name 分发;parentCallId 只限制本决定覆盖的 diff/read/search/web 结构化模型。Skill、Cordis 等已经直接读取 raw block 的专用 renderer 保持现状。

缺失调用头

结果节点在当前窗口没有配对 call 时,ToolResultNode.call 保持 null。Client 不扫描窗口、不发额外 RPC,也不根据 result 文本猜测工具名称。

需要名称或参数的专用派生在 call === null 时走当前 Generic fallback。只依赖 result metadata 的模型也不借机增强,因为当前 Host presentResult 必须先取得配对调用。

older page 后续补入调用头时,Conversation Context 按既有 replay 规则重建,届时才允许生成当前已有的专用卡片。

参数与 metadata 收窄

Client 从 argsRaw 解析 JSON,解析失败返回 Generic,不抛出 React render 错误。

Chat 与 Details 通过纯 helper 复用同一 block 的解析。未来缓存必须使用 immutable block identity,不能按 callId 建立跨 Session 全局状态。

每个专用模型只检查它需要的字段。Client 不复制完整 Host tool schema,也不调用 Host defineTool validator。

合法第一方事件必须与当前 presenter 输出等价。畸形、旧版本或手工修改日志只承诺不崩溃并使用 Generic fallback。

Client card-model 设计

现有 ui-tool/src/client/tool/models/ 继续是 Chat 与 Details 共享派生的唯一位置。helper 直接返回组件 props,不返回 ToolCallView/ToolResultView,也不创建同构的 ClientToolView union。

工具名称分支只存在于 ui-tool card model、现有 row 分类表,或拥有该工具 keyed renderer 的 Client 插件;不得进入 Session Controller、Client Session、Conversation assembler 或通用 Slot renderer。

未知工具继续由 GenericToolCard 显示 name、原始 args、结果 content 与错误。

通用工具行

toolRowModel() 直接从 toolName、argsRaw、result content、error、cwd 与 home 派生通用行,并保持以下行为:

  • search、read、bash、write、edit、code 与 others 分类;
  • 现有标题与工具专用标题;
  • summary 字段优先级和单行截断;
  • 多 query 的逗号拼接;
  • cwd 相对化与 home 缩写;
  • file path 点击;
  • args pretty JSON 与非 JSON 原文 fallback;
  • result content flatten 与 structured error fallback;
  • running、ok、error 与 stopped 状态。

Generic Host presentCall 的 title、kind、rawInput、content 与 locations 当前并不驱动普通 Web 行;Generic presentResult.content 也不驱动 Web 输出,因此无需把这些未消费值复制到 Client。

<a id="terminal-card"></a>

Terminal 卡片

Client terminal model 从工具名称、调用参数、结果 content、error 与 Session cwd 派生现有 TerminalBlock props,不依赖 parentCallId。后台 Shell 命令展示拥有主动后台 bash/pwsh 启动的展示决定;下述其余所有权和 fallback 规则继续有效。

输入保持的结果
标准 bash/pwsh 前台 runningterminal prompt、description、cwd、running 状态
标准前台 successterminal output、exit code/signal 与结算状态呈现
标准 bash/pwsh 主动后台调用Start 使用原 background 标志和调用 running;回执清除 running 后保留静态命令,不显示输出或任务状态
工具执行 errorGeneric IN/OUT 与错误摘要
persistent bash/pwsh runningterminal prompt
persistent bash/pwsh settledGeneric flattened result,不新增 exit card
terminal_send 前台terminal prompt 与 output
terminal_send background/errorGeneric 结果
PTC dispatch child与根调用相同的 terminal 适用规则与 fallback 规则

标准前台 shell 结果解析末尾 [exit code: N] 与 [killed by signal: X];主动后台启动回执不进入输出或退出状态解析。末尾已识别的 spill 策略提示会改用 Generic 输出:在 shell 行中可展开,在 Details 中显示原文,因为退出标记可能被移位或省略。已解析的 marker 从 terminal 正文移除;timeout、sandbox denial 与没有 pill 的 marker 留在正文。

调用 description 继续显示在 card 上方并覆盖折叠摘要。workdir 继续按绝对、相对和缺失三种情况处理;相对路径基于 Session cwd,且保留 .、..、盘符与 UNC root 的归一化。

对于 terminal_send,非空 input 与 session id 保持为逐字工具数据;空 input fallback 与 session label 通过 render site 的 conversation locale 解析。

同名普通与 persistent provider 是特殊兼容点。Client 使用当前有效参数与结果特征保留已交付差异;不足以无歧义识别的输入选择 Generic settled 结果,不增加新表现。

除主动后台的仅命令展示外,TerminalBlock 的 ANSI、光标重放、宽字符、行数上限、展开、复制与辅助技术文本保持不变。

Diff 卡片

输入保持的结果
running write从 file_path 与 content 生成 intended added-only diff
running edit从 file_path、old_string、new_string 生成 intended replacement diff
running str_replace_editor create从 path 与 file_text 生成 intended added-only diff
running str_replace_editor str_replace从 path、old_str 与 new_str 生成 intended replacement diff
settled write/edit success从 meta.diffs 生成 applied contextual hunks
settled str_replace_editorGeneric,因为该工具没有 result presenter
write create 或 applied metadata 缺失、畸形、为空当前 args fallback
error、畸形 args、edit 的 metadata 畸形、PTC dispatch childGeneric

路径、oldText:null、newText、结果覆盖调用时 diff、Chat 8 行上限、Details 全高显示和文件打开行为不变。

Read 卡片

running read 继续只有摘要行。成功 settled read 从 result meta 读取 path、offset、lines、totalLines 与 lang,并确认结果是单个文本块且符合 read envelope。

meta 缺失、字段畸形、result envelope 不匹配、error、缺失 call head 或 PTC dispatch child 都走 Generic。路径 label 的 cwd 相对化、home 缩写、语法语言、总行数、Chat 8 行上限与 Details 全高显示不变。

Client 不需要构造 Host ReadResultView.content;Generic fallback 始终可直接读取原始 result content。

Search 卡片

running grep/glob 继续只有参数摘要。成功结果分别从 meta.shape:'matches' 与 meta.shape:'paths' 生成 grouped matches 或 path list。

Client 校验 path、lineNumber、line、truncated 与 total。空 matches/paths 是有效卡片;缺失/畸形 meta、未知 shape、error、缺失 call head 与 PTC dispatch child 走 Generic。

truncated:true 时继续从原始 result content 显示 recovery locator;未截断时不显示。Chat 8 行上限、Details 全高显示和展开行为不变。

Web 卡片

running web_search/web_fetch 继续只有摘要行。成功 search 从 meta.sources、meta.answer、meta.truncated 生成卡片;成功 fetch 从 meta.url、meta.statusCode、meta.truncated 生成卡片。

Client 校验每个 source 的 url、title、snippet 与 publishedAt,并继续只把 http/https URL 渲染为链接。meta 缺失或畸形、error、缺失 call head 与 PTC dispatch child 走 Generic。

search 的 answer、来源顺序、label fallback 与截断提示不变;fetch 的最终 URL、状态、截断提示与 Details 下方原始正文不变。

已直接使用 raw block 的 renderer

  • Todo row 继续从 args 计算 completed/active 摘要。
  • Question row 继续从 result content 与 error 计算等待、回答、取消和中止状态。
  • Skill row 继续从 args/result 计算名称与状态。
  • Cordis define/run/action rows 继续从 args/result 与各自 Client service 计算。
  • 这些 renderer 的 props、slot key、注册顺序与可见结果不变。

Deliverables

ui-deliverables 独立于展示意图派生 mutation 业务事实,因此 produced-file 行为不与卡片截图耦合。

Deliverables Definition 按 callId 观察 root tool/call 与成功 tool/result,保存最小的 Client-owned mutation candidate,不扫描 Session window,也不依赖 UI renderer。

工具mutation 判定path 来源
write任意成功调用file_path
edit任意成功调用file_path
str_replace_editorcreate、str_replace、insertpath
str_replace_editorview不产生 path
其他无当前第一方 mutation 语义不产生 path

失败、interrupted、orphan result、缺失 path 与畸形 args 不产生 deliverable。同一路径保持 first-seen 去重,closing Assistant seq 之后落定的结果继续排除。

本次不新增通用“工具副作用”注册表。Host-only 第三方 presenter 通过 kind:'edit'/locations 自动加入 Deliverables 的能力被有意移除;未来若有真实第三方 mutation 需求,应由 Client 业务贡献表达,不能恢复 Session view。

Fixture 与测试数据

组装 RemoteMock 场景不包含手写 presentCall()、presentResult()、viewFor() 或 tool-view 类型。它提供与真实日志相同的 raw call、result content 和 result meta。

Fixture必须保留的原始事实
terminal参数与真实结果 status marker
diff参数与 result meta.diffs
readresult meta 的 path/offset/lines/totalLines/lang
grep/globresult meta 的 shape/files 或 paths/truncated/total
webresult meta 的 sources/answer 或 url/statusCode/truncated
generic/customname、argsRaw、content、error

该场景不导入 Host 工具包来计算页面展示,也不保留 presenter 镜像。同一 raw 场景在 jsdom 下驱动 built Web snapshot;真实 Host 浏览器用例独立覆盖网络路径。

展示等价矩阵

“当前展示”由已提交的组件测试、组装测试与 Web browser expected 共同定义。transport 或 ownership 重构不能作为 refresh snapshot 的理由;获批产品变化需要独立证据。

场景必须保持的展示
未知工具 runningGeneric 行,工具名与 args 摘要
未知工具 settledGeneric 行与原始 output
malformed args安全 Generic fallback
orphan resultcallId 标题与 Generic output
interrupted callwarning/stopped 状态
bash/pwsh 前台当前 terminal prompt、正文、cwd 与状态
bash/pwsh 主动后台调用terminal Start 与静态仅命令回执展示;启动错误保留 Generic IN/OUT
persistent shell当前 running terminal、settled Generic
terminal_send当前前台 terminal、后台/error Generic
write/edit当前 intended/applied diff 与 error fallback
read当前 running 摘要、settled ReadBlock 与 error fallback
grep/glob当前 grouped/path card、截断与 recovery
web_search/web_fetch当前来源/摘要 card 与原始正文
Todo/Question/Skill/Cordis当前专用行
PTC dispatch subcall满足条件时显示 terminal 卡片;diff/read/search/web 保持 Generic/flattened
Chat 与 Details同一调用使用相同 card fields
Trajectory当前 identity、树、选择和 details
Deliverables当前成功 mutation chips 与链接

Client 扩展约定

tool.call.toolview 继续是唯一工具 UI 注册机制。一个工具若要在 Client 获得专用表现,必须由 Client 插件注册自己的 wire tool name。

注册方接收 raw ToolCallBlock、Session path 信息和宿主动作,自行校验它认识的 args/meta 字段。注册方不调用 Host tool registry,不依赖 presentCall/presentResult,也不能要求 SessionEventEntry.view。

没有 Client renderer 的工具稳定降级为 Generic。同一 tool name 只能有一个生效 keyed registration,重复 key 继续 loud failure。

Session-scoped slot 可以表达 Client 侧会话差异,但不从 preset 推断 renderer 变体。Host-only presenter 不自动赋予 Web rich card,这是“Host 描述展示”与“Client 插件拥有展示”的明确边界。

失败与 fallback

  • Client 把 args 与 meta 当作 wire JSON,在消费点收窄。
  • 参数 JSON 解析失败走 Generic。
  • 已知工具缺少必要字段走 Generic。
  • metadata 缺失或畸形走 Generic;成功 write 例外,它按当前 presenter 行为保留由参数派生的整文件 diff。
  • error result 不因 metadata 存在而显示成功卡片。
  • 缺失 call head 不猜测工具名称或参数。
  • 未知 metadata 字段被忽略。
  • 新 metadata variant 在旧 Client 中走 Generic。
  • card-model helper 捕获可预期解析失败,不依赖 React error boundary 完成普通 fallback。
  • keyed renderer 自身的意外异常仍由现有 Slot error isolation 处理。

同名 Host provider

Host registry 允许不同 scope 为同一 tool name 提供不同定义;Session view 通过 presenter scope 理论上可以按 preset 选择不同 render intent。删除 view 后,Client keyed slot 只观察 wire name,不能观察 Host definition identity。

当前第一方显著实例是普通与 persistent bash/pwsh。Client 派生使用有效参数与结果特征保持它们的已交付差异,不增加 provider-id wire 字段;无法判别的畸形或自定义同名 provider 输入采用 Generic。

本次不承诺保留第三方同名 provider 仅通过 Host presenter 表达的差异。若未来产品确需同名 provider 的不同 Client 展示,必须定义稳定、非展示性的 Client identity;不得恢复按页 Host view 计算。

已交付范围

Session Controller

  • SessionEventEntry 只包含 raw event。
  • 两个 Session tool-view 类型都不存在。
  • history 不含 presentation import、helper 或 page/follow presentation state。
  • 地址、分页、follow 与 projection 逻辑仍由 Session owner 负责。
  • Host 测试固定 raw journal 约定。

Session Controller Client

  • Session.views 不存在。
  • EventSource replace/prepend/append delta 保持不变。
  • transport、fixture 与 test-support 类型携带 raw entry。
  • event identity 与引用稳定性保持不变。

UI Conversation、Chat 与 Trajectory

  • Conversation input 与 Tool block 不含 view 字段。
  • Chat/Trajectory Tool Definition 读取 raw event。
  • event pairing、Context replay、树与 target snapshot 保持不变。
  • child Tool block 保留现有 PTC dispatch parentCallId;row 与 Details slot owner props 都不增加独立 placement 字段。

UI Tool 与 Deliverables

  • card model 从 raw block/meta 派生。
  • Chat 与 Details 复用相同 helper。
  • Generic fallback 与 keyed dispatch 保持不变。
  • Deliverables 识别第一方 mutation args。

Fixture、文档与生成物

  • fixture 只发 raw event/meta。
  • Session Controller 与 Client README/JSDoc 描述 raw journal 和 Client presentation owner。
  • 工具 cookbook 记录 Web Client 接入路径。
  • 本文是该决定的 owner;保留的 Host presenter Note 继续拥有各自决定。
  • 手写 Remote 类型、dependency、README、pairing record 与 generated reference 保持同步。

验证矩阵

Host

  • page 返回连续 raw event entries。
  • follow 返回 opening cursor 与连续 raw event entries。
  • page/follow 在无 Tools service 时行为相同。
  • cold page 不解析或挂载 preset。
  • tail page 通过标准 projection registry 计算 baseline;provider 是否存在由 projection composition 决定,不引入 history 侧 setup 路径。
  • 地址、ownership、message-aligned boundary 与 tail projection 不变。
  • listener-before-read、reconnect catch-up 与 gap repair 不变。
  • 大量 tool results 不触发每结果回扫。
  • wire 结果不含 view。

session-history-journal.host.spec.ts 负责分页、连续性和 history error 行为,不含 presenter 断言。

Client Conversation

  • replace、prepend 与 append 接受无 view entry。
  • Chat 与 Trajectory root call/result 配对不变。
  • PTC dispatch 树不变。
  • result-only fallback 不变。
  • interruption synthetic result 不复制 view。
  • registry rebuild、older prepend 与 live append 的 Node identity 不变。

Client card model

  • terminal 用 raw args/content 得到已固定的 props。
  • diff 用 args/meta 得到已固定的 diffs。
  • read 用 meta/content 得到已固定的 lines。
  • search 用 meta/content 得到已固定的 grouped/path card 与 recovery。
  • web 用 meta/content 得到已固定的 sources/fetch summary。
  • unknown、malformed、error、missing-call 与 missing-meta 继续 Generic。
  • parentCallId 缺失与存在的用例证明 terminal 适用规则一致,并保留 diff、read、search 和 web descendant 的 Generic fallback。
  • Chat 与 Details 对同一 block 得到相同 card fields。

Deliverables

  • write/edit 成功产生 file_path。
  • str_replace_editor create/str_replace/insert 产生 path。
  • str_replace_editor view 不产生 path。
  • failure、interrupted、malformed 与 orphan 不产生 path。
  • first-seen 去重与 closing seq cut 不变。

组装与浏览器

  • terminal、diff、read、search、web browser expected 不刷新并全部通过。
  • tool tree、details、trajectory 与 deliverables 的可见断言不改预期。
  • built Client 通过真实 Remote page/follow 取得 raw events 后仍显示同样卡片。
  • fixture 与真实 Host 使用同一 Client derivation。
  • minimal preset 单独固定 persistent shell 行为。

静态与文档

  • 生产代码不存在 SessionToolView/SessionToolCallView。
  • Session history 不引用 dsh-tools/presentation、ctx.tools、presenterScopeFor 或 backscanArgs。
  • Client Conversation 不引用 ToolCallView/ToolResultView。
  • Client model 不读取 callView/resultView。
  • fixture 不定义 presenter mirror。
  • Host presentCall/presentResult 与 presentationMeta 仍存在。
  • 没有新增 Client registry 或 Host→Client presentation hint。
  • 受影响的手写类型、README、Agent Note、catalog 与 graph 保持同步。

验证命令

修改本决定时使用 dsh-pre-push-checks 按最终 diff 选择命令;所需证据包括:

  • Session Controller history/transport 聚焦测试;
  • ui-chat 与 ui-trajectory Tool Definition 测试;
  • ui-tool terminal、diff、read、search、web、row、tree 与 details 测试;
  • ui-deliverables produced-files 测试;
  • 组装 RemoteMock 与 Client runtime 测试;
  • 受影响 Host/Client TypeScript face;
  • lint 与 duplication;
  • 受影响源文件 per-file 100% coverage;
  • DSH_SNAPSHOT=replay pnpm run test:web,不得 refresh 现有展示 golden;
  • 手写 Remote 类型与 TypeScript 检查;
  • pnpm run doc-sync;
  • git diff --check。

已交付不变量

  • Session page/follow 不读取 Tools registry 或 presenter scope。
  • Session history 不存在 callId backscan、presentation cache 或 view clone。
  • Remote Session entry 不携带 view。
  • Session 日志与 SESSION_FORMAT_VERSION 不变。
  • result meta 逐字节通过日志与 Remote 到达 Client。
  • Conversation 只从 raw event 组装 ToolCallBlock。
  • ToolCallBlock 不含 Host render-intent 字段。
  • 五类结构化 card model 只读 raw block 与 Session path facts;只有 diff、read、search 和 web 使用 parentCallId 拒绝子调用。
  • Generic、Todo、Question、Skill 与 Cordis 行行为不变。
  • Deliverables 不依赖 render intent 且保持当前 paths。
  • 所有第一方顶层工具的文本、组件、展开内容、状态、链接与排序不变。
  • malformed、missing-meta、error、orphan 与 unknown-tool 继续安全 fallback。
  • PTC dispatch 的 diff、read、search 和 web 子调用保持 Generic/flattened;terminal 子调用遵循根调用适用规则。
  • Chat、Details 与 Trajectory 行为不变。
  • 现有 Web browser expected 无需刷新即可通过。
  • Host presenter API、实现与直接测试不变。
  • ACP 输出不变。
  • 没有新下行展示字段或第二套 Client registry。
  • 分页成本不再随 result 数量乘以页面事件数增长。
  • 下行 payload 不再重复 result meta 的 card DTO。

Alternatives considered

只优化 backscanArgs,保留 view

page 前建立一次 callId → {name,args} Map 可以把回扫降为线性,live 已有 openCalls 快路径;但 Host lookup、preset scope、presenter、JSON clone、重复 payload 和双重所有权仍存在,因此拒绝。

在 Client 建 presenter registry

把 presentCall/presentResult 接口复制到浏览器会与 tool.call.toolview slot 重复注册、生命周期、fallback 和覆盖语义;renderer 仍需把 presenter DTO 转成组件 props,因此拒绝。

让 Conversation Tool Definition 生成统一 view

这会把工具名称和 UI card 语义放进 target-neutral Conversation owner,并重建与 Host view 同构的中间 DTO,因此拒绝。

删除 presentationMeta

read 行结构、applied diff、search 分组、web sources 和有效 truncation 无法从模型文本无损恢复;解析自由文本也会把 UI 绑到输出措辞,因此拒绝。

持久化 canonical tool result

这会扩大 Session log、暴露内部结果结构、改变持久格式,并可能保存远超展示所需的大对象;已有 metadata 足够,因此拒绝。

删除 Host presenter API

一并删除可以继续收缩代码,但本决定保留 Host presentCall/presentResult;其 API、实现、测试与类型独立于 Session Remote。

Client 导入 Host 工具实现

工具包包含 Node、filesystem、subprocess 或 provider 依赖,不能进入浏览器 bundle;Client 只消费 raw JSON,并在自己的 renderer 内维护窄解析,因此拒绝。

按结果向 Host 查询 presentation

按需 RPC 会把一页读取变成 N 次网络调用,仍需 Host lookup、scope、callId 查找与错误协调,因此拒绝。

允许展示增强

将更丰富的 PTC dispatch 卡片、缺失 call head 的推断或其他历史展示增强与所有权变更捆绑,会使快照无法证明对等。本决定拒绝这种捆绑;嵌套 terminal 卡片例外不放宽非 terminal 子调用限制。

接受临时 Generic 退化

先停发 view 再逐步补 Client card 会让 terminal、diff、read、search、web 与 Deliverables 在中间版本退化。Client 对等实现与 Host 删除必须在同一可发布变更中完成。

Consequences

本决定从 Session 读取中删除 presentation 工作、重复扫描和重复 view payload;代价是保留的 Host presenter 与 Client card derivation 可以独立演进,因此两侧都需要 owner 专属测试,Web 展示对等仍是明确产品约束。

Client 与 Host 逻辑漂移

同一工具可以有一份 Host render intent 和一份 Client card derivation。两者面向不同消费方,不共享运行路径;不刷新的 browser expected 固定第一方 Web 视觉对等,Host presenter 测试只约束 Host API。

同名 provider 无稳定 identity

raw event 只记录 tool name,不记录具体 ToolDefinition。Client 使用有效事件字段保留普通与 persistent shell 的差异;无法判别的自定义或畸形输入回退 Generic,wire 不为理论扩展性增加 hint。

Metadata 是未知 JSON

旧 Session 可能缺字段,手工修改日志可能带畸形值。每个 Client model 必须局部收窄,不能把未知数组或对象直接传给 UI primitive。

preset-owned projection 可用性

history 不为当前组合中缺失的 projection unit 补偿。需要在冷读中保持可见的 preset-owned unit,必须由共享的 Session preparation/projection 组合在 restore 前提供其定义;history 不得重新增加 preset mount 或 presenter setup 分支。

双 target 同步

Chat 与 Trajectory 各有独立 Tool Definition,两者都携带 raw fields;card derivation 只能留在 ui-tool,不能复制进两个 Definition。

Deliverables 隐性依赖

Deliverables 不是视觉组件,因此 mutation parser 必须与受支持的第一方写工具保持同步;专用测试独立于卡片截图固定 file chips 与 Markdown links。

Fixture 假绿

fixture 下发 raw event/meta,不下发手写 view。真实 Host 组装覆盖仍然必要,因为 fixture-only snapshot 不能证明 transport 路径。

错误刷新快照

本次承诺用户可见输出不变。出现 snapshot diff 时必须修 Client 派生;除非 owner 单独批准具体视觉变化,否则不得 refresh expected。

文档漂移

raw journal 或 Client presentation owner 变化时,Agent Note、package README、cookbook、根规则与 generated reference 必须一起更新;Host API 文档保持独立。

Remote 协议收缩

optional view 的缺失是所有 consumer 共同遵守的预发布 wire 类型决定;没有兼容 shim、双写或版本协商。

与现有决策的关系

当前terminal 卡片规则允许嵌套 terminal 卡片,仅替代 terminal 子调用卡片禁令及其展示对等要求。本文继续负责原始 journal 所有权、Client 派生以及 diff/read/search/web 子调用限制。

本文部分取代 Client 工具展示所有权 中“card model 接收 Host view”的实现事实;ui-tool 拥有展示、业务插件使用 keyed slot、Conversation 只拥有生命周期与拓扑的核心决定保持不变。

本文保留 toolview 溶解 的决定:Client 仍只有 slot 注册模型,不恢复 ToolViewRegistry。

本文收窄 render-intent union 的消费范围:Host API 与类型保留,Session Remote 与 Web Client 不消费它。本文独自规定 transport 拆分,不改写该 presenter 决策。

本文更新 Session 历史与 Remote 事件传输 的 entry 约定:journal 只运输原始 event 与独立 projection baseline,不承载临时 tool view。

本文遵循 Conversation Node 组装:Tool Definition 负责事件配对与调用树,具体 card model 留在 ui-tool。

本文保留 规范工具输出约定 的 result metadata,因为它是无损、可重放 Client 派生的输入。

Deferred

  • Host presenter 若长期没有生产消费者,可由另一项明确决策评估删除;本决定不预判。
  • PTC dispatch 子调用的 diff、read、search 和 web 专用卡片仍需独立设计并更新可见快照;terminal 调用遵循链接的当前卡片规则。
  • 第三方 mutation tool 若要加入 Deliverables,需新增 Client-owned 贡献;本决定不为尚无消费者的扩展性建 registry。
  • 同名 provider 若要不同 Client 展示,需先定义稳定、非展示性的 identity;不得恢复按页 Host view。
  • Client card model 若需量化性能,可以增加 immutable-block 微基准;已交付架构禁止扫描 Session window。