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.
- package/knowledge/2.0/apps/talos/INDEX.md +1 -0
- package/knowledge/2.0/apps/talos/architecture.md +23 -0
- package/knowledge/2.0/apps/talos/features/chat-frontend.md +172 -0
- package/knowledge/2.0/apps/toga-blox/features/talos-assistant.md +52 -0
- package/knowledge/2.0/apps/toga25-supply/features/talos-integration.md +103 -2
- package/knowledge/INDEX.md +1 -1
- package/knowledge/registry.json +1 -2
- package/package.json +1 -1
|
@@ -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-
|
|
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
|
|
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).
|
package/knowledge/INDEX.md
CHANGED
|
@@ -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) —
|
|
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)
|
package/knowledge/registry.json
CHANGED
package/package.json
CHANGED