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.
- package/DOCS.md +1 -3
- package/dist/_baseForOwn-CFhWe_W6.js +48 -0
- package/dist/_baseForOwn-buOZDFbK.cjs +103 -0
- package/dist/_baseIteratee-CF4qP2a8.cjs +2377 -0
- package/dist/_baseIteratee-P_firB7b.js +2138 -0
- package/dist/_getPrototype-CG7Dxrrz.js +6 -0
- package/dist/_getPrototype-DQDlNVVM.cjs +10 -0
- package/dist/_overArg-C5Hy7oCi.cjs +196 -0
- package/dist/_overArg-CRYbj1NH.js +155 -0
- package/dist/citations.d.ts +11 -11
- package/dist/component-BwixNVH8.cjs +73 -0
- package/dist/component-DGG7d_Mz.js +44 -0
- package/dist/context.d.ts +8 -8
- package/dist/custom-client-BPmEoWKu.cjs +2618 -0
- package/dist/custom-client-NUoxrFQh.js +2547 -0
- package/dist/dual-modes-D_p-jedw.js +2750 -0
- package/dist/dual-modes-wAOAlaBS.cjs +2840 -0
- package/dist/errors-C962uJdS.cjs +220 -0
- package/dist/errors-Cg1G0Awu.js +161 -0
- package/dist/execute-BEaq3jkV.cjs +1003 -0
- package/dist/execute-DYvFRs54.js +1001 -0
- package/dist/exit-BGi0Kk0y.cjs +428 -0
- package/dist/exit-CDdxDBRT.js +417 -0
- package/dist/index.cjs +1786 -1489
- package/dist/index.d.ts +10 -9
- package/dist/index.js +1721 -1479
- package/dist/index.workerd.js +17534 -16875
- package/dist/isEmpty-BhJRCqIF.js +52 -0
- package/dist/isEmpty-LY533Wmh.cjs +57 -0
- package/dist/isPlainObject-CSxXVKCk.cjs +56 -0
- package/dist/isPlainObject-D39AzPO2.js +51 -0
- package/dist/jsx-BWvT6ukg.cjs +75 -0
- package/dist/jsx-CIgdGGVs.js +52 -0
- package/dist/jsx.d.ts +2 -2
- package/dist/prompts/chat-mode/system.md.d.ts +1 -1
- package/dist/prompts/chat-mode/user.md.d.ts +1 -1
- package/dist/prompts/worker-mode/system.md.d.ts +1 -1
- package/dist/prompts/worker-mode/user.md.d.ts +1 -1
- package/dist/quickjs-variant-BYhMBpwU.js +45 -0
- package/dist/quickjs-variant-CsIwTVFC.cjs +56 -0
- package/dist/quickjs-variant.workerd.d.ts +1 -1
- package/dist/result.d.ts +12 -12
- package/dist/rolldown-runtime-EAsCxrDo.js +14 -0
- package/dist/runtime/interpret-result.d.ts +1 -1
- package/dist/snapshots.d.ts +5 -5
- package/dist/stack-traces-BIE_5Xwf.js +21 -0
- package/dist/stack-traces-ntw_qhoy.cjs +26 -0
- package/dist/tool-CAo-BQHs.js +643 -0
- package/dist/tool-DdPZb0JW.cjs +654 -0
- package/dist/tool.d.ts +3 -3
- package/dist/truncator-B6aaJaN1.cjs +276 -0
- package/dist/truncator-DZqEqnpD.js +253 -0
- package/dist/typings-BwAz_TM9.cjs +784 -0
- package/dist/typings-CrBa7Tyy.js +743 -0
- package/dist/uniq-BYQ7GWwb.js +183 -0
- package/dist/uniq-G872RWmE.cjs +200 -0
- package/dist/utils-UBeAFeEI.js +950 -0
- package/dist/utils-ZpJm00ed.cjs +1075 -0
- package/dist/vm/errors.d.ts +2 -2
- package/dist/vm-BjkJqSQH.cjs +3438 -0
- package/dist/vm-C5vf1LH3.js +3436 -0
- package/package.json +7 -7
- package/dist/chunk-34GJUS6U.js +0 -3266
- package/dist/chunk-3UZKLXAD.cjs +0 -3249
- package/dist/chunk-4POZJQXN.js +0 -1530
- package/dist/chunk-5WBNTV42.cjs +0 -1530
- package/dist/chunk-5ZF6NTYI.cjs +0 -33
- package/dist/chunk-72ZWBNZV.js +0 -173
- package/dist/chunk-DDSJTR6Q.cjs +0 -257
- package/dist/chunk-GDJTKI7J.js +0 -792
- package/dist/chunk-GLXADNTR.js +0 -199
- package/dist/chunk-GOWPACSK.cjs +0 -199
- package/dist/chunk-HB2G32AK.js +0 -257
- package/dist/chunk-HC2QZTVT.cjs +0 -3266
- package/dist/chunk-HEAQAEFZ.cjs +0 -173
- package/dist/chunk-HMMM32RG.cjs +0 -792
- package/dist/chunk-I56MPOVG.cjs +0 -62
- package/dist/chunk-KIN7Y247.cjs +0 -32
- package/dist/chunk-KTDBAJBB.cjs +0 -1685
- package/dist/chunk-KWQTYXAT.js +0 -62
- package/dist/chunk-LOX7GOR6.js +0 -1685
- package/dist/chunk-PK72FAKD.cjs +0 -175
- package/dist/chunk-PZE6PIPG.cjs +0 -621
- package/dist/chunk-RGUG3UMF.cjs +0 -26
- package/dist/chunk-TDALRNGL.js +0 -621
- package/dist/chunk-UGHFNCPD.js +0 -26
- package/dist/chunk-WSVDMGMR.js +0 -175
- package/dist/chunk-XTEHT2TK.js +0 -33
- package/dist/chunk-YEAWWJSJ.js +0 -32
- package/dist/chunk-YRS7KRKP.js +0 -3249
- package/dist/component-2NYFY6CW.cjs +0 -14
- package/dist/component-ASNZHQUR.js +0 -14
- package/dist/dual-modes-S5TJ5JVJ.js +0 -10
- package/dist/dual-modes-TCMWBZIH.cjs +0 -10
- package/dist/execute-FF7D46TY.js +0 -1109
- package/dist/execute-IK6O4SU2.cjs +0 -1109
- package/dist/exit-MX56VN2C.cjs +0 -8
- package/dist/exit-UQ3EUX5D.js +0 -8
- package/dist/jsx-DDZ63UUF.js +0 -13
- package/dist/jsx-ULPWX5TW.cjs +0 -13
- package/dist/tool-KD7FL45V.js +0 -11
- package/dist/tool-VE2YHIY2.cjs +0 -11
- package/dist/truncator-73RCL52Y.js +0 -12
- package/dist/truncator-FZNLYSDL.cjs +0 -12
- package/dist/typings-E2RROCLT.js +0 -10
- package/dist/typings-LQOBZXSO.cjs +0 -10
- package/dist/utils-IATCUNKF.js +0 -43
- package/dist/utils-ITB4NEL7.cjs +0 -43
- package/dist/vm-6D3IIIU7.cjs +0 -12
- package/dist/vm-QO5FSGPN.js +0 -12
package/dist/index.js
CHANGED
|
@@ -1,1555 +1,1797 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
276
|
-
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
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
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
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
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
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
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
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
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
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
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
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
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
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
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
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
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
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
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
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
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
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
|
|
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**
|
|
871
|
-
- **Output directory**
|
|
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
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
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
|
-
|
|
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
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
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
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
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
|
-
|
|
1343
|
+
//#endregion
|
|
1344
|
+
//#region src/message-stream/stream.ts
|
|
1108
1345
|
var BaseResponse = class {
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
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
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
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
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
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
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
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
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
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
|
-
|
|
1454
|
+
return parseResponses(stream, options);
|
|
1202
1455
|
}
|
|
1456
|
+
/** Parses a complete text (convenience wrapper over {@link parseStream}). */
|
|
1203
1457
|
function parseText(text, options) {
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
}(),
|
|
1208
|
-
options
|
|
1209
|
-
);
|
|
1458
|
+
return parseResponses((async function* () {
|
|
1459
|
+
yield text;
|
|
1460
|
+
})(), options);
|
|
1210
1461
|
}
|
|
1211
1462
|
async function* parseResponses(stream, options) {
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
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
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
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
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
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
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
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
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
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
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1590
|
+
const errors = [];
|
|
1591
|
+
_checkValue(props, schema, "", errors);
|
|
1592
|
+
return errors;
|
|
1358
1593
|
}
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
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
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
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
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
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
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
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
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
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
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
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
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
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 };
|