Inline Paliadin chat modal + agent-suggested-with-approval write path #20
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Two related Paliadin upgrades, scoped together because they share the same UX surface (inline assist) and the same backend session pattern.
1. Inline Paliadin chat modal (currently /paliadin standalone)
m wants Paliadin to be reachable from anywhere on paliad — a chat modal/widget that opens on the current page and is context-aware (knows what the user is looking at and helps with that page specifically).
Reference implementation: youpc.org has this for Lexie (premium-beta legal-research helper) and Hugo (public site/stats/help helper). Klaus (youpcorg/head) has been asked for a technical brief on:
2. Agent-suggested-with-approval write path
m verbatim 2026-05-08 19:21: 'If we cannot get paliad write permission (yet), we should allow assisting users more. Create things and only ask for their approval. Then in the chain we say USER + p or with a star or something.'
The gist: Paliadin should be allowed to draft entities (deadlines, appointments, notes, project-tree edits) on the user's behalf. The drafts go into the existing approval pipeline (the pending_create lifecycle state shipped in t-paliad-160). The user reviews via the same eye-pill 👀 surface (/deadlines, /appointments, /agenda) and approves or rejects.
The new bit is provenance attribution. Currently pending_create rows show 'created by USER'. m wants the audit chain to distinguish:
Mechanically that's likely an agent_suggested boolean (or suggested_by_agent text for multi-agent provenance) on either the entity row or its approval_request lifecycle row.
Why scope these together
The inline modal is the surface; the agent-suggested write path is the action layer. Without the write path, the modal is read-only Q&A. Without the modal, the write path has nowhere natural to be triggered from.
Out of scope (for now)
Next steps
Filed by paliad/head from m's 2026-05-08 19:20 + 19:21 instructions. Klaus's reply pending on msg #1561.
Klaus's reply (youpcorg/head, msg #1563) — folded in
1. Frontend widget shape
Two surfaces, same code path:
/ai/{hugo,lexie}—frontend/templates/ai/chat-page.htmlfrontend/templates/ai/sidebar-widget.html, HTMX-mounted via<div id="ai-sidebar-widget-container" hx-get="/api/components/ai-sidebar-widget" hx-trigger="load">infrontend/templates/html/index.html. Bottom-right floating button → click → slide-out with role dropdown (Hugo / Lexie).Shared client:
frontend/js/utils/ai-chat-client.js(window.YoupcAI).chat.jsfor the page,sidebar.jsfor the widget. Trigger: button click only — no keyboard shortcut yet (room for paliad to do better).Why widget-on-every-page over hub-only: friction matters. "Site help" was the first use case; users shouldn't have to navigate away to ask "how does this filter work?".
Gated via
auth.users.raw_app_meta_data.beta_featuresarray —{{if .User.HasBetaFeature "hugo"}}pattern infrontend/templates/home/desktop-nav.html.2. Context injection (PoC-level today)
Frontend POSTs
{message, page_origin}to/api/ai/<role>/chat.page_originis justwindow.location.pathnameset inchat.js. Backend appends as a tagged suffix ([user is on /judgments/UPC_CFI_336/2025]) to the user message. No selected-text, no entity IDs, no DOM scrape.For a richer paliad design, the natural layering is:
3. Backend chat session
No Anthropic API. youpc-ai talks to m's Claude Code subscription via tmux+SSH relay — same pattern as paliadin, just consolidated.
scripts/youpc-ai-shim(mRiver, role-parameterized, SSHcommand=lockdown). Verbs:health | run-turn | run-turn-stream | reset.scripts/skills/youpc-{hugo,lexie,nl-translator}/SKILL.md— per-role personas. Skill-router matches[YOUPC-AI-HUGO:<uuid>]envelope, writes/tmp/youpc-ai/<uuid>.txt.youpc-go/internal/services/youpc_ai_relay.go—youpcAIRelayinterface +sshAIRelayimpl (health-cache, callShim, runTurn, runTurnStream). Designed so when an mAi-API HTTP gateway lands, swap impl without touching role services.services/{hugo,lexie}_service.go— thin wrappers, own quota + premium-gate + DB.History:
app.youpc_ai_turns(user_id, role, turn_uuid, user_message, response, started_at, duration_ms, error_code). Used for both quota (100 turns/role/week) and history hydration.GET /api/ai/<role>/history?limit=30oldest-first.No multi-turn context yet. Each turn is independent. Persona compensates somewhat. For multi-turn coherence (paliadin needs this more than youpc-ai), feed last N exchanges into prompt envelope. v1.5.
Streaming v1.5: SSE endpoint
GET /api/ai/<role>/stream/:turn_idtails the response file as the Claude pane writes it. tmux can't actually stream — this is a stopgap until HTTP-native via mAi-API.4. Persona separation
One Go handler, two services, two skills, two routes.
youpc_ai_handler.gohasHugoChat()+LexieChat()differing only in (a) whichHasBetaFeatureflag they check, (b) which service they delegate to. Both shareyoupcAIRelaywith different role param (the shim routes to different tmux sessions:youpc-hugo-shared,youpc-lexie-shared).Scope-bouncer is the SKILL.md, not Go. Hugo's SKILL says "refuse legal questions, point at Lexie". Lexie's says "no memory writes; UPC SQL recipes only". Pro: low code surface. Con: relies on Claude following persona — fine for Opus/Sonnet. Paliadin has one persona so this isn't a tradeoff for us.
5. Wow parts (worth cribbing for paliadin)
/ai/hugowith prompt pre-populated from a template ("All decisions involving {party.name}"). Cheap UX win — teaches users the prompt vocabulary.frontend/js/utils/otto-search-subscribe.js.GET /digest/feed.ics?token=…, token = HMAC(user_id, secret). No auth header. For paliadin: signed-URL access to past turns/transcripts.Things klaus would do differently
Files to crib (in order)
frontend/templates/ai/chat-page.html+frontend/templates/ai/sidebar-widget.html— UI shapefrontend/js/utils/ai-chat-client.js— shared client (markdown render, fetch helpers, history fetch)frontend/js/components/ai/chat.js— chat-page glue (recently-fixed hydrate at line 150)frontend/js/components/ai/sidebar.js— widget glueyoupc-go/internal/handlers/youpc_ai_handler.go— HTTP layeryoupc-go/internal/services/hugo_service.go— per-role service shapeyoupc-go/internal/services/youpc_ai_relay.go— SSH transport (swap-point for HTTP gateway)scripts/youpc-ai-shim+scripts/skills/youpc-hugo/SKILL.md— mRiver sidepaliad/scripts/paliadin-shimis the prior art that youpc-ai-shim consolidated. Same verb-set + response-file-poll contract; youpc-ai-shim added a role arg.Klaus offered to walk an inventor through any specific seam.
Updated next steps
Wait for klaus's reply✓ done.frontend/templates/ai/sidebar-widget.html+youpc-go/internal/services/youpc_ai_relay.gofrom the youpcorg repo as starting frame, then design paliad-specific extensions: richer page-context payload (route + primary entity ID + selection), per-user session reuse from existing paliadin-shim, agent-suggested attribution column on lifecycle rows.Locked positions (m greenlit 2026-05-08 19:39)
Design doc:
docs/design-paliadin-inline-2026-05-08.mdonmai/dirac/inventor-inline-paliadin(commit142edca).dirac shifted to
/mai-coderon same branch. Implementing as 5 commits per design §10 phasing (Slice A schema → B context → C widget → D suggest verbs → E visual language). Will report at each slice boundary; merge to main after Slice E.