Deuz SDK 2.0 已发布 — 存储、护栏、交接与零配置 MCP。 2.0 新特性
Deuz SDK
Reference

Stream Protocol Contract

The canonical StreamPart contract and the provider SSE invariants locked by tests.

文档正文为英文。导航、搜索和界面会跟随你选择的语言。

Provider bytes are never exposed directly. Each adapter parses its wire stream and emits the same additive StreamPart union. Consumers can therefore switch providers without changing text, reasoning, tool-call, source, usage, or finish handling.

The SSE parser is regression-tested for:

  • UTF-8 code points split across arbitrary byte boundaries;
  • LF, CRLF, and bare CR line endings, including a CRLF pair split across chunks;
  • UTF-8 BOMs, comments/keep-alives, named events, and multi-line data: fields;
  • a final event without a trailing blank line;
  • cancellation of the underlying reader when a consumer stops early.

Every successful provider stream must produce exactly one finish part. Provider usage shapes are normalized into Usage; provider finish values are normalized into the locked FinishReason union. A provider error event produces one typed error part and rejects usage and finishReason with the same error object.

Compatibility rule

StreamPart is additive. Applications should handle the variants they need and retain a default branch so a new non-breaking part does not break an older consumer.

for await (const part of result.fullStream) {
  switch (part.type) {
    case 'text-delta':
      process.stdout.write(part.text);
      break;
    case 'error':
      throw part.error;
    default:
      break;
  }
}

Additive parts since 1.8

typeWhenNotes
verifyverifyStep evaluated on a natural completionstepIndex, attempt, ok, willRetry, optional feedback. Since 1.9 it is also serialized onto the UI wire (v2 only), journaled to the StreamStateStore and replayed on resume — before that it was dropped by toDeuzStreamResponse, so a verified run looked identical to an unverified one client-side.
plan-updateEmitted via emitPlanUpdate from a toolLive TaskList snapshot for a to-do panel. Also a UI v2 part.
activityEmitted via emitActivity from a tool"Computer" feed line (message, optional level / data / agentPath). Also a UI v2 part.

Additive parts and fields in 1.9

type / fieldWhenNotes
warningA lossy mapping was applied (a stripped sampling param, an unknown-slug capability fallback, a tool or document the wire could not carry){ warning: CallWarning }. Emitted as it is discovered — always before the output of the step that produced it, because every warning site runs during capability resolution and request building. In a loop that means a later step can still emit one, after earlier text. Also serialized onto the UI wire (v2 only, message re-redacted), journaled to the StreamStateStore and replayed on resume; folds into AssistantTurnState.warnings. The same set resolves in bulk on StreamChatResult.warnings. CallWarning['type'] is an open union — keep a default.
false-finishThe doneWhen guard rejected a natural completion{ stepIndex, attempt, willRetry }. One part per rejection, always before the terminal finish; willRetry: false is the rejection that spent the falseFinishGuard budget, after which finish carries providerMetadata.deuz.stoppedBy = 'false-finish'. Streaming loop only. On the UI wire (v2 only), journaled and replayed, and folded into AssistantTurnState.falseFinishes — the same treatment verify got.
tool-state.denied / .deniedReasonAn approval refused a gated callOptional fields qualifying a terminal state: 'error' whose cause was a verdict, not a thrown tool. Set by the streaming loop at both terminal sites; deniedReason is the denier's own words (absent for a server-mode approveToolCall, whose verdict is a bare boolean). ToolRunState deliberately gains no 7th member, so exhaustive switches keep compiling.
data.idwriteData(name, payload, { id })Reconciliation key. The wire stays strictly append-only; last-write-wins is the client's job.

Wire v1 output is unchanged in every byte. The tool-state fields and the data-part id ride carriers a v1 client already drops wholesale, and the two brand-new part types are v2-only — including when they sit inside a sub-agent frame, which is filtered recursively.

Run the focused protocol suite with npm run test:protocol.

本页内容