@lotics/cli 0.95.1 → 0.96.0

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 (2) hide show
  1. package/dist/src/cli.js +547 -50
  2. package/package.json +1 -1
package/dist/src/cli.js CHANGED
@@ -62044,6 +62044,14 @@ var runtimeKeySchema = zod_default.enum([
62044
62044
  "organization_id",
62045
62045
  "now",
62046
62046
  "change_origin",
62047
+ // PARKED — accepted in stored ASTs, rejected for new saves at parse (see
62048
+ // `PARKED_RUNTIME_KEYS` in walk_workflow_expression.ts). It is the OWNER's
62049
+ // principal (a workflow runs under owner authority), so it can't answer the
62050
+ // caller-authorization question anyone reads it for; the generated `.d.ts`
62051
+ // omits it and the docs point at `current_member_in_any_group` /
62052
+ // `runtime.triggered_by_member_id` instead. The entry stays so any stored
62053
+ // AST keeps validating and executing — drop it only once a prod probe shows
62054
+ // zero references (`workflows.steps_v2::text LIKE '%execution_principal%'`).
62047
62055
  "execution_principal",
62048
62056
  "triggered_by_member_id"
62049
62057
  ]);
@@ -64431,9 +64439,13 @@ for (const [operation, fieldTypes] of Object.entries(OPERATION_FIELD_TYPES)) {
64431
64439
  var AGGREGATABLE_FIELD_TYPES = new Set(FIELD_TYPE_OPERATIONS.keys());
64432
64440
 
64433
64441
  // ../shared/src/chat_models.ts
64434
- var CHAT_MODEL_IDS = ["claude-haiku-4-5", "claude-sonnet-5", "claude-opus-4-8"];
64435
- var LEGACY_CHAT_MODEL_IDS = ["claude-sonnet-4-6"];
64442
+ var CHAT_MODEL_IDS = ["claude-haiku-4-5", "claude-sonnet-5", "claude-opus-5"];
64443
+ var LEGACY_CHAT_MODEL_IDS = [];
64436
64444
  var ACCEPTED_CHAT_MODEL_IDS = [...CHAT_MODEL_IDS, ...LEGACY_CHAT_MODEL_IDS];
64445
+ var UTILITY_MODEL_ID = "claude-haiku-4-5";
64446
+ var PICKER_MODEL_IDS = CHAT_MODEL_IDS.filter(
64447
+ (id) => id !== UTILITY_MODEL_ID
64448
+ );
64437
64449
  var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
64438
64450
 
64439
64451
  // ../shared/src/schemas/apps.ts
@@ -64867,7 +64879,9 @@ var appAgentDeclarationSchema = zod_default.object({
64867
64879
  knowledge_doc_ids: zod_default.array(zod_default.string().min(1)).optional().describe(
64868
64880
  "Knowledge docs the agent may read, validated at declare time against the app owner's `use` access. Small docs are inlined into the agent's system prompt each run; a doc too large to inline requires the code tools (code_exec) in tool_names and is read by staging it into a code run. Omit for an agent that needs no knowledge."
64869
64881
  ),
64870
- model_id: zod_default.string().min(1).describe("Chat model id the agent runs on. Validated against ACCEPTED_CHAT_MODEL_IDS at run time."),
64882
+ model_id: zod_default.string().min(1).optional().describe(
64883
+ "Chat model id the agent runs on, validated against ACCEPTED_CHAT_MODEL_IDS. Omit to follow the platform default chat model, resolved at run time \u2014 the preferred choice: the agent tracks model generations with no per-app rewrite. Pin only a deliberate, tested choice."
64884
+ ),
64871
64885
  effort_level: zod_default.enum(EFFORT_LEVELS).optional().describe(
64872
64886
  "Reasoning depth for adaptive-thinking models \u2014 one of the chosen model's supported levels (validated against model_id at declare time). Omit to use the model default; ignored on models without adaptive thinking."
64873
64887
  ),
@@ -65008,7 +65022,7 @@ var agentStepSchema = stepBaseSchema.extend({
65008
65022
  instructions: zod_default.string().min(1),
65009
65023
  input: zod_default.record(zod_default.string(), toolInputValueSchema),
65010
65024
  tool_names: zod_default.array(zod_default.string().min(1)),
65011
- model_id: zod_default.string().min(1),
65025
+ model_id: zod_default.string().min(1).optional(),
65012
65026
  output: agentOutputSpecSchema
65013
65027
  });
65014
65028
  var breakStepSchema = stepBaseSchema.extend({
@@ -65514,10 +65528,10 @@ function reverse(arr) {
65514
65528
  }
65515
65529
  return [...arr].reverse();
65516
65530
  }
65517
- function slice(arr, start, end) {
65518
- if (arr == null) return [];
65519
- if (!Array.isArray(arr)) {
65520
- throw new Error("slice: expected array, got " + typeof arr);
65531
+ function slice(collection, start, end) {
65532
+ if (collection == null) return [];
65533
+ if (!Array.isArray(collection) && typeof collection !== "string") {
65534
+ throw new Error("slice: expected array or string, got " + typeof collection);
65521
65535
  }
65522
65536
  if (typeof start !== "number") {
65523
65537
  throw new Error("slice: start must be a number");
@@ -65525,7 +65539,7 @@ function slice(arr, start, end) {
65525
65539
  if (end != null && typeof end !== "number") {
65526
65540
  throw new Error("slice: end must be a number");
65527
65541
  }
65528
- return arr.slice(start, end);
65542
+ return collection.slice(start, end);
65529
65543
  }
65530
65544
  function first2(arr) {
65531
65545
  if (arr == null) return void 0;
@@ -65563,17 +65577,17 @@ function nth(arr, index) {
65563
65577
  }
65564
65578
  return arr[index];
65565
65579
  }
65566
- function at(arr, index) {
65567
- if (arr == null) return void 0;
65568
- if (!Array.isArray(arr)) {
65569
- throw new Error("at: expected array, got " + typeof arr);
65580
+ function at(collection, index) {
65581
+ if (collection == null) return void 0;
65582
+ if (!Array.isArray(collection) && typeof collection !== "string") {
65583
+ throw new Error("at: expected array or string, got " + typeof collection);
65570
65584
  }
65571
65585
  if (typeof index !== "number") {
65572
65586
  throw new Error("at: index must be a number");
65573
65587
  }
65574
- const resolved = index < 0 ? arr.length + index : index;
65575
- if (resolved < 0 || resolved >= arr.length) return void 0;
65576
- return arr[resolved];
65588
+ const resolved = index < 0 ? collection.length + index : index;
65589
+ if (resolved < 0 || resolved >= collection.length) return void 0;
65590
+ return collection[resolved];
65577
65591
  }
65578
65592
  function size(arr) {
65579
65593
  if (arr == null) return 0;
@@ -65745,10 +65759,28 @@ function list(...items) {
65745
65759
  }
65746
65760
  return items;
65747
65761
  }
65748
- function concat(...arrays) {
65749
- if (arrays.length === 0) {
65750
- throw new Error("concat: expected at least one array argument");
65762
+ function concat(...values3) {
65763
+ if (values3.length === 0) {
65764
+ throw new Error("concat: expected at least one array or string argument");
65765
+ }
65766
+ if (typeof values3.find((value) => value != null) === "string") {
65767
+ return concatStrings(values3);
65768
+ }
65769
+ return concatArrays(values3);
65770
+ }
65771
+ function concatStrings(values3) {
65772
+ let out = "";
65773
+ for (let i2 = 0; i2 < values3.length; i2++) {
65774
+ const value = values3[i2];
65775
+ if (value == null) continue;
65776
+ if (typeof value !== "string") {
65777
+ throw new Error(`concat: argument ${i2} expected string, got ${typeof value}`);
65778
+ }
65779
+ out += value;
65751
65780
  }
65781
+ return out;
65782
+ }
65783
+ function concatArrays(arrays) {
65752
65784
  let total = 0;
65753
65785
  for (let i2 = 0; i2 < arrays.length; i2++) {
65754
65786
  const arr = arrays[i2];
@@ -67131,6 +67163,18 @@ function isRuntimeKey(name) {
67131
67163
  return RUNTIME_KEYS.has(name);
67132
67164
  }
67133
67165
  var CURRENT_MEMBER_IN_ANY_GROUP = "current_member_in_any_group";
67166
+ var PARKED_RUNTIME_KEYS = /* @__PURE__ */ new Map([
67167
+ [
67168
+ "execution_principal",
67169
+ `runtime.execution_principal is not readable \u2014 a workflow runs under the OWNER's authority, so it never answers "is the person who triggered this allowed?". Authorize the caller with ${CURRENT_MEMBER_IN_ANY_GROUP}(["grp_\u2026"]) or read runtime.triggered_by_member_id.`
67170
+ ]
67171
+ ]);
67172
+ function parkedRuntimeKeyConstruct(key) {
67173
+ return `${key}_read`;
67174
+ }
67175
+ var READABLE_RUNTIME_KEYS = Array.from(RUNTIME_KEYS).filter(
67176
+ (key) => !PARKED_RUNTIME_KEYS.has(key)
67177
+ );
67134
67178
  var INLINE_HELPERS = [
67135
67179
  "formatCurrency",
67136
67180
  "randomNumber",
@@ -67162,11 +67206,22 @@ var HELPER_MENU = [
67162
67206
  `date \u2014 ${Object.keys(getDateExpressionFunctions()).sort().join(", ")}`,
67163
67207
  `other \u2014 ${[...INLINE_HELPERS].sort().join(", ")}`
67164
67208
  ].join("; ");
67209
+ var MAX_INLINE_EXPANSIONS = 1e3;
67210
+ function makeInlineState() {
67211
+ return {
67212
+ args: /* @__PURE__ */ new Map(),
67213
+ stack: [],
67214
+ budget: { remaining: MAX_INLINE_EXPANSIONS },
67215
+ used: /* @__PURE__ */ new Set()
67216
+ };
67217
+ }
67165
67218
  function makeScope(init = {}) {
67166
67219
  return {
67167
67220
  stepIds: init.stepIds ?? /* @__PURE__ */ new Set(),
67168
67221
  foreachBinds: init.foreachBinds ?? [],
67169
67222
  toolNames: init.toolNames ?? /* @__PURE__ */ new Set(),
67223
+ functions: init.functions ?? /* @__PURE__ */ new Map(),
67224
+ inline: init.inline ?? makeInlineState(),
67170
67225
  lambdaParams: init.lambdaParams ?? [],
67171
67226
  loopDepth: init.loopDepth ?? 0,
67172
67227
  lexicalBindings: init.lexicalBindings ?? [/* @__PURE__ */ new Set()],
@@ -67197,7 +67252,7 @@ var CALLBACK_HELPERS = /* @__PURE__ */ new Set([
67197
67252
  "intersectionBy",
67198
67253
  "reduce"
67199
67254
  ]);
67200
- var LAMBDA_POSITION_HINT = `Function expressions are not supported here. Lambdas are legal only INLINE as a callback argument to ${[...CALLBACK_HELPERS].sort().join(" / ")} \u2014 write the lambda directly in the call, e.g. pluck(rows, (r) => r.qty * r.price). Binding one to a name (const f = (x) => ...) or calling one directly is not supported: inline it at each call site.`;
67255
+ var LAMBDA_POSITION_HINT = `Function expressions are not supported here. A lambda \u2014 either \`(x) => <expr>\` or \`function (x) { return <expr>; }\` \u2014 is legal only INLINE as a callback argument to ${[...CALLBACK_HELPERS].sort().join(" / ")}, e.g. pluck(rows, (r) => r.qty * r.price). To NAME reusable logic, declare it at the top level instead: \`function toLine(l = {}) { return { ... }; }\` \u2014 every parameter needs a default, and the body is inlined at each call site. Binding a lambda to a name (const f = (x) => ...) or calling one directly stays unsupported.`;
67201
67256
  var BINARY_OPS = {
67202
67257
  // Both equality forms work as in JS: `==`/`!=` are loose (coercing), `===`/
67203
67258
  // `!==` are strict. The author picks the semantics; we don't second-guess.
@@ -67242,7 +67297,46 @@ var NAMESPACE_HELPERS = /* @__PURE__ */ new Map([
67242
67297
  ["Object", /* @__PURE__ */ new Map([["keys", "keys"], ["values", "values"], ["entries", "entries"]])],
67243
67298
  ["Number", /* @__PURE__ */ new Map([["parseFloat", "toNumber"]])]
67244
67299
  ]);
67300
+ var METHOD_FORM_NAMES = /* @__PURE__ */ new Set([
67301
+ // String.prototype
67302
+ "trim",
67303
+ "toUpperCase",
67304
+ "toLowerCase",
67305
+ "replace",
67306
+ "replaceAll",
67307
+ "split",
67308
+ "substring",
67309
+ "padStart",
67310
+ "padEnd",
67311
+ "startsWith",
67312
+ "endsWith",
67313
+ // String.prototype + Array.prototype
67314
+ "includes",
67315
+ "slice",
67316
+ "at",
67317
+ "concat",
67318
+ // Array.prototype
67319
+ "join",
67320
+ "reverse",
67321
+ "map",
67322
+ "filter",
67323
+ "find",
67324
+ "some",
67325
+ "every",
67326
+ "reduce",
67327
+ // Number.prototype / Object.prototype
67328
+ "toFixed",
67329
+ "toString"
67330
+ ]);
67331
+ var METHOD_FORM_MENU = [...METHOD_FORM_NAMES].sort().join(", ");
67245
67332
  var PROPERTY_HELPERS = /* @__PURE__ */ new Map([["length", "length"]]);
67333
+ var RESERVED_CALLABLE_NAMES = /* @__PURE__ */ new Set([
67334
+ ...HELPER_ALIASES.keys(),
67335
+ ...REJECTED_HELPERS.keys(),
67336
+ ...NAMESPACE_HELPERS.keys(),
67337
+ "Math",
67338
+ "Boolean"
67339
+ ]);
67246
67340
  var MAX_EXPRESSION_DEPTH = 500;
67247
67341
  function walkExpression(node, scope) {
67248
67342
  const depth = scope.depth + 1;
@@ -67298,7 +67392,7 @@ function walkExpressionNode(node, scope) {
67298
67392
  return walkObjectLiteral(node, scope);
67299
67393
  case "ArrowFunctionExpression":
67300
67394
  case "FunctionExpression":
67301
- fail(node, LAMBDA_POSITION_HINT);
67395
+ fail(node, LAMBDA_POSITION_HINT, "lambda_position");
67302
67396
  case "AssignmentExpression":
67303
67397
  fail(node, 'Assignment is only allowed for "const x = await tool(...)" tool-call bindings.');
67304
67398
  case "UpdateExpression":
@@ -67486,10 +67580,14 @@ function walkObjectLiteral(node, scope) {
67486
67580
  function walkHelperCall(node, scope) {
67487
67581
  const callee = node.callee;
67488
67582
  if (callee.type === "ArrowFunctionExpression" || callee.type === "FunctionExpression") {
67489
- fail(callee, LAMBDA_POSITION_HINT);
67583
+ fail(callee, LAMBDA_POSITION_HINT, "lambda_position");
67490
67584
  }
67491
67585
  if (node.type === "OptionalCallExpression" && node.optional === true) {
67492
- fail(node, "Optional calls (`x?.()`) are not supported. Use `?.` on member access only.");
67586
+ fail(
67587
+ node,
67588
+ "Optional calls (`x?.()`) are not supported. Use `?.` on member access only.",
67589
+ "optional_call"
67590
+ );
67493
67591
  }
67494
67592
  if ((callee.type === "MemberExpression" || callee.type === "OptionalMemberExpression") && callee.object.type === "Identifier" && callee.object.name === "Math" && !callee.computed && callee.property.type === "Identifier") {
67495
67593
  const mathFn = callee.property.name;
@@ -67598,6 +67696,17 @@ function isOptionalChainGuard(e) {
67598
67696
  return e.kind === "ternary" && e.then.kind === "lit" && e.then.value === null && e.cond.kind === "call" && e.cond.fn === "isNull";
67599
67697
  }
67600
67698
  function buildHelperCall(node, rawFn, receiver, scope) {
67699
+ const declared = scope.functions.get(rawFn);
67700
+ if (declared) {
67701
+ if (receiver !== null) {
67702
+ fail(
67703
+ node,
67704
+ `"${rawFn}" is a function declared in this workflow \u2014 call it as ${rawFn}(x), not as a method.`,
67705
+ "function_method_form"
67706
+ );
67707
+ }
67708
+ return inlineFunctionCall(node, declared, scope);
67709
+ }
67601
67710
  const rejected = REJECTED_HELPERS.get(rawFn);
67602
67711
  if (rejected) fail(node, rejected.message, rejected.construct);
67603
67712
  const fn = HELPER_ALIASES.get(rawFn) ?? rawFn;
@@ -67608,6 +67717,8 @@ function buildHelperCall(node, rawFn, receiver, scope) {
67608
67717
  fail(node, `${fn} is a reserved identifier and cannot be called.`);
67609
67718
  }
67610
67719
  if (isRuntimeKey(fn) && !isHelper(fn)) {
67720
+ const parked = PARKED_RUNTIME_KEYS.get(fn);
67721
+ if (parked) fail(node, parked, parkedRuntimeKeyConstruct(fn));
67611
67722
  fail(node, `${fn} is a runtime key, not a helper. Read it as runtime.${fn}.`);
67612
67723
  }
67613
67724
  if (!isHelper(fn)) {
@@ -67617,6 +67728,13 @@ function buildHelperCall(node, rawFn, receiver, scope) {
67617
67728
  "unknown_helper"
67618
67729
  );
67619
67730
  }
67731
+ if (receiver !== null && !METHOD_FORM_NAMES.has(rawFn)) {
67732
+ fail(
67733
+ node,
67734
+ `"${rawFn}" has no method form \u2014 call it as ${fn}(x), with the receiver as the first argument. The method form works only for helper names that are also real JS methods: ${METHOD_FORM_MENU}.`,
67735
+ "helper_method_form"
67736
+ );
67737
+ }
67620
67738
  if (fn === "filter") {
67621
67739
  const predNode = node.arguments.length > 0 ? node.arguments[node.arguments.length - 1] : void 0;
67622
67740
  if (predNode && predNode.type === "Identifier" && predNode.name === "Boolean") {
@@ -67643,11 +67761,11 @@ function walkHelperArg(arg, fn, scope) {
67643
67761
  "spread_element"
67644
67762
  );
67645
67763
  }
67646
- if (arg.type === "ArrowFunctionExpression") {
67764
+ if (arg.type === "ArrowFunctionExpression" || arg.type === "FunctionExpression") {
67647
67765
  if (!CALLBACK_HELPERS.has(fn)) {
67648
67766
  fail(
67649
67767
  arg,
67650
- `Arrow functions are not supported as arguments to "${fn}". Callbacks work on ${[...CALLBACK_HELPERS].sort().join(" / ")}.`,
67768
+ `Callback functions are not supported as arguments to "${fn}". Callbacks work on ${[...CALLBACK_HELPERS].sort().join(" / ")}.`,
67651
67769
  "lambda_not_allowed"
67652
67770
  );
67653
67771
  }
@@ -67657,7 +67775,15 @@ function walkHelperArg(arg, fn, scope) {
67657
67775
  }
67658
67776
  function walkLambda(node, scope) {
67659
67777
  if (node.async) {
67660
- fail(node, "Async arrow functions are not supported in callbacks.");
67778
+ fail(node, "Async callbacks are not supported \u2014 a callback body must be a pure, synchronous expression.");
67779
+ }
67780
+ if (node.type === "FunctionExpression") {
67781
+ if (node.generator) {
67782
+ fail(node, "Generator functions (`function*`) are not supported.", "generator_function");
67783
+ }
67784
+ if (node.id) {
67785
+ fail(node.id, `Named function expressions are not supported as callbacks \u2014 drop the name "${node.id.name}".`);
67786
+ }
67661
67787
  }
67662
67788
  const params = [];
67663
67789
  for (const p of node.params) {
@@ -67670,6 +67796,12 @@ function walkLambda(node, scope) {
67670
67796
  if (scope.toolNames.has(p.name) || isHelper(p.name)) {
67671
67797
  fail(p, `Lambda parameter "${p.name}" collides with a tool or helper.`);
67672
67798
  }
67799
+ if (scope.functions.has(p.name)) {
67800
+ fail(p, `Lambda parameter "${p.name}" collides with a function declared in this workflow.`);
67801
+ }
67802
+ if (scope.inline.args.has(p.name)) {
67803
+ fail(p, `Lambda parameter "${p.name}" collides with an enclosing function's parameter.`);
67804
+ }
67673
67805
  if (scope.stepIds.has(p.name)) {
67674
67806
  fail(p, `Lambda parameter "${p.name}" collides with a step id.`);
67675
67807
  }
@@ -67681,20 +67813,177 @@ function walkLambda(node, scope) {
67681
67813
  }
67682
67814
  params.push(p.name);
67683
67815
  }
67684
- if (node.body.type === "BlockStatement") {
67685
- fail(
67686
- node.body,
67687
- "Lambda body must be a single expression \u2014 block bodies (`(x) => { ... }`) are not supported. Fold the branches into one expression: use a ternary (`x => x.qty > 0 ? x.qty : 0`) or nested helper calls.",
67688
- "block_body_lambda"
67689
- );
67690
- }
67816
+ const bodyExpr = lambdaBodyExpression(node);
67691
67817
  const innerScope = {
67692
67818
  ...scope,
67693
67819
  lambdaParams: [...scope.lambdaParams, ...params]
67694
67820
  };
67695
- const body = walkExpression(node.body, innerScope);
67821
+ const body = walkExpression(bodyExpr, innerScope);
67696
67822
  return { kind: "lambda", params, body };
67697
67823
  }
67824
+ function lambdaBodyExpression(node) {
67825
+ if (node.body.type !== "BlockStatement") {
67826
+ return node.body;
67827
+ }
67828
+ const statements = node.body.body;
67829
+ const only = statements.length === 1 ? statements[0] : null;
67830
+ if (only && only.type === "ReturnStatement" && only.argument) {
67831
+ return only.argument;
67832
+ }
67833
+ fail(
67834
+ node.body,
67835
+ "Lambda body must reduce to a single expression \u2014 either `(x) => <expr>` or a block whose only statement is `return <expr>`. Fold the branches into one expression: use a ternary (`x => x.qty > 0 ? x.qty : 0`) or nested helper calls.",
67836
+ "block_body_lambda"
67837
+ );
67838
+ }
67839
+ function failFunctionDeclaration(node, why) {
67840
+ fail(
67841
+ node,
67842
+ `${why} A workflow function must be a single-expression helper declared at the top level: \`function name(p = <default>) { return <expr>; }\` \u2014 it is inlined at every call site.
67843
+ For logic that doesn't fit that shape, choose one:
67844
+ - bind step: \`const tag = record.Score > 80 ? "hot" : "cold";\` \u2014 compute once, reference by name in later steps.
67845
+ - inline expression: write the logic directly at each call site (often shorter than extracting).
67846
+ - separate workflow: factor the steps out and invoke from this workflow via a tool call.`,
67847
+ "function_declaration"
67848
+ );
67849
+ }
67850
+ function failUnusedFunction(node, name) {
67851
+ fail(
67852
+ node,
67853
+ `Function "${name}" is declared but never called. A declaration is inlined at its call sites and erased from the saved workflow, so an uncalled one would disappear with its body never checked \u2014 call it, or delete it.`,
67854
+ "function_unused"
67855
+ );
67856
+ }
67857
+ function collectFunctionDeclarations(body, toolNames) {
67858
+ const functions = /* @__PURE__ */ new Map();
67859
+ for (const stmt of body) {
67860
+ if (stmt.type !== "FunctionDeclaration") continue;
67861
+ if (stmt.async) {
67862
+ failFunctionDeclaration(stmt, "Async functions are not supported.");
67863
+ }
67864
+ if (stmt.generator) {
67865
+ fail(stmt, "Generator functions (`function*`) are not supported.", "generator_function");
67866
+ }
67867
+ if (!stmt.id) {
67868
+ failFunctionDeclaration(stmt, "A function declaration must be named.");
67869
+ }
67870
+ const name = stmt.id.name;
67871
+ assertFunctionName(stmt.id, name, `Function "${name}"`, toolNames);
67872
+ if (functions.has(name)) {
67873
+ fail(stmt.id, `Function "${name}" is already declared in this workflow.`);
67874
+ }
67875
+ const params = [];
67876
+ const seen = /* @__PURE__ */ new Set();
67877
+ for (const p of stmt.params) {
67878
+ if (p.type === "Identifier") {
67879
+ fail(
67880
+ p,
67881
+ `Parameter "${p.name}" of function "${name}" needs a default value \u2014 write \`${p.name} = <default>\`. The subset has no type annotations, so the default is what gives the parameter a type; without it the save-time type check reports an implicit \`any\`.`,
67882
+ "function_param_no_default"
67883
+ );
67884
+ }
67885
+ if (p.type !== "AssignmentPattern" || p.left.type !== "Identifier") {
67886
+ fail(
67887
+ p,
67888
+ `Parameters of function "${name}" must be plain names with a default (\`p = <default>\`) \u2014 destructuring and rest are not supported.`,
67889
+ "function_param_shape"
67890
+ );
67891
+ }
67892
+ const paramName = p.left.name;
67893
+ assertFunctionName(p.left, paramName, `Parameter "${paramName}"`, toolNames);
67894
+ if (seen.has(paramName)) {
67895
+ fail(p.left, `Parameter "${paramName}" of function "${name}" is declared twice.`);
67896
+ }
67897
+ seen.add(paramName);
67898
+ params.push({ name: paramName, defaultNode: p.right, node: p.left });
67899
+ }
67900
+ const statements = stmt.body.body;
67901
+ const only = statements.length === 1 ? statements[0] : null;
67902
+ if (!only || only.type !== "ReturnStatement" || !only.argument) {
67903
+ failFunctionDeclaration(
67904
+ stmt.body,
67905
+ `The body of "${name}" must be exactly one \`return <expression>;\`.`
67906
+ );
67907
+ }
67908
+ functions.set(name, { name, params, body: only.argument, node: stmt });
67909
+ }
67910
+ for (const fn of functions.values()) {
67911
+ for (const p of fn.params) {
67912
+ if (functions.has(p.name)) {
67913
+ fail(p.node, `Parameter "${p.name}" of function "${fn.name}" collides with a function declared in this workflow.`);
67914
+ }
67915
+ }
67916
+ }
67917
+ return functions;
67918
+ }
67919
+ function assertFunctionName(node, name, label, toolNames) {
67920
+ if (RESERVED_ROOTS.has(name)) {
67921
+ fail(node, `${label} uses the reserved identifier "${name}".`);
67922
+ }
67923
+ if (toolNames.has(name)) {
67924
+ fail(node, `${label} collides with the tool "${name}".`);
67925
+ }
67926
+ if (isHelper(name)) {
67927
+ fail(node, `${label} collides with the built-in helper "${name}".`);
67928
+ }
67929
+ if (RESERVED_CALLABLE_NAMES.has(name)) {
67930
+ fail(node, `${label} collides with "${name}", which the subset already gives a meaning.`);
67931
+ }
67932
+ }
67933
+ function inlineFunctionCall(node, decl, scope) {
67934
+ if (scope.inline.stack.includes(decl.name)) {
67935
+ fail(
67936
+ node,
67937
+ `Function "${decl.name}" is recursive (${[...scope.inline.stack, decl.name].join(" \u2192 ")}). Inlined functions cannot recurse \u2014 express the repetition with a loop step or a helper such as reduce.`,
67938
+ "function_recursion"
67939
+ );
67940
+ }
67941
+ if (scope.inline.budget.remaining <= 0) {
67942
+ fail(
67943
+ node,
67944
+ `This workflow expands to too many inlined function calls (max ${MAX_INLINE_EXPANSIONS}). Reduce nesting between functions, or inline the logic by hand.`,
67945
+ "function_expansion_limit"
67946
+ );
67947
+ }
67948
+ scope.inline.budget.remaining -= 1;
67949
+ scope.inline.used.add(decl.name);
67950
+ if (node.arguments.length > decl.params.length) {
67951
+ fail(
67952
+ node,
67953
+ `Function "${decl.name}" takes ${decl.params.length} argument(s), but ${node.arguments.length} were passed.`,
67954
+ "function_arity"
67955
+ );
67956
+ }
67957
+ const emptyScope = functionBodyScope(scope, decl.name, /* @__PURE__ */ new Map());
67958
+ const args = /* @__PURE__ */ new Map();
67959
+ for (let i2 = 0; i2 < decl.params.length; i2++) {
67960
+ const p = decl.params[i2];
67961
+ const argNode = node.arguments[i2];
67962
+ args.set(
67963
+ p.name,
67964
+ argNode === void 0 ? walkExpression(p.defaultNode, emptyScope) : walkHelperArg(argNode, decl.name, scope)
67965
+ );
67966
+ }
67967
+ return walkExpression(decl.body, functionBodyScope(scope, decl.name, args));
67968
+ }
67969
+ function functionBodyScope(scope, name, args) {
67970
+ return {
67971
+ stepIds: /* @__PURE__ */ new Set(),
67972
+ foreachBinds: [],
67973
+ toolNames: scope.toolNames,
67974
+ functions: scope.functions,
67975
+ inline: {
67976
+ args,
67977
+ stack: [...scope.inline.stack, name],
67978
+ budget: scope.inline.budget,
67979
+ used: scope.inline.used
67980
+ },
67981
+ lambdaParams: [],
67982
+ loopDepth: 0,
67983
+ lexicalBindings: [/* @__PURE__ */ new Set()],
67984
+ depth: scope.depth
67985
+ };
67986
+ }
67698
67987
  function flattenChain(node, scope) {
67699
67988
  let cur = node;
67700
67989
  const reverse2 = [];
@@ -67784,7 +68073,30 @@ function walkRead(node, scope) {
67784
68073
  if (chain2.segments.length === 0 && scope.toolNames.has(chain2.rootName)) {
67785
68074
  fail(node, `Tools may only be invoked, not referenced. Write "const x = await ${chain2.rootName}({...})".`);
67786
68075
  }
68076
+ if (scope.functions.has(chain2.rootName)) {
68077
+ fail(
68078
+ node,
68079
+ `Function "${chain2.rootName}" may only be called, not referenced. Write ${chain2.rootName}(...).`,
68080
+ "function_as_value"
68081
+ );
68082
+ }
67787
68083
  const root = chain2.rootName;
68084
+ const inlined = scope.inline.args.get(root);
68085
+ if (inlined !== void 0) {
68086
+ if (chain2.segments.length === 0) return inlined;
68087
+ if (inlined.kind !== "read") {
68088
+ fail(
68089
+ node,
68090
+ `Cannot read a property off parameter "${root}" \u2014 the argument passed at the call site is a computed value, not a record or binding. Bind it first (\`const x = <expr>;\`) and pass the binding.`,
68091
+ "function_arg_not_read"
68092
+ );
68093
+ }
68094
+ const base = inlined;
68095
+ return wrapOptionalGuards(chain2, (segments) => ({
68096
+ ...base,
68097
+ path: [...base.path, ...classifyRecordPath(segments)]
68098
+ }));
68099
+ }
67788
68100
  if (root === "record" || root === "prev_record") {
67789
68101
  return readFromRecord(
67790
68102
  root === "record" ? "trigger_record" : "trigger_record_prev",
@@ -67801,6 +68113,13 @@ function walkRead(node, scope) {
67801
68113
  return readFromRuntime(chain2);
67802
68114
  }
67803
68115
  if (root === "index") {
68116
+ if (scope.inline.stack.length > 0) {
68117
+ fail(
68118
+ node,
68119
+ "index is not readable inside a function \u2014 it belongs to the enclosing loop, so an inlined body would mean something different at each call site. Pass it as a parameter.",
68120
+ "function_reads_index"
68121
+ );
68122
+ }
67804
68123
  if (chain2.segments.length > 0) {
67805
68124
  fail(node, "index has no properties.");
67806
68125
  }
@@ -67829,6 +68148,7 @@ function walkRead(node, scope) {
67829
68148
  "runtime",
67830
68149
  "trigger",
67831
68150
  "index",
68151
+ ...scope.inline.args.keys(),
67832
68152
  ...lexicalNames,
67833
68153
  ...Array.from(scope.lambdaParams),
67834
68154
  ...Array.from(scope.foreachBinds),
@@ -67878,7 +68198,20 @@ function readFromChanges(chain2) {
67878
68198
  return { kind: "read", source: { from: "trigger_changes" }, path: path8 };
67879
68199
  });
67880
68200
  }
68201
+ var TRIGGER_RECORD_VIEWS = /* @__PURE__ */ new Map([
68202
+ ["data", "trigger_record"],
68203
+ ["prev_data", "trigger_record_prev"],
68204
+ ["changes", "trigger_changes"]
68205
+ ]);
67881
68206
  function readFromTrigger(chain2) {
68207
+ const head = chain2.segments[0];
68208
+ const view = head && (head.kind === "dot" || head.kind === "bracket-string") ? TRIGGER_RECORD_VIEWS.get(head.key) : void 0;
68209
+ if (head && view !== void 0) {
68210
+ const rest = chain2.segments.slice(1);
68211
+ const segments = head.optional === true && rest.length > 0 ? [{ ...rest[0], optional: true }, ...rest.slice(1)] : rest;
68212
+ const lowered = { ...chain2, segments };
68213
+ return view === "trigger_changes" ? readFromChanges(lowered) : readFromRecord(view, lowered);
68214
+ }
67882
68215
  return wrapOptionalGuards(chain2, (segments) => {
67883
68216
  const path8 = [];
67884
68217
  for (const seg of segments) {
@@ -67905,10 +68238,13 @@ function readFromRuntime(chain2) {
67905
68238
  if (first3.kind !== "dot" && first3.kind !== "bracket-string") {
67906
68239
  fail(first3.node, "runtime is keyed by name (runtime.timezone, runtime.now, ...).");
67907
68240
  }
67908
- if (!isRuntimeKey(first3.key)) {
67909
- fail(first3.node, `Unknown runtime key "${first3.key}". Allowed: ${Array.from(RUNTIME_KEYS).join(", ")}.`);
68241
+ const key = first3.key ?? "";
68242
+ if (!isRuntimeKey(key)) {
68243
+ fail(first3.node, `Unknown runtime key "${key}". Allowed: ${READABLE_RUNTIME_KEYS.join(", ")}.`);
67910
68244
  }
67911
- const source = { from: "runtime", key: first3.key };
68245
+ const parked = PARKED_RUNTIME_KEYS.get(key);
68246
+ if (parked) fail(first3.node, parked, parkedRuntimeKeyConstruct(key));
68247
+ const source = { from: "runtime", key };
67912
68248
  const path8 = [];
67913
68249
  for (const seg of segments.slice(1)) {
67914
68250
  if (seg.kind === "bracket-number") {
@@ -68099,14 +68435,25 @@ function parseWorkflowJs(source, opts) {
68099
68435
  }
68100
68436
  }
68101
68437
  function walkProgram(body, scope, stepIds, opts) {
68438
+ const functions = collectFunctionDeclarations(body, opts.toolNames ?? /* @__PURE__ */ new Set());
68439
+ const progScope = { ...scope, functions };
68102
68440
  let synthetic = 0;
68103
68441
  const steps = [];
68104
68442
  for (const stmt of body) {
68105
- steps.push(...walkStatement(stmt, scope, stepIds, opts, () => `_s${synthetic++}`, 1));
68443
+ if (stmt.type === "FunctionDeclaration") continue;
68444
+ steps.push(...walkStatement(stmt, progScope, stepIds, opts, () => `_s${synthetic++}`, 1));
68445
+ }
68446
+ for (const fn of functions.values()) {
68447
+ if (!progScope.inline.used.has(fn.name)) failUnusedFunction(fn.node, fn.name);
68106
68448
  }
68107
68449
  return steps;
68108
68450
  }
68109
68451
  var MAX_STATEMENT_DEPTH = 500;
68452
+ function assertNoFunctionCollision(node, name, scope, label) {
68453
+ if (scope.functions.has(name)) {
68454
+ fail(node, `${label} "${name}" collides with a function declared in this workflow.`);
68455
+ }
68456
+ }
68110
68457
  function walkStatement(stmt, scope, stepIds, opts, nextSyntheticId, depth) {
68111
68458
  if (depth > MAX_STATEMENT_DEPTH) {
68112
68459
  fail(stmt, `Workflow nesting is too deep (max ${MAX_STATEMENT_DEPTH} levels).`, "depth_limit");
@@ -68115,6 +68462,9 @@ function walkStatement(stmt, scope, stepIds, opts, nextSyntheticId, depth) {
68115
68462
  }
68116
68463
  function walkStatementInner(stmt, scope, stepIds, opts, nextSyntheticId, depth) {
68117
68464
  const { id: explicitId, description } = readLeadingComments(stmt);
68465
+ if (explicitId !== void 0) {
68466
+ assertNoFunctionCollision(stmt, explicitId, scope, "Step id");
68467
+ }
68118
68468
  switch (stmt.type) {
68119
68469
  case "VariableDeclaration":
68120
68470
  return walkConstBinding(stmt, scope, stepIds, opts, explicitId, description, nextSyntheticId);
@@ -68175,10 +68525,9 @@ function walkStatementInner(stmt, scope, stepIds, opts, nextSyntheticId, depth)
68175
68525
  case "ReturnStatement":
68176
68526
  return [walkReturnStatement(stmt, scope, explicitId ?? nextSyntheticId(), description)];
68177
68527
  case "FunctionDeclaration":
68178
- fail(
68528
+ failFunctionDeclaration(
68179
68529
  stmt,
68180
- 'Function declarations are not supported. For reusable logic, choose one:\n - bind step: `const tag = record.Score > 80 ? "hot" : "cold";` \u2014 compute once, reference by name in later steps.\n - inline expression: write the logic directly at each call site (often shorter than extracting).\n - separate workflow: factor the steps out and invoke from this workflow via a tool call.\nIf you were translating a JS helper habit, the bind step usually fits \u2014 workflows already let you name an expression for reuse.',
68181
- "function_declaration"
68530
+ "A function declared inside a block is not supported."
68182
68531
  );
68183
68532
  case "ClassDeclaration":
68184
68533
  fail(stmt, "Class declarations are not allowed.");
@@ -68241,6 +68590,7 @@ function walkConstDeclarator(decl, scope, stepIds, opts, explicitId, description
68241
68590
  if (opts.toolNames && opts.toolNames.has(id) || isHelper(id)) {
68242
68591
  fail(decl.id, `Cannot bind to "${id}" \u2014 name collides with a tool or helper.`);
68243
68592
  }
68593
+ assertNoFunctionCollision(decl.id, id, scope, "Binding");
68244
68594
  if (stepIds.has(id)) {
68245
68595
  fail(decl.id, `Step id "${id}" is already used.`);
68246
68596
  }
@@ -68417,6 +68767,7 @@ function walkPatternBinding(decl, scope, stepIds, opts, explicitId, description,
68417
68767
  if (opts.toolNames && opts.toolNames.has(entry.name) || isHelper(entry.name)) {
68418
68768
  fail(entry.node, `Cannot bind to "${entry.name}" \u2014 name collides with a tool or helper.`);
68419
68769
  }
68770
+ assertNoFunctionCollision(entry.node, entry.name, scope, "Binding");
68420
68771
  if (stepIds.has(entry.name)) {
68421
68772
  fail(entry.node, `Step id "${entry.name}" is already used.`);
68422
68773
  }
@@ -68460,6 +68811,7 @@ function walkLetDeclaration(stmt, scope, stepIds, opts, explicitId, description)
68460
68811
  if (opts.toolNames && opts.toolNames.has(name2) || isHelper(name2)) {
68461
68812
  fail(entry.node, `let binding "${name2}" collides with a tool or helper.`);
68462
68813
  }
68814
+ assertNoFunctionCollision(entry.node, name2, scope, "let binding");
68463
68815
  if (stepIds.has(name2)) {
68464
68816
  fail(entry.node, `let binding "${name2}" collides with a step id.`);
68465
68817
  }
@@ -68490,6 +68842,7 @@ function walkLetDeclaration(stmt, scope, stepIds, opts, explicitId, description)
68490
68842
  if (opts.toolNames && opts.toolNames.has(name) || isHelper(name)) {
68491
68843
  fail(decl.id, `let binding "${name}" collides with a tool or helper.`);
68492
68844
  }
68845
+ assertNoFunctionCollision(decl.id, name, scope, "let binding");
68493
68846
  if (stepIds.has(name)) {
68494
68847
  fail(decl.id, `let binding "${name}" collides with a step id.`);
68495
68848
  }
@@ -68820,15 +69173,13 @@ function walkForOf(stmt, scope, stepIds, opts, stepId, description, nextSyntheti
68820
69173
  if (opts.toolNames && opts.toolNames.has(bindName) || isHelper(bindName)) {
68821
69174
  fail(decl.id, `Foreach bind name "${bindName}" collides with a tool or helper.`);
68822
69175
  }
69176
+ assertNoFunctionCollision(decl.id, bindName, scope, "Foreach bind name");
68823
69177
  const items = walkForOfRight(stmt.right, scope);
68824
69178
  const innerScope = {
68825
- stepIds: scope.stepIds,
69179
+ ...scope,
68826
69180
  foreachBinds: [...scope.foreachBinds, bindName],
68827
- toolNames: scope.toolNames,
68828
- lambdaParams: scope.lambdaParams,
68829
69181
  loopDepth: scope.loopDepth + 1,
68830
- lexicalBindings: [...scope.lexicalBindings, /* @__PURE__ */ new Set()],
68831
- depth: scope.depth
69182
+ lexicalBindings: [...scope.lexicalBindings, /* @__PURE__ */ new Set()]
68832
69183
  };
68833
69184
  if (stmt.body.type !== "BlockStatement") {
68834
69185
  fail(stmt.body, "for-of body must be a { ... } block.");
@@ -68842,6 +69193,7 @@ function walkForOf(stmt, scope, stepIds, opts, stepId, description, nextSyntheti
68842
69193
  if (opts.toolNames && opts.toolNames.has(entry.name) || isHelper(entry.name)) {
68843
69194
  fail(entry.node, `Cannot bind to "${entry.name}" \u2014 name collides with a tool or helper.`);
68844
69195
  }
69196
+ assertNoFunctionCollision(entry.node, entry.name, scope, "Binding");
68845
69197
  if (stepIds.has(entry.name)) {
68846
69198
  fail(entry.node, `Step id "${entry.name}" is already used.`);
68847
69199
  }
@@ -69024,6 +69376,7 @@ function walkTry(stmt, scope, stepIds, opts, stepId, description, nextSyntheticI
69024
69376
  if (opts.toolNames && opts.toolNames.has(errorBinding) || isHelper(errorBinding)) {
69025
69377
  fail(handler.param, `Catch binding "${errorBinding}" collides with a tool or helper.`);
69026
69378
  }
69379
+ assertNoFunctionCollision(handler.param, errorBinding, scope, "Catch binding");
69027
69380
  if (stepIds.has(errorBinding)) {
69028
69381
  fail(handler.param, `Catch binding "${errorBinding}" collides with an existing step id.`);
69029
69382
  }
@@ -69283,8 +69636,7 @@ function walkAgentInvocation(call, scope, stepId, description) {
69283
69636
  if (!toolsNode) fail(arg, "agent requires `tools` (use [] for none).");
69284
69637
  const tool_names = readStaticStringArray(toolsNode, "agent `tools`");
69285
69638
  const modelNode = kw.get("model");
69286
- if (!modelNode) fail(arg, "agent requires `model`.");
69287
- const model_id = readStaticString(modelNode, "agent `model`");
69639
+ const model_id = modelNode ? readStaticString(modelNode, "agent `model`") : void 0;
69288
69640
  const outputNode = kw.get("output");
69289
69641
  if (!outputNode) fail(arg, "agent requires `output`.");
69290
69642
  const outputResult = agentOutputSpecSchema.safeParse(walkStaticJsonLiteral(outputNode));
@@ -69299,7 +69651,7 @@ function walkAgentInvocation(call, scope, stepId, description) {
69299
69651
  instructions,
69300
69652
  input,
69301
69653
  tool_names,
69302
- model_id,
69654
+ ...model_id !== void 0 ? { model_id } : {},
69303
69655
  output: outputResult.data
69304
69656
  };
69305
69657
  }
@@ -69400,6 +69752,133 @@ function readLeadingComments(stmt) {
69400
69752
  return { id, description };
69401
69753
  }
69402
69754
 
69755
+ // ../shared/src/rewrite_accumulator_appends.ts
69756
+ function rewriteAccumulatorAppends(tsApi, source) {
69757
+ const file2 = tsApi.createSourceFile(
69758
+ "/checked_source.ts",
69759
+ source,
69760
+ tsApi.ScriptTarget.ES2022,
69761
+ true
69762
+ );
69763
+ const seeded = emptyArrayLetNames(tsApi, file2);
69764
+ if (seeded.size === 0) return source;
69765
+ const edits = [];
69766
+ const visit = (node) => {
69767
+ if (tsApi.isExpressionStatement(node)) collectAppendEdits(tsApi, node, file2, seeded, edits);
69768
+ tsApi.forEachChild(node, visit);
69769
+ };
69770
+ tsApi.forEachChild(file2, visit);
69771
+ if (edits.length === 0) return source;
69772
+ return applyEdits(source, edits);
69773
+ }
69774
+ function emptyArrayLetNames(tsApi, file2) {
69775
+ const names = /* @__PURE__ */ new Set();
69776
+ const visit = (node) => {
69777
+ if (tsApi.isVariableDeclarationList(node) && (node.flags & tsApi.NodeFlags.Let) !== 0) {
69778
+ for (const decl of node.declarations) {
69779
+ if (!tsApi.isIdentifier(decl.name)) continue;
69780
+ if (decl.type) continue;
69781
+ const init = decl.initializer;
69782
+ if (init && tsApi.isArrayLiteralExpression(init) && init.elements.length === 0) {
69783
+ names.add(decl.name.text);
69784
+ }
69785
+ }
69786
+ }
69787
+ tsApi.forEachChild(node, visit);
69788
+ };
69789
+ tsApi.forEachChild(file2, visit);
69790
+ return names;
69791
+ }
69792
+ function collectAppendEdits(tsApi, stmt, file2, seeded, edits) {
69793
+ const expr = stmt.expression;
69794
+ if (!tsApi.isBinaryExpression(expr)) return;
69795
+ if (expr.operatorToken.kind !== tsApi.SyntaxKind.EqualsToken) return;
69796
+ const target = expr.left;
69797
+ if (!tsApi.isIdentifier(target) || !seeded.has(target.text)) return;
69798
+ const name = target.text;
69799
+ const start = expr.getStart(file2);
69800
+ const rhs = expr.right;
69801
+ if (tsApi.isArrayLiteralExpression(rhs)) {
69802
+ const [head, ...rest] = rhs.elements;
69803
+ if (!head || !tsApi.isSpreadElement(head)) return;
69804
+ if (!tsApi.isIdentifier(head.expression) || head.expression.text !== name) return;
69805
+ if (rest.length === 0 || rest.some((e) => tsApi.isSpreadElement(e))) return;
69806
+ pushEdits(edits, file2, {
69807
+ start,
69808
+ firstItem: rest[0],
69809
+ lastItem: rest[rest.length - 1],
69810
+ closeEnd: rhs.getEnd(),
69811
+ tokens: [name, ".push("]
69812
+ });
69813
+ return;
69814
+ }
69815
+ if (!tsApi.isCallExpression(rhs)) return;
69816
+ if (!tsApi.isIdentifier(rhs.expression) || rhs.expression.text !== "concat") return;
69817
+ if (rhs.arguments.length !== 2) return;
69818
+ const [selfArg, appended] = rhs.arguments;
69819
+ if (!tsApi.isIdentifier(selfArg) || selfArg.text !== name) return;
69820
+ if (tsApi.isSpreadElement(appended)) return;
69821
+ if (!tsApi.isArrayLiteralExpression(appended)) return;
69822
+ const items = appended.elements;
69823
+ if (items.length === 0 || items.some((e) => tsApi.isSpreadElement(e))) return;
69824
+ pushEdits(edits, file2, {
69825
+ start,
69826
+ firstItem: items[0],
69827
+ lastItem: items[items.length - 1],
69828
+ closeEnd: rhs.getEnd(),
69829
+ tokens: [name, ".push("]
69830
+ });
69831
+ }
69832
+ function pushEdits(edits, file2, plan) {
69833
+ const headStart = plan.start;
69834
+ const headEnd = plan.firstItem.getStart(file2);
69835
+ const tailStart = plan.lastItem.getEnd();
69836
+ const headText = fillRegion(plan.tokens, file2.text.slice(headStart, headEnd));
69837
+ const tailText = fillRegion([")"], file2.text.slice(tailStart, plan.closeEnd));
69838
+ if (headText === null || tailText === null) return;
69839
+ edits.push(
69840
+ { start: headStart, end: headEnd, text: headText },
69841
+ { start: tailStart, end: plan.closeEnd, text: tailText }
69842
+ );
69843
+ }
69844
+ function applyEdits(source, edits) {
69845
+ const ordered = [...edits].sort((a, b) => a.start - b.start);
69846
+ const parts = [];
69847
+ let cursor = 0;
69848
+ for (const edit of ordered) {
69849
+ parts.push(source.slice(cursor, edit.start), edit.text);
69850
+ cursor = edit.end;
69851
+ }
69852
+ parts.push(source.slice(cursor));
69853
+ return parts.join("");
69854
+ }
69855
+ function fillRegion(tokens, original) {
69856
+ const chars = original.split("").map((c) => c === "\n" || c === "\r" ? c : " ");
69857
+ let cursor = 0;
69858
+ for (const token of tokens) {
69859
+ const at2 = findPlaceableWindow(chars, cursor, token.length);
69860
+ if (at2 === null) return null;
69861
+ for (let i2 = 0; i2 < token.length; i2++) chars[at2 + i2] = token[i2];
69862
+ cursor = at2 + token.length;
69863
+ }
69864
+ return chars.join("");
69865
+ }
69866
+ function findPlaceableWindow(chars, from, width) {
69867
+ for (let i2 = from; i2 + width <= chars.length; i2++) {
69868
+ let ok = true;
69869
+ for (let j = 0; j < width; j++) {
69870
+ const c = chars[i2 + j];
69871
+ if (c === "\n" || c === "\r") {
69872
+ ok = false;
69873
+ i2 += j;
69874
+ break;
69875
+ }
69876
+ }
69877
+ if (ok) return i2;
69878
+ }
69879
+ return null;
69880
+ }
69881
+
69403
69882
  // src/workflow_envelope.ts
69404
69883
  var FALLBACK_ENVELOPE_PREFIX = "async function __workflow(): Promise<__WorkflowReturn | void> {\n";
69405
69884
  var FALLBACK_ENVELOPE_SUFFIX = "\n}";
@@ -69487,9 +69966,16 @@ function checkOneWorkflowBody(tsApi, input) {
69487
69966
  const offset = bodyLineOffset(wrapped);
69488
69967
  const subset = subsetIssues(stripWorkflowHeader(wrapped), offset);
69489
69968
  if (subset) return subset;
69969
+ const options = workflowCheckCompilerOptions(tsApi);
69490
69970
  const program = tsApi.createProgram({
69491
69971
  rootNames: [input.globalsPath, input.bodyPath],
69492
- options: workflowCheckCompilerOptions(tsApi)
69972
+ options,
69973
+ host: createCheckedSourceHost(
69974
+ tsApi,
69975
+ options,
69976
+ input.bodyPath,
69977
+ rewriteAccumulatorAppends(tsApi, wrapped)
69978
+ )
69493
69979
  });
69494
69980
  const bodyFile = program.getSourceFile(input.bodyPath);
69495
69981
  if (!bodyFile) {
@@ -69521,6 +70007,17 @@ function checkOneWorkflowBody(tsApi, input) {
69521
70007
  }
69522
70008
  return issues;
69523
70009
  }
70010
+ function createCheckedSourceHost(tsApi, options, bodyPath, bodyText) {
70011
+ const base = tsApi.createCompilerHost(options, false);
70012
+ const key = (fileName) => base.getCanonicalFileName(path4.resolve(fileName).replace(/\\/g, "/"));
70013
+ const bodyKey = key(bodyPath);
70014
+ return {
70015
+ ...base,
70016
+ getSourceFile: (fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile) => key(fileName) === bodyKey ? tsApi.createSourceFile(fileName, bodyText, languageVersionOrOptions, true) : base.getSourceFile(fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile),
70017
+ readFile: (fileName) => key(fileName) === bodyKey ? bodyText : base.readFile(fileName),
70018
+ fileExists: (fileName) => key(fileName) === bodyKey ? true : base.fileExists(fileName)
70019
+ };
70020
+ }
69524
70021
  function checkWorkflowBodies(tsApi, aliases) {
69525
70022
  return aliases.map(({ alias, input }) => ({
69526
70023
  alias,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/cli",
3
- "version": "0.95.1",
3
+ "version": "0.96.0",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {