llmz 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/DOCS.md +8 -4
  2. package/ERRORS.md +5 -2
  3. package/dist/{component-BpMVZYOa.cjs → component-CMFoCHyu.cjs} +3 -3
  4. package/dist/{component-C27qu6Zv.js → component-DncqP4fB.js} +3 -3
  5. package/dist/context.d.ts +1 -1
  6. package/dist/{custom-client-BDkgBR2w.js → custom-client-C-WXvLD9.js} +37 -13
  7. package/dist/{custom-client-MytucGI-.cjs → custom-client-Cgt9_0r1.cjs} +37 -13
  8. package/dist/errors/catalog.d.ts +7 -0
  9. package/dist/{errors-IhnTKUp7.cjs → errors-C6ppQUZX.cjs} +22 -0
  10. package/dist/{errors-H4JJFH3S.js → errors-Crk5P8jo.js} +17 -1
  11. package/dist/errors.d.ts +6 -0
  12. package/dist/{execute-BYI_U5sR.js → execute-CiYvPs5Q.js} +196 -78
  13. package/dist/{execute-DLas4V2n.cjs → execute-DTTfacsi.cjs} +196 -78
  14. package/dist/{exit-BUgJ5qi5.cjs → exit-BOtKWPuT.cjs} +2 -2
  15. package/dist/{exit-CPNk3vbz.js → exit-DMPwCSba.js} +2 -2
  16. package/dist/index.cjs +18 -17
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.js +18 -18
  19. package/dist/index.workerd.js +252 -79
  20. package/dist/inspection.d.ts +2 -0
  21. package/dist/{native-Bm6L9zOb.cjs → native-0bq5ATZM.cjs} +3 -3
  22. package/dist/{native-DvTH9vFe.js → native-n_718Z-T.js} +3 -3
  23. package/dist/{quickjs-variant-DJwYDSsb.cjs → quickjs-variant-21IO97PF.cjs} +15 -8
  24. package/dist/{quickjs-variant-DlPi9-Sx.js → quickjs-variant-CtuGagsb.js} +10 -9
  25. package/dist/runtime/chat-completion.test.d.ts +1 -0
  26. package/dist/runtime/chat-last-speaker.test.d.ts +1 -0
  27. package/dist/runtime/empty-completion.test.d.ts +1 -0
  28. package/dist/runtime/execution-report.d.ts +3 -0
  29. package/dist/runtime/forced-inspection.d.ts +13 -0
  30. package/dist/runtime/forced-inspection.test.d.ts +1 -0
  31. package/dist/runtime/javascript-api.d.ts +3 -0
  32. package/dist/runtime/think-signal.test.d.ts +1 -0
  33. package/dist/runtime/tool-wrapper.d.ts +4 -2
  34. package/dist/runtime/types.d.ts +8 -0
  35. package/dist/{schema-DBkjo0b7.cjs → schema-CXlnbb3n.cjs} +1 -1
  36. package/dist/{schema-Aer_W7Al.js → schema-GE4ISPCR.js} +1 -1
  37. package/dist/session/messages.d.ts +2 -0
  38. package/dist/session/serialization.d.ts +2 -0
  39. package/dist/session/session.d.ts +4 -0
  40. package/dist/session/speaker.test.d.ts +1 -0
  41. package/dist/{tool--gK91eez.js → tool-DstssiAs.js} +4 -4
  42. package/dist/{tool-Da_F-N-5.cjs → tool-_wK9hQOb.cjs} +4 -4
  43. package/dist/{truncate-BSoH2-nY.js → truncate-BEKbdo3t.js} +1 -1
  44. package/dist/{truncate-nV3Mxr6-.cjs → truncate-uomAMGPs.cjs} +1 -1
  45. package/dist/types.d.ts +6 -0
  46. package/dist/{typings-C1ihexWE.cjs → typings-BA_XxAcY.cjs} +1 -1
  47. package/dist/{typings-D9_aC8aq.js → typings-BEVDvtuk.js} +1 -1
  48. package/dist/vm/types.d.ts +2 -0
  49. package/dist/{vm-8jxt8PTK.js → vm-C7SfutHn.js} +28 -3
  50. package/dist/{vm-XuVPuW3Y.cjs → vm-gmekHc0i.cjs} +28 -2
  51. package/package.json +1 -1
@@ -49,7 +49,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
49
49
  }) : target, mod));
50
50
  //#endregion
51
51
  //#region package.json
52
- var version = "1.0.1";
52
+ var version = "1.0.2";
53
53
  //#endregion
54
54
  //#region src/errors/catalog.ts
55
55
  function validationMessage(subject, issues, expectedInput) {
@@ -68,7 +68,7 @@ function isLLMzError(error, code) {
68
68
  function isCriticalError(error) {
69
69
  return isLLMzError(error) && error.critical;
70
70
  }
71
- var ERROR_BRAND, LLMzError, CodeExecutionError, InvalidCodeError, CodeFormattingError, ToolInputError, ExitInputError, ObjectPropertyError, ComponentInputError, UnknownToolError, UnknownComponentError, UnknownExitError, ReservedIdentifierError, ToolExecutionError, LoopExceededError, TokenOverflowError, MemoryCapacityError, AssignmentError, CognitiveError, CompactionError, InvalidToolError, InvalidObjectError, InvalidExitError, InvalidComponentError, InvalidConfigurationError, InvalidMessageError, InvalidEventError, InvalidSessionError, SessionStateError, MemoryValueError, NativeProtocolError, HostOperationError, DeliveryError, HookError, ExecutionAbortedError, InternalError, errorClasses$1, ERROR_CODES;
71
+ var ERROR_BRAND, LLMzError, CodeExecutionError, InvalidCodeError, CodeFormattingError, ToolInputError, ExitInputError, ObjectPropertyError, ComponentInputError, UnknownToolError, UnknownComponentError, UnknownExitError, ReservedIdentifierError, ToolExecutionError, LoopExceededError, TokenOverflowError, MemoryCapacityError, AssignmentError, CognitiveError, CompactionError, InvalidToolError, InvalidObjectError, InvalidExitError, InvalidComponentError, InvalidConfigurationError, InvalidMessageError, InvalidEventError, InvalidSessionError, SessionStateError, MemoryValueError, NativeProtocolError, HostOperationError, MissingChatResponseError, DeliveryError, HookError, ExecutionAbortedError, InternalError, errorClasses$1, ERROR_CODES;
72
72
  var init_catalog = __esmMin((() => {
73
73
  ERROR_BRAND = Symbol.for("llmz.error.v1");
74
74
  LLMzError = class extends Error {
@@ -358,6 +358,13 @@ var init_catalog = __esmMin((() => {
358
358
  super(message, false, options);
359
359
  }
360
360
  };
361
+ MissingChatResponseError = class extends LLMzError {
362
+ static is = (value) => isLLMzError(value, "MISSING_CHAT_RESPONSE");
363
+ code = "MISSING_CHAT_RESPONSE";
364
+ constructor(message, options) {
365
+ super(message, false, options);
366
+ }
367
+ };
361
368
  DeliveryError = class extends LLMzError {
362
369
  static is = (value) => isLLMzError(value, "DELIVERY_FAILED");
363
370
  code = "DELIVERY_FAILED";
@@ -418,6 +425,7 @@ var init_catalog = __esmMin((() => {
418
425
  NativeProtocolError,
419
426
  HostOperationError,
420
427
  DeliveryError,
428
+ MissingChatResponseError,
421
429
  HookError,
422
430
  ExecutionAbortedError,
423
431
  InternalError
@@ -454,6 +462,7 @@ var init_catalog = __esmMin((() => {
454
462
  "INVALID_NATIVE_CALL",
455
463
  "HOST_OPERATION_FAILED",
456
464
  "DELIVERY_FAILED",
465
+ "MISSING_CHAT_RESPONSE",
457
466
  "HOOK_FAILED",
458
467
  "EXECUTION_ABORTED",
459
468
  "INTERNAL_ERROR"
@@ -6175,7 +6184,7 @@ function createInspector(onInspect) {
6175
6184
  if (onInspect) try {
6176
6185
  const budget = resolveInspectionBudget(value, formatting);
6177
6186
  const output = onInspect(Object.freeze({
6178
- value: snapshot(value),
6187
+ value: snapshotInspectionValue(value),
6179
6188
  purpose: options.purpose,
6180
6189
  maxTokens: budget.tokens,
6181
6190
  preserve: budget.preserve,
@@ -6187,12 +6196,13 @@ function createInspector(onInspect) {
6187
6196
  return inspect(value, void 0, formatting);
6188
6197
  };
6189
6198
  }
6190
- function snapshot(value, copies = /* @__PURE__ */ new WeakMap()) {
6199
+ /** Isolate evidence from subsequent guest mutations without invoking getters. */
6200
+ function snapshotInspectionValue(value, copies = /* @__PURE__ */ new WeakMap()) {
6191
6201
  if (!value || typeof value !== "object") return typeof value === "function" ? "[Function]" : value;
6192
6202
  if (copies.has(value)) return copies.get(value);
6193
6203
  if (isTruncated(value)) {
6194
6204
  copies.set(value, "[Circular]");
6195
- const copy = snapshot(value.value, copies);
6205
+ const copy = snapshotInspectionValue(value.value, copies);
6196
6206
  copies.set(value, copy);
6197
6207
  return copy;
6198
6208
  }
@@ -6203,7 +6213,7 @@ function snapshot(value, copies = /* @__PURE__ */ new WeakMap()) {
6203
6213
  if (Array.isArray(value) && key === "length") continue;
6204
6214
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
6205
6215
  if (descriptor) Object.defineProperty(copy, key, {
6206
- value: "value" in descriptor ? snapshot(descriptor.value, copies) : "[Getter]",
6216
+ value: "value" in descriptor ? snapshotInspectionValue(descriptor.value, copies) : "[Getter]",
6207
6217
  enumerable: descriptor.enumerable
6208
6218
  });
6209
6219
  }
@@ -8464,6 +8474,10 @@ function normalizeMessage(message) {
8464
8474
  validateInputMessage(normalized);
8465
8475
  return normalized;
8466
8476
  }
8477
+ /** Only actual participant content counts as speech, including image/audio-only messages. */
8478
+ function inputSpeaker(message) {
8479
+ return (Array.isArray(message.content) ? message.content.some((part) => part.type !== "text" || Boolean(part.text?.trim())) : Boolean(message.content?.trim())) && (message.role === "user" || message.role === "assistant") ? message.role : null;
8480
+ }
8467
8481
  function createAssistantMessage(response) {
8468
8482
  if (response.assistantMessage) return asSessionMessage(response.assistantMessage);
8469
8483
  const message = {
@@ -8635,6 +8649,7 @@ function validateRestoredHistory(state) {
8635
8649
  if (!Array.isArray(state.groups) || !Array.isArray(state.pendingInputs)) throw new InvalidSessionError("Session history and queued inputs must be arrays.");
8636
8650
  if (!Number.isSafeInteger(state.turn) || state.turn < 0 || !Number.isSafeInteger(state.iteration) || state.iteration < 0) throw new InvalidSessionError("Session turn and iteration counters must be non-negative safe integers");
8637
8651
  if (typeof state.activeTurn !== "boolean" || state.activeTurn && (!state.turn || !state.turnId)) throw new InvalidSessionError("Session processing state must identify an active turn.");
8652
+ if (state.lastSpeaker !== void 0 && state.lastSpeaker !== null && !["user", "assistant"].includes(state.lastSpeaker)) throw new InvalidSessionError("Session last speaker must be user, assistant, or null.");
8638
8653
  const groupIds = /* @__PURE__ */ new Set();
8639
8654
  const callIds = /* @__PURE__ */ new Set();
8640
8655
  let previousIteration = 0;
@@ -8749,6 +8764,7 @@ var init_session = __esmMin((() => {
8749
8764
  #pendingInputs = [];
8750
8765
  #activeTurn = false;
8751
8766
  #locked = false;
8767
+ #lastSpeaker = null;
8752
8768
  constructor(options = {}) {
8753
8769
  this.id = `session_${ulid()}`;
8754
8770
  this.memory = new Memory({
@@ -8773,6 +8789,15 @@ var init_session = __esmMin((() => {
8773
8789
  get transcript() {
8774
8790
  return structuredClone([...this.#allGroups().flatMap((group) => group.source ? [group.source] : group.messages), ...this.#pendingInputs.map((input) => input.source ?? input.message)]);
8775
8791
  }
8792
+ /** Last participant who spoke in the active history. Events and queued inputs do not count. */
8793
+ get lastSpeaker() {
8794
+ return this.#lastSpeaker;
8795
+ }
8796
+ /** Record acknowledged text or component delivery, never generation alone. */
8797
+ recordAssistantDelivery(iterationId) {
8798
+ this.#getActiveIteration(iterationId);
8799
+ this.#lastSpeaker = "assistant";
8800
+ }
8776
8801
  get pendingMessages() {
8777
8802
  return structuredClone(this.#pendingInputs.map((input) => input.message));
8778
8803
  }
@@ -8861,12 +8886,15 @@ var init_session = __esmMin((() => {
8861
8886
  this.#turn++;
8862
8887
  this.#turnId = `turn_${ulid()}`;
8863
8888
  this.#activeTurn = true;
8864
- for (const input of this.#pendingInputs) this.#groups.push({
8865
- id: input.id,
8866
- turn: this.#turn,
8867
- messages: [input.message],
8868
- ...input.source ? { source: input.source } : {}
8869
- });
8889
+ for (const input of this.#pendingInputs) {
8890
+ if (!input.source) this.#lastSpeaker = inputSpeaker(input.message) ?? this.#lastSpeaker;
8891
+ this.#groups.push({
8892
+ id: input.id,
8893
+ turn: this.#turn,
8894
+ messages: [input.message],
8895
+ ...input.source ? { source: input.source } : {}
8896
+ });
8897
+ }
8870
8898
  this.#pendingInputs = [];
8871
8899
  }
8872
8900
  /** Mark the active input batch complete without consuming newly queued input. */
@@ -9167,6 +9195,7 @@ var init_session = __esmMin((() => {
9167
9195
  groups: this.#groups.map(serializeGroup),
9168
9196
  memory: this.memory.serialize(),
9169
9197
  pendingInputs: structuredClone(this.#pendingInputs),
9198
+ lastSpeaker: this.#lastSpeaker,
9170
9199
  activeTurn: this.#activeTurn,
9171
9200
  latestResultId: this.#latestResultId,
9172
9201
  compaction
@@ -9185,6 +9214,7 @@ var init_session = __esmMin((() => {
9185
9214
  session.#groups = state.groups.map(restoreGroup);
9186
9215
  session.#pendingInputs = structuredClone(state.pendingInputs);
9187
9216
  session.#activeTurn = state.activeTurn;
9217
+ session.#lastSpeaker = state.lastSpeaker ?? null;
9188
9218
  session.#latestResultId = state.latestResultId;
9189
9219
  Object.defineProperty(session, "memory", {
9190
9220
  value: Memory.restore(state.memory, () => session.#resultBytes()),
@@ -9703,7 +9733,7 @@ function runtimeRules(props, chat) {
9703
9733
  }
9704
9734
  rules.push("Execution may overlap the remaining response stream. The runtime waits for the stream, code, and queued message deliveries before the next response.", "# Memory", "Declare retained variables at top level with const or let. Bare assignment only updates a variable already declared in this program or listed in Memory. Named variables survive transcript compaction; assignment age records when they were set, not when the underlying data was fetched.", "Object properties are immutable snapshots. Replace a writable property completely, such as account.profile = { ...account.profile, age: 42 }; nested edits are forbidden and replacements are schema-validated.", "$return is the latest successful inspection result. $iterations is read-only retained history, newest first: [0] is the last settled iteration and [1] the preceding one. The current execution is not included. Entries without results still occupy indexes; check hasResult. Compaction prunes automatic results. Assign useful data to named variables to retain it independently.", "# Recovery", "Execution reports identify completed actions, inspected values, memory changes, and errors. Reuse preserved variables and completed actions; retry only failed work. Build completion payloads from those values, not by calling successful tools again. An inspection has already run the recorded calls; it is not a dry run.", "An operational error does not by itself finish the task. Inspect the actual error, repair its cause when safe and authorized, then retry. Unchanged retries do not establish that recovery is exhausted. If another response must diagnose the error, return inspect({ errors, completed }) or let the error reach the runtime. Honor task instructions defining terminal failure outcomes.");
9705
9735
  if (hasExits) rules.push("Use a failure exit only when recovery is unavailable, unsafe, forbidden, its budget is exhausted, or task instructions make the failure terminal. Do not select failure in advance for an unseen error. Do not turn an unexpected exception or rejected Promise.allSettled entry directly into failure completion: first expose it with inspect or let it reach the runtime, then diagnose the actual cause and available recovery tools. Explicit task instructions may define an error as terminal.");
9706
- rules.push("A thinking interruption stops the current program and requests another model response. Statements after the interruption have not run; continue with a new program without repeating completed actions.", "Respect tool-attempt limits separately from the model-response budget. An empty successful inspection result is not a failure.");
9736
+ rules.push("A tool may request forced inspection through ThinkSignal. The tool succeeded and its context is returned to JavaScript. Other calls and assignments continue normally. At the end of the program, forced inspection takes precedence over completion: review the reported results as an inspect() call, then continue from retained values without repeating successful calls.", "Respect tool-attempt limits separately from the model-response budget. An empty successful inspection result is not a failure.");
9707
9737
  return rules.join("\n\n");
9708
9738
  }
9709
9739
  async function describeRuntimeAPI(props, chat) {
@@ -13901,10 +13931,11 @@ var init_errors = __esmMin((() => {
13901
13931
  }));
13902
13932
  //#endregion
13903
13933
  //#region src/vm/types.ts
13904
- var VM_PROGRAM_COMPLETE, VM_TERMINATION, VM_ON_ERROR;
13934
+ var VM_PROGRAM_COMPLETE, VM_TERMINATION, VM_CALL_SITE, VM_ON_ERROR;
13905
13935
  var init_types = __esmMin((() => {
13906
13936
  VM_PROGRAM_COMPLETE = Symbol("llmz.programComplete");
13907
13937
  VM_TERMINATION = Symbol("llmz.termination");
13938
+ VM_CALL_SITE = Symbol("llmz.callSite");
13908
13939
  VM_ON_ERROR = Symbol("llmz.onError");
13909
13940
  }));
13910
13941
  //#endregion
@@ -14043,6 +14074,15 @@ var init_node = __esmMin((() => {
14043
14074
  async execute(ctx) {
14044
14075
  const { transformed, consumer, context, traces, recordTrace, code, lines_executed, variables } = ctx;
14045
14076
  const state = instrumentContext(context, transformed, recordTrace, variables, lines_executed, consumer, findUserCodeStartLine(transformed), ctx.memoryNames);
14077
+ context[VM_CALL_SITE] = () => {
14078
+ const frame = (/* @__PURE__ */ new Error()).stack?.match(/<anonymous>:(\d+):(\d+)/);
14079
+ if (!frame) return;
14080
+ const position = consumer.originalPositionFor({
14081
+ line: Number(frame[1]) - 1,
14082
+ column: Number(frame[2])
14083
+ });
14084
+ return position.line ? Math.max(1, position.line - 3) : void 0;
14085
+ };
14046
14086
  const AsyncFunction = async function() {}.constructor;
14047
14087
  return finalizeMemoryCapture(await (async () => {
14048
14088
  const descriptors = Object.getOwnPropertyDescriptors(context);
@@ -14497,6 +14537,19 @@ var init_quickjs = __esmMin((() => {
14497
14537
  const disposeIfNeeded = (handle) => {
14498
14538
  if (handle !== vm.true && handle !== vm.false && handle !== vm.null && handle !== vm.undefined) handle.dispose();
14499
14539
  };
14540
+ context[VM_CALL_SITE] = () => {
14541
+ const stack = vm.evalCode("new Error().stack");
14542
+ if ("error" in stack) {
14543
+ stack.error?.dispose();
14544
+ return;
14545
+ }
14546
+ try {
14547
+ const frame = vm.getString(stack.value).match(/<quickjs>:(\d+)/);
14548
+ return frame ? Math.max(1, Number(frame[1]) - 9 - userCodeStartLine + 1) : void 0;
14549
+ } finally {
14550
+ stack.value.dispose();
14551
+ }
14552
+ };
14500
14553
  const bridgeFunction = (fn, _fnName = "anonymous") => {
14501
14554
  return (...argHandles) => {
14502
14555
  const args = argHandles.map((h) => vm.dump(h));
@@ -14991,10 +15044,7 @@ var init_execution_activity = __esmMin((() => {
14991
15044
  //#region src/runtime/execution-diagnostics.ts
14992
15045
  /** XML-style boundaries keep diagnostic text separate from recovery instructions. */
14993
15046
  function reportSection(name, content, maxTokens = 2e3, options = {}) {
14994
- const escape = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;");
14995
- const heading = options.heading ? `${escape(options.heading)}\n` : "";
14996
- const body = limitInspectionOutput(content, maxTokens, false, options.preserve ?? "top");
14997
- return `<${name}>\n${heading}${/[<&]|\]\]>/.test(body) ? `<![CDATA[\n${body.replaceAll("]]>", "]]]]><![CDATA[>")}\n]]>` : body}\n</${name}>`;
15047
+ return `<${name}>\n${options.heading ? `${options.heading}\n` : ""}${limitInspectionOutput(content, maxTokens, false, options.preserve ?? "top")}\n</${name}>`;
14998
15048
  }
14999
15049
  /** Show the specific failure and guest source, never the host's implementation stack. */
15000
15050
  function renderExecutionDiagnostics(error, message, inspector, identity) {
@@ -15039,6 +15089,56 @@ var init_execution_diagnostics = __esmMin((() => {
15039
15089
  init_inspect();
15040
15090
  }));
15041
15091
  //#endregion
15092
+ //#region src/runtime/forced-inspection.ts
15093
+ /** Keep attribution and content together, using the same display budgets as inspect(). */
15094
+ function renderForcedInspection(inspections, inspector, identity, maxTokens, policies) {
15095
+ return [
15096
+ "<forced_inspection>",
15097
+ "Forced inspection: the tools listed below completed successfully and requested review of their results.",
15098
+ "This is the equivalent of an inspect() call. Do not repeat these tool calls; use the results below and retained variables.",
15099
+ "No exit was applied. Review the evidence before continuing or completing the task.",
15100
+ "",
15101
+ "Tools requesting inspection (lines refer to the executed JavaScript):",
15102
+ ...inspections.map((entry) => {
15103
+ const entryIdentity = {
15104
+ ...identity,
15105
+ tool: entry.tool
15106
+ };
15107
+ const name = inspector(entry.tool, {
15108
+ purpose: "name",
15109
+ maxTokens: 80,
15110
+ identity: entryIdentity
15111
+ });
15112
+ const reason = inspector(entry.reason, {
15113
+ purpose: "error",
15114
+ maxTokens: 1e3,
15115
+ identity: entryIdentity
15116
+ });
15117
+ const budget = resolveInspectionBudget(entry.value, {
15118
+ tokens: maxTokens,
15119
+ policies
15120
+ });
15121
+ const result = inspector(entry.value, {
15122
+ purpose: "result",
15123
+ maxTokens,
15124
+ policies,
15125
+ identity: entryIdentity
15126
+ });
15127
+ return [
15128
+ `<tool name="${name}" line="${entry.line ?? "unknown"}">`,
15129
+ `<reason>${reason}</reason>`,
15130
+ reportSection("result", result, budget.tokens, { preserve: budget.preserve }),
15131
+ "</tool>"
15132
+ ].join("\n");
15133
+ }),
15134
+ "</forced_inspection>"
15135
+ ].join("\n");
15136
+ }
15137
+ var init_forced_inspection = __esmMin((() => {
15138
+ init_inspect();
15139
+ init_execution_diagnostics();
15140
+ }));
15141
+ //#endregion
15042
15142
  //#region src/runtime/execution-report.ts
15043
15143
  /** Format one settled outcome; execution is responsible for deciding what happened. */
15044
15144
  function renderExecutionReport({ outcome, activity, memory, source, requiresExit = false, iteration, identity = { iteration }, inspector = createInspector(), maxTokens = DEFAULT_TOOL_RESULT_MAX_TOKENS, policies }) {
@@ -15068,6 +15168,13 @@ function renderExecutionReport({ outcome, activity, memory, source, requiresExit
15068
15168
  });
15069
15169
  };
15070
15170
  const sections = [renderStatus(outcome, memory)];
15171
+ if (outcome.type === "inspect" && outcome.forcedInspections?.length) {
15172
+ sections.push(renderForcedInspection(outcome.forcedInspections, inspector, identity, maxTokens, policies));
15173
+ if (outcome.downstreamError) {
15174
+ sections.push(...renderExecutionDiagnostics(outcome.downstreamError, getErrorMessage(outcome.downstreamError), inspector, identity));
15175
+ sections.push(reportSection("recovery", "A later operation failed. The tool results above were successfully retrieved; inspect them and repair only the failed work. Do not repeat successful calls."));
15176
+ }
15177
+ }
15071
15178
  if (outcome.type === "error") {
15072
15179
  sections.push(...renderExecutionDiagnostics(outcome.error, outcome.message, inspector, identity));
15073
15180
  sections.push(reportSection("recovery", [
@@ -15100,7 +15207,7 @@ function renderExecutionReport({ outcome, activity, memory, source, requiresExit
15100
15207
  if (memory.unavailable.length > failures.length) entries.push(`- ${memory.unavailable.length - failures.length} additional values could not be retained.`);
15101
15208
  sections.push(reportSection("memory_errors", `Memory errors\n${entries.join("\n")}`));
15102
15209
  }
15103
- if (outcome.type === "inspect") {
15210
+ if (outcome.type === "inspect" && outcome.forcedInspections?.length && (!outcome.explicit || outcome.forcedInspections.some((entry) => isEqual(entry.value, outcome.value)))) {} else if (outcome.type === "inspect") {
15104
15211
  const heading = outcome.explicit ? "inspect() result" : "Result";
15105
15212
  sections.push(outcome.available ? resultSection("result", heading, outcome.value) : reportSection("result", "Unavailable; see memory errors above.", maxTokens, { heading }));
15106
15213
  } else if (outcome.type === "exit") sections.push(resultSection("completion", `Completion\nExit "${outcome.name}" completed.`, outcome.value === void 0 ? "" : outcome.value));
@@ -15149,12 +15256,15 @@ function renderChanges(label, changes, name) {
15149
15256
  }
15150
15257
  var MAX_REPORTED_CHANGES, MAX_OVERRIDE_SOURCE_LENGTH;
15151
15258
  var init_execution_report = __esmMin((() => {
15259
+ init_lodash();
15152
15260
  init_errors$1();
15153
15261
  init_inspect();
15154
15262
  init_inspection();
15155
15263
  init_truncate();
15264
+ init_utils$1();
15156
15265
  init_execution_activity();
15157
15266
  init_execution_diagnostics();
15267
+ init_forced_inspection();
15158
15268
  MAX_REPORTED_CHANGES = 40;
15159
15269
  MAX_OVERRIDE_SOURCE_LENGTH = 3e3;
15160
15270
  }));
@@ -15650,6 +15760,7 @@ var init_inspection_values = __esmMin((() => {
15650
15760
  function createJavaScriptApi({ iteration, components, exits, deliver, signal, onError }) {
15651
15761
  const decisions = /* @__PURE__ */ new Map();
15652
15762
  const pending = /* @__PURE__ */ new Set();
15763
+ const inspections = [];
15653
15764
  let open = true;
15654
15765
  let outstanding = [];
15655
15766
  let delivery;
@@ -15785,6 +15896,10 @@ function createJavaScriptApi({ iteration, components, exits, deliver, signal, on
15785
15896
  isReceipt: (value) => resolve(value) !== void 0,
15786
15897
  getTerminalOutcome: () => terminalOutcome,
15787
15898
  getInterruption: () => interruption,
15899
+ requestInspection: (inspection) => {
15900
+ inspections.push(inspection);
15901
+ },
15902
+ getForcedInspections: () => inspections,
15788
15903
  throwIfTerminated,
15789
15904
  track,
15790
15905
  assertOpen,
@@ -15824,8 +15939,8 @@ var init_utils = __esmMin((() => {
15824
15939
  }));
15825
15940
  //#endregion
15826
15941
  //#region src/runtime/tool-wrapper.ts
15827
- function wrapTool({ tool, object, iteration, beforeHook, afterHook, onTruncation, onResult, controller }) {
15828
- return async function(input) {
15942
+ function wrapTool({ tool, object, iteration, beforeHook, afterHook, onTruncation, onResult, controller, onThink }) {
15943
+ return async function(input, line) {
15829
15944
  controller.signal.throwIfAborted();
15830
15945
  const toolCallId = `tcall_${ulid()}`;
15831
15946
  const originalInput = input;
@@ -15844,7 +15959,7 @@ function wrapTool({ tool, object, iteration, beforeHook, afterHook, onTruncation
15844
15959
  let output;
15845
15960
  let error;
15846
15961
  let success = true;
15847
- let signalToThrow;
15962
+ const inspections = [];
15848
15963
  const pushToolCallTrace = () => {
15849
15964
  iteration.recordTrace({
15850
15965
  type: "tool_call",
@@ -15860,32 +15975,21 @@ function wrapTool({ tool, object, iteration, beforeHook, afterHook, onTruncation
15860
15975
  success
15861
15976
  });
15862
15977
  };
15863
- const handleSignals = async (err) => {
15864
- if (output === err) return true;
15865
- if (ThinkSignal.is(err)) {
15866
- signalToThrow = err;
15867
- iteration.recordTrace({
15868
- type: "think_signal",
15869
- started_at: Date.now(),
15870
- line: 0,
15871
- ended_at: Date.now()
15872
- });
15873
- success = true;
15874
- output = err;
15875
- const afterRes = await callHook(() => afterHook?.({
15876
- iteration,
15877
- tool,
15878
- input: originalInput,
15879
- output,
15880
- controller,
15881
- object,
15882
- toolCallId,
15883
- nativeCallId: iteration.nativeCallId
15884
- }));
15885
- if (typeof afterRes?.output !== "undefined") output = afterRes.output;
15886
- return true;
15887
- }
15888
- return false;
15978
+ const unwrapSignal = (value) => {
15979
+ if (!ThinkSignal.is(value)) return value;
15980
+ const context = value.context;
15981
+ const output = unwrapTruncated(context);
15982
+ if (isTruncated(context)) onTruncation?.(output, context.$$truncate);
15983
+ const inspection = {
15984
+ tool: object ? `${object}.${tool.name}` : tool.name,
15985
+ toolCallId,
15986
+ line,
15987
+ reason: value.reason,
15988
+ value: output,
15989
+ metadata: value.metadata
15990
+ };
15991
+ inspections.push(inspection);
15992
+ return output;
15889
15993
  };
15890
15994
  try {
15891
15995
  const beforeRes = await callHook(() => beforeHook?.({
@@ -15897,18 +16001,25 @@ function wrapTool({ tool, object, iteration, beforeHook, afterHook, onTruncation
15897
16001
  toolCallId,
15898
16002
  nativeCallId: iteration.nativeCallId
15899
16003
  }));
16004
+ if (ThinkSignal.is(beforeRes)) throw new HookError("Tool hooks cannot request inspection with ThinkSignal. Return it from the tool handler instead.");
15900
16005
  if (typeof beforeRes?.input !== "undefined") effectiveInput = beforeRes.input;
15901
16006
  controller.signal.throwIfAborted();
15902
- output = await tool.execute(effectiveInput, {
15903
- callId: toolCallId,
15904
- iterationId: iteration.id,
15905
- nativeCallId: iteration.nativeCallId,
15906
- onTruncation,
15907
- onInput: (parsed) => {
15908
- controller.signal.throwIfAborted();
15909
- if (effectiveInput === originalInput) reportedInput = parsed;
15910
- }
15911
- });
16007
+ try {
16008
+ output = await tool.execute(effectiveInput, {
16009
+ callId: toolCallId,
16010
+ iterationId: iteration.id,
16011
+ nativeCallId: iteration.nativeCallId,
16012
+ onTruncation,
16013
+ onInput: (parsed) => {
16014
+ controller.signal.throwIfAborted();
16015
+ if (effectiveInput === originalInput) reportedInput = parsed;
16016
+ }
16017
+ });
16018
+ } catch (error) {
16019
+ if (!ThinkSignal.is(error)) throw error;
16020
+ output = error;
16021
+ }
16022
+ output = unwrapSignal(output);
15912
16023
  const afterRes = await callHook(() => afterHook?.({
15913
16024
  iteration,
15914
16025
  tool,
@@ -15919,19 +16030,39 @@ function wrapTool({ tool, object, iteration, beforeHook, afterHook, onTruncation
15919
16030
  toolCallId,
15920
16031
  nativeCallId: iteration.nativeCallId
15921
16032
  }));
16033
+ if (ThinkSignal.is(afterRes) || ThinkSignal.is(afterRes?.output)) throw new HookError("Tool hooks cannot request inspection with ThinkSignal. Return it from the tool handler instead.");
15922
16034
  if (typeof afterRes?.output !== "undefined") output = afterRes.output;
16035
+ controller.signal.throwIfAborted();
15923
16036
  } catch (err) {
15924
- if (!await handleSignals(err)) {
15925
- success = false;
15926
- error = isLLMzError(err) ? err : new ToolExecutionError(tool.name, err);
15927
- iteration.recordError(error);
15928
- }
16037
+ success = false;
16038
+ output = void 0;
16039
+ if (ThinkSignal.is(err)) error = new HookError("Tool hooks cannot request inspection with ThinkSignal. Return it from the tool handler instead.");
16040
+ else error = isLLMzError(err) ? err : new ToolExecutionError(tool.name, err);
16041
+ iteration.recordError(error);
15929
16042
  } finally {
15930
16043
  clearTimeout(alertSlowTool);
16044
+ for (const inspection of success ? inspections : []) {
16045
+ const value = snapshotInspectionValue(output);
16046
+ onThink?.({
16047
+ ...inspection,
16048
+ value
16049
+ });
16050
+ iteration.recordTrace({
16051
+ type: "think_signal",
16052
+ started_at: Date.now(),
16053
+ ended_at: Date.now(),
16054
+ line: line ?? 0,
16055
+ tool_name: tool.name,
16056
+ tool_call_id: toolCallId,
16057
+ object,
16058
+ reason: inspection.reason,
16059
+ context: value,
16060
+ metadata: inspection.metadata
16061
+ });
16062
+ }
15931
16063
  pushToolCallTrace();
15932
16064
  }
15933
16065
  if (!success) throw error;
15934
- if (signalToThrow) throw signalToThrow;
15935
16066
  onResult?.(output);
15936
16067
  return output;
15937
16068
  };
@@ -15940,6 +16071,8 @@ var SLOW_TOOL_WARNING;
15940
16071
  var init_tool_wrapper = __esmMin((() => {
15941
16072
  init_hooks();
15942
16073
  init_errors$1();
16074
+ init_inspection();
16075
+ init_truncate();
15943
16076
  SLOW_TOOL_WARNING = ms("15s");
15944
16077
  }));
15945
16078
  //#endregion
@@ -16046,9 +16179,13 @@ var init_vm_context = __esmMin((() => {
16046
16179
  afterHook: onAfterTool,
16047
16180
  onTruncation,
16048
16181
  onResult: onToolResult,
16049
- controller
16182
+ controller,
16183
+ onThink: javascriptApi?.requestInspection
16050
16184
  });
16051
- instance[tool.name] = javascriptApi ? (input) => javascriptApi.track(() => wrapped(input)) : wrapped;
16185
+ instance[tool.name] = javascriptApi ? (input) => {
16186
+ const line = vmContext[VM_CALL_SITE]?.();
16187
+ return javascriptApi.track(() => wrapped(input, line));
16188
+ } : wrapped;
16052
16189
  }
16053
16190
  vmContext[obj.name] = withMissingMember(instance, (name) => {
16054
16191
  const error = new UnknownToolError(`${obj.name}.${name}`, (obj.tools ?? []).map((tool) => `${obj.name}.${tool.name}`));
@@ -16064,9 +16201,13 @@ var init_vm_context = __esmMin((() => {
16064
16201
  afterHook: onAfterTool,
16065
16202
  onTruncation,
16066
16203
  onResult: onToolResult,
16067
- controller
16204
+ controller,
16205
+ onThink: javascriptApi?.requestInspection
16068
16206
  });
16069
- const callable = javascriptApi ? (input) => javascriptApi.track(() => wrapped(input)) : wrapped;
16207
+ const callable = javascriptApi ? (input) => {
16208
+ const line = vmContext[VM_CALL_SITE]?.();
16209
+ return javascriptApi.track(() => wrapped(input, line));
16210
+ } : wrapped;
16070
16211
  for (const key of [tool.name, ...tool.aliases ?? []]) vmContext[key] = callable;
16071
16212
  }
16072
16213
  return vmContext;
@@ -16100,6 +16241,7 @@ async function executeContextInternal(props) {
16100
16241
  try {
16101
16242
  if ("snapshot" in props) throw new InvalidConfigurationError("Snapshots and external pause/resume are no longer supported. Use a session for conversation history and memory.");
16102
16243
  if ("messages" in props) throw new InvalidConfigurationError("Append input with session.append(message) before calling execute(). execute.messages is no longer supported.");
16244
+ if (props.options?.requireChatResponse !== void 0 && typeof props.options.requireChatResponse !== "boolean") throw new InvalidConfigurationError("Invalid requireChatResponse. Expected a boolean.");
16103
16245
  release = ctx.session.acquire();
16104
16246
  prepareSession(ctx);
16105
16247
  const client = props.client ?? new Client();
@@ -16270,18 +16412,19 @@ async function rejectNativeBatch({ ctx, iteration }, generated, errors) {
16270
16412
  }
16271
16413
  async function deliverAssistantText(state, generated) {
16272
16414
  const { ctx, iteration } = state;
16273
- if (!generated.output.trim() || !ctx.chat) return;
16415
+ if (!generated.output.trim() || !ctx.chat || !iteration.response?.handler) return;
16274
16416
  const startedAt = Date.now();
16275
16417
  const message = {
16276
16418
  type: "text",
16277
16419
  text: generated.output
16278
16420
  };
16279
16421
  try {
16280
- await iteration.response?.handler?.(generated.output.trim(), generated.messageMetadata);
16422
+ await iteration.response.handler(generated.output.trim(), generated.messageMetadata);
16281
16423
  } catch (cause) {
16282
16424
  if (isCriticalError(cause)) throw cause;
16283
16425
  throw new DeliveryError(`Assistant response delivery failed: ${getErrorMessage(cause)}`, { cause });
16284
16426
  }
16427
+ ctx.session.recordAssistantDelivery(iteration.id);
16285
16428
  iteration.recordTrace({
16286
16429
  type: "message_delivery",
16287
16430
  value: message,
@@ -16319,6 +16462,31 @@ async function settleJavaScriptCall(state, execution) {
16319
16462
  inspected: outcome?.type === "inspect"
16320
16463
  };
16321
16464
  if (outcome?.type === "inspect" && result.success) result.return_value = outcome.value;
16465
+ const forcedInspections = api.getForcedInspections();
16466
+ if (forcedInspections.length && !controller.signal.aborted && !isCriticalError(result.error)) {
16467
+ if (!result.success && result.error) {
16468
+ iteration.recordError(result.error);
16469
+ state.capture.downstreamError = result.error;
16470
+ }
16471
+ state.capture.forcedInspections = forcedInspections;
16472
+ let value = forcedInspections.length === 1 ? forcedInspections[0].value : forcedInspections.map((entry) => entry.value);
16473
+ if (outcome?.type === "inspect") value = outcome.value;
16474
+ state.capture.result = {
16475
+ ...result,
16476
+ success: true,
16477
+ signal: void 0,
16478
+ return_value: value
16479
+ };
16480
+ iteration.end({
16481
+ type: "thinking_requested",
16482
+ thinking_requested: {
16483
+ reason: "Tools completed successfully and requested forced inspection.",
16484
+ variables: value,
16485
+ metadata: forcedInspections.length === 1 ? forcedInspections[0].metadata : void 0
16486
+ }
16487
+ });
16488
+ return;
16489
+ }
16322
16490
  if (outcome?.type === "exit") {
16323
16491
  await completeJavaScriptExit(state, result, outcome);
16324
16492
  return;
@@ -16378,11 +16546,11 @@ function containsDecision(value, api, seen = /* @__PURE__ */ new Set()) {
16378
16546
  return Object.values(value).some((nested) => containsDecision(nested, api, seen));
16379
16547
  }
16380
16548
  async function completeJavaScriptExit(state, result, outcome) {
16381
- const { iteration, controller, props } = state;
16549
+ const { iteration, controller } = state;
16382
16550
  state.memory = commitMemory(state, result, false);
16383
16551
  try {
16384
16552
  controller.signal.throwIfAborted();
16385
- await applyNativeExit(iteration, outcome.exit, outcome.value, controller, props.onExit);
16553
+ await applyNativeExit(state, outcome.exit, outcome.value);
16386
16554
  } catch (error) {
16387
16555
  endFailedIteration(iteration, controller, error);
16388
16556
  }
@@ -16414,6 +16582,7 @@ async function deliverJavaScriptMessages({ ctx, iteration, controller }, message
16414
16582
  });
16415
16583
  throw isCriticalError(error) ? error : new DeliveryError(`Delivery ${message.id} failed: ${getErrorMessage(error)}. Its external outcome is uncertain. Later messages were skipped; earlier acknowledged messages remain delivered.`, { cause: error });
16416
16584
  }
16585
+ ctx.session.recordAssistantDelivery(iteration.id);
16417
16586
  iteration.recordTrace({
16418
16587
  type: "message_delivery",
16419
16588
  value: message.component,
@@ -16426,9 +16595,9 @@ async function deliverJavaScriptMessages({ ctx, iteration, controller }, message
16426
16595
  }
16427
16596
  }
16428
16597
  async function finishNativeResponse(state, generated) {
16429
- const { ctx, iteration, controller, props } = state;
16430
- if (!generated.toolCalls.length && generated.output.trim() && ctx.chat) {
16431
- await applyNativeExit(iteration, ListenExit, {}, controller, props.onExit);
16598
+ const { ctx, iteration } = state;
16599
+ if (!generated.toolCalls.length && ctx.chat && (generated.output.trim() || generated.metadata.stopReason === "stop")) {
16600
+ await applyNativeExit(state, ListenExit, {});
16432
16601
  if (!iteration.hasExited()) ctx.session.appendContext(ctx.inspector(iteration.error ?? "Completion rejected.", {
16433
16602
  purpose: "error",
16434
16603
  maxTokens: 200
@@ -16524,7 +16693,9 @@ function getExecutionOutcome(state, memory) {
16524
16693
  type: "inspect",
16525
16694
  value: capture?.result.success ? capture.result.return_value : void 0,
16526
16695
  available: memory.resultAvailable,
16527
- explicit: capture?.inspected ?? false
16696
+ explicit: capture?.inspected ?? false,
16697
+ forcedInspections: capture?.forcedInspections,
16698
+ downstreamError: capture?.downstreamError
16528
16699
  };
16529
16700
  }
16530
16701
  return {
@@ -16615,9 +16786,11 @@ function getIterationResult(state) {
16615
16786
  }
16616
16787
  if (iteration.status.type === "aborted") return new ErrorExecutionResult(ctx, iteration.error);
16617
16788
  }
16618
- async function applyNativeExit(iteration, exit, value, controller, onExit) {
16789
+ async function applyNativeExit(state, exit, value) {
16790
+ const { ctx, iteration, controller, props } = state;
16619
16791
  try {
16620
- await callHook(() => onExit?.({
16792
+ if (ctx.chat && exit === ListenExit && props.options?.requireChatResponse !== false && ctx.session.lastSpeaker !== "assistant") throw new MissingChatResponseError("The assistant has not delivered a message since the last user message. Send a response using the inspected results before waiting for the user. Do not repeat completed tool calls.");
16793
+ await callHook(() => props.onExit?.({
16621
16794
  exit,
16622
16795
  result: value
16623
16796
  }, controller));
@@ -16875,4 +17048,4 @@ const init = async () => {
16875
17048
  await Promise.resolve().then(() => (init_native(), native_exports));
16876
17049
  };
16877
17050
  //#endregion
16878
- export { AssignmentError, Chat, CitationsManager, CodeExecutionError, CodeFormattingError, CognitiveError, CompactionError, Component, ComponentInputError, DefaultComponents, DefaultExit, DeliveryError, ErrorExecutionResult, ExecutionAbortedError, ExecutionResult, Exit, ExitInputError, HookError, HostOperationError, InternalError, InvalidCodeError, InvalidComponentError, InvalidConfigurationError, InvalidEventError, InvalidExitError, InvalidMessageError, InvalidObjectError, InvalidSessionError, InvalidToolError, LLMzError, ListenExit, LoopExceededError, Memory, MemoryCapacityError, MemoryValueError, NativeProtocolError, ObjectInstance, ObjectPropertyError, ReservedIdentifierError, Session, SessionStateError, SuccessExecutionResult, ThinkSignal, TokenOverflowError, Tool, ToolExecutionError, ToolInputError, UnknownComponentError, UnknownExitError, UnknownToolError, _CustomModelClient, assertValidComponent, configureQuickJS, configureTokenizer, createInspector, execute, getValue, init, inspect, isAnyComponent, isComponent, isCriticalError, isLLMzError, truncate, utils, version };
17051
+ export { AssignmentError, Chat, CitationsManager, CodeExecutionError, CodeFormattingError, CognitiveError, CompactionError, Component, ComponentInputError, DefaultComponents, DefaultExit, DeliveryError, ErrorExecutionResult, ExecutionAbortedError, ExecutionResult, Exit, ExitInputError, HookError, HostOperationError, InternalError, InvalidCodeError, InvalidComponentError, InvalidConfigurationError, InvalidEventError, InvalidExitError, InvalidMessageError, InvalidObjectError, InvalidSessionError, InvalidToolError, LLMzError, ListenExit, LoopExceededError, Memory, MemoryCapacityError, MemoryValueError, MissingChatResponseError, NativeProtocolError, ObjectInstance, ObjectPropertyError, ReservedIdentifierError, Session, SessionStateError, SuccessExecutionResult, ThinkSignal, TokenOverflowError, Tool, ToolExecutionError, ToolInputError, UnknownComponentError, UnknownExitError, UnknownToolError, _CustomModelClient, assertValidComponent, configureQuickJS, configureTokenizer, createInspector, execute, getValue, init, inspect, isAnyComponent, isComponent, isCriticalError, isLLMzError, truncate, utils, version };