@llui/agent 0.0.32 → 0.0.35

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 (145) hide show
  1. package/README.md +82 -1
  2. package/dist/client/agentConfirm.d.ts +48 -18
  3. package/dist/client/agentConfirm.d.ts.map +1 -1
  4. package/dist/client/agentConfirm.js +28 -25
  5. package/dist/client/agentConfirm.js.map +1 -1
  6. package/dist/client/agentConnect.d.ts +95 -34
  7. package/dist/client/agentConnect.d.ts.map +1 -1
  8. package/dist/client/agentConnect.js +85 -47
  9. package/dist/client/agentConnect.js.map +1 -1
  10. package/dist/client/agentLog.d.ts +31 -14
  11. package/dist/client/agentLog.d.ts.map +1 -1
  12. package/dist/client/agentLog.js +39 -20
  13. package/dist/client/agentLog.js.map +1 -1
  14. package/dist/client/effect-handler.d.ts +23 -0
  15. package/dist/client/effect-handler.d.ts.map +1 -1
  16. package/dist/client/effect-handler.js +185 -126
  17. package/dist/client/effect-handler.js.map +1 -1
  18. package/dist/client/effects.d.ts +13 -2
  19. package/dist/client/effects.d.ts.map +1 -1
  20. package/dist/client/effects.js.map +1 -1
  21. package/dist/client/factory.d.ts +55 -3
  22. package/dist/client/factory.d.ts.map +1 -1
  23. package/dist/client/factory.js +30 -5
  24. package/dist/client/factory.js.map +1 -1
  25. package/dist/client/rpc/describe-visible-content.d.ts +18 -5
  26. package/dist/client/rpc/describe-visible-content.d.ts.map +1 -1
  27. package/dist/client/rpc/describe-visible-content.js +112 -7
  28. package/dist/client/rpc/describe-visible-content.js.map +1 -1
  29. package/dist/client/rpc/list-actions.d.ts +52 -2
  30. package/dist/client/rpc/list-actions.d.ts.map +1 -1
  31. package/dist/client/rpc/list-actions.js +187 -5
  32. package/dist/client/rpc/list-actions.js.map +1 -1
  33. package/dist/client/rpc/query-state.d.ts +32 -0
  34. package/dist/client/rpc/query-state.d.ts.map +1 -0
  35. package/dist/client/rpc/query-state.js +82 -0
  36. package/dist/client/rpc/query-state.js.map +1 -0
  37. package/dist/client/rpc/send-message.d.ts +2 -0
  38. package/dist/client/rpc/send-message.d.ts.map +1 -1
  39. package/dist/client/rpc/send-message.js +119 -9
  40. package/dist/client/rpc/send-message.js.map +1 -1
  41. package/dist/client/rpc/would-dispatch.d.ts +66 -0
  42. package/dist/client/rpc/would-dispatch.d.ts.map +1 -0
  43. package/dist/client/rpc/would-dispatch.js +21 -0
  44. package/dist/client/rpc/would-dispatch.js.map +1 -0
  45. package/dist/client/ws-client.d.ts +3 -1
  46. package/dist/client/ws-client.d.ts.map +1 -1
  47. package/dist/client/ws-client.js +29 -0
  48. package/dist/client/ws-client.js.map +1 -1
  49. package/dist/codecs.d.ts +107 -0
  50. package/dist/codecs.d.ts.map +1 -0
  51. package/dist/codecs.js +166 -0
  52. package/dist/codecs.js.map +1 -0
  53. package/dist/protocol.d.ts +172 -12
  54. package/dist/protocol.d.ts.map +1 -1
  55. package/dist/protocol.js +7 -1
  56. package/dist/protocol.js.map +1 -1
  57. package/dist/server/cloudflare/durable-object.d.ts +11 -4
  58. package/dist/server/cloudflare/durable-object.d.ts.map +1 -1
  59. package/dist/server/cloudflare/durable-object.js.map +1 -1
  60. package/dist/server/cloudflare/index.d.ts +8 -4
  61. package/dist/server/cloudflare/index.d.ts.map +1 -1
  62. package/dist/server/cloudflare/index.js +8 -4
  63. package/dist/server/cloudflare/index.js.map +1 -1
  64. package/dist/server/cloudflare/worker.d.ts +10 -2
  65. package/dist/server/cloudflare/worker.d.ts.map +1 -1
  66. package/dist/server/cloudflare/worker.js +13 -6
  67. package/dist/server/cloudflare/worker.js.map +1 -1
  68. package/dist/server/core-entry.d.ts +2 -2
  69. package/dist/server/core-entry.d.ts.map +1 -1
  70. package/dist/server/core-entry.js +1 -1
  71. package/dist/server/core-entry.js.map +1 -1
  72. package/dist/server/core.d.ts +1 -3
  73. package/dist/server/core.d.ts.map +1 -1
  74. package/dist/server/core.js +13 -12
  75. package/dist/server/core.js.map +1 -1
  76. package/dist/server/factory.d.ts +1 -1
  77. package/dist/server/factory.d.ts.map +1 -1
  78. package/dist/server/factory.js +1 -2
  79. package/dist/server/factory.js.map +1 -1
  80. package/dist/server/http/mint.d.ts +6 -1
  81. package/dist/server/http/mint.d.ts.map +1 -1
  82. package/dist/server/http/mint.js +14 -6
  83. package/dist/server/http/mint.js.map +1 -1
  84. package/dist/server/http/resume.d.ts +3 -1
  85. package/dist/server/http/resume.d.ts.map +1 -1
  86. package/dist/server/http/resume.js +9 -7
  87. package/dist/server/http/resume.js.map +1 -1
  88. package/dist/server/index.d.ts +2 -2
  89. package/dist/server/index.d.ts.map +1 -1
  90. package/dist/server/index.js +1 -1
  91. package/dist/server/index.js.map +1 -1
  92. package/dist/server/lap/confirm-result.d.ts +0 -1
  93. package/dist/server/lap/confirm-result.d.ts.map +1 -1
  94. package/dist/server/lap/confirm-result.js +1 -1
  95. package/dist/server/lap/confirm-result.js.map +1 -1
  96. package/dist/server/lap/describe.d.ts +13 -2
  97. package/dist/server/lap/describe.d.ts.map +1 -1
  98. package/dist/server/lap/describe.js +23 -6
  99. package/dist/server/lap/describe.js.map +1 -1
  100. package/dist/server/lap/forward.d.ts +13 -1
  101. package/dist/server/lap/forward.d.ts.map +1 -1
  102. package/dist/server/lap/forward.js +75 -1
  103. package/dist/server/lap/forward.js.map +1 -1
  104. package/dist/server/lap/message.d.ts +0 -1
  105. package/dist/server/lap/message.d.ts.map +1 -1
  106. package/dist/server/lap/message.js +1 -1
  107. package/dist/server/lap/message.js.map +1 -1
  108. package/dist/server/lap/observe.d.ts +0 -1
  109. package/dist/server/lap/observe.d.ts.map +1 -1
  110. package/dist/server/lap/observe.js +1 -1
  111. package/dist/server/lap/observe.js.map +1 -1
  112. package/dist/server/lap/router.d.ts.map +1 -1
  113. package/dist/server/lap/router.js +7 -1
  114. package/dist/server/lap/router.js.map +1 -1
  115. package/dist/server/lap/wait.d.ts +0 -1
  116. package/dist/server/lap/wait.d.ts.map +1 -1
  117. package/dist/server/lap/wait.js +1 -1
  118. package/dist/server/lap/wait.js.map +1 -1
  119. package/dist/server/options.d.ts +7 -5
  120. package/dist/server/options.d.ts.map +1 -1
  121. package/dist/server/options.js.map +1 -1
  122. package/dist/server/token-store.d.ts +22 -0
  123. package/dist/server/token-store.d.ts.map +1 -1
  124. package/dist/server/token-store.js +24 -0
  125. package/dist/server/token-store.js.map +1 -1
  126. package/dist/server/token.d.ts +32 -17
  127. package/dist/server/token.d.ts.map +1 -1
  128. package/dist/server/token.js +40 -103
  129. package/dist/server/token.js.map +1 -1
  130. package/dist/server/web/upgrade.d.ts +1 -1
  131. package/dist/server/web/upgrade.js +1 -1
  132. package/dist/server/web/upgrade.js.map +1 -1
  133. package/dist/server/ws/pairing-registry.d.ts +22 -6
  134. package/dist/server/ws/pairing-registry.d.ts.map +1 -1
  135. package/dist/server/ws/pairing-registry.js +49 -0
  136. package/dist/server/ws/pairing-registry.js.map +1 -1
  137. package/dist/server/ws/upgrade.d.ts +0 -1
  138. package/dist/server/ws/upgrade.d.ts.map +1 -1
  139. package/dist/server/ws/upgrade.js +12 -4
  140. package/dist/server/ws/upgrade.js.map +1 -1
  141. package/dist/state-diff.d.ts +52 -0
  142. package/dist/state-diff.d.ts.map +1 -0
  143. package/dist/state-diff.js +119 -0
  144. package/dist/state-diff.js.map +1 -0
  145. package/package.json +7 -3
@@ -1,132 +1,173 @@
1
+ /**
2
+ * Top-level dispatcher. The switch is intentionally thin — each
3
+ * `case` delegates to a per-effect function below. Splitting was
4
+ * motivated by the previous 150-line monolith mixing HTTP, WS, and
5
+ * browser-only side effects in one switch; per-effect handlers are
6
+ * directly unit-testable and the dispatcher reads as a flat catalogue
7
+ * of supported effect types.
8
+ */
1
9
  export function createEffectHandler(host) {
2
10
  const doFetch = host.fetch ?? fetch.bind(globalThis);
3
11
  return async function handle(effect) {
4
12
  switch (effect.type) {
5
- case 'AgentMintRequest': {
6
- try {
7
- const res = await doFetch(effect.mintUrl, { method: 'POST', credentials: 'include' });
8
- if (!res.ok) {
9
- const detail = await safeText(res);
10
- host.send(host.wrapAgentConnect({
11
- type: 'MintFailed',
12
- error: { code: `http-${res.status}`, detail },
13
- }));
14
- return;
15
- }
16
- const body = (await res.json());
17
- host.send(host.wrapAgentConnect({
18
- type: 'MintSucceeded',
19
- token: body.token,
20
- tid: body.tid,
21
- lapUrl: body.lapUrl,
22
- wsUrl: body.wsUrl,
23
- expiresAt: body.expiresAt,
24
- }));
25
- }
26
- catch (e) {
27
- host.send(host.wrapAgentConnect({
28
- type: 'MintFailed',
29
- error: { code: 'network', detail: String(e) },
30
- }));
31
- }
32
- return;
33
- }
34
- case 'AgentOpenWS': {
35
- host.openWs(effect.token, effect.wsUrl);
36
- return;
37
- }
38
- case 'AgentCloseWS': {
39
- host.closeWs();
40
- return;
41
- }
42
- case 'AgentResumeCheck': {
43
- // For v1 we call /agent/resume/list via the mint URL's origin; the mintUrl is a POST
44
- // endpoint at `/agent/mint`, so we derive the origin and hit `/agent/resume/list`.
45
- const origin = deriveOrigin(host);
46
- if (!origin)
47
- return;
48
- try {
49
- const res = await doFetch(`${origin}/agent/resume/list`, {
50
- method: 'POST',
51
- credentials: 'include',
52
- headers: { 'content-type': 'application/json' },
53
- body: JSON.stringify({ tids: effect.tids }),
54
- });
55
- if (!res.ok)
56
- return;
57
- const body = (await res.json());
58
- host.send(host.wrapAgentConnect({ type: 'ResumeListLoaded', sessions: body.sessions }));
59
- }
60
- catch {
61
- /* quiet failure; user can retry */
62
- }
63
- return;
64
- }
65
- case 'AgentResumeClaim': {
66
- const origin = deriveOrigin(host);
67
- if (!origin)
68
- return;
69
- try {
70
- const res = await doFetch(`${origin}/agent/resume/claim`, {
71
- method: 'POST',
72
- credentials: 'include',
73
- headers: { 'content-type': 'application/json' },
74
- body: JSON.stringify({ tid: effect.tid }),
75
- });
76
- if (!res.ok)
77
- return;
78
- const body = (await res.json());
79
- host.openWs(body.token, body.wsUrl);
80
- host.send(host.wrapAgentConnect({ type: 'WsOpened' }));
81
- }
82
- catch {
83
- /* quiet */
84
- }
85
- return;
86
- }
87
- case 'AgentRevoke': {
88
- const origin = deriveOrigin(host);
89
- if (!origin)
90
- return;
91
- try {
92
- await doFetch(`${origin}/agent/revoke`, {
93
- method: 'POST',
94
- credentials: 'include',
95
- headers: { 'content-type': 'application/json' },
96
- body: JSON.stringify({ tid: effect.tid }),
97
- });
98
- }
99
- catch {
100
- /* quiet */
101
- }
102
- return;
103
- }
104
- case 'AgentSessionsList': {
105
- const origin = deriveOrigin(host);
106
- if (!origin)
107
- return;
108
- try {
109
- const res = await doFetch(`${origin}/agent/sessions`, {
110
- method: 'GET',
111
- credentials: 'include',
112
- });
113
- if (!res.ok)
114
- return;
115
- const body = (await res.json());
116
- host.send(host.wrapAgentConnect({ type: 'SessionsLoaded', sessions: body.sessions }));
117
- }
118
- catch {
119
- /* quiet */
120
- }
121
- return;
122
- }
123
- case 'AgentForwardMsg': {
124
- host.forward(effect.payload);
125
- return;
126
- }
13
+ case 'AgentMintRequest':
14
+ return handleMintRequest(host, effect, doFetch);
15
+ case 'AgentOpenWS':
16
+ return handleOpenWs(host, effect);
17
+ case 'AgentCloseWS':
18
+ return handleCloseWs(host);
19
+ case 'AgentResumeCheck':
20
+ return handleResumeCheck(host, effect, doFetch);
21
+ case 'AgentResumeClaim':
22
+ return handleResumeClaim(host, effect, doFetch);
23
+ case 'AgentRevoke':
24
+ return handleRevoke(host, effect, doFetch);
25
+ case 'AgentSessionsList':
26
+ return handleSessionsList(host, doFetch);
27
+ case 'AgentForwardMsg':
28
+ return handleForwardMsg(host, effect);
29
+ case 'AgentClipboardWrite':
30
+ return handleClipboardWrite(effect);
127
31
  }
128
32
  };
129
33
  }
34
+ // ── HTTP-bound handlers ─────────────────────────────────────────────
35
+ async function handleMintRequest(host, effect, doFetch) {
36
+ // Derive a default `mintUrl` from `agentBasePath` so consumers can
37
+ // change the base path in one place (the effect handler) without
38
+ // also having to keep the `agentConnect` opts in sync. `agentBase`
39
+ // accepts both absolute paths and full URLs.
40
+ const base = agentBase(host);
41
+ if (!base)
42
+ return;
43
+ const mintUrl = effect.mintUrl ?? `${base}/mint`;
44
+ try {
45
+ const res = await doFetch(mintUrl, { method: 'POST', credentials: 'include' });
46
+ if (!res.ok) {
47
+ const detail = await safeText(res);
48
+ host.send(host.wrapAgentConnect({
49
+ type: 'MintFailed',
50
+ error: { code: `http-${res.status}`, detail },
51
+ }));
52
+ return;
53
+ }
54
+ const body = (await res.json());
55
+ host.send(host.wrapAgentConnect({
56
+ type: 'MintSucceeded',
57
+ token: body.token,
58
+ tid: body.tid,
59
+ lapUrl: body.lapUrl,
60
+ wsUrl: body.wsUrl,
61
+ expiresAt: body.expiresAt,
62
+ }));
63
+ }
64
+ catch (e) {
65
+ host.send(host.wrapAgentConnect({
66
+ type: 'MintFailed',
67
+ error: { code: 'network', detail: String(e) },
68
+ }));
69
+ }
70
+ }
71
+ async function handleResumeCheck(host, effect, doFetch) {
72
+ const base = agentBase(host);
73
+ if (!base)
74
+ return;
75
+ try {
76
+ const res = await doFetch(`${base}/resume/list`, {
77
+ method: 'POST',
78
+ credentials: 'include',
79
+ headers: { 'content-type': 'application/json' },
80
+ body: JSON.stringify({ tids: effect.tids }),
81
+ });
82
+ if (!res.ok)
83
+ return;
84
+ const body = (await res.json());
85
+ host.send(host.wrapAgentConnect({ type: 'ResumeListLoaded', sessions: body.sessions }));
86
+ }
87
+ catch {
88
+ /* quiet failure; user can retry */
89
+ }
90
+ }
91
+ async function handleResumeClaim(host, effect, doFetch) {
92
+ const base = agentBase(host);
93
+ if (!base)
94
+ return;
95
+ try {
96
+ const res = await doFetch(`${base}/resume/claim`, {
97
+ method: 'POST',
98
+ credentials: 'include',
99
+ headers: { 'content-type': 'application/json' },
100
+ body: JSON.stringify({ tid: effect.tid }),
101
+ });
102
+ if (!res.ok)
103
+ return;
104
+ const body = (await res.json());
105
+ host.openWs(body.token, body.wsUrl);
106
+ host.send(host.wrapAgentConnect({ type: 'WsOpened' }));
107
+ }
108
+ catch {
109
+ /* quiet */
110
+ }
111
+ }
112
+ async function handleRevoke(host, effect, doFetch) {
113
+ const base = agentBase(host);
114
+ if (!base)
115
+ return;
116
+ try {
117
+ await doFetch(`${base}/revoke`, {
118
+ method: 'POST',
119
+ credentials: 'include',
120
+ headers: { 'content-type': 'application/json' },
121
+ body: JSON.stringify({ tid: effect.tid }),
122
+ });
123
+ }
124
+ catch {
125
+ /* quiet */
126
+ }
127
+ }
128
+ async function handleSessionsList(host, doFetch) {
129
+ const base = agentBase(host);
130
+ if (!base)
131
+ return;
132
+ try {
133
+ const res = await doFetch(`${base}/sessions`, {
134
+ method: 'GET',
135
+ credentials: 'include',
136
+ });
137
+ if (!res.ok)
138
+ return;
139
+ const body = (await res.json());
140
+ host.send(host.wrapAgentConnect({ type: 'SessionsLoaded', sessions: body.sessions }));
141
+ }
142
+ catch {
143
+ /* quiet */
144
+ }
145
+ }
146
+ // ── WS-bound handlers ───────────────────────────────────────────────
147
+ function handleOpenWs(host, effect) {
148
+ host.openWs(effect.token, effect.wsUrl);
149
+ }
150
+ function handleCloseWs(host) {
151
+ host.closeWs();
152
+ }
153
+ // ── Local handlers (no network) ─────────────────────────────────────
154
+ function handleForwardMsg(host, effect) {
155
+ host.forward(effect.payload);
156
+ }
157
+ async function handleClipboardWrite(effect) {
158
+ // Browser-only — `navigator.clipboard` is undefined in Node/jsdom
159
+ // test environments. Silently no-op rather than throw, matching the
160
+ // rest of the agent effect handlers' failure-quiet pattern.
161
+ if (typeof navigator === 'undefined' || !('clipboard' in navigator))
162
+ return;
163
+ try {
164
+ await navigator.clipboard.writeText(effect.text);
165
+ }
166
+ catch {
167
+ /* quiet — clipboard permission denied or document not focused */
168
+ }
169
+ }
170
+ // ── Helpers ─────────────────────────────────────────────────────────
130
171
  async function safeText(res) {
131
172
  try {
132
173
  return await res.text();
@@ -135,12 +176,30 @@ async function safeText(res) {
135
176
  return '';
136
177
  }
137
178
  }
138
- function deriveOrigin(_host) {
139
- // When running in the browser, `location.origin` is correct (the agent endpoints
140
- // are same-origin with the app per spec §6.2). When not in a browser (tests),
141
- // the test-side host can override by monkeypatching in its own effect handler.
179
+ function deriveOrigin() {
180
+ // When running in the browser, `location.origin` is correct for
181
+ // same-origin agent endpoints. Tests override `host.fetch` and
182
+ // short-circuit before this is reached.
142
183
  if (typeof location !== 'undefined')
143
184
  return location.origin;
144
185
  return null;
145
186
  }
187
+ const ABSOLUTE_URL_RE = /^https?:\/\//i;
188
+ /**
189
+ * Resolve the absolute base URL for agent HTTP endpoints. Accepts both
190
+ * absolute paths (`/agent`) and full URLs (`https://api.example/agent`)
191
+ * — the absolute URL form lets consumers point at a cross-origin agent
192
+ * server without pre-composing every endpoint URL. Trailing slashes
193
+ * are normalized so callers can always concatenate `${base}/mint`.
194
+ */
195
+ function agentBase(host) {
196
+ const raw = host.agentBasePath ?? '/agent';
197
+ const trimmed = raw.endsWith('/') ? raw.slice(0, -1) : raw;
198
+ if (ABSOLUTE_URL_RE.test(trimmed))
199
+ return trimmed;
200
+ const origin = deriveOrigin();
201
+ if (!origin)
202
+ return null;
203
+ return `${origin}${trimmed}`;
204
+ }
146
205
  //# sourceMappingURL=effect-handler.js.map
@@ -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'\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 /** 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\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 try {\n const res = await doFetch(effect.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 return\n }\n case 'AgentOpenWS': {\n host.openWs(effect.token, effect.wsUrl)\n return\n }\n case 'AgentCloseWS': {\n host.closeWs()\n return\n }\n case 'AgentResumeCheck': {\n // For v1 we call /agent/resume/list via the mint URL's origin; the mintUrl is a POST\n // endpoint at `/agent/mint`, so we derive the origin and hit `/agent/resume/list`.\n const origin = deriveOrigin(host)\n if (!origin) return\n try {\n const res = await doFetch(`${origin}/agent/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 return\n }\n case 'AgentResumeClaim': {\n const origin = deriveOrigin(host)\n if (!origin) return\n try {\n const res = await doFetch(`${origin}/agent/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 return\n }\n case 'AgentRevoke': {\n const origin = deriveOrigin(host)\n if (!origin) return\n try {\n await doFetch(`${origin}/agent/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 return\n }\n case 'AgentSessionsList': {\n const origin = deriveOrigin(host)\n if (!origin) return\n try {\n const res = await doFetch(`${origin}/agent/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 return\n }\n case 'AgentForwardMsg': {\n host.forward(effect.payload)\n return\n }\n }\n }\n}\n\nasync function safeText(res: Response): Promise<string> {\n try {\n return await res.text()\n } catch {\n return ''\n }\n}\n\nfunction deriveOrigin(_host: EffectHandlerHost): string | null {\n // When running in the browser, `location.origin` is correct (the agent endpoints\n // are same-origin with the app per spec §6.2). When not in a browser (tests),\n // the test-side host can override by monkeypatching in its own effect handler.\n if (typeof location !== 'undefined') return location.origin\n return null\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'\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 /** 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 * 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 }\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,7 +1,15 @@
1
1
  import type { AgentToken } from '../protocol.js';
2
- export type AgentEffect = {
2
+ export type AgentEffect =
3
+ /**
4
+ * Mint a fresh agent token. `mintUrl` is optional — when omitted the
5
+ * effect handler derives it from `EffectHandlerHost.agentBasePath`
6
+ * (default `/agent`), producing `<agentBasePath>/mint`. Pass an
7
+ * explicit value when the mint endpoint lives outside the configured
8
+ * base path.
9
+ */
10
+ {
3
11
  type: 'AgentMintRequest';
4
- mintUrl: string;
12
+ mintUrl?: string;
5
13
  } | {
6
14
  type: 'AgentOpenWS';
7
15
  token: AgentToken;
@@ -22,6 +30,9 @@ export type AgentEffect = {
22
30
  } | {
23
31
  type: 'AgentForwardMsg';
24
32
  payload: unknown;
33
+ } | {
34
+ type: 'AgentClipboardWrite';
35
+ text: string;
25
36
  };
26
37
  export type AgentEffectHandler = (effect: AgentEffect) => Promise<void>;
27
38
  //# 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,GACnB;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC7C;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,CAAA;AAGjD,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,CAAA;AAGjD,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 | { 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\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// Handler implementation lands in Plan 7 alongside the WS client.\nexport type AgentEffectHandler = (effect: AgentEffect) => Promise<void>\n"]}
@@ -2,13 +2,44 @@ import type { AppHandle } from '@llui/dom';
2
2
  import type { AgentEffect } from './effects.js';
3
3
  import type { AgentConfirmState } from './agentConfirm.js';
4
4
  import type { AgentDocs, AgentContext, MessageAnnotations } from '../protocol.js';
5
+ import { type CodecRegistry } from '../codecs.js';
6
+ /**
7
+ * The shape the compiler emits as `__msgSchema`. Mirrors `MsgField`
8
+ * from `@llui/vite-plugin/src/msg-schema.ts`. Three coexisting forms:
9
+ *
10
+ * 1. Bare primitive: `'string' | 'number' | 'boolean' | 'unknown'`
11
+ * and bare enum: `{enum: [...]}`. Compact form for unannotated
12
+ * required fields.
13
+ * 2. Bare nested types: `{kind: 'object', shape}` for inline /
14
+ * followed-via-typeIndex shapes; `{kind: 'array', element}` for
15
+ * `T[]` / `readonly T[]` / `Array<T>`. The synthesizer recurses
16
+ * to build copy-paste-ready nested examples.
17
+ * 3. Rich descriptor: wraps any of the above with `{optional?,
18
+ * priority?, hint?}` carrying TS optionality and `@should` hints.
19
+ */
20
+ export type MsgSchemaBareType = string | {
21
+ enum: string[];
22
+ } | {
23
+ kind: 'object';
24
+ shape: Record<string, MsgSchemaField>;
25
+ } | {
26
+ kind: 'array';
27
+ element: MsgSchemaBareType;
28
+ };
29
+ export type MsgSchemaField = MsgSchemaBareType | {
30
+ type: MsgSchemaBareType;
31
+ optional?: boolean;
32
+ priority?: 'should';
33
+ hint?: string;
34
+ };
35
+ export type MsgSchemaShape = {
36
+ discriminant: string;
37
+ variants: Record<string, Record<string, MsgSchemaField>>;
38
+ };
5
39
  type ComponentMetadata = {
6
40
  __msgSchema?: unknown;
7
41
  __stateSchema?: unknown;
8
42
  __msgAnnotations?: Record<string, MessageAnnotations>;
9
- __bindingDescriptors?: Array<{
10
- variant: string;
11
- }>;
12
43
  __schemaHash?: string;
13
44
  name: string;
14
45
  agentAffordances?: (state: unknown) => Array<{
@@ -36,6 +67,27 @@ export type CreateAgentClientOpts<State, Msg> = {
36
67
  */
37
68
  wrapLogMsg?: (m: unknown) => Msg;
38
69
  };
70
+ /**
71
+ * Codec registry for non-JSON-safe values (Date, Blob, Map, …)
72
+ * crossing the LAP boundary. Defaults to `makeDefaultCodecs()`
73
+ * which ships `iso-date` and `epoch-millis`. Provide a custom
74
+ * registry to register additional codecs (e.g. `base64-blob` for
75
+ * file uploads). See `@llui/agent/codecs` for the convention.
76
+ */
77
+ codecs?: CodecRegistry;
78
+ /**
79
+ * Base path for agent HTTP endpoints. Default: `'/agent'` (matches
80
+ * the canonical paths in `@llui/vite-plugin`'s dev middleware and
81
+ * `@llui/agent/server`). The mint URL, resume URLs, and revoke URL
82
+ * derive from this so consumers don't have to keep them in sync.
83
+ *
84
+ * Override when:
85
+ * - **Cross-origin agent server**: pass the full base, e.g.
86
+ * `'https://api.example.com/agent'` or `'http://localhost:8787/agent'`.
87
+ * - **`@cloudflare/vite-plugin` in dev**: pass `'/cdn-cgi/agent'`
88
+ * because cloudflare-vite shadows non-`/cdn-cgi/*` routes.
89
+ */
90
+ agentBasePath?: string;
39
91
  };
40
92
  export type AgentClient = {
41
93
  effectHandler: (effect: AgentEffect) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/client/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EAEZ,kBAAkB,EAEnB,MAAM,gBAAgB,CAAA;AAIvB,KAAK,iBAAiB,GAAG;IACvB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;IACrD,oBAAoB,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACjD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAA;IACpF,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,YAAY,CAAA;CAChD,CAAA;AAED,MAAM,MAAM,qBAAqB,CAAC,KAAK,EAAE,GAAG,IAAI;IAC9C,MAAM,EAAE,SAAS,CAAA;IACjB,GAAG,EAAE,iBAAiB,CAAA;IACtB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,OAAO,GAAG,IAAI,CAAA;IAC3B,MAAM,EAAE;QACN,UAAU,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,OAAO,CAAA;QACjC,UAAU,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,iBAAiB,CAAA;QAC3C,cAAc,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,GAAG,CAAA;QACnC,cAAc,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,GAAG,CAAA;QACnC;;;;;WAKG;QACH,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,GAAG,CAAA;KACjC,CAAA;CACF,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,aAAa,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrD,KAAK,IAAI,IAAI,CAAA;IACb,IAAI,IAAI,IAAI,CAAA;CACb,CAAA;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAC1C,IAAI,EAAE,qBAAqB,CAAC,KAAK,EAAE,GAAG,CAAC,GACtC,WAAW,CAoJb"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/client/factory.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { attachWsClient } from './ws-client.js';
2
2
  import { createEffectHandler } from './effect-handler.js';
3
+ import { makeDefaultCodecs, encodeForWire, decodeFromWire } from '../codecs.js';
3
4
  export function createAgentClient(opts) {
4
5
  let ws = null;
5
6
  let wsClient = null;
@@ -52,14 +53,34 @@ export function createAgentClient(opts) {
52
53
  onRejectionEvt = null;
53
54
  errorListenersInstalled = false;
54
55
  }
56
+ // Codec registry handles non-JSON-safe values (Date, etc.) crossing
57
+ // the LAP boundary. `getState` encodes outgoing snapshots; `send`
58
+ // decodes incoming agent messages before they hit the reducer. The
59
+ // tagged-value convention is documented in `@llui/agent/codecs`.
60
+ const codecs = opts.codecs ?? makeDefaultCodecs();
55
61
  const rpcHost = {
56
- getState: () => opts.handle.getState(),
57
- send: (m) => opts.handle.send(m),
62
+ getState: () => encodeForWire(opts.handle.getState(), codecs),
63
+ send: (m) => opts.handle.send(decodeFromWire(m, codecs)),
58
64
  flush: () => opts.handle.flush(),
59
65
  subscribe: (listener) => opts.handle.subscribe(() => listener()),
60
66
  getAndClearDrainErrors: () => drainErrors.splice(0, drainErrors.length),
61
67
  getMsgAnnotations: () => opts.def.__msgAnnotations ?? null,
62
- getBindingDescriptors: () => opts.def.__bindingDescriptors ?? null,
68
+ // The compiler-injected message schema. Used by `list_actions` to
69
+ // synthesize payload examples for `@agentOnly` variants that have
70
+ // no live UI binding — the agent should still see them as
71
+ // affordances even though no human can click them.
72
+ getMsgSchema: () => opts.def.__msgSchema ?? null,
73
+ // Run the reducer in isolation for `would_dispatch`. Wraps the
74
+ // AppHandle's same-named method so the host doesn't need a direct
75
+ // reference to the live ComponentInstance.
76
+ runReducer: (msg) => opts.handle.runReducer(msg),
77
+ // Live binding descriptors: read from the runtime registry that
78
+ // tracks which Msg variants are dispatchable from currently-mounted
79
+ // event handlers. Empty array when the app wasn't compiled with
80
+ // agent metadata (no tagger pass) or has no view bindings yet —
81
+ // both produce the same "no live affordances" signal at the agent
82
+ // layer.
83
+ getBindingDescriptors: () => opts.handle.getBindingDescriptors(),
63
84
  getAgentAffordances: () => opts.def.agentAffordances ?? null,
64
85
  getAgentContext: () => opts.def.agentContext ?? null,
65
86
  getRootElement: () => opts.rootElement,
@@ -83,6 +104,7 @@ export function createAgentClient(opts) {
83
104
  send: (m) => opts.handle.send(m),
84
105
  wrapAgentConnect: (m) => opts.slices.wrapConnectMsg(m),
85
106
  forward: (payload) => opts.handle.send(payload),
107
+ agentBasePath: opts.agentBasePath,
86
108
  openWs: (token, wsUrl) => {
87
109
  if (ws)
88
110
  ws.close();
@@ -120,7 +142,7 @@ export function createAgentClient(opts) {
120
142
  continue;
121
143
  resolvedConfirms.add(entry.id);
122
144
  if (entry.status === 'approved') {
123
- wsClient?.resolveConfirm(entry.id, 'confirmed', opts.handle.getState());
145
+ wsClient?.resolveConfirm(entry.id, 'confirmed', encodeForWire(opts.handle.getState(), codecs));
124
146
  }
125
147
  else if (entry.status === 'rejected') {
126
148
  wsClient?.resolveConfirm(entry.id, 'user-cancelled');
@@ -134,7 +156,10 @@ export function createAgentClient(opts) {
134
156
  confirmPollTimer = setInterval(pollConfirms, 200);
135
157
  if (!stateSubscription) {
136
158
  stateSubscription = opts.handle.subscribe((state) => {
137
- wsClient?.emitStateUpdate('/', state);
159
+ // Same codec convention as `getState`: outgoing snapshots
160
+ // pass through the encoder so non-JSON-safe values (Date,
161
+ // etc.) become tagged-wire form.
162
+ wsClient?.emitStateUpdate('/', encodeForWire(state, codecs));
138
163
  });
139
164
  }
140
165
  installErrorListeners();