toga-ai 1.0.973 → 1.0.974

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.
Files changed (64) hide show
  1. package/CLAUDE.md +1 -1
  2. package/knowledge/1.0/apps/tools/features/talos-pricing-ui.md +8 -8
  3. package/knowledge/2.0/apps/_underscore/features/email-send-pipeline.md +2 -2
  4. package/knowledge/2.0/apps/api2/features/nested-relationship-writes.md +3 -3
  5. package/knowledge/2.0/apps/toga-blox/features/talos-assistant.md +5 -5
  6. package/knowledge/2.0/apps/toga-blox/workflows/local-link-into-a-consumer-app.md +3 -3
  7. package/knowledge/2.0/apps/toga25-supply/INDEX.md +1 -1
  8. package/knowledge/2.0/apps/toga25-supply/features/talos-integration.md +40 -142
  9. package/knowledge/2.0/apps/worker2/INDEX.md +1 -0
  10. package/knowledge/2.0/apps/worker2/features/callback-scheduling.md +8 -8
  11. package/knowledge/2.0/apps/worker2/features/clickup-task-fluffer-talos.md +2 -2
  12. package/knowledge/2.0/apps/worker2/features/talos-meeting-notes-integration.md +3 -3
  13. package/knowledge/2.0/apps/worker2/features/talos-pricing-automation.md +7 -7
  14. package/knowledge/2.0/apps/worker2/features/vapi-webhook-handler.md +11 -11
  15. package/knowledge/CONVENTIONS.md +3 -2
  16. package/knowledge/INDEX.md +6 -6
  17. package/knowledge/clients/compass-usa/INDEX.md +1 -0
  18. package/knowledge/clients/compass-usa/profile.md +7 -1
  19. package/knowledge/clients/nychh/INDEX.md +1 -1
  20. package/knowledge/clients/nychh/features/talos-assistant-enablement.md +40 -72
  21. package/knowledge/clients/nychh/profile.md +8 -2
  22. package/knowledge/clients/office-depot/INDEX.md +2 -2
  23. package/knowledge/clients/office-depot/features/odp-tech-support.md +46 -89
  24. package/knowledge/clients/office-depot/profile.md +13 -38
  25. package/knowledge/clients/tow-foundation/INDEX.md +1 -0
  26. package/knowledge/clients/tow-foundation/profile.md +9 -2
  27. package/knowledge/clients/true/INDEX.md +2 -0
  28. package/knowledge/clients/true/profile.md +12 -2
  29. package/knowledge/registry.json +21 -11
  30. package/knowledge/standalone/apps/claude/workflows/mcp-tool-usage.md +4 -4
  31. package/knowledge/standalone/apps/talos-backend/features/deep-agent-harness.md +3 -2
  32. package/knowledge/standalone/apps/talos-backend/features/tenant-configuration.md +3 -2
  33. package/package.json +1 -1
  34. package/knowledge/2.0/apps/ai-bdr/INDEX.md +0 -17
  35. package/knowledge/2.0/apps/ai-bdr/architecture.md +0 -154
  36. package/knowledge/2.0/apps/ai-bdr/features/bdr-web-funnel-plan.md +0 -282
  37. package/knowledge/2.0/apps/ai-bdr/features/call-orchestration.md +0 -207
  38. package/knowledge/2.0/apps/ai-bdr/features/campaign-chat-handoff.md +0 -115
  39. package/knowledge/2.0/apps/ai-bdr/features/landing-call-flow.md +0 -158
  40. package/knowledge/2.0/apps/ai-bdr/features/landing-chat-drawer.md +0 -124
  41. package/knowledge/2.0/apps/ai-bdr/features/live-call-status.md +0 -104
  42. package/knowledge/2.0/apps/ai-bdr/features/security-landing-page.md +0 -146
  43. package/knowledge/2.0/apps/ai-bdr/features/vapi-integration.md +0 -148
  44. package/knowledge/2.0/apps/ai-bdr/features/web-funnel-app.md +0 -305
  45. package/knowledge/2.0/apps/ai-bdr/features/web-funnel-content-model.md +0 -98
  46. package/knowledge/2.0/apps/ai-bdr/workflows/new-campaign-onboarding.md +0 -111
  47. package/knowledge/2.0/apps/ai-bdr/workflows/safe-call-loop-testing.md +0 -86
  48. package/knowledge/2.0/apps/talos/INDEX.md +0 -12
  49. package/knowledge/2.0/apps/talos/architecture.md +0 -140
  50. package/knowledge/2.0/apps/talos/features/aegra-api.md +0 -195
  51. package/knowledge/2.0/apps/talos/features/chat-frontend.md +0 -129
  52. package/knowledge/2.0/apps/talos/features/deployment.md +0 -94
  53. package/knowledge/2.0/apps/talos/features/mcp-servers.md +0 -109
  54. package/knowledge/2.0/apps/talos/features/observability.md +0 -107
  55. package/knowledge/2.0/apps/talos/features/pricing-cogs-model.md +0 -221
  56. package/knowledge/2.0/apps/talos/features/talos-agent.md +0 -167
  57. package/knowledge/2.0/apps/talos-backend/INDEX.md +0 -5
  58. package/knowledge/2.0/apps/talos-backend/workflows/toga-supply-client-onboarding.md +0 -210
  59. package/knowledge/2.0/apps/voice-to-voice/INDEX.md +0 -8
  60. package/knowledge/2.0/apps/voice-to-voice/architecture.md +0 -137
  61. package/knowledge/2.0/apps/voice-to-voice/features/livekit-pipeline.md +0 -120
  62. package/knowledge/2.0/apps/voice-to-voice/features/post-call-lambda.md +0 -146
  63. package/knowledge/2.0/apps/voice-to-voice/features/ticket-api-integration.md +0 -205
  64. package/knowledge/2.0/standards/python.md +0 -109
package/CLAUDE.md CHANGED
@@ -232,7 +232,7 @@ See `package.json` for full details.
232
232
 
233
233
  ## TOGA Technology Claude Harness
234
234
 
235
- Installed from: npm bundle v1.0.174
235
+ Installed from: npm bundle v1.0.768
236
236
 
237
237
  ---
238
238
 
@@ -25,9 +25,9 @@ related:
25
25
  - ./persona-gated-navigation.md
26
26
  - ./mvc-data-access-patterns.md
27
27
  - ../architecture.md
28
- - ../../../2.0/apps/talos/architecture.md
29
- - ../../../2.0/apps/talos/features/pricing-cogs-model.md
30
- - ../../../2.0/apps/worker2/features/talos-pricing-automation.md
28
+ - ../../../../standalone/apps/talos-backend/architecture.md
29
+ - ../../../../2.0/apps/worker2/features/talos-pricing-cogs-model.md
30
+ - ../../../../2.0/apps/worker2/features/talos-pricing-automation.md
31
31
  ---
32
32
 
33
33
  The 1.0 (tools) face of the Talos Pricing Platform — Contracts / Pricing Dashboard / Usage / Pricing Settings pages + the estimate engine, reading the Team DB via `db_team`; open for the contract lifecycle, worker-owned read-only rows, or the fetchOne/PHP 8.5 traps.
@@ -43,7 +43,7 @@ The old Onboarding page could only **INSERT** (hardcoded `status='ONBOARDED'`, n
43
43
  - **A signed contract can never be hard-deleted.** `TalosPricingBands` is `ON DELETE CASCADE`, so deleting the client would destroy the agreed rate schedule and the margin history with it. Signed contracts **churn**; they do not delete.
44
44
  - **Post-signature edits append to `TalosContractAmendments`** rather than mutating the signed terms.
45
45
  - Includes an **Actual-vs-Estimate** comparison with variance chips, a month-by-month history table plus cost/margin chart, and a one-click **"apply recommended org fee"**.
46
- - **Pricing model toggle + contract terms (2026-09-17).** The page now sets `pricingModel` (`PER_USER` / `PER_INTERACTION`), the fixed 1/3/5-year term dropdown (`contractTermMonths`), the per-interaction inputs (`contractedInteractionCount`, voice-only `avgMinutesPerInteraction`, `perInteractionFee`), and the two one-time contract fees (`implementationFee`, `trainingFee`). Model design, term ramp, fee amortization, and the recurring-vs-blended margin split are all in [pricing-cogs-model](../../../2.0/apps/talos/features/pricing-cogs-model.md) — not restated here.
46
+ - **Pricing model toggle + contract terms (2026-09-17).** The page now sets `pricingModel` (`PER_USER` / `PER_INTERACTION`), the fixed 1/3/5-year term dropdown (`contractTermMonths`), the per-interaction inputs (`contractedInteractionCount`, voice-only `avgMinutesPerInteraction`, `perInteractionFee`), and the two one-time contract fees (`implementationFee`, `trainingFee`). Model design, term ramp, fee amortization, and the recurring-vs-blended margin split are all in [pricing-cogs-model](../../../../2.0/apps/worker2/features/talos-pricing-cogs-model.md) — not restated here.
47
47
 
48
48
  ### Pricing Dashboard (`pricing/get.php`) — rebuilt for sales leadership
49
49
  Portfolio KPIs, a 12-month blended-margin trend with the target band shaded, an exception list with deterministic explanations, and licensed-vs-active utilisation.
@@ -59,7 +59,7 @@ Technical-only (persona-narrowed on the action, per `mvc-data-access-patterns`).
59
59
  - `mvc/talos/onboarding/*` and `mvc/talos/factors/*` — superseded above.
60
60
 
61
61
  ### Estimator (`App_Talos_Estimator`, `_/app/talos/estimator.php`)
62
- Now driven by the **token unit price** and **workload profiles** instead of the feature checklist and per-client calibration factor (methodology in `pricing-cogs-model`). Service offering (CHAT / VOICE_TO_VOICE / NATURAL_VOICE) describes **modality only** and says nothing about cost; cost is driven by tool-call **intensity** via the profile (retrieval / general / analytics). Per-interaction model adds `costPerInteraction`, `estimateByInteraction`, `recommendedPricePerInteraction`, `recommendedOrgFeeInteraction`, `orgFeeOptionsInteraction`. Constants live in Team-DB `TalosCostFactors` (shared with worker2); a golden-master parity test (`estimator.parity.test.php` ↔ worker2 `PricingParityTest.php`, 11/11 pass) guards drift — see [pricing-cogs-model](../../../2.0/apps/talos/features/pricing-cogs-model.md).
62
+ Now driven by the **token unit price** and **workload profiles** instead of the feature checklist and per-client calibration factor (methodology in `pricing-cogs-model`). Service offering (CHAT / VOICE_TO_VOICE / NATURAL_VOICE) describes **modality only** and says nothing about cost; cost is driven by tool-call **intensity** via the profile (retrieval / general / analytics). Per-interaction model adds `costPerInteraction`, `estimateByInteraction`, `recommendedPricePerInteraction`, `recommendedOrgFeeInteraction`, `orgFeeOptionsInteraction`. Constants live in Team-DB `TalosCostFactors` (shared with worker2); a golden-master parity test (`estimator.parity.test.php` ↔ worker2 `PricingParityTest.php`, 11/11 pass) guards drift — see [pricing-cogs-model](../../../../2.0/apps/worker2/features/talos-pricing-cogs-model.md).
63
63
 
64
64
  ## Gotchas
65
65
  - **`App_Database::fetchOne()` is not an existence test** — it 500s on zero rows. See [mvc-data-access-patterns](./mvc-data-access-patterns.md); the original `onboarding/post.php` carried this exact bug and would have fataled on the first save anyone attempted, which is good evidence nobody ever completed an onboarding through it.
@@ -80,6 +80,6 @@ Now driven by the **token unit price** and **workload profiles** instead of the
80
80
  - [Persona-gated navigation](./persona-gated-navigation.md)
81
81
  - [Tools MVC — data-access patterns](./mvc-data-access-patterns.md)
82
82
  - [Tools architecture](../architecture.md)
83
- - [Talos architecture (2.0)](../../../2.0/apps/talos/architecture.md)
84
- - [Pricing COGS model (2.0)](../../../2.0/apps/talos/features/pricing-cogs-model.md)
85
- - [Talos pricing automation (worker2)](../../../2.0/apps/worker2/features/talos-pricing-automation.md)
83
+ - [Talos architecture (standalone)](../../../../standalone/apps/talos-backend/architecture.md)
84
+ - [Pricing COGS model (worker2)](../../../../2.0/apps/worker2/features/talos-pricing-cogs-model.md)
85
+ - [Talos pricing automation (worker2)](../../../../2.0/apps/worker2/features/talos-pricing-automation.md)
@@ -18,7 +18,7 @@ related:
18
18
  - email-template-sending.md
19
19
  - string-html-entity-helpers.md
20
20
  - ../../worker2/features/all-client-email-queue-monitor.md
21
- - ../../ai-bdr/features/web-funnel-app.md
21
+ - ../../../../standalone/apps/bdr/features/web-funnel-app.md
22
22
  - ../../../clients/compass-canada/features/french-order-email-localization.md
23
23
  ---
24
24
 
@@ -169,7 +169,7 @@ derive one from the other).
169
169
  be paired with updating BDR's env, or BDR's Send button can only reach its *failed* state (it
170
170
  has no mail-app fallback). Settings BDR mirrors: AWS SES **us-west-2**, port **587**, TLS;
171
171
  `DevTeam@goagilant.com` is a known-good verified sender identity in worker2 config. See
172
- [ai-bdr web-funnel-app.md](../../ai-bdr/features/web-funnel-app.md).
172
+ [ai-bdr web-funnel-app.md](../../../../standalone/apps/bdr/features/web-funnel-app.md).
173
173
 
174
174
  - **⚠ READ-AFTER-WRITE ON THE READER REPLICA LOSES `send()`'S OWN LOG ROW (deterministic).**
175
175
  `_Email::send()` INSERTs the `PENDING` `Logs_[Client].Email` row, then reads `$emailLog->id`
@@ -22,7 +22,7 @@ related:
22
22
  - ../../toga2-supply/features/currency-amount-lines-editor.md
23
23
  - cxml-shipnotice-gateway.md
24
24
  - ../../../clients/aig/features/entitlement-intake.md
25
- - ../../ai-bdr/features/web-funnel-app.md
25
+ - ../../../../standalone/apps/bdr/features/web-funnel-app.md
26
26
  - ../../worker2/features/vapi-webhook-handler.md
27
27
  - ../../../clients/elite/features/netsuite-togasupply-sync.md
28
28
  - ../../dbchanges2/workflows/client-schema-drift-audit.md
@@ -52,7 +52,7 @@ Implemented in `Component/Api/V2/V2.php` (`getForeignKeyValue()` / the nested-wr
52
52
  To **create** a new related record, send its data **without** an identifier (and accept that a new row is written). Never rely on a non-identifier field (a custom field, a name, an email) to de-duplicate — the engine will not use it to match, and you accumulate duplicate empty child rows on every write. If you need to link by a business key (like an employee XID), **resolve that key to a uuid in your caller first** (e.g. a `key → uuid` map), then send `{uuid}`.
53
53
 
54
54
  **UPDATE-path back-reference injection defeats the uuid forced-MATCH (primary-pointer bug).** A **single-key identifier object** (`{uuid: <x>}` and nothing else) is supposed to hit the engine's **forced-MATCH fast-path** (`V2.php:6952`) — link the existing child, never create, and let the caller re-point a parent's forward FK to it. On the **CREATE** path this works: the set-loop (`V2.php:4601–4616`) applies the child object and the parent FK is assigned at `V2.php:4647` with no interference. On the **UPDATE** path it does **not**. When a child model carries a `contactId` FK *back* to the parent (as `ContactPhoneNumbers` → `Contacts` does), the update path **injects a reverse back-reference** (sets the child's `contactId = parent id`) into the nested child object at `V2.php:4985–4996` — added by commit `91d803f` "guard child FK set". That injection turns the single-key object into a **multi-key** object, so it no longer qualifies for forced-MATCH, and it diverts what `getForeignKeyValue()` returns **before** the parent's forward FK is assigned at `V2.php:5047`. Net effect: a `PUT {primaryContactPhoneNumber: {uuid}}` does **not** set `Contacts.primaryContactPhoneNumberId`, and the `PUT` response **echoes `primaryContactPhoneNumber: null`** on the miss.
55
- - **Scope.** Any **forward singular FK** whose child model has a back-FK to the parent is exposed on update — `primaryContactEmailAddress` / `primaryContactAddress` on `Contacts` are likely affected the same way. Root cause of the **BDR web-funnel "phone-less contact is never dialed"** bug: see [web-funnel-app.md](../../ai-bdr/features/web-funnel-app.md) and [vapi-webhook-handler.md](../../worker2/features/vapi-webhook-handler.md).
55
+ - **Scope.** Any **forward singular FK** whose child model has a back-FK to the parent is exposed on update — `primaryContactEmailAddress` / `primaryContactAddress` on `Contacts` are likely affected the same way. Root cause of the **BDR web-funnel "phone-less contact is never dialed"** bug: see [web-funnel-app.md](../../../../standalone/apps/bdr/features/web-funnel-app.md) and [vapi-webhook-handler.md](../../worker2/features/vapi-webhook-handler.md).
56
56
  - **Suggested platform fix (deferred to its own PR/ticket):** gate the `4985–4996` injection to **true reverse / has-many** relations only, so a forward singular-FK child keeps its single-key forced-MATCH; confirm `RecordFields.childPolicy` for `Contacts.primaryContactPhoneNumberId` when fixing. Until then the caller-side workaround is a follow-up identifier-only `PUT` re-link with a self-heal that detects the null/mismatched echo (shipped on the BDR funnel side — see web-funnel-app.md).
57
57
 
58
58
  **⚠ CREATE path: a nested singular FK is matched TENANT-WIDE by VALUE and RE-PARENTED.** The forward-singular-FK back-reference machinery has a second, worse manifestation — on the **CREATE** path, and the damage is a **stolen child row** rather than a null pointer. A nested `primaryContactEmailAddress` / `primaryContactPhoneNumber` object is matched **across the whole tenant by its VALUE** (`emailAddress` / `phoneNumber`) — **not** by `uuid` — and the matched child's back-reference `contactId` is then **flipped to the newest writer**, silently detaching it from its previous owner. **Measured on beta `Client_Aig` 2026-08-17 (TRUE-80562).** Two sequential POSTs each created a new Contact (**854** at 10:19, **856** at 10:41). Afterwards: Contacts **854 AND 856** both have `primaryContactEmailAddressId = 250` and `primaryContactPhoneNumberId = 18`; `ContactEmailAddresses` row **250** has `contactId = 856`; so contact **854 still points at a child row that now belongs to contact 856** — 854's primary email is effectively **detached**. **Singular and collection paths DIVERGE — only the forward singular FK re-parents.** The collection members were **not** stolen: the nested `contactEmailAddresses` entries produced rows **251/252** owned by 854 and **separate new rows 255/256** owned by 856. Same payload, same request — different behavior per path.
@@ -212,5 +212,5 @@ behaviour — it bites any write to a nested child, not one feature. Worked exam
212
212
  - [assetType from NetSuite itemtype](../../../1.0/apps/library/features/netsuite-item-assettype-sync.md)
213
213
  - [Per-currency amount lines editor](../../toga2-supply/features/currency-amount-lines-editor.md)
214
214
  - [cXML ShipNotice Gateway](cxml-shipnotice-gateway.md)
215
- - [BDR web-funnel app](../../ai-bdr/features/web-funnel-app.md) / [VAPI webhook handler](../../worker2/features/vapi-webhook-handler.md)
215
+ - [BDR web-funnel app](../../../../standalone/apps/bdr/features/web-funnel-app.md) / [VAPI webhook handler](../../worker2/features/vapi-webhook-handler.md)
216
216
  - [Client schema-drift audit](../../dbchanges2/workflows/client-schema-drift-audit.md)
@@ -27,8 +27,8 @@ related:
27
27
  - ../workflows/dynamic-publish-pipeline.md
28
28
  - ../workflows/local-link-into-a-consumer-app.md
29
29
  - ../../toga25-supply/features/talos-integration.md
30
- - ../../ai-bdr/features/landing-chat-drawer.md
31
- - ../../talos/features/chat-frontend.md
30
+ - ../../../../standalone/apps/bdr/features/landing-chat-drawer.md
31
+ - ../../../../standalone/apps/talos/features/chat-frontend.md
32
32
  ---
33
33
 
34
34
  Shared pure-UI AI-assistant (launcher + docked/drawer chat panel, adapter-injected, fully tokenized); open for its adapter/controlled contracts, streaming limits, branch divergence, and the per-role message-field gotcha.
@@ -109,7 +109,7 @@ onSend: (text: string) => Promise<TalosResponse> // resolves ONCE, with the fi
109
109
 
110
110
  `TalosMessageModel` / `TalosConversation` are documented "in-memory only". There is **no token delta, no `threadId`, no tool-call surface, and no interrupt representation** anywhere in `types.ts`.
111
111
 
112
- **So token streaming is NOT an adapter-implementation detail.** No adapter, however clever, can stream through a `Promise<TalosResponse>` — the promise resolves once. Wiring the live Aegra/LangGraph backend (which streams tokens, emits tool calls, surfaces HITL interrupts — see [Talos chat frontend](../../talos/features/chat-frontend.md)) means changing this component's props. That was accepted deliberately rather than shipping a non-streaming v1.
112
+ **So token streaming is NOT an adapter-implementation detail.** No adapter, however clever, can stream through a `Promise<TalosResponse>` — the promise resolves once. Wiring the live Aegra/LangGraph backend (which streams tokens, emits tool calls, surfaces HITL interrupts — see [Talos chat frontend](../../../../standalone/apps/talos/features/chat-frontend.md)) means changing this component's props. That was accepted deliberately rather than shipping a non-streaming v1.
113
113
 
114
114
  ### Controlled mode — BUILT 2026-09-01 (the TRUE-80692 contract change)
115
115
 
@@ -185,7 +185,7 @@ So the launcher was **already painted under the backdrop and its clicks were alr
185
185
  - **A `renderMessages`-style slot can silently delete panel-synthesized UI.** The loading bubble is not in `messages`; it was lost exactly this way. Check what the panel mixes into a list before replacing that list.
186
186
  - **`onSend` cannot stream** — use **controlled mode** (above). A live streaming backend needs the controlled props, not a cleverer adapter.
187
187
  - **Tool calls and HITL interrupts still have no representation** in `TalosMessageModel`. A host mapping from LangGraph must drop them. Surfacing them needs new blox props.
188
- - The real chat backend is a later blox ticket — `onSend` is stubbed here; do not assume live responses from blox alone. **A working implementation exists outside blox and is now wired to this component:** `bdr/src/lib/talosChat.ts` implements the same `onSend(text) => Promise<TalosResponse>` seam against the live Talos campaign-chat backend (session token, fingerprint, strict message shaping, offline fallback) and plugs straight in — see [AI-BDR landing chat drawer](../../ai-bdr/features/landing-chat-drawer.md). **The seam held: no bridge/translation layer was needed.** Consider upstreaming it when the adapter ticket is picked up; the only env-ism to change is its Next-specific `process.env.NEXT_PUBLIC_TALOS_API_BASE` default, which becomes a required `baseUrl` option for Vite consumers.
188
+ - The real chat backend is a later blox ticket — `onSend` is stubbed here; do not assume live responses from blox alone. **A working implementation exists outside blox and is now wired to this component:** `bdr/src/lib/talosChat.ts` implements the same `onSend(text) => Promise<TalosResponse>` seam against the live Talos campaign-chat backend (session token, fingerprint, strict message shaping, offline fallback) and plugs straight in — see [AI-BDR landing chat drawer](../../../../standalone/apps/bdr/features/landing-chat-drawer.md). **The seam held: no bridge/translation layer was needed.** Consider upstreaming it when the adapter ticket is picked up; the only env-ism to change is its Next-specific `process.env.NEXT_PUBLIC_TALOS_API_BASE` default, which becomes a required `baseUrl` option for Vite consumers.
189
189
  - **BDR consumes this component today, but only through a local link.** The React `^18` peer cap is not a hard blocker in practice — BDR (React 19.2.4) runs it via a junction-linked checkout with a single React (procedure: [Local-linking blox into a consumer app](../workflows/local-link-into-a-consumer-app.md); framer-motion 11 and Pro FontAwesome behave fine on React 19). **What is still blocked is deployment:** BDR cannot ship until this branch merges and publishes and BDR consumes the npm version.
190
190
  - **Hosts can import the Talos subpath to avoid the barrel's CSS.** `@agilant/toga-blox/dist/components/Talos/index.js` resolves (package `exports` allows `./dist/*`); importing the package barrel pulls **every** blox component's CSS into the consumer bundle.
191
191
  - **⚠ `dist/` must be REBUILT for a blox change to reach any browser — and a green consumer typecheck does NOT prove it did.** The consumer's `tsc` resolves blox **types from source** through the `node_modules` symlink, but Vite loads the package at **runtime from `dist/`**. So `tsc --noEmit` can be perfectly clean while the browser runs stale blox code. Every blox change needs `npm run build` in the blox repo **plus** `rm -rf node_modules/.vite` in the consumer. See [Local-linking blox into a consumer app §4](../workflows/local-link-into-a-consumer-app.md). **Correction:** an earlier note claimed blox `npm run build` fails on a missing `tsconfig.app.json` — it does not; it builds fine.
@@ -198,4 +198,4 @@ So the launcher was **already painted under the backdrop and its clicks were alr
198
198
 
199
199
  - [TableRecordModal](table-record-modal.md) — sets the `data-blox-modal-open` marker the launcher dims on.
200
200
  - [Dynamic npm Publish Pipeline](../workflows/dynamic-publish-pipeline.md) and [Local-linking blox](../workflows/local-link-into-a-consumer-app.md) — how consumers get Talos.
201
- - [toga25-supply Talos integration](../../toga25-supply/features/talos-integration.md), [AI-BDR landing chat drawer](../../ai-bdr/features/landing-chat-drawer.md), [Talos chat frontend](../../talos/features/chat-frontend.md).
201
+ - [toga25-supply Talos integration](../../toga25-supply/features/talos-integration.md), [AI-BDR landing chat drawer](../../../../standalone/apps/bdr/features/landing-chat-drawer.md), [Talos chat frontend](../../../../standalone/apps/talos/features/chat-frontend.md).
@@ -18,7 +18,7 @@ related:
18
18
  - ./dynamic-publish-pipeline.md
19
19
  - ../features/talos-assistant.md
20
20
  - ../architecture.md
21
- - ../../ai-bdr/features/landing-chat-drawer.md
21
+ - ../../../../standalone/apps/bdr/features/landing-chat-drawer.md
22
22
  - ../../toga25-supply/features/talos-integration.md
23
23
  ---
24
24
 
@@ -26,7 +26,7 @@ How to develop an unpublished blox against a real consumer on Windows (incl. a R
26
26
 
27
27
  ## Summary
28
28
 
29
- How to develop a **blox component that is not published yet** against a real consuming app, on Windows, including the hard case: a **React 19 / Next.js (Turbopack)** host consuming a blox whose peers cap React at `^18`. Proven 2026-08-27 wiring blox `Talos` into `ai-bdr` ([landing-chat-drawer.md](../../ai-bdr/features/landing-chat-drawer.md)).
29
+ How to develop a **blox component that is not published yet** against a real consuming app, on Windows, including the hard case: a **React 19 / Next.js (Turbopack)** host consuming a blox whose peers cap React at `^18`. Proven 2026-08-27 wiring blox `Talos` into `ai-bdr` ([landing-chat-drawer.md](../../../../standalone/apps/bdr/features/landing-chat-drawer.md)).
30
30
 
31
31
  Use it for the inner loop only. **It is not a deploy path** — see [Dynamic npm Publish Pipeline](./dynamic-publish-pipeline.md) for how the consumer eventually gets the code.
32
32
 
@@ -204,4 +204,4 @@ So `tsc` can be completely clean while the browser runs **stale** blox code —
204
204
  - [dynamic-publish-pipeline](./dynamic-publish-pipeline.md) — how the consumer gets blox for real (not this dev loop).
205
205
  - [Talos assistant](../features/talos-assistant.md) — the component this loop was proven on.
206
206
  - [architecture](../architecture.md) — library overview.
207
- - [AI-BDR landing chat drawer](../../ai-bdr/features/landing-chat-drawer.md), [toga25-supply Talos integration](../../toga25-supply/features/talos-integration.md).
207
+ - [AI-BDR landing chat drawer](../../../../standalone/apps/bdr/features/landing-chat-drawer.md), [toga25-supply Talos integration](../../toga25-supply/features/talos-integration.md).
@@ -17,7 +17,7 @@
17
17
  | [Side navigation & default route — an empty nav renders a BLANK PAGE and gets reported as "cannot log in"](features/side-navigation-and-default-route.md) | 2.5 nav/routes/landing are 100% backend-driven by the `navigation` surface; a user with zero visible nav elements gets a BLANK page reported as "can't log in" — |
18
18
  | [SSO redirect & public-vs-user session gating (useAuthenticationFlow)](features/sso-redirect-and-session-gating.md) | How 2.5 decides on every navigation whether to bounce an anonymous visitor to their client's SSO IdP vs the local `/login` (useAuthenticationFlow); open for SSO |
19
19
  | [Surface Frontend (DB-driven UI consumption, src/surface/)](features/surface-frontend.md) | The FE consumer of the platform-wide Surface layer (DB-driven UI config via `GET /v2/surfaces/meta`) — hooks, registries, adapter seams, migration recipe, and t |
20
- | [Talos Integration (AppLayout host, live Aegra streaming, LangGraph→blox mapper)](features/talos-integration.md) | toga25-supply hosts the shared blox Talos assistant — AppLayout host, live Aegra/LangGraph streaming in a supply-owned provider, surface-gated per tenant (NYCHH |
20
+ | [Talos Integration in TOGa Supply](features/talos-integration.md) | TOGa Supply embeds a surface-gated blox Talos panel using the current Supply user's bearer; open for assistant discovery, streaming, frontend lifecycle gaps or |
21
21
  | [Toolbar button chrome (ToolbarButton) — and why blox BaseButton can't be resized from the theme](features/toolbar-button-chrome.md) | toga25-supply's own ToolbarButton chrome (pill/chip, both pinned 27px) for page-toolbar buttons, because blox `BaseButton` sizes are un-themeable literals; open |
22
22
  | [Transfer Orders page (TableView → Core surfaces → React page + record modal)](features/transfer-orders-page.md) | The Transfer Orders vertical — list + record modal + Create modal, built surface-first from nothing (NYCHH-only today); the cleanest worked example of the curre |
23
23
  | [AWS Amplify Multi-Environment Deployment](workflows/amplify-deployment.md) | How toga25-supply deploys to all its Amplify environments from one shared `amplify.yml` — branch `_<mode>` → Vite `--mode`; open when adding an environment or d |
@@ -1,187 +1,85 @@
1
1
  ---
2
- title: Talos Integration (AppLayout host, live Aegra streaming, LangGraph→blox mapper)
2
+ title: Talos Integration in TOGa Supply
3
3
  framework: "2.0"
4
4
  repo: toga25-supply
5
5
  project: TOGa 2.5 Supply
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-09-16
10
- owners: [apeterson]
9
+ updated: 2026-10-08
10
+ owners: [apeterson, akhokhani]
11
11
  files:
12
12
  - toga25-supply/src/api/talos.ts
13
13
  - toga25-supply/src/hooks/useTalosSession.ts
14
14
  - toga25-supply/src/hooks/useTalosThreads.ts
15
15
  - toga25-supply/src/hooks/useTalosSurface.ts
16
- - toga25-supply/src/App.tsx
17
16
  - toga25-supply/src/providers/TalosStreamProvider.tsx
18
17
  - toga25-supply/src/utils/talosMessages.ts
19
- - toga25-supply/src/utils/talosMessages.test.ts
20
18
  - toga25-supply/src/layout/AppLayout/AppLayout.tsx
21
- - toga25-supply/src/components/Header/Header.tsx
22
- - toga25-supply/src/index.css
19
+ - toga25-supply/.env.production
23
20
  related:
24
21
  - ../../toga-blox/features/talos-assistant.md
25
- - ../../toga-blox/features/table-record-modal.md
26
- - ./surface-frontend.md
27
22
  - ../../dbchanges2/features/surface-layer-schema.md
28
- - ../../../clients/nychh/features/talos-assistant-enablement.md
29
- - ../../toga-blox/workflows/local-link-into-a-consumer-app.md
30
- - ../../talos/features/chat-frontend.md
31
- - ../../talos/features/aegra-api.md
32
- - ../../../standards/frontend.md
23
+ - ../../../../standalone/apps/talos-backend/workflows/toga-supply-client-onboarding.md
24
+ - ../../../../standalone/apps/talos/features/chat-frontend.md
25
+ - ../../../../clients/nychh/features/talos-assistant-enablement.md
26
+ - ../../../../clients/compass-usa/features/talos-supply-assistant.md
33
27
  ---
34
28
 
35
- toga25-supply hosts the shared blox Talos assistant — AppLayout host, live Aegra/LangGraph streaming in a supply-owned provider, surface-gated per tenant (NYCHH only today); open for Talos wiring, gating, or Aegra debugging.
29
+ TOGa Supply embeds a surface-gated blox Talos panel using the current Supply user's bearer; open for assistant discovery, streaming, frontend lifecycle gaps or tenant enablement.
36
30
 
37
31
  ## Summary
38
32
 
39
- toga25-supply is the first host of the shared blox [Talos assistant](../../toga-blox/features/talos-assistant.md). `AppLayout` is the host: the launcher renders in the header, the panel at app root.
40
-
41
- **⚠ Corrected 2026-09-02 — Talos is NO LONGER enabled for all clients.** It is now gated by the **surface layer**, seeded **OFF for every client**, and opted in per tenant. **NYCHH is the only tenant granted it today.** The suggestion chips come from the same surface, not from blox.
42
-
43
- As of **2026-09-01 the live Aegra/LangGraph streaming backend is BUILT** (TRUE-80693). `onSend` is no longer a stub — the panel runs in blox's new **controlled mode** and the stream lives in a supply-owned provider. All LangChain code is **app-local**; blox stays pure UI.
33
+ `AppLayout` hosts the launcher and controlled panel; one Supply-owned `TalosStreamProvider` wraps LangGraph SDK `useStream`. Blox is UI only. The shared `talos-assistant` Surface is seeded off and granted per tenant; a provisioned backend assistant does not establish that a customer's panel is enabled.
44
34
 
45
35
  ## How it works
46
36
 
47
- ```
48
- src/api/talos.ts — sole VITE_TALOS_API read chokepoint + assistants lookup
49
- src/hooks/useTalosSurface.ts — DB feature gate + suggestion chips (surface bundle)
50
- src/hooks/useTalosSession.ts — resolves token + assistantId → a `connection` state
51
- src/hooks/useTalosThreads.ts — Aegra thread list → blox TalosThreadSummary[] + title polling
52
- src/providers/TalosStreamProvider.tsx — the single useStream() call, exposed via own context
53
- src/utils/talosMessages.ts — LangGraph Message[] → blox TalosMessageModel[]
54
- src/layout/AppLayout/AppLayout.tsx — host: provider wrapper + controlled panel props
55
- ```
56
-
57
- ### `src/api/talos.ts` — the env chokepoint
58
-
59
- The only place `VITE_TALOS_API` is read. Exposes `getTalosApiUrl()`, `isTalosConfigured()`, `fetchTalosAssistants()`, `pickTalosAssistant()`.
60
-
61
- `fetchTalosAssistants()` **logs the HTTP status before throwing** — `401` = token/issuer mismatch, `403` = tenant not provisioned. Without that log React Query reduces both to a silent boolean and you debug blind.
62
-
63
- **Do NOT send a body or a `Content-Type` on the `GET /assistants` call.** `fetch` forbids a body on GET, and a JSON content-type only adds a CORS preflight. (The curl example circulated internally used `--data '{}'`; do not copy it into the browser client.)
64
-
65
- ### `assistantId` is PER-TENANT — fetch it, never pin it in env
66
-
67
- `GET {aegra}/assistants?limit=100&offset=0` with the bearer returns:
68
-
69
- ```jsonc
70
- { "assistants": [ { "assistant_id": "…", "name": "…", "graph_id": "…",
71
- "metadata": { "customer_facing": true, "read_only": true, "tenant_slug": "…" } } ],
72
- "total": 1 }
73
- ```
74
-
75
- NYCHH returns exactly one — name `TOGa Supply`, `graph_id: deep_agent`, `read_only: true`, `customer_facing: true`.
76
-
77
- **Selection rule (`pickTalosAssistant`):** prefer `metadata.customer_facing === true`; else fall back to the full list; take the first; `console.warn` when there is more than one candidate. Never silently pick, never throw.
78
-
79
- **Design consequence:** `useStream` takes **both** the token and `assistantId` as *values*, so there are now **two** async prerequisites that must resolve above the hook. That is why the panel's `connection` prop is load-bearing rather than polish.
80
-
81
- ### `useTalosSession()` — the gate
82
-
83
- Returns `connection: "disabled" | "connecting" | "ready" | "error"` plus the token and assistantId.
84
- - **Gate on `isAuthenticated` (the `"user"` key), NOT on the presence of an accessToken.** blox `fetchPublicToken` leaves a **public** token in storage for logged-out visitors, so "there is a token" is not "there is a user."
85
- - **Key the assistants query by USER UUID, never by the token.** Supply persists the React Query cache to `localStorage` (`supply-chain-query-cache`), so a token in a query key gets written to disk.
86
- - `staleTime: Infinity`, `gcTime: Infinity`, `retry: false`.
87
- - **Anti-pattern — do not gate with `useSuspenseQuery`/Suspense.** It suspends the subtree, so the panel **unmounts and remounts** — flashing and dropping whatever the user typed in the composer. Keep it a plain query and gate on the returned `connection`.
88
-
89
- ### `TalosStreamProvider` — outer gate + inner session
90
-
91
- One `useStream` call from `@langchain/langgraph-sdk/react`, exposed through a supply-owned context. `threadId` lives in the URL via react-router `useSearchParams` (**never `nuqs`** — Next-only).
92
-
93
- It is deliberately **split in two**: an outer gate component and an inner `TalosStreamSession`. Hooks cannot be called conditionally, and the stream must not mount until the token *and* the assistantId are real values — so the gate renders the session only once both resolve.
37
+ ### Source, environment and request boundary
94
38
 
95
- Why a provider at all: `useStream` is stateful (two call sites = two conversations), it must sit **above the router outlet** so navigation does not kill an in-flight response, and it is the app↔blox boundary where LangGraph shapes are translated.
39
+ Use the locally saved `origin/_production` source, not a stale working clone. The October 7 survey inspected Supply commit `bb35181`: React 18.3, Vite 7, LangGraph SDK `^1.10.0` and blox `1.1.4-production.196`. `.env.production` is tracked and sets `VITE_TALOS_API=https://api.togaiq.com`; non-production env files target `https://api.beta.togaiq.com`. There is a production backend.
96
40
 
97
- ### `AppLayout` — split into `AppLayoutInner`
41
+ `src/api/talos.ts` is the only env read chokepoint. Browser requests go directly to Aegra using the current Supply access token and real browser Origin; there is no token exchange or Next proxy. Aegra's TogaHub auth resolves the exact JWT client slug into the Talos control plane; `/v2/users/me` supplies persona-linked AI-model slug grants. The platform MCP receives that same user's bearer and trusted frontend routing context; api2 remains the business-record/field authorization authority.
98
42
 
99
- `AppLayout` now wraps `AppLayoutInner` in `TalosStreamProvider`, **because a component cannot consume a provider it renders**. The panel is driven by the controlled props (`messages` / `isStreaming` / `onSubmit` / `onStop` / `connection`); the adapter now carries only navigation side effects (`onOpenRecord` → `/sales-orders?sales-orders=<id>`, `onAction("approvals")` → `/sales-orders`, `onDockWidthChange` → reserve layout width).
43
+ `useTalosSession` requires `isAuthenticated`, not merely a token (logged-out public tokens exist). Assistant query keys use user UUID, never the token: React Query cache is persisted locally. Query state produces disabled/connecting/ready/error; keep it a non-Suspense query so reconnect does not unmount the composer.
100
44
 
101
- **Maps the app-only `"disabled"` state to `"error"`** when passing `connection` down — blox has no `"disabled"` value.
45
+ `GET /assistants?limit=100&offset=0` discovers the client assistant. Prefer `metadata.customer_facing===true`, otherwise use the list, choose first, and warn on multiple candidates. Never pin an assistant ID in env. GET carries no body or unnecessary JSON Content-Type. HTTP status logs distinguish rejected identity/tenant/entitlement from absent configuration.
102
46
 
103
- ### `useTalosSurface()` — the DB feature gate (2026-09-02)
47
+ ### Surface and provider lifecycle
104
48
 
105
- Reads the Core surface `talos-assistant` (type `DRAWER`, not record-bound) and returns **whether Talos is on for this tenant** plus the suggestion chips. **TWO elements** in the bundle:
49
+ `useTalosSurface` finds a `featureEnabled` element in the `talos-assistant` bundle; missing/loading/failed/no-marker means disabled. Its `suggestion` element owns the whole ordered chip row. `config.items` has label/prompt/icon; prompt falls back to label. Invalid items are skipped, while a visible empty row is permitted.
106
50
 
107
- | Element | Meaning |
108
- |---|---|
109
- | `config.role = 'featureEnabled'`, `config.key = 'feature'` | **master switch** — its `isVisible` turns the launcher, the panel and the Aegra stream on or off |
110
- | `config.role = 'suggestion'`, `config.key = 'suggestions'` | **the whole chip row in ONE element.** `config.items` is an ordered array of `{label, prompt, icon}` (`icon` = a literal FontAwesome name). Its `isVisible` switches the entire row. |
51
+ The outer provider gate waits for enabled Surface, authenticated token and resolved assistant. The inner session owns the single `useStream` above the router outlet, so navigation does not recreate the conversation. Thread ID is URL state via React Router `useSearchParams`, not Next-only nuqs. `talosMessages` maps LangGraph to blox models; tool-call/HITL payloads are not rendered as general user chat.
111
52
 
112
- **⚠ SUPERSEDED: chips were briefly ONE ELEMENT PER CHIP** — four `config.role='suggestion'` elements with `labelMessageId` / `config.prompt` / `iconToken` / `sortOrder` and keys `pendingApproval`, `blockedApprovals`, `pendingFulfillment`, `statusCounts`. **That shape is gone.** There is no `config.prompt` and no per-chip `sortOrder`; **order is array order.** Reason: per-chip elements meant a tenant could only switch on wording **Core** had already seeded, so rephrasing a tenant's own business question needed a Core migration. One element lets a tenant author its own set in a single `CONFIG` override. Costs (labels not translatable; whole sets scope, not chips): [surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md).
53
+ The inspected implementation shows final-answer outcomes and suppresses final-answer content after failed/cancelled runs. It supports thread list/title polling and metadata writes with `If-Match`. That code alone does not satisfy all backend recovery requirements.
113
54
 
114
- **The FE FAILS CLOSED — deliberately, not defensively.** Missing bundle, failed fetch, absent marker element, or *still loading* all resolve to **disabled**. Talos answers questions from a tenant's live supply-chain data through an external AI service, so an unresolved gate must never read as "on".
55
+ ### Backend contract versus inspected gaps
115
56
 
116
- `TalosStreamProvider` gained an **`enabled`** prop off the back of this: a tenant without Talos never even calls `GET /assistants`.
57
+ The intended embedded-chat contract requires explicit server cancellation, persisted `/transcript` reload, run-ID rejoin with replay cursor, meaningful terminal-state handling, feedback and refreshed bearer retry. In the inspected production provider, Stop calls only `stream.stop()`; the dedicated cancellation helper is not wired. The saved Compass handoff also identified missing persisted transcript reload and run-ID rejoin. Treat these as release gaps, not finished behavior.
117
58
 
118
- #### `readItems` is TOLERANT on purpose — one bad item costs one chip
59
+ The backend assistant is `toga-supply` on `deep_agent`, with user-bearer `toga-platform` tools and tenant-specific persona/BLP. Compass and NYCHH backend deployments existed in the dated survey; neither tenant's current production Surface grants were read from Core/Client DB during that survey. See their client pages for exact evidence.
119
60
 
120
- `config.items` is a **hand-edited JSON column that differs per tenant**, so `readItems` **skips a malformed item instead of throwing**. One bad entry in a tenant's `CONFIG` override must cost them **one chip** — not every chip, and not a render crash. **14 tests** cover: `null`, a bare string, a number, a missing `label`, a whitespace-only `label`, a non-string `icon`, `items` absent, `items` not an array, and **array order being the authored order**.
61
+ ### Hosting evidence
121
62
 
122
- 🚨 **The gate the FE cannot defend against:** a tenant `CONFIG` override that omits `"role":"suggestion"` **wipes** it (a `CONFIG` override replaces `config` wholesale — verified at `_underscore Model/Core/Surface.php:839`, no merge). The element then no longer matches, the chips **silently disappear**, and there is no error anywhere. That is a seeding rule, not a parser fix. Seeding side, the marker-element choice, and the prod-cluster procedure: [surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md).
63
+ As of 2026-10-07, production Supply used Amplify app `d3v9s1i7kge4ll` and its CloudFront distribution. `*.beta.togasupply.com` used a different Amplify app/repo, `toga2-supply`, not this production 2.5 source. Confirm hostname→build mapping before reproducing a version-specific failure.
123
64
 
124
- ### `useTalosThreads()` — the history drawer's data (2026-09-02)
125
-
126
- Maps Aegra `AegraThread` rows → blox `TalosThreadSummary[]` (`{ id, title, updated }`); the provider exposes `threads` / `threadsStatus` / `activeThreadId` / `selectThread` and `AppLayout` passes them straight into the panel's data-driven drawer props.
127
-
128
- **Refresh fires on two edges, and both are needed:**
129
- 1. **On `threadId` assignment.** The server creates the thread at the **START** of the first run, so without this the *currently active* chat is missing from its own drawer.
130
- 2. **On the falling edge of a run** (streaming → idle).
131
-
132
- **Opening an existing thread needs NO transcript parser.** `@langchain/langgraph-sdk`'s **`setThreadId`** (`dist/ui/orchestrator.js`, `#fetchHistoryForThread`) **clears the stream and fetches that thread's history itself** when `fetchStateHistory: true`. So selecting a history row is just "set the threadId", and the existing LangGraph→blox mapper handles the rest. This took `fromTalosTranscript()` **off the critical path for history**. It is still needed for the durable message ids that feedback requires — do not delete it.
133
-
134
- **Auto-title polling — `titlePollInterval`.** Aegra names a thread **asynchronously after the first run** and emits **no SSE event**, so the only way to see a title is to ask again. `titlePollInterval` feeds React Query's `refetchInterval`: **4s while any RECENT thread is unnamed, `false` otherwise.** Two deliberate deviations from the handoff doc §8.10:
135
- - The doc says poll *"while the first run is active"*. **The title lands AFTER the run**, so polling only during it usually misses it entirely.
136
- - Added a **2-minute age window** the doc does not specify. A thread whose first message was never text-bearing **never gets named at all**, so "stop when the title arrives" on its own polls every 4s for the whole session.
137
-
138
- 7 tests cover the stop condition, including a server clock running ahead of the client.
139
-
140
- > Server-side, auto-naming is currently **off or broken** for NYCHH's assistant, so this polling has nothing to find yet — see [aegra-api](../../talos/features/aegra-api.md). Supply reads `metadata.thread_name` correctly and cannot fix it.
141
-
142
- ### `talosMessages.ts` — the LangGraph → blox mapper
143
-
144
- - Handles LangGraph's **string-OR-content-blocks** union for `content`.
145
- - Filters to `human` / `ai` only — `system`, `tool` and `remove` are dropped.
146
- - An **empty AI turn becomes `{ thinking: true }`** (the stream opening). An empty human turn is dropped.
147
- - Streamed text goes to **`message`** (light markdown), *not* `paras`: during a stream the text is partial, and splitting it into finished paragraphs reflows the bubble on every token.
148
- - **Deliberate loss:** tool calls and HITL interrupts have **no representation** in `TalosMessageModel` and are dropped. Surfacing them needs new blox props — not a mapper change.
149
-
150
- 13 unit tests in `src/utils/talosMessages.test.ts`.
151
-
152
- ### The `talos` query namespace is EXCLUDED from the persisted cache (2026-09-02)
153
-
154
- `App.tsx`'s `PersistQueryClientProvider` used to persist **every** query to `localStorage`. A `shouldDehydrateQuery` filter now excludes the **`talos`** namespace alongside `surface-meta` / `surface-meta-group`: chat titles and assistant ids are **user/session data that must not outlive the session on disk**. The surface half of the same fix is what makes a Talos grant (or a **revoke**) actually reach a running browser — see [surface-frontend](./surface-frontend.md).
155
-
156
- ### Environment config
157
-
158
- `VITE_TALOS_API` is set to `https://api.beta.togaiq.com` in **7 non-prod env files**. It is left **commented out in `.env` (production)** because no live production Aegra host is confirmed — `isTalosConfigured()` therefore returns false there and `connection` resolves to `"disabled"`.
159
-
160
- ### Package choice: `@langchain/langgraph-sdk/react`, app-local
161
-
162
- - **`useStream` from `@langchain/langgraph-sdk/react`** — the same import talos uses — **not `@langchain/react`.** The decision stands: the path is proven in-house so `talos/src/providers/Stream.tsx` lifts nearly verbatim, and `@langchain/react`'s extra surface is largely **media hooks/players** blox has no components to render.
163
- - **⚠ Correction (verified 2026-09-01): the old "one fewer peer" reason was WRONG.** `@langchain/langgraph-sdk@1.10.0` declares `@langchain/core: ^1.1.48` as a **required (non-optional) peer** itself; `react`/`react-dom` are the optional ones. Symptom when it is missing: Vite/esbuild dep-optimize **hard-fails** with `Could not resolve "@langchain/core/messages"` and the dev server exits. Install `@langchain/core` alongside the SDK.
164
- - **Take only `useStream` for v1.** Add `useToolCalls` when tool activity is actually shown, and the headless interrupt helpers for human-in-the-loop — that is when `@langchain/react` earns its keep.
165
- - **It stays in supply, never in blox.** Per [frontend.md §21](../../../standards/frontend.md) this is n=1, and per §13(b) a blox peer would force `@langchain/core` on all five consumer apps.
65
+ ## Gotchas
166
66
 
167
- **Still open:** mid-stream 401 behavior — silent refresh and resume vs. surface an error and let the user resend. Decide deliberately rather than discovering it in QA.
67
+ - Surface visibility, active `/users/me` slug grant, control-plane tenant, assistant capability assignments and MCP data ACL are independent gates. Do not repair a denied user with an admin/service token.
68
+ - A backend assistant with successful historical runs is not proof of a production panel launch. Read the actual Surface bundle as the intended user.
69
+ - SDK Stop closes browser streaming; it is not a verified server-run cancellation in the inspected implementation.
70
+ - The mapper hides tools/HITL. Keep this customer-facing read-only assistant within its declared boundary; richer decision UI requires deliberate contract work.
71
+ - Stable query keys must not include credentials. Caches and logs can persist long after a token changes.
72
+ - Code comments calling Aegra “TOGa IQ” are legacy naming. Talos is the backend agent platform; TOGa IQ is the separate management UI.
168
73
 
169
- ### Aegra-side status (was "three blockers")
74
+ ## Related
170
75
 
171
- - **(a) CORS — NARROWED, still open.** The old worry that Aegra's `Domains` gate needs a spoofed `Origin` (talos pins `Origin: http://talos` server-side, and `Origin` is a forbidden header) is **moot for supply**: supply's real browser origin *is* the registered hostname, so the browser sets it correctly for free. What **is** still a blocker: **Aegra returns CORS headers on the OPTIONS preflight only, not on the actual response** — verified 2026-09-01 from both `http://nychh.dev.sandbox.togasupply:5173` and `https://nychh.togasupply.com` (`access-control-allow-origin: *` on the preflight, **no** `access-control-*` header on the real GET). The browser will block every real response until this is fixed. Reported to `akhokhani`.
172
- - **(b) Tenant provisioning is explicit, no auto-create.** Unchanged — an unregistered org gets a clean **403 on every call even with a valid token**.
173
- - **(c) Token audience — RESOLVED, and it is the good outcome.** Aegra `AUTH_TYPE=togahub` **accepts a supply-issued api2 JWT.** Proven by a working `GET /assistants` whose bearer carried `aud` = a **supply** hostname (`https://nychh.togasupply.com`), `iss`/`sub` = `http://api.beta.togahub.com`, `id.client.slug: Nychh`, 3600s life. Answer from `akhokhani`. **Consequence: no token exchange, no `/auth/delegator` handoff, no auth-exchange layer.** Supply passes the token it already has straight into `defaultHeaders`.
76
+ - [talos assistant](../../toga-blox/features/talos-assistant.md)
77
+ - [surface layer schema](../../dbchanges2/features/surface-layer-schema.md)
78
+ - [toga supply client onboarding](../../../../standalone/apps/talos-backend/workflows/toga-supply-client-onboarding.md)
79
+ - [chat frontend](../../../../standalone/apps/talos/features/chat-frontend.md)
80
+ - [talos assistant enablement](../../../../clients/nychh/features/talos-assistant-enablement.md)
81
+ - [talos supply assistant](../../../../clients/compass-usa/features/talos-supply-assistant.md)
174
82
 
175
- ## Gotchas
83
+ ## Change history
176
84
 
177
- - **React Query sets `isError` on a FAILED BACKGROUND REFETCH while KEEPING the data.** This printed **"Couldn't load your chats." over a full, usable chat list.** The status line was `isError ? "error" : …`, so **one** failed 4s title poll flipped a good list into an error state. **Fix:** a pure `threadsStatusFrom` helper that returns `"error"` **only when there is no data to show**. Pair it with the blox-side fix (drawer notes gated on an empty row list) — both halves were needed. Any hook with a `refetchInterval` inherits this trap: `isError` is not "the data is unusable", it is "the last fetch failed".
178
- - **user turns use `text`, Talos turns use `message`/`paras`.** blox `TalosMessage` renders **`message.text` for a `role: "user"` turn** but **`message.message` / `message.paras` for a `role: "talos"` turn**. The mapper originally put user turns in `message`, which renders an **empty bubble**. Caught only by writing the tests.
179
- ```ts
180
- // CORRECT
181
- { role: "user", text } // user turn
182
- { role: "talos", message } // assistant turn
183
- ```
184
- Anyone writing to `TalosMessageModel` by hand will hit this.
185
- - **The launcher greys out under a modal via a ROOT ATTRIBUTE, and keyboard access is still open.** blox `TableRecordModal` sets **`data-blox-modal-open` on `document.documentElement`** while open, and blox dims the launcher under `:root[data-blox-modal-open]`. It applies to **every** `TableRecordModal` in supply automatically — do not add a per-page dim. Why an attribute and not a prop: the launcher renders in the **header**, outside the modal's subtree, so no shared React state reaches it (same boundary-crossing trick as `--talos-dock-width`). Mechanism + the counter it needs: [TableRecordModal](../../toga-blox/features/table-record-modal.md). 🚨 **Known gap, unfixed:** the launcher is still **keyboard-reachable** behind a modal. `pointer-events: none` stops the mouse only. Supply's header receives `inert` for the **side nav** but **not** for modals — closing this is a **supply** change that affects every header control, so it was flagged rather than patched.
186
- - **Reserve dock width on the OUTER wrapper, not just `<main>`.** The docked panel is `position: fixed`, full-height on the right. The blox `Header` is in **normal document flow** (NOT fixed). Reserving the dock width only on `<main>` leaves the header's right edge sitting **under** the panel, hiding the launcher + avatar. **Fix:** apply the reserved dock width (`marginRight` = the `onDockWidthChange` value) to the **outer app wrapper**.
187
- - **`npm install` silently un-links blox and Talos disappears.** Any `npm install` in this repo replaces the `node_modules/@agilant/toga-blox` **symlink** with the registry version, which contains no Talos. The symptom looks nothing like a dependency problem. Full symptom + relink recipe: [Local-linking blox into a consumer app §1a](../../toga-blox/workflows/local-link-into-a-consumer-app.md).
85
+ - 2026-10-08 — Reconciled local source and dated 2026-10-07 research into the canonical KB (akhokhani).
@@ -60,6 +60,7 @@
60
60
  | [Stranded WorkerJobs (deploy-window job loss) and how to repair them](features/stranded-worker-jobs.md) | Jobs queued while worker2 restarts are dropped silently with no retry and no alert; open when a cron "never ran" or a webhook vanished. |
61
61
  | [Talos Meeting-Notes Integration & Token Auto-Refresh (consumer)](features/talos-meeting-notes-integration.md) | Consumer side of Talos: the endpoints, DevCore assistant, and JWT auto-refresh a worker2 dev tool needs to query the meeting-notes corpus; open when a tool must |
62
62
  | [Talos Pricing Automation (worker2 Cron — Usage Import, AWS Actuals, Margins, Profiles)](features/talos-pricing-automation.md) | Monthly worker2 crons that turn raw usage + AWS cost into a measured platform unit price, per-client cost, and margins; open for the pricing pipeline, its AWS c |
63
+ | [Pricing & COGS Model (Talos Pricing Calculator)](features/talos-pricing-cogs-model.md) | Cost basis + pricing methodology for selling Talos chat and voice; open to re-price Talos or understand COGS drivers, token rates, and the band/org-fee model. |
63
64
  | [Talos Transcript Ingestion Pipeline (worker2 → AWS Bedrock KBs)](features/talos-transcript-ingestion.md) | `_Worker_Team_Transcripts` cron pipeline ingesting Teams VTT transcripts into Talos Bedrock KBs + emailing organizer recaps; open for Export/Process/Retry/SyncK |
64
65
  | [Team Sprint Management & Reporting](features/team-sprint-management.md) | `_Worker_Team_Sprint` engine for TOGA's biweekly dev-sprint process + reporting (warnings, lock, daily dashboard, sprint-end scoring, AI release notes, 7 PM In |
65
66
  | [Teams Meeting Transcript Export](features/teams-transcript-export.md) | How `Team/Transcripts/Export` polls Microsoft Graph for Teams transcripts; open for the Graph organizer-resolution, GUID-vs-UPN, and Entra-permission details (t |
@@ -14,9 +14,9 @@ files:
14
14
  - worker2/Controller/Index.php
15
15
  related:
16
16
  - vapi-webhook-handler.md
17
- - ../../ai-bdr/features/vapi-integration.md
18
- - ../../ai-bdr/features/call-orchestration.md
19
- - ../../ai-bdr/workflows/safe-call-loop-testing.md
17
+ - ../../../../standalone/apps/ai-bdr/features/vapi-integration.md
18
+ - ../../../../standalone/apps/ai-bdr/features/call-orchestration.md
19
+ - ../../../../standalone/apps/ai-bdr/workflows/safe-call-loop-testing.md
20
20
  ---
21
21
 
22
22
  The AI-BDR callback path (prospect says "call me back" → dialer redials) and its production defects; open when working on Vapi callback capture/parse/schedule or the callback root cause.
@@ -43,14 +43,14 @@ Callback path spans the webhook (`Worker/Vapi.php` — capture, persist, parse,
43
43
 
44
44
  ### End to end
45
45
 
46
- 1. **Capture.** Requested time arrives at `message.artifact.structuredOutputs.<schema-uuid>.result.callbackTime` (`Worker/Vapi.php:99` → `:109–122` → `:144`) — **not** in `analysis`, which is always empty. The published schema documents ISO 8601 ([vapi-integration.md](../../ai-bdr/features/vapi-integration.md)), but in practice the field is **unconstrained free text**; the file's own sample payload contains `"callbackTime": "later"` (`Worker/Vapi.php:45`).
46
+ 1. **Capture.** Requested time arrives at `message.artifact.structuredOutputs.<schema-uuid>.result.callbackTime` (`Worker/Vapi.php:99` → `:109–122` → `:144`) — **not** in `analysis`, which is always empty. The published schema documents ISO 8601 ([vapi-integration.md](../../../../standalone/apps/ai-bdr/features/vapi-integration.md)), but in practice the field is **unconstrained free text**; the file's own sample payload contains `"callbackTime": "later"` (`Worker/Vapi.php:45`).
47
47
  2. **Persist (raw).** `updateContactAttempt` stores the string with **no conversion** (`:213–215`) alongside `c_callOutcome='CALLBACK_SCHEDULED'` / `c_actionToTake='SCHEDULE_CALLBACK'` (map at `:161–173`, applied `:189–194`), via `PUT /contact-attempts/{uuid}` (`:256–266`). `c_callbackTime` is `VARCHAR(255)` (dbchanges2 `_modules/Ai/Bdr/2026-03-04 bdrCustomfields.sql:84`), so a malformed value **survives persistence intact** and only dies at the act step.
48
48
  3. **Act.** `executePostCallActions` dispatches `SCHEDULE_CALLBACK` → `actionScheduleCallback`, which resolves the campaign timezone (`:400`), calls `self::convertDateTime($callbackTime, …)` (`:401`), and on success writes `Contacts.dtNextContactRequested` (`:414`) and `contactCallTypeId = CALLBACK` (`:417`) via `PUT /contacts/{uuid}` (`:420–430`).
49
49
  4. **Redial.** The dialer treats a due `dtNextContactRequested` as the privileged path: it **bypasses call windows, weekly cadence, call-window exception dates and prior DNC** (`Ai/Bdr/Vapi.php:238–303`, `:314–331`). Callbacks also sort first in the per-run queue (`:334`).
50
50
 
51
51
  ### Recommended fix direction (decided 2026-07-30 — priority order)
52
52
 
53
- 1. **DURABLE FIX — Vapi assistant config side.** Constrain `callbackTime` in the structured-output schema to **strict ISO 8601 with offset**, and prompt the assistant to resolve relative speech to an absolute timestamp before emitting. ISO parses cleanly today, so this removes the whole class of failure. See [vapi-integration.md](../../ai-bdr/features/vapi-integration.md).
53
+ 1. **DURABLE FIX — Vapi assistant config side.** Constrain `callbackTime` in the structured-output schema to **strict ISO 8601 with offset**, and prompt the assistant to resolve relative speech to an absolute timestamp before emitting. ISO parses cleanly today, so this removes the whole class of failure. See [vapi-integration.md](../../../../standalone/apps/ai-bdr/features/vapi-integration.md).
54
54
  2. **`Worker/Vapi.php:71–79` and `:135`** — add `if (($message->type ?? '') !== 'end-of-call-report') return 'ok';` and stop force-stamping `dtEnded` from `NOW()` (set it only from a real `endedAt`). Independent of the callback bug and arguably **more urgent** — see the concurrency gotcha in [vapi-webhook-handler.md](vapi-webhook-handler.md).
55
55
  3. **`:609–635` and `:403–406`** — defensive normalization (strip leading `in `/`at `/`around `/`sometime `, map spelled-out numbers) and, on failure, **schedule a flagged sane default rather than `return`** — never silently drop a promised callback.
56
56
  4. **`:401–414`** — clamp the parsed time: if `<= now`, push to the next valid slot; snap into the campaign's call windows; reject beyond ~90 days.
@@ -174,6 +174,6 @@ A prospect who asks for a callback **on their 3rd attempt** gets `dtNextContactR
174
174
  ## Related
175
175
 
176
176
  - [vapi-webhook-handler](vapi-webhook-handler.md)
177
- - [vapi-integration](../../ai-bdr/features/vapi-integration.md)
178
- - [call-orchestration](../../ai-bdr/features/call-orchestration.md)
179
- - [safe-call-loop-testing](../../ai-bdr/workflows/safe-call-loop-testing.md)
177
+ - [vapi-integration](../../../../standalone/apps/ai-bdr/features/vapi-integration.md)
178
+ - [call-orchestration](../../../../standalone/apps/ai-bdr/features/call-orchestration.md)
179
+ - [safe-call-loop-testing](../../../../standalone/apps/ai-bdr/workflows/safe-call-loop-testing.md)
@@ -20,7 +20,7 @@ related:
20
20
  - ./talos-transcript-ingestion.md
21
21
  - ./creating-worker-actions.md
22
22
  - ../architecture.md
23
- - ../../talos/features/talos-agent.md
23
+ - ../../../../standalone/apps/talos-backend/features/talos-agent.md
24
24
  ---
25
25
 
26
26
  Async worker that "fluffs" a new ClickUp task by running its name+description through the Talos `dev-core` KB agent and writing the report back; open for the fluffer flow, the distinct Talos agent endpoint/key, or the open prod 403.
@@ -99,4 +99,4 @@ Both are separate from the existing `[talos]` `api_key`/`endpoint` (used by Tran
99
99
  - [Talos transcript ingestion](./talos-transcript-ingestion.md)
100
100
  - [Creating Worker Actions](./creating-worker-actions.md)
101
101
  - [Worker (worker2) Architecture](../architecture.md)
102
- - [Talos agent](../../talos/features/talos-agent.md)
102
+ - [Talos agent](../../../../standalone/apps/talos-backend/features/talos-agent.md)
@@ -11,7 +11,7 @@ owners: [dfranks]
11
11
  files:
12
12
  - .claude/skills/plan-ticket/scripts/talos.js
13
13
  related:
14
- - ../../talos/architecture.md
14
+ - ../../../../standalone/apps/talos-backend/architecture.md
15
15
  - ./clickup-richtext-api.md
16
16
  - ../workflows/ticket-to-pseudocode-planning.md
17
17
  ---
@@ -20,7 +20,7 @@ Consumer side of Talos: the endpoints, DevCore assistant, and JWT auto-refresh a
20
20
 
21
21
  ## Summary
22
22
 
23
- How a **dev tool / agent consumes Talos** to query the team meeting-notes corpus programmatically. Talos itself (the LangGraph Agent Protocol server) is documented under [`2.0/apps/talos/`](../../talos/architecture.md); this doc is the **consumer side**.
23
+ How a **dev tool / agent consumes Talos** to query the team meeting-notes corpus programmatically. Talos itself (the LangGraph Agent Protocol server) is documented under [`standalone/apps/talos-backend/architecture.md`](../../../../standalone/apps/talos-backend/architecture.md); this doc is the **consumer side**.
24
24
 
25
25
  **Key distinction:** the `[talos]` key in worker2 config only reaches the **stateless** `/api/ai/generate` and `/api/ai/chat` endpoints, which have **no meeting-notes access**. Meeting-notes access comes only from **assistants wired to knowledge bases**, reached through the Agent Protocol `/threads` + `/threads/{id}/runs/wait` surface with a **user Bearer JWT**.
26
26
 
@@ -81,6 +81,6 @@ The consumer tool stores its token pair at **`~/.talos/credentials.json`** (user
81
81
 
82
82
  ## Related
83
83
 
84
- - [Talos architecture](../../talos/architecture.md)
84
+ - [Talos architecture](../../../../standalone/apps/talos-backend/architecture.md)
85
85
  - [ClickUp rich-text API](./clickup-richtext-api.md)
86
86
  - [Ticket-to-pseudocode planning workflow](../workflows/ticket-to-pseudocode-planning.md)
@@ -21,9 +21,9 @@ files:
21
21
  related:
22
22
  - ./creating-worker-actions.md
23
23
  - ../architecture.md
24
- - ../../talos/architecture.md
25
- - ../../talos/features/pricing-cogs-model.md
26
- - ../../../1.0/apps/tools/features/talos-pricing-ui.md
24
+ - ../../../../standalone/apps/talos-backend/architecture.md
25
+ - talos-pricing-cogs-model.md
26
+ - ../../../../1.0/apps/tools/features/talos-pricing-ui.md
27
27
  ---
28
28
 
29
29
  Monthly worker2 crons that turn raw usage + AWS cost into a measured platform unit price, per-client cost, and margins; open for the pricing pipeline, its AWS cost query, or the `Talos*` Team-DB schema.
@@ -64,7 +64,7 @@ Untagged spend is attributed to the **defaultTenant**, and every row records `is
64
64
 
65
65
  Computes `clientMonthlyCost = client tokens × unitCostPer1kTokens`, margin vs. the client's band, the consecutive out-of-band streak, and emits a `TalosFeeRecommendations` row only once the streak ≥ the client's `consecutiveMonths`. **Internal tenants are excluded from fee recommendations** (see below). The old per-client "calibration factor" is gone.
66
66
 
67
- **Per-interaction clients (`pricingModel = PER_INTERACTION`, 2026-09-17):** revenue = `orgFee + interactions × perInteractionFee` (no bands); everything else — the streak, the org-fee recommendation — is identical. This **recurring** margin is the only input to the recommendation/streak. The **blended** margin (`blendedMarginPct`) additionally includes `amortizedFeeMonthly` (the amortized contract fees) and is written for **display only** — never fed to the streak. Design/rationale in [pricing-cogs-model](../../talos/features/pricing-cogs-model.md).
67
+ **Per-interaction clients (`pricingModel = PER_INTERACTION`, 2026-09-17):** revenue = `orgFee + interactions × perInteractionFee` (no bands); everything else — the streak, the org-fee recommendation — is identical. This **recurring** margin is the only input to the recommendation/streak. The **blended** margin (`blendedMarginPct`) additionally includes `amortizedFeeMonthly` (the amortized contract fees) and is written for **display only** — never fed to the streak. Design/rationale in [pricing-cogs-model](talos-pricing-cogs-model.md).
68
68
 
69
69
  ### 4. RecomputeProfiles (09:00)
70
70
 
@@ -111,6 +111,6 @@ The existing `[talos]` config group gains **`usage_url`** and **`usage_api_key`*
111
111
 
112
112
  - [Creating worker actions](./creating-worker-actions.md)
113
113
  - [Worker (worker2) architecture](../architecture.md)
114
- - [Talos architecture](../../talos/architecture.md)
115
- - [Talos pricing COGS model](../../talos/features/pricing-cogs-model.md)
116
- - [Talos pricing UI (tools)](../../../1.0/apps/tools/features/talos-pricing-ui.md)
114
+ - [Talos architecture](../../../../standalone/apps/talos-backend/architecture.md)
115
+ - [Talos pricing COGS model](talos-pricing-cogs-model.md)
116
+ - [Talos pricing UI (tools)](../../../../1.0/apps/tools/features/talos-pricing-ui.md)