Skip to content

feat(message): align message contracts with Python - #52

Merged
DavdGao merged 1 commit into
agentscope-ai:mainfrom
rayrayraykk:feat/python-message-event-parity
Sep 16, 2026
Merged

DavdGao merged 1 commit into
agentscope-ai:mainfrom
rayrayraykk:feat/python-message-event-parity

Conversation

@rayrayraykk

Copy link
Copy Markdown
Member

Linked Issues

Description

AgentScope Python models messages and content blocks as validated runtime entities. The TypeScript package previously exposed mostly structural interfaces, so default fields and serialized payload handling could differ between the two SDKs.

This PR aligns the public message behavior while retaining TypeScript-native APIs:

  • Add factory functions for every content block with Python-compatible IDs, timestamps, states, and nullable fields.
  • Align Msg, Usage, UserMsg, AssistantMsg, and SystemMsg defaults with Python.
  • Add hasContentBlocks and list-based filtering to match Python message helpers.
  • Add Zod schemas and parseMsg / parseContentBlock for validated wire payloads.
  • Accumulate all four Python usage counters from model events.
  • Place reply termination and error contracts in the shared type layer to avoid duplicating them between messages and events.
  • Update the agent and desktop consumers for the normalized nullable fields.

No generated parity inventory, migration tooling, or internal development documentation is included.

Verification

pnpm --filter @agentscope-ai/agentscope build
pnpm --filter @agentscope-ai/agentscope exec jest --runInBand
pnpm build:desktop

Results:

  • Core ESM, CommonJS, and declaration builds passed.
  • 19 test suites and 128 tests passed.
  • Desktop type checks and production build passed.
  • ESLint, Prettier, and git diff --check passed for all changed files.

Checklist

  • This PR is linked to a related issue (see above)
  • Code has been formatted with pnpm format
  • Related documentation has been updated (public API JSDoc; no guide changes required)
  • All tests are passing (pnpm test)
  • Code is ready for review

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Event timestamps are replaced during processing, malformed permission rules pass validation, and desktop usage updates mutate prior React state.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Aligns TypeScript message models, content blocks, parsing, usage accounting, and desktop consumers with Python SDK contracts.

Changes:

  • Adds normalized content-block factories and Zod wire schemas.
  • Aligns message defaults, filtering helpers, shared error types, and usage counters.
  • Updates event handling, tests, and desktop state updates.
File summaries
File Description
packages/agentscope/src/type/index.ts Adds shared reply and error contracts.
packages/agentscope/src/message/schema.ts Adds runtime message and block schemas.
packages/agentscope/src/message/message.ts Normalizes messages and event accumulation.
packages/agentscope/src/message/message.test.ts Tests defaults, helpers, and parsing.
packages/agentscope/src/message/index.ts Exports new message APIs.
packages/agentscope/src/message/block.ts Adds normalized block factories.
packages/agentscope/src/message/append-event.test.ts Updates event and usage tests.
packages/agentscope/src/event/index.ts Shares error types and expands usage events.
packages/agentscope/src/agent/agent.ts Adapts stored usage fields.
packages/agentscope/src/agent/agent.test.ts Updates normalized-message expectations.
packages/agentscope/src/_utils/common.ts Adds ID and timestamp generators.
apps/desktop/src/renderer/src/hooks/agent-event-handler.ts Delegates event handling to shared logic.
Review details

Suppressed comments (8)

packages/agentscope/src/message/message.ts:423

  • The factory now uses wall-clock time instead of the event's timestamp. Replaying or processing this event asynchronously changes the serialized block chronology; preserve event.created_at.
            msg.content.push(ThinkingBlock({ id: event.block_id, thinking: '' }));

packages/agentscope/src/message/message.ts:453

  • This one-shot block should be timestamped from the event, but both timestamps now come from the receiver's clock. That makes replayed events nondeterministic and can misorder them; pass the event timestamp for both fields.
            const hintBlock = HintBlock({
                id: event.block_id,
                hint: event.hint,
                source: event.source ?? null,
            });

packages/agentscope/src/message/message.ts:464

  • The data block's creation time is now generated when the event is handled rather than copied from the start event. Preserve event.created_at so delayed and replayed streams retain their original timeline.
            msg.content.push(
                DataBlock({
                    id: event.block_id,
                    source: Base64Source({ data: '', media_type: event.media_type }),
                })

packages/agentscope/src/message/message.ts:504

  • Omitting created_at replaces the tool-call event timestamp with the local handling time. This changes persisted event history whenever delivery is delayed or replayed; pass through event.created_at.
            msg.content.push(
                ToolCallBlock({
                    id: event.tool_call_id,
                    name: event.tool_call_name,
                    input: '',
                })

packages/agentscope/src/message/message.ts:534

  • The result block is stamped with processing time instead of event.created_at, so event replay no longer reconstructs the original message. Pass the event timestamp to the factory.
            msg.content.push(
                ToolResultBlock({
                    id: event.tool_call_id,
                    name: event.tool_call_name,
                    output: [],
                })

packages/agentscope/src/message/message.ts:549

  • Both text blocks created while applying this delta use the receiver's current time. The previous code used event.created_at; retaining that timestamp is necessary for deterministic replay and correct block ordering.
                    trb.output = [TextBlock({ text: trb.output })];
                }
                const last = trb.output[trb.output.length - 1];
                if (!last || last.type !== 'text') {
                    trb.output.push(TextBlock({ text: event.delta }));

packages/agentscope/src/message/message.ts:564

  • Converting an existing string output now assigns the conversion time rather than the data event timestamp used previously. Pass event.created_at so reconstructed nested output is deterministic.
                    trb.output = [TextBlock({ text: trb.output })];

packages/agentscope/src/message/message.ts:570

  • This nested data block loses the event's timestamp and receives the local processing time. Delayed/replayed events therefore serialize differently; pass event.created_at to DataBlock.
                trb.output.push(DataBlock({ id: event.block_id ?? _generateId(), source }));
  • Files reviewed: 12/12 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread apps/desktop/src/renderer/src/hooks/agent-event-handler.ts
Comment thread packages/agentscope/src/message/message.ts
Comment thread packages/agentscope/src/message/schema.ts
@DavdGao
DavdGao merged commit 29f35ec into agentscope-ai:main Sep 16, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants