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