@lotics/cli 0.95.1 → 0.96.1
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/src/cli.js +547 -50
- package/docs/document_templates.md +9 -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-
|
|
64435
|
-
var LEGACY_CHAT_MODEL_IDS = [
|
|
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(
|
|
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(
|
|
65518
|
-
if (
|
|
65519
|
-
if (!Array.isArray(
|
|
65520
|
-
throw new Error("slice: expected array, got " + typeof
|
|
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
|
|
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(
|
|
65567
|
-
if (
|
|
65568
|
-
if (!Array.isArray(
|
|
65569
|
-
throw new Error("at: expected array, got " + typeof
|
|
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 ?
|
|
65575
|
-
if (resolved < 0 || resolved >=
|
|
65576
|
-
return
|
|
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(...
|
|
65749
|
-
if (
|
|
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.
|
|
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(
|
|
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
|
-
`
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
67909
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
68528
|
+
failFunctionDeclaration(
|
|
68179
68529
|
stmt,
|
|
68180
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
@@ -76,8 +76,15 @@ format handles display.
|
|
|
76
76
|
### Word (`create_word_template`)
|
|
77
77
|
|
|
78
78
|
1. Build a `.docx` with `{{marker}}`s where values go. `lotics upload ./template.docx`.
|
|
79
|
-
2. `create_word_template` with the `template_file_id
|
|
80
|
-
|
|
79
|
+
2. `create_word_template` with the `template_file_id`. `variables` is optional — every
|
|
80
|
+
marker in the document is derived automatically (`{{name}}` → string,
|
|
81
|
+
`{{FOR … IN name}}` → array, `{{IF name}}` → boolean). When you do declare, an unmarked
|
|
82
|
+
declaration comes back in `unmarked_variables` and is skipped, and markers you didn't
|
|
83
|
+
declare are still auto-added (`derived_variables`) — the stored contract always covers
|
|
84
|
+
every marker in the document.
|
|
85
|
+
|
|
86
|
+
At generate time, `data` must provide a key for **every** marker — pass `""` for fields
|
|
87
|
+
that should render blank; a missing key fails with the full list of missing markers.
|
|
81
88
|
|
|
82
89
|
To add markers/loops/conditionals to the uploaded file from the CLI: `word_replace_text`,
|
|
83
90
|
`word_insert_loop`, `word_insert_conditional` (and their `remove_*` counterparts).
|