@llui/agent 0.0.29

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 (151) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/dist/client/agentConfirm.d.ts +60 -0
  4. package/dist/client/agentConfirm.d.ts.map +1 -0
  5. package/dist/client/agentConfirm.js +66 -0
  6. package/dist/client/agentConfirm.js.map +1 -0
  7. package/dist/client/agentConnect.d.ts +125 -0
  8. package/dist/client/agentConnect.d.ts.map +1 -0
  9. package/dist/client/agentConnect.js +114 -0
  10. package/dist/client/agentConnect.js.map +1 -0
  11. package/dist/client/agentLog.d.ts +51 -0
  12. package/dist/client/agentLog.d.ts.map +1 -0
  13. package/dist/client/agentLog.js +53 -0
  14. package/dist/client/agentLog.js.map +1 -0
  15. package/dist/client/effect-handler.d.ts +15 -0
  16. package/dist/client/effect-handler.d.ts.map +1 -0
  17. package/dist/client/effect-handler.js +146 -0
  18. package/dist/client/effect-handler.js.map +1 -0
  19. package/dist/client/effects.d.ts +27 -0
  20. package/dist/client/effects.d.ts.map +1 -0
  21. package/dist/client/effects.js +2 -0
  22. package/dist/client/effects.js.map +1 -0
  23. package/dist/client/factory.d.ts +47 -0
  24. package/dist/client/factory.d.ts.map +1 -0
  25. package/dist/client/factory.js +105 -0
  26. package/dist/client/factory.js.map +1 -0
  27. package/dist/client/index.d.ts +7 -0
  28. package/dist/client/index.d.ts.map +1 -0
  29. package/dist/client/index.js +5 -0
  30. package/dist/client/index.js.map +1 -0
  31. package/dist/client/rpc/describe-context.d.ts +10 -0
  32. package/dist/client/rpc/describe-context.d.ts.map +1 -0
  33. package/dist/client/rpc/describe-context.js +8 -0
  34. package/dist/client/rpc/describe-context.js.map +1 -0
  35. package/dist/client/rpc/describe-visible-content.d.ts +22 -0
  36. package/dist/client/rpc/describe-visible-content.d.ts.map +1 -0
  37. package/dist/client/rpc/describe-visible-content.js +66 -0
  38. package/dist/client/rpc/describe-visible-content.js.map +1 -0
  39. package/dist/client/rpc/get-state.d.ts +15 -0
  40. package/dist/client/rpc/get-state.d.ts.map +1 -0
  41. package/dist/client/rpc/get-state.js +37 -0
  42. package/dist/client/rpc/get-state.js.map +1 -0
  43. package/dist/client/rpc/list-actions.d.ts +27 -0
  44. package/dist/client/rpc/list-actions.d.ts.map +1 -0
  45. package/dist/client/rpc/list-actions.js +38 -0
  46. package/dist/client/rpc/list-actions.js.map +1 -0
  47. package/dist/client/rpc/query-dom.d.ts +20 -0
  48. package/dist/client/rpc/query-dom.d.ts.map +1 -0
  49. package/dist/client/rpc/query-dom.js +37 -0
  50. package/dist/client/rpc/query-dom.js.map +1 -0
  51. package/dist/client/rpc/send-message.d.ts +28 -0
  52. package/dist/client/rpc/send-message.d.ts.map +1 -0
  53. package/dist/client/rpc/send-message.js +40 -0
  54. package/dist/client/rpc/send-message.js.map +1 -0
  55. package/dist/client/uuid.d.ts +2 -0
  56. package/dist/client/uuid.d.ts.map +1 -0
  57. package/dist/client/uuid.js +24 -0
  58. package/dist/client/uuid.js.map +1 -0
  59. package/dist/client/ws-client.d.ts +44 -0
  60. package/dist/client/ws-client.d.ts.map +1 -0
  61. package/dist/client/ws-client.js +176 -0
  62. package/dist/client/ws-client.js.map +1 -0
  63. package/dist/protocol.d.ts +319 -0
  64. package/dist/protocol.d.ts.map +1 -0
  65. package/dist/protocol.js +6 -0
  66. package/dist/protocol.js.map +1 -0
  67. package/dist/server/audit.d.ts +6 -0
  68. package/dist/server/audit.d.ts.map +1 -0
  69. package/dist/server/audit.js +6 -0
  70. package/dist/server/audit.js.map +1 -0
  71. package/dist/server/factory.d.ts +10 -0
  72. package/dist/server/factory.d.ts.map +1 -0
  73. package/dist/server/factory.js +69 -0
  74. package/dist/server/factory.js.map +1 -0
  75. package/dist/server/http/mint.d.ts +23 -0
  76. package/dist/server/http/mint.d.ts.map +1 -0
  77. package/dist/server/http/mint.js +63 -0
  78. package/dist/server/http/mint.js.map +1 -0
  79. package/dist/server/http/resume.d.ts +14 -0
  80. package/dist/server/http/resume.d.ts.map +1 -0
  81. package/dist/server/http/resume.js +89 -0
  82. package/dist/server/http/resume.js.map +1 -0
  83. package/dist/server/http/revoke.d.ts +11 -0
  84. package/dist/server/http/revoke.d.ts.map +1 -0
  85. package/dist/server/http/revoke.js +24 -0
  86. package/dist/server/http/revoke.js.map +1 -0
  87. package/dist/server/http/router.d.ts +13 -0
  88. package/dist/server/http/router.d.ts.map +1 -0
  89. package/dist/server/http/router.js +28 -0
  90. package/dist/server/http/router.js.map +1 -0
  91. package/dist/server/http/sessions.d.ts +8 -0
  92. package/dist/server/http/sessions.d.ts.map +1 -0
  93. package/dist/server/http/sessions.js +27 -0
  94. package/dist/server/http/sessions.js.map +1 -0
  95. package/dist/server/identity.d.ts +8 -0
  96. package/dist/server/identity.d.ts.map +1 -0
  97. package/dist/server/identity.js +41 -0
  98. package/dist/server/identity.js.map +1 -0
  99. package/dist/server/index.d.ts +11 -0
  100. package/dist/server/index.d.ts.map +1 -0
  101. package/dist/server/index.js +6 -0
  102. package/dist/server/index.js.map +1 -0
  103. package/dist/server/lap/confirm-result.d.ts +14 -0
  104. package/dist/server/lap/confirm-result.d.ts.map +1 -0
  105. package/dist/server/lap/confirm-result.js +60 -0
  106. package/dist/server/lap/confirm-result.js.map +1 -0
  107. package/dist/server/lap/describe.d.ts +22 -0
  108. package/dist/server/lap/describe.d.ts.map +1 -0
  109. package/dist/server/lap/describe.js +67 -0
  110. package/dist/server/lap/describe.js.map +1 -0
  111. package/dist/server/lap/forward.d.ts +24 -0
  112. package/dist/server/lap/forward.d.ts.map +1 -0
  113. package/dist/server/lap/forward.js +68 -0
  114. package/dist/server/lap/forward.js.map +1 -0
  115. package/dist/server/lap/message.d.ts +14 -0
  116. package/dist/server/lap/message.d.ts.map +1 -0
  117. package/dist/server/lap/message.js +97 -0
  118. package/dist/server/lap/message.js.map +1 -0
  119. package/dist/server/lap/router.d.ts +4 -0
  120. package/dist/server/lap/router.d.ts.map +1 -0
  121. package/dist/server/lap/router.js +37 -0
  122. package/dist/server/lap/router.js.map +1 -0
  123. package/dist/server/lap/wait.d.ts +14 -0
  124. package/dist/server/lap/wait.d.ts.map +1 -0
  125. package/dist/server/lap/wait.js +35 -0
  126. package/dist/server/lap/wait.js.map +1 -0
  127. package/dist/server/options.d.ts +41 -0
  128. package/dist/server/options.d.ts.map +1 -0
  129. package/dist/server/options.js +2 -0
  130. package/dist/server/options.js.map +1 -0
  131. package/dist/server/rate-limit.d.ts +14 -0
  132. package/dist/server/rate-limit.d.ts.map +1 -0
  133. package/dist/server/rate-limit.js +43 -0
  134. package/dist/server/rate-limit.js.map +1 -0
  135. package/dist/server/token-store.d.ts +27 -0
  136. package/dist/server/token-store.d.ts.map +1 -0
  137. package/dist/server/token-store.js +55 -0
  138. package/dist/server/token-store.js.map +1 -0
  139. package/dist/server/token.d.ts +24 -0
  140. package/dist/server/token.d.ts.map +1 -0
  141. package/dist/server/token.js +77 -0
  142. package/dist/server/token.js.map +1 -0
  143. package/dist/server/ws/pairing-registry.d.ts +53 -0
  144. package/dist/server/ws/pairing-registry.d.ts.map +1 -0
  145. package/dist/server/ws/pairing-registry.js +205 -0
  146. package/dist/server/ws/pairing-registry.js.map +1 -0
  147. package/dist/server/ws/upgrade.d.ts +23 -0
  148. package/dist/server/ws/upgrade.d.ts.map +1 -0
  149. package/dist/server/ws/upgrade.js +81 -0
  150. package/dist/server/ws/upgrade.js.map +1 -0
  151. package/package.json +57 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Franco Ponticelli
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,210 @@
1
+ # @llui/agent
2
+
3
+ Server and browser-client libraries for the [LLui Agent Protocol (LAP)](../../docs/superpowers/specs/2026-04-19-llui-agent-design.md).
4
+
5
+ ## What this buys you
6
+
7
+ Your app's users can install the `llui-agent` bridge into Claude Desktop once, paste a token you mint for them, and drive your LLui app from Claude. Same Msgs and State you're already using — Claude dispatches like a remote user.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pnpm add @llui/agent @llui/effects ws
13
+ pnpm add -D @llui/vite-plugin # if not already present
14
+ ```
15
+
16
+ Enable agent-metadata emission in `vite.config.ts`:
17
+
18
+ ```ts
19
+ import llui from '@llui/vite-plugin'
20
+ export default { plugins: [llui({ agent: true })] }
21
+ ```
22
+
23
+ ## Server
24
+
25
+ ```ts
26
+ import { createLluiAgentServer } from '@llui/agent/server'
27
+ import express from 'express'
28
+
29
+ const agent = createLluiAgentServer({
30
+ signingKey: process.env.LLUI_AGENT_KEY!,
31
+ identityResolver: async (req) => req.cookies.user_id ?? null,
32
+ })
33
+
34
+ const app = express()
35
+ // The router is Web-standards; adapt it:
36
+ app.use('/agent', async (req, res) => {
37
+ const webReq = expressToWebRequest(req) // adapter
38
+ const webRes = await agent.router(webReq)
39
+ if (!webRes) {
40
+ res.status(404).end()
41
+ return
42
+ }
43
+ webRes.headers.forEach((v, k) => res.setHeader(k, v))
44
+ res.status(webRes.status).send(await webRes.text())
45
+ })
46
+
47
+ const server = app.listen(8787)
48
+ server.on('upgrade', agent.wsUpgrade)
49
+ ```
50
+
51
+ ## Client
52
+
53
+ ```ts
54
+ import { mountApp } from '@llui/dom'
55
+ import { createAgentClient, agentConnect, agentConfirm, agentLog } from '@llui/agent/client'
56
+ import { handleEffects } from '@llui/effects'
57
+ import { App } from './App'
58
+
59
+ const root = document.getElementById('app')!
60
+ const handle = mountApp(root, App)
61
+
62
+ const client = createAgentClient({
63
+ handle,
64
+ def: App,
65
+ rootElement: root,
66
+ slices: {
67
+ getConnect: (s) => s.agent.connect,
68
+ getConfirm: (s) => s.agent.confirm,
69
+ wrapConnectMsg: (m) => ({ type: 'agent', sub: 'connect', msg: m }),
70
+ wrapConfirmMsg: (m) => ({ type: 'agent', sub: 'confirm', msg: m }),
71
+ },
72
+ })
73
+ client.start()
74
+
75
+ // Chain client.effectHandler into your onEffect:
76
+ const onEffect = handleEffects<MyEffect | AgentEffect>()
77
+ .when('http', ...)
78
+ .else(client.effectHandler)
79
+ ```
80
+
81
+ ## App-side annotations
82
+
83
+ ```ts
84
+ type Msg =
85
+ /** @intent("Increment the counter") */
86
+ | { type: 'inc' }
87
+ /** @intent("Delete item") @requiresConfirm */
88
+ | { type: 'delete', id: string }
89
+ /** @intent("Place order") @humanOnly */
90
+ | { type: 'checkout' }
91
+ /** @intent("Navigate") @alwaysAffordable */
92
+ | { type: 'nav', to: 'reports' | 'settings' | 'home' }
93
+
94
+ export const App = component<State, Msg, Effect>({
95
+ name: 'App',
96
+ init: ...,
97
+ update: ...,
98
+ view: ...,
99
+ agentAffordances: (state) => [
100
+ { type: 'nav', to: 'reports' },
101
+ ...(state.user ? [{ type: 'signOut' }] : []),
102
+ ],
103
+ agentDocs: {
104
+ purpose: 'Kanban for a 3-person design team.',
105
+ overview: 'Columns: To do / Doing / Done. Cards carry owner, due date, tags.',
106
+ cautions: ['Moving to Done locks edits — reopen first.'],
107
+ },
108
+ agentContext: (state) => ({
109
+ summary: `Viewing board "${state.boardName}", ${state.cards.length} cards visible.`,
110
+ hints: state.selectedCard
111
+ ? ['Card focused; enter advances status.']
112
+ : ['Tab to list, arrow to select.'],
113
+ }),
114
+ })
115
+ ```
116
+
117
+ ## Annotations reference
118
+
119
+ | Tag | Semantics |
120
+ | ------------------- | ------------------------------------------------------------ |
121
+ | `@intent("...")` | Human-readable label for Claude + confirmation UI + log |
122
+ | `@alwaysAffordable` | Surfaces to Claude even when no binding is currently visible |
123
+ | `@requiresConfirm` | Claude must propose; user approves before dispatch |
124
+ | `@humanOnly` | Claude cannot dispatch; not in `list_actions` |
125
+
126
+ ## App state shape (host integration)
127
+
128
+ Wire your root state and Msg to include agent sub-slices:
129
+
130
+ ```ts
131
+ type State = {
132
+ // ...your app state...
133
+ agent: {
134
+ connect: agentConnect.State
135
+ confirm: agentConfirm.State
136
+ log: agentLog.State
137
+ }
138
+ }
139
+
140
+ type Msg =
141
+ // ...your app msgs...
142
+ | { type: 'agent'; sub: 'connect'; msg: agentConnect.Msg }
143
+ | { type: 'agent'; sub: 'confirm'; msg: agentConfirm.Msg }
144
+ | { type: 'agent'; sub: 'log'; msg: agentLog.Msg }
145
+ ```
146
+
147
+ Delegate in `update`:
148
+
149
+ ```ts
150
+ update: (state, msg) => {
151
+ if (msg.type === 'agent') {
152
+ if (msg.sub === 'connect') {
153
+ const [connect, effects] = agentConnect.update(state.agent.connect, msg.msg)
154
+ return [{ ...state, agent: { ...state.agent, connect } }, effects]
155
+ }
156
+ if (msg.sub === 'confirm') {
157
+ const [confirm, effects] = agentConfirm.update(state.agent.confirm, msg.msg)
158
+ return [{ ...state, agent: { ...state.agent, confirm } }, effects]
159
+ }
160
+ if (msg.sub === 'log') {
161
+ const [log, effects] = agentLog.update(state.agent.log, msg.msg)
162
+ return [{ ...state, agent: { ...state.agent, log } }, effects]
163
+ }
164
+ }
165
+ // ...your app logic...
166
+ }
167
+ ```
168
+
169
+ ## View wiring
170
+
171
+ Render `agentConnect`, `agentConfirm`, and `agentLog` anywhere in your view tree:
172
+
173
+ ```ts
174
+ view: ({ send, branch, show }) => {
175
+ const connectParts = agentConnect.connect(
176
+ (s) => s.agent.connect,
177
+ (m) => send({ type: 'agent', sub: 'connect', msg: m }),
178
+ { mintUrl: '/agent/mint' },
179
+ )
180
+
181
+ const confirmParts = agentConfirm.connect(
182
+ (s) => s.agent.confirm,
183
+ (m) => send({ type: 'agent', sub: 'confirm', msg: m }),
184
+ )
185
+
186
+ return [
187
+ // Renders the "Connect with Claude" button + token copy box + session list:
188
+ div(connectParts.root, [
189
+ button(connectParts.mintTrigger, ['Connect with Claude']),
190
+ ...show({
191
+ when: (s) => s.agent.connect.pendingToken !== null,
192
+ render: () => [
193
+ pre(connectParts.pendingTokenBox),
194
+ button(connectParts.copyConnectSnippetButton, ['Copy']),
195
+ ],
196
+ }),
197
+ ]),
198
+ // Renders pending confirmation cards:
199
+ div(confirmParts.root),
200
+ ]
201
+ }
202
+ ```
203
+
204
+ ## Entry points
205
+
206
+ - `@llui/agent/protocol` — all LAP types, WS frame types, token types, audit types.
207
+ - `@llui/agent/server` — `createLluiAgentServer`, `InMemoryTokenStore`, `consoleAuditSink`, interfaces.
208
+ - `@llui/agent/client` — `createAgentClient`, `agentConnect`, `agentConfirm`, `agentLog`, `AgentEffect`.
209
+
210
+ See the [Agent Protocol doc](../../docs/designs/10%20Agent%20Protocol.md) for the full wire protocol and security model.
@@ -0,0 +1,60 @@
1
+ import type { AgentEffect } from './effects.js';
2
+ export type ConfirmEntry = {
3
+ id: string;
4
+ variant: string;
5
+ payload: unknown;
6
+ intent: string;
7
+ reason: string | null;
8
+ proposedAt: number;
9
+ status: 'pending' | 'approved' | 'rejected';
10
+ };
11
+ export type AgentConfirmState = {
12
+ pending: ConfirmEntry[];
13
+ };
14
+ export type AgentConfirmMsg = {
15
+ type: 'Propose';
16
+ entry: ConfirmEntry;
17
+ } | {
18
+ type: 'Approve';
19
+ id: string;
20
+ } | {
21
+ type: 'Reject';
22
+ id: string;
23
+ } | {
24
+ type: 'ExpireStale';
25
+ now: number;
26
+ maxAgeMs: number;
27
+ };
28
+ export declare function init(): [AgentConfirmState, AgentEffect[]];
29
+ export declare function update(state: AgentConfirmState, msg: AgentConfirmMsg): [AgentConfirmState, AgentEffect[]];
30
+ import { type Send } from '@llui/dom';
31
+ type ConnectBag = {
32
+ root: {
33
+ 'data-scope': string;
34
+ };
35
+ entry: (id: string) => {
36
+ card: {
37
+ 'data-part': string;
38
+ 'data-status': string;
39
+ 'data-id': string;
40
+ };
41
+ approveButton: {
42
+ onClick: () => void;
43
+ disabled: boolean;
44
+ };
45
+ rejectButton: {
46
+ onClick: () => void;
47
+ disabled: boolean;
48
+ };
49
+ intentText: string;
50
+ reasonText: string | null;
51
+ payloadText: string;
52
+ } | null;
53
+ empty: {
54
+ 'data-part': string;
55
+ 'data-visible': boolean;
56
+ };
57
+ };
58
+ export declare function connect<S>(get: (s: S) => AgentConfirmState, send: Send<AgentConfirmMsg>): (state: S) => ConnectBag;
59
+ export {};
60
+ //# sourceMappingURL=agentConfirm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentConfirm.d.ts","sourceRoot":"","sources":["../../src/client/agentConfirm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE/C,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAA;CAC5C,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAAE,OAAO,EAAE,YAAY,EAAE,CAAA;CAAE,CAAA;AAE3D,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAA;AAE1D,wBAAgB,IAAI,IAAI,CAAC,iBAAiB,EAAE,WAAW,EAAE,CAAC,CAEzD;AAED,wBAAgB,MAAM,CACpB,KAAK,EAAE,iBAAiB,EACxB,GAAG,EAAE,eAAe,GACnB,CAAC,iBAAiB,EAAE,WAAW,EAAE,CAAC,CAgCpC;AAGD,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAA;AAErC,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9B,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK;QACrB,IAAI,EAAE;YAAE,WAAW,EAAE,MAAM,CAAC;YAAC,aAAa,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,CAAA;QACvE,aAAa,EAAE;YAAE,OAAO,EAAE,MAAM,IAAI,CAAC;YAAC,QAAQ,EAAE,OAAO,CAAA;SAAE,CAAA;QACzD,YAAY,EAAE;YAAE,OAAO,EAAE,MAAM,IAAI,CAAC;YAAC,QAAQ,EAAE,OAAO,CAAA;SAAE,CAAA;QACxD,UAAU,EAAE,MAAM,CAAA;QAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;QACzB,WAAW,EAAE,MAAM,CAAA;KACpB,GAAG,IAAI,CAAA;IACR,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAA;CACxD,CAAA;AAED,wBAAgB,OAAO,CAAC,CAAC,EACvB,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,iBAAiB,EAChC,IAAI,EAAE,IAAI,CAAC,eAAe,CAAC,GAC1B,CAAC,KAAK,EAAE,CAAC,KAAK,UAAU,CA0B1B"}
@@ -0,0 +1,66 @@
1
+ export function init() {
2
+ return [{ pending: [] }, []];
3
+ }
4
+ export function update(state, msg) {
5
+ switch (msg.type) {
6
+ case 'Propose':
7
+ return [{ pending: [...state.pending, msg.entry] }, []];
8
+ case 'Approve': {
9
+ const entry = state.pending.find((e) => e.id === msg.id);
10
+ if (!entry || entry.status !== 'pending')
11
+ return [state, []];
12
+ return [
13
+ { pending: state.pending.map((e) => (e.id === msg.id ? { ...e, status: 'approved' } : e)) },
14
+ [
15
+ {
16
+ type: 'AgentForwardMsg',
17
+ payload: { type: entry.variant, ...entry.payload },
18
+ },
19
+ ],
20
+ ];
21
+ }
22
+ case 'Reject':
23
+ return [
24
+ { pending: state.pending.map((e) => (e.id === msg.id ? { ...e, status: 'rejected' } : e)) },
25
+ [],
26
+ ];
27
+ case 'ExpireStale':
28
+ return [
29
+ {
30
+ pending: state.pending.filter((e) => msg.now - e.proposedAt <= msg.maxAgeMs || e.status !== 'pending'),
31
+ },
32
+ [],
33
+ ];
34
+ }
35
+ }
36
+ // Connect bag:
37
+ import {} from '@llui/dom';
38
+ export function connect(get, send) {
39
+ return (state) => {
40
+ const s = get(state);
41
+ return {
42
+ root: { 'data-scope': 'agent-confirm' },
43
+ entry: (id) => {
44
+ const e = s.pending.find((x) => x.id === id);
45
+ if (!e)
46
+ return null;
47
+ return {
48
+ card: { 'data-part': 'entry', 'data-status': e.status, 'data-id': e.id },
49
+ approveButton: {
50
+ onClick: () => send({ type: 'Approve', id }),
51
+ disabled: e.status !== 'pending',
52
+ },
53
+ rejectButton: {
54
+ onClick: () => send({ type: 'Reject', id }),
55
+ disabled: e.status !== 'pending',
56
+ },
57
+ intentText: e.intent,
58
+ reasonText: e.reason,
59
+ payloadText: JSON.stringify(e.payload, null, 2),
60
+ };
61
+ },
62
+ empty: { 'data-part': 'empty', 'data-visible': s.pending.length === 0 },
63
+ };
64
+ };
65
+ }
66
+ //# sourceMappingURL=agentConfirm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentConfirm.js","sourceRoot":"","sources":["../../src/client/agentConfirm.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentEffect } from './effects.js'\n\nexport type ConfirmEntry = {\n id: string\n variant: string\n payload: unknown\n intent: string\n reason: string | null\n proposedAt: number\n status: 'pending' | 'approved' | 'rejected'\n}\n\nexport type AgentConfirmState = { pending: ConfirmEntry[] }\n\nexport type AgentConfirmMsg =\n | { type: 'Propose'; entry: ConfirmEntry }\n | { type: 'Approve'; id: string }\n | { type: 'Reject'; id: string }\n | { type: 'ExpireStale'; now: number; maxAgeMs: number }\n\nexport function init(): [AgentConfirmState, AgentEffect[]] {\n return [{ pending: [] }, []]\n}\n\nexport function update(\n state: AgentConfirmState,\n msg: AgentConfirmMsg,\n): [AgentConfirmState, AgentEffect[]] {\n switch (msg.type) {\n case 'Propose':\n return [{ pending: [...state.pending, msg.entry] }, []]\n case 'Approve': {\n const entry = state.pending.find((e) => e.id === msg.id)\n if (!entry || entry.status !== 'pending') return [state, []]\n return [\n { pending: state.pending.map((e) => (e.id === msg.id ? { ...e, status: 'approved' } : e)) },\n [\n {\n type: 'AgentForwardMsg',\n payload: { type: entry.variant, ...(entry.payload as object) },\n },\n ],\n ]\n }\n case 'Reject':\n return [\n { pending: state.pending.map((e) => (e.id === msg.id ? { ...e, status: 'rejected' } : e)) },\n [],\n ]\n case 'ExpireStale':\n return [\n {\n pending: state.pending.filter(\n (e) => msg.now - e.proposedAt <= msg.maxAgeMs || e.status !== 'pending',\n ),\n },\n [],\n ]\n }\n}\n\n// Connect bag:\nimport { type Send } from '@llui/dom'\n\ntype ConnectBag = {\n root: { 'data-scope': string }\n entry: (id: string) => {\n card: { 'data-part': string; 'data-status': string; 'data-id': string }\n approveButton: { onClick: () => void; disabled: boolean }\n rejectButton: { onClick: () => void; disabled: boolean }\n intentText: string\n reasonText: string | null\n payloadText: string\n } | null\n empty: { 'data-part': string; 'data-visible': boolean }\n}\n\nexport function connect<S>(\n get: (s: S) => AgentConfirmState,\n send: Send<AgentConfirmMsg>,\n): (state: S) => ConnectBag {\n return (state) => {\n const s = get(state)\n return {\n root: { 'data-scope': 'agent-confirm' },\n entry: (id) => {\n const e = s.pending.find((x) => x.id === id)\n if (!e) return null\n return {\n card: { 'data-part': 'entry', 'data-status': e.status, 'data-id': e.id },\n approveButton: {\n onClick: () => send({ type: 'Approve', id }),\n disabled: e.status !== 'pending',\n },\n rejectButton: {\n onClick: () => send({ type: 'Reject', id }),\n disabled: e.status !== 'pending',\n },\n intentText: e.intent,\n reasonText: e.reason,\n payloadText: JSON.stringify(e.payload, null, 2),\n }\n },\n empty: { 'data-part': 'empty', 'data-visible': s.pending.length === 0 },\n }\n }\n}\n"]}
@@ -0,0 +1,125 @@
1
+ import type { Send } from '@llui/dom';
2
+ import type { AgentSession, AgentToken } from '../protocol.js';
3
+ import type { AgentEffect } from './effects.js';
4
+ export type AgentConnectStatus = 'idle' | 'minting' | 'pending-claude' | 'active' | 'error';
5
+ export type AgentConnectPendingToken = {
6
+ token: AgentToken;
7
+ tid: string;
8
+ lapUrl: string;
9
+ connectSnippet: string;
10
+ expiresAt: number;
11
+ };
12
+ export type AgentConnectState = {
13
+ status: AgentConnectStatus;
14
+ pendingToken: AgentConnectPendingToken | null;
15
+ sessions: AgentSession[];
16
+ resumable: AgentSession[];
17
+ error: {
18
+ code: string;
19
+ detail: string;
20
+ } | null;
21
+ };
22
+ export type AgentConnectMsg = {
23
+ type: 'Mint';
24
+ } | {
25
+ type: 'MintSucceeded';
26
+ token: AgentToken;
27
+ tid: string;
28
+ lapUrl: string;
29
+ wsUrl: string;
30
+ expiresAt: number;
31
+ } | {
32
+ type: 'MintFailed';
33
+ error: {
34
+ code: string;
35
+ detail: string;
36
+ };
37
+ } | {
38
+ type: 'WsOpened';
39
+ } | {
40
+ type: 'WsClosed';
41
+ } | {
42
+ type: 'ActivatedByClaude';
43
+ } | {
44
+ type: 'ResumeList';
45
+ tids: string[];
46
+ } | {
47
+ type: 'ResumeListLoaded';
48
+ sessions: AgentSession[];
49
+ } | {
50
+ type: 'Resume';
51
+ tid: string;
52
+ } | {
53
+ type: 'Revoke';
54
+ tid: string;
55
+ } | {
56
+ type: 'ClearError';
57
+ } | {
58
+ type: 'SessionsLoaded';
59
+ sessions: AgentSession[];
60
+ } | {
61
+ type: 'RefreshSessions';
62
+ };
63
+ export type AgentConnectInitOpts = {
64
+ mintUrl: string;
65
+ };
66
+ /** Component shape is [State, Effect[]] — consistent with @llui/components. */
67
+ export declare function init(_opts: AgentConnectInitOpts): [AgentConnectState, AgentEffect[]];
68
+ export declare function update(state: AgentConnectState, msg: AgentConnectMsg, opts: AgentConnectInitOpts): [AgentConnectState, AgentEffect[]];
69
+ export type AgentConnectConnectOptions = {
70
+ id?: string;
71
+ };
72
+ type ConnectBag = {
73
+ root: {
74
+ 'data-scope': string;
75
+ 'data-state': string;
76
+ };
77
+ mintTrigger: {
78
+ onClick: () => void;
79
+ disabled: boolean;
80
+ };
81
+ pendingTokenBox: {
82
+ 'data-part': string;
83
+ 'data-visible': boolean;
84
+ };
85
+ copyConnectSnippetButton: {
86
+ onClick: () => void;
87
+ disabled: boolean;
88
+ };
89
+ sessionsList: {
90
+ 'data-part': string;
91
+ };
92
+ sessionItem: (tid: string) => {
93
+ 'data-part': string;
94
+ 'data-tid': string;
95
+ };
96
+ revokeButton: (tid: string) => {
97
+ onClick: () => void;
98
+ };
99
+ resumeBanner: {
100
+ 'data-part': string;
101
+ 'data-visible': boolean;
102
+ };
103
+ resumeItem: (tid: string) => {
104
+ 'data-part': string;
105
+ 'data-tid': string;
106
+ };
107
+ resumeButton: (tid: string) => {
108
+ onClick: () => void;
109
+ };
110
+ dismissButton: (tid: string) => {
111
+ onClick: () => void;
112
+ };
113
+ error: {
114
+ 'data-part': string;
115
+ 'data-visible': boolean;
116
+ onClick: () => void;
117
+ };
118
+ };
119
+ /**
120
+ * Builds prop bags for the view. See spec §9.1 and the @llui/components
121
+ * dialog.ts pattern.
122
+ */
123
+ export declare function connect<S>(get: (s: S) => AgentConnectState, send: Send<AgentConnectMsg>, _opts?: AgentConnectConnectOptions): (state: S) => ConnectBag;
124
+ export {};
125
+ //# sourceMappingURL=agentConnect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentConnect.d.ts","sourceRoot":"","sources":["../../src/client/agentConnect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ /** Component shape is [State, Effect[]] — consistent with @llui/components. */
2
+ export function init(_opts) {
3
+ return [
4
+ {
5
+ status: 'idle',
6
+ pendingToken: null,
7
+ sessions: [],
8
+ resumable: [],
9
+ error: null,
10
+ },
11
+ [],
12
+ ];
13
+ }
14
+ export function update(state, msg, opts) {
15
+ switch (msg.type) {
16
+ case 'Mint':
17
+ return [
18
+ { ...state, status: 'minting' },
19
+ [{ type: 'AgentMintRequest', mintUrl: opts.mintUrl }],
20
+ ];
21
+ case 'MintSucceeded': {
22
+ const pending = {
23
+ token: msg.token,
24
+ tid: msg.tid,
25
+ lapUrl: msg.lapUrl,
26
+ connectSnippet: `/llui-connect ${msg.lapUrl} ${msg.token}`,
27
+ expiresAt: msg.expiresAt,
28
+ };
29
+ return [
30
+ { ...state, status: 'pending-claude', pendingToken: pending, error: null },
31
+ [{ type: 'AgentOpenWS', token: msg.token, wsUrl: msg.wsUrl }],
32
+ ];
33
+ }
34
+ case 'MintFailed':
35
+ return [{ ...state, status: 'error', error: msg.error }, []];
36
+ case 'WsOpened':
37
+ // WS is open but Claude hasn't bound yet; stay at pending-claude.
38
+ return [state, []];
39
+ case 'WsClosed':
40
+ return [{ ...state, status: 'idle', pendingToken: null }, []];
41
+ case 'ActivatedByClaude':
42
+ return [{ ...state, status: 'active' }, []];
43
+ case 'ResumeList':
44
+ return [state, [{ type: 'AgentResumeCheck', tids: msg.tids }]];
45
+ case 'ResumeListLoaded':
46
+ return [{ ...state, resumable: msg.sessions }, []];
47
+ case 'Resume':
48
+ return [state, [{ type: 'AgentResumeClaim', tid: msg.tid }]];
49
+ case 'Revoke': {
50
+ // Optimistically remove from sessions + resumable.
51
+ return [
52
+ {
53
+ ...state,
54
+ sessions: state.sessions.filter((s) => s.tid !== msg.tid),
55
+ resumable: state.resumable.filter((s) => s.tid !== msg.tid),
56
+ },
57
+ [{ type: 'AgentRevoke', tid: msg.tid }],
58
+ ];
59
+ }
60
+ case 'ClearError':
61
+ return [{ ...state, error: null }, []];
62
+ case 'SessionsLoaded':
63
+ return [{ ...state, sessions: msg.sessions }, []];
64
+ case 'RefreshSessions':
65
+ return [state, [{ type: 'AgentSessionsList' }]];
66
+ }
67
+ }
68
+ /**
69
+ * Builds prop bags for the view. See spec §9.1 and the @llui/components
70
+ * dialog.ts pattern.
71
+ */
72
+ export function connect(get, send, _opts = {}) {
73
+ return (state) => {
74
+ const s = get(state);
75
+ return {
76
+ root: { 'data-scope': 'agent-connect', 'data-state': s.status },
77
+ mintTrigger: {
78
+ onClick: () => send({ type: 'Mint' }),
79
+ disabled: s.status === 'minting' || s.status === 'pending-claude' || s.status === 'active',
80
+ },
81
+ pendingTokenBox: { 'data-part': 'pending-token', 'data-visible': s.pendingToken !== null },
82
+ copyConnectSnippetButton: {
83
+ onClick: () => {
84
+ if (s.pendingToken && typeof navigator !== 'undefined' && 'clipboard' in navigator) {
85
+ void navigator.clipboard.writeText(s.pendingToken.connectSnippet);
86
+ }
87
+ },
88
+ disabled: s.pendingToken === null,
89
+ },
90
+ sessionsList: { 'data-part': 'sessions-list' },
91
+ sessionItem: (tid) => ({ 'data-part': 'session-item', 'data-tid': tid }),
92
+ revokeButton: (tid) => ({ onClick: () => send({ type: 'Revoke', tid }) }),
93
+ resumeBanner: { 'data-part': 'resume-banner', 'data-visible': s.resumable.length > 0 },
94
+ resumeItem: (tid) => ({ 'data-part': 'resume-item', 'data-tid': tid }),
95
+ resumeButton: (tid) => ({ onClick: () => send({ type: 'Resume', tid }) }),
96
+ dismissButton: (tid) => ({
97
+ // For dismiss, we currently just remove the resumable record locally.
98
+ // A "dismiss forever" flag could land in a follow-up; for v1, dismiss
99
+ // is a client-side-only state prune by reusing the Revoke Msg path
100
+ // with intent-split; for now Emit Revoke which both revokes server-side
101
+ // AND removes locally. Alternative: emit a new DismissResume msg —
102
+ // spec §9.1 lists dismissButton but doesn't spell out the emitted msg.
103
+ // V1 pragmatic choice: same as revoke (mark revoked on server).
104
+ onClick: () => send({ type: 'Revoke', tid }),
105
+ }),
106
+ error: {
107
+ 'data-part': 'error',
108
+ 'data-visible': s.error !== null,
109
+ onClick: () => send({ type: 'ClearError' }),
110
+ },
111
+ };
112
+ };
113
+ }
114
+ //# sourceMappingURL=agentConnect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentConnect.js","sourceRoot":"","sources":["../../src/client/agentConnect.ts"],"names":[],"mappings":"AA8CA,+EAA+E;AAC/E,MAAM,UAAU,IAAI,CAAC,KAA2B;IAC9C,OAAO;QACL;YACE,MAAM,EAAE,MAAM;YACd,YAAY,EAAE,IAAI;YAClB,QAAQ,EAAE,EAAE;YACZ,SAAS,EAAE,EAAE;YACb,KAAK,EAAE,IAAI;SACZ;QACD,EAAE;KACH,CAAA;AACH,CAAC;AAED,MAAM,UAAU,MAAM,CACpB,KAAwB,EACxB,GAAoB,EACpB,IAA0B;IAE1B,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;QACjB,KAAK,MAAM;YACT,OAAO;gBACL,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE;gBAC/B,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;aACtD,CAAA;QACH,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,MAAM,OAAO,GAA6B;gBACxC,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,cAAc,EAAE,iBAAiB,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,KAAK,EAAE;gBAC1D,SAAS,EAAE,GAAG,CAAC,SAAS;aACzB,CAAA;YACD,OAAO;gBACL,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE;gBAC1E,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;aAC9D,CAAA;QACH,CAAC;QACD,KAAK,YAAY;YACf,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,EAAE,EAAE,CAAC,CAAA;QAC9D,KAAK,UAAU;YACb,kEAAkE;YAClE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QACpB,KAAK,UAAU;YACb,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAA;QAC/D,KAAK,mBAAmB;YACtB,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAA;QAC7C,KAAK,YAAY;YACf,OAAO,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;QAChE,KAAK,kBAAkB;YACrB,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAA;QACpD,KAAK,QAAQ;YACX,OAAO,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;QAC9D,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,mDAAmD;YACnD,OAAO;gBACL;oBACE,GAAG,KAAK;oBACR,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC;oBACzD,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC;iBAC5D;gBACD,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;aACxC,CAAA;QACH,CAAC;QACD,KAAK,YAAY;YACf,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAA;QACxC,KAAK,gBAAgB;YACnB,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAA;QACnD,KAAK,iBAAiB;YACpB,OAAO,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC,CAAC,CAAA;IACnD,CAAC;AACH,CAAC;AAuBD;;;GAGG;AACH,MAAM,UAAU,OAAO,CACrB,GAAgC,EAChC,IAA2B,EAC3B,QAAoC,EAAE;IAEtC,OAAO,CAAC,KAAK,EAAE,EAAE;QACf,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAA;QACpB,OAAO;YACL,IAAI,EAAE,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;YAC/D,WAAW,EAAE;gBACX,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;gBACrC,QAAQ,EAAE,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,gBAAgB,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ;aAC3F;YACD,eAAe,EAAE,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC,CAAC,YAAY,KAAK,IAAI,EAAE;YAC1F,wBAAwB,EAAE;gBACxB,OAAO,EAAE,GAAG,EAAE;oBACZ,IAAI,CAAC,CAAC,YAAY,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,WAAW,IAAI,SAAS,EAAE,CAAC;wBACnF,KAAK,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,cAAc,CAAC,CAAA;oBACnE,CAAC;gBACH,CAAC;gBACD,QAAQ,EAAE,CAAC,CAAC,YAAY,KAAK,IAAI;aAClC;YACD,YAAY,EAAE,EAAE,WAAW,EAAE,eAAe,EAAE;YAC9C,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC;YACxE,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;YACzE,YAAY,EAAE,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE;YACtF,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC;YACtE,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;YACzE,aAAa,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;gBACvB,sEAAsE;gBACtE,sEAAsE;gBACtE,mEAAmE;gBACnE,wEAAwE;gBACxE,mEAAmE;gBACnE,uEAAuE;gBACvE,gEAAgE;gBAChE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;aAC7C,CAAC;YACF,KAAK,EAAE;gBACL,WAAW,EAAE,OAAO;gBACpB,cAAc,EAAE,CAAC,CAAC,KAAK,KAAK,IAAI;gBAChC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;aAC5C;SACF,CAAA;IACH,CAAC,CAAA;AACH,CAAC","sourcesContent":["import type { Send } from '@llui/dom'\nimport type { AgentSession, AgentToken } from '../protocol.js'\nimport type { AgentEffect } from './effects.js'\n\nexport type AgentConnectStatus = 'idle' | 'minting' | 'pending-claude' | 'active' | 'error'\n\nexport type AgentConnectPendingToken = {\n token: AgentToken\n tid: string\n lapUrl: string\n connectSnippet: string // \"/llui-connect <lapUrl> <token>\"\n expiresAt: number\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\nexport type AgentConnectMsg =\n | { type: 'Mint' }\n | {\n type: 'MintSucceeded'\n token: AgentToken\n tid: string\n lapUrl: string\n wsUrl: string\n expiresAt: number\n }\n | { type: 'MintFailed'; error: { code: string; detail: string } }\n | { type: 'WsOpened' }\n | { type: 'WsClosed' }\n | { type: 'ActivatedByClaude' }\n | { type: 'ResumeList'; tids: string[] }\n | { type: 'ResumeListLoaded'; sessions: AgentSession[] }\n | { type: 'Resume'; tid: string }\n | { type: 'Revoke'; tid: string }\n | { type: 'ClearError' }\n | { type: 'SessionsLoaded'; sessions: AgentSession[] }\n | { type: 'RefreshSessions' }\n\nexport type AgentConnectInitOpts = { mintUrl: string }\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 },\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 return [\n { ...state, status: 'minting' },\n [{ type: 'AgentMintRequest', mintUrl: opts.mintUrl }],\n ]\n case 'MintSucceeded': {\n const pending: AgentConnectPendingToken = {\n token: msg.token,\n tid: msg.tid,\n lapUrl: msg.lapUrl,\n connectSnippet: `/llui-connect ${msg.lapUrl} ${msg.token}`,\n expiresAt: msg.expiresAt,\n }\n return [\n { ...state, status: 'pending-claude', pendingToken: pending, error: null },\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 return [state, []]\n case 'WsClosed':\n return [{ ...state, status: 'idle', pendingToken: null }, []]\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.\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 [{ type: 'AgentRevoke', tid: msg.tid }],\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 }\n}\n\n// ── Connect helper ────────────────────────────────────────────────────────────\n\nexport type AgentConnectConnectOptions = {\n id?: string // optional DOM id prefix\n}\n\ntype ConnectBag = {\n root: { 'data-scope': string; 'data-state': string }\n mintTrigger: { onClick: () => void; disabled: boolean }\n pendingTokenBox: { 'data-part': string; 'data-visible': boolean }\n copyConnectSnippetButton: { onClick: () => void; disabled: boolean }\n sessionsList: { 'data-part': string }\n sessionItem: (tid: string) => { 'data-part': string; 'data-tid': string }\n revokeButton: (tid: string) => { onClick: () => void }\n resumeBanner: { 'data-part': string; 'data-visible': boolean }\n resumeItem: (tid: string) => { 'data-part': string; 'data-tid': string }\n resumeButton: (tid: string) => { onClick: () => void }\n dismissButton: (tid: string) => { onClick: () => void }\n error: { 'data-part': string; 'data-visible': boolean; onClick: () => void }\n}\n\n/**\n * Builds prop bags for the view. See spec §9.1 and the @llui/components\n * dialog.ts pattern.\n */\nexport function connect<S>(\n get: (s: S) => AgentConnectState,\n send: Send<AgentConnectMsg>,\n _opts: AgentConnectConnectOptions = {},\n): (state: S) => ConnectBag {\n return (state) => {\n const s = get(state)\n return {\n root: { 'data-scope': 'agent-connect', 'data-state': s.status },\n mintTrigger: {\n onClick: () => send({ type: 'Mint' }),\n disabled: s.status === 'minting' || s.status === 'pending-claude' || s.status === 'active',\n },\n pendingTokenBox: { 'data-part': 'pending-token', 'data-visible': s.pendingToken !== null },\n copyConnectSnippetButton: {\n onClick: () => {\n if (s.pendingToken && typeof navigator !== 'undefined' && 'clipboard' in navigator) {\n void navigator.clipboard.writeText(s.pendingToken.connectSnippet)\n }\n },\n disabled: s.pendingToken === null,\n },\n sessionsList: { 'data-part': 'sessions-list' },\n sessionItem: (tid) => ({ 'data-part': 'session-item', 'data-tid': tid }),\n revokeButton: (tid) => ({ onClick: () => send({ type: 'Revoke', tid }) }),\n resumeBanner: { 'data-part': 'resume-banner', 'data-visible': s.resumable.length > 0 },\n resumeItem: (tid) => ({ 'data-part': 'resume-item', 'data-tid': tid }),\n resumeButton: (tid) => ({ onClick: () => send({ type: 'Resume', tid }) }),\n dismissButton: (tid) => ({\n // For dismiss, we currently just remove the resumable record locally.\n // A \"dismiss forever\" flag could land in a follow-up; for v1, dismiss\n // is a client-side-only state prune by reusing the Revoke Msg path\n // with intent-split; for now Emit Revoke which both revokes server-side\n // AND removes locally. Alternative: emit a new DismissResume msg —\n // spec §9.1 lists dismissButton but doesn't spell out the emitted msg.\n // V1 pragmatic choice: same as revoke (mark revoked on server).\n onClick: () => send({ type: 'Revoke', tid }),\n }),\n error: {\n 'data-part': 'error',\n 'data-visible': s.error !== null,\n onClick: () => send({ type: 'ClearError' }),\n },\n }\n }\n}\n"]}