llmz 0.0.13 → 0.0.14

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/dist/index.js CHANGED
@@ -8,11 +8,11 @@ import {
8
8
  SuccessExecutionResult,
9
9
  ThinkExit,
10
10
  getValue
11
- } from "./chunk-PRVFVXT4.js";
11
+ } from "./chunk-2Z5SFF6R.js";
12
12
  import "./chunk-KH6JQYQA.js";
13
13
  import {
14
14
  Tool
15
- } from "./chunk-JAGB2AOU.js";
15
+ } from "./chunk-OKTHMXRT.js";
16
16
  import {
17
17
  formatTypings,
18
18
  getTypings
@@ -26,7 +26,7 @@ import {
26
26
  import "./chunk-JQBT7UWN.js";
27
27
  import {
28
28
  Exit
29
- } from "./chunk-JMSZKB4T.js";
29
+ } from "./chunk-XAN7HQP5.js";
30
30
  import {
31
31
  Component,
32
32
  assertValidComponent,
@@ -307,6 +307,62 @@ var ObjectInstance = class {
307
307
  properties;
308
308
  tools;
309
309
  metadata;
310
+ /**
311
+ * Creates a new ObjectInstance.
312
+ *
313
+ * @param props - Object configuration
314
+ * @param props.name - Unique object name (must be valid TypeScript identifier)
315
+ * @param props.description - Human-readable description of the object
316
+ * @param props.tools - Array of tools to group under this object namespace
317
+ * @param props.properties - Array of stateful properties for this object
318
+ * @param props.metadata - Additional metadata for the object
319
+ *
320
+ * @throws Error if name is not a valid identifier
321
+ * @throws Error if description is not a string
322
+ * @throws Error if metadata is not an object
323
+ * @throws Error if properties/tools are not arrays
324
+ * @throws Error if properties exceed 100 limit
325
+ * @throws Error if property names are duplicated or invalid
326
+ * @throws Error if property descriptions exceed 5000 characters
327
+ *
328
+ * @example
329
+ * ```typescript
330
+ * const userProfile = new ObjectInstance({
331
+ * name: 'user',
332
+ * description: 'User profile management',
333
+ * properties: [
334
+ * {
335
+ * name: 'name',
336
+ * value: 'John Doe',
337
+ * type: z.string().min(1),
338
+ * description: 'User full name',
339
+ * writable: true,
340
+ * },
341
+ * {
342
+ * name: 'email',
343
+ * value: null,
344
+ * type: z.string().email().nullable(),
345
+ * description: 'User email address',
346
+ * writable: true,
347
+ * },
348
+ * ],
349
+ * tools: [
350
+ * new Tool({
351
+ * name: 'updateProfile',
352
+ * input: z.object({ name: z.string(), email: z.string() }),
353
+ * handler: async ({ name, email }) => {
354
+ * // Update external system
355
+ * await updateUserInDatabase({ name, email })
356
+ * },
357
+ * }),
358
+ * ],
359
+ * metadata: {
360
+ * version: '1.0',
361
+ * category: 'user-management',
362
+ * },
363
+ * })
364
+ * ```
365
+ */
310
366
  constructor(props) {
311
367
  var _a;
312
368
  if (!isValidIdentifier(props.name)) {
@@ -368,6 +424,43 @@ var ObjectInstance = class {
368
424
  this.properties = props.properties;
369
425
  this.tools = Tool.withUniqueNames(props.tools ?? []);
370
426
  }
427
+ /**
428
+ * Generates TypeScript namespace declarations for this object.
429
+ *
430
+ * This method creates TypeScript definitions that are included in the LLM context
431
+ * to help it understand the available properties and tools. Properties become
432
+ * const declarations with appropriate Readonly/Writable types, and tools become
433
+ * function signatures.
434
+ *
435
+ * @returns Promise resolving to TypeScript namespace declaration
436
+ *
437
+ * @example
438
+ * ```typescript
439
+ * const obj = new ObjectInstance({
440
+ * name: 'user',
441
+ * properties: [
442
+ * { name: 'name', value: 'John', writable: true },
443
+ * { name: 'id', value: 123, writable: false },
444
+ * ],
445
+ * tools: [
446
+ * new Tool({
447
+ * name: 'save',
448
+ * input: z.object({ data: z.string() }),
449
+ * handler: async ({ data }) => { /* ... *\/ },
450
+ * }),
451
+ * ],
452
+ * })
453
+ *
454
+ * const typings = await obj.getTypings()
455
+ * console.log(typings)
456
+ * // Output:
457
+ * // export namespace user {
458
+ * // const name: Writable<string> = "John"
459
+ * // const id: Readonly<number> = 123
460
+ * // function save(args: { data: string }): Promise<void>
461
+ * // }
462
+ * ```
463
+ */
371
464
  async getTypings() {
372
465
  return getObjectTypings(this).withProperties().withTools().build();
373
466
  }
@@ -866,25 +959,129 @@ var Chat = class {
866
959
  handler;
867
960
  transcript;
868
961
  components;
962
+ /**
963
+ * Creates a new Chat instance.
964
+ *
965
+ * @param props - Chat configuration
966
+ * @param props.handler - Function to handle agent messages (called for every agent output)
967
+ * @param props.components - Available UI components (static array or dynamic function)
968
+ * @param props.transcript - Conversation history (static array or dynamic function, defaults to empty)
969
+ *
970
+ * @example
971
+ * ```typescript
972
+ * // Basic chat with static configuration
973
+ * const chat = new Chat({
974
+ * handler: async (input) => {
975
+ * if (isComponent(input, DefaultComponents.Text)) {
976
+ * console.log('Agent:', input.children.join(''))
977
+ * }
978
+ * },
979
+ * components: [DefaultComponents.Text, DefaultComponents.Button],
980
+ * transcript: [
981
+ * { role: 'user', content: 'Hello', timestamp: Date.now() }
982
+ * ],
983
+ * })
984
+ * ```
985
+ *
986
+ * @example
987
+ * ```typescript
988
+ * // Dynamic chat with functions for real-time updates
989
+ * class MyChat extends Chat {
990
+ * private messages: Transcript.Message[] = []
991
+ *
992
+ * constructor() {
993
+ * super({
994
+ * handler: (input) => this.handleMessage(input),
995
+ *
996
+ * // Dynamic components - can change during execution
997
+ * components: () => [
998
+ * DefaultComponents.Text,
999
+ * DefaultComponents.Button,
1000
+ * ...this.getCustomComponents()
1001
+ * ],
1002
+ *
1003
+ * // Dynamic transcript - always reflects current state
1004
+ * transcript: () => this.messages,
1005
+ * })
1006
+ * }
1007
+ * }
1008
+ * ```
1009
+ */
869
1010
  constructor(props) {
870
1011
  this.handler = props.handler;
871
1012
  this.components = props.components;
872
1013
  this.transcript = props.transcript || [];
873
1014
  }
1015
+ /**
1016
+ * Called when an execution cycle completes, regardless of the outcome.
1017
+ *
1018
+ * Override this method to handle execution results, manage conversation flow,
1019
+ * or perform cleanup tasks. This is called after each `execute()` call completes,
1020
+ * whether it succeeds, fails, or is interrupted.
1021
+ *
1022
+ * @param result - The execution result containing status, iterations, and exit information
1023
+ *
1024
+ * @example
1025
+ * ```typescript
1026
+ * class MyChat extends Chat {
1027
+ * public result?: ExecutionResult
1028
+ *
1029
+ * onExecutionDone(result: ExecutionResult) {
1030
+ * // Store result for conversation flow control
1031
+ * this.result = result
1032
+ *
1033
+ * // Handle different result types
1034
+ * if (result.isSuccess()) {
1035
+ * console.log('✅ Execution completed successfully')
1036
+ * console.log('Exit:', result.output.exit_name)
1037
+ * } else if (result.isError()) {
1038
+ * console.error('❌ Execution failed:', result.output.error)
1039
+ * } else if (result.isInterrupted()) {
1040
+ * console.log('⏸️ Execution interrupted (partial result)')
1041
+ * }
1042
+ * }
1043
+ *
1044
+ * // Use stored result for conversation flow
1045
+ * isWaitingForInput(): boolean {
1046
+ * return this.result?.is(ListenExit) ?? false
1047
+ * }
1048
+ * }
1049
+ * ```
1050
+ *
1051
+ * @example
1052
+ * ```typescript
1053
+ * // CLIChat implementation example
1054
+ * class CLIChat extends Chat {
1055
+ * public status?: IterationStatus
1056
+ * public result?: ExecutionResult
1057
+ *
1058
+ * onExecutionDone(result: ExecutionResult) {
1059
+ * this.result = result
1060
+ * this.status = result.iterations.at(-1)?.status
1061
+ * }
1062
+ *
1063
+ * // Check if agent exited with specific exit type
1064
+ * hasExitedWith<R>(exit: Exit<R>): boolean {
1065
+ * return this.status?.type === 'exit_success' &&
1066
+ * this.status.exit_success.exit_name === exit.name
1067
+ * }
1068
+ * }
1069
+ * ```
1070
+ */
874
1071
  onExecutionDone(_result) {
875
1072
  }
876
1073
  };
877
1074
 
878
1075
  // src/index.ts
879
1076
  var execute = async (props) => {
880
- const { executeContext } = await import("./llmz-ROOX7RYI.js");
1077
+ const { executeContext } = await import("./llmz-MCHRHRTD.js");
881
1078
  return executeContext(props);
882
1079
  };
883
1080
  var init = async () => {
884
- await import("./llmz-ROOX7RYI.js");
1081
+ await import("./llmz-MCHRHRTD.js");
885
1082
  await import("./component-WFVDVSDK.js");
886
- await import("./tool-N6ODRRGH.js");
887
- await import("./exit-YORW76T3.js");
1083
+ await import("./tool-4AJIJ3QB.js");
1084
+ await import("./exit-7HDRH27N.js");
888
1085
  await import("./jsx-AEHVFB3L.js");
889
1086
  await import("./vm-FLBMZUA2.js");
890
1087
  await import("./utils-N24IHDFA.js");
@@ -7,9 +7,9 @@ import {
7
7
  PartialExecutionResult,
8
8
  Snapshot,
9
9
  SuccessExecutionResult
10
- } from "./chunk-PRVFVXT4.js";
10
+ } from "./chunk-2Z5SFF6R.js";
11
11
  import "./chunk-KH6JQYQA.js";
12
- import "./chunk-JAGB2AOU.js";
12
+ import "./chunk-OKTHMXRT.js";
13
13
  import "./chunk-IH2WQFO5.js";
14
14
  import {
15
15
  AssignmentError,
@@ -24,7 +24,7 @@ import {
24
24
  import {
25
25
  cleanStackTrace
26
26
  } from "./chunk-JQBT7UWN.js";
27
- import "./chunk-JMSZKB4T.js";
27
+ import "./chunk-XAN7HQP5.js";
28
28
  import "./chunk-GGWM6X2K.js";
29
29
  import "./chunk-ORQP26SZ.js";
30
30
  import {
@@ -46,6 +46,37 @@ import { Cognitive } from "@botpress/cognitive";
46
46
  import { z } from "@bpinternal/zui";
47
47
  import ms from "ms";
48
48
  import { ulid } from "ulid";
49
+
50
+ // src/abort-signal.ts
51
+ function createJoinedAbortController(signals) {
52
+ const controller = new AbortController();
53
+ const validSignals = signals.filter((signal) => signal != null);
54
+ if (validSignals.length === 0) {
55
+ return controller;
56
+ }
57
+ for (const signal of validSignals) {
58
+ if (signal.aborted) {
59
+ controller.abort(signal.reason);
60
+ return controller;
61
+ }
62
+ }
63
+ const abortListeners = [];
64
+ for (const signal of validSignals) {
65
+ const listener = () => {
66
+ controller.abort(signal.reason);
67
+ cleanup();
68
+ };
69
+ signal.addEventListener("abort", listener);
70
+ abortListeners.push(() => signal.removeEventListener("abort", listener));
71
+ }
72
+ const cleanup = () => {
73
+ abortListeners.forEach((removeListener) => removeListener());
74
+ };
75
+ controller.signal.addEventListener("abort", cleanup, { once: true });
76
+ return controller;
77
+ }
78
+
79
+ // src/llmz.ts
49
80
  var getErrorMessage = (err) => err instanceof Error ? err.message : JSON.stringify(err);
50
81
  var SLOW_TOOL_WARNING = ms("15s");
51
82
  var RESPONSE_LENGTH_BUFFER = {
@@ -70,7 +101,8 @@ var executeContext = async (props) => {
70
101
  };
71
102
  var _executeContext = async (props) => {
72
103
  var _a, _b, _c, _d;
73
- const { signal, onIterationEnd, onTrace, onExit, onBeforeExecution } = props;
104
+ const controller = createJoinedAbortController([props.signal]);
105
+ const { onIterationEnd, onTrace, onExit, onBeforeExecution, onAfterTool, onBeforeTool } = props;
74
106
  const cognitive = Cognitive.isCognitiveClient(props.client) ? props.client : new Cognitive({ client: props.client });
75
107
  const cleanups = [];
76
108
  const ctx = new Context({
@@ -91,14 +123,14 @@ var _executeContext = async (props) => {
91
123
  return new ErrorExecutionResult(ctx, new LoopExceededError());
92
124
  }
93
125
  const iteration = await ctx.nextIteration();
94
- if (signal == null ? void 0 : signal.aborted) {
126
+ if (controller.signal.aborted) {
95
127
  iteration.end({
96
128
  type: "aborted",
97
129
  aborted: {
98
- reason: signal.reason ?? "The operation was aborted"
130
+ reason: controller.signal.reason ?? "The operation was aborted"
99
131
  }
100
132
  });
101
- return new ErrorExecutionResult(ctx, signal.reason ?? "The operation was aborted");
133
+ return new ErrorExecutionResult(ctx, controller.signal.reason ?? "The operation was aborted");
102
134
  }
103
135
  cleanups.push(
104
136
  iteration.traces.onPush((traces) => {
@@ -112,9 +144,11 @@ var _executeContext = async (props) => {
112
144
  iteration,
113
145
  ctx,
114
146
  cognitive,
115
- abortSignal: signal,
147
+ controller,
116
148
  onExit,
117
- onBeforeExecution
149
+ onBeforeExecution,
150
+ onAfterTool,
151
+ onBeforeTool
118
152
  });
119
153
  } catch (err) {
120
154
  iteration.end({
@@ -126,7 +160,7 @@ var _executeContext = async (props) => {
126
160
  });
127
161
  }
128
162
  try {
129
- await (onIterationEnd == null ? void 0 : onIterationEnd(iteration));
163
+ await (onIterationEnd == null ? void 0 : onIterationEnd(iteration, controller));
130
164
  } catch (err) {
131
165
  console.error(err);
132
166
  }
@@ -164,9 +198,11 @@ var executeIteration = async ({
164
198
  iteration,
165
199
  ctx,
166
200
  cognitive,
167
- abortSignal,
201
+ controller,
168
202
  onExit,
169
- onBeforeExecution
203
+ onBeforeExecution,
204
+ onBeforeTool,
205
+ onAfterTool
170
206
  }) => {
171
207
  var _a, _b, _c, _d, _e;
172
208
  let startedAt = Date.now();
@@ -192,7 +228,7 @@ var executeIteration = async ({
192
228
  model: model.ref
193
229
  });
194
230
  const output = await cognitive.generateContent({
195
- signal: abortSignal,
231
+ signal: controller.signal,
196
232
  systemPrompt: (_a = messages.find((x) => x.role === "system")) == null ? void 0 : _a.content,
197
233
  model: model.ref,
198
234
  temperature: ctx.temperature,
@@ -208,7 +244,10 @@ var executeIteration = async ({
208
244
  iteration.code = assistantResponse.code.trim();
209
245
  if (typeof onBeforeExecution === "function") {
210
246
  try {
211
- await onBeforeExecution(iteration);
247
+ const hookRes = await onBeforeExecution(iteration, controller);
248
+ if (typeof (hookRes == null ? void 0 : hookRes.code) === "string" && hookRes.code.trim().length > 0) {
249
+ iteration.code = hookRes.code.trim();
250
+ }
212
251
  } catch (err) {
213
252
  if (err instanceof ThinkSignal) {
214
253
  return iteration.end({
@@ -288,19 +327,27 @@ var executeIteration = async ({
288
327
  });
289
328
  }
290
329
  for (const tool of obj.tools ?? []) {
291
- instance[tool.name] = wrapTool({ tool, traces, object: obj.name });
330
+ instance[tool.name] = wrapTool({
331
+ tool,
332
+ traces,
333
+ object: obj.name,
334
+ iteration,
335
+ beforeHook: onBeforeTool,
336
+ afterHook: onAfterTool,
337
+ controller
338
+ });
292
339
  }
293
340
  Object.preventExtensions(instance);
294
341
  Object.seal(instance);
295
342
  vmContext[obj.name] = instance;
296
343
  }
297
344
  for (const tool of iteration.tools) {
298
- const wrapped = wrapTool({ tool, traces });
345
+ const wrapped = wrapTool({ tool, traces, iteration, beforeHook: onBeforeTool, afterHook: onAfterTool, controller });
299
346
  for (const key of [tool.name, ...tool.aliases ?? []]) {
300
347
  vmContext[key] = wrapped;
301
348
  }
302
349
  }
303
- if (abortSignal == null ? void 0 : abortSignal.aborted) {
350
+ if (controller.signal.aborted) {
304
351
  traces.push({
305
352
  type: "abort_signal",
306
353
  started_at: Date.now(),
@@ -309,22 +356,26 @@ var executeIteration = async ({
309
356
  return iteration.end({
310
357
  type: "aborted",
311
358
  aborted: {
312
- reason: (abortSignal == null ? void 0 : abortSignal.reason) ?? "The operation was aborted"
359
+ reason: controller.signal.reason ?? "The operation was aborted"
313
360
  }
314
361
  });
315
362
  }
316
363
  startedAt = Date.now();
317
- const result = await runAsyncFunction(vmContext, iteration.code, traces, abortSignal, ctx.timeout).catch(
318
- (err) => {
319
- return {
320
- success: false,
321
- error: err,
322
- lines_executed: [],
323
- traces: [],
324
- variables: {}
325
- };
326
- }
327
- );
364
+ const result = await runAsyncFunction(
365
+ vmContext,
366
+ iteration.code,
367
+ traces,
368
+ controller.signal,
369
+ ctx.timeout
370
+ ).catch((err) => {
371
+ return {
372
+ success: false,
373
+ error: err,
374
+ lines_executed: [],
375
+ traces: [],
376
+ variables: {}
377
+ };
378
+ });
328
379
  if (result.error && result.error instanceof InvalidCodeError) {
329
380
  return iteration.end({
330
381
  type: "invalid_code_error",
@@ -348,11 +399,11 @@ var executeIteration = async ({
348
399
  }
349
400
  });
350
401
  }
351
- if (abortSignal == null ? void 0 : abortSignal.aborted) {
402
+ if (controller.signal.aborted) {
352
403
  return iteration.end({
353
404
  type: "aborted",
354
405
  aborted: {
355
- reason: (abortSignal == null ? void 0 : abortSignal.reason) ?? "The operation was aborted"
406
+ reason: controller.signal.reason ?? "The operation was aborted"
356
407
  }
357
408
  });
358
409
  }
@@ -462,7 +513,7 @@ var executeIteration = async ({
462
513
  }
463
514
  });
464
515
  };
465
- function wrapTool({ tool, traces, object }) {
516
+ function wrapTool({ tool, traces, object, iteration, beforeHook, afterHook, controller }) {
466
517
  const getToolInput = (input) => tool.zInput.safeParse(input).data ?? input;
467
518
  return function(input) {
468
519
  const toolCallId = `tcall_${ulid()}`;
@@ -510,9 +561,32 @@ function wrapTool({ tool, traces, object }) {
510
561
  return false;
511
562
  };
512
563
  try {
513
- const result = tool.execute(input, {
514
- callId: toolCallId
515
- });
564
+ const withHooks = async (input2) => {
565
+ const beforeRes = await (beforeHook == null ? void 0 : beforeHook({
566
+ iteration,
567
+ tool,
568
+ input: input2,
569
+ controller
570
+ }));
571
+ if (typeof (beforeRes == null ? void 0 : beforeRes.input) !== "undefined") {
572
+ input2 = beforeRes.input;
573
+ }
574
+ let output2 = await tool.execute(input2, {
575
+ callId: toolCallId
576
+ });
577
+ const afterRes = await (afterHook == null ? void 0 : afterHook({
578
+ iteration,
579
+ tool,
580
+ input: input2,
581
+ output: output2,
582
+ controller
583
+ }));
584
+ if (typeof (afterRes == null ? void 0 : afterRes.output) !== "undefined") {
585
+ output2 = afterRes.output;
586
+ }
587
+ return output2;
588
+ };
589
+ const result = withHooks(input);
516
590
  if (result instanceof Promise || (result == null ? void 0 : result.then) && (result == null ? void 0 : result.catch)) {
517
591
  return result.then((res) => {
518
592
  output = res;