toga-ai 1.0.736 → 1.0.737

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,307 @@
1
+ ---
2
+ type: session
3
+ slug: talos-streaming-supply
4
+ title: Talos streaming chat BUILT in toga25-supply + blox controlled panel
5
+ author: apeterson
6
+ repos: [toga25-supply, toga-blox, talos]
7
+ framework: "2.0"
8
+ client: shared
9
+ status: active
10
+ created: 2026-09-02
11
+ updated: 2026-09-02
12
+ ---
13
+
14
+ # Session: talos-streaming-supply
15
+ **Date:** 2026-09-02
16
+ **Project/Repo:** toga25-supply + toga-blox (2.0)
17
+ **Task:** Build the live Aegra/LangGraph Talos chat in `toga25-supply` — app-local stream provider, controlled `TalosPanel` in blox, markdown/link rendering, dock-aware modal geometry — and align it to the backend team's implementation handoff.
18
+
19
+ > **Supersedes `2026-08-28-talos-streaming-supply-apeterson`**, which was research/design only
20
+ > ("NO application code was written"). This session wrote the code. Read that file only for the
21
+ > original decision rationale; its status lines are stale.
22
+
23
+ > **⚠ READ FIRST: `~/Desktop/toga-supply-streaming-chat-handoff.md`** (1230 lines, from
24
+ > akhokhani/backend, contract date 2026-08-28, "implementation-ready"). It is the authoritative
25
+ > contract for endpoints, the event/privacy boundary, progress copy, feedback, and markdown scope.
26
+ > It was NOT in this session's context until late — it had been uploaded to a *different* Claude
27
+ > conversation ("cont: talos-streaming-supply"), and `/session-resume` restores the saved summary,
28
+ > not attachments. **Keep it on disk and name the path in every future session file.**
29
+
30
+ ---
31
+
32
+ ## What WORKED
33
+
34
+ - **Aegra accepts a supply-issued api2 JWT — blocker (c) from the prior session is CLOSED (World A).**
35
+ akhokhani supplied a working `GET /assistants` call whose bearer had `aud: https://nychh.togasupply.com`
36
+ (a **supply** hostname), `iss/sub: http://api.beta.togahub.com`, 3600s life. No token exchange,
37
+ no `/auth/delegator`, no auth-exchange layer needed.
38
+ - **`assistantId` is PER-TENANT and must be fetched at runtime.**
39
+ `GET /assistants?limit=100&offset=0` with the bearer. NYCHH returns one: name "TOGa Supply",
40
+ `graph_id: deep_agent`, `metadata.customer_facing: true`, `read_only: true`,
41
+ `tenant_slug: "NYC Health + Hospitals"`. Selection rule adopted: prefer
42
+ `metadata.customer_facing === true`, else the full list, take the first, `console.warn` when >1.
43
+ - **CORS is NOT a blocker — blocker (a) is CLOSED.** Aegra returns
44
+ `access-control-allow-origin: *` on the OPTIONS preflight for **both** a local dev origin
45
+ (`http://nychh.dev.sandbox.togasupply:5173`) and the prod origin. The old worry about needing to
46
+ spoof `Origin` for the `Domains` gate is **moot for supply**: supply's real browser origin *is*
47
+ the registered hostname, so the browser sets it correctly for free. (talos had to spoof
48
+ server-side only because its origin differed.)
49
+ - **`https://api.togaiq.com` (production Aegra) is LIVE.** `/health` 200, `/info` 200,
50
+ `/assistants` 401 without a token. Developer is running against it and it works end-to-end
51
+ (real answers about real NYCHH orders). `.env.development` now has
52
+ `VITE_TALOS_API = 'https://api.togaiq.com'` and `VITE_API = 'https://api.togahub.com/v2'`.
53
+ - **The blox `TalosPanel` controlled-mode contract change is BUILT** (was only DECIDED before).
54
+ New optional props: `messages`, `isStreaming`, `progress`, `onSubmit`, `onStop`, `connection`,
55
+ `renderMarkdown`. Controlled mode keys off `messages` being **passed at all**, so BDR and
56
+ `createTalosStubAdapter()` are untouched. 22 blox Talos tests pass, including two that pin
57
+ uncontrolled behaviour as unchanged.
58
+ - **Final-answer-only reveal implemented** per handoff §8.6/§8.8. Chunks buffer privately; the
59
+ assistant turn is revealed only on `talos.run.terminal.v1` with `status: "completed"`. Fails
60
+ closed — a run that ends with no terminal event shows approved error copy, never the partial.
61
+ - **Progress copy** per §8.7 verbatim: `Thinking…` → `Checking your Supply information…` (narration
62
+ event **or** ~1500ms) → `Preparing your answer…` (first buffered chunk). Narration text is never
63
+ echoed, only used as proof work started. Rendered via a new blox `progress` prop with
64
+ `aria-live="polite"` and a `prefers-reduced-motion` guard.
65
+ - **Markdown option (b) implemented**: blox got a `renderMarkdown?: (text) => ReactNode` slot;
66
+ `toga25-supply/src/components/TalosMarkdown/` fills it with `react-markdown@10.1.0` +
67
+ `remark-gfm@4.0.1`. Covers the full §10 list (headings, ordered/nested lists, inline+fenced code,
68
+ blockquotes, GFM tables). No raw HTML, links allowlisted to `http:`/`https:`/`mailto:`,
69
+ external → new tab, same-origin → react-router. 10 tests.
70
+ - **Clickable links in replies.** blox `RichText` now parses `[label](url)`; same-origin links go
71
+ through a new `adapter.onNavigate` (supply wires it to react-router `navigate()`), cross-origin
72
+ open in a new tab with `rel="noopener noreferrer"`, unsafe schemes render as inert text.
73
+ - **Record modal now responds to the docked panel.** Read the real design source rather than
74
+ guessing: `demo/demo-modal.jsx` binds the overlay's `right` to the dock width with
75
+ `260ms cubic-bezier(0.2,0.8,0.2,1)`, confirmed by `slide-up-modal-geometry.md`.
76
+ `AppLayout` publishes `--talos-dock-width` on `document.documentElement`; blox
77
+ `TableRecordModal` reads it (`.backdrop`, `.modal-bottom`, `.modal-center`, `.modal-right`).
78
+ Defaults to `0px` so all other blox consumers are unaffected.
79
+ - **Reading the Claude Design project via the `DesignSync` MCP worked** without a design-login
80
+ prompt: `get_project` / `list_files` / `get_file` on project
81
+ `05d4a4a0-9062-4479-9e10-225021e45cc9` ("TOGa Supply", owner David). The self-contained HTML is
82
+ too large for `get_file` (256KiB cap) — read the **JSX sources** (`demo/demo-shell.jsx`,
83
+ `demo/demo-modal.jsx`) and the `*.md` geometry notes instead.
84
+ - **Two real bugs caught by writing tests**, both described under *Decisions*/*Not retry* below.
85
+
86
+ ## What did NOT work — DO NOT RETRY THESE
87
+
88
+ - **`npx tsc --noEmit` at the `toga25-supply` repo root is a SILENT NO-OP.** The root
89
+ `tsconfig.json` is references-only (`files: []`), so it compiles nothing and **always exits 0**.
90
+ It reported "clean" twice on a codebase with real type errors. **Always use
91
+ `npx tsc --noEmit -p tsconfig.app.json`** (or `tsc -b`). The repo architecture doc used to
92
+ recommend the broken command; that has been corrected in the KB this session, and the rule is
93
+ now in `2.0/standards/frontend.md` §2.
94
+ - **Mapping LangGraph user turns to `TalosMessageModel.message` renders an EMPTY BUBBLE.**
95
+ `TalosMessage` renders `message.text` for a **user** turn and `message.message`/`paras` for a
96
+ **talos** turn. The mapper must emit `{role:"user", text}` vs `{role:"talos", message}`.
97
+ - **Mapping empty AI turns to `{thinking: true}` leaves a permanent row of stuck loading dots.**
98
+ An empty AI turn is a **tool-call turn** — LangGraph puts the call in `tool_calls` and leaves
99
+ `content` blank — and a thread accumulates several. Drop every empty turn; the live thinking
100
+ indicator comes from `isStreaming`, rendered as ONE synthetic trailing bubble by the panel.
101
+ - **`ln -sfn <target> node_modules/@agilant/toga-blox` does NOT replace a real directory** — it
102
+ creates the link *inside* it. Must `rm -rf` the directory first.
103
+ - **"one fewer peer" was the WRONG reason to pick `@langchain/langgraph-sdk` over
104
+ `@langchain/react`.** The SDK (`1.10.0`) itself declares `@langchain/core: ^1.1.48` as a
105
+ **required, non-optional** peer. Symptom when missing: Vite/esbuild dep-optimize hard-fails with
106
+ `Could not resolve "@langchain/core/messages"` and the dev server exits. The decision still
107
+ stands (proven in-house, smaller surface) — only that rationale is wrong. Corrected in the KB.
108
+ - **`@assistant-ui/react-markdown` cannot be used "markdown only, without the runtime."** It
109
+ declares a **required peer on `@assistant-ui/react` ^0.15.0`**, which pulls `zustand ^5.0.15`
110
+ (supply pins `5.0.14`), `radix-ui`, `assistant-cloud`, `assistant-stream`. Its actual engine is
111
+ `react-markdown@^10` — so if you only want markdown, use `react-markdown` + `remark-gfm`
112
+ directly (what this session did).
113
+ - **`stream.stop()` does not stop the run server-side** — it only drops the client transport; the
114
+ run keeps executing and billing. The real stop is
115
+ `POST /threads/{id}/runs/{run_id}/cancel?wait=0&action=cancel`. **Still wired to `stream.stop()`
116
+ — this is an open defect** (see Blockers).
117
+ - **Putting the thread id in the URL query string wiped the conversation.** Any navigation that
118
+ rewrote the search string — a record link in a reply (`?sales-orders=<uuid>`), a side-nav click —
119
+ dropped `talosThread`, remounted `useStream` with no thread, and cleared the visible chat. Do not
120
+ reintroduce a URL-param thread. It is now `sessionStorage`, keyed by tenant + user.
121
+ - **Guessing at Aegra host names is a waste of time.** `api.gamma.togaiq.com`,
122
+ `api.prod.togaiq.com`, `api.dev.togaiq.com`, `api.sandbox.togaiq.com`, `togaiq.com` all fail to
123
+ connect (`000`). Only `api.beta.togaiq.com` and `api.togaiq.com` exist.
124
+ - **`api.beta.togaiq.com` was returning 502 for most of this session** (`/health`, `/info`,
125
+ `/assistants`) after being healthy ~10 minutes earlier. Not a code problem. Production was used
126
+ instead. Also note: **Aegra sends CORS headers on the OPTIONS preflight but NOT on actual
127
+ responses** — reported to akhokhani, still unfixed as far as this session knows.
128
+ - **`knowledge.js kickoff-preflight` returned `features: []`** for toga25-supply, toga-blox and
129
+ talos on `--q="talos streaming chat"` even though three highly relevant feature docs exist. That
130
+ is the **second** session in a row. Search `knowledge/2.0/apps/<repo>/features/` directly.
131
+
132
+ ## Not tried yet (candidates for next session)
133
+
134
+ **This is the remaining build, and it is the bulk of the feature. All endpoints are already
135
+ implemented in `src/api/talos.ts`; what is missing is the parser, the hook, and the blox views.**
136
+
137
+ - **`fromTalosTranscript()` — the centralized transcript parser.** `GET /threads/{id}/transcript`
138
+ returns `{messages:[{id, logical_message_id, type, content, run_id, created_at, active}], next_cursor}`,
139
+ oldest-first. `content` is **polymorphic**: normalize a string directly; for an array of
140
+ LangChain content blocks concatenate only ordered `type:"text"` blocks with a string `text`;
141
+ ignore reasoning, tool-use/tool-result, binary and unknown block types; **do not create an empty
142
+ bubble** if no visible text remains. Render only **active** `human`/`ai`. Deduplicate by stable
143
+ `id`; for revisions use `logical_message_id` + `active`, **never content matching**. The handoff
144
+ requires this parser be centralized and regression-tested.
145
+ - **Terminal-completion reconciliation (§8.8).** On `completed`: refetch the newest transcript page,
146
+ select the active AI message for this `run_id` (prefer `final_message_id`), replace the optimistic
147
+ human message with its durable record, publish in ONE update, then refetch the thread for its
148
+ generated title and refresh the list. **The transcript is authoritative — if buffer and transcript
149
+ disagree, display the transcript.** Currently the buffer is displayed on completion.
150
+ - **`useTalosThreads` hook** — React Query list/rename/delete/select over the client functions.
151
+ Cache scope MUST include the authenticated subject + org identity, and must be cleared on
152
+ logout/account switch (§8.2). Page 20 at a time, sort by `updated_at` desc, dedupe by `thread_id`.
153
+ - **blox view props for the history drawer**: `threads`, `activeThreadId`, `onSelectThread`,
154
+ `onNewChat`, `onRenameThread`, `onDeleteThread`. blox already has a history-drawer UI built for
155
+ its *internal* conversation list (`feat.history`, `showHistory`, `styles.history`) — make it
156
+ render host-supplied threads when controlled. Title = `metadata.thread_name` falling back to
157
+ `New conversation`.
158
+ - **Re-enable `history` / `newChat` in controlled mode** once the host supplies threads. They are
159
+ currently **force-disabled** in `TalosPanel` (`isControlled ? {history:false,newChat:false} : {}`)
160
+ — that is why the design's chat-history and `+` buttons are missing from supply. The `+` is the
161
+ cheap half: the context already exposes `newThread()`; it needs an `onNewChat` prop.
162
+ - **Message actions**: copy, plus thumbs up/down. Show actions **only for completed AI messages**.
163
+ - **Thumbs-down dialog (§9)** — must match standalone Talos: title `What went wrong?`; multi-select
164
+ reasons where selecting one does not clear another; always show a comments textarea; placeholder
165
+ `Additional comments (optional)` until `other` is selected, then `Please describe the issue…` and
166
+ **required non-empty**; Submit disabled until a reason is selected (and text present for `other`);
167
+ Cancel submits nothing and restores focus to the thumbs-down button.
168
+ - **Rename ETag/`If-Match` flow (§8.10).** `renameTalosThread` already sends `If-Match` when given
169
+ `metadataVersion`, but nothing fetches/threads the ETag yet.
170
+ - **Reconnect/rejoin (§8.13)** — `GET /threads/{id}/runs/{run_id}/stream` on transport loss,
171
+ `talos.run.transport.v1` → delayed `Reconnecting…`, `talos.run.replay_gap.v1` → refetch run +
172
+ transcript. Also `GET /threads/{id}/runs?limit=1` to recover the latest run after reload.
173
+ - **Wire Stop to `cancelTalosRun`** instead of `stream.stop()` — needs the `run_id`, which comes
174
+ from the `metadata` SSE event (§8.5, "capture run identity"). Not captured yet.
175
+ - **Fix the pre-existing `AdvancedSelect` type error** at
176
+ `src/pages/TransferOrders/view/CreateTransferOrderModal/CreateTransferOrderModal.tsx:603` —
177
+ passes `name` to single-mode `AdvancedSelect`, which does not accept it. Predates this session
178
+ and is unrelated to Talos; left alone deliberately.
179
+ - **Confirm which api2 issuer each Aegra environment trusts.** Never resolved: the demo token came
180
+ from `api.beta.togahub.com`, supply's envs use `api.togahub.com` / `api.gamma.togahub.com` /
181
+ `api.dev.sandbox.togahub.com`. Production works today, so this only matters for beta testing.
182
+ - **Ask akhokhani to point MCP at beta** if beta testing is wanted — MCP currently targets
183
+ production `api.togahub.com`, so a beta/dev token cannot answer data questions like "what is the
184
+ last sales order?"
185
+ - **Confirm the per-client CORS/`Domains` coverage** for the other supply hostnames (compass, quad,
186
+ prudential, growrk, …). Only nychh has been exercised.
187
+ - **No test covers `TalosStreamProvider`** — `useStream` would need mocking. The mapper, the panel
188
+ and the markdown renderer are covered; the provider wiring is hand-verified only.
189
+
190
+ ## Current file state
191
+
192
+ **toga25-supply** (branch `features-talos-transferorders`)
193
+
194
+ | File | Status | Notes |
195
+ |------|--------|-------|
196
+ | `src/api/talos.ts` | CREATED | Sole `VITE_TALOS_API` chokepoint. `isTalosConfigured`, `getTalosApiUrl`, `fetchTalosAssistants`, `pickTalosAssistant`, plus **threads/transcript/feedback/cancel**: `fetchTalosThreads`, `threadTitle`, `renameTalosThread` (sends `If-Match`), `deleteTalosThread`, `fetchTalosTranscript`, `fetchTalosFeedback` (indexed by `message_id`), `putTalosFeedback`, `deleteTalosFeedback`, `cancelTalosRun`. All route through one `talosRequest` helper that logs status before throwing. **Written and typechecking; only the assistants half is exercised at runtime.** |
197
+ | `src/utils/talosMessages.ts` | CREATED | `extractMessageText` (string-or-blocks union), `toTalosMessage`, `toTalosMessages`, `withoutTrailingAssistant`. Drops all empty turns and every non-`human`/`ai` type. Tool calls + interrupts deliberately dropped. |
198
+ | `src/utils/talosMessages.test.ts` | CREATED | 13 tests. |
199
+ | `src/hooks/useTalosSession.ts` | CREATED | Resolves token + assistantId → `connection: "disabled"\|"connecting"\|"ready"\|"error"`. Gates on `isAuthenticated` (the `user` key) **not** on an accessToken's presence (blox `fetchPublicToken` leaves a PUBLIC token in storage). Assistants query keyed by **user uuid, never the token** — supply persists the query cache to storage. |
200
+ | `src/providers/TalosStreamProvider.tsx` | CREATED | Single `useStream`; outer gate + inner `TalosStreamSession` (hooks cannot be conditional). Thread in `sessionStorage` keyed by tenant+user, re-seeded when the key changes. Terminal-truth reveal gate, progress stage machine, `TERMINAL_TEXT` copy map. Exposes `messages`/`isStreaming`/`connection`/`progress`/`submit`/`stop`/`newThread`. |
201
+ | `src/components/TalosMarkdown/{TalosMarkdown.tsx,TalosMarkdown.module.css,index.ts}` | CREATED | `react-markdown` + `remark-gfm`; no raw HTML, link scheme allowlist, external → new tab, same-origin → react-router, tables/code scroll in their own box, Supply tokens. |
202
+ | `src/components/TalosMarkdown/TalosMarkdown.test.tsx` | CREATED | 10 tests incl. raw-HTML and `javascript:` rejection. |
203
+ | `src/layout/AppLayout/AppLayout.tsx` | MODIFIED | Split into `AppLayoutInner` wrapped by `TalosStreamProvider`. Panel driven by controlled props + `progress` + `renderMarkdown`; adapter carries navigation only (`onOpenRecord`, `onAction`, `onNavigate`, `onDockWidthChange`). Publishes `--talos-dock-width` on `document.documentElement`. |
204
+ | `.env.development` and 6 other non-prod env files | MODIFIED | `VITE_TALOS_API` added. `.env.development` now points at `https://api.togaiq.com` (production Aegra); the others still at `https://api.beta.togaiq.com`. `.env` (production) leaves it **commented out**. |
205
+ | `package.json` / `package-lock.json` | MODIFIED | Added `@langchain/langgraph-sdk@1.10.0`, `@langchain/core@1.2.9`, `react-markdown@10.1.0`, `remark-gfm@4.0.1`. |
206
+ | `src/pages/TransferOrders/.../CreateTransferOrderModal.tsx` | UNTOUCHED | Pre-existing `AdvancedSelect` `name` type error at line 603. Not mine, not fixed. |
207
+
208
+ **toga-blox** (branch `features-talos-transferorders`, `origin/TRUE-80692-BDR` merged in this session)
209
+
210
+ | File | Status | Notes |
211
+ |------|--------|-------|
212
+ | `src/components/Talos/types.ts` | MODIFIED | Added `TalosConnection`; on `TalosPanelProps`: `messages`, `isStreaming`, `progress`, `onSubmit`, `onStop`, `connection`, `renderMarkdown`. On `TalosAdapter`: `onNavigate`. On `TalosMessageModel`: `thinkingLabel`. On `TalosMessageHandlers`: `onNavigate`, `renderMarkdown`. |
213
+ | `src/components/Talos/TalosPanel.tsx` | MODIFIED | Controlled mode keyed off `messages !== undefined`; `history`/`newChat` **force-disabled** when controlled; composer disabled unless `connection === "ready"` with staged placeholder copy; send↔stop swap; ONE synthetic trailing thinking bubble driven by `isStreaming`. Docblock added. |
214
+ | `src/components/Talos/TalosMessage.tsx` | MODIFIED | Uses `renderMarkdown` when supplied, else built-in `TalosMarkdown`; renders `thinkingLabel`; threads `onNavigate`. |
215
+ | `src/components/Talos/helpers.tsx` | MODIFIED | `RichText` parses `[label](url)` with a scheme allowlist + same-origin `onNavigate`; `toMarkdownBlocks` gained GFM **table** parsing; `TalosMarkdown` renders tables and takes `onNavigate`. |
216
+ | `src/components/Talos/Talos.module.css` | MODIFIED | Added `.mdTable`, `.mdLink`, `.thinkingRow`, `.thinkingLabel`, `.mdHost` (+ reduced-motion guard). |
217
+ | `src/components/Talos/TalosPanel.test.tsx` | CREATED | 12 tests — first tests ever for Talos. |
218
+ | `src/components/Talos/helpers.test.tsx` | CREATED | 10 tests — table parsing + link safety. |
219
+ | `src/components/Talos/README.md` | CREATED | 225 lines: the two modes, prop/adapter tables, `features` defaults, message model with the `text`-vs-`message` trap, markdown rules, theming + density ceiling, dock-width rule, file map, gotchas. |
220
+ | `src/components/TableRecordModal/tableRecordModal.module.css` | MODIFIED | `.backdrop`/`.modal-right` stop at `var(--talos-dock-width, 0px)`; `.modal-bottom`/`.modal-center` re-centre via `left: calc(50% - var(--talos-dock-width,0px)/2)` and cap `max-width` to the remaining space; 260ms easing. Defaults to 0px ⇒ non-breaking. |
221
+ | `package-lock.json` | MODIFIED | pre-existing dirty before this session. |
222
+
223
+ **Test status:** blox Talos 22 pass; supply 57 pass (10 files). blox's ~40 pre-existing unrelated
224
+ failures elsewhere in the suite still stand.
225
+
226
+ ## Decisions made
227
+
228
+ - **Merged `origin/TRUE-80692-BDR` into `features-talos-transferorders` before implementing.**
229
+ Rationale: Talos exists on NO mainline blox branch; `origin/TRUE-80692` has the **narrow**
230
+ contract while tcox's widening landed on `TRUE-80692-BDR` — two divergent contracts. BDR
231
+ validated the widened one live. *Rejected:* building on `TRUE-80692` and merging tcox later.
232
+ - **Kept blox `TalosPanel` as the conversation surface; did NOT adopt `@assistant-ui/react`.**
233
+ The handoff §1 recommends assistant-ui's `ExternalStoreRuntime`. Rationale for deviating: our
234
+ provider already implements the principle that matters (host owns message state, view is dumb);
235
+ the blox panel is built, themed, tested and has a **second consumer in BDR**; and David's Claude
236
+ Design mockup shows the blox panel, not assistant-ui. **This is a live, unratified deviation from
237
+ the handoff — flag it to the doc's author.**
238
+ - **Markdown option (b): `renderMarkdown` slot in blox, `react-markdown` in the app.**
239
+ Rationale: blox peers do not hoist (frontend.md §13b), so a markdown dependency in blox lands on
240
+ all five consumer apps; the slot mirrors blox's existing `footer`/`titleIcon` pattern and is
241
+ reusable by desk/commerce. *Rejected:* (a) hand-extending `TalosMarkdown` with headings/ordered
242
+ lists/fenced code/blockquotes — exactly the edge-case work a parser exists for.
243
+ - **Thread id in `sessionStorage`, NOT the URL.** Rationale: a thread is scoped to one user on
244
+ Aegra so the URL was never meaningfully shareable; it only bought reload-persistence, which
245
+ storage gives without the coupling that wiped the chat on every query-string rewrite. Keyed by
246
+ tenant+user per §18. *Rejected:* URL search param (the original design decision — reverse it
247
+ knowingly); `localStorage` (survives too long for "which chat is open").
248
+ - **`--talos-dock-width` CSS custom property, not a `window` event bus.** Rationale: the design
249
+ prototype uses `window.__talosDockW` + `window.__talosDockSubs`, but blox deliberately removed a
250
+ `window.dispatchEvent` side channel during its own review; a CSS var crosses the portal boundary
251
+ with no global mutable state and defaults cleanly to `0px`. *Rejected:* copying the prototype's
252
+ subscriber set.
253
+ - **Reveal gate keyed on the terminal event, failing closed.** Rationale: §8.6 requires
254
+ `messages`/`messages/partial` never be revealed and §8.8 makes the terminal authoritative. A run
255
+ that ends with no terminal shows approved copy rather than the buffer. *Rejected:* treating
256
+ `isLoading === false` as success.
257
+ - **Progress copy comes from the host, never inferred by blox.** blox renders `thinkingLabel`; it
258
+ never invents a step name, percentage or ETA (§8.7 forbids those).
259
+ - **`max-width` cap rather than the design's `width` swap for the docked modal.** The design does
260
+ `width: dockW ? "calc(100% - 72px)" : "90%"`; `width` here is a % of the viewport and cannot see
261
+ the dock, so capping `max-width` to the remaining space achieves the same visible result with a
262
+ `--tableRecordModal-gutter` (24px) knob.
263
+ - **Assistant selection prefers `metadata.customer_facing`, warns when ambiguous, never throws.**
264
+ Matches the frontend.md §20 resolver doctrine (unknown ⇒ disabled + `console.warn`, never a
265
+ silent no-op or a throw).
266
+
267
+ ## Blockers
268
+
269
+ - **Stop does not actually stop the run.** `onStop` → `stream.stop()` drops only the client
270
+ transport; the run continues executing and billing. Fixing it needs the `run_id` captured from
271
+ the `metadata` SSE event (§8.5), then `cancelTalosRun`. `cancelTalosRun` is written; the run_id
272
+ capture is not. **Treat as a real defect, not a nicety.**
273
+ - **The buffer, not the transcript, is displayed on completion.** §8.8 makes the transcript
274
+ authoritative. Until the reconciliation is built, a divergence between buffer and durable record
275
+ shows the wrong text, and message ids needed for feedback are unavailable.
276
+ - **Chat history and `+ new chat` are missing from the supply UI** because `TalosPanel`
277
+ force-disables them in controlled mode. This is the visible gap the developer reported against
278
+ the design.
279
+ - **Aegra `api.beta.togaiq.com` was 502 throughout most of the session** and, separately, **Aegra
280
+ returns CORS headers on the OPTIONS preflight only, not on actual responses.** Both reported to
281
+ akhokhani; neither confirmed fixed. Production (`api.togaiq.com`) is healthy and is what works.
282
+ - **Deviating from the handoff on the conversation surface (blox vs assistant-ui) is unratified.**
283
+ Every remaining item (history, message actions, feedback) is view work, which is exactly where
284
+ the two paths diverge. Get agreement before building those, or expect rework.
285
+ - **`npm install` in `toga25-supply` destroys the blox symlink** every time, replacing it with
286
+ registry `1.1.1-production.139` which has **no Talos** — symptom is
287
+ `does not provide an export named 'TalosLauncher'`. Always follow an install with
288
+ `rm -rf node_modules/@agilant/toga-blox && ln -s ../../../toga-blox-npm node_modules/@agilant/toga-blox`
289
+ then `rm -rf node_modules/.vite`.
290
+ - **Nothing is committed.** Both repos have substantial uncommitted work on
291
+ `features-talos-transferorders`. blox `dist/` is gitignored, so other consumers see none of this
292
+ until blox is published.
293
+
294
+ ## Exact next step
295
+
296
+ > Decide the blox-vs-`@assistant-ui/react` question with the handoff doc's author **before**
297
+ > building any more view code — every remaining item is view work.
298
+ >
299
+ > Then, assuming blox: implement `fromTalosTranscript()` in
300
+ > `toga25-supply/src/utils/talosMessages.ts` with tests, using the shape in
301
+ > `~/Desktop/toga-supply-streaming-chat-handoff.md` §8.11 (polymorphic `content`, active
302
+ > `human`/`ai` only, dedupe by `id`, `logical_message_id` + `active` for revisions, no empty
303
+ > bubbles). It is the prerequisite for terminal reconciliation, for message ids that feedback
304
+ > needs, and for opening an existing thread from the history drawer.
305
+
306
+ ---
307
+ _Saved by /session-save on 2026-09-02_
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.736",
3
+ "version": "1.0.737",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",