638 Commits

Author SHA1 Message Date
Maxim 563665e03a feat(reskinnable-demo): add logistics identity, theme, nav, and layout chrome
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-04 01:56:12 +02:00
Maxim e25ff08c76 fix(reskinnable-demo): allow-list PATCH fields so pricing inputs cannot bypass the authority gate
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-04 01:56:12 +02:00
Maxim bcdaa51f55 feat(reskinnable-demo): add logistics REST routes with server-enforced authority gate
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-04 01:56:11 +02:00
Maxim b540b5db27 fix(reskinnable-demo): add same-destination alternate lanes so reroute is offerable
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-04 01:56:11 +02:00
Maxim dfe1ef259d feat(reskinnable-demo): add logistics role-based authority gate 2026-08-04 01:56:11 +02:00
Maxim c670247de6 feat(reskinnable-demo): compute logistics mitigation options 2026-08-04 01:56:10 +02:00
Maxim 8c7f983125 feat(reskinnable-demo): add logistics escalation code catalogue 2026-08-04 01:56:10 +02:00
Maxim 7e70670fc7 feat(reskinnable-demo): add logistics domain types, seed, and store
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-04 01:56:10 +02:00
Maxim 731c23d595 feat(reskinnable-demo): wire and register the keel skin
The agent (grounding rule first and most emphatic, temperature 0 for
deterministic routing, two server tools), the OGUI design brief, the
suggestion pills, tools.tsx, and skin.tsx -- registered in both
registries under the id 'keel'. defaultSkinId is unchanged.

tools.tsx is the join point where corpus, run engine, and chat components
meet. Two details are load-bearing: agent-context readables memoize on
summaryKey (never on runs) so the 900ms ticker cannot thrash the agent's
context, and showSources takes only (docId, sectionId) pairs and resolves
the ref/heading/snippet from the real corpus client-side -- so a citation
the model invents fails to resolve and is dropped rather than rendering
as a convincing fake.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3
2026-08-03 01:44:47 +02:00
Maxim b3404d490b feat(reskinnable-demo): add keel chat components and canvas surface
The five in-chat cards plus the a2ui catalog, ops-report builder, canvas
surface, and OGUI sandbox functions.

Every interactive card carries pointer-events-auto: CopilotKit paints
useComponent renders with pointer-events:none, so without it the cards
render perfectly and are completely unclickable.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3
2026-08-03 01:44:46 +02:00
Maxim 4e58f25b76 feat(reskinnable-demo): add keel chrome, identity, and pages
App-shell chrome with the persona switcher (which gates what is
approvable), the RuntimeProviders/useRuntimeProperties/identifyUser triad
for per-persona memory scoping, and the six pages. Two routes are
parameterized -- knowledge/<docId> and runs/<runId> -- so resolvePage
destructures the segment array rather than using a flat lookup. An
unknown id renders a not-found body, never a 404: the route is
structurally valid and a citation deep-link must not break on a rename.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3
2026-08-03 01:44:46 +02:00
Maxim 8eb9fcbb2b feat(reskinnable-demo): add keel playbooks, run engine, and ticker
Four playbooks whose every step carries a policyRef into the corpus, four
seeded runs, and a pure reducer driving them: steps advance on a 900ms
ticker and halt at approval gates keyed to the current persona's role.

seed.test.ts asserts all 23 policyRefs resolve to a real doc + section.
That invariant is what fuses the knowledge and process substrates -- a
dangling ref means an approval card cites a document that does not exist.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3
2026-08-03 01:44:45 +02:00
Maxim 95095bea54 feat(reskinnable-demo): add keel knowledge corpus and deterministic retrieval
Nine Harbor Point Health policy documents across three spaces, and a
pure lexical scorer over them (heading 3 / title 2 / body 1, stopword +
synonym expansion, total-order tie-break). Server-safe: imported by both
the agent's search tool and the client Knowledge pages. Determinism is a
requirement, not a nicety -- the same question returns the same citations
every run, which is what makes the demo reproducible.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3
2026-08-03 01:44:45 +02:00
Maxim e3efa79d0a feat(reskinnable-demo): add keel skin contracts, theme, and shared atoms
The frozen interfaces every other part of the keel skin compiles against:
knowledge + process types, the four demo personas, the role context, the
two shared presentational atoms, plus brand identity, nav, and the
.theme-keel token block (deep pine on warm neutrals, tightest radius in
the app, --brand-violet carrying the amber awaiting-approval accent).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3
2026-08-03 01:44:44 +02:00
github-actions[bot] 04b445a04d style: auto-fix formatting 2026-07-31 18:58:10 +00:00
Maxim a40427a6f3 docs(reskinnable-demo): state the cardsRef sync trade-off honestly
The previous comment presented the effect-based ref sync as merely avoiding
the react-hooks/refs lint rule. It is a trade-off: the render reads
cardsRef.current, and an effect writes after commit, so a card mutation
landing while the PIN card is open can leave the picker one render behind.
Records why that is accepted and what fixing it properly would require.
2026-07-31 20:55:38 +02:00
Maxim 08e10deb59 Merge branch 'blitz/banking-genui-replay/report' into blitz/reskinnable-demo-port/integration 2026-07-31 20:51:24 +02:00
Maxim 52461637ff feat(reskinnable-demo): port PIN, charges-confirm and note polish to the banking skin
Replays three generative-UI changes from the banking showcase (#6259) into
the reskinnable demo's banking skin:

- setCardPin keys its collapsed render on the tool result and remembers
  answered calls in a module-scope map, so a resolved PIN change survives
  re-render. Drops the [cards] dependency in favour of a ref, which is what
  stopped the tool tearing down mid-flight.
- showCharges becomes human-in-the-loop: it asks before moving the user's
  whole screen, and routes through the skin-scoped base prefix.
- Notes about reported charges are prefixed with a red alert emoji.
2026-07-31 20:49:05 +02:00
Maxim 3a2ea211a0 feat(reskinnable-demo): curate the banking report charts to the final trio
Replays the report chart curation from the banking showcase (#6259, #6263)
into the reskinnable demo's banking skin. The three columns become spend
breakdown, spend over time and largest charges — the last of which answers
"which line items drive the total", a question a three-team aggregate
cannot. Additions carry a policyId so their bars colour by owning team.
2026-07-31 20:46:45 +02:00
Maxim e5b54d81b8 Merge branch 'blitz/banking-genui-replay/seed-policy' into blitz/reskinnable-demo-port/integration 2026-07-31 20:41:01 +02:00
Maxim 83d3bfa31a Merge branch 'blitz/banking-genui-replay/seed-memo' into blitz/reskinnable-demo-port/integration 2026-07-31 20:41:00 +02:00
Maxim 5af07bf489 Merge branch 'blitz/banking-genui-replay/charges-tint' into blitz/reskinnable-demo-port/integration 2026-07-31 20:41:00 +02:00
Maxim 109583caf4 Merge branch 'blitz/banking-genui-replay/prompt' into blitz/reskinnable-demo-port/integration 2026-07-31 20:40:59 +02:00
Maxim 37cf227d8b Merge branch 'blitz/banking-genui-replay/top-charges' into blitz/reskinnable-demo-port/integration 2026-07-31 20:40:59 +02:00
Maxim d9d50139e7 Merge branch 'blitz/banking-genui-replay/pin-card' into blitz/reskinnable-demo-port/integration 2026-07-31 20:40:59 +02:00
Maxim a6c5c93238 feat(reskinnable-demo): tint the charges Sort and Show selects when set
Ports the brand-tint treatment for explicitly-set filter selects from the
banking showcase charges page (#6259) into the reskinnable demo's banking
skin, so an agent-applied filter reads as deliberate.
2026-07-31 20:39:59 +02:00
Maxim ec00c42c50 feat(reskinnable-demo): rebalance seeded policy limits in the banking skin
Ports the seed rebalance from the banking showcase (#6259) into the
reskinnable demo's banking skin so the report's spend-share donut shows a
meaningful split rather than three near-equal slices.
2026-07-31 20:38:50 +02:00
Maxim 9eef856d29 feat(reskinnable-demo): add TopChargesChart to the banking skin
Ports the ranked largest-charges chart from the banking showcase (#6263)
into the reskinnable demo's banking skin. The report-card wiring that
consumes it lands separately.
2026-07-31 20:37:21 +02:00
Maxim 4b4b937599 feat(reskinnable-demo): seed the alert-emoji note convention in the banking skin
Ports the red-alert-emoji instruction from the banking showcase seeded
memories (#6259) into the reskinnable demo's banking skin, so recalled
memory drives the same note formatting.
2026-07-31 20:37:05 +02:00
Maxim a83afc6db8 feat(reskinnable-demo): add prose house-style rules to the banking prompt
Ports the "format prose the same way every time" instruction block from
the banking showcase agent prompt (#6259) into the reskinnable demo's
banking skin, where the prompt lives in agent.ts rather than the route.
2026-07-31 20:36:29 +02:00
Maxim 92756f4a41 feat(reskinnable-demo): add PinChangedCard to the banking skin
Ports the resolved-state PIN card from the banking showcase (#6259) into
the reskinnable demo's banking skin. The consumer wiring lands separately.
2026-07-31 20:36:27 +02:00
Maxim c8fd81a4fe feat(reskinnable-demo): add NavigateConfirmCard to the banking skin
Ports the confirm-before-navigate chat card from the banking showcase
(#6259) into the reskinnable demo's banking skin. The showCharges
human-in-the-loop wiring that consumes it lands separately.
2026-07-31 20:36:02 +02:00
Maxim 916f4b7f24 Merge remote-tracking branch 'origin/main' into blitz/reskinnable-demo-port/integration 2026-07-31 20:31:11 +02:00
Maxim acd8d0715a feat(reskinnable-demo): isolate memory by organization as well as port
Points this app at the stack's second seeded organization
(haus-von-haskell) instead of the one banking uses (casa-de-erlang). Org is
resolved from the authenticated cpk key, and seed.sql already provisions three
orgs for exactly this, so it is a key swap with no backend change.

Ports and orgs isolate on independent axes and the second one matters: ports are
a local convention that copying banking's .env over this one silently undoes,
whereas the org key still holds when someone does. Verified against a freshly
seeded stack — writing under one key is invisible to the other even for an
identical user id, so neither demo can read or delete the other's memories.

Also corrects an inherited claim. banking's .env.example warns that non-seeded
ids 403; measured, they do not — GET/POST /api/memories returns 200/201 for an
unseeded id and for a nonsense one, the scope being created on demand. That
mattered because DEMO_DEFAULT_USER_ID is absent from seed.sql, so the warning
implied the unpinned interactive config it recommends was broken. It is not.
2026-07-31 19:04:05 +02:00
Maxim 3453b228e2 feat(reskinnable-demo): make the resolved Intelligence backend explicit
This app and the sibling banking demo vendor the same durable-memory stack with
identical seeded ids and api key, so which backend a process attached to was
invisible — a `pnpm dev` pointed at the neighbour's live stack shared its memory
buckets silently, and the presenter reset could mutate the other demo's state.
Surface the target so a human notices:

- Runtime route logs the resolved Intelligence API URL once at startup when
  memory is enabled ("[reskinnable-demo] Intelligence: <url>  (memory enabled)").
  Never logs the api key or license token.
- The destructive presenter reset now names its target: it logs a warning with
  the apiUrl and the exact user ids before forgetting, and includes the resolved
  apiUrl in both the success and error response bodies so the caller can see
  which backend was mutated. Behaviour is otherwise unchanged (same ids forgotten,
  same PRESENTER_RESET_ENABLED gate, no confirmation prompt); only observability
  improves. route.test.ts updated to assert the new apiUrl field, still exact.
2026-07-31 17:04:26 +02:00
Maxim 9fe2714d24 fix(reskinnable-demo): shift Intelligence stack host ports by +200 to isolate from banking
reskinnable-demo was cloned from examples/showcases/banking and vendors an
identical Intelligence (durable-memory) docker stack — same seeded ids
(jordan-beamson / morgan-fluxx / northwind-demo-user), same INTELLIGENCE_API_KEY
and org. Both apps identified their backend purely by address, and both pointed
at http://localhost:7050. So with banking's stack already up, a bare `pnpm dev`
here (which, unlike run-demo.sh, checks no port bind) would silently attach to
banking's backend and read/write the SAME memory buckets — worst case, the
presenter reset button forgetting the neighbour demo's memories.

Shift every published host port by +200 (705x/715x -> 725x/727x) in lockstep
across compose, env, scripts, e2e config and tests so attaching to the wrong
stack is impossible by accident:

  app-api        7050 -> 7250
  gateway        7053 -> 7253
  postgres       7156 -> 7256
  redis          7158 -> 7258
  minio API      7160 -> 7260
  minio console  7161 -> 7261
  bundled TEI    7167 -> 7267

The native Metal TEI on :7067 is left shared ON PURPOSE: it holds no demo state
(buckets live in the now-isolated postgres/redis), the same TEI version + model
yields byte-identical embeddings, and run-demo.sh reuses it when healthy rather
than forcing a second ~20x-slower model load. A comment records why.
2026-07-31 17:04:16 +02:00
David McKay de2bc6efc0 feat(banking): rank largest charges instead of repeating the team split
The report's third column was budget-usage bars: the same three team totals the
donut beside it already showed, redrawn with limits added. Two charts, one fact.

It now ranks the largest individual charges, which changes the unit of analysis
from team to transaction. A team aggregate cannot distinguish one $15,000
charge from thirty $500 ones, so this is information the donut genuinely cannot
carry. Bars stay coloured by owning team, so a row still ties back to its slice
without the two charts duplicating each other.

Budget-vs-limit is not lost: it remains the "Over policy limit" KPI above and
the "Needs a decision" rows below.

Invoice-derived line items now carry the policy id of the team they belong to,
so they colour like any other charge. Without it they fell back to a generic
swatch and a Marketing charge did not match Marketing's slice.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 13:11:26 -07:00
Maxim 4b8b6572d9 chore(reskinnable-demo): retarget the demo tooling
Points the demo runner, smoke scripts and env template at the skin-scoped
routes and relocated modules, and gives the vendored Intelligence stack its own
compose project name.

That last one matters: cloned from banking, this app inherited the same project
name, so both stacks resolved to one Docker project sharing containers and
named volumes — a compose down in either destroyed the other's memory. The
smoke scripts also still posted to /agent/default/run, which no longer exists
now that agents are keyed per skin; nothing in the toolchain reads those files,
so no build or test could have caught it.
2026-07-30 21:49:02 +02:00
Maxim 1387f32dee docs(reskinnable-demo): document the architecture and add the reskin skill
Describes the app that exists: shell versus skins, the Skin contract field by
field, the client/server boundary that keeps a skin's agent out of the browser
bundle, per-skin server identity, and the theming split. Adds a repo-local
authoring skill that walks someone through creating a skin and registering it
in both registries.

The inherited docs described a single-skin banking demo, which was actively
misleading once a second skin shipped, and carried claims that were false here
— that OGUI can only render inline in the chat (a published-SDK limitation
that does not apply on workspace packages) and that the airline skin omits
every optional contract slot when it ships nine tool labels.
2026-07-30 21:49:02 +02:00
Maxim 12b1ac6b0d test(reskinnable-demo): cover reskinning and retarget the suite
Retargets the e2e suite onto skin-scoped routes and covers the app's headline
feature for the first time: the index redirect, each skin rendering its own
chrome, an unknown skin 404ing, and switching in both directions.

Those assertions check content only a mounted skin can produce. An earlier
draft matched on brand text, which the shell's skin selector renders on every
page for every skin — so it passed against a deliberately broken airline. Each
assertion here was verified by breaking the app and watching it fail.

ogui-routing is marked fixme: it clicks seven suggestion pills, six of which
were dropped from the registered set before this change, so it has been
failing on main unnoticed — nothing runs this suite in CI.
2026-07-30 21:49:02 +02:00
Maxim 4ad47517aa feat(reskinnable-demo): route by skin
/[skin] resolves a skin from the URL and mounts it keyed by its id, so
switching fully remounts into a fresh world; / redirects to the default skin.
The runtime route registers one agent per skin and resolves end-user identity
through that skin's resolver, falling back to the default skin for app-level
routes such as the inspector's memory endpoints.

globals.css now defines only the design-token vocabulary plus a brand-neutral
default set; each skin supplies its own values through a .theme-<id> block, so
a reskin is a pure value swap and the shared chrome styles itself with
semantic utilities.
2026-07-30 21:49:01 +02:00
Maxim 788783bb58 feat(reskinnable-demo): add the airline skin
A second skin — identity, theme, app-shell, three pages, ten presentational
components, frontend tools, a2ui catalog, suggestions and an OGUI design
brief — sharing nothing with banking beyond the contract and a few primitives.

It backs its data with plain client state rather than a REST API and
contributes no identity resolver, so the contract is exercised across both
substrates. A contract that only ever hosted one kind of skin proves much
less than one that spans two.
2026-07-30 21:49:01 +02:00
Maxim 00f95834e5 feat(reskinnable-demo): make banking a self-contained skin
Banking's domain types, in-memory store, seed data, REST backend, components,
gen-UI beats, a2ui catalog, OGUI sandbox, pages, tools and agent now live
entirely under src/skins/banking, and its backend is namespaced to
/api/banking/v1 rather than mounted at the app root.

Every route under it is banking domain — cards, transactions, exceptions,
policies, reports, users, dev/reset — so with a second skin sharing the app,
leaving one skin's whole backend at the root contradicts the premise that
skins are self-contained. Shared UI primitives stay in src/components/ui,
which the shell and both skins import.

Banking contributes its identity through the contract (RuntimeProviders +
useRuntimeProperties) so the CopilotKit provider owns the active member from
its first commit, and a server-safe resolver so Intelligence scopes memory to
the selected persona.
2026-07-30 21:49:01 +02:00
Maxim 651f7dd7aa feat(reskinnable-demo): add the skin-agnostic shell and Skin contract
The shell owns everything independent of which skin is mounted: the Skin
contract, a provider that runs a skin's data hook, split client/server
registries, the chat surface, the a2ui/OGUI canvas region and thread
selection.

The client/server split is deliberate. A skin's agent pulls in
@copilotkit/runtime, which must never reach the browser bundle, so agents are
registered in a separate server-only registry and linked to their skin by a
shared id rather than by being part of the client contract. Skins may also
contribute a server-safe identity resolver there, which is what lets per-user
durable memory be scoped per skin instead of the runtime hard-coding one.
2026-07-30 21:49:01 +02:00
Maxim 3790bbb431 feat(reskinnable-demo): add the app to the workspace
Registers examples/showcases/reskinnable-demo as its own pnpm workspace member
resolving @copilotkit/* via workspace:*, alongside the other showcases.

No dependency differs from the banking showcase this app started as: the
standalone prototype's recharts dep is unused (banking hand-rolls its SVG
charts), and the airline skin's lucide icons all exist in the pinned range.
2026-07-30 21:49:00 +02:00
David McKay 269d49a2c0 feat(banking): generative-UI polish for PIN, charges, report and notes
Five presentation fixes to the Northwind Finance demo, all from running the
beats live.

PIN change now resolves into a card rather than the sentence "New PIN saved."
It shows the card face, brand and last4, a masked new-PIN row and an active
badge. Digits are never rendered: they are never sent to the agent, so the mask
is the honest representation.

Reopening a thread replays setCardPin with status "inProgress" and no result,
so the answered card sat on "Loading..." forever. Other human-in-the-loop tools
here (showCharges) do replay their result, so this is specific to that call.
The outcome is now remembered per tool call id for the session and consulted
ahead of the replayed status. Both this card and the charges card key their
resolved state on the RESULT rather than the status, so an answered call can
never replay with live buttons.

setCardPin also registers once via a ref instead of depending on `cards`:
useFrontendTool re-registers whenever JSON.stringify(deps) changes and
re-registration removes the tool, so the PIN write tore down the very tool that
was servicing it.

showCharges becomes human-in-the-loop. Opening a filtered list is safe, but it
replaces the whole screen, and an agent that does that unasked reads as the
agent being in charge. The confirm card names the sort and filters before the
page changes, and on arrival the Sort and Show controls carry the brand tint
whenever they are non-default, so what the agent set is what lights up.

The Q2 report shows three different chart forms (share-of-total pie, time
series, budget bars) instead of three bar charts, and the seed is rebalanced so
team shares read 42/28/30 instead of 98/2/2 while all three pending charges
still exceed their limits. This drops the income-vs-expenses chart that was
showing $0.00.

Notes about reported charges carry a leading alert emoji so they cannot be
skimmed past. The seeded procedure asks for it and the handler applies it
regardless, because a model is not a reliable emoji emitter.

Finally, prose answers get a house style. The agent was formatting the first
few bullets of a list and then lapsing into plain text, which reads as a
rendering bug rather than a style choice.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 11:12:00 -07:00
Maxim 1e575233e6 test(banking): make the memory-recall e2e approval loop actually completable
The approval loop could never pass, independent of the demo's behavior:

  for (let step = 0; step < 4; step++) {
    const approve = page.getByRole("button", { name: /^approve$/i });
    await approve.first().click({ timeout: 30_000 }).catch(...)
  }

Two compounding problems. The recall path emits at most three HITL cards
(openPolicyException -> finalizePolicyException -> approveTransaction) but the
loop demanded four, so the last iteration always waited on a card that never
arrives. And the per-click timeout (30s) equalled the whole test budget
(playwright.config.ts sets no `timeout`, so Playwright's 30s default applied),
so the first miss consumed the entire test and the `.catch()` fallback to the
other APPROVE_LABELS was unreachable dead code.

Now: poll every label for the next visible approve control, treat "no card
left" as flow completion rather than a timeout, keep a runaway guard that is
explicitly not the expected card count, and give the test a budget that can
actually hold one agent turn plus the closing server poll.

Confirmed against a local run (aimock + the docker Intelligence stack): the
agent recalls the procedure, files the EXC-BOARD-APPROVED exception, and
approves the charge, and the "Record a workflow?" card never appears. The
header's "verify on first green run" notes are updated to record what that
run settled and what it did not.

The spec still fails at its closing assertion: PUT /api/v1/transactions/t-3
{status:"approved"} keeps returning 422, i.e. store.hasApprovedException() is
false, so the over-limit gate is never lifted even though the (fixture-scripted)
agent text claims it was. That is pre-existing and out of scope here — this PR
touches no part of the approval path (lib/store, the exceptions/transactions
routes, copilot-context, components/wow are all untouched). Left failing rather
than skipped so it stays visible.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-29 15:27:08 +02:00
Maxim 0850c5e522 Merge remote-tracking branch 'origin/main' into blitz/glass-inspector/integration
Realigns the inspector/memory work onto the banking demo as it shipped in
#6136 (ChatGPT-style shell, gen-UI beats, durable-memory self-learning) and
#6202 (README refresh).

All six conflicts were the same collision: this branch removes the bespoke
Glass Engine inspector, while #6136 kept and rebuilt around it.

- run-handler.ts: kept both sides (our CopilotKitCoreCatalogComponent and
  main's MAX_FOLLOW_UP_DEPTH landed at the same spot).
- wrapper.tsx / layout.tsx: took main's rewritten provider tree and
  right-hand icon rail, minus the Glass Engine providers, pane, and
  telescope toggle. Also dropped main's `padClass` (it reserved space for
  the Glass pane and referenced a now-removed `glassActive`) and
  `<ProactiveNotice />` (main removed it; the import is already gone).
- memory-tab.tsx, lib/intelligence/memory.ts: confirmed the deletions.
  Their only remaining importers were the bespoke inspector and the
  banking-local /api/memories routes, all removed here. seed-memories.ts
  is unaffected: it POSTs to INTELLIGENCE_API_URL, not the local route.
- README.md: kept our product-inspector section over main's Glass Engine
  availability/activation prose, and documented the Capabilities tab.

Drive-by fixes to comment rot the migration created: user-id.ts and the
copilotkit route doc comments referenced the deleted Memory-panel proxies,
and the README pointed the presenter-reset control at the removed
telescope toggle.

Also replaces a literal NUL byte in capabilityKey() with a unicode escape.
The raw control character made tsc/grep/diff treat run-handler.ts as a
binary file, which hid this very merge's conflict markers from grep.
Behavior is unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-28 21:10:21 +02:00
David McKay b2f4b47a2d docs(banking): refresh the demo README to match what shipped
#6136 changed the demo substantially and left the README describing an older
build. Corrections:

- It documented CopilotPopup; the chat is a left-docked CopilotSidebar rendering
  two columns (thread rail + conversation) with the app's nav rail on the right.
- It called the learned over-limit memory `procedural`; the code writes
  `operational`.
- It said the Glass Engine Memory tab was "top-k semantic recall, not a full
  enumeration". It is now a complete enumeration ("Stored memories (N)"),
  aggregated across the demo's identities, with semantic search as a separate
  box.
- It listed per-run reset as a future follow-up. It ships, gated behind
  PRESENTER_RESET_ENABLED, and also re-seeds memory and returns to the bare root.
- It described showTransactions as a useFrontendTool; it is a useComponent so it
  persists in the transcript.
- The "change my Visa PIN from the team page" example described a navigation
  fallback; PIN entry is now a component in the chat and the agent never sees the
  digits.

Adds what was missing: the eight-pill demo script, the /charges page, the PDF/Q2
multimodal beat, seeded memory (and why the over-limit procedure is deliberately
NOT seeded), the components-not-markdown-tables rule, per-team chart colour, and
the two incompatible helper families in actions.ts that make destructuring `ok`
report a success as a failure.

All three screenshots are replaced — they showed the old right-docked chat, so
they contradicted the first paragraph.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 09:22:11 -07:00
David McKay 66a6af5d3e feat(banking): ChatGPT-style shell, gen-UI beats, and durable-memory self-learning
Turns the Northwind Finance banking demo into a presentable end-to-end demo.
Every beat was driven live and verified against real app state, not just the
agent's own claims.

The demo is eight suggestion pills, in order: show a chart; change a card PIN in
an interactive in-chat component; ask what is on screen; open the 10 most
expensive charges; prep the Q2 report from an attached PDF invoice; summarize
spend using a remembered preference; handle an unrecognized charge using a
remembered procedure; and finally learn a brand-new procedure by watching the
officer clear an over-limit charge. The last three are sequenced deliberately:
already-knows-a-preference, already-knows-a-procedure, learns-a-new-one — and on
a fresh thread the newly learned procedure is recalled and applied to a
different charge unaided.

Shell and UI:
- Chat docks LEFT as two columns (thread rail + conversation) with the app's
  icon rail moved right via flex `order`, so nav still precedes <main> in the
  DOM. The chat surface runs ChatGPT's neutral palette while the app keeps its
  violet identity.
- Tool activity renders as ChatGPT-style expandable lines; internal AG-UI
  plumbing is filtered out of the transcript.
- Tabular data always renders as components, never markdown tables; a one-row
  table is blocked structurally because a single-record table is narration.
- Filed reports are a dashboard (KPI tiles, three charts, decision rows) rather
  than a memo, with one colour per team across every chart and ranked bars in
  place of a donut that collapsed when an invoice pushed one team to 96%.
- PIN entry happens in the chat component; the agent never asks for or receives
  the digits.

Fixes worth review attention:
- Anything a RECALLED procedure can call must be registered globally. Four
  action tools were registered on the cards route, so a saved procedure only
  worked from one page.
- An unanswered human-in-the-loop card poisons the thread: skipping it left an
  unresolved tool call and the next message failed with "Tool result is missing
  for tool call ...". The procedure's closing step no longer gates; approvals
  that move money still do.
- actions.ts has two helper families that are not interchangeable —
  openPolicyException / finalizePolicyException / changeTransactionStatus return
  {ok, data/error}, while changePin / addNoteToTransaction resolve with the
  updated record. Destructuring `ok` off the second family reports success as
  failure.
- A stale canvas surface hid the page: it is derived from the last a2ui-surface
  message and replaces the page body until dismissed, but the dismiss effect
  keyed on pathname only, so a ?tab= switch left it covering the page.
- Suggestion pills ignore clicks while a run is in flight, since a double-click
  orphaned the previous run's tool call.

Deliberately NOT included: the @copilotkit/license-verifier pnpm patch (local
workaround for the "Invalid license token" banner) stays uncommitted, because it
patches a dependency for every install and is not demo scope.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 16:22:36 -07:00