Skip to content

feat(conversation): add the chat data contract and its helpers - #113

Merged
TheGreatAxios merged 3 commits into
mainfrom
conversation/01-contract
Oct 28, 2026
Merged

TheGreatAxios merged 3 commits into
mainfrom
conversation/01-contract

Conversation

@pratikbuilds

Copy link
Copy Markdown
Collaborator

Add src/lib/conversation.ts, the message model and part union every chat component reads: a tool part carrying an approval record beside the call, parallel tool batches, generated images, sources, a streaming status, replies and reactions. It reuses the chat-parts.ts members unchanged.

Add groupActivity and summarizeActivity to fold thinking and tool steps into one run, mediaKindOf, and checkAttachments for count, size and duplicate limits. Add the --attention-wash token, mixed from --primary.

First in the stack; targets feat/conversation, which starts at main (8300019). Part of #94.

Notes for review

  • --attention-wash mixes 12% of --primary (reference design: 11% light / 14% dark; one value serves both).
  • Summary durations are whole seconds ("4s", "1m 05s"); the old parts-renderer shows "4.0s" until it is removed.
  • An approval resolving merges the activity runs on either side; the later run's key changes (documented, tested).

Verification

  • This commit passes bun run typecheck and bun run lint on its own (the stack is checked commit by commit).
  • At the top of the stack: bun run check, bun run build, bun run dep-guard, bun run contrast-test and bun run test:e2e (Playwright specs and the Storybook axe sweep in light and dark) pass.

Closes #95

Add src/lib/conversation.ts, the message model and part union every chat
component reads: a tool part carrying an approval record beside the
call, parallel tool batches, generated images, sources, a streaming
status, replies and reactions. It reuses the chat-parts.ts members
unchanged.

Add groupActivity and summarizeActivity to fold thinking and tool steps
into one run, mediaKindOf, and checkAttachments for count, size and
duplicate limits. Add the --attention-wash token, mixed from --primary.
@corbits-triage

corbits-triage Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Triage: Pass

No changes needed from you. A maintainer will review this pull request.

Notes:

  • added code matches a code pattern: src/lib/conversation-activity.ts:112 Nested ternary; use if statements or a lookup.
  • added code matches a code pattern: src/lib/media-kind.ts:94 Invented fallback (an empty string, || [] or || 0); handle the missing value instead.

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated multi-agent review: each PR got an independent review, then a second agent re-verified every flag against the branch head; only two-agent-confirmed items are listed below. No blocking issues in this PR.

  • minor src/lib/conversation.ts:60-67 — the approval id lives only inside ToolApprovalRecord; the plan review asks to carry approvalId on PartToolTrace so a host can link a call to its own durable approval entity. Either add an optional approvalId to PartToolTrace or note the deviation in the PR body.
  • minor same lines — timeoutAt is required-nullable vs the plan's optional timeoutAt? (arguably clearer; worth a note), and resolvedAt is absent though Interchange carries it — without it, "denied 2m ago" can't render from the record alone. Smallest fix: add resolvedAt?: string.
  • minor src/lib/conversation-activity.ts:81-84 — formatDuration has no hours bucket: one hour of thinking renders "Thought for 60m 00s" (verified by running the helper). Add an hours case, or a regression test documenting the current output as intended.
  • nit src/lib/attachment-limits.ts:27 — identity reads cleaner as identityOf/duplicateKeyOf. Mechanical rename.

summarizeActivity only knew seconds and minutes, so an hour of
thinking read "Thought for 60m 00s". Durations of an hour or more now
read as hours and minutes ("1h 30m"); shorter ones are unchanged.
@pratikbuilds
pratikbuilds force-pushed the conversation/01-contract branch from 4a3b9ae to 863187b Compare October 27, 2026 13:25
@pratikbuilds

pratikbuilds commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator Author

Thanks for the review. Here's what happened to each point.

Fixed

An hour of thinking showed as "Thought for 60m 00s" (minor)

  • Fix: an hour or more now reads as hours and minutes: "1h 00m", "1h 30m". Anything under an hour is unchanged ("59m 59s").
  • Test: "formats durations past an hour in hours and minutes" in conversation-activity.test.ts. It failed before the fix.

Not changed, and why

  • Add approvalId to PartToolTrace: the approval's id is already on its record (part.approval.id). A second copy on the call could drift out of sync with the first.
  • Make timeoutAt optional and add resolvedAt: nothing in this stack shows a decision time yet, and timeoutAt: string | null is a deliberate, documented choice. Adding resolvedAt together with its first user is cleaner than adding it unused.
  • Rename identity: it's a private helper with one caller, so I left the name.

Commit: fix(conversation): format thinking past an hour in hours

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review · Approve

Adds the chat message model and the activity, attachment, and media-kind helpers.

Findings

No findings.

mediaKindOf let any video/* media type win over the extension. Servers
and desktop browsers report a TypeScript file as an MPEG transport
stream (video/mp2t, or video/vnd.dlna.mpeg-tts on Windows), since both
use .ts, so index.ts read as video and previewed as a broken player.

Those two types now defer to the extension, and stay video when the
name has no known extension.
@pratikbuilds
pratikbuilds force-pushed the conversation/01-contract branch from 863187b to 0fb8583 Compare October 28, 2026 14:47
@pratikbuilds

Copy link
Copy Markdown
Collaborator Author

Round 3 fixes

A fresh review pass over the stack turned up the points below; each is fixed in a new commit at the end of this branch, and the stack is rebased onto it.

  • 0fb8583: A .ts file sent as video/mp2t was classified (and previewed) as video. mediaKindOf now lets the transport-stream types (video/mp2t, video/vnd.dlna.mpeg-tts) defer to the file extension, so index.ts is text and a bare capture or .m2ts stays video.

bun run check and dep-guard pass on every branch of the stack, and the full e2e suite passes at the top of the stack (163/163, including the stories axe sweep). Each behaviour fix comes with a test that fails without it; the forwardRef tests can only guard, since the suite runs on React 19.

@TheGreatAxios
TheGreatAxios removed this pull request from stack #130 October 28, 2026 21:37
@TheGreatAxios
TheGreatAxios changed the base branch from feat/conversation to main October 28, 2026 21:38
@TheGreatAxios
TheGreatAxios added this pull request to stack #139 October 28, 2026 21:38
@TheGreatAxios
TheGreatAxios force-pushed the conversation/01-contract branch from 0fb8583 to b98472e Compare October 28, 2026 21:42
@TheGreatAxios
TheGreatAxios merged commit 725775a into main Oct 28, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add the conversation data contract, its helpers and the attention wash

2 participants