@llui/agent 0.0.51 → 0.0.52

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 (46) hide show
  1. package/dist/client/agentConnect.d.ts.map +1 -1
  2. package/dist/client/agentConnect.js +45 -31
  3. package/dist/client/agentConnect.js.map +1 -1
  4. package/dist/client/effect-handler.d.ts +0 -21
  5. package/dist/client/effect-handler.d.ts.map +1 -1
  6. package/dist/client/effect-handler.js +0 -26
  7. package/dist/client/effect-handler.js.map +1 -1
  8. package/dist/client/effects.d.ts +0 -23
  9. package/dist/client/effects.d.ts.map +1 -1
  10. package/dist/client/effects.js.map +1 -1
  11. package/dist/client/factory.d.ts +0 -9
  12. package/dist/client/factory.d.ts.map +1 -1
  13. package/dist/client/factory.js +0 -2
  14. package/dist/client/factory.js.map +1 -1
  15. package/dist/client/index.d.ts +0 -1
  16. package/dist/client/index.d.ts.map +1 -1
  17. package/dist/client/index.js +0 -1
  18. package/dist/client/index.js.map +1 -1
  19. package/dist/client/ws-client.d.ts +0 -9
  20. package/dist/client/ws-client.d.ts.map +1 -1
  21. package/dist/client/ws-client.js +0 -32
  22. package/dist/client/ws-client.js.map +1 -1
  23. package/dist/protocol.d.ts +1 -50
  24. package/dist/protocol.d.ts.map +1 -1
  25. package/dist/protocol.js.map +1 -1
  26. package/dist/server/cloudflare/durable-object.d.ts +0 -41
  27. package/dist/server/cloudflare/durable-object.d.ts.map +1 -1
  28. package/dist/server/cloudflare/durable-object.js +0 -46
  29. package/dist/server/cloudflare/durable-object.js.map +1 -1
  30. package/dist/server/cloudflare/index.d.ts +3 -10
  31. package/dist/server/cloudflare/index.d.ts.map +1 -1
  32. package/dist/server/cloudflare/index.js +3 -10
  33. package/dist/server/cloudflare/index.js.map +1 -1
  34. package/dist/server/core.d.ts +1 -11
  35. package/dist/server/core.d.ts.map +1 -1
  36. package/dist/server/core.js +0 -1
  37. package/dist/server/core.js.map +1 -1
  38. package/dist/server/lap/router.d.ts.map +1 -1
  39. package/dist/server/lap/router.js +0 -3
  40. package/dist/server/lap/router.js.map +1 -1
  41. package/dist/server/ws/pairing-registry.d.ts +0 -101
  42. package/dist/server/ws/pairing-registry.d.ts.map +1 -1
  43. package/dist/server/ws/pairing-registry.js +0 -160
  44. package/dist/server/ws/pairing-registry.js.map +1 -1
  45. package/package.json +1 -1
  46. package/styles/agent-panel.css +0 -13
@@ -1 +1 @@
1
- {"version":3,"file":"agentConnect.d.ts","sourceRoot":"","sources":["../../src/client/agentConnect.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agentConnect.d.ts","sourceRoot":"","sources":["../../src/client/agentConnect.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,49 @@ function reconnectDelayMs(attempt) {
23
23
  * silently spinning.
24
24
  */
25
25
  const RECONNECT_GIVE_UP_MS = 5 * 60 * 1000;
26
+ /**
27
+ * Build the user-pasted connect snippet that lands in the LLM's chat
28
+ * window. Three things in one paragraph that an LLM will follow on
29
+ * first read:
30
+ *
31
+ * 1. Tell the LLM which tool to call to bind the session
32
+ * (`connect_session`).
33
+ *
34
+ * 2. Encourage `narrate` for surfacing intent during multi-step
35
+ * actions. Without the nudge the LLM tends to dispatch silently;
36
+ * with it, the user sees a running commentary in the app's
37
+ * activity log alongside the action entries. One-way channel
38
+ * (LLM → user); the conversation itself stays in the LLM's own
39
+ * chat window.
40
+ *
41
+ * 3. Survive tool-namespacing edge cases: Claude Desktop exposes MCP
42
+ * tools as bare names (`connect_session`) but Claude Code and
43
+ * other namespacing clients emit them as
44
+ * `mcp__llui__connect_session` and may defer-load them — so an
45
+ * LLM that searches its tool list for a literal name won't find
46
+ * it. Naming the LLui MCP server explicitly (with its canonical
47
+ * install name `llui`) gives the model enough to resolve the
48
+ * right tool on either platform; the parenthetical surfaces the
49
+ * edge case for deferred-tool clients.
50
+ *
51
+ * Phrased generically (`AI assistant`, `Some MCP clients`) since
52
+ * MCP support is rapidly expanding past Claude — the snippet
53
+ * shouldn't telegraph "this is Claude-only" when it works against
54
+ * any compliant client. The framework owns this string; updates
55
+ * ride along the `@llui/agent` package version.
56
+ */
57
+ function buildConnectSnippet(lapUrl, token) {
58
+ return (`Connect this AI assistant to the LLui app. Call the LLui MCP server's ` +
59
+ `\`connect_session\` tool with url=${JSON.stringify(lapUrl)} and ` +
60
+ `token=${JSON.stringify(token)}. ` +
61
+ `When you're working through a multi-step task, call \`narrate\` to ` +
62
+ `surface what you're doing — the user sees your prose in the app's ` +
63
+ `activity log alongside each action you dispatch. ` +
64
+ `(Some MCP clients namespace tools as ` +
65
+ `\`mcp__llui__connect_session\` / \`mcp__llui__narrate\` and load them ` +
66
+ `lazily — search the tool list if the bare names aren't immediately ` +
67
+ `available.)`);
68
+ }
26
69
  /** Component shape is [State, Effect[]] — consistent with @llui/components. */
27
70
  export function init(_opts) {
28
71
  return [
@@ -50,32 +93,11 @@ export function update(state, msg, opts = {}) {
50
93
  return [{ ...state, status: 'minting' }, [mintEffect]];
51
94
  }
52
95
  case 'MintSucceeded': {
53
- // The connect snippet has to work across every MCP surface.
54
- // Claude Desktop and similar clients expose MCP tools as bare
55
- // names (`connect_session`), but Claude Code (and other tool-list-
56
- // namespacing clients) emit them as `mcp__llui__connect_session`
57
- // and may defer-load them — so an LLM that searches its tool list
58
- // for a literal `connect_session` won't find it. Naming the LLui
59
- // MCP server explicitly (with its canonical `llui` install name,
60
- // matching the install docs) gives the model enough to resolve
61
- // the right tool on either platform; the parenthetical names the
62
- // edge case so a deferred-tool client doesn't bail out.
63
- //
64
- // Phrased generically (`AI assistant`, `Some MCP clients`) since
65
- // MCP support is rapidly expanding past Claude — the snippet
66
- // shouldn't telegraph "this is Claude-only" when it works against
67
- // any compliant client. The literal `mcp__llui__` prefix matches
68
- // the install command in `site/content/agents.md`; users who
69
- // renamed the server in their config can substitute their name.
70
96
  const pending = {
71
97
  token: msg.token,
72
98
  tid: msg.tid,
73
99
  lapUrl: msg.lapUrl,
74
- connectSnippet: `Connect this AI assistant to the LLui app. Call the LLui MCP server's ` +
75
- `\`connect_session\` tool with url=${JSON.stringify(msg.lapUrl)} and ` +
76
- `token=${JSON.stringify(msg.token)}. ` +
77
- `(Some MCP clients namespace tools as ` +
78
- `\`mcp__llui__connect_session\` and load them lazily — search the tool list if \`connect_session\` isn't immediately available.)`,
100
+ connectSnippet: buildConnectSnippet(msg.lapUrl, msg.token),
79
101
  expiresAt: msg.expiresAt,
80
102
  wsUrl: msg.wsUrl,
81
103
  };
@@ -113,19 +135,11 @@ export function update(state, msg, opts = {}) {
113
135
  // than to coordinate the race in the host.
114
136
  if (state.status !== 'idle')
115
137
  return [state, []];
116
- // Regenerate the connectSnippet so the user can re-paste if
117
- // their AI lost the original tool call (same shape as
118
- // MintSucceeded — the framework owns this string and updates
119
- // to it ride along the agent package version).
120
138
  const restored = {
121
139
  token: msg.token,
122
140
  tid: msg.tid,
123
141
  lapUrl: msg.lapUrl,
124
- connectSnippet: `Connect this AI assistant to the LLui app. Call the LLui MCP server's ` +
125
- `\`connect_session\` tool with url=${JSON.stringify(msg.lapUrl)} and ` +
126
- `token=${JSON.stringify(msg.token)}. ` +
127
- `(Some MCP clients namespace tools as ` +
128
- `\`mcp__llui__connect_session\` and load them lazily — search the tool list if \`connect_session\` isn't immediately available.)`,
142
+ connectSnippet: buildConnectSnippet(msg.lapUrl, msg.token),
129
143
  expiresAt: msg.expiresAt,
130
144
  wsUrl: msg.wsUrl,
131
145
  };
@@ -1 +1 @@
1
- {"version":3,"file":"agentConnect.js","sourceRoot":"","sources":["../../src/client/agentConnect.ts"],"names":[],"mappings":"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","sourcesContent":["import { tagSend, type Send } from '@llui/dom'\nimport type { AgentSession, AgentToken } from '../protocol.js'\nimport type { AgentEffect } from './effects.js'\n\nexport type AgentConnectStatus =\n | 'idle'\n | 'minting'\n | 'pending-claude'\n | 'active'\n | 'reconnecting'\n | 'failed'\n | 'error'\n\nexport type AgentConnectPendingToken = {\n token: AgentToken\n tid: string\n lapUrl: string\n /**\n * Natural-language connect instruction the user copies into Claude.\n * Includes URL, token, and the explicit `connect_session` tool\n * call. Works in any Claude client (Desktop, CC CLI, etc.) — the\n * Desktop-specific `/llui-connect` slash command is sugar over the\n * same tool call.\n */\n connectSnippet: string\n expiresAt: number\n /**\n * Cached so the auto-reconnect path can re-open the WS without\n * re-minting. The MintSucceeded → AgentOpenWS path stores it; the\n * RestoreSession path also fills it in. Cleared by `Disconnect`.\n */\n wsUrl: string\n}\n\nexport type AgentConnectState = {\n status: AgentConnectStatus\n pendingToken: AgentConnectPendingToken | null\n sessions: AgentSession[]\n resumable: AgentSession[]\n error: { code: string; detail: string } | null\n /**\n * Reconnect attempt counter. Incremented on each WS-close that\n * triggers an auto-reconnect; reset on `WsOpened` and on user\n * actions (`Disconnect`, fresh `Mint`). Drives the backoff schedule\n * (1s, 2s, 4s, 8s, 16s, 30s, 30s, …) and surfaces to UI as\n * \"reconnecting (attempt 3 / next in 4s)\".\n */\n reconnectAttempt: number\n /**\n * Total cumulative ms spent in `reconnecting` for the current\n * outage. Compared against `reconnectGiveUpMs` (effect-side option,\n * default 5 min) to decide when to surface `failed` to the user.\n * Reset whenever a WS opens successfully.\n */\n reconnectElapsedMs: number\n}\n\nexport type AgentConnectMsg =\n /** @intent(\"Mint a new agent token and open the pairing WebSocket\") */\n | { type: 'Mint' }\n /**\n * @humanOnly — internal: dispatched by the AgentMintRequest effect\n * handler when the mint endpoint replies success. Carries the token\n * and connection URLs into state.\n */\n | {\n type: 'MintSucceeded'\n token: AgentToken\n tid: string\n lapUrl: string\n wsUrl: string\n expiresAt: number\n }\n /** @humanOnly — internal: dispatched by the AgentMintRequest handler on failure. */\n | { type: 'MintFailed'; error: { code: string; detail: string } }\n /** @humanOnly — internal: WS adapter signalled the pairing socket is open. */\n | { type: 'WsOpened' }\n /** @humanOnly — internal: WS adapter signalled the pairing socket is closed. */\n | { type: 'WsClosed' }\n /** @humanOnly — internal: Claude bound the session via /agent/claim. */\n | { type: 'ActivatedByClaude' }\n /** @intent(\"Check which previously-issued agent sessions can be resumed\") */\n | { type: 'ResumeList'; tids: string[] }\n /** @humanOnly — internal: AgentResumeCheck effect handler returned the list. */\n | { type: 'ResumeListLoaded'; sessions: AgentSession[] }\n /** @intent(\"Resume an existing agent session by tid\") */\n | { type: 'Resume'; tid: string }\n /** @intent(\"Revoke an agent session by tid\") */\n | { type: 'Revoke'; tid: string }\n /** @intent(\"Dismiss the current agent connect error\") */\n | { type: 'ClearError' }\n /** @humanOnly — internal: AgentSessionsList effect handler returned the list. */\n | { type: 'SessionsLoaded'; sessions: AgentSession[] }\n /** @intent(\"Refresh the list of active agent sessions\") */\n | { type: 'RefreshSessions' }\n /**\n * @intent(\"Copy the agent connect snippet to the clipboard\")\n * Resolves the pendingToken's snippet in update() (state-reading is\n * what update() is for) and dispatches a clipboard-write effect.\n */\n | { type: 'CopyConnectSnippet' }\n /**\n * @humanOnly — internal: app boot dispatches this with credentials\n * read from sessionStorage to skip the mint round-trip after page\n * refresh. The agent's token (still alive on the server) keeps\n * working since we don't go through the rotate-on-resume path. The\n * reducer is idempotent against an in-flight Mint — only fires from\n * `idle`.\n */\n | {\n type: 'RestoreSession'\n token: AgentToken\n tid: string\n lapUrl: string\n wsUrl: string\n expiresAt: number\n }\n /**\n * @intent(\"Disconnect the active agent session and clear all\n * persisted credentials. Stops any in-flight reconnect attempt;\n * subsequent WS closures stay in `idle` instead of triggering\n * auto-reconnect. Use when the user explicitly clicks Disconnect\n * in the panel — for transient drops, do nothing and let the\n * reconnect loop run.\")\n */\n | { type: 'Disconnect' }\n /**\n * @humanOnly — internal: scheduler effect dispatched this when the\n * backoff timer fired. The reducer increments the attempt counter,\n * adds the just-elapsed delay to `reconnectElapsedMs`, and emits\n * `AgentOpenWS` with the cached pendingToken/wsUrl so the WS can\n * reattach without minting.\n */\n | { type: 'ReconnectAttempt'; elapsedMs: number }\n /**\n * @humanOnly — internal: scheduler effect dispatched this when the\n * give-up ceiling was reached without a successful WS open.\n * Reducer flips status to `failed` so the UI can surface a clear\n * error and offer a manual reconnect.\n */\n | { type: 'ReconnectGaveUp' }\n\n/**\n * Options threaded through `init()` and `update()`. `mintUrl` is\n * optional — when omitted the agent effect handler derives it from\n * `EffectHandlerHost.agentBasePath` (default `/agent` → `/agent/mint`).\n * Set explicitly only when the mint endpoint lives outside the\n * configured base path.\n */\nexport type AgentConnectInitOpts = { mintUrl?: string }\n\n/**\n * Backoff schedule for the auto-reconnect loop. Doubles starting at\n * 1s, caps at 30s. Translates `state.reconnectAttempt` into the next\n * delay; the effect handler schedules a `setTimeout` for that long\n * and dispatches `ReconnectAttempt` when it fires.\n *\n * Lives in the reducer so tests can pin the timings without poking\n * effect-handler internals; the constants are not exported because\n * tweaking them changes UX more than tweaks to the give-up ceiling.\n */\nconst RECONNECT_BASE_MS = 1000\nconst RECONNECT_CAP_MS = 30_000\nfunction reconnectDelayMs(attempt: number): number {\n const factor = Math.min(Math.pow(2, attempt), RECONNECT_CAP_MS / RECONNECT_BASE_MS)\n return Math.min(RECONNECT_BASE_MS * factor, RECONNECT_CAP_MS)\n}\n\n/**\n * Total cumulative wait, across all reconnect attempts, before the\n * loop gives up and transitions to `'failed'`. 5 minutes is long\n * enough to weather a brief server outage but short enough that a\n * permanently-down endpoint surfaces clearly to the user instead of\n * silently spinning.\n */\nconst RECONNECT_GIVE_UP_MS = 5 * 60 * 1000\n\n/** Component shape is [State, Effect[]] — consistent with @llui/components. */\nexport function init(_opts: AgentConnectInitOpts): [AgentConnectState, AgentEffect[]] {\n return [\n {\n status: 'idle',\n pendingToken: null,\n sessions: [],\n resumable: [],\n error: null,\n reconnectAttempt: 0,\n reconnectElapsedMs: 0,\n },\n [],\n ]\n}\n\nexport function update(\n state: AgentConnectState,\n msg: AgentConnectMsg,\n opts: AgentConnectInitOpts = {},\n): [AgentConnectState, AgentEffect[]] {\n switch (msg.type) {\n case 'Mint': {\n // mintUrl: undefined means \"let the effect handler derive it\n // from agentBasePath\". Only include the property when explicitly\n // set, so the effect's discriminated shape stays clean.\n const mintEffect: AgentEffect =\n opts.mintUrl !== undefined\n ? { type: 'AgentMintRequest', mintUrl: opts.mintUrl }\n : { type: 'AgentMintRequest' }\n return [{ ...state, status: 'minting' }, [mintEffect]]\n }\n case 'MintSucceeded': {\n // The connect snippet has to work across every MCP surface.\n // Claude Desktop and similar clients expose MCP tools as bare\n // names (`connect_session`), but Claude Code (and other tool-list-\n // namespacing clients) emit them as `mcp__llui__connect_session`\n // and may defer-load them — so an LLM that searches its tool list\n // for a literal `connect_session` won't find it. Naming the LLui\n // MCP server explicitly (with its canonical `llui` install name,\n // matching the install docs) gives the model enough to resolve\n // the right tool on either platform; the parenthetical names the\n // edge case so a deferred-tool client doesn't bail out.\n //\n // Phrased generically (`AI assistant`, `Some MCP clients`) since\n // MCP support is rapidly expanding past Claude — the snippet\n // shouldn't telegraph \"this is Claude-only\" when it works against\n // any compliant client. The literal `mcp__llui__` prefix matches\n // the install command in `site/content/agents.md`; users who\n // renamed the server in their config can substitute their name.\n const pending: AgentConnectPendingToken = {\n token: msg.token,\n tid: msg.tid,\n lapUrl: msg.lapUrl,\n connectSnippet:\n `Connect this AI assistant to the LLui app. Call the LLui MCP server's ` +\n `\\`connect_session\\` tool with url=${JSON.stringify(msg.lapUrl)} and ` +\n `token=${JSON.stringify(msg.token)}. ` +\n `(Some MCP clients namespace tools as ` +\n `\\`mcp__llui__connect_session\\` and load them lazily — search the tool list if \\`connect_session\\` isn't immediately available.)`,\n expiresAt: msg.expiresAt,\n wsUrl: msg.wsUrl,\n }\n return [\n {\n ...state,\n status: 'pending-claude',\n pendingToken: pending,\n error: null,\n reconnectAttempt: 0,\n reconnectElapsedMs: 0,\n },\n [\n { type: 'AgentOpenWS', token: msg.token, wsUrl: msg.wsUrl },\n // Persist alongside opening the WS so the host can store the\n // credentials in sessionStorage; on page refresh, app boot\n // dispatches `RestoreSession` with the same shape and we\n // re-enter the same state without re-minting.\n {\n type: 'AgentSessionPersist',\n token: msg.token,\n tid: msg.tid,\n lapUrl: msg.lapUrl,\n wsUrl: msg.wsUrl,\n expiresAt: msg.expiresAt,\n },\n ],\n ]\n }\n case 'RestoreSession': {\n // Idempotent guard: only fires from idle. A racing Mint click\n // would already have moved us to `minting` — restoring on top\n // would clobber the in-flight pending state with stale\n // credentials read from sessionStorage. Easier to no-op here\n // than to coordinate the race in the host.\n if (state.status !== 'idle') return [state, []]\n // Regenerate the connectSnippet so the user can re-paste if\n // their AI lost the original tool call (same shape as\n // MintSucceeded — the framework owns this string and updates\n // to it ride along the agent package version).\n const restored: AgentConnectPendingToken = {\n token: msg.token,\n tid: msg.tid,\n lapUrl: msg.lapUrl,\n connectSnippet:\n `Connect this AI assistant to the LLui app. Call the LLui MCP server's ` +\n `\\`connect_session\\` tool with url=${JSON.stringify(msg.lapUrl)} and ` +\n `token=${JSON.stringify(msg.token)}. ` +\n `(Some MCP clients namespace tools as ` +\n `\\`mcp__llui__connect_session\\` and load them lazily — search the tool list if \\`connect_session\\` isn't immediately available.)`,\n expiresAt: msg.expiresAt,\n wsUrl: msg.wsUrl,\n }\n return [\n {\n ...state,\n status: 'pending-claude',\n pendingToken: restored,\n error: null,\n reconnectAttempt: 0,\n reconnectElapsedMs: 0,\n },\n [{ type: 'AgentOpenWS', token: msg.token, wsUrl: msg.wsUrl }],\n ]\n }\n case 'MintFailed':\n return [{ ...state, status: 'error', error: msg.error }, []]\n case 'WsOpened': {\n // WS is open but Claude hasn't bound yet; stay at pending-claude.\n // If we were `reconnecting`, this is a successful reattach —\n // back to pending-claude and reset the attempt counters.\n if (state.status === 'reconnecting') {\n return [\n { ...state, status: 'pending-claude', reconnectAttempt: 0, reconnectElapsedMs: 0 },\n [],\n ]\n }\n return [state, []]\n }\n case 'WsClosed': {\n // Three cases:\n // 1. We had no pendingToken (already idle / pre-mint) → no-op.\n // 2. Status is `idle` or `failed` (Disconnect already cleared,\n // or we previously gave up) → no-op so a delayed close\n // event after Disconnect doesn't accidentally restart the\n // loop.\n // 3. We're connected/connecting and the close was unsolicited\n // → schedule a reconnect with backoff.\n if (state.pendingToken === null) return [{ ...state, status: 'idle' }, []]\n if (state.status === 'idle' || state.status === 'failed') return [state, []]\n const delayMs = reconnectDelayMs(state.reconnectAttempt)\n return [\n { ...state, status: 'reconnecting', error: null },\n [{ type: 'AgentReconnectSchedule', delayMs }],\n ]\n }\n case 'ReconnectAttempt': {\n // Backoff timer fired. If the user disconnected in the gap, we\n // moved to idle and ignore. Otherwise, increment attempt + add\n // the elapsed delay to the cumulative window. Past the give-up\n // ceiling, transition to `failed` so the UI can offer a manual\n // reconnect; otherwise re-open the WS with the cached\n // credentials (no mint, same token — the server's grace window\n // is what makes this transparent to the agent).\n if (state.status !== 'reconnecting' || state.pendingToken === null) {\n return [state, []]\n }\n const newElapsed = state.reconnectElapsedMs + msg.elapsedMs\n if (newElapsed >= RECONNECT_GIVE_UP_MS) {\n return [{ ...state, status: 'failed', reconnectElapsedMs: newElapsed }, []]\n }\n return [\n {\n ...state,\n reconnectAttempt: state.reconnectAttempt + 1,\n reconnectElapsedMs: newElapsed,\n },\n [\n {\n type: 'AgentOpenWS',\n token: state.pendingToken.token,\n wsUrl: state.pendingToken.wsUrl,\n },\n ],\n ]\n }\n case 'ReconnectGaveUp':\n return [{ ...state, status: 'failed' }, []]\n case 'Disconnect': {\n // User-initiated. Revoke the active tid (server kills the\n // pairing), wipe the persisted credentials so a refresh can't\n // restore them, and zero the reconnect counters so any in-\n // flight backoff timer that fires post-disconnect becomes a\n // no-op (the status guard in `ReconnectAttempt` keeps it from\n // re-opening the WS).\n const tid = state.pendingToken?.tid\n const effects: AgentEffect[] = []\n if (tid !== undefined) effects.push({ type: 'AgentRevoke', tid })\n effects.push({ type: 'AgentSessionClear' })\n effects.push({ type: 'AgentCloseWS' })\n return [\n {\n ...state,\n status: 'idle',\n pendingToken: null,\n error: null,\n reconnectAttempt: 0,\n reconnectElapsedMs: 0,\n },\n effects,\n ]\n }\n case 'ActivatedByClaude':\n return [{ ...state, status: 'active' }, []]\n case 'ResumeList':\n return [state, [{ type: 'AgentResumeCheck', tids: msg.tids }]]\n case 'ResumeListLoaded':\n return [{ ...state, resumable: msg.sessions }, []]\n case 'Resume':\n return [state, [{ type: 'AgentResumeClaim', tid: msg.tid }]]\n case 'Revoke': {\n // Optimistically remove from sessions + resumable. If the\n // revoked tid matches the currently-pending session, also fire\n // AgentSessionClear so the host wipes its persisted credentials\n // — otherwise a refresh would try to RestoreSession with a\n // server-side-revoked token and end up at an auth-failed WS.\n const isActiveTid = state.pendingToken !== null && state.pendingToken.tid === msg.tid\n const effects: AgentEffect[] = [{ type: 'AgentRevoke', tid: msg.tid }]\n if (isActiveTid) effects.push({ type: 'AgentSessionClear' })\n return [\n {\n ...state,\n sessions: state.sessions.filter((s) => s.tid !== msg.tid),\n resumable: state.resumable.filter((s) => s.tid !== msg.tid),\n },\n effects,\n ]\n }\n case 'ClearError':\n return [{ ...state, error: null }, []]\n case 'SessionsLoaded':\n return [{ ...state, sessions: msg.sessions }, []]\n case 'RefreshSessions':\n return [state, [{ type: 'AgentSessionsList' }]]\n case 'CopyConnectSnippet': {\n // No-op when there's no pending token — the button's\n // `disabled` accessor already gates the click, but we accept\n // the message for runtime safety.\n if (!state.pendingToken) return [state, []]\n return [state, [{ type: 'AgentClipboardWrite', text: state.pendingToken.connectSnippet }]]\n }\n }\n}\n\n// ── Connect helper ────────────────────────────────────────────────────────────\n\nexport type AgentConnectConnectOptions = {\n id?: string // optional DOM id prefix\n}\n\n/**\n * Static prop bag with reactive accessors. Mirrors the @llui/components\n * pattern (e.g. `dialog.connect`): callers spread bag keys directly\n * into element helpers, and function-valued props re-evaluate per\n * binding-mask hit. The previous shape — `(state) => bag` — required\n * callers to wrap every prop access in their own arrow, which the\n * documented usage didn't do (and silently produced `undefined` props\n * when spread).\n */\nexport type ConnectBag<S> = {\n root: { 'data-scope': 'agent-connect'; 'data-state': (s: S) => AgentConnectStatus }\n mintTrigger: { onClick: () => void; disabled: (s: S) => boolean }\n pendingTokenBox: { 'data-part': 'pending-token'; 'data-visible': (s: S) => boolean }\n copyConnectSnippetButton: { onClick: () => void; disabled: (s: S) => boolean }\n sessionsList: { 'data-part': 'sessions-list' }\n sessionItem: (tid: string) => { 'data-part': 'session-item'; 'data-tid': string }\n revokeButton: (tid: string) => { onClick: () => void }\n resumeBanner: { 'data-part': 'resume-banner'; 'data-visible': (s: S) => boolean }\n resumeItem: (tid: string) => { 'data-part': 'resume-item'; 'data-tid': string }\n resumeButton: (tid: string) => { onClick: () => void }\n dismissButton: (tid: string) => { onClick: () => void }\n error: {\n 'data-part': 'error'\n 'data-visible': (s: S) => boolean\n onClick: () => void\n }\n}\n\n/**\n * Builds prop bags for the view. Static-bag-with-reactive-accessors\n * shape (matches the @llui/components convention); spread directly\n * into element helpers.\n */\nexport function connect<S>(\n get: (s: S) => AgentConnectState,\n send: Send<AgentConnectMsg>,\n _opts: AgentConnectConnectOptions = {},\n): ConnectBag<S> {\n return {\n root: {\n 'data-scope': 'agent-connect',\n 'data-state': (s) => get(s).status,\n },\n mintTrigger: {\n onClick: tagSend(send, ['Mint'], () => send({ type: 'Mint' })),\n disabled: (s) => {\n const cs = get(s)\n return cs.status === 'minting' || cs.status === 'pending-claude' || cs.status === 'active'\n },\n },\n pendingTokenBox: {\n 'data-part': 'pending-token',\n 'data-visible': (s) => get(s).pendingToken !== null,\n },\n copyConnectSnippetButton: {\n // The handler reads state at click time via the Msg/effect path:\n // CopyConnectSnippet → update() reads pendingToken.connectSnippet\n // → effect AgentClipboardWrite writes to navigator.clipboard.\n // Routing through update() keeps state reads out of event\n // handlers, which is what makes the static-bag-with-reactive-\n // accessors shape work cleanly.\n onClick: tagSend(send, ['CopyConnectSnippet'], () => send({ type: 'CopyConnectSnippet' })),\n disabled: (s) => get(s).pendingToken === null,\n },\n sessionsList: { 'data-part': 'sessions-list' },\n sessionItem: (tid) => ({ 'data-part': 'session-item', 'data-tid': tid }),\n revokeButton: (tid) => ({\n onClick: tagSend(send, ['Revoke'], () => send({ type: 'Revoke', tid })),\n }),\n resumeBanner: {\n 'data-part': 'resume-banner',\n 'data-visible': (s) => get(s).resumable.length > 0,\n },\n resumeItem: (tid) => ({ 'data-part': 'resume-item', 'data-tid': tid }),\n resumeButton: (tid) => ({\n onClick: tagSend(send, ['Resume'], () => send({ type: 'Resume', tid })),\n }),\n dismissButton: (tid) => ({\n // For dismiss, we currently just remove the resumable record\n // locally. A \"dismiss forever\" flag could land in a follow-up;\n // for v1, dismiss is a client-side-only state prune by reusing\n // the Revoke Msg path with intent-split; for now emit Revoke\n // which both revokes server-side AND removes locally.\n onClick: tagSend(send, ['Revoke'], () => send({ type: 'Revoke', tid })),\n }),\n error: {\n 'data-part': 'error',\n 'data-visible': (s) => get(s).error !== null,\n onClick: tagSend(send, ['ClearError'], () => send({ type: 'ClearError' })),\n },\n }\n}\n"]}
1
+ {"version":3,"file":"agentConnect.js","sourceRoot":"","sources":["../../src/client/agentConnect.ts"],"names":[],"mappings":"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","sourcesContent":["import { tagSend, type Send } from '@llui/dom'\nimport type { AgentSession, AgentToken } from '../protocol.js'\nimport type { AgentEffect } from './effects.js'\n\nexport type AgentConnectStatus =\n | 'idle'\n | 'minting'\n | 'pending-claude'\n | 'active'\n | 'reconnecting'\n | 'failed'\n | 'error'\n\nexport type AgentConnectPendingToken = {\n token: AgentToken\n tid: string\n lapUrl: string\n /**\n * Natural-language connect instruction the user copies into Claude.\n * Includes URL, token, and the explicit `connect_session` tool\n * call. Works in any Claude client (Desktop, CC CLI, etc.) — the\n * Desktop-specific `/llui-connect` slash command is sugar over the\n * same tool call.\n */\n connectSnippet: string\n expiresAt: number\n /**\n * Cached so the auto-reconnect path can re-open the WS without\n * re-minting. The MintSucceeded → AgentOpenWS path stores it; the\n * RestoreSession path also fills it in. Cleared by `Disconnect`.\n */\n wsUrl: string\n}\n\nexport type AgentConnectState = {\n status: AgentConnectStatus\n pendingToken: AgentConnectPendingToken | null\n sessions: AgentSession[]\n resumable: AgentSession[]\n error: { code: string; detail: string } | null\n /**\n * Reconnect attempt counter. Incremented on each WS-close that\n * triggers an auto-reconnect; reset on `WsOpened` and on user\n * actions (`Disconnect`, fresh `Mint`). Drives the backoff schedule\n * (1s, 2s, 4s, 8s, 16s, 30s, 30s, …) and surfaces to UI as\n * \"reconnecting (attempt 3 / next in 4s)\".\n */\n reconnectAttempt: number\n /**\n * Total cumulative ms spent in `reconnecting` for the current\n * outage. Compared against `reconnectGiveUpMs` (effect-side option,\n * default 5 min) to decide when to surface `failed` to the user.\n * Reset whenever a WS opens successfully.\n */\n reconnectElapsedMs: number\n}\n\nexport type AgentConnectMsg =\n /** @intent(\"Mint a new agent token and open the pairing WebSocket\") */\n | { type: 'Mint' }\n /**\n * @humanOnly — internal: dispatched by the AgentMintRequest effect\n * handler when the mint endpoint replies success. Carries the token\n * and connection URLs into state.\n */\n | {\n type: 'MintSucceeded'\n token: AgentToken\n tid: string\n lapUrl: string\n wsUrl: string\n expiresAt: number\n }\n /** @humanOnly — internal: dispatched by the AgentMintRequest handler on failure. */\n | { type: 'MintFailed'; error: { code: string; detail: string } }\n /** @humanOnly — internal: WS adapter signalled the pairing socket is open. */\n | { type: 'WsOpened' }\n /** @humanOnly — internal: WS adapter signalled the pairing socket is closed. */\n | { type: 'WsClosed' }\n /** @humanOnly — internal: Claude bound the session via /agent/claim. */\n | { type: 'ActivatedByClaude' }\n /** @intent(\"Check which previously-issued agent sessions can be resumed\") */\n | { type: 'ResumeList'; tids: string[] }\n /** @humanOnly — internal: AgentResumeCheck effect handler returned the list. */\n | { type: 'ResumeListLoaded'; sessions: AgentSession[] }\n /** @intent(\"Resume an existing agent session by tid\") */\n | { type: 'Resume'; tid: string }\n /** @intent(\"Revoke an agent session by tid\") */\n | { type: 'Revoke'; tid: string }\n /** @intent(\"Dismiss the current agent connect error\") */\n | { type: 'ClearError' }\n /** @humanOnly — internal: AgentSessionsList effect handler returned the list. */\n | { type: 'SessionsLoaded'; sessions: AgentSession[] }\n /** @intent(\"Refresh the list of active agent sessions\") */\n | { type: 'RefreshSessions' }\n /**\n * @intent(\"Copy the agent connect snippet to the clipboard\")\n * Resolves the pendingToken's snippet in update() (state-reading is\n * what update() is for) and dispatches a clipboard-write effect.\n */\n | { type: 'CopyConnectSnippet' }\n /**\n * @humanOnly — internal: app boot dispatches this with credentials\n * read from sessionStorage to skip the mint round-trip after page\n * refresh. The agent's token (still alive on the server) keeps\n * working since we don't go through the rotate-on-resume path. The\n * reducer is idempotent against an in-flight Mint — only fires from\n * `idle`.\n */\n | {\n type: 'RestoreSession'\n token: AgentToken\n tid: string\n lapUrl: string\n wsUrl: string\n expiresAt: number\n }\n /**\n * @intent(\"Disconnect the active agent session and clear all\n * persisted credentials. Stops any in-flight reconnect attempt;\n * subsequent WS closures stay in `idle` instead of triggering\n * auto-reconnect. Use when the user explicitly clicks Disconnect\n * in the panel — for transient drops, do nothing and let the\n * reconnect loop run.\")\n */\n | { type: 'Disconnect' }\n /**\n * @humanOnly — internal: scheduler effect dispatched this when the\n * backoff timer fired. The reducer increments the attempt counter,\n * adds the just-elapsed delay to `reconnectElapsedMs`, and emits\n * `AgentOpenWS` with the cached pendingToken/wsUrl so the WS can\n * reattach without minting.\n */\n | { type: 'ReconnectAttempt'; elapsedMs: number }\n /**\n * @humanOnly — internal: scheduler effect dispatched this when the\n * give-up ceiling was reached without a successful WS open.\n * Reducer flips status to `failed` so the UI can surface a clear\n * error and offer a manual reconnect.\n */\n | { type: 'ReconnectGaveUp' }\n\n/**\n * Options threaded through `init()` and `update()`. `mintUrl` is\n * optional — when omitted the agent effect handler derives it from\n * `EffectHandlerHost.agentBasePath` (default `/agent` → `/agent/mint`).\n * Set explicitly only when the mint endpoint lives outside the\n * configured base path.\n */\nexport type AgentConnectInitOpts = { mintUrl?: string }\n\n/**\n * Backoff schedule for the auto-reconnect loop. Doubles starting at\n * 1s, caps at 30s. Translates `state.reconnectAttempt` into the next\n * delay; the effect handler schedules a `setTimeout` for that long\n * and dispatches `ReconnectAttempt` when it fires.\n *\n * Lives in the reducer so tests can pin the timings without poking\n * effect-handler internals; the constants are not exported because\n * tweaking them changes UX more than tweaks to the give-up ceiling.\n */\nconst RECONNECT_BASE_MS = 1000\nconst RECONNECT_CAP_MS = 30_000\nfunction reconnectDelayMs(attempt: number): number {\n const factor = Math.min(Math.pow(2, attempt), RECONNECT_CAP_MS / RECONNECT_BASE_MS)\n return Math.min(RECONNECT_BASE_MS * factor, RECONNECT_CAP_MS)\n}\n\n/**\n * Total cumulative wait, across all reconnect attempts, before the\n * loop gives up and transitions to `'failed'`. 5 minutes is long\n * enough to weather a brief server outage but short enough that a\n * permanently-down endpoint surfaces clearly to the user instead of\n * silently spinning.\n */\nconst RECONNECT_GIVE_UP_MS = 5 * 60 * 1000\n\n/**\n * Build the user-pasted connect snippet that lands in the LLM's chat\n * window. Three things in one paragraph that an LLM will follow on\n * first read:\n *\n * 1. Tell the LLM which tool to call to bind the session\n * (`connect_session`).\n *\n * 2. Encourage `narrate` for surfacing intent during multi-step\n * actions. Without the nudge the LLM tends to dispatch silently;\n * with it, the user sees a running commentary in the app's\n * activity log alongside the action entries. One-way channel\n * (LLM → user); the conversation itself stays in the LLM's own\n * chat window.\n *\n * 3. Survive tool-namespacing edge cases: Claude Desktop exposes MCP\n * tools as bare names (`connect_session`) but Claude Code and\n * other namespacing clients emit them as\n * `mcp__llui__connect_session` and may defer-load them — so an\n * LLM that searches its tool list for a literal name won't find\n * it. Naming the LLui MCP server explicitly (with its canonical\n * install name `llui`) gives the model enough to resolve the\n * right tool on either platform; the parenthetical surfaces the\n * edge case for deferred-tool clients.\n *\n * Phrased generically (`AI assistant`, `Some MCP clients`) since\n * MCP support is rapidly expanding past Claude — the snippet\n * shouldn't telegraph \"this is Claude-only\" when it works against\n * any compliant client. The framework owns this string; updates\n * ride along the `@llui/agent` package version.\n */\nfunction buildConnectSnippet(lapUrl: string, token: string): string {\n return (\n `Connect this AI assistant to the LLui app. Call the LLui MCP server's ` +\n `\\`connect_session\\` tool with url=${JSON.stringify(lapUrl)} and ` +\n `token=${JSON.stringify(token)}. ` +\n `When you're working through a multi-step task, call \\`narrate\\` to ` +\n `surface what you're doing — the user sees your prose in the app's ` +\n `activity log alongside each action you dispatch. ` +\n `(Some MCP clients namespace tools as ` +\n `\\`mcp__llui__connect_session\\` / \\`mcp__llui__narrate\\` and load them ` +\n `lazily — search the tool list if the bare names aren't immediately ` +\n `available.)`\n )\n}\n\n/** Component shape is [State, Effect[]] — consistent with @llui/components. */\nexport function init(_opts: AgentConnectInitOpts): [AgentConnectState, AgentEffect[]] {\n return [\n {\n status: 'idle',\n pendingToken: null,\n sessions: [],\n resumable: [],\n error: null,\n reconnectAttempt: 0,\n reconnectElapsedMs: 0,\n },\n [],\n ]\n}\n\nexport function update(\n state: AgentConnectState,\n msg: AgentConnectMsg,\n opts: AgentConnectInitOpts = {},\n): [AgentConnectState, AgentEffect[]] {\n switch (msg.type) {\n case 'Mint': {\n // mintUrl: undefined means \"let the effect handler derive it\n // from agentBasePath\". Only include the property when explicitly\n // set, so the effect's discriminated shape stays clean.\n const mintEffect: AgentEffect =\n opts.mintUrl !== undefined\n ? { type: 'AgentMintRequest', mintUrl: opts.mintUrl }\n : { type: 'AgentMintRequest' }\n return [{ ...state, status: 'minting' }, [mintEffect]]\n }\n case 'MintSucceeded': {\n const pending: AgentConnectPendingToken = {\n token: msg.token,\n tid: msg.tid,\n lapUrl: msg.lapUrl,\n connectSnippet: buildConnectSnippet(msg.lapUrl, msg.token),\n expiresAt: msg.expiresAt,\n wsUrl: msg.wsUrl,\n }\n return [\n {\n ...state,\n status: 'pending-claude',\n pendingToken: pending,\n error: null,\n reconnectAttempt: 0,\n reconnectElapsedMs: 0,\n },\n [\n { type: 'AgentOpenWS', token: msg.token, wsUrl: msg.wsUrl },\n // Persist alongside opening the WS so the host can store the\n // credentials in sessionStorage; on page refresh, app boot\n // dispatches `RestoreSession` with the same shape and we\n // re-enter the same state without re-minting.\n {\n type: 'AgentSessionPersist',\n token: msg.token,\n tid: msg.tid,\n lapUrl: msg.lapUrl,\n wsUrl: msg.wsUrl,\n expiresAt: msg.expiresAt,\n },\n ],\n ]\n }\n case 'RestoreSession': {\n // Idempotent guard: only fires from idle. A racing Mint click\n // would already have moved us to `minting` — restoring on top\n // would clobber the in-flight pending state with stale\n // credentials read from sessionStorage. Easier to no-op here\n // than to coordinate the race in the host.\n if (state.status !== 'idle') return [state, []]\n const restored: AgentConnectPendingToken = {\n token: msg.token,\n tid: msg.tid,\n lapUrl: msg.lapUrl,\n connectSnippet: buildConnectSnippet(msg.lapUrl, msg.token),\n expiresAt: msg.expiresAt,\n wsUrl: msg.wsUrl,\n }\n return [\n {\n ...state,\n status: 'pending-claude',\n pendingToken: restored,\n error: null,\n reconnectAttempt: 0,\n reconnectElapsedMs: 0,\n },\n [{ type: 'AgentOpenWS', token: msg.token, wsUrl: msg.wsUrl }],\n ]\n }\n case 'MintFailed':\n return [{ ...state, status: 'error', error: msg.error }, []]\n case 'WsOpened': {\n // WS is open but Claude hasn't bound yet; stay at pending-claude.\n // If we were `reconnecting`, this is a successful reattach —\n // back to pending-claude and reset the attempt counters.\n if (state.status === 'reconnecting') {\n return [\n { ...state, status: 'pending-claude', reconnectAttempt: 0, reconnectElapsedMs: 0 },\n [],\n ]\n }\n return [state, []]\n }\n case 'WsClosed': {\n // Three cases:\n // 1. We had no pendingToken (already idle / pre-mint) → no-op.\n // 2. Status is `idle` or `failed` (Disconnect already cleared,\n // or we previously gave up) → no-op so a delayed close\n // event after Disconnect doesn't accidentally restart the\n // loop.\n // 3. We're connected/connecting and the close was unsolicited\n // → schedule a reconnect with backoff.\n if (state.pendingToken === null) return [{ ...state, status: 'idle' }, []]\n if (state.status === 'idle' || state.status === 'failed') return [state, []]\n const delayMs = reconnectDelayMs(state.reconnectAttempt)\n return [\n { ...state, status: 'reconnecting', error: null },\n [{ type: 'AgentReconnectSchedule', delayMs }],\n ]\n }\n case 'ReconnectAttempt': {\n // Backoff timer fired. If the user disconnected in the gap, we\n // moved to idle and ignore. Otherwise, increment attempt + add\n // the elapsed delay to the cumulative window. Past the give-up\n // ceiling, transition to `failed` so the UI can offer a manual\n // reconnect; otherwise re-open the WS with the cached\n // credentials (no mint, same token — the server's grace window\n // is what makes this transparent to the agent).\n if (state.status !== 'reconnecting' || state.pendingToken === null) {\n return [state, []]\n }\n const newElapsed = state.reconnectElapsedMs + msg.elapsedMs\n if (newElapsed >= RECONNECT_GIVE_UP_MS) {\n return [{ ...state, status: 'failed', reconnectElapsedMs: newElapsed }, []]\n }\n return [\n {\n ...state,\n reconnectAttempt: state.reconnectAttempt + 1,\n reconnectElapsedMs: newElapsed,\n },\n [\n {\n type: 'AgentOpenWS',\n token: state.pendingToken.token,\n wsUrl: state.pendingToken.wsUrl,\n },\n ],\n ]\n }\n case 'ReconnectGaveUp':\n return [{ ...state, status: 'failed' }, []]\n case 'Disconnect': {\n // User-initiated. Revoke the active tid (server kills the\n // pairing), wipe the persisted credentials so a refresh can't\n // restore them, and zero the reconnect counters so any in-\n // flight backoff timer that fires post-disconnect becomes a\n // no-op (the status guard in `ReconnectAttempt` keeps it from\n // re-opening the WS).\n const tid = state.pendingToken?.tid\n const effects: AgentEffect[] = []\n if (tid !== undefined) effects.push({ type: 'AgentRevoke', tid })\n effects.push({ type: 'AgentSessionClear' })\n effects.push({ type: 'AgentCloseWS' })\n return [\n {\n ...state,\n status: 'idle',\n pendingToken: null,\n error: null,\n reconnectAttempt: 0,\n reconnectElapsedMs: 0,\n },\n effects,\n ]\n }\n case 'ActivatedByClaude':\n return [{ ...state, status: 'active' }, []]\n case 'ResumeList':\n return [state, [{ type: 'AgentResumeCheck', tids: msg.tids }]]\n case 'ResumeListLoaded':\n return [{ ...state, resumable: msg.sessions }, []]\n case 'Resume':\n return [state, [{ type: 'AgentResumeClaim', tid: msg.tid }]]\n case 'Revoke': {\n // Optimistically remove from sessions + resumable. If the\n // revoked tid matches the currently-pending session, also fire\n // AgentSessionClear so the host wipes its persisted credentials\n // — otherwise a refresh would try to RestoreSession with a\n // server-side-revoked token and end up at an auth-failed WS.\n const isActiveTid = state.pendingToken !== null && state.pendingToken.tid === msg.tid\n const effects: AgentEffect[] = [{ type: 'AgentRevoke', tid: msg.tid }]\n if (isActiveTid) effects.push({ type: 'AgentSessionClear' })\n return [\n {\n ...state,\n sessions: state.sessions.filter((s) => s.tid !== msg.tid),\n resumable: state.resumable.filter((s) => s.tid !== msg.tid),\n },\n effects,\n ]\n }\n case 'ClearError':\n return [{ ...state, error: null }, []]\n case 'SessionsLoaded':\n return [{ ...state, sessions: msg.sessions }, []]\n case 'RefreshSessions':\n return [state, [{ type: 'AgentSessionsList' }]]\n case 'CopyConnectSnippet': {\n // No-op when there's no pending token — the button's\n // `disabled` accessor already gates the click, but we accept\n // the message for runtime safety.\n if (!state.pendingToken) return [state, []]\n return [state, [{ type: 'AgentClipboardWrite', text: state.pendingToken.connectSnippet }]]\n }\n }\n}\n\n// ── Connect helper ────────────────────────────────────────────────────────────\n\nexport type AgentConnectConnectOptions = {\n id?: string // optional DOM id prefix\n}\n\n/**\n * Static prop bag with reactive accessors. Mirrors the @llui/components\n * pattern (e.g. `dialog.connect`): callers spread bag keys directly\n * into element helpers, and function-valued props re-evaluate per\n * binding-mask hit. The previous shape — `(state) => bag` — required\n * callers to wrap every prop access in their own arrow, which the\n * documented usage didn't do (and silently produced `undefined` props\n * when spread).\n */\nexport type ConnectBag<S> = {\n root: { 'data-scope': 'agent-connect'; 'data-state': (s: S) => AgentConnectStatus }\n mintTrigger: { onClick: () => void; disabled: (s: S) => boolean }\n pendingTokenBox: { 'data-part': 'pending-token'; 'data-visible': (s: S) => boolean }\n copyConnectSnippetButton: { onClick: () => void; disabled: (s: S) => boolean }\n sessionsList: { 'data-part': 'sessions-list' }\n sessionItem: (tid: string) => { 'data-part': 'session-item'; 'data-tid': string }\n revokeButton: (tid: string) => { onClick: () => void }\n resumeBanner: { 'data-part': 'resume-banner'; 'data-visible': (s: S) => boolean }\n resumeItem: (tid: string) => { 'data-part': 'resume-item'; 'data-tid': string }\n resumeButton: (tid: string) => { onClick: () => void }\n dismissButton: (tid: string) => { onClick: () => void }\n error: {\n 'data-part': 'error'\n 'data-visible': (s: S) => boolean\n onClick: () => void\n }\n}\n\n/**\n * Builds prop bags for the view. Static-bag-with-reactive-accessors\n * shape (matches the @llui/components convention); spread directly\n * into element helpers.\n */\nexport function connect<S>(\n get: (s: S) => AgentConnectState,\n send: Send<AgentConnectMsg>,\n _opts: AgentConnectConnectOptions = {},\n): ConnectBag<S> {\n return {\n root: {\n 'data-scope': 'agent-connect',\n 'data-state': (s) => get(s).status,\n },\n mintTrigger: {\n onClick: tagSend(send, ['Mint'], () => send({ type: 'Mint' })),\n disabled: (s) => {\n const cs = get(s)\n return cs.status === 'minting' || cs.status === 'pending-claude' || cs.status === 'active'\n },\n },\n pendingTokenBox: {\n 'data-part': 'pending-token',\n 'data-visible': (s) => get(s).pendingToken !== null,\n },\n copyConnectSnippetButton: {\n // The handler reads state at click time via the Msg/effect path:\n // CopyConnectSnippet → update() reads pendingToken.connectSnippet\n // → effect AgentClipboardWrite writes to navigator.clipboard.\n // Routing through update() keeps state reads out of event\n // handlers, which is what makes the static-bag-with-reactive-\n // accessors shape work cleanly.\n onClick: tagSend(send, ['CopyConnectSnippet'], () => send({ type: 'CopyConnectSnippet' })),\n disabled: (s) => get(s).pendingToken === null,\n },\n sessionsList: { 'data-part': 'sessions-list' },\n sessionItem: (tid) => ({ 'data-part': 'session-item', 'data-tid': tid }),\n revokeButton: (tid) => ({\n onClick: tagSend(send, ['Revoke'], () => send({ type: 'Revoke', tid })),\n }),\n resumeBanner: {\n 'data-part': 'resume-banner',\n 'data-visible': (s) => get(s).resumable.length > 0,\n },\n resumeItem: (tid) => ({ 'data-part': 'resume-item', 'data-tid': tid }),\n resumeButton: (tid) => ({\n onClick: tagSend(send, ['Resume'], () => send({ type: 'Resume', tid })),\n }),\n dismissButton: (tid) => ({\n // For dismiss, we currently just remove the resumable record\n // locally. A \"dismiss forever\" flag could land in a follow-up;\n // for v1, dismiss is a client-side-only state prune by reusing\n // the Revoke Msg path with intent-split; for now emit Revoke\n // which both revokes server-side AND removes locally.\n onClick: tagSend(send, ['Revoke'], () => send({ type: 'Revoke', tid })),\n }),\n error: {\n 'data-part': 'error',\n 'data-visible': (s) => get(s).error !== null,\n onClick: tagSend(send, ['ClearError'], () => send({ type: 'ClearError' })),\n },\n }\n}\n"]}
@@ -12,27 +12,6 @@ export type EffectHandlerHost = {
12
12
  * provide this; hosts that don't can leave it unset.
13
13
  */
14
14
  wrapAgentAttention?(m: unknown): unknown;
15
- /**
16
- * Wraps an agentChat msg into an app-Msg. Required for hosts
17
- * wiring the chat composer — the `AgentChatSendInput` handler
18
- * dispatches `SubmitComplete` back into the slice via this wrapper
19
- * after the frame send completes (success or failure), so the
20
- * input field re-enables. Hosts that don't render chat leave it
21
- * unset and the chat-send effect no-ops (the agentChat slice in
22
- * that case has no live presence to begin with).
23
- */
24
- wrapAgentChat?(m: unknown): unknown;
25
- /**
26
- * Active WS client (if any) — set lazily by the factory after
27
- * `openWs` resolves a connection. The chat-send handler reads
28
- * `submitUserInput` from here. Marked optional because the
29
- * handler sits across the connection lifecycle: pre-WS / post-close
30
- * `submitUserInput` is null and the handler degrades gracefully
31
- * (still dispatches SubmitComplete so the UI re-enables).
32
- */
33
- getWsClient?: () => {
34
- submitUserInput(text: string, at?: number): void;
35
- } | null;
36
15
  /** Called for AgentForwardMsg — the payload is re-dispatched via send. */
37
16
  forward(payload: unknown): void;
38
17
  /** fetch for HTTP effects; override in tests. */
@@ -1 +1 @@
1
- {"version":3,"file":"effect-handler.d.ts","sourceRoot":"","sources":["../../src/client/effect-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAO/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAEvD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;IACxB,iDAAiD;IACjD,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IACrC;;;;;;OAMG;IACH,kBAAkB,CAAC,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IACxC;;;;;;;;OAQG;IACH,aAAa,CAAC,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IACnC;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM;QAAE,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;IAC/E,0EAA0E;IAC1E,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IAC/B,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,yDAAyD;IACzD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1C,OAAO,IAAI,IAAI,CAAA;IACf;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAA;IAC3C;;;;;;;;;;;;;OAaG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAID;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,IAG5B,QAAQ,WAAW,KAAG,OAAO,CAAC,IAAI,CAAC,CA8CjE"}
1
+ {"version":3,"file":"effect-handler.d.ts","sourceRoot":"","sources":["../../src/client/effect-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAO/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAEvD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;IACxB,iDAAiD;IACjD,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IACrC;;;;;;OAMG;IACH,kBAAkB,CAAC,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IACxC,0EAA0E;IAC1E,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IAC/B,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,yDAAyD;IACzD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1C,OAAO,IAAI,IAAI,CAAA;IACf;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAA;IAC3C;;;;;;;;;;;;;OAaG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAID;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,IAG5B,QAAQ,WAAW,KAAG,OAAO,CAAC,IAAI,CAAC,CA4CjE"}
@@ -51,8 +51,6 @@ export function createEffectHandler(host) {
51
51
  return handleReconnectSchedule(host, effect);
52
52
  case 'AgentAttentionFlashTimeout':
53
53
  return handleAttentionFlashTimeout(host, effect);
54
- case 'AgentChatSendInput':
55
- return handleChatSendInput(host, effect);
56
54
  }
57
55
  };
58
56
  }
@@ -75,30 +73,6 @@ async function handleAttentionFlashTimeout(host, effect) {
75
73
  await new Promise((resolve) => setTimeout(resolve, effect.delayMs));
76
74
  host.send(host.wrapAgentAttention({ type: 'Clear', entryId: effect.entryId }));
77
75
  }
78
- async function handleChatSendInput(host, effect) {
79
- const wrapper = host.wrapAgentChat;
80
- // Frame send: best-effort. `submitUserInput` swallows its own
81
- // throws (it's just `ws.send(JSON.stringify(...))` × 2), but a
82
- // missing client (pre-open or post-close) silently no-ops the send
83
- // and we just hit the SubmitComplete fall-through. The user's
84
- // text was already cleared from `pendingInput` by the reducer; if
85
- // the send fails, the host can render a session-status indicator
86
- // separately (the `agentConnect.status` field is the canonical
87
- // source of "are we online").
88
- const client = host.getWsClient?.() ?? null;
89
- if (client) {
90
- try {
91
- client.submitUserInput(effect.text, effect.at);
92
- }
93
- catch {
94
- // best-effort — same contract as the frame-send path inside
95
- // submitUserInput itself
96
- }
97
- }
98
- if (wrapper) {
99
- host.send(wrapper({ type: 'SubmitComplete' }));
100
- }
101
- }
102
76
  // ── HTTP-bound handlers ─────────────────────────────────────────────
103
77
  async function handleMintRequest(host, effect, doFetch) {
104
78
  // Derive a default `mintUrl` from `agentBasePath` so consumers can
@@ -1 +1 @@
1
- {"version":3,"file":"effect-handler.js","sourceRoot":"","sources":["../../src/client/effect-handler.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentEffect } from './effects.js'\nimport type {\n MintResponse,\n ResumeListResponse,\n ResumeClaimResponse,\n SessionsResponse,\n} from '../protocol.js'\nimport type { AgentSessionStorage } from './factory.js'\n\nexport type EffectHandlerHost = {\n send(msg: unknown): void // root app send; wraps agent sub-msgs into the app Msg envelope\n /** Wraps an agentConnect msg into an app-Msg. */\n wrapAgentConnect(m: unknown): unknown\n /**\n * Wraps an agentAttention msg into an app-Msg. Optional; when\n * undefined, the `AgentAttentionFlashTimeout` effect no-ops on\n * fire and the attention spotlight stays set until the next\n * dispatch replaces it. Hosts that wire the attention slice\n * provide this; hosts that don't can leave it unset.\n */\n wrapAgentAttention?(m: unknown): unknown\n /**\n * Wraps an agentChat msg into an app-Msg. Required for hosts\n * wiring the chat composer — the `AgentChatSendInput` handler\n * dispatches `SubmitComplete` back into the slice via this wrapper\n * after the frame send completes (success or failure), so the\n * input field re-enables. Hosts that don't render chat leave it\n * unset and the chat-send effect no-ops (the agentChat slice in\n * that case has no live presence to begin with).\n */\n wrapAgentChat?(m: unknown): unknown\n /**\n * Active WS client (if any) — set lazily by the factory after\n * `openWs` resolves a connection. The chat-send handler reads\n * `submitUserInput` from here. Marked optional because the\n * handler sits across the connection lifecycle: pre-WS / post-close\n * `submitUserInput` is null and the handler degrades gracefully\n * (still dispatches SubmitComplete so the UI re-enables).\n */\n getWsClient?: () => { submitUserInput(text: string, at?: number): void } | null\n /** Called for AgentForwardMsg — the payload is re-dispatched via send. */\n forward(payload: unknown): void\n /** fetch for HTTP effects; override in tests. */\n fetch?: typeof fetch\n /** Called before opening WS / on WS lifecycle events. */\n openWs(token: string, wsUrl: string): void\n closeWs(): void\n /**\n * Optional storage adapter. When set, `AgentSessionPersist` writes\n * to it and `AgentSessionClear` clears it; the host doesn't need\n * to handle these effects itself. When `null` or `undefined`, the\n * effects no-op here and host code (if any) handles them in the\n * outer effect router. The factory passes\n * `defaultSessionStorage()` by default, so the framework is\n * refresh-survival-ready out of the box.\n */\n sessionStorage?: AgentSessionStorage | null\n /**\n * Base path for agent HTTP endpoints. Default: `'/agent'` (matches\n * the canonical paths in `@llui/vite-plugin`'s dev middleware and\n * `@llui/agent/server/http/router.ts`).\n *\n * Override when the consumer ships `@cloudflare/vite-plugin` in\n * dev — that plugin routes every non-`/cdn-cgi/*` path to the\n * worker, shadowing canonical `/agent/*` URLs. The vite-plugin\n * registers a parallel handler at `/cdn-cgi/agent/*`; pass\n * `agentBasePath: '/cdn-cgi/agent'` here so the client hits that.\n *\n * Production deployments without cloudflare-vite leave this\n * unset; the agent server's router serves the canonical paths.\n */\n agentBasePath?: string\n}\n\ntype Fetch = typeof fetch\n\n/**\n * Top-level dispatcher. The switch is intentionally thin — each\n * `case` delegates to a per-effect function below. Splitting was\n * motivated by the previous 150-line monolith mixing HTTP, WS, and\n * browser-only side effects in one switch; per-effect handlers are\n * directly unit-testable and the dispatcher reads as a flat catalogue\n * of supported effect types.\n */\nexport function createEffectHandler(host: EffectHandlerHost) {\n const doFetch = host.fetch ?? fetch.bind(globalThis)\n\n return async function handle(effect: AgentEffect): Promise<void> {\n switch (effect.type) {\n case 'AgentMintRequest':\n return handleMintRequest(host, effect, doFetch)\n case 'AgentOpenWS':\n return handleOpenWs(host, effect)\n case 'AgentCloseWS':\n return handleCloseWs(host)\n case 'AgentResumeCheck':\n return handleResumeCheck(host, effect, doFetch)\n case 'AgentResumeClaim':\n return handleResumeClaim(host, effect, doFetch)\n case 'AgentRevoke':\n return handleRevoke(host, effect, doFetch)\n case 'AgentSessionsList':\n return handleSessionsList(host, doFetch)\n case 'AgentForwardMsg':\n return handleForwardMsg(host, effect)\n case 'AgentClipboardWrite':\n return handleClipboardWrite(effect)\n case 'AgentSessionPersist':\n // Framework-owned when a storage adapter is configured;\n // otherwise no-op and let the host's outer effect router\n // handle it (the legacy contract). See factory.ts'\n // `sessionStorage` option.\n if (host.sessionStorage) {\n host.sessionStorage.write({\n token: effect.token,\n tid: effect.tid,\n lapUrl: effect.lapUrl,\n wsUrl: effect.wsUrl,\n expiresAt: effect.expiresAt,\n })\n }\n return\n case 'AgentSessionClear':\n if (host.sessionStorage) host.sessionStorage.clear()\n return\n case 'AgentReconnectSchedule':\n return handleReconnectSchedule(host, effect)\n case 'AgentAttentionFlashTimeout':\n return handleAttentionFlashTimeout(host, effect)\n case 'AgentChatSendInput':\n return handleChatSendInput(host, effect)\n }\n }\n}\n\nasync function handleReconnectSchedule(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentReconnectSchedule' }>,\n): Promise<void> {\n // Single-shot timer. The reducer owns cancellation semantics via\n // the status guard in `ReconnectAttempt` — if the user dispatches\n // `Disconnect` while we're sleeping, the dispatched message hits\n // an `idle` reducer and is a no-op. No cancel handle needed.\n await new Promise<void>((resolve) => setTimeout(resolve, effect.delayMs))\n host.send(host.wrapAgentConnect({ type: 'ReconnectAttempt', elapsedMs: effect.delayMs }))\n}\n\nasync function handleAttentionFlashTimeout(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentAttentionFlashTimeout' }>,\n): Promise<void> {\n // Mirror of handleReconnectSchedule's race-tolerant pattern: timer\n // fires, the reducer's `Clear { entryId }` guard handles the case\n // where a newer dispatch already replaced the spotlight. No cancel\n // handle — keeping the timer-cancel logic out of the effect handler\n // simplifies it and reduces the surface area for bugs.\n if (!host.wrapAgentAttention) return // Host opted out — graceful degradation.\n await new Promise<void>((resolve) => setTimeout(resolve, effect.delayMs))\n host.send(host.wrapAgentAttention({ type: 'Clear', entryId: effect.entryId }))\n}\n\nasync function handleChatSendInput(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentChatSendInput' }>,\n): Promise<void> {\n const wrapper = host.wrapAgentChat\n // Frame send: best-effort. `submitUserInput` swallows its own\n // throws (it's just `ws.send(JSON.stringify(...))` × 2), but a\n // missing client (pre-open or post-close) silently no-ops the send\n // and we just hit the SubmitComplete fall-through. The user's\n // text was already cleared from `pendingInput` by the reducer; if\n // the send fails, the host can render a session-status indicator\n // separately (the `agentConnect.status` field is the canonical\n // source of \"are we online\").\n const client = host.getWsClient?.() ?? null\n if (client) {\n try {\n client.submitUserInput(effect.text, effect.at)\n } catch {\n // best-effort — same contract as the frame-send path inside\n // submitUserInput itself\n }\n }\n if (wrapper) {\n host.send(wrapper({ type: 'SubmitComplete' }))\n }\n}\n\n// ── HTTP-bound handlers ─────────────────────────────────────────────\n\nasync function handleMintRequest(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentMintRequest' }>,\n doFetch: Fetch,\n): Promise<void> {\n // Derive a default `mintUrl` from `agentBasePath` so consumers can\n // change the base path in one place (the effect handler) without\n // also having to keep the `agentConnect` opts in sync. `agentBase`\n // accepts both absolute paths and full URLs.\n const base = agentBase(host)\n if (!base) return\n const mintUrl = effect.mintUrl ?? `${base}/mint`\n try {\n const res = await doFetch(mintUrl, { method: 'POST', credentials: 'include' })\n if (!res.ok) {\n const detail = await safeText(res)\n host.send(\n host.wrapAgentConnect({\n type: 'MintFailed',\n error: { code: `http-${res.status}`, detail },\n }),\n )\n return\n }\n const body = (await res.json()) as MintResponse\n host.send(\n host.wrapAgentConnect({\n type: 'MintSucceeded',\n token: body.token,\n tid: body.tid,\n lapUrl: body.lapUrl,\n wsUrl: body.wsUrl,\n expiresAt: body.expiresAt,\n }),\n )\n } catch (e) {\n host.send(\n host.wrapAgentConnect({\n type: 'MintFailed',\n error: { code: 'network', detail: String(e) },\n }),\n )\n }\n}\n\nasync function handleResumeCheck(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentResumeCheck' }>,\n doFetch: Fetch,\n): Promise<void> {\n const base = agentBase(host)\n if (!base) return\n try {\n const res = await doFetch(`${base}/resume/list`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ tids: effect.tids }),\n })\n if (!res.ok) return\n const body = (await res.json()) as ResumeListResponse\n host.send(host.wrapAgentConnect({ type: 'ResumeListLoaded', sessions: body.sessions }))\n } catch {\n /* quiet failure; user can retry */\n }\n}\n\nasync function handleResumeClaim(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentResumeClaim' }>,\n doFetch: Fetch,\n): Promise<void> {\n const base = agentBase(host)\n if (!base) return\n try {\n const res = await doFetch(`${base}/resume/claim`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ tid: effect.tid }),\n })\n if (!res.ok) return\n const body = (await res.json()) as ResumeClaimResponse\n host.openWs(body.token, body.wsUrl)\n host.send(host.wrapAgentConnect({ type: 'WsOpened' }))\n } catch {\n /* quiet */\n }\n}\n\nasync function handleRevoke(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentRevoke' }>,\n doFetch: Fetch,\n): Promise<void> {\n const base = agentBase(host)\n if (!base) return\n try {\n await doFetch(`${base}/revoke`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ tid: effect.tid }),\n })\n } catch {\n /* quiet */\n }\n}\n\nasync function handleSessionsList(host: EffectHandlerHost, doFetch: Fetch): Promise<void> {\n const base = agentBase(host)\n if (!base) return\n try {\n const res = await doFetch(`${base}/sessions`, {\n method: 'GET',\n credentials: 'include',\n })\n if (!res.ok) return\n const body = (await res.json()) as SessionsResponse\n host.send(host.wrapAgentConnect({ type: 'SessionsLoaded', sessions: body.sessions }))\n } catch {\n /* quiet */\n }\n}\n\n// ── WS-bound handlers ───────────────────────────────────────────────\n\nfunction handleOpenWs(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentOpenWS' }>,\n): void {\n host.openWs(effect.token, effect.wsUrl)\n}\n\nfunction handleCloseWs(host: EffectHandlerHost): void {\n host.closeWs()\n}\n\n// ── Local handlers (no network) ─────────────────────────────────────\n\nfunction handleForwardMsg(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentForwardMsg' }>,\n): void {\n host.forward(effect.payload)\n}\n\nasync function handleClipboardWrite(\n effect: Extract<AgentEffect, { type: 'AgentClipboardWrite' }>,\n): Promise<void> {\n // Browser-only — `navigator.clipboard` is undefined in Node/jsdom\n // test environments. Silently no-op rather than throw, matching the\n // rest of the agent effect handlers' failure-quiet pattern.\n if (typeof navigator === 'undefined' || !('clipboard' in navigator)) return\n try {\n await navigator.clipboard.writeText(effect.text)\n } catch {\n /* quiet — clipboard permission denied or document not focused */\n }\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────\n\nasync function safeText(res: Response): Promise<string> {\n try {\n return await res.text()\n } catch {\n return ''\n }\n}\n\nfunction deriveOrigin(): string | null {\n // When running in the browser, `location.origin` is correct for\n // same-origin agent endpoints. Tests override `host.fetch` and\n // short-circuit before this is reached.\n if (typeof location !== 'undefined') return location.origin\n return null\n}\n\nconst ABSOLUTE_URL_RE = /^https?:\\/\\//i\n\n/**\n * Resolve the absolute base URL for agent HTTP endpoints. Accepts both\n * absolute paths (`/agent`) and full URLs (`https://api.example/agent`)\n * — the absolute URL form lets consumers point at a cross-origin agent\n * server without pre-composing every endpoint URL. Trailing slashes\n * are normalized so callers can always concatenate `${base}/mint`.\n */\nfunction agentBase(host: EffectHandlerHost): string | null {\n const raw = host.agentBasePath ?? '/agent'\n const trimmed = raw.endsWith('/') ? raw.slice(0, -1) : raw\n if (ABSOLUTE_URL_RE.test(trimmed)) return trimmed\n const origin = deriveOrigin()\n if (!origin) return null\n return `${origin}${trimmed}`\n}\n"]}
1
+ {"version":3,"file":"effect-handler.js","sourceRoot":"","sources":["../../src/client/effect-handler.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentEffect } from './effects.js'\nimport type {\n MintResponse,\n ResumeListResponse,\n ResumeClaimResponse,\n SessionsResponse,\n} from '../protocol.js'\nimport type { AgentSessionStorage } from './factory.js'\n\nexport type EffectHandlerHost = {\n send(msg: unknown): void // root app send; wraps agent sub-msgs into the app Msg envelope\n /** Wraps an agentConnect msg into an app-Msg. */\n wrapAgentConnect(m: unknown): unknown\n /**\n * Wraps an agentAttention msg into an app-Msg. Optional; when\n * undefined, the `AgentAttentionFlashTimeout` effect no-ops on\n * fire and the attention spotlight stays set until the next\n * dispatch replaces it. Hosts that wire the attention slice\n * provide this; hosts that don't can leave it unset.\n */\n wrapAgentAttention?(m: unknown): unknown\n /** Called for AgentForwardMsg — the payload is re-dispatched via send. */\n forward(payload: unknown): void\n /** fetch for HTTP effects; override in tests. */\n fetch?: typeof fetch\n /** Called before opening WS / on WS lifecycle events. */\n openWs(token: string, wsUrl: string): void\n closeWs(): void\n /**\n * Optional storage adapter. When set, `AgentSessionPersist` writes\n * to it and `AgentSessionClear` clears it; the host doesn't need\n * to handle these effects itself. When `null` or `undefined`, the\n * effects no-op here and host code (if any) handles them in the\n * outer effect router. The factory passes\n * `defaultSessionStorage()` by default, so the framework is\n * refresh-survival-ready out of the box.\n */\n sessionStorage?: AgentSessionStorage | null\n /**\n * Base path for agent HTTP endpoints. Default: `'/agent'` (matches\n * the canonical paths in `@llui/vite-plugin`'s dev middleware and\n * `@llui/agent/server/http/router.ts`).\n *\n * Override when the consumer ships `@cloudflare/vite-plugin` in\n * dev — that plugin routes every non-`/cdn-cgi/*` path to the\n * worker, shadowing canonical `/agent/*` URLs. The vite-plugin\n * registers a parallel handler at `/cdn-cgi/agent/*`; pass\n * `agentBasePath: '/cdn-cgi/agent'` here so the client hits that.\n *\n * Production deployments without cloudflare-vite leave this\n * unset; the agent server's router serves the canonical paths.\n */\n agentBasePath?: string\n}\n\ntype Fetch = typeof fetch\n\n/**\n * Top-level dispatcher. The switch is intentionally thin — each\n * `case` delegates to a per-effect function below. Splitting was\n * motivated by the previous 150-line monolith mixing HTTP, WS, and\n * browser-only side effects in one switch; per-effect handlers are\n * directly unit-testable and the dispatcher reads as a flat catalogue\n * of supported effect types.\n */\nexport function createEffectHandler(host: EffectHandlerHost) {\n const doFetch = host.fetch ?? fetch.bind(globalThis)\n\n return async function handle(effect: AgentEffect): Promise<void> {\n switch (effect.type) {\n case 'AgentMintRequest':\n return handleMintRequest(host, effect, doFetch)\n case 'AgentOpenWS':\n return handleOpenWs(host, effect)\n case 'AgentCloseWS':\n return handleCloseWs(host)\n case 'AgentResumeCheck':\n return handleResumeCheck(host, effect, doFetch)\n case 'AgentResumeClaim':\n return handleResumeClaim(host, effect, doFetch)\n case 'AgentRevoke':\n return handleRevoke(host, effect, doFetch)\n case 'AgentSessionsList':\n return handleSessionsList(host, doFetch)\n case 'AgentForwardMsg':\n return handleForwardMsg(host, effect)\n case 'AgentClipboardWrite':\n return handleClipboardWrite(effect)\n case 'AgentSessionPersist':\n // Framework-owned when a storage adapter is configured;\n // otherwise no-op and let the host's outer effect router\n // handle it (the legacy contract). See factory.ts'\n // `sessionStorage` option.\n if (host.sessionStorage) {\n host.sessionStorage.write({\n token: effect.token,\n tid: effect.tid,\n lapUrl: effect.lapUrl,\n wsUrl: effect.wsUrl,\n expiresAt: effect.expiresAt,\n })\n }\n return\n case 'AgentSessionClear':\n if (host.sessionStorage) host.sessionStorage.clear()\n return\n case 'AgentReconnectSchedule':\n return handleReconnectSchedule(host, effect)\n case 'AgentAttentionFlashTimeout':\n return handleAttentionFlashTimeout(host, effect)\n }\n }\n}\n\nasync function handleReconnectSchedule(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentReconnectSchedule' }>,\n): Promise<void> {\n // Single-shot timer. The reducer owns cancellation semantics via\n // the status guard in `ReconnectAttempt` — if the user dispatches\n // `Disconnect` while we're sleeping, the dispatched message hits\n // an `idle` reducer and is a no-op. No cancel handle needed.\n await new Promise<void>((resolve) => setTimeout(resolve, effect.delayMs))\n host.send(host.wrapAgentConnect({ type: 'ReconnectAttempt', elapsedMs: effect.delayMs }))\n}\n\nasync function handleAttentionFlashTimeout(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentAttentionFlashTimeout' }>,\n): Promise<void> {\n // Mirror of handleReconnectSchedule's race-tolerant pattern: timer\n // fires, the reducer's `Clear { entryId }` guard handles the case\n // where a newer dispatch already replaced the spotlight. No cancel\n // handle — keeping the timer-cancel logic out of the effect handler\n // simplifies it and reduces the surface area for bugs.\n if (!host.wrapAgentAttention) return // Host opted out — graceful degradation.\n await new Promise<void>((resolve) => setTimeout(resolve, effect.delayMs))\n host.send(host.wrapAgentAttention({ type: 'Clear', entryId: effect.entryId }))\n}\n\n// ── HTTP-bound handlers ─────────────────────────────────────────────\n\nasync function handleMintRequest(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentMintRequest' }>,\n doFetch: Fetch,\n): Promise<void> {\n // Derive a default `mintUrl` from `agentBasePath` so consumers can\n // change the base path in one place (the effect handler) without\n // also having to keep the `agentConnect` opts in sync. `agentBase`\n // accepts both absolute paths and full URLs.\n const base = agentBase(host)\n if (!base) return\n const mintUrl = effect.mintUrl ?? `${base}/mint`\n try {\n const res = await doFetch(mintUrl, { method: 'POST', credentials: 'include' })\n if (!res.ok) {\n const detail = await safeText(res)\n host.send(\n host.wrapAgentConnect({\n type: 'MintFailed',\n error: { code: `http-${res.status}`, detail },\n }),\n )\n return\n }\n const body = (await res.json()) as MintResponse\n host.send(\n host.wrapAgentConnect({\n type: 'MintSucceeded',\n token: body.token,\n tid: body.tid,\n lapUrl: body.lapUrl,\n wsUrl: body.wsUrl,\n expiresAt: body.expiresAt,\n }),\n )\n } catch (e) {\n host.send(\n host.wrapAgentConnect({\n type: 'MintFailed',\n error: { code: 'network', detail: String(e) },\n }),\n )\n }\n}\n\nasync function handleResumeCheck(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentResumeCheck' }>,\n doFetch: Fetch,\n): Promise<void> {\n const base = agentBase(host)\n if (!base) return\n try {\n const res = await doFetch(`${base}/resume/list`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ tids: effect.tids }),\n })\n if (!res.ok) return\n const body = (await res.json()) as ResumeListResponse\n host.send(host.wrapAgentConnect({ type: 'ResumeListLoaded', sessions: body.sessions }))\n } catch {\n /* quiet failure; user can retry */\n }\n}\n\nasync function handleResumeClaim(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentResumeClaim' }>,\n doFetch: Fetch,\n): Promise<void> {\n const base = agentBase(host)\n if (!base) return\n try {\n const res = await doFetch(`${base}/resume/claim`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ tid: effect.tid }),\n })\n if (!res.ok) return\n const body = (await res.json()) as ResumeClaimResponse\n host.openWs(body.token, body.wsUrl)\n host.send(host.wrapAgentConnect({ type: 'WsOpened' }))\n } catch {\n /* quiet */\n }\n}\n\nasync function handleRevoke(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentRevoke' }>,\n doFetch: Fetch,\n): Promise<void> {\n const base = agentBase(host)\n if (!base) return\n try {\n await doFetch(`${base}/revoke`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ tid: effect.tid }),\n })\n } catch {\n /* quiet */\n }\n}\n\nasync function handleSessionsList(host: EffectHandlerHost, doFetch: Fetch): Promise<void> {\n const base = agentBase(host)\n if (!base) return\n try {\n const res = await doFetch(`${base}/sessions`, {\n method: 'GET',\n credentials: 'include',\n })\n if (!res.ok) return\n const body = (await res.json()) as SessionsResponse\n host.send(host.wrapAgentConnect({ type: 'SessionsLoaded', sessions: body.sessions }))\n } catch {\n /* quiet */\n }\n}\n\n// ── WS-bound handlers ───────────────────────────────────────────────\n\nfunction handleOpenWs(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentOpenWS' }>,\n): void {\n host.openWs(effect.token, effect.wsUrl)\n}\n\nfunction handleCloseWs(host: EffectHandlerHost): void {\n host.closeWs()\n}\n\n// ── Local handlers (no network) ─────────────────────────────────────\n\nfunction handleForwardMsg(\n host: EffectHandlerHost,\n effect: Extract<AgentEffect, { type: 'AgentForwardMsg' }>,\n): void {\n host.forward(effect.payload)\n}\n\nasync function handleClipboardWrite(\n effect: Extract<AgentEffect, { type: 'AgentClipboardWrite' }>,\n): Promise<void> {\n // Browser-only — `navigator.clipboard` is undefined in Node/jsdom\n // test environments. Silently no-op rather than throw, matching the\n // rest of the agent effect handlers' failure-quiet pattern.\n if (typeof navigator === 'undefined' || !('clipboard' in navigator)) return\n try {\n await navigator.clipboard.writeText(effect.text)\n } catch {\n /* quiet — clipboard permission denied or document not focused */\n }\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────\n\nasync function safeText(res: Response): Promise<string> {\n try {\n return await res.text()\n } catch {\n return ''\n }\n}\n\nfunction deriveOrigin(): string | null {\n // When running in the browser, `location.origin` is correct for\n // same-origin agent endpoints. Tests override `host.fetch` and\n // short-circuit before this is reached.\n if (typeof location !== 'undefined') return location.origin\n return null\n}\n\nconst ABSOLUTE_URL_RE = /^https?:\\/\\//i\n\n/**\n * Resolve the absolute base URL for agent HTTP endpoints. Accepts both\n * absolute paths (`/agent`) and full URLs (`https://api.example/agent`)\n * — the absolute URL form lets consumers point at a cross-origin agent\n * server without pre-composing every endpoint URL. Trailing slashes\n * are normalized so callers can always concatenate `${base}/mint`.\n */\nfunction agentBase(host: EffectHandlerHost): string | null {\n const raw = host.agentBasePath ?? '/agent'\n const trimmed = raw.endsWith('/') ? raw.slice(0, -1) : raw\n if (ABSOLUTE_URL_RE.test(trimmed)) return trimmed\n const origin = deriveOrigin()\n if (!origin) return null\n return `${origin}${trimmed}`\n}\n"]}
@@ -92,29 +92,6 @@ export type AgentEffect =
92
92
  type: 'AgentAttentionFlashTimeout';
93
93
  entryId: string;
94
94
  delayMs: number;
95
- }
96
- /**
97
- * Send a user chat-composer submission upstream. Handler routes
98
- * to `WsClient.submitUserInput(text, at)`, which:
99
- *
100
- * 1. emits a `user-input-submitted` WS frame so the server's
101
- * parked `wait_for_user_input` waiters resolve;
102
- * 2. synthesizes a `LogEntry { kind: 'user-input', detail: text }`
103
- * and calls the factory's `onLogEntry` so `agentLog` (and
104
- * mirror channels) render the user's reply inline with agent
105
- * actions.
106
- *
107
- * After the frame sends, the handler dispatches
108
- * `AgentChat.SubmitComplete` back into the chat slice via
109
- * `wrapAgentChat` so the UI re-enables the input. The
110
- * frame-send is best-effort: a closed/missing WS still resolves
111
- * the SubmitComplete (so the input doesn't lock up); the user can
112
- * retry once the connection is back.
113
- */
114
- | {
115
- type: 'AgentChatSendInput';
116
- text: string;
117
- at: number;
118
95
  };
119
96
  export type AgentEffectHandler = (effect: AgentEffect) => Promise<void>;
120
97
  //# sourceMappingURL=effects.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"effects.d.ts","sourceRoot":"","sources":["../../src/client/effects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEhD,MAAM,MAAM,WAAW;AACrB;;;;;;GAMG;AACD;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAK7C;IAAE,IAAI,EAAE,qBAAqB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AAC/C;;;;;;;;;;;GAWG;GACD;IACE,IAAI,EAAE,qBAAqB,CAAA;IAC3B,KAAK,EAAE,UAAU,CAAA;IACjB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;CAClB,GACD;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE;AAC/B;;;;;;;;;;;;GAYG;GACD;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AACrD;;;;;;;;;;;;;;GAcG;GACD;IAAE,IAAI,EAAE,4BAA4B,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AAC1E;;;;;;;;;;;;;;;;;GAiBG;GACD;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAA;AAG5D,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA"}
1
+ {"version":3,"file":"effects.d.ts","sourceRoot":"","sources":["../../src/client/effects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEhD,MAAM,MAAM,WAAW;AACrB;;;;;;GAMG;AACD;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAK7C;IAAE,IAAI,EAAE,qBAAqB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AAC/C;;;;;;;;;;;GAWG;GACD;IACE,IAAI,EAAE,qBAAqB,CAAA;IAC3B,KAAK,EAAE,UAAU,CAAA;IACjB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;CAClB,GACD;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE;AAC/B;;;;;;;;;;;;GAYG;GACD;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AACrD;;;;;;;;;;;;;;GAcG;GACD;IAAE,IAAI,EAAE,4BAA4B,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAG5E,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"effects.js","sourceRoot":"","sources":["../../src/client/effects.ts"],"names":[],"mappings":"","sourcesContent":["import type { AgentToken } from '../protocol.js'\n\nexport type AgentEffect =\n /**\n * Mint a fresh agent token. `mintUrl` is optional — when omitted the\n * effect handler derives it from `EffectHandlerHost.agentBasePath`\n * (default `/agent`), producing `<agentBasePath>/mint`. Pass an\n * explicit value when the mint endpoint lives outside the configured\n * base path.\n */\n | { type: 'AgentMintRequest'; mintUrl?: string }\n | { type: 'AgentOpenWS'; token: AgentToken; wsUrl: string }\n | { type: 'AgentCloseWS' }\n | { type: 'AgentResumeCheck'; tids: string[] }\n | { type: 'AgentResumeClaim'; tid: string }\n | { type: 'AgentRevoke'; tid: string }\n | { type: 'AgentSessionsList' }\n | { type: 'AgentForwardMsg'; payload: unknown }\n // Handler reads `text` (no state lookup needed at handler time —\n // update() resolved it from the current state.pendingToken). Lets\n // the static-bag `connect()` shape avoid leaking state-reads into\n // event handlers.\n | { type: 'AgentClipboardWrite'; text: string }\n /**\n * Persist active session credentials so a page refresh can restore\n * the same WS without re-minting (and without invalidating the\n * agent's token via the rotate-on-resume path). Hosts typically\n * write to `sessionStorage` so the credentials are tab-scoped:\n * survive refresh, die on tab close. The framework emits this on\n * `MintSucceeded`; the matching `AgentSessionClear` is emitted on\n * `Revoke` of the active tid. Hosts that don't implement the\n * persist/restore loop can ignore both — the rest of the connect\n * lifecycle still works (the page just falls back to \"mint a new\n * session\" after refresh, same as before this effect existed).\n */\n | {\n type: 'AgentSessionPersist'\n token: AgentToken\n tid: string\n lapUrl: string\n wsUrl: string\n expiresAt: number\n }\n | { type: 'AgentSessionClear' }\n /**\n * Schedule the next WS-reconnect attempt. The handler waits\n * `delayMs` and dispatches `ReconnectAttempt { elapsedMs: delayMs }`\n * back into the reducer, which decides whether to re-open the WS\n * or transition to `failed` based on the cumulative wait. The\n * delay schedule itself is computed reducer-side from\n * `reconnectAttempt` — this effect is a thin setTimeout wrapper.\n *\n * The handler doesn't track cancellation: if the user dispatches\n * `Disconnect` while the timer is pending, the reducer transitions\n * to `idle` and the subsequent `ReconnectAttempt` becomes a no-op\n * via the status guard. Simpler than coordinating cancel handles.\n */\n | { type: 'AgentReconnectSchedule'; delayMs: number }\n /**\n * Auto-clear the `agentAttention` spotlight after `delayMs`. The\n * handler waits and dispatches `Clear { entryId }` back into the\n * attention slice via `wrapAgentAttention`. The clear is conditional\n * (matches `entryId` against `latestDispatch.entryId` in the reducer),\n * so a fast follow-up dispatch isn't wiped by the previous dispatch's\n * pending timer — same race-avoidance pattern as\n * `AgentReconnectSchedule`'s status guard.\n *\n * No cancel handle: the handler is a thin `setTimeout` wrapper. If\n * the host doesn't wire `wrapAttentionMsg` in the factory, the\n * handler no-ops and the spotlight stays set until the next dispatch\n * overwrites it (graceful degradation — the activity log still\n * works, just without auto-clearing visual highlights).\n */\n | { type: 'AgentAttentionFlashTimeout'; entryId: string; delayMs: number }\n /**\n * Send a user chat-composer submission upstream. Handler routes\n * to `WsClient.submitUserInput(text, at)`, which:\n *\n * 1. emits a `user-input-submitted` WS frame so the server's\n * parked `wait_for_user_input` waiters resolve;\n * 2. synthesizes a `LogEntry { kind: 'user-input', detail: text }`\n * and calls the factory's `onLogEntry` so `agentLog` (and\n * mirror channels) render the user's reply inline with agent\n * actions.\n *\n * After the frame sends, the handler dispatches\n * `AgentChat.SubmitComplete` back into the chat slice via\n * `wrapAgentChat` so the UI re-enables the input. The\n * frame-send is best-effort: a closed/missing WS still resolves\n * the SubmitComplete (so the input doesn't lock up); the user can\n * retry once the connection is back.\n */\n | { type: 'AgentChatSendInput'; text: string; at: number }\n\n// Handler implementation lands in Plan 7 alongside the WS client.\nexport type AgentEffectHandler = (effect: AgentEffect) => Promise<void>\n"]}
1
+ {"version":3,"file":"effects.js","sourceRoot":"","sources":["../../src/client/effects.ts"],"names":[],"mappings":"","sourcesContent":["import type { AgentToken } from '../protocol.js'\n\nexport type AgentEffect =\n /**\n * Mint a fresh agent token. `mintUrl` is optional — when omitted the\n * effect handler derives it from `EffectHandlerHost.agentBasePath`\n * (default `/agent`), producing `<agentBasePath>/mint`. Pass an\n * explicit value when the mint endpoint lives outside the configured\n * base path.\n */\n | { type: 'AgentMintRequest'; mintUrl?: string }\n | { type: 'AgentOpenWS'; token: AgentToken; wsUrl: string }\n | { type: 'AgentCloseWS' }\n | { type: 'AgentResumeCheck'; tids: string[] }\n | { type: 'AgentResumeClaim'; tid: string }\n | { type: 'AgentRevoke'; tid: string }\n | { type: 'AgentSessionsList' }\n | { type: 'AgentForwardMsg'; payload: unknown }\n // Handler reads `text` (no state lookup needed at handler time —\n // update() resolved it from the current state.pendingToken). Lets\n // the static-bag `connect()` shape avoid leaking state-reads into\n // event handlers.\n | { type: 'AgentClipboardWrite'; text: string }\n /**\n * Persist active session credentials so a page refresh can restore\n * the same WS without re-minting (and without invalidating the\n * agent's token via the rotate-on-resume path). Hosts typically\n * write to `sessionStorage` so the credentials are tab-scoped:\n * survive refresh, die on tab close. The framework emits this on\n * `MintSucceeded`; the matching `AgentSessionClear` is emitted on\n * `Revoke` of the active tid. Hosts that don't implement the\n * persist/restore loop can ignore both — the rest of the connect\n * lifecycle still works (the page just falls back to \"mint a new\n * session\" after refresh, same as before this effect existed).\n */\n | {\n type: 'AgentSessionPersist'\n token: AgentToken\n tid: string\n lapUrl: string\n wsUrl: string\n expiresAt: number\n }\n | { type: 'AgentSessionClear' }\n /**\n * Schedule the next WS-reconnect attempt. The handler waits\n * `delayMs` and dispatches `ReconnectAttempt { elapsedMs: delayMs }`\n * back into the reducer, which decides whether to re-open the WS\n * or transition to `failed` based on the cumulative wait. The\n * delay schedule itself is computed reducer-side from\n * `reconnectAttempt` — this effect is a thin setTimeout wrapper.\n *\n * The handler doesn't track cancellation: if the user dispatches\n * `Disconnect` while the timer is pending, the reducer transitions\n * to `idle` and the subsequent `ReconnectAttempt` becomes a no-op\n * via the status guard. Simpler than coordinating cancel handles.\n */\n | { type: 'AgentReconnectSchedule'; delayMs: number }\n /**\n * Auto-clear the `agentAttention` spotlight after `delayMs`. The\n * handler waits and dispatches `Clear { entryId }` back into the\n * attention slice via `wrapAgentAttention`. The clear is conditional\n * (matches `entryId` against `latestDispatch.entryId` in the reducer),\n * so a fast follow-up dispatch isn't wiped by the previous dispatch's\n * pending timer — same race-avoidance pattern as\n * `AgentReconnectSchedule`'s status guard.\n *\n * No cancel handle: the handler is a thin `setTimeout` wrapper. If\n * the host doesn't wire `wrapAttentionMsg` in the factory, the\n * handler no-ops and the spotlight stays set until the next dispatch\n * overwrites it (graceful degradation — the activity log still\n * works, just without auto-clearing visual highlights).\n */\n | { type: 'AgentAttentionFlashTimeout'; entryId: string; delayMs: number }\n\n// Handler implementation lands in Plan 7 alongside the WS client.\nexport type AgentEffectHandler = (effect: AgentEffect) => Promise<void>\n"]}
@@ -95,15 +95,6 @@ export type CreateAgentClientOpts<State, Msg> = {
95
95
  * the host needing to write the routing.
96
96
  */
97
97
  wrapAttentionMsg?: (m: unknown) => Msg;
98
- /**
99
- * Optional: wrap an agentChat msg so the chat-composer's send
100
- * effect can dispatch `SubmitComplete` back to re-enable the
101
- * input. Hosts that wire the chat composer set this; hosts that
102
- * don't can leave it unset and the chat-send effect no-ops
103
- * (the chat slice was never wired anyway, so no UI is waiting
104
- * for the SubmitComplete signal).
105
- */
106
- wrapChatMsg?: (m: unknown) => Msg;
107
98
  };
108
99
  /**
109
100
  * Codec registry for non-JSON-safe values (Date, Blob, Map, …)
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/client/factory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/client/factory.ts"],"names":[],"mappings":"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"}
@@ -202,8 +202,6 @@ export function createAgentClient(opts) {
202
202
  wrapAgentAttention: opts.slices.wrapAttentionMsg
203
203
  ? (m) => opts.slices.wrapAttentionMsg(m)
204
204
  : undefined,
205
- wrapAgentChat: opts.slices.wrapChatMsg ? (m) => opts.slices.wrapChatMsg(m) : undefined,
206
- getWsClient: () => wsClient,
207
205
  forward: (payload) => opts.handle.send(payload),
208
206
  agentBasePath: opts.agentBasePath,
209
207
  sessionStorage,