GHSA-72qq-p3r5-f7wq: the basic catalog openUrl action accepted any URL
scheme and passed it to window.open, so an agent-supplied A2UI Button
carrying a javascript: URI would execute script in the app origin on click.
0.10.2 resolves the URL and refuses anything that is not http or https.
* fix: make dashboard chat fill the main column (fixes#322)
Chat and Chat+Canvas built-in pages declare layout: full-bleed. selectPage()
toggles a db-fill class on #db-main-content and skips the duplicate page
header, so .chat-root sizes from flex allocation instead of
calc(100vh - 140px). Session rail stacks above the thread below 900px.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: make full-bleed page metadata test non-vacuous
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
The frontend build gate added on 2026-09-23 (166091b) has failed on every
main run since: five April PRs left code that never compiled — imports of
shadcn components that were never added (ui/alert, ui/textarea), seven
example widgets that never existed (CustomElementHost), react-router-dom
(not a dependency), a useSSE call written for a different hook, a wrong
ChatMessages prop set, and unused locals under noUnusedLocals.
- add the standard shadcn alert and textarea components
- CustomElementHost: empty registry instead of imports of missing widgets
- useWindowMessage: small local EventSource hook for /sse/window-message
- SharedThread: read the token from /shared/{token}; pass the props
ChatMessages takes; type messages as ChatMessage[]
- drop unused imports/locals
None of these files are imported by the app today, so nothing users see
changes; the gate now builds and catches real breakage again.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
An agent created without name= records agent_name null. The Replay map
skipped every such event, so watch(Agent(instructions=...)) showed
"Nothing to map yet" and its steps were narrated as "The run".
- agentOf(ev): agent_name, else the package's id (agent:Agent from the
companion core fix), else tool:None/<tool> or nothing in 4.7.19 traces,
falling back to "Agent" for an agent's own run events.
- deriveGraph / nodeIdsForEvent / narrate use it, so the agent and its tools
are drawn and lit, and steps read "Agent started.".
- Only when no graph was recorded: a team graph leaves unnamed agents out on
purpose, and they must not reappear as "Outside the team".
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- create_replay_app(run=None): the Replay view as a small ASGI app (no dashboard)
to mount in any FastAPI/Starlette app; /?run=<id> pins one run (iframe-friendly).
With run(input, run_id) it shows a Run box: POST /api/replay/run (JSON only,
one run at a time) starts a run and the page follows it live.
- render(el, {base, run, runner, approve}) so the view works under a mount prefix.
- Map nodes show their state at the cursor: not reached / running / done / failed /
skipped, and "×N" counts real runs instead of events (a step that ran once
no longer reads "×2"). Scrubbing or Play rewinds the map with the cursor.
- ▶ Play steps through a run on its own.
- events?wait=1 answers a not-yet-started run without a 404.
- Node names stay whole (squeezed or clipped) so accessible names contain the
visible text (WCAG 2.5.3; axe flagged truncated tool names).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- At <=1023 px the hidden sidebar still took 260 px of the flex row,
because buildSidebar() sets an inline position:relative that beat the
mobile position:fixed; and the shell header slot took another 53 px.
Every page's content was squeezed to a few pixels at phone width. The
sidebar is now fixed (!important), the header slot gets its own row,
and phones get a 16 px gutter.
- Pages may set icon_svg next to the text icon. The sidebar and page
header parse it as SVG and drop it if it holds scripts, foreignObject,
on* handlers or javascript: URLs; other UIs keep using `icon`.
- Replay's heading uses its own inline mark (three lanes and a cursor)
instead of the emoji, and its description no longer says "why" (it
made Playwright's text=Why match the page header, not the tab).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
From a review against real recorded runs:
- Timeline: an agent's LLM span encloses its tool calls in real runs, so
tool blocks covered the LLM label. Each lane now has an LLM row with a
thinner tool row under it, blocks are packed into tracks in pixel space
so nothing overlaps, and labels are ellipsized; the full label is in
the title, the accessible name and a hover/focus tip. Visible labels
sit in a decorative layer so names never contain a cut-off word.
- Theme: defaults to the system setting (prefers-color-scheme, tracked
live in CSS) with a System / Light / Dark menu as an override.
- Map: fits the whole graph on open (shares -/Fit/+), drag to pan, never
below a readable scale, and gets the full width with the step list
underneath. Agents seen in events but not in the graph (e.g. a
hand-off to an agent outside the team) are added by observedGraph()
and laid out by layoutRunMap() as an "Outside the team" group.
- Phone: 16 px gutter, stacked layout, no horizontal page scroll.
- Standalone boot waits for DOMContentLoaded so the export works with
either script order, takes data.run_id as the title, and keeps the
documented #replay-root / window.__REPLAY_DATA__ contract.
- New fixture team_outside.jsonl with the real-run shape; node tests
for observedGraph/layoutRunMap; browser tests for no-overlap, the Why
tab by role and keyboard, system theme, map fit, phone width and the
wrapper-style export in both script orders.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The fit-to-width minimum (480 px of plot) made the timeline overflow in
the dashboard's narrower content column; lower it to 260 px. Pending
approvals and running blocks showed "Duration 0 ms"; show "still waiting"
or "still running" instead.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
docs/features/replay.md describes the page, keys, approvals, export and
HTTP API. docs/adr/0001-replay-view-clean-room.md records the ideas we
took (graph plus trace, score-like swimlanes, OTel GenAI vocabulary), that
no third-party UI code, CSS, icons, layouts or screenshots were used, and
the palette, accessibility and licence reasoning.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
_UIStoreBackend returned "Awaiting UI approval" (a denial) immediately, so
any tool needing approval was always denied and the UI's Approve button
never reached the agent. It also dropped request.approval_id and any
edited arguments.
- each request is stored under request.approval_id and the caller blocks
until /api/approvals/{id}/approve|deny resolves it: sync callers on a
threading.Event, async callers on a future of their own loop
- no answer before the timeout (registry timeout, default 300 s) is a
denial and the entry is resolved as denied (fail-closed)
- a sync call made on an event-loop thread is denied at once rather than
freezing that loop
- POST /approve accepts modified_args (object) and the decision carries
it as ApprovalDecision(modified_args=...); the handler change is two
lines so the auth checks in #312 rebase cleanly
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
SDKTracingManager looked up praisonaiagents.trace.list_traces, list_spans
and get_trace, which do not exist, so /api/traces never showed a recorded
run and silently fell back to an empty in-memory store.
JsonlTracingManager reads the same directory as Replay: one file is one
trace, spans come from start/end pairs (parent_seq on schema 1.1, sequence
order on 1.0), and records posted to /api/traces/record still work.
health() reports source: jsonl|memory. SDKTracingManager stays as a
deprecated alias.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
New `replay` feature (contract v1 §5), read-only:
- GET /api/replay/runs: run-picker rows, newest first, with a live flag
- GET /api/replay/runs/{id}/events?after_seq=N: {events, graph, run}
- GET /api/replay/runs/{id}/stream: SSE tail of the JSONL (stdlib seeks),
`ctx` events with id=sequence_num, ping every 15 s, `end` after the run
finishes plus 2 s quiet; honours after_seq and Last-Event-ID
- ids must match ^[A-Za-z0-9._-]+$ and resolve inside the traces dir
- traces dir: PRAISONAI_TRACES_DIR > PRAISON_HOME/traces > ~/.praison/traces
- half-written last lines, unknown fields/types and schema 1.0 are tolerated
Plain-JS view `replay` (no React, no CDN): replay-core.js holds pure
buildModel/narrate/layoutMap/deriveGraph (node --test); replay.js draws the
timeline (agent, Manager and You lanes, LLM/tool blocks, hand-off arrows,
parallel sub-lanes, xN repeats, task/step chapters, scrubber, live follow),
the narration list, What happened/Why/Raw JSON drawer, cost/latency
overlay, map view, and Approve/Deny/Edit-args for pending approvals.
Registered in BUILTIN_VIEWS and as a Control page; /#/replay?run=<id> deep
links resolve to /replay?run=<id>. It also boots standalone from
window.__REPLAY_DATA__; standalone_html() inlines both files for exports.
praisonaiui/replay_app.py + configure_replay_app() open aiui straight on
Replay for `praisonai replay view`.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Writers-only auto-Claude, least-privilege tokens for Claude on fork PRs and
in the merge-gate assessor, merge-gate held-out BLOCKs (no repeat verdicts on
an unchanged HEAD), portable prompts. auto-pr-comment sweep moves to hourly so
stale FINAL reviews are re-posted once a push settles.
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
The /api/approvals payloads emit created_at as epoch seconds (time.time()),
but mapApprovalToCardProps used asString(), silently dropping the value for
real payloads so ApprovalPromptModel.createdAt was always undefined. Coerce
numeric epochs to an ISO-8601 string (ISO strings still pass through) and
align the unit test with the real wire shape.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Correct the fallback reference to templates/frontend/plugins/views/chat.js
(the file lives under views/, not directly under plugins/).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The shipped docs site is built by `aiui build` from aiui.template.yaml
(see ci.yml / deploy-pages.yml), not mkdocs.yml. Add a Frontend nav group
so the playbook is discoverable in the built site navigation.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The approve/deny endpoints only store `reason`; map the selected option or
typed custom answer into it so the human's decision note is recorded, while
keeping `option`/`custom_answer` for a future richer backend.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The adapter unit tests use Node's built-in runner, but the documented
command (`node --test src/agent-ui/`) fails on Node 22 — it neither
strips TypeScript types nor discovers the files, so the suite never ran.
- Add a `test` script to src/frontend that runs the suite with
`--experimental-strip-types` (8/8 passing on Node 22).
- Fix the misleading run instructions in the test header.
Closes the "unit tests" acceptance criterion of #294 with an executable
suite. Purely additive; no runtime code changed.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds a `frontend` job to CI that runs `npm ci && npm run build` for
src/frontend on every push/PR to main, failing on TypeScript/Vite build
errors. Uses Node 22 with npm caching keyed on the frontend lockfile.
Also documents the Node version, lockfile, and build/ship path in
src/frontend/README.md.
Co-Authored-By: Claude Opus 4.8 <[email protected]>