Skip to content

Build the Platform Core, Chat, and member integrations UI - #9687

Open
emir-karabeg wants to merge 124 commits into
improvement/platformfrom
improvement/platform-core-chat
Open

emir-karabeg wants to merge 124 commits into
improvement/platformfrom
improvement/platform-core-chat

Conversation

@emir-karabeg

@emir-karabeg emir-karabeg commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Home, Chat, Settings and organization integrations use the shared navigation shell and real routes in the isolated Platform Next.js app. The browser and Sim Desktop share the renderer. A default-off server fixture supplies populated, empty and stress states without a database.

The shared App Router layout owns the sidebar. Selecting a chat, visiting Settings, or reopening the compact drawer preserves the reader’s position. Command K uses the shared menu rows and headings: Home, Settings and Integrations keep their icons, and workspace and chat results are text-only. Search, results and shortcut hints align to one inset, and moving from pointer to keyboard leaves only the active result highlighted.

Home and Chat share a reading column and a Recents component. Home opens recent websites in another tab; dismissal carries into Chat. The activity chart switches between Tasks run and Searches without losing its date range. Widgets and notifications share a height ceiling and scroll independently. Calendar’s Now control returns to the current day and time. Events open an external tab only when a provider supplies a URL; attendees have their own popover.

Navigation, tabs, recents and attachment previews share ControlRow. Invite recipients use compact address tags with an always-visible remove button inside each tag. Form dialogs focus the first available field and restore focus on close. Invite accepts typed and pasted addresses, supports keyboard removal and leaves sending to Phase 3. Chat keeps prose and tool activity aligned, and shares its time formatting and copy feedback with the rest of the Platform.

The member Integrations inventory uses workspace catalog descriptions for each provider. Its detail shows the provider name and description, then one connected credential with its actions menu. The inline connection prompt shares Home’s Email and Calendar styling and links to the Sim Search setup guide. Eligible tool approvals remain individual. Known OAuth permissions are grouped by purpose and always visible. Managed Search providers show their actual read-only operations, including Fireflies meeting search, transcripts, and summaries. The server fixture flag gates all sample connections. Member inventory and operation mapping.

See the component ownership map, Platform contract and Core/Chat source-state inventory. To run locally: SIM_PLATFORM_FIXTURES=1 bun run --cwd apps/sim dev:platform; visit /home?state=full, /chat/weekly-review?state=full /settings?state=full, or /integrations?state=full.

Verification exercises the production Next build, Chromium with fixture-on and fixture-off servers, and the native Electron renderer. Browser journeys measure scroll bounds, hit targets and spacing, exercise keyboard selection and focus return, and distinguish resting/hover/pressed feedback in both themes. The design audit checks ownership, token and variant use, timers, modal chrome and the fixture boundary. It recognizes JSX returned by local render helpers and active route fallbacks. The tracked Platform skill and contract route new work, cleanup and Settings reviews to the correct component library, and distinguish shared preferences from decisions owned by one feature.

Stacked on #9667, which targets staging.

waleedlatif1 and others added 22 commits November 4, 2026 01:37
…f a mocked route call (#9662)

* test(files): prove files/ VFS path downloads over real HTTP instead of a mocked route call

The v2 file download case added in #9637 passed the decoded path straight to the
handler and asserted on the mocked use case, so it could not catch an encoded
files/ path failing to reach the route or resolving to the wrong file. The file
delivery e2e now uploads same-named files at the root and in a folder and
downloads each by its %2F-encoded VFS path through the running app.

* test(files): keep the HTTP check to the routing it proves
…eck drops (#9664)

The v2 apply-operations count treated an operation as applied when its
connection created an edge that the end-of-batch scope check then removed
(cross-subflow, trigger target, or note endpoint). The engine now records
which operation's deferred connection created each edge and charges a
dropped edge to that operation. Existing edges a move leaves crossing
scopes stay uncharged: losing them is the move's effect, not a refusal.
looksLikeAnId's single regex backtracked quadratically on a long dash token
that ends in a non-id character (CodeQL js/polynomial-redos on the v0.9.43
release). Two linear checks with the same meaning replace it.
…ill already queued (#9666)

* fix(file-search): claim a file write that lands on an owner the backfill already queued

Backfill reconciliation wake-ups enter the owner queue as if just dispatched so they rotate in
behind owners woken for a write. A write that landed while such a row was still queued only
advanced its updated_at through the file trigger's upsert, so it kept the reconciliation stamp
and waited behind every older-stamped owner. The same held for an owner that was served, left
queued with work, and then woken again.

Claims now take owners woken since their last dispatch first: never dispatched, or updated_at
later than last_dispatched_at, which every real wake-up (file write, released claim, reaped claim,
artifact-ready requeue) advances and a reconciliation wake-up never does; it no longer touches an
owner already queued. Within each group the dispatch-age rotation is unchanged. Queue timestamps
the comparison reads come from the transaction clock, the same clock the trigger writes.

* fix(file-search): rotate a woken owner whose slots are full by dispatch age

An owner already holding both of its outstanding claims can claim nothing, but a write between
passes kept marking it woken, so with little global capacity left it took every owner turn and
the free slot went unused. Woken owners now go first only while they have a free slot; a full
owner keeps the dispatch-age rotation it had before.

Also gives the due-reconcile work-check case the same one-minute margin as its not-yet-due
counterpart: exactly at the interval it compared a database timestamp with the application
clock and flaked on skew.
…actions menu (#9661)

The new-link form moved to the provider root, so the source card's actions
menu no longer renders a sibling and needs no fragment.
* fix(media): preserve video settings and Google URI delivery

* fix(image): keep legacy prompt documentation accurate
Allow selected read-only custom-element text, preserve the initiating composer focus through deferred capture, and correct encoded organization bookmark names and legacy Sim aliases.

Retain keyboard and mouse denial coverage with real typing assertions. Four regressions reproduced on the unfixed code; final CI passed with 55 successful checks and 26 intentional skips.
…reaped claims ahead of the rotation (#9671)

* fix(file-search): fill dispatch capacity in selection order and reap before the backfill

The claim cut ranked every selected owner's candidates by revision age, so with less capacity
than candidates a woken owner selected first could lose every slot to an older backlog selected
after it, and the claim's stamp then filed it behind the rotation. Claims now fill capacity owner
by owner in the pass's selection order (each still up to its free slots).

The backfill page ran before the reaper in the same transaction, so an owner it queued as a
reconciliation wake-up absorbed the reaper's wake-up (both writes at the transaction time) and
the released claim waited behind the rotation. The reaper now runs first; the backfill's wake-ups
leave queued owners untouched.

Tests: a two-slot pass with a woken owner against an older backlog, and a reaped claim whose
owner the backfill queues in the same pass; the capacity filler's files are deleted so the
backfill never re-queues it.

* fix(file-search): keep the dispatch pass's file-then-queue lock order

Reaping before the backfill held the owner's queue row while the backfill took FOR SHARE on its
unindexed files; a concurrent save holds its file while its trigger waits on that queue row, so the
two could deadlock. The pass runs backfill then reap again, and a wake that conflicts with a row this
transaction's backfill just stamped as a reconciliation clears the stamp, so a reaped claim stays
ahead of the rotation.
…nknown sync target (#9672)

* fix(trigger): keep secret text out of env sync errors and refuse an unknown sync target

A malformed env-vars secret failed with the JSON parser's message, which quotes the text around
the fault, and the Trigger.dev deploy prints that error to a public CI log; the error now names
only the failure. TRIGGER_ENV_SYNC_TARGETS was split on commas without trimming or validation, so
a typo or a space silently kept an environment on its old configuration; it is now parsed by
parseTriggerEnvSyncTargets, which trims entries and refuses a name with no mapped secret (by own
key, so prototype names cannot pass), and the deploy logs which path it took.

Comments now say the CLI applies synced variables when it uploads the version and that the sync
never deletes, describe the retry as every failed request, and point the worker marker at
TRIGGER_CONSTANT_VARS.

* fix(trigger): accept only the exact sync-target list the deploy workflows match

The workflows grant the secret-read credentials by matching TRIGGER_ENV_SYNC_TARGETS as an exact
comma list, so a trimmed parser could accept a padded entry the credential step skipped and fail
inside the sync instead. The parser now refuses a padded, empty, or unknown entry with a message
naming the expected format.
…the preview budget (#9673)

The enterprise issuance preflight counted the organization's billable workflow runs under the
ledger's default 60s bound, the same COUNT DISTINCT the organization detail page already caps at
5s because it can outlast the admin proxy's request timeout on a large payer. The preflight now
reads it through getBillingPeriodWorkflowRunCountWithinBudget, which reports null past the shared
WORKFLOW_RUN_COUNT_BUDGET_MS, so the preview still loads; the contract already allows null. The
over-budget classification moves next to readLedgerBounded as isLedgerReadOverBudget, shared by
both callers.
…s-2026 (#9675)

* docs(library): update best-marketing-automation-platforms-ai-workflows-2026

* Pi Babysit: address PR #9675 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
* docs(library): update best-ai-agent-builders-with-mcp-support

* Pi Babysit: address PR #9676 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
…nd Reporting? (#9677)

* feat(library): How Do You Build an Airtable AI Agent for Data Entry and Reporting?

* Pi Babysit: address PR #9677 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
…ases? (#9678)

* feat(library): How Do You Build a Notion AI Agent for Pages and Databases?

* Pi Babysit: address PR #9678 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
…lose? (#9679)

* feat(library): How Do You Build a QuickBooks AI Agent for Month End Close?

* Pi Babysit: address PR #9679 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
* improvement(ci): avoid redundant browser dependency updates

* fix(ci): retain historical benchmark provisioning and docs triggers
…get runs out (#9682)

* fix(logs): keep checked trace content when the display projection budget runs out

- Project each log display value on its own, so one oversized or ref-backed value withholds only itself instead of the error, message and every other envelope field
- When the whole-trace content walk exhausts its node budget, keep the spans that already passed it and withhold only the rest; every other projection failure still withholds the whole trace
- Put the bulky block dumps after the trace spans so budget pressure drops them first
- Attribute projection warnings to their execution, workflow, workspace, mode, span and field

* fix(logs): bound the withheld-span fallback by the trace's structure budget and make envelope span ids unguessable

* fix(logs): resume the withheld-span fallback from the retained spans' structure budget

* improvement(logs): name the projection warning's read/write phase explicitly
@emir-karabeg
emir-karabeg requested a review from a team as a code owner November 4, 2026 23:24
@vercel

vercel Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
docs Ignored Ignored Preview Nov 5, 2026 7:39am UTC

Request Review

@greptile-apps

greptile-apps Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 3/5

[Medium impact] The changes reach the standalone Platform’s Home, Chat, and Settings flows on web and Desktop.

Fix the lost Stop control and covered desktop panel tabs before merging.

Findings

  1. P1 Follow-up removes Stop ▶
  2. P1 Toolbar covers panel tabs ▶
  3. P2 Unsent messages disappear ▶
  4. P2 Saved profile edits vanish ▶
  5. P2 Navigation resets shell changes ▶

Summary

Adds standalone Home, Chat and Settings routes for browsers and Sim Desktop, with shared controls, gated fixtures and browser/Electron checks.

  • The Platform opens on Home with shared navigation and a dashboard people can customize.
  • Chat keeps drafts, attachments, and panel interactions local to the Platform session.
  • Settings adds profile, appearance, notification, and member screens to the Platform.
  • Shared controls support the new product routes, and the component workbench has its own route.
  • Build and design checks now cover the Platform product routes and Desktop renderer.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Layout["Shared ProductSession"] --> Preferences["Interface preferences"]
  Layout --> Changes["Chat edits"]
  Layout --> Page["Home, Chat or Settings page"]
  Page --> Frame["ProductFrame"]
  Frame --> Shell["PlatformShell"]
  Shell --> Local["Inbox, notices and sort state"]
  Shell --> Feature["Feature screen"]
  Feature --> Draft["Local draft or profile edits"]
Loading

Reviews (1) · Last reviewed commit: "feat(platform): build Core and Chat for ..." · Reviewed by Greptile

Comment on lines +102 to +108
onStop={
last?.response?.state === 'running' ? () => transition(last, 'stopped') : undefined
}
onSubmit={(draft) => {
const next = userTurn(draft)
if (id && !removed) {
changes.setTurnsFor(id, [...turns, next])

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.

P1 Follow-up removes Stop

In /chat/customer-onboarding?state=full, pressing Enter on a follow-up appends a user turn while the response is running. onStop then disappears because it checks only the last turn, which now has no response. The earlier response stays “Working” with no way to stop it. Track the running turn separately, or block submission while it is running.

Comment on lines +17 to +24
padding-inline: var(--spacing-12);
min-inline-size: 0;
height: var(--height-nav);
border-block-end: var(--spacing-stroke-default) solid var(--ui-divider);
}
:global([data-sim-desktop-title-bar]) .header {
height: var(--spacing-48);
-webkit-app-region: drag;

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.

P1 Toolbar covers panel tabs

With the desktop sidebar collapsed, expanding the chat panel puts its tabs under the desktop toolbar and native window buttons. The shell places the content at the top-left, but the panel header keeps only 12px of left padding. Unlike the conversation header, it does not use --ui-navigation-inset. Reserve that space when the panel takes over so its first tab and close control remain clickable.

Comment on lines +18 to +19
<ChatPanels workspaces={shell?.workspaces ?? []} data={panel} key={id?.[0] ?? 'new'}>
<ChatScreen data={data?.chat ?? null} chatId={id?.[0]} />

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.

P2 Unsent messages disappear

Switching chats discards unsent text and selected files. MessageComposer keeps both only in local state, while this route keys the whole panel tree by chat ID. Returning to the original chat creates a fresh composer. Keep drafts in the session, keyed by chat ID, so ordinary navigation does not erase unfinished messages.

Comment on lines +20 to +21
const [displayName, setDisplayName] = useState(name ?? '')
const [picture, setPicture] = useState<string>()

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.

P2 Saved profile edits vanish

Saving a display name or choosing a profile picture lasts only until the user leaves Preferences. ProfileSettings owns these values locally and is removed on other Settings sections. Returning restores the fixture name and drops the picture. Keep these local edits in the session provider; no database write is needed.

Comment on lines +30 to +33
<ProductSurface preference={appearance}>
<PlatformShell data={shell} desktopTitle={desktopTitle} navigation={navigation}>
{children}
</PlatformShell>

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.

P2 Navigation resets shell changes

Each page creates its own PlatformShell, so moving between Home, Chat and Settings resets inbox read marks, dismissed notices and chat sorting. For example, reading an unread item, opening a chat and returning makes that item unread again. Move this session state above the page boundary, alongside the existing chat edits and interface preferences.

@cubic-dev-ai cubic-dev-ai Bot 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.

6 issues found and verified against the latest diff

Confidence score: 3/5

  • chat-screen.tsx can bring submitted attachments back into the composer when the first send creates a chat. Clear the draft attachments before the route change remounts the composer.
  • recent-items.tsx leaves the remove and select controls too small to tap reliably on coarse-pointer devices. Give both controls larger hit targets.
  • workspace-list.tsx has no visible focus indicator, so keyboard users may lose track of which workspace is focused. Add a :focus-visible treatment.
  • product-surface.tsx briefly renders light for users whose system theme is dark. Resolve the system theme before first paint to avoid the flash.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/platform/design-system/components/recent-items.tsx">

<violation number="1" location="apps/sim/platform/design-system/components/recent-items.tsx:32">
P2: These new recents controls do not provide a coarse-pointer hit target: the row grows to 44px, but the remove button remains an intrinsic icon-sized target and the select button remains text-sized. Give both controls a 44px touch target, such as the existing `IconButton` touch sizing or `TouchTarget` wrapper, while preserving the row layout.</violation>
</file>

<file name="apps/sim/platform/design-system/components/workspace-list.tsx">

<violation number="1" location="apps/sim/platform/design-system/components/workspace-list.tsx:43">
P2: This new keyboard-focusable workspace button has no visible focus indicator because the global stylesheet removes outlines and `.workspaceHit` adds none. Add a shared or workspace-specific `:focus-visible` treatment so keyboard users can identify the active row.</violation>
</file>

<file name="apps/sim/platform/design-system/components/product-surface.tsx">

<violation number="1" location="apps/sim/platform/design-system/components/product-surface.tsx:10">
P2: System appearance always renders light on the server, causing dark-system users to see a light flash before hydration applies the media-query result. Resolve the system theme before first paint, such as with a CSS media-query path plus explicit-theme override or a small theme bootstrap.</violation>
</file>

<file name="apps/sim/platform/design-system/components/select.tsx">

<violation number="1" location="apps/sim/platform/design-system/components/select.tsx:37">
P2: An arbitrary `theme` query can reach this controlled value without matching any option, so the Theme picker renders blank even though the surrounding product falls back to `system`. Ignore values outside `options` here or pass the normalized theme from the settings screen.</violation>
</file>

<file name="apps/sim/platform/design-system/features/chat/chat-screen.tsx">

<violation number="1" location="apps/sim/platform/design-system/features/chat/chat-screen.tsx:100">
P2: The first send from `/chat` clears the composer only until `router.replace` changes `id`; this prop then remounts `MessageComposer` with the unchanged draft attachments, so submitted files reappear in the new chat’s composer. Track and consume the initial draft when submitting before navigation.</violation>
</file>

<file name="apps/sim/platform/design-system/components/notification-tray.module.css">

<violation number="1" location="apps/sim/platform/design-system/components/notification-tray.module.css:10">
P2: The fixed notification stack ignores the bottom safe-area inset, so its action and dismiss controls can sit under the home indicator on notched mobile browsers. Use `max(var(--spacing-20), env(safe-area-inset-bottom))` for the bottom offset.

(Based on your team's feedback about toast safe-area minimum.) [8c7692a3-398e-4004-b266-f2a76922d36c]</violation>
</file>

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

{item.label}
{onRemove ? (
<button
className={styles.removeRecent}

@cubic-dev-ai cubic-dev-ai Bot Nov 4, 2026 •

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.

P2: These new recents controls do not provide a coarse-pointer hit target: the row grows to 44px, but the remove button remains an intrinsic icon-sized target and the select button remains text-sized. Give both controls a 44px touch target, such as the existing IconButton touch sizing or TouchTarget wrapper, while preserving the row layout.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/platform/design-system/components/recent-items.tsx, line 32:

<comment>These new recents controls do not provide a coarse-pointer hit target: the row grows to 44px, but the remove button remains an intrinsic icon-sized target and the select button remains text-sized. Give both controls a 44px touch target, such as the existing `IconButton` touch sizing or `TouchTarget` wrapper, while preserving the row layout.</comment>

<file context>
@@ -25,8 +27,26 @@ export function RecentItems({ label, title = label, items }: RecentItemsProps) {
-              {item.label}
+              {onRemove ? (
+                <button
+                  className={styles.removeRecent}
+                  type='button'
+                  aria-label={`Remove ${item.label} from recents`}
</file context>
Fix with cubic

Comment thread apps/sim/platform/app/layout.tsx Resolved
* fix(files): let Mothership tool runs write workspace files

`tools execute` authorizes the chat's delegated principal for
`sim:tool-execution` and hands it to the tool layer unchanged, but every
workspace-file use case admits only `sim:workspace-files`. Every File tool
run directly from a Mothership chat therefore failed with a 403
"Delegated workspace access is no longer valid", which the agent relayed
to the user as lost workspace access. The File tool handler now rebinds a
delegated caller to the workspace-files audience (keeping subject,
workspace, expiry ceiling and resource scope), as function-execute and
payload materialization already do; the file use cases still check the
subject's current workspace role.

`image_generate` outside a workflow run returned only a URL for its
copilot-storage upload, so the file-output processor downloaded and
re-uploaded it, storing every image twice under two keys and returning
`imageUrl` and `image` that disagreed. It now returns the stored file.

* improvement(files): sniff generated images once and refuse foreign delegations with 403

Generated images are now content-sniffed before they are stored, so every
output field references the same verified object on both the workflow and
chat paths; skipping the re-upload had otherwise skipped the sniff it did.
A delegation minted for another workspace is no longer rebound (which
threw and surfaced a 500); the workspace-file use cases refuse it with
their ordinary 403. The regression test now uses the shared principal
factory and uploads mock.

* chore(files): import the principal factory directly in the delegated write test
…inks (#9686)

* improvement(links): open go/ links alongside sim/ and call the page Links

* improvement(links): share the go/ and sim/ host list and show link names without a prefix

* improvement(links): name both prefixes in the extension description and recovery policy

* improvement(links): drop extension version qualifiers from the docs
waleedlatif1 and others added 14 commits November 4, 2026 23:02
Members and Requests fill their Settings sections, and People is a gated
route with a directory, a fixed-layout org chart, profiles, import and sync.

Moves the people URL parsers, sheet reader, role lock reason, invite failure
message and policy descriptions out of app and component folders so both
surfaces read one copy. Legacy behavior is unchanged.
…he Platform

Each section reads through a lib/data module over the existing hooks. Helpers
tangled in legacy panels move out and are reimported, so both surfaces share one
implementation: the picture upload hook, timezone picker, date-string parser,
subscription permissions, invoice formatting, the usage-limit decision, whitelabel
form rules, usage folding, personal account grouping and consent copy.

BarChart stacks up to six layers. Fixtures keep their two-series output.
Usage-limit saves fire from typing rather than an effect, failed reads get a
retry where they read as empty, searches debounce their fetch, and the settings
search and Sim Links URL definitions move to shared modules so each has one owner.
A TableLabel owner replaces three copies of the name-with-mark cell.
Email and Calendar read through the viewer's own grants, the same records the
Integrations page lists, over two new session-only routes. The member inventory
moves to lib so both surfaces share one rule for what an organization offers.
Recents remove for real, the activity chart appears only for those who can read
it, and every widget distinguishes loading, empty, not offered, reconnect and retry.
Adds Permission groups, Audit logs, Single sign-on with Domains and Provisioning,
Security, Data retention and Data drains as live sections over the existing hooks and
routes, with their shared parts and the parity ledger and live spec.
One discard dialog and the legacy leave and history hooks replace a forked guard,
so browser Back is held as well. Read failures, query status, paging and boolean
rows each get one owner, and logo and wordmark share a draft hook. Heatmap, chart
and link rows stop rebuilding on every hover or keystroke.
Pins the audit feed's rolling start date so its query key is stable, distinguishes
failed reads from empty ones, and moves rows, dates and form booleans onto their
design-system owners.
The hook reached the block registry, trigger registry and legacy UI through a
few hub modules. Pull the pure pieces it needs into their own modules and
re-export from the old paths, so legacy keeps identical behavior and the
Platform can drive the same hook:

- parseBlocks, the special-tag parser, question answers and activity labels
- deployment, log and OAuth credential query keys
- the query client singleton, feature flags and workspace host contexts
- useChat gains isNewChatRoute; useRestoredChatEntry gains chatPath
- the Platform maps Search and Assistant chat routes
Conversation surfaces read a ChatRuntime, filled by the existing chat hook on
live data and by session state on fixtures. toPlatformTurns projects the hook's
transcript into ordered parts with per-tool status and subagent lanes.

Rebuild rendering on Platform EMCN: markdown, every tool state, lanes, tool
approval, question, credential and assistant review cards. The composer uploads,
persists drafts and carries effort, model and plan behind their flags. The panel
reads real resources through ?resource=. Add /search and /search/chat/[id], and
hand Home's Build and Search entries to them.
A concurrent read rotating a refused token no longer reads as needing reauth, and a
failed background refetch no longer hides data already on screen. Provider requests
ask only for the fields a widget shows, so a large mailbox cannot trip the byte cap.
The selected day is an absolute date in the URL, so midnight cannot move it. Get
started's completion rule and the integrations gate now have one definition each.
Extracts the in-app link predicate and audit window and refresh rules so legacy and
Platform read one implementation, mounts permission grids and audit details only when
opened, and drops pass-through parts.
Carry the hook's own types through the models instead of unknown casts, parse a
Search target once in the projection, and move secret saving out of the card.
Drop fields nothing reads. Cache settled turns with their copy text and pairing,
serialize a tool payload once, follow the stream with a ResizeObserver, and read
reference inventories only when a picker opens. Search stays idle behind
Assistant. The read-path grammar is one shared module.
Refuse a personal secret save when its pre-save read fails, since saving replaces
the whole map. A query in the URL now outranks the remembered mode, and a search is
recorded on submit rather than on any URL change, Home's entry included. Keep typed
edits on undecided review cards. Use the owning design-system components for links,
secrets, notices and rows, and distinguish history loading from empty.

This branch was previously deployed

1 inactive (outdated) deployment
Preview — f5128ff3 Deployed Nov 5, 2026 by vercel[bot]
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.

2 participants