@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
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Walk data-agent-tagged subtrees and produce a structured outline.
3
+ * Buttons cross-reference __bindingDescriptors so Claude can tie
4
+ * visible text to variant names.
5
+ */
6
+ export function handleDescribeVisibleContent(host) {
7
+ const root = host.getRootElement();
8
+ if (!root)
9
+ return { outline: [] };
10
+ const out = [];
11
+ const allZones = Array.from(root.querySelectorAll('[data-agent]'));
12
+ // Only walk top-level zones; skip zones that are descendants of other zones
13
+ const topLevel = allZones.filter((zone) => !allZones.some((other) => other !== zone && other.contains(zone)));
14
+ for (const zone of topLevel) {
15
+ walk(zone, out);
16
+ }
17
+ return { outline: out };
18
+ }
19
+ function walk(el, out) {
20
+ const tag = el.tagName.toLowerCase();
21
+ const text = (el.textContent ?? '').trim();
22
+ if (/^h[1-6]$/.test(tag)) {
23
+ out.push({ kind: 'heading', level: Number(tag[1]), text });
24
+ return;
25
+ }
26
+ if (tag === 'button') {
27
+ out.push({
28
+ kind: 'button',
29
+ text,
30
+ disabled: el.disabled,
31
+ actionVariant: el.getAttribute('data-agent') ?? null,
32
+ });
33
+ return;
34
+ }
35
+ if (tag === 'a' && el.getAttribute('href')) {
36
+ out.push({ kind: 'link', text, href: el.getAttribute('href') ?? '' });
37
+ return;
38
+ }
39
+ if (tag === 'input') {
40
+ out.push({
41
+ kind: 'input',
42
+ label: el.getAttribute('aria-label') ?? el.getAttribute('name') ?? null,
43
+ value: el.value ?? null,
44
+ type: el.type ?? 'text',
45
+ });
46
+ return;
47
+ }
48
+ if (tag === 'ul' || tag === 'ol') {
49
+ const items = [];
50
+ for (const child of Array.from(el.children)) {
51
+ if (child.tagName.toLowerCase() === 'li') {
52
+ items.push({ kind: 'item', text: (child.textContent ?? '').trim() });
53
+ }
54
+ }
55
+ out.push({ kind: 'list', items });
56
+ return;
57
+ }
58
+ if (text.length > 0 && el.children.length === 0) {
59
+ out.push({ kind: 'text', text });
60
+ return;
61
+ }
62
+ for (const child of Array.from(el.children)) {
63
+ walk(child, out);
64
+ }
65
+ }
66
+ //# sourceMappingURL=describe-visible-content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"describe-visible-content.js","sourceRoot":"","sources":["../../../src/client/rpc/describe-visible-content.ts"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAAC,IAAyB;IACpE,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,EAAE,CAAA;IAClC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAA;IACjC,MAAM,GAAG,GAAkB,EAAE,CAAA;IAC7B,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC,CAAA;IAClE,4EAA4E;IAC5E,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAC9B,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAC5E,CAAA;IACD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACjB,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAA;AACzB,CAAC;AAED,SAAS,IAAI,CAAC,EAAW,EAAE,GAAkB;IAC3C,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAA;IACpC,MAAM,IAAI,GAAG,CAAC,EAAE,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAC1C,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;QAC1D,OAAM;IACR,CAAC;IACD,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;QACrB,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,QAAQ;YACd,IAAI;YACJ,QAAQ,EAAG,EAAwB,CAAC,QAAQ;YAC5C,aAAa,EAAE,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,IAAI,IAAI;SACrD,CAAC,CAAA;QACF,OAAM;IACR,CAAC;IACD,IAAI,GAAG,KAAK,GAAG,IAAI,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;QACrE,OAAM;IACR,CAAC;IACD,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACpB,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,IAAI;YACvE,KAAK,EAAG,EAAuB,CAAC,KAAK,IAAI,IAAI;YAC7C,IAAI,EAAG,EAAuB,CAAC,IAAI,IAAI,MAAM;SAC9C,CAAC,CAAA;QACF,OAAM;IACR,CAAC;IACD,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjC,MAAM,KAAK,GAAkB,EAAE,CAAA;QAC/B,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5C,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC;gBACzC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;YACtE,CAAC;QACH,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;QACjC,OAAM;IACR,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;QAChC,OAAM;IACR,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IAClB,CAAC;AACH,CAAC","sourcesContent":["import type { OutlineNode } from '../../protocol.js'\n\nexport type DescribeVisibleArgs = Record<string, never>\nexport type DescribeVisibleResult = { outline: OutlineNode[] }\n\nexport type DescribeVisibleHost = {\n getRootElement(): Element | null\n getBindingDescriptors(): Array<{ variant: string }> | null\n getMsgAnnotations(): Record<string, { intent: string | null; humanOnly: boolean }> | null\n}\n\n/**\n * Walk data-agent-tagged subtrees and produce a structured outline.\n * Buttons cross-reference __bindingDescriptors so Claude can tie\n * visible text to variant names.\n */\nexport function handleDescribeVisibleContent(host: DescribeVisibleHost): DescribeVisibleResult {\n const root = host.getRootElement()\n if (!root) return { outline: [] }\n const out: OutlineNode[] = []\n const allZones = Array.from(root.querySelectorAll('[data-agent]'))\n // Only walk top-level zones; skip zones that are descendants of other zones\n const topLevel = allZones.filter(\n (zone) => !allZones.some((other) => other !== zone && other.contains(zone)),\n )\n for (const zone of topLevel) {\n walk(zone, out)\n }\n return { outline: out }\n}\n\nfunction walk(el: Element, out: OutlineNode[]): void {\n const tag = el.tagName.toLowerCase()\n const text = (el.textContent ?? '').trim()\n if (/^h[1-6]$/.test(tag)) {\n out.push({ kind: 'heading', level: Number(tag[1]), text })\n return\n }\n if (tag === 'button') {\n out.push({\n kind: 'button',\n text,\n disabled: (el as HTMLButtonElement).disabled,\n actionVariant: el.getAttribute('data-agent') ?? null,\n })\n return\n }\n if (tag === 'a' && el.getAttribute('href')) {\n out.push({ kind: 'link', text, href: el.getAttribute('href') ?? '' })\n return\n }\n if (tag === 'input') {\n out.push({\n kind: 'input',\n label: el.getAttribute('aria-label') ?? el.getAttribute('name') ?? null,\n value: (el as HTMLInputElement).value ?? null,\n type: (el as HTMLInputElement).type ?? 'text',\n })\n return\n }\n if (tag === 'ul' || tag === 'ol') {\n const items: OutlineNode[] = []\n for (const child of Array.from(el.children)) {\n if (child.tagName.toLowerCase() === 'li') {\n items.push({ kind: 'item', text: (child.textContent ?? '').trim() })\n }\n }\n out.push({ kind: 'list', items })\n return\n }\n if (text.length > 0 && el.children.length === 0) {\n out.push({ kind: 'text', text })\n return\n }\n for (const child of Array.from(el.children)) {\n walk(child, out)\n }\n}\n"]}
@@ -0,0 +1,15 @@
1
+ export type GetStateArgs = {
2
+ path?: string;
3
+ };
4
+ export type GetStateResult = {
5
+ state: unknown;
6
+ };
7
+ export type GetStateHost = {
8
+ getState(): unknown;
9
+ };
10
+ /**
11
+ * Spec §8.2: get_state returns a JSON-pointer-scoped slice of the
12
+ * app's current state, or the whole root state if no path is given.
13
+ */
14
+ export declare function handleGetState(host: GetStateHost, args: GetStateArgs): GetStateResult;
15
+ //# sourceMappingURL=get-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-state.d.ts","sourceRoot":"","sources":["../../../src/client/rpc/get-state.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAC5C,MAAM,MAAM,cAAc,GAAG;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,CAAA;AAC/C,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,IAAI,OAAO,CAAA;CAAE,CAAA;AAElD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,GAAG,cAAc,CAIrF"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Spec §8.2: get_state returns a JSON-pointer-scoped slice of the
3
+ * app's current state, or the whole root state if no path is given.
4
+ */
5
+ export function handleGetState(host, args) {
6
+ const state = host.getState();
7
+ if (!args.path)
8
+ return { state };
9
+ return { state: resolveJsonPointer(state, args.path) };
10
+ }
11
+ function resolveJsonPointer(root, pointer) {
12
+ if (pointer === '' || pointer === '/')
13
+ return root;
14
+ // Accept either "/a/b" or "a/b"
15
+ const parts = pointer.split('/').filter((p) => p !== '');
16
+ let cur = root;
17
+ for (const raw of parts) {
18
+ // RFC 6901 escaping: ~1 → /, ~0 → ~
19
+ const key = raw.replace(/~1/g, '/').replace(/~0/g, '~');
20
+ if (cur === null || cur === undefined)
21
+ return undefined;
22
+ if (Array.isArray(cur)) {
23
+ const idx = Number(key);
24
+ if (!Number.isInteger(idx))
25
+ return undefined;
26
+ cur = cur[idx];
27
+ }
28
+ else if (typeof cur === 'object') {
29
+ cur = cur[key];
30
+ }
31
+ else {
32
+ return undefined;
33
+ }
34
+ }
35
+ return cur;
36
+ }
37
+ //# sourceMappingURL=get-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-state.js","sourceRoot":"","sources":["../../../src/client/rpc/get-state.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAkB,EAAE,IAAkB;IACnE,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAA;IAC7B,IAAI,CAAC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,KAAK,EAAE,CAAA;IAChC,OAAO,EAAE,KAAK,EAAE,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAA;AACxD,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAa,EAAE,OAAe;IACxD,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,IAAI,CAAA;IAClD,gCAAgC;IAChC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAA;IACxD,IAAI,GAAG,GAAY,IAAI,CAAA;IACvB,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,oCAAoC;QACpC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QACvD,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QACvD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;YACvB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC;gBAAE,OAAO,SAAS,CAAA;YAC5C,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAA;QAChB,CAAC;aAAM,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YACnC,GAAG,GAAI,GAA+B,CAAC,GAAG,CAAC,CAAA;QAC7C,CAAC;aAAM,CAAC;YACN,OAAO,SAAS,CAAA;QAClB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC","sourcesContent":["export type GetStateArgs = { path?: string }\nexport type GetStateResult = { state: unknown }\nexport type GetStateHost = { getState(): unknown }\n\n/**\n * Spec §8.2: get_state returns a JSON-pointer-scoped slice of the\n * app's current state, or the whole root state if no path is given.\n */\nexport function handleGetState(host: GetStateHost, args: GetStateArgs): GetStateResult {\n const state = host.getState()\n if (!args.path) return { state }\n return { state: resolveJsonPointer(state, args.path) }\n}\n\nfunction resolveJsonPointer(root: unknown, pointer: string): unknown {\n if (pointer === '' || pointer === '/') return root\n // Accept either \"/a/b\" or \"a/b\"\n const parts = pointer.split('/').filter((p) => p !== '')\n let cur: unknown = root\n for (const raw of parts) {\n // RFC 6901 escaping: ~1 → /, ~0 → ~\n const key = raw.replace(/~1/g, '/').replace(/~0/g, '~')\n if (cur === null || cur === undefined) return undefined\n if (Array.isArray(cur)) {\n const idx = Number(key)\n if (!Number.isInteger(idx)) return undefined\n cur = cur[idx]\n } else if (typeof cur === 'object') {\n cur = (cur as Record<string, unknown>)[key]\n } else {\n return undefined\n }\n }\n return cur\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import type { MessageAnnotations } from '../../protocol.js';
2
+ type Binding = {
3
+ variant: string;
4
+ };
5
+ type Annotations = Record<string, MessageAnnotations>;
6
+ export type ListActionsHost = {
7
+ getState(): unknown;
8
+ getBindingDescriptors(): Binding[] | null;
9
+ getMsgAnnotations(): Annotations | null;
10
+ getAgentAffordances(): ((state: unknown) => Array<{
11
+ type: string;
12
+ [k: string]: unknown;
13
+ }>) | null;
14
+ };
15
+ export type ListActionsResult = {
16
+ actions: Array<{
17
+ variant: string;
18
+ intent: string;
19
+ requiresConfirm: boolean;
20
+ source: 'binding' | 'always-affordable';
21
+ selectorHint: string | null;
22
+ payloadHint: object | null;
23
+ }>;
24
+ };
25
+ export declare function handleListActions(host: ListActionsHost): ListActionsResult;
26
+ export {};
27
+ //# sourceMappingURL=list-actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-actions.d.ts","sourceRoot":"","sources":["../../../src/client/rpc/list-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAE3D,KAAK,OAAO,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAClC,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;AAErD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,IAAI,OAAO,CAAA;IACnB,qBAAqB,IAAI,OAAO,EAAE,GAAG,IAAI,CAAA;IACzC,iBAAiB,IAAI,WAAW,GAAG,IAAI,CAAA;IACvC,mBAAmB,IAAI,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC,GAAG,IAAI,CAAA;CAClG,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,KAAK,CAAC;QACb,OAAO,EAAE,MAAM,CAAA;QACf,MAAM,EAAE,MAAM,CAAA;QACd,eAAe,EAAE,OAAO,CAAA;QACxB,MAAM,EAAE,SAAS,GAAG,mBAAmB,CAAA;QACvC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;QAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAC3B,CAAC,CAAA;CACH,CAAA;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,iBAAiB,CAsC1E"}
@@ -0,0 +1,38 @@
1
+ export function handleListActions(host) {
2
+ const annotations = host.getMsgAnnotations() ?? {};
3
+ const state = host.getState();
4
+ const descriptors = host.getBindingDescriptors() ?? [];
5
+ const affordances = host.getAgentAffordances()?.(state) ?? [];
6
+ const out = [];
7
+ // From bindings
8
+ for (const d of descriptors) {
9
+ const ann = annotations[d.variant];
10
+ if (ann?.humanOnly)
11
+ continue;
12
+ out.push({
13
+ variant: d.variant,
14
+ intent: ann?.intent ?? d.variant,
15
+ requiresConfirm: ann?.requiresConfirm ?? false,
16
+ source: 'binding',
17
+ selectorHint: null,
18
+ payloadHint: null,
19
+ });
20
+ }
21
+ // From always-affordable
22
+ for (const msg of affordances) {
23
+ const ann = annotations[msg.type];
24
+ if (ann?.humanOnly)
25
+ continue;
26
+ const { type, ...rest } = msg;
27
+ out.push({
28
+ variant: type,
29
+ intent: ann?.intent ?? type,
30
+ requiresConfirm: ann?.requiresConfirm ?? false,
31
+ source: 'always-affordable',
32
+ selectorHint: null,
33
+ payloadHint: Object.keys(rest).length > 0 ? rest : null,
34
+ });
35
+ }
36
+ return { actions: out };
37
+ }
38
+ //# sourceMappingURL=list-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-actions.js","sourceRoot":"","sources":["../../../src/client/rpc/list-actions.ts"],"names":[],"mappings":"AAuBA,MAAM,UAAU,iBAAiB,CAAC,IAAqB;IACrD,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAA;IAClD,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAA;IAC7B,MAAM,WAAW,GAAG,IAAI,CAAC,qBAAqB,EAAE,IAAI,EAAE,CAAA;IACtD,MAAM,WAAW,GAAG,IAAI,CAAC,mBAAmB,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAA;IAE7D,MAAM,GAAG,GAAiC,EAAE,CAAA;IAE5C,gBAAgB;IAChB,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;QAClC,IAAI,GAAG,EAAE,SAAS;YAAE,SAAQ;QAC5B,GAAG,CAAC,IAAI,CAAC;YACP,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,MAAM,EAAE,GAAG,EAAE,MAAM,IAAI,CAAC,CAAC,OAAO;YAChC,eAAe,EAAE,GAAG,EAAE,eAAe,IAAI,KAAK;YAC9C,MAAM,EAAE,SAAS;YACjB,YAAY,EAAE,IAAI;YAClB,WAAW,EAAE,IAAI;SAClB,CAAC,CAAA;IACJ,CAAC;IAED,yBAAyB;IACzB,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACjC,IAAI,GAAG,EAAE,SAAS;YAAE,SAAQ;QAC5B,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,GAAG,CAAA;QAC7B,GAAG,CAAC,IAAI,CAAC;YACP,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,GAAG,EAAE,MAAM,IAAI,IAAI;YAC3B,eAAe,EAAE,GAAG,EAAE,eAAe,IAAI,KAAK;YAC9C,MAAM,EAAE,mBAAmB;YAC3B,YAAY,EAAE,IAAI;YAClB,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;SACxD,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAA;AACzB,CAAC","sourcesContent":["import type { MessageAnnotations } from '../../protocol.js'\n\ntype Binding = { variant: string }\ntype Annotations = Record<string, MessageAnnotations>\n\nexport type ListActionsHost = {\n getState(): unknown\n getBindingDescriptors(): Binding[] | null\n getMsgAnnotations(): Annotations | null\n getAgentAffordances(): ((state: unknown) => Array<{ type: string; [k: string]: unknown }>) | null\n}\n\nexport type ListActionsResult = {\n actions: Array<{\n variant: string\n intent: string\n requiresConfirm: boolean\n source: 'binding' | 'always-affordable'\n selectorHint: string | null\n payloadHint: object | null\n }>\n}\n\nexport function handleListActions(host: ListActionsHost): ListActionsResult {\n const annotations = host.getMsgAnnotations() ?? {}\n const state = host.getState()\n const descriptors = host.getBindingDescriptors() ?? []\n const affordances = host.getAgentAffordances()?.(state) ?? []\n\n const out: ListActionsResult['actions'] = []\n\n // From bindings\n for (const d of descriptors) {\n const ann = annotations[d.variant]\n if (ann?.humanOnly) continue\n out.push({\n variant: d.variant,\n intent: ann?.intent ?? d.variant,\n requiresConfirm: ann?.requiresConfirm ?? false,\n source: 'binding',\n selectorHint: null,\n payloadHint: null,\n })\n }\n\n // From always-affordable\n for (const msg of affordances) {\n const ann = annotations[msg.type]\n if (ann?.humanOnly) continue\n const { type, ...rest } = msg\n out.push({\n variant: type,\n intent: ann?.intent ?? type,\n requiresConfirm: ann?.requiresConfirm ?? false,\n source: 'always-affordable',\n selectorHint: null,\n payloadHint: Object.keys(rest).length > 0 ? rest : null,\n })\n }\n\n return { actions: out }\n}\n"]}
@@ -0,0 +1,20 @@
1
+ export type QueryDomArgs = {
2
+ name: string;
3
+ multiple?: boolean;
4
+ };
5
+ export type QueryDomResult = {
6
+ elements: Array<{
7
+ text: string;
8
+ attrs: Record<string, string>;
9
+ path: number[];
10
+ }>;
11
+ };
12
+ export type QueryDomHost = {
13
+ getRootElement(): Element | null;
14
+ };
15
+ /**
16
+ * Spec §7.7 / §8.2: reads only elements explicitly tagged
17
+ * `data-agent="<name>"`. No full-DOM access in v1.
18
+ */
19
+ export declare function handleQueryDom(host: QueryDomHost, args: QueryDomArgs): QueryDomResult;
20
+ //# sourceMappingURL=query-dom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-dom.d.ts","sourceRoot":"","sources":["../../../src/client/rpc/query-dom.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAC/D,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAA;CACjF,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,cAAc,IAAI,OAAO,GAAG,IAAI,CAAA;CACjC,CAAA;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,GAAG,cAAc,CAerF"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Spec §7.7 / §8.2: reads only elements explicitly tagged
3
+ * `data-agent="<name>"`. No full-DOM access in v1.
4
+ */
5
+ export function handleQueryDom(host, args) {
6
+ const root = host.getRootElement();
7
+ if (!root)
8
+ return { elements: [] };
9
+ const selector = `[data-agent="${cssEscape(args.name)}"]`;
10
+ const nodes = args.multiple
11
+ ? Array.from(root.querySelectorAll(selector))
12
+ : [root.querySelector(selector)].filter(Boolean);
13
+ return {
14
+ elements: nodes.map((n) => ({
15
+ text: (n.textContent ?? '').trim(),
16
+ attrs: Object.fromEntries(Array.from(n.attributes).map((a) => [a.name, a.value])),
17
+ path: computePath(root, n),
18
+ })),
19
+ };
20
+ }
21
+ function cssEscape(s) {
22
+ // Simple escape for double-quotes; most data-agent names won't need it.
23
+ return s.replace(/"/g, '\\"');
24
+ }
25
+ function computePath(root, target) {
26
+ const out = [];
27
+ let cur = target;
28
+ while (cur !== null && cur !== root) {
29
+ const parent = cur.parentElement;
30
+ if (!parent)
31
+ break;
32
+ out.unshift(Array.from(parent.children).indexOf(cur));
33
+ cur = parent;
34
+ }
35
+ return out;
36
+ }
37
+ //# sourceMappingURL=query-dom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-dom.js","sourceRoot":"","sources":["../../../src/client/rpc/query-dom.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAkB,EAAE,IAAkB;IACnE,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,EAAE,CAAA;IAClC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAA;IAClC,MAAM,QAAQ,GAAG,gBAAgB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAA;IACzD,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ;QACzB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC7C,CAAC,CAAE,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAe,CAAA;IAEjE,OAAO;QACL,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC1B,IAAI,EAAE,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;YAClC,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YACjF,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;SAC3B,CAAC,CAAC;KACJ,CAAA;AACH,CAAC;AAED,SAAS,SAAS,CAAC,CAAS;IAC1B,wEAAwE;IACxE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;AAC/B,CAAC;AAED,SAAS,WAAW,CAAC,IAAa,EAAE,MAAe;IACjD,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,IAAI,GAAG,GAAmB,MAAM,CAAA;IAChC,OAAO,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACpC,MAAM,MAAM,GAAmB,GAAG,CAAC,aAAa,CAAA;QAChD,IAAI,CAAC,MAAM;YAAE,MAAK;QAClB,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;QACrD,GAAG,GAAG,MAAM,CAAA;IACd,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC","sourcesContent":["export type QueryDomArgs = { name: string; multiple?: boolean }\nexport type QueryDomResult = {\n elements: Array<{ text: string; attrs: Record<string, string>; path: number[] }>\n}\n\nexport type QueryDomHost = {\n getRootElement(): Element | null\n}\n\n/**\n * Spec §7.7 / §8.2: reads only elements explicitly tagged\n * `data-agent=\"<name>\"`. No full-DOM access in v1.\n */\nexport function handleQueryDom(host: QueryDomHost, args: QueryDomArgs): QueryDomResult {\n const root = host.getRootElement()\n if (!root) return { elements: [] }\n const selector = `[data-agent=\"${cssEscape(args.name)}\"]`\n const nodes = args.multiple\n ? Array.from(root.querySelectorAll(selector))\n : ([root.querySelector(selector)].filter(Boolean) as Element[])\n\n return {\n elements: nodes.map((n) => ({\n text: (n.textContent ?? '').trim(),\n attrs: Object.fromEntries(Array.from(n.attributes).map((a) => [a.name, a.value])),\n path: computePath(root, n),\n })),\n }\n}\n\nfunction cssEscape(s: string): string {\n // Simple escape for double-quotes; most data-agent names won't need it.\n return s.replace(/\"/g, '\\\\\"')\n}\n\nfunction computePath(root: Element, target: Element): number[] {\n const out: number[] = []\n let cur: Element | null = target\n while (cur !== null && cur !== root) {\n const parent: Element | null = cur.parentElement\n if (!parent) break\n out.unshift(Array.from(parent.children).indexOf(cur))\n cur = parent\n }\n return out\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import type { LapMessageResponse, MessageAnnotations } from '../../protocol.js';
2
+ export type SendMessageArgs = {
3
+ msg: {
4
+ type: string;
5
+ [k: string]: unknown;
6
+ };
7
+ reason?: string;
8
+ waitFor?: 'idle' | 'none';
9
+ timeoutMs?: number;
10
+ };
11
+ export type SendMessageHost = {
12
+ getState(): unknown;
13
+ send(msg: unknown): void;
14
+ flush(): void;
15
+ getMsgAnnotations(): Record<string, MessageAnnotations> | null;
16
+ /** Called when @requiresConfirm; caller stores a ConfirmEntry in state. */
17
+ proposeConfirm(entry: {
18
+ id: string;
19
+ variant: string;
20
+ payload: unknown;
21
+ intent: string;
22
+ reason: string | null;
23
+ proposedAt: number;
24
+ status: 'pending';
25
+ }): void;
26
+ };
27
+ export declare function handleSendMessage(host: SendMessageHost, args: SendMessageArgs): Promise<LapMessageResponse>;
28
+ //# sourceMappingURL=send-message.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-message.d.ts","sourceRoot":"","sources":["../../../src/client/rpc/send-message.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAE/E,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,IAAI,OAAO,CAAA;IACnB,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;IACxB,KAAK,IAAI,IAAI,CAAA;IACb,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAAA;IAC9D,2EAA2E;IAC3E,cAAc,CAAC,KAAK,EAAE;QACpB,EAAE,EAAE,MAAM,CAAA;QACV,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,OAAO,CAAA;QAChB,MAAM,EAAE,MAAM,CAAA;QACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;QACrB,UAAU,EAAE,MAAM,CAAA;QAClB,MAAM,EAAE,SAAS,CAAA;KAClB,GAAG,IAAI,CAAA;CACT,CAAA;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,eAAe,EACrB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,kBAAkB,CAAC,CAwC7B"}
@@ -0,0 +1,40 @@
1
+ import { randomUUID } from '../uuid.js';
2
+ export async function handleSendMessage(host, args) {
3
+ if (!args.msg || typeof args.msg.type !== 'string') {
4
+ return { status: 'rejected', reason: 'invalid' };
5
+ }
6
+ const annotations = host.getMsgAnnotations() ?? {};
7
+ const ann = annotations[args.msg.type];
8
+ // If annotations map is non-empty and this variant isn't in it, it's an
9
+ // unknown msg type that the app never declared — reject early so the
10
+ // browser never dispatches an unrecognised variant into update().
11
+ const hasAnnotations = Object.keys(annotations).length > 0;
12
+ if (hasAnnotations && !ann) {
13
+ return { status: 'rejected', reason: 'invalid', detail: `unknown variant: ${args.msg.type}` };
14
+ }
15
+ if (ann?.humanOnly) {
16
+ return { status: 'rejected', reason: 'humanOnly' };
17
+ }
18
+ if (ann?.requiresConfirm) {
19
+ const id = randomUUID();
20
+ const { type: _type, ...payload } = args.msg;
21
+ host.proposeConfirm({
22
+ id,
23
+ variant: args.msg.type,
24
+ payload,
25
+ intent: ann?.intent ?? args.msg.type,
26
+ reason: args.reason ?? null,
27
+ proposedAt: Date.now(),
28
+ status: 'pending',
29
+ });
30
+ return { status: 'pending-confirmation', confirmId: id };
31
+ }
32
+ host.send(args.msg);
33
+ if (args.waitFor !== 'none') {
34
+ host.flush();
35
+ // Let the microtask queue settle:
36
+ await Promise.resolve();
37
+ }
38
+ return { status: 'dispatched', stateAfter: host.getState() };
39
+ }
40
+ //# sourceMappingURL=send-message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-message.js","sourceRoot":"","sources":["../../../src/client/rpc/send-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AA2BvC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAqB,EACrB,IAAqB;IAErB,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACnD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;IAClD,CAAC;IACD,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAA;IAClD,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAEtC,wEAAwE;IACxE,qEAAqE;IACrE,kEAAkE;IAClE,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC,CAAA;IAC1D,IAAI,cAAc,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,oBAAoB,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAA;IAC/F,CAAC;IAED,IAAI,GAAG,EAAE,SAAS,EAAE,CAAC;QACnB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,CAAA;IACpD,CAAC;IACD,IAAI,GAAG,EAAE,eAAe,EAAE,CAAC;QACzB,MAAM,EAAE,GAAG,UAAU,EAAE,CAAA;QACvB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,CAAA;QAC5C,IAAI,CAAC,cAAc,CAAC;YAClB,EAAE;YACF,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI;YACtB,OAAO;YACP,MAAM,EAAE,GAAG,EAAE,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI;YACpC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI;YAC3B,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;YACtB,MAAM,EAAE,SAAS;SAClB,CAAC,CAAA;QACF,OAAO,EAAE,MAAM,EAAE,sBAAsB,EAAE,SAAS,EAAE,EAAE,EAAE,CAAA;IAC1D,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACnB,IAAI,IAAI,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,EAAE,CAAA;QACZ,kCAAkC;QAClC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;IACzB,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAA;AAC9D,CAAC","sourcesContent":["import { randomUUID } from '../uuid.js'\nimport type { LapMessageResponse, MessageAnnotations } from '../../protocol.js'\n\nexport type SendMessageArgs = {\n msg: { type: string; [k: string]: unknown }\n reason?: string\n waitFor?: 'idle' | 'none'\n timeoutMs?: number\n}\n\nexport type SendMessageHost = {\n getState(): unknown\n send(msg: unknown): void\n flush(): void\n getMsgAnnotations(): Record<string, MessageAnnotations> | null\n /** Called when @requiresConfirm; caller stores a ConfirmEntry in state. */\n proposeConfirm(entry: {\n id: string\n variant: string\n payload: unknown\n intent: string\n reason: string | null\n proposedAt: number\n status: 'pending'\n }): void\n}\n\nexport async function handleSendMessage(\n host: SendMessageHost,\n args: SendMessageArgs,\n): Promise<LapMessageResponse> {\n if (!args.msg || typeof args.msg.type !== 'string') {\n return { status: 'rejected', reason: 'invalid' }\n }\n const annotations = host.getMsgAnnotations() ?? {}\n const ann = annotations[args.msg.type]\n\n // If annotations map is non-empty and this variant isn't in it, it's an\n // unknown msg type that the app never declared — reject early so the\n // browser never dispatches an unrecognised variant into update().\n const hasAnnotations = Object.keys(annotations).length > 0\n if (hasAnnotations && !ann) {\n return { status: 'rejected', reason: 'invalid', detail: `unknown variant: ${args.msg.type}` }\n }\n\n if (ann?.humanOnly) {\n return { status: 'rejected', reason: 'humanOnly' }\n }\n if (ann?.requiresConfirm) {\n const id = randomUUID()\n const { type: _type, ...payload } = args.msg\n host.proposeConfirm({\n id,\n variant: args.msg.type,\n payload,\n intent: ann?.intent ?? args.msg.type,\n reason: args.reason ?? null,\n proposedAt: Date.now(),\n status: 'pending',\n })\n return { status: 'pending-confirmation', confirmId: id }\n }\n\n host.send(args.msg)\n if (args.waitFor !== 'none') {\n host.flush()\n // Let the microtask queue settle:\n await Promise.resolve()\n }\n return { status: 'dispatched', stateAfter: host.getState() }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export declare function randomUUID(): string;
2
+ //# sourceMappingURL=uuid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../../src/client/uuid.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,IAAI,MAAM,CAmBnC"}
@@ -0,0 +1,24 @@
1
+ export function randomUUID() {
2
+ if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
3
+ return crypto.randomUUID();
4
+ }
5
+ // Simple v4 fallback for environments without crypto.randomUUID
6
+ const chars = '0123456789abcdef';
7
+ let s = '';
8
+ for (let i = 0; i < 36; i++) {
9
+ if (i === 8 || i === 13 || i === 18 || i === 23) {
10
+ s += '-';
11
+ }
12
+ else if (i === 14) {
13
+ s += '4';
14
+ }
15
+ else if (i === 19) {
16
+ s += chars[((Math.random() * 4) | 0) + 8];
17
+ }
18
+ else {
19
+ s += chars[(Math.random() * 16) | 0];
20
+ }
21
+ }
22
+ return s;
23
+ }
24
+ //# sourceMappingURL=uuid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uuid.js","sourceRoot":"","sources":["../../src/client/uuid.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU;IACxB,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,YAAY,IAAI,MAAM,EAAE,CAAC;QAC5D,OAAO,MAAM,CAAC,UAAU,EAAE,CAAA;IAC5B,CAAC;IACD,gEAAgE;IAChE,MAAM,KAAK,GAAG,kBAAkB,CAAA;IAChC,IAAI,CAAC,GAAG,EAAE,CAAA;IACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;YAChD,CAAC,IAAI,GAAG,CAAA;QACV,CAAC;aAAM,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;YACpB,CAAC,IAAI,GAAG,CAAA;QACV,CAAC;aAAM,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;YACpB,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QAC3C,CAAC;aAAM,CAAC;YACN,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;QACtC,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC","sourcesContent":["export function randomUUID(): string {\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {\n return crypto.randomUUID()\n }\n // Simple v4 fallback for environments without crypto.randomUUID\n const chars = '0123456789abcdef'\n let s = ''\n for (let i = 0; i < 36; i++) {\n if (i === 8 || i === 13 || i === 18 || i === 23) {\n s += '-'\n } else if (i === 14) {\n s += '4'\n } else if (i === 19) {\n s += chars[((Math.random() * 4) | 0) + 8]\n } else {\n s += chars[(Math.random() * 16) | 0]\n }\n }\n return s\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import type { HelloFrame, LogEntry } from '../protocol.js';
2
+ import { type GetStateHost } from './rpc/get-state.js';
3
+ import { type SendMessageHost } from './rpc/send-message.js';
4
+ import { type ListActionsHost } from './rpc/list-actions.js';
5
+ import { type QueryDomHost } from './rpc/query-dom.js';
6
+ import { type DescribeVisibleHost } from './rpc/describe-visible-content.js';
7
+ import { type DescribeContextHost } from './rpc/describe-context.js';
8
+ export interface WsLike {
9
+ send(data: string): void;
10
+ close(): void;
11
+ addEventListener(event: 'message', h: (e: {
12
+ data: string | ArrayBuffer;
13
+ }) => void): void;
14
+ addEventListener(event: 'open' | 'close', h: () => void): void;
15
+ }
16
+ export type RpcHosts = GetStateHost & SendMessageHost & ListActionsHost & QueryDomHost & DescribeVisibleHost & DescribeContextHost;
17
+ export type HelloBuilder = () => HelloFrame;
18
+ export type WsClient = {
19
+ /** Resolve a pending confirmation; emits confirm-resolved frame to the server. */
20
+ resolveConfirm(confirmId: string, outcome: 'confirmed' | 'user-cancelled', stateAfter?: unknown): void;
21
+ /** Emit a state-update frame so the server can resolve waitForChange promises. */
22
+ emitStateUpdate(path: string, stateAfter: unknown): void;
23
+ /** Emit a log-append frame so the server can mirror client-observed actions to the audit sink. */
24
+ emitLogAppend(entry: LogEntry): void;
25
+ /** Close the socket cleanly. */
26
+ close(): void;
27
+ };
28
+ export type WsClientOpts = {
29
+ /** Called once when the server sends an `{t: 'active'}` frame. Idempotent. */
30
+ onActivated?: () => void;
31
+ /**
32
+ * Called with every LogEntry emitted by the ws-client (one per rpc
33
+ * dispatched or errored). Used by the factory to mirror the entries
34
+ * into the app's local `agent.log` slice so the UI can show activity.
35
+ * The ws-client still sends the outbound `log-append` frame to the
36
+ * server regardless.
37
+ */
38
+ onLogEntry?: (entry: LogEntry) => void;
39
+ };
40
+ /**
41
+ * Wires up a WebSocket to serve rpc requests from the server. See spec §9.4.
42
+ */
43
+ export declare function attachWsClient(ws: WsLike, rpc: RpcHosts, hello: HelloBuilder, opts?: WsClientOpts): WsClient;
44
+ //# sourceMappingURL=ws-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-client.d.ts","sourceRoot":"","sources":["../../src/client/ws-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,UAAU,EACV,QAAQ,EAGT,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtE,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtE,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAAyB,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AAE3F,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,KAAK,IAAI,IAAI,CAAA;IACb,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI,CAAA;IACxF,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAAA;CAC/D;AAED,MAAM,MAAM,QAAQ,GAAG,YAAY,GACjC,eAAe,GACf,eAAe,GACf,YAAY,GACZ,mBAAmB,GACnB,mBAAmB,CAAA;AAErB,MAAM,MAAM,YAAY,GAAG,MAAM,UAAU,CAAA;AAE3C,MAAM,MAAM,QAAQ,GAAG;IACrB,kFAAkF;IAClF,cAAc,CACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,WAAW,GAAG,gBAAgB,EACvC,UAAU,CAAC,EAAE,OAAO,GACnB,IAAI,CAAA;IACP,kFAAkF;IAClF,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,IAAI,CAAA;IACxD,kGAAkG;IAClG,aAAa,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAA;IACpC,gCAAgC;IAChC,KAAK,IAAI,IAAI,CAAA;CACd,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAA;CACvC,CAAA;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,YAAY,EACnB,IAAI,GAAE,YAAiB,GACtB,QAAQ,CAsFV"}
@@ -0,0 +1,176 @@
1
+ import { handleGetState } from './rpc/get-state.js';
2
+ import { handleSendMessage } from './rpc/send-message.js';
3
+ import { handleListActions } from './rpc/list-actions.js';
4
+ import { handleQueryDom } from './rpc/query-dom.js';
5
+ import { handleDescribeVisibleContent, } from './rpc/describe-visible-content.js';
6
+ import { handleDescribeContext } from './rpc/describe-context.js';
7
+ /**
8
+ * Wires up a WebSocket to serve rpc requests from the server. See spec §9.4.
9
+ */
10
+ export function attachWsClient(ws, rpc, hello, opts = {}) {
11
+ let activated = false;
12
+ ws.addEventListener('open', () => {
13
+ ws.send(JSON.stringify(hello()));
14
+ });
15
+ ws.addEventListener('message', async (ev) => {
16
+ let frame;
17
+ try {
18
+ const raw = typeof ev.data === 'string' ? ev.data : new TextDecoder().decode(ev.data);
19
+ frame = JSON.parse(raw);
20
+ }
21
+ catch {
22
+ return;
23
+ }
24
+ if (frame.t === 'revoked') {
25
+ ws.close();
26
+ return;
27
+ }
28
+ if (frame.t === 'active') {
29
+ if (!activated) {
30
+ activated = true;
31
+ opts.onActivated?.();
32
+ }
33
+ return;
34
+ }
35
+ if (frame.t !== 'rpc')
36
+ return;
37
+ let result;
38
+ let rpcErr = null;
39
+ try {
40
+ result = await dispatch(frame.tool, frame.args, rpc);
41
+ const reply = { t: 'rpc-reply', id: frame.id, result };
42
+ ws.send(JSON.stringify(reply));
43
+ }
44
+ catch (e) {
45
+ rpcErr = e;
46
+ // When a plain JS exception bubbles up (TypeError, RangeError, etc.),
47
+ // rpcErr has no .code/.detail. Enrich the detail with the actual
48
+ // message + stack so the server/Claude can see the real cause.
49
+ const detail = rpcErr.detail ??
50
+ (e instanceof Error
51
+ ? `${e.name}: ${e.message}${e.stack ? '\n' + e.stack.split('\n').slice(0, 5).join('\n') : ''}`
52
+ : undefined);
53
+ const errFrame = {
54
+ t: 'rpc-error',
55
+ id: frame.id,
56
+ code: rpcErr.code ?? 'internal',
57
+ detail,
58
+ };
59
+ ws.send(JSON.stringify(errFrame));
60
+ // Also log to the browser console so operators see the real cause even
61
+ // when the server/Claude just show "internal".
62
+ console.error(`[llui-agent] rpc handler threw for ${frame.tool}:`, e);
63
+ }
64
+ const kind = getLogKindForTool(frame.tool, result, rpcErr);
65
+ const logEntry = {
66
+ id: frame.id,
67
+ at: Date.now(),
68
+ kind,
69
+ variant: extractVariant(frame.tool, frame.args),
70
+ intent: buildIntent(frame.tool, frame.args, rpc.getMsgAnnotations()),
71
+ };
72
+ opts.onLogEntry?.(logEntry);
73
+ ws.send(JSON.stringify({ t: 'log-append', entry: logEntry }));
74
+ });
75
+ return {
76
+ resolveConfirm(confirmId, outcome, stateAfter) {
77
+ const frame = {
78
+ t: 'confirm-resolved',
79
+ confirmId,
80
+ outcome,
81
+ stateAfter,
82
+ };
83
+ ws.send(JSON.stringify(frame));
84
+ },
85
+ emitStateUpdate(path, stateAfter) {
86
+ const frame = { t: 'state-update', path, stateAfter };
87
+ ws.send(JSON.stringify(frame));
88
+ },
89
+ emitLogAppend(entry) {
90
+ const frame = { t: 'log-append', entry };
91
+ ws.send(JSON.stringify(frame));
92
+ },
93
+ close() {
94
+ ws.close();
95
+ },
96
+ };
97
+ }
98
+ async function dispatch(tool, args, rpc) {
99
+ switch (tool) {
100
+ case 'get_state':
101
+ return handleGetState(rpc, (args ?? {}));
102
+ case 'list_actions':
103
+ return handleListActions(rpc);
104
+ case 'send_message':
105
+ return handleSendMessage(rpc, args);
106
+ case 'query_dom':
107
+ return handleQueryDom(rpc, args);
108
+ case 'describe_visible_content':
109
+ return handleDescribeVisibleContent(rpc);
110
+ case 'describe_context':
111
+ return handleDescribeContext(rpc);
112
+ default:
113
+ throw { code: 'invalid', detail: `unknown tool: ${tool}` };
114
+ }
115
+ }
116
+ const READ_TOOLS = new Set([
117
+ 'get_state',
118
+ 'list_actions',
119
+ 'describe_context',
120
+ 'query_dom',
121
+ 'describe_visible_content',
122
+ ]);
123
+ function getLogKindForTool(tool, result, err) {
124
+ if (err !== null)
125
+ return 'error';
126
+ if (tool === 'send_message') {
127
+ const r = result;
128
+ const status = r?.status;
129
+ if (status === 'dispatched' || status === 'confirmed')
130
+ return 'dispatched';
131
+ if (status === 'pending-confirmation')
132
+ return 'proposed';
133
+ if (status === 'rejected')
134
+ return 'blocked';
135
+ return 'dispatched';
136
+ }
137
+ if (READ_TOOLS.has(tool))
138
+ return 'read';
139
+ return 'read';
140
+ }
141
+ function extractVariant(tool, args) {
142
+ if (tool === 'send_message') {
143
+ const a = args;
144
+ const t = a?.msg?.type;
145
+ return typeof t === 'string' ? t : undefined;
146
+ }
147
+ return undefined;
148
+ }
149
+ // Human-readable label for each rpc. For send_message, prefer the @intent
150
+ // annotation authored on the Msg union; fall back to the raw variant name.
151
+ // For read tools, return a short fixed label so the activity feed doesn't
152
+ // show opaque tool ids like "describe_visible_content".
153
+ function buildIntent(tool, args, annotations) {
154
+ if (tool === 'send_message') {
155
+ const a = args;
156
+ const variant = typeof a?.msg?.type === 'string' ? a.msg.type : undefined;
157
+ const annotated = variant ? annotations?.[variant]?.intent : null;
158
+ if (annotated)
159
+ return annotated;
160
+ return variant ?? 'Send message';
161
+ }
162
+ if (tool === 'get_state')
163
+ return 'Read app state';
164
+ if (tool === 'list_actions')
165
+ return 'List available actions';
166
+ if (tool === 'describe_context')
167
+ return 'Read current context';
168
+ if (tool === 'describe_visible_content')
169
+ return 'Read visible content';
170
+ if (tool === 'query_dom') {
171
+ const a = args;
172
+ return a?.name ? `Query DOM: ${a.name}` : 'Query DOM';
173
+ }
174
+ return tool;
175
+ }
176
+ //# sourceMappingURL=ws-client.js.map