Skip to content

feat(chat): support custom local chats in portal (long system prompt + flat render)#6

Merged
albanm merged 14 commits into
mainfrom
feat-custom-agent
May 29, 2026
Merged

feat(chat): support custom local chats in portal (long system prompt + flat render)#6
albanm merged 14 commits into
mainfrom
feat-custom-agent

Conversation

@albanm
Copy link
Copy Markdown
Member

@albanm albanm commented May 29, 2026

Add the capabilities needed to embed custom local chats in a portal: an arbitrarily long system prompt and a flat (inline) chat render.

What changed:

  • Arbitrary system prompt lengthsystemPrompt and title are handed to the chat iframe through same-origin sessionStorage init-config (lib-vue/agent-init-config.ts, keyed by a ?initConfig=<key> URL param) instead of URL query params. resolveAgentChatUrl now only appends ?initConfig=<key>; AgentChat.vue reads the config on mount, taking precedence over props.
  • Flat render — new DfAgentChatBlock + useAgentChatBlock: the chat iframe rendered inline in a page, always visible, no FAB/drawer/popover. createAgentChatBase's storageKey is now optional since a block has no open/close state to persist.
  • Dev pages (_dev/chat-block, _dev/chat-drawer) and e2e specs for the block, system-prompt transmission, and the legacy ?systemPrompt= path. Existing chat e2e helpers now match the iframe by pathname to tolerate the new initConfig query param.
  • Docs updated: docs/embedding-guide.md (new Block component, init-config section, corrected URL-resolution section) and docs/architecture.md (block variant + init-config channel).

Why: the iframe URL passes through nginx (~8KB request-line limit → HTTP 414); after percent-encoding, a URL-borne system prompt safely tops out around ~2KB, too small for real custom-agent prompts, and it leaked into nginx logs / history / Referer. sessionStorage removes both the size limit and the exposure. The block variant lets a portal embed the chat inline rather than only as a floating overlay.

Regression risks:

  • resolveAgentChatUrl no longer carries title/systemPrompt in the URL from the drawer/menu/block components. The legacy direct-URL path (chat.vue reading those query params) is unchanged and still works (covered by a dedicated e2e test). Risk only if an embedder mounts these components against an iframe target that reads URL params exclusively, or uses a custom src that ignores init-config.
  • createAgentChatBase(isOpen, storageKey?) signature change — storageKey is now optional with guarded localStorage writes. Existing drawer/menu callers pass it unchanged; only the new block omits it.
  • Stable per-variant init-config keys (drawer/menu/block) mean two instances of the same variant in one tab would clobber each other's config; overridable via the initConfigKey prop.

albanm and others added 14 commits May 29, 2026 10:14
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… export type

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the BroadcastChannel-based system-prompt transmission with a
generic one-shot "set then get": the host stores an AgentInitConfig
({ prompt, title }, extensible) in sessionStorage under a per-instance
crypto.randomUUID() key and passes that key to the iframe via the
initConfig query param. The iframe reads its own key from the URL and
applies the config once on mount.

This is same-origin only, not reactive, and isolates several chats in
the same tab (each instance has a distinct key). Drops useSystemPromptChannel,
the AgentSetSystemPrompt message type, the fixed df-agent-system-prompt
storage key, and the title URL param (title now lives in the config).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a third agent-chat variant alongside menu and drawer: a block that
renders the chat iframe flat in the page (the caller wraps it in a card).
Being always visible, it has no toggle, open/close state, unread badge, or
auto-open — but it reuses createAgentChatBase so it still participates in the
ready/ping/pong discovery handshake (action buttons can target it) and handles
navigate messages.

- make storageKey optional in createAgentChatBase (a flat block has no
  open/closed state to persist); menu/drawer keep passing their keys
- add useAgentChatBlock singleton composable (isOpen = ref(true), no key)
- add DfAgentChatBlock.vue filling its container (height/width 100%)
- export the component, composable and AgentChatBlockState type
- add _dev/chat-block demo page and e2e spec

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The per-chat initConfig refactor appended `?initConfig=<key>` to the chat
iframe URL, which broke the `url().endsWith('/_dev/chat')` frame matcher in the
drawer, menu and action specs (chat-system-prompt already used the fix). Match
on `new URL(url).pathname` instead, as those specs now do.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ndom UUIDs

setAgentInitConfig writes to sessionStorage under a per-key entry that is never
removed, so a fresh crypto.randomUUID() per mount leaked a new entry on every
page visit within a tab. Default each variant to a stable key ('menu', 'drawer',
'block') so re-mounts overwrite in place and storage stays bounded to one entry
per variant. A page can host one of each variant without clobbering; mounting
several of the same variant is the only case that needs the new optional
initConfigKey prop to force a distinct key.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d plan

Remove the superseded BroadcastChannel plan/spec and update architecture
docs to reflect the shipped approach: the DfAgentChatBlock flat render and
the sessionStorage init-config handoff for systemPrompt/title.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@albanm albanm merged commit 17e0578 into main May 29, 2026
4 checks passed
@albanm albanm deleted the feat-custom-agent branch May 29, 2026 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant