llmz 0.6.0 → 0.6.2

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 (110) hide show
  1. package/DOCS.md +1 -3
  2. package/dist/_baseForOwn-CFhWe_W6.js +48 -0
  3. package/dist/_baseForOwn-buOZDFbK.cjs +103 -0
  4. package/dist/_baseIteratee-CF4qP2a8.cjs +2377 -0
  5. package/dist/_baseIteratee-P_firB7b.js +2138 -0
  6. package/dist/_getPrototype-CG7Dxrrz.js +6 -0
  7. package/dist/_getPrototype-DQDlNVVM.cjs +10 -0
  8. package/dist/_overArg-C5Hy7oCi.cjs +196 -0
  9. package/dist/_overArg-CRYbj1NH.js +155 -0
  10. package/dist/citations.d.ts +11 -11
  11. package/dist/component-BwixNVH8.cjs +73 -0
  12. package/dist/component-DGG7d_Mz.js +44 -0
  13. package/dist/context.d.ts +8 -8
  14. package/dist/custom-client-BPmEoWKu.cjs +2618 -0
  15. package/dist/custom-client-NUoxrFQh.js +2547 -0
  16. package/dist/dual-modes-D_p-jedw.js +2750 -0
  17. package/dist/dual-modes-wAOAlaBS.cjs +2840 -0
  18. package/dist/errors-C962uJdS.cjs +220 -0
  19. package/dist/errors-Cg1G0Awu.js +161 -0
  20. package/dist/execute-BEaq3jkV.cjs +1003 -0
  21. package/dist/execute-DYvFRs54.js +1001 -0
  22. package/dist/exit-BGi0Kk0y.cjs +428 -0
  23. package/dist/exit-CDdxDBRT.js +417 -0
  24. package/dist/index.cjs +1786 -1489
  25. package/dist/index.d.ts +10 -9
  26. package/dist/index.js +1721 -1479
  27. package/dist/index.workerd.js +17534 -16875
  28. package/dist/isEmpty-BhJRCqIF.js +52 -0
  29. package/dist/isEmpty-LY533Wmh.cjs +57 -0
  30. package/dist/isPlainObject-CSxXVKCk.cjs +56 -0
  31. package/dist/isPlainObject-D39AzPO2.js +51 -0
  32. package/dist/jsx-BWvT6ukg.cjs +75 -0
  33. package/dist/jsx-CIgdGGVs.js +52 -0
  34. package/dist/jsx.d.ts +2 -2
  35. package/dist/prompts/chat-mode/system.md.d.ts +1 -1
  36. package/dist/prompts/chat-mode/user.md.d.ts +1 -1
  37. package/dist/prompts/worker-mode/system.md.d.ts +1 -1
  38. package/dist/prompts/worker-mode/user.md.d.ts +1 -1
  39. package/dist/quickjs-variant-BYhMBpwU.js +45 -0
  40. package/dist/quickjs-variant-CsIwTVFC.cjs +56 -0
  41. package/dist/quickjs-variant.workerd.d.ts +1 -1
  42. package/dist/result.d.ts +12 -12
  43. package/dist/rolldown-runtime-EAsCxrDo.js +14 -0
  44. package/dist/runtime/interpret-result.d.ts +1 -1
  45. package/dist/snapshots.d.ts +5 -5
  46. package/dist/stack-traces-BIE_5Xwf.js +21 -0
  47. package/dist/stack-traces-ntw_qhoy.cjs +26 -0
  48. package/dist/tool-CAo-BQHs.js +643 -0
  49. package/dist/tool-DdPZb0JW.cjs +654 -0
  50. package/dist/tool.d.ts +3 -3
  51. package/dist/truncator-B6aaJaN1.cjs +276 -0
  52. package/dist/truncator-DZqEqnpD.js +253 -0
  53. package/dist/typings-BwAz_TM9.cjs +784 -0
  54. package/dist/typings-CrBa7Tyy.js +743 -0
  55. package/dist/uniq-BYQ7GWwb.js +183 -0
  56. package/dist/uniq-G872RWmE.cjs +200 -0
  57. package/dist/utils-UBeAFeEI.js +950 -0
  58. package/dist/utils-ZpJm00ed.cjs +1075 -0
  59. package/dist/vm/errors.d.ts +2 -2
  60. package/dist/vm-BjkJqSQH.cjs +3438 -0
  61. package/dist/vm-C5vf1LH3.js +3436 -0
  62. package/package.json +7 -7
  63. package/dist/chunk-34GJUS6U.js +0 -3266
  64. package/dist/chunk-3UZKLXAD.cjs +0 -3249
  65. package/dist/chunk-4POZJQXN.js +0 -1530
  66. package/dist/chunk-5WBNTV42.cjs +0 -1530
  67. package/dist/chunk-5ZF6NTYI.cjs +0 -33
  68. package/dist/chunk-72ZWBNZV.js +0 -173
  69. package/dist/chunk-DDSJTR6Q.cjs +0 -257
  70. package/dist/chunk-GDJTKI7J.js +0 -792
  71. package/dist/chunk-GLXADNTR.js +0 -199
  72. package/dist/chunk-GOWPACSK.cjs +0 -199
  73. package/dist/chunk-HB2G32AK.js +0 -257
  74. package/dist/chunk-HC2QZTVT.cjs +0 -3266
  75. package/dist/chunk-HEAQAEFZ.cjs +0 -173
  76. package/dist/chunk-HMMM32RG.cjs +0 -792
  77. package/dist/chunk-I56MPOVG.cjs +0 -62
  78. package/dist/chunk-KIN7Y247.cjs +0 -32
  79. package/dist/chunk-KTDBAJBB.cjs +0 -1685
  80. package/dist/chunk-KWQTYXAT.js +0 -62
  81. package/dist/chunk-LOX7GOR6.js +0 -1685
  82. package/dist/chunk-PK72FAKD.cjs +0 -175
  83. package/dist/chunk-PZE6PIPG.cjs +0 -621
  84. package/dist/chunk-RGUG3UMF.cjs +0 -26
  85. package/dist/chunk-TDALRNGL.js +0 -621
  86. package/dist/chunk-UGHFNCPD.js +0 -26
  87. package/dist/chunk-WSVDMGMR.js +0 -175
  88. package/dist/chunk-XTEHT2TK.js +0 -33
  89. package/dist/chunk-YEAWWJSJ.js +0 -32
  90. package/dist/chunk-YRS7KRKP.js +0 -3249
  91. package/dist/component-2NYFY6CW.cjs +0 -14
  92. package/dist/component-ASNZHQUR.js +0 -14
  93. package/dist/dual-modes-S5TJ5JVJ.js +0 -10
  94. package/dist/dual-modes-TCMWBZIH.cjs +0 -10
  95. package/dist/execute-FF7D46TY.js +0 -1109
  96. package/dist/execute-IK6O4SU2.cjs +0 -1109
  97. package/dist/exit-MX56VN2C.cjs +0 -8
  98. package/dist/exit-UQ3EUX5D.js +0 -8
  99. package/dist/jsx-DDZ63UUF.js +0 -13
  100. package/dist/jsx-ULPWX5TW.cjs +0 -13
  101. package/dist/tool-KD7FL45V.js +0 -11
  102. package/dist/tool-VE2YHIY2.cjs +0 -11
  103. package/dist/truncator-73RCL52Y.js +0 -12
  104. package/dist/truncator-FZNLYSDL.cjs +0 -12
  105. package/dist/typings-E2RROCLT.js +0 -10
  106. package/dist/typings-LQOBZXSO.cjs +0 -10
  107. package/dist/utils-IATCUNKF.js +0 -43
  108. package/dist/utils-ITB4NEL7.cjs +0 -43
  109. package/dist/vm-6D3IIIU7.cjs +0 -12
  110. package/dist/vm-QO5FSGPN.js +0 -12
package/dist/index.js CHANGED
@@ -1,1555 +1,1797 @@
1
- import {
2
- DefaultExit,
3
- ErrorExecutionResult,
4
- ExecutionResult,
5
- ListenExit,
6
- PartialExecutionResult,
7
- Snapshot,
8
- SuccessExecutionResult,
9
- ThinkExit,
10
- _CustomModelClient,
11
- getValue,
12
- parseExit
13
- } from "./chunk-4POZJQXN.js";
14
- import {
15
- MARKER,
16
- NAME_REGEX,
17
- StreamingMessageParser,
18
- generateInstructions,
19
- tryParseJson
20
- } from "./chunk-LOX7GOR6.js";
21
- import {
22
- configureQuickJS
23
- } from "./chunk-UGHFNCPD.js";
24
- import {
25
- Tool
26
- } from "./chunk-TDALRNGL.js";
27
- import {
28
- formatTypings,
29
- getTypings,
30
- isGroup,
31
- isPunct,
32
- parse,
33
- tokenize
34
- } from "./chunk-GDJTKI7J.js";
35
- import {
36
- CodeFormattingError,
37
- LoopExceededError,
38
- SnapshotSignal,
39
- ThinkSignal
40
- } from "./chunk-WSVDMGMR.js";
41
- import "./chunk-YEAWWJSJ.js";
42
- import {
43
- Exit
44
- } from "./chunk-HB2G32AK.js";
45
- import {
46
- Component,
47
- assertValidComponent,
48
- isAnyComponent,
49
- isComponent
50
- } from "./chunk-KWQTYXAT.js";
51
- import "./chunk-XTEHT2TK.js";
52
- import {
53
- stripTruncationTags,
54
- truncateWrappedContent,
55
- wrapContent
56
- } from "./chunk-72ZWBNZV.js";
57
- import {
58
- configureTokenizer,
59
- escapeString,
60
- getMultilineComment,
61
- isJsonSchema,
62
- isValidIdentifier,
63
- isZuiSchema,
64
- toValidFunctionName,
65
- toValidObjectName
66
- } from "./chunk-GLXADNTR.js";
67
- import {
68
- upperFirst_default
69
- } from "./chunk-34GJUS6U.js";
70
-
71
- // package.json
72
- var version = "0.6.0";
73
-
74
- // src/objects.ts
75
- import { z } from "@bpinternal/zui";
76
-
77
- // src/hoist.ts
1
+ import { a as StreamingMessageParser, c as NAME_REGEX, o as tryParseJson, r as generateInstructions, s as MARKER } from "./dual-modes-D_p-jedw.js";
2
+ import { a as ThinkExit, c as ExecutionResult, d as Snapshot, f as parseExit, i as ListenExit, l as PartialExecutionResult, o as getValue, r as DefaultExit, s as ErrorExecutionResult, t as _CustomModelClient, u as SuccessExecutionResult } from "./custom-client-NUoxrFQh.js";
3
+ import { a as getMultilineComment, c as isJsonSchema, l as isValidIdentifier, m as toValidObjectName, n as escapeString, p as toValidFunctionName, t as configureTokenizer, u as isZuiSchema, v as upperFirst } from "./utils-UBeAFeEI.js";
4
+ import { t as Tool } from "./tool-CAo-BQHs.js";
5
+ import { c as SnapshotSignal, l as ThinkSignal, o as LoopExceededError, r as CodeFormattingError } from "./errors-Cg1G0Awu.js";
6
+ import { a as isPunct, i as isGroup, o as parse, r as formatTypings, s as tokenize, t as getTypings } from "./typings-CrBa7Tyy.js";
7
+ import { t as Exit } from "./exit-CDdxDBRT.js";
8
+ import { a as isComponent, i as isAnyComponent, n as assertValidComponent, t as Component } from "./component-DGG7d_Mz.js";
9
+ import { i as wrapContent, n as truncateWrappedContent, t as stripTruncationTags } from "./truncator-DZqEqnpD.js";
10
+ import { t as configureQuickJS } from "./quickjs-variant-BYhMBpwU.js";
11
+ import { transforms, z } from "@bpinternal/zui";
12
+ //#region package.json
13
+ var version = "0.6.2";
14
+ //#endregion
15
+ //#region src/hoist.ts
16
+ /**
17
+ * Hoists object types (`{ ... }`) that appear more than once in a block of
18
+ * typings into named `type` aliases, replacing every occurrence with a
19
+ * reference. Runs repeatedly until a fixpoint so nested duplicates converge.
20
+ */
21
+ /** Prints a node tree back to text, preserving line structure (formatTypings normalizes later) */
78
22
  function printNodes(nodes) {
79
- let out = "";
80
- let afterLineComment = false;
81
- const emit = (n) => {
82
- const newlines = Math.min(n.nl ?? 0, 2);
83
- if (newlines > 0) {
84
- out += "\n".repeat(newlines);
85
- } else if (afterLineComment) {
86
- out += "\n";
87
- } else if (out.length && !out.endsWith("\n")) {
88
- out += " ";
89
- }
90
- afterLineComment = false;
91
- if (isGroup(n)) {
92
- out += n.open;
93
- for (const child of n.children) {
94
- emit(child);
95
- }
96
- if (afterLineComment) {
97
- out += "\n";
98
- afterLineComment = false;
99
- }
100
- out += n.close;
101
- } else {
102
- out += n.text;
103
- afterLineComment = n.kind === "line-comment";
104
- }
105
- };
106
- for (const n of nodes) {
107
- emit(n);
108
- }
109
- return out;
23
+ let out = "";
24
+ let afterLineComment = false;
25
+ const emit = (n) => {
26
+ const newlines = Math.min(n.nl ?? 0, 2);
27
+ if (newlines > 0) out += "\n".repeat(newlines);
28
+ else if (afterLineComment) out += "\n";
29
+ else if (out.length && !out.endsWith("\n")) out += " ";
30
+ afterLineComment = false;
31
+ if (isGroup(n)) {
32
+ out += n.open;
33
+ for (const child of n.children) emit(child);
34
+ if (afterLineComment) {
35
+ out += "\n";
36
+ afterLineComment = false;
37
+ }
38
+ out += n.close;
39
+ } else {
40
+ out += n.text;
41
+ afterLineComment = n.kind === "line-comment";
42
+ }
43
+ };
44
+ for (const n of nodes) emit(n);
45
+ return out;
110
46
  }
47
+ /** Canonical structural key of a type literal: comments and separators ignored */
111
48
  function keyOf(group) {
112
- const parts = [];
113
- const visit = (n) => {
114
- if (isGroup(n)) {
115
- parts.push(n.open);
116
- n.children.forEach(visit);
117
- parts.push(n.close);
118
- } else if (n.kind === "word" || n.kind === "string" || n.kind === "punct" && n.text !== ";" && n.text !== ",") {
119
- parts.push(n.text);
120
- }
121
- };
122
- visit(group);
123
- return parts.join(" ");
49
+ const parts = [];
50
+ const visit = (n) => {
51
+ if (isGroup(n)) {
52
+ parts.push(n.open);
53
+ n.children.forEach(visit);
54
+ parts.push(n.close);
55
+ } else if (n.kind === "word" || n.kind === "string" || n.kind === "punct" && n.text !== ";" && n.text !== ",") parts.push(n.text);
56
+ };
57
+ visit(group);
58
+ return parts.join(" ");
124
59
  }
125
- var prevMeaningful = (siblings, index) => {
126
- for (let i = index - 1; i >= 0; i--) {
127
- const n = siblings[i];
128
- if (isGroup(n) || n.kind !== "block-comment" && n.kind !== "line-comment") {
129
- return n;
130
- }
131
- }
132
- return void 0;
60
+ const prevMeaningful = (siblings, index) => {
61
+ for (let i = index - 1; i >= 0; i--) {
62
+ const n = siblings[i];
63
+ if (isGroup(n) || n.kind !== "block-comment" && n.kind !== "line-comment") return n;
64
+ }
133
65
  };
134
- var asName = (text) => upperFirst_default(text.replace(/['"]/g, "").replace(/[^A-Za-z0-9_$]/g, ""));
66
+ const asName = (text) => upperFirst(text.replace(/['"]/g, "").replace(/[^A-Za-z0-9_$]/g, ""));
67
+ /** The naming segment for a `{`/`<` group from its surrounding tokens (`key:`, `fn(...)`, `): `, `=>`) */
135
68
  function segmentsAt(siblings, index) {
136
- var _a;
137
- const prev = prevMeaningful(siblings, index);
138
- if (!prev || isGroup(prev)) {
139
- return [];
140
- }
141
- if (isPunct(prev, "=>")) {
142
- return ["Output"];
143
- }
144
- if (isPunct(prev, ":") || isPunct(prev, "<")) {
145
- let i = siblings.indexOf(prev) - 1;
146
- while (i >= 0 && !isGroup(siblings[i]) && ((_a = siblings[i].kind) == null ? void 0 : _a.includes("comment"))) {
147
- i--;
148
- }
149
- let q = siblings[i];
150
- if (q && !isGroup(q) && q.kind === "punct" && q.text === "?") {
151
- q = siblings[--i];
152
- }
153
- if (q && isGroup(q) && q.open === "(") {
154
- const fnWord = prevMeaningful(siblings, i);
155
- return fnWord && !isGroup(fnWord) && fnWord.kind === "word" ? [asName(fnWord.text), "Output"] : ["Output"];
156
- }
157
- if (q && !isGroup(q) && (q.kind === "word" || q.kind === "string")) {
158
- return [asName(q.text)];
159
- }
160
- }
161
- return [];
69
+ const prev = prevMeaningful(siblings, index);
70
+ if (!prev || isGroup(prev)) return [];
71
+ if (isPunct(prev, "=>")) return ["Output"];
72
+ if (isPunct(prev, ":") || isPunct(prev, "<")) {
73
+ var _kind;
74
+ let i = siblings.indexOf(prev) - 1;
75
+ while (i >= 0 && !isGroup(siblings[i]) && ((_kind = siblings[i].kind) === null || _kind === void 0 ? void 0 : _kind.includes("comment"))) i--;
76
+ let q = siblings[i];
77
+ if (q && !isGroup(q) && q.kind === "punct" && q.text === "?") q = siblings[--i];
78
+ if (q && isGroup(q) && q.open === "(") {
79
+ const fnWord = prevMeaningful(siblings, i);
80
+ return fnWord && !isGroup(fnWord) && fnWord.kind === "word" ? [asName(fnWord.text), "Output"] : ["Output"];
81
+ }
82
+ if (q && !isGroup(q) && (q.kind === "word" || q.kind === "string")) return [asName(q.text)];
83
+ }
84
+ return [];
162
85
  }
86
+ /**
87
+ * Visits every hoistable `{ ... }` type literal with its naming path. Value
88
+ * objects (after `=`) are skipped — except `type X = { ... }` alias bodies,
89
+ * which are visited with their alias name so they can act as the canonical
90
+ * definition.
91
+ */
163
92
  function walkLiterals(siblings, path, visit) {
164
- var _a;
165
- for (let i = 0; i < siblings.length; i++) {
166
- const node = siblings[i];
167
- if (!isGroup(node)) {
168
- continue;
169
- }
170
- if (node.open === "(") {
171
- const fnWord = prevMeaningful(siblings, i);
172
- const fnName = fnWord && !isGroup(fnWord) && fnWord.kind === "word" ? [asName(fnWord.text)] : [];
173
- walkLiterals(node.children, [...path, ...fnName], visit);
174
- continue;
175
- }
176
- if (node.open === "<" || node.open === "[") {
177
- walkLiterals(node.children, [...path, ...segmentsAt(siblings, i)], visit);
178
- continue;
179
- }
180
- const prev = prevMeaningful(siblings, i);
181
- if (prev && isPunct(prev, "=")) {
182
- const eqIdx = siblings.indexOf(prev);
183
- const nameTok = siblings[eqIdx - 1];
184
- const typeTok = siblings[eqIdx - 2];
185
- const isAlias = nameTok && typeTok && !isGroup(nameTok) && !isGroup(typeTok) && nameTok.kind === "word" && typeTok.kind === "word" && typeTok.text === "type";
186
- if (!isAlias) {
187
- continue;
188
- }
189
- const aliasName = nameTok.text;
190
- visit({ siblings, index: i, group: node, path: [aliasName], aliasName });
191
- if (siblings[i] === node) {
192
- walkLiterals(node.children, [aliasName], visit);
193
- }
194
- continue;
195
- }
196
- const prevIsNamespace = prev && !isGroup(prev) && prev.kind === "word" && ((_a = prevMeaningful(siblings, siblings.indexOf(prev))) == null ? void 0 : _a.kind) === "word";
197
- const namespaceLike = prevIsNamespace && ["namespace", "module"].includes(prevMeaningful(siblings, siblings.indexOf(prev)).text);
198
- const segments = segmentsAt(siblings, i);
199
- const newPath = [...path, ...segments];
200
- if (!namespaceLike) {
201
- visit({ siblings, index: i, group: node, path: newPath });
202
- }
203
- if (siblings[i] === node) {
204
- walkLiterals(node.children, namespaceLike ? [...path, asName(prev.text)] : newPath, visit);
205
- }
206
- }
93
+ for (let i = 0; i < siblings.length; i++) {
94
+ var _prevMeaningful;
95
+ const node = siblings[i];
96
+ if (!isGroup(node)) continue;
97
+ if (node.open === "(") {
98
+ const fnWord = prevMeaningful(siblings, i);
99
+ const fnName = fnWord && !isGroup(fnWord) && fnWord.kind === "word" ? [asName(fnWord.text)] : [];
100
+ walkLiterals(node.children, [...path, ...fnName], visit);
101
+ continue;
102
+ }
103
+ if (node.open === "<" || node.open === "[") {
104
+ walkLiterals(node.children, [...path, ...segmentsAt(siblings, i)], visit);
105
+ continue;
106
+ }
107
+ const prev = prevMeaningful(siblings, i);
108
+ if (prev && isPunct(prev, "=")) {
109
+ const eqIdx = siblings.indexOf(prev);
110
+ const nameTok = siblings[eqIdx - 1];
111
+ const typeTok = siblings[eqIdx - 2];
112
+ if (!(nameTok && typeTok && !isGroup(nameTok) && !isGroup(typeTok) && nameTok.kind === "word" && typeTok.kind === "word" && typeTok.text === "type")) continue;
113
+ const aliasName = nameTok.text;
114
+ visit({
115
+ siblings,
116
+ index: i,
117
+ group: node,
118
+ path: [aliasName],
119
+ aliasName
120
+ });
121
+ if (siblings[i] === node) walkLiterals(node.children, [aliasName], visit);
122
+ continue;
123
+ }
124
+ const namespaceLike = prev && !isGroup(prev) && prev.kind === "word" && ((_prevMeaningful = prevMeaningful(siblings, siblings.indexOf(prev))) === null || _prevMeaningful === void 0 ? void 0 : _prevMeaningful.kind) === "word" && ["namespace", "module"].includes(prevMeaningful(siblings, siblings.indexOf(prev)).text);
125
+ const segments = segmentsAt(siblings, i);
126
+ const newPath = [...path, ...segments];
127
+ if (!namespaceLike) visit({
128
+ siblings,
129
+ index: i,
130
+ group: node,
131
+ path: newPath
132
+ });
133
+ if (siblings[i] === node) walkLiterals(node.children, namespaceLike ? [...path, asName(prev.text)] : newPath, visit);
134
+ }
207
135
  }
208
136
  function extractAndHoistTypes(code) {
209
- const top = parse(tokenize(code));
210
- const counts = /* @__PURE__ */ new Map();
211
- const aliasByKey = /* @__PURE__ */ new Map();
212
- const usedNames = /* @__PURE__ */ new Set();
213
- walkLiterals(top, [], ({ group, path, aliasName }) => {
214
- const key = keyOf(group);
215
- counts.set(key, (counts.get(key) ?? 0) + 1);
216
- if (aliasName && !aliasByKey.has(key)) {
217
- aliasByKey.set(key, aliasName);
218
- }
219
- for (const segment of path) {
220
- usedNames.add(segment);
221
- }
222
- if (aliasName) {
223
- usedNames.add(aliasName);
224
- }
225
- });
226
- const uniqueName = (base) => {
227
- let name = base;
228
- let counter = 1;
229
- while (usedNames.has(name)) {
230
- name = `${base}${counter++}`;
231
- }
232
- usedNames.add(name);
233
- return name;
234
- };
235
- const newAliases = [];
236
- walkLiterals(top, [], ({ siblings, index, group, aliasName, path }) => {
237
- const key = keyOf(group);
238
- if ((counts.get(key) ?? 0) < 2 || aliasName) {
239
- return;
240
- }
241
- let name = aliasByKey.get(key);
242
- if (!name) {
243
- const deduped = [...new Set(path)];
244
- name = uniqueName(deduped.length ? deduped.join("") : "UnnamedType");
245
- aliasByKey.set(key, name);
246
- newAliases.push(`type ${name} = ${printNodes([{ ...group, nl: 0 }])}`);
247
- }
248
- siblings[index] = { kind: "word", text: name, nl: group.nl };
249
- });
250
- const body = printNodes(top);
251
- return newAliases.length ? `${newAliases.join("\n")}
252
- ${body}` : body;
137
+ const top = parse(tokenize(code));
138
+ const counts = /* @__PURE__ */ new Map();
139
+ const aliasByKey = /* @__PURE__ */ new Map();
140
+ const usedNames = /* @__PURE__ */ new Set();
141
+ walkLiterals(top, [], ({ group, path, aliasName }) => {
142
+ const key = keyOf(group);
143
+ counts.set(key, (counts.get(key) ?? 0) + 1);
144
+ if (aliasName && !aliasByKey.has(key)) aliasByKey.set(key, aliasName);
145
+ for (const segment of path) usedNames.add(segment);
146
+ if (aliasName) usedNames.add(aliasName);
147
+ });
148
+ const uniqueName = (base) => {
149
+ let name = base;
150
+ let counter = 1;
151
+ while (usedNames.has(name)) name = `${base}${counter++}`;
152
+ usedNames.add(name);
153
+ return name;
154
+ };
155
+ const newAliases = [];
156
+ walkLiterals(top, [], ({ siblings, index, group, aliasName, path }) => {
157
+ const key = keyOf(group);
158
+ if ((counts.get(key) ?? 0) < 2 || aliasName) return;
159
+ let name = aliasByKey.get(key);
160
+ if (!name) {
161
+ const deduped = [...new Set(path)];
162
+ name = uniqueName(deduped.length ? deduped.join("") : "UnnamedType");
163
+ aliasByKey.set(key, name);
164
+ newAliases.push(`type ${name} = ${printNodes([{
165
+ ...group,
166
+ nl: 0
167
+ }])}`);
168
+ }
169
+ siblings[index] = {
170
+ kind: "word",
171
+ text: name,
172
+ nl: group.nl
173
+ };
174
+ });
175
+ const body = printNodes(top);
176
+ return newAliases.length ? `${newAliases.join("\n")}\n${body}` : body;
253
177
  }
254
178
  async function hoistTypings(code, formatOptions) {
255
- formatOptions ??= {};
256
- formatOptions.throwOnError ??= true;
257
- for (let i = 1; i <= 5; i++) {
258
- try {
259
- const initialCode = code;
260
- code = extractAndHoistTypes(code);
261
- if (initialCode === code) {
262
- break;
263
- }
264
- } catch (err) {
265
- console.error(err);
266
- if (formatOptions.throwOnError) {
267
- throw new CodeFormattingError(err instanceof Error ? err.message : String(err ?? "Unknown Error"), code);
268
- }
269
- break;
270
- }
271
- }
272
- return formatTypings(code, formatOptions);
179
+ formatOptions ??= {};
180
+ formatOptions.throwOnError ??= true;
181
+ for (let i = 1; i <= 5; i++) try {
182
+ const initialCode = code;
183
+ code = extractAndHoistTypes(code);
184
+ if (initialCode === code) break;
185
+ } catch (err) {
186
+ console.error(err);
187
+ if (formatOptions.throwOnError) throw new CodeFormattingError(err instanceof Error ? err.message : String(err ?? "Unknown Error"), code);
188
+ break;
189
+ }
190
+ return formatTypings(code, formatOptions);
273
191
  }
274
-
275
- // src/objects.ts
276
- var LARGE_OBJECT_LINES_OF_CODE = 10;
192
+ //#endregion
193
+ //#region src/objects.ts
194
+ /** How many lines of code an object needs to have before we insert a comment for end tag */
195
+ const LARGE_OBJECT_LINES_OF_CODE = 10;
196
+ /**
197
+ * ObjectInstance creates stateful, namespace-scoped objects for LLMz agents.
198
+ *
199
+ * Objects combine properties (stateful variables) and tools (functions) into a
200
+ * single namespace that the LLM can interact with. This provides organized,
201
+ * type-safe interfaces for complex data and functionality.
202
+ *
203
+ * ## Key Features
204
+ * - **Stateful Properties**: Variables that persist across execution iterations
205
+ * - **Validation**: Zod schema validation for property changes
206
+ * - **Tool Grouping**: Organize related tools under a common namespace
207
+ * - **Type Safety**: Full TypeScript inference and validation
208
+ * - **Dynamic Updates**: Properties can reflect real-time state changes
209
+ *
210
+ * ## Use Cases
211
+ * - **User Profile Management**: Collect and validate user data over time
212
+ * - **API Namespacing**: Group related API calls under a common interface
213
+ * - **State Machines**: Track and modify execution state
214
+ * - **Configuration Objects**: Manage settings and preferences
215
+ * - **Multi-Agent Systems**: Per-agent state and capabilities
216
+ *
217
+ * ## Basic Usage
218
+ *
219
+ * ### Simple Property Object
220
+ * ```typescript
221
+ * const userProfile = new ObjectInstance({
222
+ * name: 'user',
223
+ * description: 'User profile data',
224
+ * properties: [
225
+ * {
226
+ * name: 'name',
227
+ * value: 'John Doe',
228
+ * type: z.string().min(1),
229
+ * writable: true,
230
+ * },
231
+ * {
232
+ * name: 'email',
233
+ * value: null,
234
+ * type: z.string().email().nullable(),
235
+ * writable: true,
236
+ * },
237
+ * {
238
+ * name: 'id',
239
+ * value: 'user_123',
240
+ * writable: false, // Read-only
241
+ * },
242
+ * ],
243
+ * })
244
+ *
245
+ * // LLM can access and modify: user.name, user.email
246
+ * // LLM can read only: user.id
247
+ * ```
248
+ *
249
+ * ### Tool Grouping
250
+ * ```typescript
251
+ * const fileSystem = new ObjectInstance({
252
+ * name: 'fs',
253
+ * description: 'File system operations',
254
+ * tools: [
255
+ * new Tool({
256
+ * name: 'readFile',
257
+ * input: z.object({ path: z.string() }),
258
+ * output: z.string(),
259
+ * handler: async ({ path }) => readFileSync(path, 'utf8'),
260
+ * }),
261
+ * new Tool({
262
+ * name: 'writeFile',
263
+ * input: z.object({ path: z.string(), content: z.string() }),
264
+ * handler: async ({ path, content }) => writeFileSync(path, content),
265
+ * }),
266
+ * ],
267
+ * })
268
+ *
269
+ * // LLM can call: fs.readFile(), fs.writeFile()
270
+ * ```
271
+ *
272
+ * ### Combined Properties and Tools
273
+ * ```typescript
274
+ * const database = new ObjectInstance({
275
+ * name: 'db',
276
+ * description: 'Database connection with state',
277
+ * properties: [
278
+ * {
279
+ * name: 'connectionString',
280
+ * value: 'postgresql://localhost:5432/mydb',
281
+ * writable: false,
282
+ * },
283
+ * {
284
+ * name: 'lastQuery',
285
+ * value: null,
286
+ * type: z.string().nullable(),
287
+ * writable: true,
288
+ * },
289
+ * ],
290
+ * tools: [
291
+ * new Tool({
292
+ * name: 'query',
293
+ * input: z.object({ sql: z.string() }),
294
+ * output: z.array(z.record(z.any())),
295
+ * handler: async ({ sql }) => {
296
+ * // Execute query and update lastQuery property
297
+ * const results = await executeQuery(sql)
298
+ * return results
299
+ * },
300
+ * }),
301
+ * ],
302
+ * })
303
+ * ```
304
+ *
305
+ * ## Dynamic Objects
306
+ *
307
+ * Objects can be created dynamically to reflect current state:
308
+ *
309
+ * ```typescript
310
+ * const memory: Record<string, any> = {}
311
+ *
312
+ * const getObjects = () => [
313
+ * new ObjectInstance({
314
+ * name: 'user',
315
+ * properties: [
316
+ * {
317
+ * name: 'name',
318
+ * value: memory.name ?? null,
319
+ * type: z.string().nullable(),
320
+ * writable: true,
321
+ * },
322
+ * {
323
+ * name: 'age',
324
+ * value: memory.age ?? null,
325
+ * type: z.number().min(18).max(100).nullable(),
326
+ * writable: true,
327
+ * },
328
+ * ],
329
+ * }),
330
+ * ]
331
+ *
332
+ * await execute({
333
+ * objects: getObjects, // Function returning current state
334
+ * onTrace: ({ trace }) => {
335
+ * if (trace.type === 'property') {
336
+ * // Persist property changes
337
+ * memory[trace.property] = trace.value
338
+ * }
339
+ * },
340
+ * // ...
341
+ * })
342
+ * ```
343
+ *
344
+ * ## Property Validation
345
+ *
346
+ * Properties support comprehensive validation through Zod schemas:
347
+ *
348
+ * ```typescript
349
+ * const userSettings = new ObjectInstance({
350
+ * name: 'settings',
351
+ * properties: [
352
+ * {
353
+ * name: 'theme',
354
+ * value: 'light',
355
+ * type: z.enum(['light', 'dark']),
356
+ * writable: true,
357
+ * },
358
+ * {
359
+ * name: 'maxRetries',
360
+ * value: 3,
361
+ * type: z.number().min(1).max(10),
362
+ * writable: true,
363
+ * },
364
+ * {
365
+ * name: 'email',
366
+ * value: null,
367
+ * type: z.string().email().nullable(),
368
+ * writable: true,
369
+ * },
370
+ * ],
371
+ * })
372
+ * ```
373
+ *
374
+ * ## TypeScript Integration
375
+ *
376
+ * Objects generate TypeScript namespace declarations:
377
+ *
378
+ * ```typescript
379
+ * // Generated types for the LLM context:
380
+ * export namespace user {
381
+ * const name: Writable<string | null> = null
382
+ * const age: Writable<number | null> = null
383
+ * const id: Readonly<string> = "user_123"
384
+ * }
385
+ *
386
+ * export namespace fs {
387
+ * function readFile(args: { path: string }): Promise<string>
388
+ * function writeFile(args: { path: string, content: string }): Promise<void>
389
+ * }
390
+ * ```
391
+ *
392
+ * @see {@link https://github.com/botpress/botpress/blob/master/packages/llmz/examples/09_chat_variables/index.ts} Example usage
393
+ */
277
394
  var ObjectInstance = class {
278
- name;
279
- description;
280
- properties;
281
- tools;
282
- metadata;
283
- /**
284
- * Creates a new ObjectInstance.
285
- *
286
- * @param props - Object configuration
287
- * @param props.name - Unique object name (must be valid TypeScript identifier)
288
- * @param props.description - Human-readable description of the object
289
- * @param props.tools - Array of tools to group under this object namespace
290
- * @param props.properties - Array of stateful properties for this object
291
- * @param props.metadata - Additional metadata for the object
292
- *
293
- * @throws Error if name is not a valid identifier
294
- * @throws Error if description is not a string
295
- * @throws Error if metadata is not an object
296
- * @throws Error if properties/tools are not arrays
297
- * @throws Error if properties exceed 100 limit
298
- * @throws Error if property names are duplicated or invalid
299
- * @throws Error if property descriptions exceed 5000 characters
300
- *
301
- * @example
302
- * ```typescript
303
- * const userProfile = new ObjectInstance({
304
- * name: 'user',
305
- * description: 'User profile management',
306
- * properties: [
307
- * {
308
- * name: 'name',
309
- * value: 'John Doe',
310
- * type: z.string().min(1),
311
- * description: 'User full name',
312
- * writable: true,
313
- * },
314
- * {
315
- * name: 'email',
316
- * value: null,
317
- * type: z.string().email().nullable(),
318
- * description: 'User email address',
319
- * writable: true,
320
- * },
321
- * ],
322
- * tools: [
323
- * new Tool({
324
- * name: 'updateProfile',
325
- * input: z.object({ name: z.string(), email: z.string() }),
326
- * handler: async ({ name, email }) => {
327
- * // Update external system
328
- * await updateUserInDatabase({ name, email })
329
- * },
330
- * }),
331
- * ],
332
- * metadata: {
333
- * version: '1.0',
334
- * category: 'user-management',
335
- * },
336
- * })
337
- * ```
338
- */
339
- constructor(props) {
340
- var _a;
341
- if (!isValidIdentifier(props.name)) {
342
- throw new Error(
343
- `Invalid name for tool ${props.name}. A tool name must start with a letter and contain only letters, numbers, and underscores. It must be 1-50 characters long.`
344
- );
345
- }
346
- if (props.description !== void 0 && typeof props.description !== "string") {
347
- throw new Error(
348
- `Invalid description for tool ${props.name}. Expected a string, but got type "${typeof props.description}"`
349
- );
350
- }
351
- if (props.metadata !== void 0 && typeof props.metadata !== "object") {
352
- throw new Error(
353
- `Invalid metadata for tool ${props.name}. Expected an object, but got type "${typeof props.metadata}"`
354
- );
355
- }
356
- if (props.properties !== void 0 && !Array.isArray(props.properties)) {
357
- throw new Error(
358
- `Invalid properties for tool ${props.name}. Expected an array, but got type "${typeof props.properties}"`
359
- );
360
- }
361
- if (props.tools !== void 0 && !Array.isArray(props.tools)) {
362
- throw new Error(`Invalid tools for tool ${props.name}. Expected an array, but got type "${typeof props.tools}"`);
363
- }
364
- if ((_a = props.properties) == null ? void 0 : _a.length) {
365
- if (props.properties.length > 100) {
366
- throw new Error(
367
- `Too many properties for tool ${props.name}. Expected at most 100 properties, but got ${props.properties.length}`
368
- );
369
- }
370
- for (const prop of props.properties) {
371
- if (props.properties.filter((p) => p.name === prop.name).length > 1) {
372
- throw new Error(`Duplicate property name "${prop.name}" in tool ${props.name}`);
373
- }
374
- if (!isValidIdentifier(prop.name)) {
375
- throw new Error(
376
- `Invalid name for property ${prop.name}. A property name must start with a letter and contain only letters, numbers, and underscores. It must be 1-50 characters long.`
377
- );
378
- }
379
- if (prop.description !== void 0 && typeof prop.description !== "string") {
380
- throw new Error(
381
- `Invalid description for property ${prop.name}. Expected a string, but got type "${typeof prop.description}"`
382
- );
383
- }
384
- if (props.description && props.description.length >= 5e3) {
385
- throw new Error(
386
- `Description for property ${prop.name} is too long. Expected at most 5000 characters, but got ${props.description.length}`
387
- );
388
- }
389
- if (typeof prop.writable !== "boolean") {
390
- prop.writable = false;
391
- }
392
- }
393
- }
394
- this.name = props.name;
395
- this.description = props.description;
396
- this.metadata = props.metadata ?? {};
397
- this.properties = props.properties;
398
- this.tools = Tool.withUniqueNames(props.tools ?? []);
399
- }
400
- /**
401
- * Generates TypeScript namespace declarations for this object.
402
- *
403
- * This method creates TypeScript definitions that are included in the LLM context
404
- * to help it understand the available properties and tools. Properties become
405
- * const declarations with appropriate Readonly/Writable types, and tools become
406
- * function signatures.
407
- *
408
- * @returns Promise resolving to TypeScript namespace declaration
409
- *
410
- * @example
411
- * ```typescript
412
- * const obj = new ObjectInstance({
413
- * name: 'user',
414
- * properties: [
415
- * { name: 'name', value: 'John', writable: true },
416
- * { name: 'id', value: 123, writable: false },
417
- * ],
418
- * tools: [
419
- * new Tool({
420
- * name: 'save',
421
- * input: z.object({ data: z.string() }),
422
- * handler: async ({ data }) => { /* ... *\/ },
423
- * }),
424
- * ],
425
- * })
426
- *
427
- * const typings = await obj.getTypings()
428
- * console.log(typings)
429
- * // Output:
430
- * // export namespace user {
431
- * // const name: Writable<string> = "John"
432
- * // const id: Readonly<number> = 123
433
- * // function save(args: { data: string }): Promise<void>
434
- * // }
435
- * ```
436
- */
437
- async getTypings() {
438
- return getObjectTypings(this).withProperties().withTools().build();
439
- }
440
- /**
441
- * Converts this ObjectInstance to its JSON representation.
442
- *
443
- * This method serializes the object into a JSON format that includes its name,
444
- * description, properties, tools, and metadata. It is used for serialization
445
- * and transmission of the object state.
446
- *
447
- * @returns JSON representation of the ObjectInstance
448
- */
449
- toJSON() {
450
- return {
451
- name: this.name,
452
- description: this.description,
453
- properties: this.properties,
454
- tools: (this.tools ?? []).map((tool) => tool.toJSON()),
455
- metadata: this.metadata
456
- };
457
- }
395
+ name;
396
+ description;
397
+ properties;
398
+ tools;
399
+ metadata;
400
+ /**
401
+ * Creates a new ObjectInstance.
402
+ *
403
+ * @param props - Object configuration
404
+ * @param props.name - Unique object name (must be valid TypeScript identifier)
405
+ * @param props.description - Human-readable description of the object
406
+ * @param props.tools - Array of tools to group under this object namespace
407
+ * @param props.properties - Array of stateful properties for this object
408
+ * @param props.metadata - Additional metadata for the object
409
+ *
410
+ * @throws Error if name is not a valid identifier
411
+ * @throws Error if description is not a string
412
+ * @throws Error if metadata is not an object
413
+ * @throws Error if properties/tools are not arrays
414
+ * @throws Error if properties exceed 100 limit
415
+ * @throws Error if property names are duplicated or invalid
416
+ * @throws Error if property descriptions exceed 5000 characters
417
+ *
418
+ * @example
419
+ * ```typescript
420
+ * const userProfile = new ObjectInstance({
421
+ * name: 'user',
422
+ * description: 'User profile management',
423
+ * properties: [
424
+ * {
425
+ * name: 'name',
426
+ * value: 'John Doe',
427
+ * type: z.string().min(1),
428
+ * description: 'User full name',
429
+ * writable: true,
430
+ * },
431
+ * {
432
+ * name: 'email',
433
+ * value: null,
434
+ * type: z.string().email().nullable(),
435
+ * description: 'User email address',
436
+ * writable: true,
437
+ * },
438
+ * ],
439
+ * tools: [
440
+ * new Tool({
441
+ * name: 'updateProfile',
442
+ * input: z.object({ name: z.string(), email: z.string() }),
443
+ * handler: async ({ name, email }) => {
444
+ * // Update external system
445
+ * await updateUserInDatabase({ name, email })
446
+ * },
447
+ * }),
448
+ * ],
449
+ * metadata: {
450
+ * version: '1.0',
451
+ * category: 'user-management',
452
+ * },
453
+ * })
454
+ * ```
455
+ */
456
+ constructor(props) {
457
+ var _props$properties;
458
+ if (!isValidIdentifier(props.name)) throw new Error(`Invalid name for tool ${props.name}. A tool name must start with a letter and contain only letters, numbers, and underscores. It must be 1-50 characters long.`);
459
+ if (props.description !== void 0 && typeof props.description !== "string") throw new Error(`Invalid description for tool ${props.name}. Expected a string, but got type "${typeof props.description}"`);
460
+ if (props.metadata !== void 0 && typeof props.metadata !== "object") throw new Error(`Invalid metadata for tool ${props.name}. Expected an object, but got type "${typeof props.metadata}"`);
461
+ if (props.properties !== void 0 && !Array.isArray(props.properties)) throw new Error(`Invalid properties for tool ${props.name}. Expected an array, but got type "${typeof props.properties}"`);
462
+ if (props.tools !== void 0 && !Array.isArray(props.tools)) throw new Error(`Invalid tools for tool ${props.name}. Expected an array, but got type "${typeof props.tools}"`);
463
+ if ((_props$properties = props.properties) === null || _props$properties === void 0 ? void 0 : _props$properties.length) {
464
+ if (props.properties.length > 100) throw new Error(`Too many properties for tool ${props.name}. Expected at most 100 properties, but got ${props.properties.length}`);
465
+ for (const prop of props.properties) {
466
+ if (props.properties.filter((p) => p.name === prop.name).length > 1) throw new Error(`Duplicate property name "${prop.name}" in tool ${props.name}`);
467
+ if (!isValidIdentifier(prop.name)) throw new Error(`Invalid name for property ${prop.name}. A property name must start with a letter and contain only letters, numbers, and underscores. It must be 1-50 characters long.`);
468
+ if (prop.description !== void 0 && typeof prop.description !== "string") throw new Error(`Invalid description for property ${prop.name}. Expected a string, but got type "${typeof prop.description}"`);
469
+ if (props.description && props.description.length >= 5e3) throw new Error(`Description for property ${prop.name} is too long. Expected at most 5000 characters, but got ${props.description.length}`);
470
+ if (typeof prop.writable !== "boolean") prop.writable = false;
471
+ }
472
+ }
473
+ this.name = props.name;
474
+ this.description = props.description;
475
+ this.metadata = props.metadata ?? {};
476
+ this.properties = props.properties;
477
+ this.tools = Tool.withUniqueNames(props.tools ?? []);
478
+ }
479
+ /**
480
+ * Generates TypeScript namespace declarations for this object.
481
+ *
482
+ * This method creates TypeScript definitions that are included in the LLM context
483
+ * to help it understand the available properties and tools. Properties become
484
+ * const declarations with appropriate Readonly/Writable types, and tools become
485
+ * function signatures.
486
+ *
487
+ * @returns Promise resolving to TypeScript namespace declaration
488
+ *
489
+ * @example
490
+ * ```typescript
491
+ * const obj = new ObjectInstance({
492
+ * name: 'user',
493
+ * properties: [
494
+ * { name: 'name', value: 'John', writable: true },
495
+ * { name: 'id', value: 123, writable: false },
496
+ * ],
497
+ * tools: [
498
+ * new Tool({
499
+ * name: 'save',
500
+ * input: z.object({ data: z.string() }),
501
+ * handler: async ({ data }) => { /* ... *\/ },
502
+ * }),
503
+ * ],
504
+ * })
505
+ *
506
+ * const typings = await obj.getTypings()
507
+ * console.log(typings)
508
+ * // Output:
509
+ * // export namespace user {
510
+ * // const name: Writable<string> = "John"
511
+ * // const id: Readonly<number> = 123
512
+ * // function save(args: { data: string }): Promise<void>
513
+ * // }
514
+ * ```
515
+ */
516
+ async getTypings() {
517
+ return getObjectTypings(this).withProperties().withTools().build();
518
+ }
519
+ /**
520
+ * Converts this ObjectInstance to its JSON representation.
521
+ *
522
+ * This method serializes the object into a JSON format that includes its name,
523
+ * description, properties, tools, and metadata. It is used for serialization
524
+ * and transmission of the object state.
525
+ *
526
+ * @returns JSON representation of the ObjectInstance
527
+ */
528
+ toJSON() {
529
+ return {
530
+ name: this.name,
531
+ description: this.description,
532
+ properties: this.properties,
533
+ tools: (this.tools ?? []).map((tool) => tool.toJSON()),
534
+ metadata: this.metadata
535
+ };
536
+ }
458
537
  };
538
+ /**
539
+ * Creates a TypeScript namespace builder for an ObjectInstance.
540
+ *
541
+ * This function provides a fluent API for generating TypeScript declarations
542
+ * that include properties, tools, or both. It's used internally by ObjectInstance.getTypings().
543
+ *
544
+ * @param obj - The ObjectInstance to generate typings for
545
+ * @returns A builder object with methods to configure and build the namespace
546
+ * @internal
547
+ */
459
548
  function getObjectTypings(obj) {
460
- let includeProperties = false;
461
- let includeTools = false;
462
- let hoisting = false;
463
- const typings = [];
464
- const addProperties = async () => {
465
- var _a;
466
- if (includeProperties && ((_a = obj.properties) == null ? void 0 : _a.length)) {
467
- typings.push("");
468
- typings.push("// ---------------- //");
469
- typings.push("// Properties //");
470
- typings.push("// ---------------- //");
471
- typings.push("");
472
- for (const prop of obj.properties ?? []) {
473
- const description = prop.description ?? "";
474
- if (description == null ? void 0 : description.trim().length) {
475
- typings.push(getMultilineComment(description));
476
- }
477
- let type = "unknown";
478
- if (prop.type) {
479
- type = await getTypings(prop.type, {});
480
- } else if (prop.value !== void 0) {
481
- type = typeof prop.value;
482
- }
483
- type = prop.writable ? `Writable<${type}>` : `Readonly<${type}>`;
484
- const value = embedPropertyValue(prop);
485
- typings.push(`const ${prop.name}: ${type} = ${value}`);
486
- }
487
- }
488
- };
489
- const addTools = async () => {
490
- var _a;
491
- if (includeTools && ((_a = obj.tools) == null ? void 0 : _a.length)) {
492
- typings.push("");
493
- typings.push("// ---------------- //");
494
- typings.push("// Tools //");
495
- typings.push("// ---------------- //");
496
- typings.push("");
497
- for (const tool of obj.tools) {
498
- const fnType = z.function(tool.zInput, tool.zOutput).title(tool.name).describe(tool.description ?? "");
499
- let temp = await getTypings(fnType, {
500
- declaration: true
501
- });
502
- temp = temp.replace("declare function ", "function ");
503
- typings.push(temp);
504
- }
505
- }
506
- };
507
- const finalize = async () => {
508
- var _a;
509
- let closingBracket = "";
510
- if (typings.length >= LARGE_OBJECT_LINES_OF_CODE) {
511
- closingBracket = ` // end namespace "${obj.name}"`;
512
- }
513
- let body = typings.join("\n");
514
- if (hoisting) {
515
- body = await hoistTypings(body, { throwOnError: false });
516
- }
517
- typings.push("}" + closingBracket);
518
- let header = "";
519
- if ((_a = obj.description) == null ? void 0 : _a.trim().length) {
520
- header = getMultilineComment(obj.description);
521
- }
522
- return formatTypings(
523
- `${header}
549
+ let includeProperties = false;
550
+ let includeTools = false;
551
+ let hoisting = false;
552
+ const typings = [];
553
+ const addProperties = async () => {
554
+ var _obj$properties;
555
+ if (includeProperties && ((_obj$properties = obj.properties) === null || _obj$properties === void 0 ? void 0 : _obj$properties.length)) {
556
+ typings.push("");
557
+ typings.push("// ---------------- //");
558
+ typings.push("// Properties //");
559
+ typings.push("// ---------------- //");
560
+ typings.push("");
561
+ for (const prop of obj.properties ?? []) {
562
+ const description = prop.description ?? "";
563
+ if (description === null || description === void 0 ? void 0 : description.trim().length) typings.push(getMultilineComment(description));
564
+ let type = "unknown";
565
+ if (prop.type) type = await getTypings(prop.type, {});
566
+ else if (prop.value !== void 0) type = typeof prop.value;
567
+ type = prop.writable ? `Writable<${type}>` : `Readonly<${type}>`;
568
+ const value = embedPropertyValue(prop);
569
+ typings.push(`const ${prop.name}: ${type} = ${value}`);
570
+ }
571
+ }
572
+ };
573
+ const addTools = async () => {
574
+ var _obj$tools;
575
+ if (includeTools && ((_obj$tools = obj.tools) === null || _obj$tools === void 0 ? void 0 : _obj$tools.length)) {
576
+ typings.push("");
577
+ typings.push("// ---------------- //");
578
+ typings.push("// Tools //");
579
+ typings.push("// ---------------- //");
580
+ typings.push("");
581
+ for (const tool of obj.tools) {
582
+ const fnType = z.function(tool.zInput, tool.zOutput).title(tool.name).describe(tool.description ?? "");
583
+ let temp = await getTypings(fnType, { declaration: true });
584
+ temp = temp.replace("declare function ", "function ");
585
+ typings.push(temp);
586
+ }
587
+ }
588
+ };
589
+ const finalize = async () => {
590
+ var _obj$description;
591
+ let closingBracket = "";
592
+ if (typings.length >= LARGE_OBJECT_LINES_OF_CODE) closingBracket = ` // end namespace "${obj.name}"`;
593
+ let body = typings.join("\n");
594
+ if (hoisting) body = await hoistTypings(body, { throwOnError: false });
595
+ typings.push("}" + closingBracket);
596
+ let header = "";
597
+ if ((_obj$description = obj.description) === null || _obj$description === void 0 ? void 0 : _obj$description.trim().length) header = getMultilineComment(obj.description);
598
+ return formatTypings(`${header}
524
599
  export namespace ${obj.name} {
525
600
  ${body}
526
- } ${closingBracket}`.trim(),
527
- { throwOnError: false }
528
- );
529
- };
530
- const api = {
531
- withProperties: () => {
532
- includeProperties = true;
533
- return api;
534
- },
535
- withTools: () => {
536
- includeTools = true;
537
- return api;
538
- },
539
- withHoisting: () => {
540
- hoisting = true;
541
- return api;
542
- },
543
- async build() {
544
- await addProperties();
545
- await addTools();
546
- return finalize();
547
- }
548
- };
549
- return api;
601
+ } ${closingBracket}`.trim(), { throwOnError: false });
602
+ };
603
+ const api = {
604
+ withProperties: () => {
605
+ includeProperties = true;
606
+ return api;
607
+ },
608
+ withTools: () => {
609
+ includeTools = true;
610
+ return api;
611
+ },
612
+ withHoisting: () => {
613
+ hoisting = true;
614
+ return api;
615
+ },
616
+ async build() {
617
+ await addProperties();
618
+ await addTools();
619
+ return finalize();
620
+ }
621
+ };
622
+ return api;
550
623
  }
624
+ /**
625
+ * Converts a property value to its TypeScript literal representation.
626
+ *
627
+ * This function handles the serialization of various JavaScript types into
628
+ * TypeScript code that can be embedded in generated namespace declarations.
629
+ * It supports primitives, objects, arrays, dates, regex, and other common types.
630
+ *
631
+ * @param property - The ObjectProperty containing the value to embed
632
+ * @returns String representation of the value for use in TypeScript code
633
+ * @internal
634
+ */
551
635
  function embedPropertyValue(property) {
552
- if (typeof property.value === "string") {
553
- return escapeString(property.value);
554
- }
555
- if (Number.isNaN(property.value)) {
556
- return "NaN";
557
- }
558
- if (typeof property.value === "number" && Number.isInteger(property.value)) {
559
- return property.value.toString();
560
- }
561
- if (typeof property.value === "boolean") {
562
- return property.value.toString();
563
- }
564
- if (Array.isArray(property.value) || typeof property.value === "object") {
565
- return JSON.stringify(property.value);
566
- }
567
- if (property.value instanceof Date) {
568
- return `new Date('${property.value.toISOString()}')`;
569
- }
570
- if (property.value instanceof RegExp) {
571
- return `new RegExp(${escapeString(property.value.source)}, ${escapeString(property.value.flags)})`;
572
- }
573
- if (property.value === null) {
574
- return "null";
575
- }
576
- if (property.value === void 0) {
577
- return "undefined";
578
- }
579
- if (typeof property.value === "function") {
580
- return "function() {}";
581
- }
582
- if (typeof property.value === "symbol") {
583
- return "Symbol()";
584
- }
585
- if (typeof property.value === "bigint") {
586
- return `${property.value}n`;
587
- }
588
- if (property.value instanceof Error) {
589
- return `Error(${escapeString(property.value.message)})`;
590
- }
591
- if (property.value instanceof Map) {
592
- return `new Map(${JSON.stringify(Array.from(property.value.entries()))})`;
593
- }
594
- if (property.value instanceof Set) {
595
- return `new Set(${JSON.stringify(Array.from(property.value.values()))})`;
596
- }
597
- return "unknown";
636
+ if (typeof property.value === "string") return escapeString(property.value);
637
+ if (Number.isNaN(property.value)) return "NaN";
638
+ if (typeof property.value === "number" && Number.isInteger(property.value)) return property.value.toString();
639
+ if (typeof property.value === "boolean") return property.value.toString();
640
+ if (Array.isArray(property.value) || typeof property.value === "object") return JSON.stringify(property.value);
641
+ if (property.value instanceof Date) return `new Date('${property.value.toISOString()}')`;
642
+ if (property.value instanceof RegExp) return `new RegExp(${escapeString(property.value.source)}, ${escapeString(property.value.flags)})`;
643
+ if (property.value === null) return "null";
644
+ if (property.value === void 0) return "undefined";
645
+ if (typeof property.value === "function") return "function() {}";
646
+ if (typeof property.value === "symbol") return "Symbol()";
647
+ if (typeof property.value === "bigint") return `${property.value}n`;
648
+ if (property.value instanceof Error) return `Error(${escapeString(property.value.message)})`;
649
+ if (property.value instanceof Map) return `new Map(${JSON.stringify(Array.from(property.value.entries()))})`;
650
+ if (property.value instanceof Set) return `new Set(${JSON.stringify(Array.from(property.value.values()))})`;
651
+ return "unknown";
598
652
  }
599
-
600
- // src/citations.ts
601
- var RARE_SYMBOLS = {
602
- ARROW_UP: "\u2191",
603
- CIRCLE_BULLET: "\u30FB",
604
- STAR_BULLET_FULL: "\u2605",
605
- STAR_BULLET_EMPTY: "\u2606",
606
- ARROW_BULLET: "\u2192",
607
- SQUARE_BULLET: "\u25A0",
608
- TRIANGLE_BULLET: "\u25BA",
609
- OPENING_TAG: "\u3010",
610
- CLOSING_TAG: "\u3011",
611
- SS: "\xA7",
612
- CROSS: "\u2020"
653
+ //#endregion
654
+ //#region src/citations.ts
655
+ /** These are all single-token symbols that are very rare in natural texts, making them suitable for usage within GPT Prompts. */
656
+ const RARE_SYMBOLS = {
657
+ ARROW_UP: "↑",
658
+ CIRCLE_BULLET: "・",
659
+ STAR_BULLET_FULL: "★",
660
+ STAR_BULLET_EMPTY: "☆",
661
+ ARROW_BULLET: "→",
662
+ SQUARE_BULLET: "■",
663
+ TRIANGLE_BULLET: "►",
664
+ OPENING_TAG: "【",
665
+ CLOSING_TAG: "】",
666
+ SS: "§",
667
+ CROSS: "†"
613
668
  };
614
669
  var CitationsManager = class {
615
- _citations = /* @__PURE__ */ new Map();
616
- _nextId = 0;
617
- /**
618
- * Registers a new source and returns the created Citation.
619
- * @param source An object representing the source of the citation.
620
- * @returns The created Citation.
621
- */
622
- registerSource(source) {
623
- const id = this._nextId++;
624
- const tag = `${RARE_SYMBOLS.OPENING_TAG}${id}${RARE_SYMBOLS.CLOSING_TAG}`;
625
- const citation = {
626
- id,
627
- source,
628
- tag
629
- };
630
- this._citations.set(id, citation);
631
- return citation;
632
- }
633
- /**
634
- * Extracts citations from the provided content and cleans the content of citation tags.
635
- * Non-found citations are replaced with a special "Not Found" citation.
636
- * @param content The string content containing citation tags.
637
- * @returns An object containing the cleaned content and an array of found citations.
638
- */
639
- extractCitations(content, replace) {
640
- const citations = [];
641
- const notFoundCitation = {
642
- id: -1,
643
- source: "Not Found",
644
- tag: ""
645
- };
646
- const regex = new RegExp(`${RARE_SYMBOLS.OPENING_TAG}([\\d|\\w|\\s|,]{0,})${RARE_SYMBOLS.CLOSING_TAG}`, "ig");
647
- let match;
648
- const offsets = [];
649
- while ((match = regex.exec(content)) !== null) {
650
- const offset = match.index;
651
- const length = match[0].length;
652
- offsets.push({ start: offset, length });
653
- const multi = (match[1] ?? "").split(/\D/g).map((s) => s.trim()).filter(Boolean).map((s) => parseInt(s, 10)).filter((s) => !isNaN(s) && s >= 0);
654
- for (const citationId of multi) {
655
- const citation = this._citations.get(citationId);
656
- if (citation) {
657
- citations.push({ ...citation, offset });
658
- } else {
659
- citations.push({ ...notFoundCitation, offset });
660
- }
661
- }
662
- }
663
- const entries = offsets.map((o) => ({
664
- start: o.start,
665
- length: o.length,
666
- citations: citations.filter((x) => x.offset === o.start && x.id !== -1)
667
- })).sort((a, b) => a.start - b.start);
668
- let result = "";
669
- let cursor = 0;
670
- for (const { start, length, citations: citations2 } of entries) {
671
- result += content.slice(cursor, start);
672
- const replacement = citations2.map((citation) => replace ? replace(citation) : "").join("");
673
- result += replacement;
674
- cursor = start + length;
675
- }
676
- result += content.slice(cursor);
677
- return { cleaned: result, citations };
678
- }
679
- /**
680
- * Strips citation tags from the provided content.
681
- * @param content The string content containing citation tags.
682
- * @returns The cleaned content without citation tags.
683
- */
684
- static stripCitationTags(content) {
685
- const regex = new RegExp(`${RARE_SYMBOLS.OPENING_TAG}([\\d|\\w|\\s|,]{0,})${RARE_SYMBOLS.CLOSING_TAG}?`, "g");
686
- return content.replace(regex, "");
687
- }
688
- /**
689
- * Removes citations from a deeply nested plain object and returns a new object with citations removed.
690
- * @param obj The plain object to process.
691
- * @returns A tuple containing the new object and an array of extracted citations with paths.
692
- */
693
- removeCitationsFromObject(obj) {
694
- const result = [];
695
- const processObject = (current, path) => {
696
- if (typeof current === "string") {
697
- const extraction = this.extractCitations(current);
698
- if (extraction.citations.length > 0) {
699
- result.push(...extraction.citations.map((citation) => ({ path, citation })));
700
- }
701
- return extraction.cleaned;
702
- } else if (typeof current === "object" && current !== null) {
703
- const newObject = Array.isArray(current) ? [] : {};
704
- for (const key of Object.keys(current)) {
705
- newObject[key] = processObject(current[key], `${path}.${key}`);
706
- }
707
- return newObject;
708
- }
709
- return current;
710
- };
711
- const newObj = processObject(obj, "root");
712
- return [newObj, result];
713
- }
714
- /**
715
- * Re-adds citations to the cleaned content based on their offsets.
716
- * @param cleaned The cleaned string without citation tags.
717
- * @param citations The array of citations with offsets.
718
- * @returns The string with citation tags re-added.
719
- */
720
- reAddCitations(cleaned, citations) {
721
- let content = cleaned;
722
- citations.sort((a, b) => (a.offset ?? 0) - (b.offset ?? 0));
723
- const adjustment = 0;
724
- for (const citation of citations) {
725
- if (citation.offset != null) {
726
- const position = citation.offset + adjustment;
727
- content = content.slice(0, position) + citation.tag + content.slice(position);
728
- }
729
- }
730
- return content;
731
- }
670
+ _citations = /* @__PURE__ */ new Map();
671
+ _nextId = 0;
672
+ /**
673
+ * Registers a new source and returns the created Citation.
674
+ * @param source An object representing the source of the citation.
675
+ * @returns The created Citation.
676
+ */
677
+ registerSource(source) {
678
+ const id = this._nextId++;
679
+ const citation = {
680
+ id,
681
+ source,
682
+ tag: `${RARE_SYMBOLS.OPENING_TAG}${id}${RARE_SYMBOLS.CLOSING_TAG}`
683
+ };
684
+ this._citations.set(id, citation);
685
+ return citation;
686
+ }
687
+ /**
688
+ * Extracts citations from the provided content and cleans the content of citation tags.
689
+ * Non-found citations are replaced with a special "Not Found" citation.
690
+ * @param content The string content containing citation tags.
691
+ * @returns An object containing the cleaned content and an array of found citations.
692
+ */
693
+ extractCitations(content, replace) {
694
+ const citations = [];
695
+ const notFoundCitation = {
696
+ id: -1,
697
+ source: "Not Found",
698
+ tag: ""
699
+ };
700
+ const regex = new RegExp(`${RARE_SYMBOLS.OPENING_TAG}([\\d|\\w|\\s|,]{0,})${RARE_SYMBOLS.CLOSING_TAG}`, "ig");
701
+ let match;
702
+ const offsets = [];
703
+ while ((match = regex.exec(content)) !== null) {
704
+ const offset = match.index;
705
+ const length = match[0].length;
706
+ offsets.push({
707
+ start: offset,
708
+ length
709
+ });
710
+ const multi = (match[1] ?? "").split(/\D/g).map((s) => s.trim()).filter(Boolean).map((s) => parseInt(s, 10)).filter((s) => !isNaN(s) && s >= 0);
711
+ for (const citationId of multi) {
712
+ const citation = this._citations.get(citationId);
713
+ if (citation) citations.push({
714
+ ...citation,
715
+ offset
716
+ });
717
+ else citations.push({
718
+ ...notFoundCitation,
719
+ offset
720
+ });
721
+ }
722
+ }
723
+ const entries = offsets.map((o) => ({
724
+ start: o.start,
725
+ length: o.length,
726
+ citations: citations.filter((x) => x.offset === o.start && x.id !== -1)
727
+ })).sort((a, b) => a.start - b.start);
728
+ let result = "";
729
+ let cursor = 0;
730
+ for (const { start, length, citations } of entries) {
731
+ result += content.slice(cursor, start);
732
+ const replacement = citations.map((citation) => replace ? replace(citation) : "").join("");
733
+ result += replacement;
734
+ cursor = start + length;
735
+ }
736
+ result += content.slice(cursor);
737
+ return {
738
+ cleaned: result,
739
+ citations
740
+ };
741
+ }
742
+ /**
743
+ * Strips citation tags from the provided content.
744
+ * @param content The string content containing citation tags.
745
+ * @returns The cleaned content without citation tags.
746
+ */
747
+ static stripCitationTags(content) {
748
+ const regex = new RegExp(`${RARE_SYMBOLS.OPENING_TAG}([\\d|\\w|\\s|,]{0,})${RARE_SYMBOLS.CLOSING_TAG}?`, "g");
749
+ return content.replace(regex, "");
750
+ }
751
+ /**
752
+ * Removes citations from a deeply nested plain object and returns a new object with citations removed.
753
+ * @param obj The plain object to process.
754
+ * @returns A tuple containing the new object and an array of extracted citations with paths.
755
+ */
756
+ removeCitationsFromObject(obj) {
757
+ const result = [];
758
+ const processObject = (current, path) => {
759
+ if (typeof current === "string") {
760
+ const extraction = this.extractCitations(current);
761
+ if (extraction.citations.length > 0) result.push(...extraction.citations.map((citation) => ({
762
+ path,
763
+ citation
764
+ })));
765
+ return extraction.cleaned;
766
+ } else if (typeof current === "object" && current !== null) {
767
+ const newObject = Array.isArray(current) ? [] : {};
768
+ for (const key of Object.keys(current)) newObject[key] = processObject(current[key], `${path}.${key}`);
769
+ return newObject;
770
+ }
771
+ return current;
772
+ };
773
+ return [processObject(obj, "root"), result];
774
+ }
775
+ /**
776
+ * Re-adds citations to the cleaned content based on their offsets.
777
+ * @param cleaned The cleaned string without citation tags.
778
+ * @param citations The array of citations with offsets.
779
+ * @returns The string with citation tags re-added.
780
+ */
781
+ reAddCitations(cleaned, citations) {
782
+ let content = cleaned;
783
+ citations.sort((a, b) => (a.offset ?? 0) - (b.offset ?? 0));
784
+ const adjustment = 0;
785
+ for (const citation of citations) if (citation.offset != null) {
786
+ const position = citation.offset + adjustment;
787
+ content = content.slice(0, position) + citation.tag + content.slice(position);
788
+ }
789
+ return content;
790
+ }
732
791
  };
733
-
734
- // src/component.default.ts
735
- import { z as z2 } from "@bpinternal/zui";
736
- var Button = new Component({
737
- type: "leaf",
738
- description: "A button component that can perform actions when clicked",
739
- name: "Button",
740
- aliases: ["btn"],
741
- generation: {
742
- usage: "Attach quick actions to a message, e.g. postback choices or a URL to open.",
743
- examples: [{ props: { action: "postback", label: "Buy", value: "buy_product" } }]
744
- },
745
- leaf: {
746
- props: z2.object({
747
- action: z2.enum(["say", "url", "postback"]).default("say").describe('The action to perform when the button is clicked. Can be "say", "url", or "postback"'),
748
- label: z2.string().describe("The text displayed on the button (min 1 character, max 250 characters)"),
749
- value: z2.string().optional().describe('The postback value to send when the button is clicked. Required if action is "postback"'),
750
- url: z2.string().optional().describe('The URL to open when the button is clicked. Required if action is "url"')
751
- })
752
- }
753
- });
754
- var Image = new Component({
755
- type: "leaf",
756
- name: "Image",
757
- description: "Displays an image from a URL.",
758
- aliases: [],
759
- generation: {
760
- examples: [{ props: { url: "https://example.com/photo.jpg", alt: "Example image" } }]
761
- },
762
- leaf: {
763
- props: z2.object({
764
- url: z2.string().describe("The URL of the image (must be valid)"),
765
- alt: z2.string().optional().describe("Alternative text describing the image")
766
- })
767
- }
792
+ //#endregion
793
+ //#region src/component.default.ts
794
+ const Button = new Component({
795
+ type: "leaf",
796
+ description: "A button component that can perform actions when clicked",
797
+ name: "Button",
798
+ aliases: ["btn"],
799
+ generation: {
800
+ usage: "Attach quick actions to a message, e.g. postback choices or a URL to open.",
801
+ examples: [{ props: {
802
+ action: "postback",
803
+ label: "Buy",
804
+ value: "buy_product"
805
+ } }]
806
+ },
807
+ leaf: { props: z.object({
808
+ action: z.enum([
809
+ "say",
810
+ "url",
811
+ "postback"
812
+ ]).default("say").describe("The action to perform when the button is clicked. Can be \"say\", \"url\", or \"postback\""),
813
+ label: z.string().describe("The text displayed on the button (min 1 character, max 250 characters)"),
814
+ value: z.string().optional().describe("The postback value to send when the button is clicked. Required if action is \"postback\""),
815
+ url: z.string().optional().describe("The URL to open when the button is clicked. Required if action is \"url\"")
816
+ }) }
768
817
  });
769
- var File = new Component({
770
- type: "leaf",
771
- name: "File",
772
- description: "Sends a downloadable file to the user.",
773
- aliases: [],
774
- generation: {
775
- examples: [{ props: { url: "https://example.com/report.pdf", name: "Report.pdf" } }]
776
- },
777
- leaf: {
778
- props: z2.object({
779
- url: z2.string().describe("The URL of the file (must be valid)"),
780
- name: z2.string().optional().describe("The display name of the file")
781
- })
782
- }
818
+ const Image = new Component({
819
+ type: "leaf",
820
+ name: "Image",
821
+ description: "Displays an image from a URL.",
822
+ aliases: [],
823
+ generation: { examples: [{ props: {
824
+ url: "https://example.com/photo.jpg",
825
+ alt: "Example image"
826
+ } }] },
827
+ leaf: { props: z.object({
828
+ url: z.string().describe("The URL of the image (must be valid)"),
829
+ alt: z.string().optional().describe("Alternative text describing the image")
830
+ }) }
783
831
  });
784
- var Video = new Component({
785
- type: "leaf",
786
- name: "Video",
787
- description: "Embeds a video from a URL.",
788
- aliases: [],
789
- generation: {
790
- examples: [{ props: { url: "https://example.com/intro.mp4", title: "Welcome" } }]
791
- },
792
- leaf: {
793
- props: z2.object({
794
- url: z2.string().describe("The URL of the video (must be valid)"),
795
- title: z2.string().optional().describe("Title for the video")
796
- })
797
- }
832
+ const File = new Component({
833
+ type: "leaf",
834
+ name: "File",
835
+ description: "Sends a downloadable file to the user.",
836
+ aliases: [],
837
+ generation: { examples: [{ props: {
838
+ url: "https://example.com/report.pdf",
839
+ name: "Report.pdf"
840
+ } }] },
841
+ leaf: { props: z.object({
842
+ url: z.string().describe("The URL of the file (must be valid)"),
843
+ name: z.string().optional().describe("The display name of the file")
844
+ }) }
798
845
  });
799
- var Audio = new Component({
800
- type: "leaf",
801
- name: "Audio",
802
- description: "Plays an audio clip from a URL.",
803
- aliases: [],
804
- generation: {
805
- examples: [{ props: { url: "https://example.com/audio.mp3", title: "Sample" } }]
806
- },
807
- leaf: {
808
- props: z2.object({
809
- url: z2.string().describe("The URL of the audio clip (must be valid)"),
810
- title: z2.string().optional().describe("Title for the audio clip")
811
- })
812
- }
846
+ const Video = new Component({
847
+ type: "leaf",
848
+ name: "Video",
849
+ description: "Embeds a video from a URL.",
850
+ aliases: [],
851
+ generation: { examples: [{ props: {
852
+ url: "https://example.com/intro.mp4",
853
+ title: "Welcome"
854
+ } }] },
855
+ leaf: { props: z.object({
856
+ url: z.string().describe("The URL of the video (must be valid)"),
857
+ title: z.string().optional().describe("Title for the video")
858
+ }) }
813
859
  });
814
- var Card = new Component({
815
- type: "container",
816
- name: "Card",
817
- description: "A visual card component that can include an image and buttons.",
818
- aliases: [],
819
- generation: {
820
- examples: [{ props: { title: "Product Name", subtitle: "Limited offer" }, body: "Featured product of the week." }]
821
- },
822
- container: {
823
- props: z2.object({
824
- title: z2.string().min(1).max(250).describe("Title text (1\u2013250 characters)"),
825
- subtitle: z2.string().optional().describe("Optional subtitle for the card")
826
- }),
827
- children: [
828
- {
829
- description: "Image (optional, max 1)",
830
- component: Image.definition
831
- },
832
- {
833
- description: "Button (optional, up to 5)",
834
- component: Button.definition
835
- }
836
- ]
837
- }
860
+ const Audio = new Component({
861
+ type: "leaf",
862
+ name: "Audio",
863
+ description: "Plays an audio clip from a URL.",
864
+ aliases: [],
865
+ generation: { examples: [{ props: {
866
+ url: "https://example.com/audio.mp3",
867
+ title: "Sample"
868
+ } }] },
869
+ leaf: { props: z.object({
870
+ url: z.string().describe("The URL of the audio clip (must be valid)"),
871
+ title: z.string().optional().describe("Title for the audio clip")
872
+ }) }
838
873
  });
839
- var Carousel = new Component({
840
- type: "container",
841
- name: "Carousel",
842
- description: "A virtual container for displaying 1 to 10 Card components as a carousel.",
843
- aliases: [],
844
- container: {
845
- props: z2.object({}),
846
- children: [
847
- {
848
- description: "Card component (required, 1\u201310 allowed)",
849
- component: Card.definition
850
- }
851
- ]
852
- }
874
+ const Card = new Component({
875
+ type: "container",
876
+ name: "Card",
877
+ description: "A visual card component that can include an image and buttons.",
878
+ aliases: [],
879
+ generation: { examples: [{
880
+ props: {
881
+ title: "Product Name",
882
+ subtitle: "Limited offer"
883
+ },
884
+ body: "Featured product of the week."
885
+ }] },
886
+ container: {
887
+ props: z.object({
888
+ title: z.string().min(1).max(250).describe("Title text (1–250 characters)"),
889
+ subtitle: z.string().optional().describe("Optional subtitle for the card")
890
+ }),
891
+ children: [{
892
+ description: "Image (optional, max 1)",
893
+ component: Image.definition
894
+ }, {
895
+ description: "Button (optional, up to 5)",
896
+ component: Button.definition
897
+ }]
898
+ }
853
899
  });
854
- var Text = new Component({
855
- type: "default",
856
- name: "Message",
857
- aliases: ["Text", "Markdown"],
858
- description: "A Markdown-formatted text message. Long answers are written the same way: plain Markdown prose.",
859
- generation: {
860
- examples: [
861
- // A long-form example matters: models shown only short bodies have been
862
- // observed drifting into JSON-wrapping long replies ({"body": "..."})
863
- {
864
- body: `Great question! Deploying happens in three steps.
900
+ const DefaultComponents = {
901
+ Button,
902
+ Image,
903
+ File,
904
+ Video,
905
+ Audio,
906
+ Card,
907
+ Carousel: new Component({
908
+ type: "container",
909
+ name: "Carousel",
910
+ description: "A virtual container for displaying 1 to 10 Card components as a carousel.",
911
+ aliases: [],
912
+ container: {
913
+ props: z.object({}),
914
+ children: [{
915
+ description: "Card component (required, 1–10 allowed)",
916
+ component: Card.definition
917
+ }]
918
+ }
919
+ }),
920
+ Text: new Component({
921
+ type: "default",
922
+ name: "Message",
923
+ aliases: ["Text", "Markdown"],
924
+ description: "A Markdown-formatted text message. Long answers are written the same way: plain Markdown prose.",
925
+ generation: { examples: [{ body: `Great question! Deploying happens in three steps.
865
926
 
866
- First, connect your repository from the dashboard \u2014 we support GitHub and GitLab. Once connected, every push to your main branch triggers a build automatically.
927
+ First, connect your repository from the dashboard — we support GitHub and GitLab. Once connected, every push to your main branch triggers a build automatically.
867
928
 
868
929
  Then configure your build settings:
869
930
 
870
- - **Build command** \u2014 usually \`npm run build\`
871
- - **Output directory** \u2014 where the compiled assets end up
931
+ - **Build command** — usually \`npm run build\`
932
+ - **Output directory** — where the compiled assets end up
872
933
 
873
- Finally, hit **Deploy**. The first build takes a couple of minutes; subsequent ones are incremental and much faster. Want me to walk you through connecting your repository?`
874
- },
875
- { body: "**Hello**, welcome to our service!" }
876
- ]
877
- },
878
- default: {
879
- props: z2.object({}),
880
- children: []
881
- }
882
- });
883
- var Speech = new Component({
884
- type: "default",
885
- name: "Speech",
886
- aliases: ["speak", "spoken"],
887
- description: "A message spoken aloud to the user via text-to-speech.",
888
- body: {
889
- format: "text",
890
- description: 'Plain conversational prose only, written to be read aloud: no Markdown, no links or URLs, no emojis, no code, no bullet points, tables or headings. Write everything the way it should be pronounced \u2014 spell out numbers, dates, units and abbreviations (e.g. "June third at three thirty PM", not "06/03 @ 3:30pm").'
891
- },
892
- generation: {
893
- usage: "Use when the reply will be played back as audio (voice conversations).",
894
- examples: [
895
- { body: "Sure! Your order shipped this morning and should arrive on Tuesday, June third, around noon." }
896
- ]
897
- },
898
- default: {
899
- props: z2.object({}),
900
- children: []
901
- }
902
- });
903
- var DefaultComponents = {
904
- Button,
905
- Image,
906
- File,
907
- Video,
908
- Audio,
909
- Card,
910
- Carousel,
911
- Text,
912
- Speech
934
+ Finally, hit **Deploy**. The first build takes a couple of minutes; subsequent ones are incremental and much faster. Want me to walk you through connecting your repository?` }, { body: "**Hello**, welcome to our service!" }] },
935
+ default: {
936
+ props: z.object({}),
937
+ children: []
938
+ }
939
+ }),
940
+ Speech: new Component({
941
+ type: "default",
942
+ name: "Speech",
943
+ aliases: ["speak", "spoken"],
944
+ description: "A message spoken aloud to the user via text-to-speech.",
945
+ body: {
946
+ format: "text",
947
+ description: "Plain conversational prose only, written to be read aloud: no Markdown, no links or URLs, no emojis, no code, no bullet points, tables or headings. Write everything the way it should be pronounced — spell out numbers, dates, units and abbreviations (e.g. \"June third at three thirty PM\", not \"06/03 @ 3:30pm\")."
948
+ },
949
+ generation: {
950
+ usage: "Use when the reply will be played back as audio (voice conversations).",
951
+ examples: [{ body: "Sure! Your order shipped this morning and should arrive on Tuesday, June third, around noon." }]
952
+ },
953
+ default: {
954
+ props: z.object({}),
955
+ children: []
956
+ }
957
+ })
913
958
  };
914
-
915
- // src/chat.ts
959
+ //#endregion
960
+ //#region src/chat.ts
961
+ /**
962
+ * Base class for implementing chat interfaces in LLMz agents.
963
+ *
964
+ * The Chat class provides the foundation for interactive conversational agents by defining
965
+ * how agents communicate with users. It manages the conversation transcript, available UI
966
+ * components, and message handling. This is the bridge between the agent's generated code
967
+ * and your user interface.
968
+ *
969
+ * ## Key Concepts
970
+ *
971
+ * **Handler Function**: The core method that receives agent messages (rendered components)
972
+ * and displays them to the user. This is called every time the agent wants to communicate.
973
+ *
974
+ * **Transcript**: An up-to-date conversation history that is evaluated at each iteration.
975
+ * The transcript must reflect the complete conversation state as the agent uses it to
976
+ * understand context and generate appropriate responses.
977
+ *
978
+ * **Components**: The UI elements available to the agent for creating rich interactive
979
+ * experiences (text, buttons, images, cards, etc.).
980
+ *
981
+ * ## Implementation Requirements
982
+ *
983
+ * To create a functional chat interface, you must:
984
+ * 1. Implement the MessageHandler to display agent messages to users
985
+ * 2. Maintain an accurate transcript of the conversation
986
+ * 3. Provide the available UI components (typically includes DefaultComponents)
987
+ * 4. Optional: Override onExecutionDone() to handle execution completion
988
+ *
989
+ * ## Basic Implementation
990
+ *
991
+ * ```typescript
992
+ * import { Chat, DefaultComponents, isComponent } from 'llmz'
993
+ *
994
+ * class MyChat extends Chat {
995
+ * private messages: Array<{role: 'user' | 'assistant', content: string}> = []
996
+ *
997
+ * constructor() {
998
+ * super({
999
+ * // Handle agent messages - this is called for every agent output
1000
+ * handler: async (input) => {
1001
+ * if (isComponent(input, DefaultComponents.Text)) {
1002
+ * const text = input.children.join('')
1003
+ * console.log('Agent:', text)
1004
+ * this.messages.push({ role: 'assistant', content: text })
1005
+ * } else if (isComponent(input, DefaultComponents.Button)) {
1006
+ * console.log('Button:', input.props.label)
1007
+ * }
1008
+ * },
1009
+ *
1010
+ * // Provide available components - agent can use these in generated code
1011
+ * components: [DefaultComponents.Text, DefaultComponents.Button],
1012
+ *
1013
+ * // Return current transcript - evaluated at each iteration
1014
+ * transcript: () => this.messages,
1015
+ * })
1016
+ * }
1017
+ *
1018
+ * // Add user message to transcript
1019
+ * addUserMessage(content: string) {
1020
+ * this.messages.push({ role: 'user', content })
1021
+ * }
1022
+ * }
1023
+ *
1024
+ * // Usage
1025
+ * const chat = new MyChat()
1026
+ * chat.addUserMessage('Hello!')
1027
+ *
1028
+ * const result = await execute({
1029
+ * instructions: 'You are a helpful assistant',
1030
+ * chat, // Enable chat mode
1031
+ * client,
1032
+ * })
1033
+ * ```
1034
+ *
1035
+ * ## Advanced Implementation (CLIChat Example)
1036
+ *
1037
+ * ```typescript
1038
+ * import { Chat, DefaultComponents, ListenExit } from 'llmz'
1039
+ *
1040
+ * class CLIChat extends Chat {
1041
+ * public transcript: Array<{role: 'user' | 'assistant', content: string}> = []
1042
+ * private buttons: string[] = []
1043
+ * public result?: ExecutionResult
1044
+ *
1045
+ * constructor() {
1046
+ * super({
1047
+ * // Dynamic component list with custom renderers
1048
+ * components: () => [
1049
+ * DefaultComponents.Text,
1050
+ * DefaultComponents.Button,
1051
+ * ...this.customComponents
1052
+ * ],
1053
+ *
1054
+ * // Dynamic transcript access
1055
+ * transcript: () => this.transcript,
1056
+ *
1057
+ * // Sophisticated message handling
1058
+ * handler: async (input) => {
1059
+ * if (isComponent(input, DefaultComponents.Text)) {
1060
+ * const text = input.children.join('')
1061
+ * console.log('🤖 Agent:', text)
1062
+ * this.transcript.push({ role: 'assistant', content: text })
1063
+ * } else if (isComponent(input, DefaultComponents.Button)) {
1064
+ * this.buttons.push(input.props.label)
1065
+ * }
1066
+ * },
1067
+ * })
1068
+ * }
1069
+ *
1070
+ * // Handle execution completion
1071
+ * onExecutionDone(result: ExecutionResult) {
1072
+ * this.result = result
1073
+ * }
1074
+ *
1075
+ * // Check if agent is waiting for user input
1076
+ * isWaitingForInput(): boolean {
1077
+ * return this.result?.is(ListenExit) ?? false
1078
+ * }
1079
+ *
1080
+ * // Conversation loop
1081
+ * async iterate(): Promise<boolean> {
1082
+ * if (this.isWaitingForInput()) {
1083
+ * const userInput = await this.promptUser()
1084
+ * this.transcript.push({ role: 'user', content: userInput })
1085
+ * return true // Continue conversation
1086
+ * }
1087
+ * return false // End conversation
1088
+ * }
1089
+ * }
1090
+ * ```
1091
+ *
1092
+ * ## Component System
1093
+ *
1094
+ * The agent generates JSX code using available components:
1095
+ *
1096
+ * ```typescript
1097
+ * // Agent generates this TSX code:
1098
+ * yield <Text>Welcome! Choose an option:</Text>
1099
+ * yield <Button action="postback" label="Get Help" value="help" />
1100
+ * yield <Button action="postback" label="Contact Sales" value="sales" />
1101
+ *
1102
+ * // Your handler receives these as RenderedComponent objects
1103
+ * ```
1104
+ *
1105
+ * ## Transcript Management
1106
+ *
1107
+ * The transcript is critical for agent context and must be kept up-to-date:
1108
+ *
1109
+ * ```typescript
1110
+ * // ❌ Bad - static transcript
1111
+ * transcript: [{ role: 'user', content: 'Hello' }]
1112
+ *
1113
+ * // ✅ Good - dynamic transcript that reflects current state
1114
+ * transcript: () => this.messages
1115
+ *
1116
+ * // The transcript is evaluated at each iteration, so the agent
1117
+ * // always sees the current conversation state
1118
+ * ```
1119
+ *
1120
+ * ## Custom Components
1121
+ *
1122
+ * Extend the UI with custom components for your specific use case:
1123
+ *
1124
+ * ```typescript
1125
+ * import { Component } from 'llmz'
1126
+ *
1127
+ * const ProductCard = new Component({
1128
+ * type: 'leaf',
1129
+ * name: 'ProductCard',
1130
+ * leaf: {
1131
+ * props: z.object({
1132
+ * name: z.string(),
1133
+ * price: z.number(),
1134
+ * imageUrl: z.string(),
1135
+ * })
1136
+ * }
1137
+ * })
1138
+ *
1139
+ * class ShoppingChat extends Chat {
1140
+ * constructor() {
1141
+ * super({
1142
+ * components: [
1143
+ * ...DefaultComponents,
1144
+ * ProductCard, // Add custom component
1145
+ * ],
1146
+ * handler: async (input) => {
1147
+ * if (isComponent(input, ProductCard)) {
1148
+ * // Handle custom component rendering
1149
+ * this.renderProduct(input.props)
1150
+ * }
1151
+ * // ... handle other components
1152
+ * },
1153
+ * })
1154
+ * }
1155
+ * }
1156
+ * ```
1157
+ *
1158
+ * @see {@link DefaultComponents} For standard UI components
1159
+ * @see {@link https://github.com/botpress/botpress/blob/master/packages/llmz/examples/utils/cli-chat.ts} Complete CLIChat implementation
1160
+ */
916
1161
  var Chat = class {
917
- handler;
918
- onMessageDelta;
919
- transcript;
920
- components;
921
- /**
922
- * Creates a new Chat instance.
923
- *
924
- * @param props - Chat configuration
925
- * @param props.handler - Function to handle agent messages (called for every agent output)
926
- * @param props.components - Available UI components (static array or dynamic function)
927
- * @param props.transcript - Conversation history (static array or dynamic function, defaults to empty)
928
- *
929
- * @example
930
- * ```typescript
931
- * // Basic chat with static configuration
932
- * const chat = new Chat({
933
- * handler: async (input) => {
934
- * if (isComponent(input, DefaultComponents.Text)) {
935
- * console.log('Agent:', input.children.join(''))
936
- * }
937
- * },
938
- * components: [DefaultComponents.Text, DefaultComponents.Button],
939
- * transcript: [
940
- * { role: 'user', content: 'Hello', timestamp: Date.now() }
941
- * ],
942
- * })
943
- * ```
944
- *
945
- * @example
946
- * ```typescript
947
- * // Dynamic chat with functions for real-time updates
948
- * class MyChat extends Chat {
949
- * private messages: Transcript.Message[] = []
950
- *
951
- * constructor() {
952
- * super({
953
- * handler: (input) => this.handleMessage(input),
954
- *
955
- * // Dynamic components - can change during execution
956
- * components: () => [
957
- * DefaultComponents.Text,
958
- * DefaultComponents.Button,
959
- * ...this.getCustomComponents()
960
- * ],
961
- *
962
- * // Dynamic transcript - always reflects current state
963
- * transcript: () => this.messages,
964
- * })
965
- * }
966
- * }
967
- * ```
968
- */
969
- constructor(props) {
970
- this.handler = props.handler;
971
- this.onMessageDelta = props.onMessageDelta;
972
- this.components = props.components;
973
- this.transcript = props.transcript || [];
974
- }
975
- /**
976
- * Called when an execution cycle completes, regardless of the outcome.
977
- *
978
- * Override this method to handle execution results, manage conversation flow,
979
- * or perform cleanup tasks. This is called after each `execute()` call completes,
980
- * whether it succeeds, fails, or is interrupted.
981
- *
982
- * @param result - The execution result containing status, iterations, and exit information
983
- *
984
- * @example
985
- * ```typescript
986
- * class MyChat extends Chat {
987
- * public result?: ExecutionResult
988
- *
989
- * onExecutionDone(result: ExecutionResult) {
990
- * // Store result for conversation flow control
991
- * this.result = result
992
- *
993
- * // Handle different result types
994
- * if (result.isSuccess()) {
995
- * console.log('✅ Execution completed successfully')
996
- * console.log('Exit:', result.output.exit_name)
997
- * } else if (result.isError()) {
998
- * console.error('❌ Execution failed:', result.output.error)
999
- * } else if (result.isInterrupted()) {
1000
- * console.log('⏸️ Execution interrupted (partial result)')
1001
- * }
1002
- * }
1003
- *
1004
- * // Use stored result for conversation flow
1005
- * isWaitingForInput(): boolean {
1006
- * return this.result?.is(ListenExit) ?? false
1007
- * }
1008
- * }
1009
- * ```
1010
- *
1011
- * @example
1012
- * ```typescript
1013
- * // CLIChat implementation example
1014
- * class CLIChat extends Chat {
1015
- * public status?: IterationStatus
1016
- * public result?: ExecutionResult
1017
- *
1018
- * onExecutionDone(result: ExecutionResult) {
1019
- * this.result = result
1020
- * this.status = result.iterations.at(-1)?.status
1021
- * }
1022
- *
1023
- * // Check if agent exited with specific exit type
1024
- * hasExitedWith<R>(exit: Exit<R>): boolean {
1025
- * return this.status?.type === 'exit_success' &&
1026
- * this.status.exit_success.exit_name === exit.name
1027
- * }
1028
- * }
1029
- * ```
1030
- */
1031
- onExecutionDone(_result) {
1032
- }
1162
+ handler;
1163
+ onMessageDelta;
1164
+ transcript;
1165
+ components;
1166
+ /**
1167
+ * Creates a new Chat instance.
1168
+ *
1169
+ * @param props - Chat configuration
1170
+ * @param props.handler - Function to handle agent messages (called for every agent output)
1171
+ * @param props.components - Available UI components (static array or dynamic function)
1172
+ * @param props.transcript - Conversation history (static array or dynamic function, defaults to empty)
1173
+ *
1174
+ * @example
1175
+ * ```typescript
1176
+ * // Basic chat with static configuration
1177
+ * const chat = new Chat({
1178
+ * handler: async (input) => {
1179
+ * if (isComponent(input, DefaultComponents.Text)) {
1180
+ * console.log('Agent:', input.children.join(''))
1181
+ * }
1182
+ * },
1183
+ * components: [DefaultComponents.Text, DefaultComponents.Button],
1184
+ * transcript: [
1185
+ * { role: 'user', content: 'Hello', timestamp: Date.now() }
1186
+ * ],
1187
+ * })
1188
+ * ```
1189
+ *
1190
+ * @example
1191
+ * ```typescript
1192
+ * // Dynamic chat with functions for real-time updates
1193
+ * class MyChat extends Chat {
1194
+ * private messages: Transcript.Message[] = []
1195
+ *
1196
+ * constructor() {
1197
+ * super({
1198
+ * handler: (input) => this.handleMessage(input),
1199
+ *
1200
+ * // Dynamic components - can change during execution
1201
+ * components: () => [
1202
+ * DefaultComponents.Text,
1203
+ * DefaultComponents.Button,
1204
+ * ...this.getCustomComponents()
1205
+ * ],
1206
+ *
1207
+ * // Dynamic transcript - always reflects current state
1208
+ * transcript: () => this.messages,
1209
+ * })
1210
+ * }
1211
+ * }
1212
+ * ```
1213
+ */
1214
+ constructor(props) {
1215
+ this.handler = props.handler;
1216
+ this.onMessageDelta = props.onMessageDelta;
1217
+ this.components = props.components;
1218
+ this.transcript = props.transcript || [];
1219
+ }
1220
+ /**
1221
+ * Called when an execution cycle completes, regardless of the outcome.
1222
+ *
1223
+ * Override this method to handle execution results, manage conversation flow,
1224
+ * or perform cleanup tasks. This is called after each `execute()` call completes,
1225
+ * whether it succeeds, fails, or is interrupted.
1226
+ *
1227
+ * @param result - The execution result containing status, iterations, and exit information
1228
+ *
1229
+ * @example
1230
+ * ```typescript
1231
+ * class MyChat extends Chat {
1232
+ * public result?: ExecutionResult
1233
+ *
1234
+ * onExecutionDone(result: ExecutionResult) {
1235
+ * // Store result for conversation flow control
1236
+ * this.result = result
1237
+ *
1238
+ * // Handle different result types
1239
+ * if (result.isSuccess()) {
1240
+ * console.log('✅ Execution completed successfully')
1241
+ * console.log('Exit:', result.output.exit_name)
1242
+ * } else if (result.isError()) {
1243
+ * console.error('❌ Execution failed:', result.output.error)
1244
+ * } else if (result.isInterrupted()) {
1245
+ * console.log('⏸️ Execution interrupted (partial result)')
1246
+ * }
1247
+ * }
1248
+ *
1249
+ * // Use stored result for conversation flow
1250
+ * isWaitingForInput(): boolean {
1251
+ * return this.result?.is(ListenExit) ?? false
1252
+ * }
1253
+ * }
1254
+ * ```
1255
+ *
1256
+ * @example
1257
+ * ```typescript
1258
+ * // CLIChat implementation example
1259
+ * class CLIChat extends Chat {
1260
+ * public status?: IterationStatus
1261
+ * public result?: ExecutionResult
1262
+ *
1263
+ * onExecutionDone(result: ExecutionResult) {
1264
+ * this.result = result
1265
+ * this.status = result.iterations.at(-1)?.status
1266
+ * }
1267
+ *
1268
+ * // Check if agent exited with specific exit type
1269
+ * hasExitedWith<R>(exit: Exit<R>): boolean {
1270
+ * return this.status?.type === 'exit_success' &&
1271
+ * this.status.exit_success.exit_name === exit.name
1272
+ * }
1273
+ * }
1274
+ * ```
1275
+ */
1276
+ onExecutionDone(_result) {}
1033
1277
  };
1034
-
1035
- // src/message-stream/replayable.ts
1278
+ //#endregion
1279
+ //#region src/message-stream/replayable.ts
1280
+ /**
1281
+ * Wraps an async iterable so it can be iterated multiple times: chunks pulled
1282
+ * from the source are cached, and every iteration replays the cache before
1283
+ * pulling new chunks. Concurrent iterations are safe — pulls on the underlying
1284
+ * source are serialized. If the source fails, the failure is cached too:
1285
+ * every iteration replays the chunks received before the failure, then
1286
+ * rethrows the source error.
1287
+ */
1036
1288
  var ReplayableAsyncIterable = class {
1037
- _source;
1038
- _chunks = [];
1039
- _done = false;
1040
- _failed = false;
1041
- _error;
1042
- _lock = Promise.resolve();
1043
- constructor(source) {
1044
- this._source = source[Symbol.asyncIterator]();
1045
- }
1046
- /** True once the underlying source has been fully consumed (or has failed). */
1047
- get done() {
1048
- return this._done;
1049
- }
1050
- /** The chunks pulled from the source so far. */
1051
- get chunks() {
1052
- return [...this._chunks];
1053
- }
1054
- async *[Symbol.asyncIterator]() {
1055
- let index = 0;
1056
- while (true) {
1057
- if (index < this._chunks.length) {
1058
- yield this._chunks[index++];
1059
- } else if (this._failed) {
1060
- throw this._error;
1061
- } else if (this._done) {
1062
- return;
1063
- } else {
1064
- await this._pull();
1065
- }
1066
- }
1067
- }
1068
- /** Consumes the entire source and returns all chunks. */
1069
- async collect() {
1070
- for await (const _ of this) {
1071
- }
1072
- return this.chunks;
1073
- }
1074
- _pull() {
1075
- const next = this._lock.then(async () => {
1076
- if (this._done) {
1077
- return;
1078
- }
1079
- let result;
1080
- try {
1081
- result = await this._source.next();
1082
- } catch (thrown) {
1083
- this._done = true;
1084
- this._failed = true;
1085
- this._error = thrown;
1086
- throw thrown;
1087
- }
1088
- if (result.done) {
1089
- this._done = true;
1090
- } else {
1091
- this._chunks.push(result.value);
1092
- }
1093
- });
1094
- this._lock = next.catch(() => {
1095
- });
1096
- return next;
1097
- }
1289
+ _source;
1290
+ _chunks = [];
1291
+ _done = false;
1292
+ _failed = false;
1293
+ _error;
1294
+ _lock = Promise.resolve();
1295
+ constructor(source) {
1296
+ this._source = source[Symbol.asyncIterator]();
1297
+ }
1298
+ /** True once the underlying source has been fully consumed (or has failed). */
1299
+ get done() {
1300
+ return this._done;
1301
+ }
1302
+ /** The chunks pulled from the source so far. */
1303
+ get chunks() {
1304
+ return [...this._chunks];
1305
+ }
1306
+ async *[Symbol.asyncIterator]() {
1307
+ let index = 0;
1308
+ while (true) if (index < this._chunks.length) yield this._chunks[index++];
1309
+ else if (this._failed) throw this._error;
1310
+ else if (this._done) return;
1311
+ else await this._pull();
1312
+ }
1313
+ /** Consumes the entire source and returns all chunks. */
1314
+ async collect() {
1315
+ for await (const _ of this);
1316
+ return this.chunks;
1317
+ }
1318
+ _pull() {
1319
+ const next = this._lock.then(async () => {
1320
+ if (this._done) return;
1321
+ let result;
1322
+ try {
1323
+ result = await this._source.next();
1324
+ } catch (thrown) {
1325
+ this._done = true;
1326
+ this._failed = true;
1327
+ this._error = thrown;
1328
+ throw thrown;
1329
+ }
1330
+ if (result.done) this._done = true;
1331
+ else this._chunks.push(result.value);
1332
+ });
1333
+ this._lock = next.catch(() => {});
1334
+ return next;
1335
+ }
1098
1336
  };
1099
- var collectText = async (iterable) => {
1100
- let text = "";
1101
- for await (const chunk of iterable) {
1102
- text += chunk;
1103
- }
1104
- return text;
1337
+ /** Drains a string iterable into a single string. */
1338
+ const collectText = async (iterable) => {
1339
+ let text = "";
1340
+ for await (const chunk of iterable) text += chunk;
1341
+ return text;
1105
1342
  };
1106
-
1107
- // src/message-stream/stream.ts
1343
+ //#endregion
1344
+ //#region src/message-stream/stream.ts
1108
1345
  var BaseResponse = class {
1109
- constructor(action, item) {
1110
- this.action = action;
1111
- this.item = item;
1112
- }
1113
- get diagnostics() {
1114
- return this.item.diagnostics;
1115
- }
1346
+ action;
1347
+ item;
1348
+ constructor(action, item) {
1349
+ this.action = action;
1350
+ this.item = item;
1351
+ }
1352
+ get diagnostics() {
1353
+ return this.item.diagnostics;
1354
+ }
1116
1355
  };
1356
+ /** `■send=<component> { props? }` followed by an optional streaming body. */
1117
1357
  var SendResponse = class extends BaseResponse {
1118
- constructor(item, content) {
1119
- super("send", item);
1120
- this.content = content;
1121
- }
1122
- get component() {
1123
- return this.item.name;
1124
- }
1125
- get props() {
1126
- return this.item.props;
1127
- }
1128
- static formatToText(component, props, body) {
1129
- const header = `${MARKER}send=${component}${props && Object.keys(props).length ? ` ${JSON.stringify(props)}` : ""}`;
1130
- return body ? `${header}
1131
- ${body}` : header;
1132
- }
1358
+ content;
1359
+ constructor(item, content) {
1360
+ super("send", item);
1361
+ this.content = content;
1362
+ }
1363
+ get component() {
1364
+ return this.item.name;
1365
+ }
1366
+ get props() {
1367
+ return this.item.props;
1368
+ }
1369
+ static formatToText(component, props, body) {
1370
+ const header = `■send=${component}${props && Object.keys(props).length ? ` ${JSON.stringify(props)}` : ""}`;
1371
+ return body ? `${header}\n${body}` : header;
1372
+ }
1133
1373
  };
1374
+ /** `■run` followed by streaming code. */
1134
1375
  var RunResponse = class extends BaseResponse {
1135
- constructor(item, code) {
1136
- super("run", item);
1137
- this.code = code;
1138
- }
1139
- static formatToText(code) {
1140
- return `${MARKER}run
1141
- ${code}`;
1142
- }
1376
+ code;
1377
+ constructor(item, code) {
1378
+ super("run", item);
1379
+ this.code = code;
1380
+ }
1381
+ static formatToText(code) {
1382
+ return `■run\n${code}`;
1383
+ }
1143
1384
  };
1385
+ /** `■next=<name> { props? }` — hands control to an exit (e.g. listen) with optional props. */
1144
1386
  var NextResponse = class extends BaseResponse {
1145
- constructor(item) {
1146
- super("next", item);
1147
- }
1148
- get name() {
1149
- return this.item.name;
1150
- }
1151
- get props() {
1152
- return this.item.props;
1153
- }
1154
- static formatToText(name, props) {
1155
- return `${MARKER}next=${name}${props && Object.keys(props).length ? ` ${JSON.stringify(props)}` : ""}`;
1156
- }
1387
+ constructor(item) {
1388
+ super("next", item);
1389
+ }
1390
+ get name() {
1391
+ return this.item.name;
1392
+ }
1393
+ get props() {
1394
+ return this.item.props;
1395
+ }
1396
+ static formatToText(name, props) {
1397
+ return `■next=${name}${props && Object.keys(props).length ? ` ${JSON.stringify(props)}` : ""}`;
1398
+ }
1157
1399
  };
1400
+ /**
1401
+ * Pulls transport chunks through the parser on demand and hands out events one
1402
+ * at a time. Shared by the main response loop and by body streams; pulls are
1403
+ * serialized so interleaved consumers cannot race the source iterator.
1404
+ */
1158
1405
  var EventPump = class {
1159
- _iterator;
1160
- _parser;
1161
- _queue = [];
1162
- _sourceDone = false;
1163
- _lock = Promise.resolve();
1164
- constructor(stream, parser) {
1165
- this._iterator = stream[Symbol.asyncIterator]();
1166
- this._parser = parser;
1167
- }
1168
- next() {
1169
- const next = this._lock.then(() => this._next());
1170
- this._lock = next.catch(() => {
1171
- });
1172
- return next;
1173
- }
1174
- unshift(event) {
1175
- this._queue.unshift(event);
1176
- }
1177
- async _next() {
1178
- while (this._queue.length === 0) {
1179
- if (this._sourceDone) {
1180
- return void 0;
1181
- }
1182
- let result;
1183
- try {
1184
- result = await this._iterator.next();
1185
- } catch {
1186
- this._sourceDone = true;
1187
- this._queue.push(...this._parser.finish("interrupted"));
1188
- continue;
1189
- }
1190
- if (result.done) {
1191
- this._sourceDone = true;
1192
- this._queue.push(...this._parser.finish());
1193
- } else {
1194
- this._queue.push(...this._parser.push(result.value));
1195
- }
1196
- }
1197
- return this._queue.shift();
1198
- }
1406
+ _iterator;
1407
+ _parser;
1408
+ _queue = [];
1409
+ _sourceDone = false;
1410
+ _lock = Promise.resolve();
1411
+ constructor(stream, parser) {
1412
+ this._iterator = stream[Symbol.asyncIterator]();
1413
+ this._parser = parser;
1414
+ }
1415
+ next() {
1416
+ const next = this._lock.then(() => this._next());
1417
+ this._lock = next.catch(() => {});
1418
+ return next;
1419
+ }
1420
+ unshift(event) {
1421
+ this._queue.unshift(event);
1422
+ }
1423
+ async _next() {
1424
+ while (this._queue.length === 0) {
1425
+ if (this._sourceDone) return;
1426
+ let result;
1427
+ try {
1428
+ result = await this._iterator.next();
1429
+ } catch {
1430
+ this._sourceDone = true;
1431
+ this._queue.push(...this._parser.finish("interrupted"));
1432
+ continue;
1433
+ }
1434
+ if (result.done) {
1435
+ this._sourceDone = true;
1436
+ this._queue.push(...this._parser.finish());
1437
+ } else this._queue.push(...this._parser.push(result.value));
1438
+ }
1439
+ return this._queue.shift();
1440
+ }
1199
1441
  };
1442
+ /**
1443
+ * Parses a raw LLM text stream into typed responses.
1444
+ *
1445
+ * `send` and `run` responses are yielded as soon as their header is parsed —
1446
+ * while their body is still streaming — with the body exposed as a replayable
1447
+ * async iterable. `next` responses are yielded once complete.
1448
+ *
1449
+ * The body streams are pull-based: consuming them drives the underlying
1450
+ * transport. Requesting the next response first drains the previous body into
1451
+ * its replay cache, so it can always be read later.
1452
+ */
1200
1453
  function parseStream(stream, options) {
1201
- return parseResponses(stream, options);
1454
+ return parseResponses(stream, options);
1202
1455
  }
1456
+ /** Parses a complete text (convenience wrapper over {@link parseStream}). */
1203
1457
  function parseText(text, options) {
1204
- return parseResponses(
1205
- async function* () {
1206
- yield text;
1207
- }(),
1208
- options
1209
- );
1458
+ return parseResponses((async function* () {
1459
+ yield text;
1460
+ })(), options);
1210
1461
  }
1211
1462
  async function* parseResponses(stream, options) {
1212
- const parser = new StreamingMessageParser(options);
1213
- const pump = new EventPump(stream, parser);
1214
- let activeBody;
1215
- while (true) {
1216
- if (activeBody) {
1217
- await activeBody.collect();
1218
- activeBody = void 0;
1219
- }
1220
- const event = await pump.next();
1221
- if (!event) {
1222
- return;
1223
- }
1224
- if (event.type === "item-ready" && (event.item.kind === "send" || event.item.kind === "run")) {
1225
- const body = new ReplayableAsyncIterable(bodyStream(pump, event.item.id));
1226
- activeBody = body;
1227
- yield event.item.kind === "send" ? new SendResponse(event.item, body) : new RunResponse(event.item, body);
1228
- continue;
1229
- }
1230
- if (event.type === "item-complete" && event.item.kind === "next") {
1231
- yield new NextResponse(event.item);
1232
- continue;
1233
- }
1234
- }
1463
+ const pump = new EventPump(stream, new StreamingMessageParser(options));
1464
+ let activeBody;
1465
+ while (true) {
1466
+ if (activeBody) {
1467
+ await activeBody.collect();
1468
+ activeBody = void 0;
1469
+ }
1470
+ const event = await pump.next();
1471
+ if (!event) return;
1472
+ if (event.type === "item-ready" && (event.item.kind === "send" || event.item.kind === "run")) {
1473
+ const body = new ReplayableAsyncIterable(bodyStream(pump, event.item.id));
1474
+ activeBody = body;
1475
+ yield event.item.kind === "send" ? new SendResponse(event.item, body) : new RunResponse(event.item, body);
1476
+ continue;
1477
+ }
1478
+ if (event.type === "item-complete" && event.item.kind === "next") {
1479
+ yield new NextResponse(event.item);
1480
+ continue;
1481
+ }
1482
+ }
1235
1483
  }
1236
1484
  async function* bodyStream(pump, itemId) {
1237
- while (true) {
1238
- const event = await pump.next();
1239
- if (!event) {
1240
- return;
1241
- }
1242
- if (event.type === "body-delta" && event.itemId === itemId) {
1243
- yield event.delta;
1244
- continue;
1245
- }
1246
- if (event.type === "body-start" && event.itemId === itemId) {
1247
- continue;
1248
- }
1249
- if (event.type === "diagnostic") {
1250
- continue;
1251
- }
1252
- if (event.type === "item-complete" && event.item.id === itemId) {
1253
- return;
1254
- }
1255
- pump.unshift(event);
1256
- return;
1257
- }
1485
+ while (true) {
1486
+ const event = await pump.next();
1487
+ if (!event) return;
1488
+ if (event.type === "body-delta" && event.itemId === itemId) {
1489
+ yield event.delta;
1490
+ continue;
1491
+ }
1492
+ if (event.type === "body-start" && event.itemId === itemId) continue;
1493
+ if (event.type === "diagnostic") continue;
1494
+ if (event.type === "item-complete" && event.item.id === itemId) return;
1495
+ pump.unshift(event);
1496
+ return;
1497
+ }
1258
1498
  }
1259
-
1260
- // src/message-stream/registry.ts
1499
+ //#endregion
1500
+ //#region src/message-stream/registry.ts
1501
+ /**
1502
+ * Stores normalized component definitions. The registry (not the parser) is the
1503
+ * single source of truth for which components exist and how they are validated.
1504
+ */
1261
1505
  var ComponentRegistry = class {
1262
- _definitions = /* @__PURE__ */ new Map();
1263
- constructor(definitions = []) {
1264
- for (const definition of definitions) {
1265
- this.register(definition);
1266
- }
1267
- }
1268
- register(definition) {
1269
- if (!NAME_REGEX.test(definition.name)) {
1270
- throw new Error(
1271
- `Invalid component name "${definition.name}". Component names must be lowercase ([a-z][a-z0-9_-]*).`
1272
- );
1273
- }
1274
- this._definitions.set(definition.name, definition);
1275
- }
1276
- unregister(name) {
1277
- this._definitions.delete(name);
1278
- }
1279
- has(name) {
1280
- return this._definitions.has(name);
1281
- }
1282
- get(name) {
1283
- return this._definitions.get(name);
1284
- }
1285
- list() {
1286
- return [...this._definitions.values()];
1287
- }
1506
+ _definitions = /* @__PURE__ */ new Map();
1507
+ constructor(definitions = []) {
1508
+ for (const definition of definitions) this.register(definition);
1509
+ }
1510
+ register(definition) {
1511
+ if (!NAME_REGEX.test(definition.name)) throw new Error(`Invalid component name "${definition.name}". Component names must be lowercase ([a-z][a-z0-9_-]*).`);
1512
+ this._definitions.set(definition.name, definition);
1513
+ }
1514
+ unregister(name) {
1515
+ this._definitions.delete(name);
1516
+ }
1517
+ has(name) {
1518
+ return this._definitions.has(name);
1519
+ }
1520
+ get(name) {
1521
+ return this._definitions.get(name);
1522
+ }
1523
+ list() {
1524
+ return [...this._definitions.values()];
1525
+ }
1288
1526
  };
1289
-
1290
- // src/message-stream/normalizer.ts
1291
- import { transforms } from "@bpinternal/zui";
1292
- var EMPTY_PROPS_SCHEMA = { type: "object", properties: {}, additionalProperties: false };
1293
- var normalizeComponentName = (name) => name.trim().toLowerCase();
1527
+ //#endregion
1528
+ //#region src/message-stream/normalizer.ts
1529
+ const EMPTY_PROPS_SCHEMA = {
1530
+ type: "object",
1531
+ properties: {},
1532
+ additionalProperties: false
1533
+ };
1534
+ const normalizeComponentName = (name) => name.trim().toLowerCase();
1294
1535
  function normalizeComponentDefinition(definition) {
1295
- const name = normalizeComponentName(definition.name ?? "");
1296
- if (!NAME_REGEX.test(name)) {
1297
- throw new Error(
1298
- `Invalid component name "${definition.name}". Component names must be lowercase ([a-z][a-z0-9_-]*).`
1299
- );
1300
- }
1301
- return {
1302
- name,
1303
- description: definition.description,
1304
- propsJsonSchema: normalizePropsSchema(definition.props, name),
1305
- body: definition.body ? {
1306
- format: definition.body.type,
1307
- description: definition.body.description,
1308
- required: definition.body.required ?? false
1309
- } : void 0,
1310
- generation: definition.generation
1311
- };
1536
+ const name = normalizeComponentName(definition.name ?? "");
1537
+ if (!NAME_REGEX.test(name)) throw new Error(`Invalid component name "${definition.name}". Component names must be lowercase ([a-z][a-z0-9_-]*).`);
1538
+ return {
1539
+ name,
1540
+ description: definition.description,
1541
+ propsJsonSchema: normalizePropsSchema(definition.props, name),
1542
+ body: definition.body ? {
1543
+ format: definition.body.type,
1544
+ description: definition.body.description,
1545
+ required: definition.body.required ?? false
1546
+ } : void 0,
1547
+ generation: definition.generation
1548
+ };
1312
1549
  }
1313
1550
  function normalizePropsSchema(props, name) {
1314
- if (props === void 0) {
1315
- return EMPTY_PROPS_SCHEMA;
1316
- }
1317
- if (isZuiSchema(props)) {
1318
- try {
1319
- return transforms.toJSONSchema(props);
1320
- } catch {
1321
- return transforms.toJSONSchemaLegacy(props);
1322
- }
1323
- }
1324
- if (isJsonSchema(props)) {
1325
- return props;
1326
- }
1327
- throw new Error(
1328
- `Invalid props schema for "${name}". Expected a ZUI/Zod object schema or a JSON schema, but got type "${typeof props}".`
1329
- );
1551
+ if (props === void 0) return EMPTY_PROPS_SCHEMA;
1552
+ if (isZuiSchema(props)) try {
1553
+ return transforms.toJSONSchema(props);
1554
+ } catch {
1555
+ return transforms.toJSONSchemaLegacy(props);
1556
+ }
1557
+ if (isJsonSchema(props)) return props;
1558
+ throw new Error(`Invalid props schema for "${name}". Expected a ZUI/Zod object schema or a JSON schema, but got type "${typeof props}".`);
1330
1559
  }
1331
-
1332
- // src/message-stream/validator.ts
1560
+ //#endregion
1561
+ //#region src/message-stream/validator.ts
1562
+ /**
1563
+ * Validates a syntactically parsed `■send` item against the registered
1564
+ * component definitions. Validation is intentionally separate from parsing: a
1565
+ * validation failure never stops the stream, it only produces errors the caller
1566
+ * can turn into diagnostics or fallback rendering.
1567
+ */
1333
1568
  function validateComponent(item, registry) {
1334
- const definition = registry.get(item.name);
1335
- if (!definition) {
1336
- const registered = registry.list().map((d) => d.name).join(", ") || "(none)";
1337
- return {
1338
- valid: false,
1339
- props: item.props,
1340
- errors: [
1341
- {
1342
- code: "unknown-component",
1343
- message: `Unknown component "${item.name}". Registered components: ${registered}`
1344
- }
1345
- ]
1346
- };
1347
- }
1348
- const errors = [
1349
- ..._validateProps(item.props, definition),
1350
- ..._validateBody(item, definition)
1351
- ];
1352
- return { valid: errors.length === 0, props: item.props, errors };
1569
+ const definition = registry.get(item.name);
1570
+ if (!definition) {
1571
+ const registered = registry.list().map((d) => d.name).join(", ") || "(none)";
1572
+ return {
1573
+ valid: false,
1574
+ props: item.props,
1575
+ errors: [{
1576
+ code: "unknown-component",
1577
+ message: `Unknown component "${item.name}". Registered components: ${registered}`
1578
+ }]
1579
+ };
1580
+ }
1581
+ const errors = [..._validateProps(item.props, definition), ..._validateBody(item, definition)];
1582
+ return {
1583
+ valid: errors.length === 0,
1584
+ props: item.props,
1585
+ errors
1586
+ };
1353
1587
  }
1588
+ /** Validates a props object against a JSON schema (also used for `■next` exit props). */
1354
1589
  function validateProps(props, schema) {
1355
- const errors = [];
1356
- _checkValue(props, schema, "", errors);
1357
- return errors;
1590
+ const errors = [];
1591
+ _checkValue(props, schema, "", errors);
1592
+ return errors;
1358
1593
  }
1359
- var _validateProps = (props, definition) => validateProps(props, definition.propsJsonSchema);
1360
- var _validateBody = (item, definition) => {
1361
- var _a;
1362
- const hasBody = typeof item.body === "string" && item.body.length > 0;
1363
- if (hasBody && !definition.body) {
1364
- return [
1365
- {
1366
- code: "body-not-allowed",
1367
- message: `Component "${definition.name}" does not support a body`
1368
- }
1369
- ];
1370
- }
1371
- if (!hasBody && ((_a = definition.body) == null ? void 0 : _a.required)) {
1372
- return [
1373
- {
1374
- code: "missing-required-body",
1375
- message: `Component "${definition.name}" requires a body`
1376
- }
1377
- ];
1378
- }
1379
- return [];
1380
- };
1381
- var _typeOf = (value) => {
1382
- if (value === null)
1383
- return "null";
1384
- if (Array.isArray(value))
1385
- return "array";
1386
- return typeof value;
1594
+ const _validateProps = (props, definition) => validateProps(props, definition.propsJsonSchema);
1595
+ const _validateBody = (item, definition) => {
1596
+ var _definition$body;
1597
+ const hasBody = typeof item.body === "string" && item.body.length > 0;
1598
+ if (hasBody && !definition.body) return [{
1599
+ code: "body-not-allowed",
1600
+ message: `Component "${definition.name}" does not support a body`
1601
+ }];
1602
+ if (!hasBody && ((_definition$body = definition.body) === null || _definition$body === void 0 ? void 0 : _definition$body.required)) return [{
1603
+ code: "missing-required-body",
1604
+ message: `Component "${definition.name}" requires a body`
1605
+ }];
1606
+ return [];
1387
1607
  };
1388
- var _matchesType = (value, type) => {
1389
- switch (type) {
1390
- case "string":
1391
- return typeof value === "string";
1392
- case "number":
1393
- return typeof value === "number";
1394
- case "integer":
1395
- return typeof value === "number" && Number.isInteger(value);
1396
- case "boolean":
1397
- return typeof value === "boolean";
1398
- case "null":
1399
- return value === null;
1400
- case "array":
1401
- return Array.isArray(value);
1402
- case "object":
1403
- return !!value && typeof value === "object" && !Array.isArray(value);
1404
- default:
1405
- return true;
1406
- }
1608
+ const _typeOf = (value) => {
1609
+ if (value === null) return "null";
1610
+ if (Array.isArray(value)) return "array";
1611
+ return typeof value;
1407
1612
  };
1408
- var _isEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
1409
- var _checkValue = (value, schema, path, errors) => {
1410
- if (schema === void 0 || typeof schema === "boolean" || typeof schema !== "object") {
1411
- return;
1412
- }
1413
- const label = path || "props";
1414
- const propError = (message) => {
1415
- errors.push({ code: "invalid-prop-value", message, path: path || void 0 });
1416
- };
1417
- if (schema.const !== void 0 && !_isEqual(value, schema.const)) {
1418
- propError(`Expected "${label}" to equal ${JSON.stringify(schema.const)}`);
1419
- return;
1420
- }
1421
- if (schema.enum && !schema.enum.some((allowed) => _isEqual(value, allowed))) {
1422
- propError(`Expected "${label}" to be one of: ${schema.enum.map((v) => JSON.stringify(v)).join(", ")}`);
1423
- return;
1424
- }
1425
- const variants = schema.anyOf ?? schema.oneOf;
1426
- if (variants == null ? void 0 : variants.length) {
1427
- const matchesAny = variants.some((variant) => {
1428
- const variantErrors = [];
1429
- _checkValue(value, variant, path, variantErrors);
1430
- return variantErrors.length === 0;
1431
- });
1432
- if (!matchesAny) {
1433
- propError(`Value of "${label}" does not match any of the allowed types`);
1434
- return;
1435
- }
1436
- }
1437
- if (schema.type) {
1438
- const types = Array.isArray(schema.type) ? schema.type : [schema.type];
1439
- if (!types.some((type) => _matchesType(value, type))) {
1440
- propError(`Expected "${label}" to be of type ${types.join(" | ")}, but got ${_typeOf(value)}`);
1441
- return;
1442
- }
1443
- }
1444
- if (Array.isArray(value) && schema.items && !Array.isArray(schema.items)) {
1445
- value.forEach((item, index) => _checkValue(item, schema.items, `${label}[${index}]`, errors));
1446
- }
1447
- if (value && typeof value === "object" && !Array.isArray(value)) {
1448
- const record = value;
1449
- const properties = schema.properties ?? {};
1450
- for (const requiredKey of schema.required ?? []) {
1451
- if (record[requiredKey] === void 0) {
1452
- errors.push({
1453
- code: "missing-required-prop",
1454
- message: `Missing required prop "${path ? `${path}.` : ""}${requiredKey}"`,
1455
- path: path ? `${path}.${requiredKey}` : requiredKey
1456
- });
1457
- }
1458
- }
1459
- for (const [key, propValue] of Object.entries(record)) {
1460
- const propSchema = properties[key];
1461
- const propPath = path ? `${path}.${key}` : key;
1462
- if (propSchema === void 0) {
1463
- if (schema.additionalProperties === false) {
1464
- errors.push({
1465
- code: "unexpected-prop",
1466
- message: `Unexpected prop "${propPath}"`,
1467
- path: propPath
1468
- });
1469
- }
1470
- continue;
1471
- }
1472
- _checkValue(propValue, propSchema, propPath, errors);
1473
- }
1474
- }
1613
+ const _matchesType = (value, type) => {
1614
+ switch (type) {
1615
+ case "string": return typeof value === "string";
1616
+ case "number": return typeof value === "number";
1617
+ case "integer": return typeof value === "number" && Number.isInteger(value);
1618
+ case "boolean": return typeof value === "boolean";
1619
+ case "null": return value === null;
1620
+ case "array": return Array.isArray(value);
1621
+ case "object": return !!value && typeof value === "object" && !Array.isArray(value);
1622
+ default: return true;
1623
+ }
1475
1624
  };
1476
-
1477
- // src/message-stream/sanitize.ts
1478
- var sanitizeMessageText = (value, options = {}) => value.replaceAll(MARKER, options.markerReplacement ?? "\u25AA");
1479
-
1480
- // src/index.ts
1481
- var utils = {
1482
- toValidObjectName,
1483
- toValidFunctionName,
1484
- wrapContent,
1485
- truncateWrappedContent,
1486
- stripTruncationTags
1625
+ const _isEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
1626
+ /**
1627
+ * Minimal JSON-schema subset checker covering what component schemas need:
1628
+ * type, enum, const, required, properties, additionalProperties, items, anyOf/oneOf.
1629
+ * Unknown keywords are ignored (lenient by design).
1630
+ */
1631
+ const _checkValue = (value, schema, path, errors) => {
1632
+ if (schema === void 0 || typeof schema === "boolean" || typeof schema !== "object") return;
1633
+ const label = path || "props";
1634
+ const propError = (message) => {
1635
+ errors.push({
1636
+ code: "invalid-prop-value",
1637
+ message,
1638
+ path: path || void 0
1639
+ });
1640
+ };
1641
+ if (schema.const !== void 0 && !_isEqual(value, schema.const)) {
1642
+ propError(`Expected "${label}" to equal ${JSON.stringify(schema.const)}`);
1643
+ return;
1644
+ }
1645
+ if (schema.enum && !schema.enum.some((allowed) => _isEqual(value, allowed))) {
1646
+ propError(`Expected "${label}" to be one of: ${schema.enum.map((v) => JSON.stringify(v)).join(", ")}`);
1647
+ return;
1648
+ }
1649
+ const variants = schema.anyOf ?? schema.oneOf;
1650
+ if (variants === null || variants === void 0 ? void 0 : variants.length) {
1651
+ if (!variants.some((variant) => {
1652
+ const variantErrors = [];
1653
+ _checkValue(value, variant, path, variantErrors);
1654
+ return variantErrors.length === 0;
1655
+ })) {
1656
+ propError(`Value of "${label}" does not match any of the allowed types`);
1657
+ return;
1658
+ }
1659
+ }
1660
+ if (schema.type) {
1661
+ const types = Array.isArray(schema.type) ? schema.type : [schema.type];
1662
+ if (!types.some((type) => _matchesType(value, type))) {
1663
+ propError(`Expected "${label}" to be of type ${types.join(" | ")}, but got ${_typeOf(value)}`);
1664
+ return;
1665
+ }
1666
+ }
1667
+ if (Array.isArray(value) && schema.items && !Array.isArray(schema.items)) value.forEach((item, index) => _checkValue(item, schema.items, `${label}[${index}]`, errors));
1668
+ if (value && typeof value === "object" && !Array.isArray(value)) {
1669
+ const record = value;
1670
+ const properties = schema.properties ?? {};
1671
+ for (const requiredKey of schema.required ?? []) if (record[requiredKey] === void 0) errors.push({
1672
+ code: "missing-required-prop",
1673
+ message: `Missing required prop "${path ? `${path}.` : ""}${requiredKey}"`,
1674
+ path: path ? `${path}.${requiredKey}` : requiredKey
1675
+ });
1676
+ for (const [key, propValue] of Object.entries(record)) {
1677
+ const propSchema = properties[key];
1678
+ const propPath = path ? `${path}.${key}` : key;
1679
+ if (propSchema === void 0) {
1680
+ if (schema.additionalProperties === false) errors.push({
1681
+ code: "unexpected-prop",
1682
+ message: `Unexpected prop "${propPath}"`,
1683
+ path: propPath
1684
+ });
1685
+ continue;
1686
+ }
1687
+ _checkValue(propValue, propSchema, propPath, errors);
1688
+ }
1689
+ }
1487
1690
  };
1488
- var execute = async (props) => {
1489
- const { executeContext } = await import("./execute-FF7D46TY.js");
1490
- return executeContext(props);
1691
+ //#endregion
1692
+ //#region src/message-stream/sanitize.ts
1693
+ /**
1694
+ * The protocol has no escaping mechanism by design. Any untrusted text that is
1695
+ * inserted into model context (user messages, tool results, documents) must be
1696
+ * sanitized so it cannot inject protocol blocks.
1697
+ */
1698
+ const sanitizeMessageText = (value, options = {}) => value.replaceAll("■", options.markerReplacement ?? "▪");
1699
+ //#endregion
1700
+ //#region src/index.ts
1701
+ const utils = {
1702
+ toValidObjectName,
1703
+ toValidFunctionName,
1704
+ wrapContent,
1705
+ truncateWrappedContent,
1706
+ stripTruncationTags
1491
1707
  };
1492
- var init = async () => {
1493
- await import("./execute-FF7D46TY.js");
1494
- await import("./component-ASNZHQUR.js");
1495
- await import("./tool-KD7FL45V.js");
1496
- await import("./exit-UQ3EUX5D.js");
1497
- await import("./jsx-DDZ63UUF.js");
1498
- await import("./vm-QO5FSGPN.js");
1499
- await import("./utils-IATCUNKF.js");
1500
- await import("./truncator-73RCL52Y.js");
1501
- await import("./typings-E2RROCLT.js");
1502
- await import("./dual-modes-S5TJ5JVJ.js");
1708
+ /**
1709
+ * Executes an LLMz agent in either Chat Mode or Worker Mode.
1710
+ *
1711
+ * LLMz is a code-first AI agent framework that generates and runs TypeScript code
1712
+ * in a sandbox rather than using traditional JSON tool calling. This enables complex
1713
+ * logic, multi-tool orchestration, and native LLM thinking via comments and code structure.
1714
+ *
1715
+ * @param props - Configuration object for the execution
1716
+ * @param props.client - Botpress Client or Cognitive Client instance for LLM generation
1717
+ * @param props.instructions - System prompt/instructions for the LLM (static string or dynamic function)
1718
+ * @param props.chat - Optional Chat instance to enable Chat Mode with user interaction
1719
+ * @param props.tools - Array of Tool instances available to the agent (static or dynamic)
1720
+ * @param props.objects - Array of ObjectInstance for namespaced tools and variables (static or dynamic)
1721
+ * @param props.exits - Array of Exit definitions for structured completion (static or dynamic)
1722
+ * @param props.snapshot - Optional Snapshot to resume paused execution
1723
+ * @param props.signal - Optional AbortSignal to cancel execution
1724
+ * @param props.model - Optional model name (or array or models to use as fallback) (static or dynamic function)
1725
+ * @param props.temperature - Optional temperature value (static or dynamic function)
1726
+ * @param props.metadata - Optional metadata attached to cognitive usage records for each LLM call
1727
+ * @param props.options - Optional execution options (loop limit, timeout, maxTokens context cap)
1728
+ * @param props.onTrace - Optional non-blocking hook for monitoring traces during execution
1729
+ * @param props.onIterationEnd - Optional blocking hook called after each iteration
1730
+ * @param props.onExit - Optional blocking hook called when an exit is reached (can prevent exit)
1731
+ * @param props.onBeforeExecution - Optional blocking hook to modify code before VM execution
1732
+ * @param props.onBeforeTool - Optional blocking hook to modify tool inputs before execution
1733
+ * @param props.onAfterTool - Optional blocking hook to modify tool outputs after execution
1734
+ *
1735
+ * @returns Promise<ExecutionResult> - Result containing success/error/interrupted status with type-safe exit checking
1736
+ *
1737
+ * @example
1738
+ * // Worker Mode - Automated execution
1739
+ * const result = await execute({
1740
+ * client: cognitiveClient,
1741
+ * instructions: 'Calculate the sum of numbers 1 to 100',
1742
+ * exits: [myExit]
1743
+ * })
1744
+ *
1745
+ * if (result.is(myExit)) {
1746
+ * console.log('Result:', result.output)
1747
+ * }
1748
+ *
1749
+ * @example
1750
+ * // Chat Mode - Interactive conversation
1751
+ * const result = await execute({
1752
+ * client: cognitiveClient,
1753
+ * instructions: 'You are a helpful assistant',
1754
+ * chat: myChatInstance,
1755
+ * tools: [searchTool, calculatorTool]
1756
+ * })
1757
+ *
1758
+ * if (result.is(ListenExit)) {
1759
+ * // Agent is waiting for user input
1760
+ * }
1761
+ *
1762
+ * @example
1763
+ * // With dynamic instructions and hooks
1764
+ * const result = await execute({
1765
+ * client: cognitiveClient,
1766
+ * instructions: (ctx) => `Process ${ctx.variables.dataCount} records`,
1767
+ * tools: async (ctx) => await getContextualTools(ctx),
1768
+ * model: 'best',
1769
+ * temperature: 0.1,
1770
+ * options: { loop: 10 },
1771
+ * onTrace: ({ trace, iteration }) => console.log(trace),
1772
+ * onExit: async (result) => await validateResult(result)
1773
+ * })
1774
+ */
1775
+ const execute = async (props) => {
1776
+ const { executeContext } = await import("./execute-DYvFRs54.js");
1777
+ return executeContext(props);
1503
1778
  };
1504
- export {
1505
- BaseResponse,
1506
- Chat,
1507
- CitationsManager,
1508
- Component,
1509
- ComponentRegistry,
1510
- DefaultComponents,
1511
- DefaultExit,
1512
- ErrorExecutionResult,
1513
- ExecutionResult,
1514
- Exit,
1515
- ListenExit,
1516
- LoopExceededError,
1517
- MARKER,
1518
- NAME_REGEX,
1519
- NextResponse,
1520
- ObjectInstance,
1521
- PartialExecutionResult,
1522
- ReplayableAsyncIterable,
1523
- RunResponse,
1524
- SendResponse,
1525
- Snapshot,
1526
- SnapshotSignal,
1527
- StreamingMessageParser,
1528
- SuccessExecutionResult,
1529
- ThinkExit,
1530
- ThinkSignal,
1531
- Tool,
1532
- _CustomModelClient,
1533
- assertValidComponent,
1534
- collectText,
1535
- configureQuickJS,
1536
- configureTokenizer,
1537
- execute,
1538
- generateInstructions,
1539
- getValue,
1540
- init,
1541
- isAnyComponent,
1542
- isComponent,
1543
- normalizeComponentDefinition,
1544
- normalizeComponentName,
1545
- normalizePropsSchema,
1546
- parseExit,
1547
- parseStream,
1548
- parseText,
1549
- sanitizeMessageText,
1550
- tryParseJson,
1551
- utils,
1552
- validateComponent,
1553
- validateProps,
1554
- version
1779
+ /**
1780
+ * Loads the necessary dependencies for the library to work
1781
+ * Calling this function is optional, but it will improve the performance of the first call to `executeContext`
1782
+ * It's recommended to call this function at the beginning of your application without awaiting it (void init())
1783
+ */
1784
+ const init = async () => {
1785
+ await import("./execute-DYvFRs54.js");
1786
+ await import("./component-DGG7d_Mz.js").then((n) => n.r);
1787
+ await import("./tool-CAo-BQHs.js").then((n) => n.n);
1788
+ await import("./exit-CDdxDBRT.js").then((n) => n.n);
1789
+ await import("./jsx-CIgdGGVs.js").then((n) => n.i);
1790
+ await import("./vm-C5vf1LH3.js");
1791
+ await import("./utils-UBeAFeEI.js").then((n) => n.h);
1792
+ await import("./truncator-DZqEqnpD.js").then((n) => n.r);
1793
+ await import("./typings-CrBa7Tyy.js").then((n) => n.n);
1794
+ await import("./dual-modes-D_p-jedw.js").then((n) => n.n);
1555
1795
  };
1796
+ //#endregion
1797
+ export { BaseResponse, Chat, CitationsManager, Component, ComponentRegistry, DefaultComponents, DefaultExit, ErrorExecutionResult, ExecutionResult, Exit, ListenExit, LoopExceededError, MARKER, NAME_REGEX, NextResponse, ObjectInstance, PartialExecutionResult, ReplayableAsyncIterable, RunResponse, SendResponse, Snapshot, SnapshotSignal, StreamingMessageParser, SuccessExecutionResult, ThinkExit, ThinkSignal, Tool, _CustomModelClient, assertValidComponent, collectText, configureQuickJS, configureTokenizer, execute, generateInstructions, getValue, init, isAnyComponent, isComponent, normalizeComponentDefinition, normalizeComponentName, normalizePropsSchema, parseExit, parseStream, parseText, sanitizeMessageText, tryParseJson, utils, validateComponent, validateProps, version };