toga-ai 1.0.679 → 1.0.680

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.
@@ -4,6 +4,7 @@
4
4
  |-----|---------|-------|
5
5
  | [TOGa IQ (talos) Architecture](architecture.md) | **TOGa IQ** is TOGA Technology's AI agent platform. | talos/libs/aegra-api/src/aegra_api/main.py, talos/libs/aegra-api/src/aegra_api/settings.py, talos/libs/aegra-api/src/aegra_api/config.py, talos/libs/aegra-api/src/aegra_api/core/tenant_router.py, talos/libs/aegra-api/src/aegra_api/core/control_plane_db.py, talos/libs/aegra-api/src/aegra_api/core/auth_middleware.py, talos/libs/aegra-api/src/aegra_api/services/run_executor.py, talos/libs/aegra-api/src/aegra_api/services/langgraph_service.py, talos/libs/aegra-api/src/aegra_api/services/graph_factory.py, talos/libs/aegra-api/src/aegra_api/services/streaming_service.py, talos/agents/talos_agent/graph.py, talos/docker-compose.yml, talos/deployments/docker/Dockerfile |
6
6
  | [aegra-api — Agent Protocol HTTP + Execution Pipeline](features/aegra-api.md) | `aegra-api` is the **FastAPI Agent Protocol server** at the heart of TOGa IQ. | talos/libs/aegra-api/src/aegra_api/main.py, talos/libs/aegra-api/src/aegra_api/settings.py, talos/libs/aegra-api/src/aegra_api/config.py, talos/libs/aegra-api/src/aegra_api/api/assistants.py, talos/libs/aegra-api/src/aegra_api/api/threads.py, talos/libs/aegra-api/src/aegra_api/api/runs.py, talos/libs/aegra-api/src/aegra_api/api/store.py, talos/libs/aegra-api/src/aegra_api/api/mcp.py, talos/libs/aegra-api/src/aegra_api/api/knowledge_bases.py, talos/libs/aegra-api/src/aegra_api/core/auth_middleware.py, talos/libs/aegra-api/src/aegra_api/core/auth_deps.py, talos/libs/aegra-api/src/aegra_api/core/tenant_router.py, talos/libs/aegra-api/src/aegra_api/core/control_plane_db.py, talos/libs/aegra-api/src/aegra_api/core/redis_manager.py, talos/libs/aegra-api/src/aegra_api/core/encryption.py, talos/libs/aegra-api/src/aegra_api/middleware/content_type_fix.py, talos/libs/aegra-api/src/aegra_api/middleware/rate_limiter.py, talos/libs/aegra-api/src/aegra_api/middleware/logger_middleware.py, talos/libs/aegra-api/src/aegra_api/services/broker.py, talos/libs/aegra-api/src/aegra_api/services/redis_broker.py, talos/libs/aegra-api/src/aegra_api/services/executor.py, talos/libs/aegra-api/src/aegra_api/services/local_executor.py, talos/libs/aegra-api/src/aegra_api/services/worker_executor.py, talos/libs/aegra-api/src/aegra_api/services/run_executor.py, talos/libs/aegra-api/src/aegra_api/services/langgraph_service.py, talos/libs/aegra-api/src/aegra_api/services/graph_factory.py, talos/libs/aegra-api/src/aegra_api/services/graph_streaming.py, talos/libs/aegra-api/src/aegra_api/services/streaming_service.py, talos/libs/aegra-api/src/aegra_api/services/event_store.py, talos/libs/aegra-api/alembic/env.py |
7
+ | [TOGa IQ Chat Frontend (Next.js) — streaming stack & Aegra wiring](features/chat-frontend.md) | The repo **`agilantsolutions/talos`** is the TOGa IQ **chat front-end** — the browser client that talks to the Aegra Agent-Protocol backend. | talos/package.json, talos/next.config.mjs, talos/src/providers/Stream.tsx, talos/src/modules/chat/viewmodel/use-chat-viewmodel.tsx, talos/src/lib/api-url.ts, talos/src/lib/env.ts, talos/src/lib/api/client.ts, talos/src/app/api/[..._path]/route.ts, talos/.env.development, talos/.env.beta, talos/.env.gamma, talos/.env.production |
7
8
  | [Deployment — Docker, Compose, Entrypoint, External PG/Redis](features/deployment.md) | TOGa IQ ships as a **single container** (`aegra` service) wrapping the `aegra-api` FastAPI server. | talos/docker-compose.yml, talos/deployments/docker/Dockerfile, talos/deployments/docker/entrypoint.sh |
8
9
  | [MCP Servers — clickup-mcp and toga-db-mcp](features/mcp-servers.md) | Two internal **FastMCP** servers exposed over **HTTP** with API-key auth and PM2 process management: - **`clickup-mcp`** — ClickUp workspace surface (spaces / f | talos/mcp-servers/clickup-mcp/src, talos/mcp-servers/clickup-mcp/ecosystem.config.js, talos/mcp-servers/clickup-mcp/ecosystem.dev.config.js, talos/mcp-servers/clickup-mcp/pyproject.toml, talos/mcp-servers/clickup-mcp/.env.example, talos/mcp-servers/toga-db-mcp/src, talos/mcp-servers/toga-db-mcp/clusters.yaml, talos/mcp-servers/toga-db-mcp/ecosystem.config.js, talos/mcp-servers/toga-db-mcp/pyproject.toml, talos/mcp-servers/toga-db-mcp/.env.example |
9
10
  | [Observability — Langfuse, OTEL, Prometheus, OneUptime](features/observability.md) | TOGa IQ uses **two complementary tracing planes** plus optional Prometheus metrics and external uptime monitoring: - **Langfuse (native v3 SDK)** — LLM-shaped t | talos/libs/aegra-api/src/aegra_api/observability/__init__.py, talos/libs/aegra-api/src/aegra_api/observability/setup.py, talos/libs/aegra-api/src/aegra_api/observability/base.py, talos/libs/aegra-api/src/aegra_api/observability/langfuse_provider.py, talos/libs/aegra-api/src/aegra_api/observability/langfuse_client.py, talos/libs/aegra-api/src/aegra_api/observability/otel.py, talos/libs/aegra-api/src/aegra_api/observability/metrics.py, talos/libs/aegra-api/src/aegra_api/observability/span_enrichment.py, talos/libs/aegra-api/src/aegra_api/observability/targets |
@@ -45,6 +45,22 @@ dependency** — it is a self-contained Python product that consumes TOGa data
45
45
  read-only via the `toga-db-mcp` server. It lives under `2.0/apps/talos/` because
46
46
  its consumers are 2.0-era TOGa apps (TOGa Hub, TOGa View) and TogaHub auth.
47
47
 
48
+ > **Repo scope — this doc covers the BACKEND ONLY, which is not in the `talos` repo.**
49
+ > Verified 2026-08-28 (`git ls-tree` across all 16 `origin/*` refs): no branch of
50
+ > `agilantsolutions/talos` contains `libs/aegra-api`, `agents/`, `mcp-servers/`, or
51
+ > `docker-compose.yml` — every branch is the chat **front-end**, documented in
52
+ > [features/chat-frontend.md](features/chat-frontend.md). The Aegra backend described below lives
53
+ > in a separate, currently unregistered repo, so the `files:` paths above do not resolve.
54
+
55
+ **Critical rules:** Clients speak the **Agent Protocol** — never invent a custom wire shape; SDK and
56
+ LangGraph-Studio compatibility depend on it. **Tenant provisioning is explicit with no auto-create**,
57
+ so an unregistered `org_id` gets a clean **403 on every call even with a valid token** — provision
58
+ the tenant before debugging the caller. `AUTH_TYPE` ∈ {`togahub`, `entra`, `noop`} and **`noop` must
59
+ never ship to prod**. `toga-db-mcp` is the **only** sanctioned path into TOGa MySQL (read-only,
60
+ `LIMIT` 1–1000, enforced inside the MCP rather than the agent), and the Fernet `MCP_ENCRYPTION_KEY`
61
+ is a single **global** key shared by every tenant — not per-tenant — so treat rotating it as a
62
+ cross-tenant event.
63
+
48
64
  ## Top-level layout
49
65
 
50
66
  ```
@@ -194,4 +210,11 @@ Always use Langfuse's cache-corrected cost (it undercounts cache badly).
194
210
  inside the MCP, not the agent.
195
211
 
196
212
  ## Change history
213
+ - 2026-08-28 — Scoped this doc to the Aegra backend and recorded that it is **not** the `talos` repo
214
+ (verified across all 16 origin refs); the `talos` repo is the chat front-end, now documented in
215
+ `features/chat-frontend.md`. Dropped `"language": "python"` from the `talos` `registry.json` entry
216
+ so the front-end repo stops loading `2.0/standards/python.md`. **Deferred half — once the Aegra
217
+ backend repo's name is known, register it and set `"language": "python"` on THAT entry**; until
218
+ then no repo loads the Python standard. Added a backend-scoped `Critical rules:` line to the
219
+ Summary (the pre-existing one under Talos Pricing Platform covers pricing only). (apeterson)
197
220
  - 2026-06-16 — Initial architecture doc for talos / TOGa IQ under 2.0/apps. (akhokhani)
@@ -0,0 +1,172 @@
1
+ ---
2
+ title: TOGa IQ Chat Frontend (Next.js) — streaming stack & Aegra wiring
3
+ framework: "2.0"
4
+ repo: talos
5
+ project: TOGa IQ
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-08-28
10
+ owners: [apeterson]
11
+ files:
12
+ - talos/package.json
13
+ - talos/next.config.mjs
14
+ - talos/src/providers/Stream.tsx
15
+ - talos/src/modules/chat/viewmodel/use-chat-viewmodel.tsx
16
+ - talos/src/lib/api-url.ts
17
+ - talos/src/lib/env.ts
18
+ - talos/src/lib/api/client.ts
19
+ - talos/src/app/api/[..._path]/route.ts
20
+ - talos/.env.development
21
+ - talos/.env.beta
22
+ - talos/.env.gamma
23
+ - talos/.env.production
24
+ related:
25
+ - ../architecture.md
26
+ - aegra-api.md
27
+ - ../../toga-blox/features/talos-assistant.md
28
+ - ../../toga25-supply/features/talos-integration.md
29
+ ---
30
+
31
+ ## Summary
32
+
33
+ The repo **`agilantsolutions/talos`** is the TOGa IQ **chat front-end** — the browser client that
34
+ talks to the Aegra Agent-Protocol backend. It is the reference implementation of **token-streaming
35
+ chat** in-house, so it is what any other app (toga25-supply, toga2-desk, toga2-commerce) should copy
36
+ when wiring a real Talos backend.
37
+
38
+ This doc exists because the front-end half of TOGa IQ was previously undocumented: anyone told
39
+ *"look at how talos does streaming"* landed on
40
+ [`../architecture.md`](../architecture.md), which describes only the Python/FastAPI backend.
41
+
42
+ ## Repo identity — read this before trusting `architecture.md`
43
+
44
+ **Verified 2026-08-28 (`git ls-tree` across all 16 `origin/*` refs): no branch of
45
+ `agilantsolutions/talos` contains `libs/aegra-api`, `agents/`, `mcp-servers/`, or
46
+ `docker-compose.yml`.** Every branch is a front-end app. The Python/Aegra backend that
47
+ `2.0/apps/talos/architecture.md` documents (and lists under `files: talos/libs/aegra-api/…`) lives
48
+ in a **different repo** that is not yet in `registry.json`.
49
+
50
+ Two live consequences until that is reconciled:
51
+
52
+ - `registry.json` marks `talos` as `"language": "python"`, so `/kickoff` loads
53
+ `2.0/standards/python.md` for a **TypeScript/Next.js** repo — and does *not* load
54
+ `2.0/standards/frontend.md`. Pull the front-end standard in manually when working here.
55
+ - The `files:` list on the architecture doc does not resolve against this repo, so file-based
56
+ knowledge search (`search --file=`) cannot bridge from Aegra code to that doc.
57
+
58
+ ## Two generations of the app live side by side
59
+
60
+ | Branches | Stack | Build / deploy |
61
+ |---|---|---|
62
+ | `_production` (and older: `ai-model-cache-fix`, `previous-version`, `talos-golive`, `version2-api`) | **Vite** SPA (`vite.config.js`, `index.html`) | `buildspec.yml` + `.platform/` → Elastic Beanstalk |
63
+ | `_beta`, `_next`, `TRUE-77539`, `TRUE-77871` | **Next.js 15** App Router, React 19 | `amplify.yml` → Amplify |
64
+
65
+ So the Next.js rewrite described below is **not yet in `_production`** — production still serves the
66
+ older Vite SPA. Check which branch you are reading before drawing conclusions.
67
+
68
+ ## The streaming stack
69
+
70
+ - **`useStream` from `@langchain/langgraph-sdk/react`** — *not* `@langchain/react`. This is the
71
+ LangGraph SDK's own React binding.
72
+ - Wrapped in an **app-owned** context (`StreamProvider` → `StreamSession` →
73
+ `StreamContext.Provider`, consumed via `useStreamContext()`), not the library's provider. That is
74
+ what lets app-specific auth/tenant/thread values be injected.
75
+ - Generative-UI messages come from a second subpath, `@langchain/langgraph-sdk/react-ui`
76
+ (`uiMessageReducer`, `isUIMessage`, `isRemoveUIMessage`), folded into stream state via
77
+ `onCustomEvent`.
78
+
79
+ Versions (`package.json`): `@langchain/core ^1.0.2`, `@langchain/langgraph ^1.0.1`,
80
+ `@langchain/langgraph-sdk ^1.0.0`, `langgraph-nextjs-api-passthrough ^0.0.4`, `next ^15.4.10`,
81
+ `react ^19.0.0`, `nuqs ^2.4.1`.
82
+
83
+ The `useStream` config actually used (`src/providers/Stream.tsx`):
84
+
85
+ ```ts
86
+ useTypedStream({
87
+ apiUrl, // absolute Aegra host — see below
88
+ apiKey: apiKey ?? undefined, // X-Api-Key (LangSmith-style), separate from the bearer
89
+ assistantId, // URL query state, defaults to env.assistantId ("agent")
90
+ threadId: threadId ?? null, // URL query state via nuqs
91
+ fetchStateHistory: true,
92
+ defaultHeaders: bearerToken ? { Authorization: `Bearer ${bearerToken}` } : undefined,
93
+ onCustomEvent: …, // reduces UIMessage events into state.ui
94
+ onThreadId: …, // writes the new threadId to the URL, then refetches the thread list
95
+ });
96
+ ```
97
+
98
+ Two independent credentials ride along: an **API key** (`apiKey`) and a **user bearer token**
99
+ (`defaultHeaders`). The bearer is an **api2** JWT — `NEXT_PUBLIC_AUTH_API_URL` is
100
+ `https://api.togahub.com/v2` (prod/gamma) or `https://api.beta.togahub.com/v2` (dev/beta) — issued
101
+ for the **TogaHub** client, matching Aegra's `AUTH_TYPE=togahub`.
102
+
103
+ **Readiness probe:** `checkGraphStatus()` `fetch`es `${apiUrl}/info` on mount and toasts on failure.
104
+ A cheap, copyable way to derive a real "connected / not connected" state.
105
+
106
+ **`threadId` and `assistantId` live in the URL** via `useQueryState` from **`nuqs`**, which is
107
+ Next-only. A Vite/react-router app must use `useSearchParams` instead — do not lift this part
108
+ verbatim.
109
+
110
+ ## The browser calls Aegra DIRECTLY; the Next.js proxy route is vestigial
111
+
112
+ This is the load-bearing finding for anyone reusing the pattern.
113
+
114
+ `getApiUrl()` returns `env.apiUrl` = `NEXT_PUBLIC_API_URL` = an **absolute external host**
115
+ (`https://api.beta.togaiq.com`), and that same absolute URL is handed to `useStream({ apiUrl })`.
116
+ Nothing routes through `src/app/api/[..._path]/route.ts`. The passthrough route is dead code:
117
+
118
+ - It reads a **server-only** `LANGGRAPH_API_URL`, which **`.env.beta` does not define at all** — the
119
+ route would 500 there.
120
+ - `env.ts`'s own doc comment on `apiUrl` still says *"proxied via /api"*. **That comment is stale**
121
+ and contradicts the value it annotates.
122
+
123
+ **Consequence 1 (enabling):** Aegra already serves CORS to a browser on a different origin.
124
+ A **static SPA with no server route handler** — e.g. toga25-supply on Amplify — *can* call Aegra
125
+ directly. An earlier assumption that a server-side proxy was structurally required is **wrong**.
126
+
127
+ **Consequence 2 (constraining):** the dead proxy pins the request origin:
128
+
129
+ ```ts
130
+ // The backend validates the requesting domain against its Domains table.
131
+ // Always send Origin: http://talos so the backend recognizes us.
132
+ headers["Origin"] = "http://talos";
133
+ ```
134
+
135
+ `Origin` is a **forbidden header** — a browser cannot set it from JS. So if that `Domains`-table
136
+ gate is real, a new consumer origin needs **its own `Domains` row**; it cannot spoof its way in the
137
+ way this server-side route did.
138
+
139
+ ## Environment config — every environment points at BETA
140
+
141
+ Verified 2026-08-28:
142
+
143
+ | File | `NEXT_PUBLIC_API_URL` | `LANGGRAPH_API_URL` (server-only, unused) |
144
+ |---|---|---|
145
+ | `.env.development` | `api.beta.togaiq.com` | `api.beta.togaiq.com` |
146
+ | `.env.beta` | `api.beta.togaiq.com` | **not defined** |
147
+ | `.env.gamma` | `api.beta.togaiq.com` | not defined |
148
+ | `.env.production` | `api.beta.togaiq.com` | `api.beta.togaiq.com` — commented *"Currently not live with new version of ACL"* |
149
+
150
+ **No environment in this repo is configured against a production Aegra host.** Treat "is there a
151
+ live prod Aegra?" as an open question to confirm with the backend owners (`akhokhani`, `jcardinal`)
152
+ rather than an assumption.
153
+
154
+ ## Gotchas
155
+
156
+ - **`@langchain/react` is a different package** from what this app uses. It is a newer wrapper
157
+ (v1.0.33) over the same `@langchain/langgraph-sdk`, and it adds a `@langchain/core ^1.1.48` peer
158
+ plus media hooks/players. Do not assume talos validates it — talos uses the SDK's own
159
+ `/react` subpath.
160
+ - **Do not copy `nuqs`** into a non-Next app (see above).
161
+ - **`env.ts` is the only sanctioned env read** ("never access `process.env` directly elsewhere") and
162
+ `validateEnv()` throws at startup on missing vars. Keep new env reads inside it — but note its
163
+ comments have already drifted from its values once.
164
+
165
+ ## Change history
166
+ - 2026-08-28 — Documented the previously-undocumented TOGa IQ chat front-end: `useStream` from
167
+ `@langchain/langgraph-sdk/react` wrapped in an app-owned context, the browser's **direct
168
+ cross-origin call to Aegra** (proving a static SPA needs no server proxy) and the vestigial
169
+ Next.js passthrough route with its unspoofable pinned `Origin`, the Vite `_production` vs Next.js
170
+ `_beta`/`_next` split, all-environments-point-at-beta env config, and the finding that no `talos`
171
+ branch contains the Python backend that `architecture.md` describes. Research only — no code
172
+ changed. (apeterson)
@@ -25,6 +25,7 @@ related:
25
25
  - ../workflows/local-link-into-a-consumer-app.md
26
26
  - ../../toga25-supply/features/talos-integration.md
27
27
  - ../../ai-bdr/features/landing-chat-drawer.md
28
+ - ../../talos/features/chat-frontend.md
28
29
  ---
29
30
 
30
31
  ## Summary
@@ -140,8 +141,52 @@ Open questions were handed to the UX/UI team.
140
141
  same 40 fail with the Talos changes stashed. Do not read them as a regression from this work.
141
142
  - The drawer variant itself was **uncommitted working tree** at capture (2026-08-27).
142
143
 
144
+ ## The contract cannot express streaming — a real backend REQUIRES a contract change
145
+
146
+ Reviewed 2026-08-28 against `TRUE-80692`. The 08-27 drawer-variant work widened `TalosResponse`
147
+ (added `message`, `suggestions`, `cta`; made `paras` optional), but **every one of those is still a
148
+ field on a single resolved value** — the contract remains **one-shot**:
149
+
150
+ ```ts
151
+ onSend: (text: string) => Promise<TalosResponse> // resolves ONCE, with the finished answer
152
+ ```
153
+
154
+ `TalosMessageModel` / `TalosConversation` are documented "in-memory only". There is **no token
155
+ delta, no `threadId`, no tool-call surface, and no interrupt representation** anywhere in
156
+ `types.ts`.
157
+
158
+ **So token streaming is NOT an adapter-implementation detail.** No adapter, however clever, can
159
+ stream through a `Promise<TalosResponse>` — the promise resolves once. Wiring the live
160
+ Aegra/LangGraph backend (which streams tokens, emits tool calls, and surfaces HITL interrupts —
161
+ see [TOGa IQ chat frontend](../../talos/features/chat-frontend.md)) means changing this component's
162
+ props. That was accepted deliberately rather than shipping a non-streaming v1.
163
+
164
+ **Agreed direction (DECIDED 2026-08-28, NOT yet implemented):** make `TalosPanel` **controlled** —
165
+ the host passes `messages` / `isStreaming` / `onSubmit` / `onStop` — plus an optional
166
+ `connection?: "connecting" | "ready" | "error"` defaulting to `"ready"`.
167
+
168
+ Two properties make this safe for a published package:
169
+
170
+ - **Non-breaking.** `connection` defaults to `"ready"`, so existing consumers and
171
+ `createTalosStubAdapter()` keep working untouched.
172
+ - **Consistent with what is already here.** `open` / `expanded` / `width` are *already* controlled
173
+ props owned by the host; moving `messages` to the host is the same pattern, not a new one.
174
+
175
+ The panel also needs real **"connecting" / "failed to connect"** states: today a thrown `onSend` is
176
+ its *only* failure path, which cannot represent "we never got a token to begin with."
177
+
178
+ **The stream itself does NOT belong in blox.** Per
179
+ [frontend.md §21](../../../standards/frontend.md) (promote only after a 2nd consumer validates the
180
+ contract — Talos streaming is n=1) and **§13(b)** (blox peers do not hoist, so a `@langchain/core`
181
+ peer would force all five consumer apps to install it, and a barrel import can surface that as a
182
+ Rollup *"failed to resolve import"* for a component they never referenced). blox keeps
183
+ `TalosLauncher` / `TalosPanel` as pure UI; the LangChain client stays app-local. blox's unbundled
184
+ `tsc` + `fix-esm-imports` build is also fragile against a dep with subpath exports.
185
+
143
186
  ## Gotchas
144
187
 
188
+ - **`onSend` cannot stream** — see the section above. A live streaming backend needs the
189
+ `TRUE-80692` contract change, not just an adapter.
145
190
  - The real chat backend is a later blox ticket — `onSend` is stubbed here; do not assume live
146
191
  responses from blox alone. **A working implementation exists outside blox and is now wired to
147
192
  this component:** `bdr/src/lib/talosChat.ts` implements the same
@@ -167,6 +212,13 @@ Open questions were handed to the UX/UI team.
167
212
  currently consumes it via a local symlink.
168
213
 
169
214
  ## Change history
215
+ - 2026-08-28 — Review only (no blox code change): established that the `onSend(text) =>
216
+ Promise<TalosResponse>` contract **cannot express token streaming** — the 08-27 `message`/
217
+ `suggestions`/`cta` additions widen the *payload* but it still resolves once — so a live
218
+ streaming backend requires a `TRUE-80692` props change. Decided on a **controlled** `TalosPanel`
219
+ (`messages`/`isStreaming`/`onSubmit`/`onStop`) plus an optional `connection` prop defaulting to
220
+ `"ready"` so the change stays non-breaking. Also decided the LangChain client stays **app-local,
221
+ never a blox peer** (frontend.md §13b/§21). (apeterson)
170
222
  - 2026-08-27 — BUILT the **`drawer` variant** on `TRUE-80692` (uncommitted at capture): scrim +
171
223
  slide-in aside that stays mounted while closed, title bar, greeting-as-first-bubble
172
224
  (`welcome`), quick-reply chips, single-line composer, host `footer` slot, and a `features`
@@ -6,7 +6,7 @@ project: TOGa 2.5 Supply
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-08-20
9
+ updated: 2026-08-28
10
10
  owners: [apeterson]
11
11
  files:
12
12
  - toga25-supply/src/layout/AppLayout/AppLayout.tsx
@@ -16,6 +16,8 @@ files:
16
16
  - toga25-supply/src/assets/talos-owl.png
17
17
  related:
18
18
  - ../../toga-blox/features/talos-assistant.md
19
+ - ../../talos/features/chat-frontend.md
20
+ - ../../../standards/frontend.md
19
21
  ---
20
22
 
21
23
  ## Summary
@@ -29,7 +31,8 @@ state and injects the adapter; the launcher renders in the header, the panel at
29
31
 
30
32
  - **AppLayout owns the state** — `open` / `expanded` / `width` / `dockWidth` — and injects the
31
33
  adapter:
32
- - `onSend`: stub (real backend is a later blox ticket).
34
+ - `onSend`: **still the stub.** The live streaming backend is designed but unbuilt — see
35
+ *Real streaming backend* below.
33
36
  - `onOpenRecord(id)` → navigate `/sales-orders?sales-orders=<id>`.
34
37
  - `onAction("approvals")` → navigate `/sales-orders`.
35
38
  - `onDockWidthChange(px)` → reserve layout width for the docked panel.
@@ -48,5 +51,103 @@ header's right edge sitting **under** the panel, hiding the launcher + avatar. *
48
51
  the reserved dock width (`marginRight` = the `onDockWidthChange` value) to the **outer app
49
52
  wrapper** so the header shifts along with the content.
50
53
 
54
+ ## Real streaming backend — design (DECIDED 2026-08-28, NOT yet implemented)
55
+
56
+ `onSend` is still the stub. The plan below replaces it with the live Aegra/LangGraph backend,
57
+ copying the proven approach in [TOGa IQ's chat front-end](../../talos/features/chat-frontend.md).
58
+ No code has been written yet.
59
+
60
+ ### Package choice: `@langchain/langgraph-sdk/react`, app-local
61
+
62
+ - **Use `useStream` from `@langchain/langgraph-sdk/react`** — the same import talos uses — **not
63
+ `@langchain/react`.** Reasons: one fewer peer (`@langchain/react` additionally needs
64
+ `@langchain/core ^1.1.48`); the path is already proven in-house so
65
+ `talos/src/providers/Stream.tsx` can be lifted nearly verbatim; and `@langchain/react`'s extra
66
+ surface is largely **media hooks/players** that blox has no components to render.
67
+ - **Take only `useStream` for v1.** Add `useToolCalls` when tool activity is actually shown, and the
68
+ headless interrupt helpers when doing human-in-the-loop — *that* is where `@langchain/react` earns
69
+ its keep, so revisit it then, not now.
70
+ - **Write our own context provider** (as talos does) rather than the library's `StreamProvider` —
71
+ supply-specific token / tenant / `threadId` / Aegra URL must be injected.
72
+ - **It stays in supply, never in blox.** Per [frontend.md §21](../../../standards/frontend.md) this
73
+ is n=1, and per §13(b) a blox peer would force `@langchain/core` on all five consumer apps. blox
74
+ keeps `TalosPanel` as pure UI.
75
+
76
+ ### Why a provider at all — three jobs
77
+
78
+ 1. **Singleton.** `useStream` is stateful; two call sites = two independent conversations.
79
+ 2. **Survive route changes.** The panel is docked in `AppLayout` while the user navigates. If the
80
+ stream lived inside the panel, any unmount would kill an in-flight response and lose history — so
81
+ it must sit **above the router outlet**.
82
+ 3. **Be the app↔blox boundary.** Supply's token/tenant/`threadId`/Aegra URL enter here and LangGraph
83
+ message shapes are translated here, keeping `TalosPanel` app-agnostic and reusable by desk and
84
+ commerce later.
85
+
86
+ ### Planned shape
87
+
88
+ ```
89
+ src/api/talos.ts — sole VITE_TALOS_API read chokepoint + token exchange
90
+ src/providers/TalosStreamProvider.tsx — the single useStream call, exposed via context
91
+ src/layout/AppLayout/viewModel/useTalosViewModel.ts — flat shape for the panel
92
+ ```
93
+
94
+ - **The mapper is where the estimate lives.** LangGraph `Message[]` → blox `TalosMessageModel[]` do
95
+ **not** line up: `type: "ai" | "human"` vs `role: "talos" | "user"`; streamed content blocks vs a
96
+ finished `paras: string[]`; and tool calls and interrupts have **no representation at all** in
97
+ blox's model. This is the same gap that forces the `TRUE-80692` contract change.
98
+ - **`threadId` in URL search params via react-router 7 `useSearchParams`** — **not `nuqs`**, which is
99
+ Next-only. Do not copy that part of talos's provider (§5, URL as source of truth).
100
+ - **Token held in React Query** per [frontend.md §3](../../../standards/frontend.md) — fetched server
101
+ state, never Zustand.
102
+ - **Tear down token + `threadId` + conversation on logout and tenant switch** per §18.
103
+
104
+ ### Async auth gating — and the anti-pattern
105
+
106
+ `useStream` takes the token as a **value** (`defaultHeaders`), not a promise, so you **cannot await
107
+ it inside the hook**. That single constraint decides the design:
108
+
109
+ - Awaiting the token inside `onSend` (via `queryClient.ensureQueryData`, reusing the panel's existing
110
+ thinking/error states, lazily fetching on first send) works **only** for the non-streaming one-shot
111
+ contract.
112
+ - For **streaming**, the gate must sit **above** the hook — do not mount the stream until the token
113
+ query resolves. This is exactly why the panel's `connection` prop stops being optional polish and
114
+ becomes required.
115
+
116
+ **Anti-pattern — do not use `useSuspenseQuery`/Suspense for this gate.** It suspends the subtree, so
117
+ the panel **unmounts and remounts** — flashing and dropping whatever the user typed in the composer —
118
+ and in a shared library the thrown promise surfaces in host apps that never opted into a boundary.
119
+ Keep it a plain query.
120
+
121
+ **Open product decision:** mid-stream 401 behavior — silent refresh and resume, vs. surface an error
122
+ and let the user resend. Decide deliberately rather than discovering it in QA.
123
+
124
+ ## Aegra-side blockers — backend config, zero front-end code
125
+
126
+ Each of these masquerades as a front-end bug. **Confirm them with the Aegra owners
127
+ (`akhokhani`, `jcardinal`) BEFORE building the provider** — answer (c) especially decides whether the
128
+ auth/loading layer gets built at all.
129
+
130
+ - **(a) CORS allowlist** must include supply's **per-client hostnames** (nychh, compass, quad, …).
131
+ The symptom is a request that never leaves the browser — no response to debug. Note the browser
132
+ calls Aegra **directly** cross-origin (proven by talos), and a static Amplify SPA has no server
133
+ route handler to proxy through, so this is unavoidable. Related: Aegra also validates the
134
+ requesting domain against a `Domains` table, and `Origin` is a forbidden header a browser cannot
135
+ set — so a new origin needs its **own `Domains` row**.
136
+ - **(b) Tenant provisioning is explicit, with no auto-create.** An unregistered org gets a clean
137
+ **403 on every call even with a valid token** (control-plane DB / `tenant_router` LRU — see
138
+ [talos architecture](../../talos/architecture.md)).
139
+ - **(c) Token audience.** Aegra `AUTH_TYPE=togahub` validates api2 JWTs, but supply's token is issued
140
+ for the **supply** client, not TogaHub. Either Aegra accepts it (trivial) or supply needs a
141
+ handoff — api2 already exposes `/auth/delegator`, `/auth/encrypted`, and
142
+ `/auth/encrypted-user-uuid` for exactly this cross-app identity pass.
143
+
51
144
  ## Change history
145
+ - 2026-08-28 — Design/research only, no code: chose `useStream` from
146
+ `@langchain/langgraph-sdk/react` (not `@langchain/react`), kept **app-local** rather than in blox
147
+ (frontend.md §13b/§21), and designed a route-surviving singleton `TalosStreamProvider` above the
148
+ router outlet plus a LangGraph→blox message mapper. Recorded that the token cannot be awaited
149
+ inside `useStream`, so streaming must gate **above** the hook (and **not** via
150
+ `useSuspenseQuery`, which remounts the panel and drops composer text), and logged three
151
+ Aegra-side blockers (per-client CORS hostnames, explicit tenant provisioning → 403, supply-vs-
152
+ TogaHub token audience) to confirm with the backend owners first. (apeterson)
52
153
  - 2026-08-20 — Wired the shared blox Talos assistant into supply for all clients: AppLayout hosts state + injects the adapter (stub `onSend`; `onOpenRecord`/`onAction` navigate to sales orders; `onDockWidthChange` reserves layout width), launcher in a new header `talosLauncher` slot, panel at app root, owl avatar + Plus Jakarta Sans loaded locally. Fixed the docked panel hiding the header launcher by reserving dock width on the outer wrapper (apeterson).
@@ -26,7 +26,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
26
26
  - **saml** (SAML SSO Gateway) — 5 doc(s) → [2.0/apps/saml/INDEX.md](2.0/apps/saml/INDEX.md)
27
27
  - **toga2-view** (TOGa View Frontend) — 12 doc(s) → [2.0/apps/toga2-view/INDEX.md](2.0/apps/toga2-view/INDEX.md)
28
28
  - **toga2-hub** (TOGa Hub) — 2 doc(s) → [2.0/apps/toga2-hub/INDEX.md](2.0/apps/toga2-hub/INDEX.md)
29
- - **talos** (TOGa IQ) — 7 doc(s) → [2.0/apps/talos/INDEX.md](2.0/apps/talos/INDEX.md)
29
+ - **talos** (TOGa IQ) — 8 doc(s) → [2.0/apps/talos/INDEX.md](2.0/apps/talos/INDEX.md)
30
30
  - **voice-to-voice** (TOGa Voice) — 4 doc(s) → [2.0/apps/voice-to-voice/INDEX.md](2.0/apps/voice-to-voice/INDEX.md)
31
31
  - **ai-bdr** (AI-BDR) — 13 doc(s) → [2.0/apps/ai-bdr/INDEX.md](2.0/apps/ai-bdr/INDEX.md)
32
32
  - **toga2-commerce** (TOGa Commerce) — 20 doc(s) → [2.0/apps/toga2-commerce/INDEX.md](2.0/apps/toga2-commerce/INDEX.md)
@@ -145,8 +145,7 @@
145
145
  "project": "TOGa IQ",
146
146
  "framework": "2.0",
147
147
  "role": "app",
148
- "dependsOn": [],
149
- "language": "python"
148
+ "dependsOn": []
150
149
  },
151
150
  {
152
151
  "repo": "test",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.679",
3
+ "version": "1.0.680",
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",