@lotics/cli 0.95.0 → 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.
- package/dist/src/cli.js +552 -57
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -45377,8 +45377,9 @@ function buildStarterTemplate(args) {
|
|
|
45377
45377
|
"react-native-svg": "^15.0.0",
|
|
45378
45378
|
"react-native-web": "^0.21.0",
|
|
45379
45379
|
// In-app routing for the scaffolded list→detail example. The app owns
|
|
45380
|
-
// its routing; the SDK's `isEmbedded()` picks memory vs browser history.
|
|
45381
|
-
|
|
45380
|
+
// its routing; the SDK's `isEmbedded()` picks memory vs browser history. `react-router` is the
|
|
45381
|
+
// canonical package (react-router-dom is a shim over it).
|
|
45382
|
+
"react-router": "^8.3.0"
|
|
45382
45383
|
},
|
|
45383
45384
|
devDependencies: {
|
|
45384
45385
|
"@testing-library/react": "^16.1.0",
|
|
@@ -45619,12 +45620,9 @@ export default defineConfig({
|
|
|
45619
45620
|
// react-router hooks and the Router context never matches
|
|
45620
45621
|
// ("useNavigate() may be used only in the context of a <Router>").
|
|
45621
45622
|
"@lotics/app-sdk/router",
|
|
45622
|
-
// react-router
|
|
45623
|
+
// react-router (App's router) also ships compiled dist JS \u2014 pin it
|
|
45623
45624
|
// into the shared chunk so its hooks don't split the react instance
|
|
45624
45625
|
// ("Invalid hook call") when a test renders the routed App tree.
|
|
45625
|
-
"react-router-dom",
|
|
45626
|
-
// v7: react-router-dom is a thin wrapper \u2014 the actual context
|
|
45627
|
-
// lives in react-router; both must ride the shared chunk.
|
|
45628
45626
|
"react-router",
|
|
45629
45627
|
],
|
|
45630
45628
|
esbuildOptions: {
|
|
@@ -45708,7 +45706,7 @@ mount(
|
|
|
45708
45706
|
// A single-screen app can delete the router and render one Screen directly.
|
|
45709
45707
|
content: `import type { ReactNode } from "react";
|
|
45710
45708
|
import { View } from "react-native";
|
|
45711
|
-
import { useNavigate, useParams } from "react-router
|
|
45709
|
+
import { useNavigate, useParams } from "react-router";
|
|
45712
45710
|
import { AppRouter } from "@lotics/app-sdk/router";
|
|
45713
45711
|
import { useAiContext } from "@lotics/app-sdk";
|
|
45714
45712
|
import { Card } from "@lotics/ui/card";
|
|
@@ -62046,6 +62044,14 @@ var runtimeKeySchema = zod_default.enum([
|
|
|
62046
62044
|
"organization_id",
|
|
62047
62045
|
"now",
|
|
62048
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%'`).
|
|
62049
62055
|
"execution_principal",
|
|
62050
62056
|
"triggered_by_member_id"
|
|
62051
62057
|
]);
|
|
@@ -64433,9 +64439,13 @@ for (const [operation, fieldTypes] of Object.entries(OPERATION_FIELD_TYPES)) {
|
|
|
64433
64439
|
var AGGREGATABLE_FIELD_TYPES = new Set(FIELD_TYPE_OPERATIONS.keys());
|
|
64434
64440
|
|
|
64435
64441
|
// ../shared/src/chat_models.ts
|
|
64436
|
-
var CHAT_MODEL_IDS = ["claude-haiku-4-5", "claude-sonnet-5", "claude-opus-
|
|
64437
|
-
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 = [];
|
|
64438
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
|
+
);
|
|
64439
64449
|
var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
64440
64450
|
|
|
64441
64451
|
// ../shared/src/schemas/apps.ts
|
|
@@ -64869,7 +64879,9 @@ var appAgentDeclarationSchema = zod_default.object({
|
|
|
64869
64879
|
knowledge_doc_ids: zod_default.array(zod_default.string().min(1)).optional().describe(
|
|
64870
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."
|
|
64871
64881
|
),
|
|
64872
|
-
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
|
+
),
|
|
64873
64885
|
effort_level: zod_default.enum(EFFORT_LEVELS).optional().describe(
|
|
64874
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."
|
|
64875
64887
|
),
|
|
@@ -65010,7 +65022,7 @@ var agentStepSchema = stepBaseSchema.extend({
|
|
|
65010
65022
|
instructions: zod_default.string().min(1),
|
|
65011
65023
|
input: zod_default.record(zod_default.string(), toolInputValueSchema),
|
|
65012
65024
|
tool_names: zod_default.array(zod_default.string().min(1)),
|
|
65013
|
-
model_id: zod_default.string().min(1),
|
|
65025
|
+
model_id: zod_default.string().min(1).optional(),
|
|
65014
65026
|
output: agentOutputSpecSchema
|
|
65015
65027
|
});
|
|
65016
65028
|
var breakStepSchema = stepBaseSchema.extend({
|
|
@@ -65516,10 +65528,10 @@ function reverse(arr) {
|
|
|
65516
65528
|
}
|
|
65517
65529
|
return [...arr].reverse();
|
|
65518
65530
|
}
|
|
65519
|
-
function slice(
|
|
65520
|
-
if (
|
|
65521
|
-
if (!Array.isArray(
|
|
65522
|
-
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);
|
|
65523
65535
|
}
|
|
65524
65536
|
if (typeof start !== "number") {
|
|
65525
65537
|
throw new Error("slice: start must be a number");
|
|
@@ -65527,7 +65539,7 @@ function slice(arr, start, end) {
|
|
|
65527
65539
|
if (end != null && typeof end !== "number") {
|
|
65528
65540
|
throw new Error("slice: end must be a number");
|
|
65529
65541
|
}
|
|
65530
|
-
return
|
|
65542
|
+
return collection.slice(start, end);
|
|
65531
65543
|
}
|
|
65532
65544
|
function first2(arr) {
|
|
65533
65545
|
if (arr == null) return void 0;
|
|
@@ -65565,17 +65577,17 @@ function nth(arr, index) {
|
|
|
65565
65577
|
}
|
|
65566
65578
|
return arr[index];
|
|
65567
65579
|
}
|
|
65568
|
-
function at(
|
|
65569
|
-
if (
|
|
65570
|
-
if (!Array.isArray(
|
|
65571
|
-
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);
|
|
65572
65584
|
}
|
|
65573
65585
|
if (typeof index !== "number") {
|
|
65574
65586
|
throw new Error("at: index must be a number");
|
|
65575
65587
|
}
|
|
65576
|
-
const resolved = index < 0 ?
|
|
65577
|
-
if (resolved < 0 || resolved >=
|
|
65578
|
-
return
|
|
65588
|
+
const resolved = index < 0 ? collection.length + index : index;
|
|
65589
|
+
if (resolved < 0 || resolved >= collection.length) return void 0;
|
|
65590
|
+
return collection[resolved];
|
|
65579
65591
|
}
|
|
65580
65592
|
function size(arr) {
|
|
65581
65593
|
if (arr == null) return 0;
|
|
@@ -65747,10 +65759,28 @@ function list(...items) {
|
|
|
65747
65759
|
}
|
|
65748
65760
|
return items;
|
|
65749
65761
|
}
|
|
65750
|
-
function concat(...
|
|
65751
|
-
if (
|
|
65752
|
-
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;
|
|
65753
65780
|
}
|
|
65781
|
+
return out;
|
|
65782
|
+
}
|
|
65783
|
+
function concatArrays(arrays) {
|
|
65754
65784
|
let total = 0;
|
|
65755
65785
|
for (let i2 = 0; i2 < arrays.length; i2++) {
|
|
65756
65786
|
const arr = arrays[i2];
|
|
@@ -67133,6 +67163,18 @@ function isRuntimeKey(name) {
|
|
|
67133
67163
|
return RUNTIME_KEYS.has(name);
|
|
67134
67164
|
}
|
|
67135
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
|
+
);
|
|
67136
67178
|
var INLINE_HELPERS = [
|
|
67137
67179
|
"formatCurrency",
|
|
67138
67180
|
"randomNumber",
|
|
@@ -67164,11 +67206,22 @@ var HELPER_MENU = [
|
|
|
67164
67206
|
`date \u2014 ${Object.keys(getDateExpressionFunctions()).sort().join(", ")}`,
|
|
67165
67207
|
`other \u2014 ${[...INLINE_HELPERS].sort().join(", ")}`
|
|
67166
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
|
+
}
|
|
67167
67218
|
function makeScope(init = {}) {
|
|
67168
67219
|
return {
|
|
67169
67220
|
stepIds: init.stepIds ?? /* @__PURE__ */ new Set(),
|
|
67170
67221
|
foreachBinds: init.foreachBinds ?? [],
|
|
67171
67222
|
toolNames: init.toolNames ?? /* @__PURE__ */ new Set(),
|
|
67223
|
+
functions: init.functions ?? /* @__PURE__ */ new Map(),
|
|
67224
|
+
inline: init.inline ?? makeInlineState(),
|
|
67172
67225
|
lambdaParams: init.lambdaParams ?? [],
|
|
67173
67226
|
loopDepth: init.loopDepth ?? 0,
|
|
67174
67227
|
lexicalBindings: init.lexicalBindings ?? [/* @__PURE__ */ new Set()],
|
|
@@ -67199,7 +67252,7 @@ var CALLBACK_HELPERS = /* @__PURE__ */ new Set([
|
|
|
67199
67252
|
"intersectionBy",
|
|
67200
67253
|
"reduce"
|
|
67201
67254
|
]);
|
|
67202
|
-
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.`;
|
|
67203
67256
|
var BINARY_OPS = {
|
|
67204
67257
|
// Both equality forms work as in JS: `==`/`!=` are loose (coercing), `===`/
|
|
67205
67258
|
// `!==` are strict. The author picks the semantics; we don't second-guess.
|
|
@@ -67244,7 +67297,46 @@ var NAMESPACE_HELPERS = /* @__PURE__ */ new Map([
|
|
|
67244
67297
|
["Object", /* @__PURE__ */ new Map([["keys", "keys"], ["values", "values"], ["entries", "entries"]])],
|
|
67245
67298
|
["Number", /* @__PURE__ */ new Map([["parseFloat", "toNumber"]])]
|
|
67246
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(", ");
|
|
67247
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
|
+
]);
|
|
67248
67340
|
var MAX_EXPRESSION_DEPTH = 500;
|
|
67249
67341
|
function walkExpression(node, scope) {
|
|
67250
67342
|
const depth = scope.depth + 1;
|
|
@@ -67300,7 +67392,7 @@ function walkExpressionNode(node, scope) {
|
|
|
67300
67392
|
return walkObjectLiteral(node, scope);
|
|
67301
67393
|
case "ArrowFunctionExpression":
|
|
67302
67394
|
case "FunctionExpression":
|
|
67303
|
-
fail(node, LAMBDA_POSITION_HINT);
|
|
67395
|
+
fail(node, LAMBDA_POSITION_HINT, "lambda_position");
|
|
67304
67396
|
case "AssignmentExpression":
|
|
67305
67397
|
fail(node, 'Assignment is only allowed for "const x = await tool(...)" tool-call bindings.');
|
|
67306
67398
|
case "UpdateExpression":
|
|
@@ -67488,10 +67580,14 @@ function walkObjectLiteral(node, scope) {
|
|
|
67488
67580
|
function walkHelperCall(node, scope) {
|
|
67489
67581
|
const callee = node.callee;
|
|
67490
67582
|
if (callee.type === "ArrowFunctionExpression" || callee.type === "FunctionExpression") {
|
|
67491
|
-
fail(callee, LAMBDA_POSITION_HINT);
|
|
67583
|
+
fail(callee, LAMBDA_POSITION_HINT, "lambda_position");
|
|
67492
67584
|
}
|
|
67493
67585
|
if (node.type === "OptionalCallExpression" && node.optional === true) {
|
|
67494
|
-
fail(
|
|
67586
|
+
fail(
|
|
67587
|
+
node,
|
|
67588
|
+
"Optional calls (`x?.()`) are not supported. Use `?.` on member access only.",
|
|
67589
|
+
"optional_call"
|
|
67590
|
+
);
|
|
67495
67591
|
}
|
|
67496
67592
|
if ((callee.type === "MemberExpression" || callee.type === "OptionalMemberExpression") && callee.object.type === "Identifier" && callee.object.name === "Math" && !callee.computed && callee.property.type === "Identifier") {
|
|
67497
67593
|
const mathFn = callee.property.name;
|
|
@@ -67600,6 +67696,17 @@ function isOptionalChainGuard(e) {
|
|
|
67600
67696
|
return e.kind === "ternary" && e.then.kind === "lit" && e.then.value === null && e.cond.kind === "call" && e.cond.fn === "isNull";
|
|
67601
67697
|
}
|
|
67602
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
|
+
}
|
|
67603
67710
|
const rejected = REJECTED_HELPERS.get(rawFn);
|
|
67604
67711
|
if (rejected) fail(node, rejected.message, rejected.construct);
|
|
67605
67712
|
const fn = HELPER_ALIASES.get(rawFn) ?? rawFn;
|
|
@@ -67610,6 +67717,8 @@ function buildHelperCall(node, rawFn, receiver, scope) {
|
|
|
67610
67717
|
fail(node, `${fn} is a reserved identifier and cannot be called.`);
|
|
67611
67718
|
}
|
|
67612
67719
|
if (isRuntimeKey(fn) && !isHelper(fn)) {
|
|
67720
|
+
const parked = PARKED_RUNTIME_KEYS.get(fn);
|
|
67721
|
+
if (parked) fail(node, parked, parkedRuntimeKeyConstruct(fn));
|
|
67613
67722
|
fail(node, `${fn} is a runtime key, not a helper. Read it as runtime.${fn}.`);
|
|
67614
67723
|
}
|
|
67615
67724
|
if (!isHelper(fn)) {
|
|
@@ -67619,6 +67728,13 @@ function buildHelperCall(node, rawFn, receiver, scope) {
|
|
|
67619
67728
|
"unknown_helper"
|
|
67620
67729
|
);
|
|
67621
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
|
+
}
|
|
67622
67738
|
if (fn === "filter") {
|
|
67623
67739
|
const predNode = node.arguments.length > 0 ? node.arguments[node.arguments.length - 1] : void 0;
|
|
67624
67740
|
if (predNode && predNode.type === "Identifier" && predNode.name === "Boolean") {
|
|
@@ -67645,11 +67761,11 @@ function walkHelperArg(arg, fn, scope) {
|
|
|
67645
67761
|
"spread_element"
|
|
67646
67762
|
);
|
|
67647
67763
|
}
|
|
67648
|
-
if (arg.type === "ArrowFunctionExpression") {
|
|
67764
|
+
if (arg.type === "ArrowFunctionExpression" || arg.type === "FunctionExpression") {
|
|
67649
67765
|
if (!CALLBACK_HELPERS.has(fn)) {
|
|
67650
67766
|
fail(
|
|
67651
67767
|
arg,
|
|
67652
|
-
`
|
|
67768
|
+
`Callback functions are not supported as arguments to "${fn}". Callbacks work on ${[...CALLBACK_HELPERS].sort().join(" / ")}.`,
|
|
67653
67769
|
"lambda_not_allowed"
|
|
67654
67770
|
);
|
|
67655
67771
|
}
|
|
@@ -67659,7 +67775,15 @@ function walkHelperArg(arg, fn, scope) {
|
|
|
67659
67775
|
}
|
|
67660
67776
|
function walkLambda(node, scope) {
|
|
67661
67777
|
if (node.async) {
|
|
67662
|
-
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
|
+
}
|
|
67663
67787
|
}
|
|
67664
67788
|
const params = [];
|
|
67665
67789
|
for (const p of node.params) {
|
|
@@ -67672,6 +67796,12 @@ function walkLambda(node, scope) {
|
|
|
67672
67796
|
if (scope.toolNames.has(p.name) || isHelper(p.name)) {
|
|
67673
67797
|
fail(p, `Lambda parameter "${p.name}" collides with a tool or helper.`);
|
|
67674
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
|
+
}
|
|
67675
67805
|
if (scope.stepIds.has(p.name)) {
|
|
67676
67806
|
fail(p, `Lambda parameter "${p.name}" collides with a step id.`);
|
|
67677
67807
|
}
|
|
@@ -67683,20 +67813,177 @@ function walkLambda(node, scope) {
|
|
|
67683
67813
|
}
|
|
67684
67814
|
params.push(p.name);
|
|
67685
67815
|
}
|
|
67686
|
-
|
|
67687
|
-
fail(
|
|
67688
|
-
node.body,
|
|
67689
|
-
"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.",
|
|
67690
|
-
"block_body_lambda"
|
|
67691
|
-
);
|
|
67692
|
-
}
|
|
67816
|
+
const bodyExpr = lambdaBodyExpression(node);
|
|
67693
67817
|
const innerScope = {
|
|
67694
67818
|
...scope,
|
|
67695
67819
|
lambdaParams: [...scope.lambdaParams, ...params]
|
|
67696
67820
|
};
|
|
67697
|
-
const body = walkExpression(
|
|
67821
|
+
const body = walkExpression(bodyExpr, innerScope);
|
|
67698
67822
|
return { kind: "lambda", params, body };
|
|
67699
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
|
+
}
|
|
67700
67987
|
function flattenChain(node, scope) {
|
|
67701
67988
|
let cur = node;
|
|
67702
67989
|
const reverse2 = [];
|
|
@@ -67786,7 +68073,30 @@ function walkRead(node, scope) {
|
|
|
67786
68073
|
if (chain2.segments.length === 0 && scope.toolNames.has(chain2.rootName)) {
|
|
67787
68074
|
fail(node, `Tools may only be invoked, not referenced. Write "const x = await ${chain2.rootName}({...})".`);
|
|
67788
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
|
+
}
|
|
67789
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
|
+
}
|
|
67790
68100
|
if (root === "record" || root === "prev_record") {
|
|
67791
68101
|
return readFromRecord(
|
|
67792
68102
|
root === "record" ? "trigger_record" : "trigger_record_prev",
|
|
@@ -67803,6 +68113,13 @@ function walkRead(node, scope) {
|
|
|
67803
68113
|
return readFromRuntime(chain2);
|
|
67804
68114
|
}
|
|
67805
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
|
+
}
|
|
67806
68123
|
if (chain2.segments.length > 0) {
|
|
67807
68124
|
fail(node, "index has no properties.");
|
|
67808
68125
|
}
|
|
@@ -67831,6 +68148,7 @@ function walkRead(node, scope) {
|
|
|
67831
68148
|
"runtime",
|
|
67832
68149
|
"trigger",
|
|
67833
68150
|
"index",
|
|
68151
|
+
...scope.inline.args.keys(),
|
|
67834
68152
|
...lexicalNames,
|
|
67835
68153
|
...Array.from(scope.lambdaParams),
|
|
67836
68154
|
...Array.from(scope.foreachBinds),
|
|
@@ -67880,7 +68198,20 @@ function readFromChanges(chain2) {
|
|
|
67880
68198
|
return { kind: "read", source: { from: "trigger_changes" }, path: path8 };
|
|
67881
68199
|
});
|
|
67882
68200
|
}
|
|
68201
|
+
var TRIGGER_RECORD_VIEWS = /* @__PURE__ */ new Map([
|
|
68202
|
+
["data", "trigger_record"],
|
|
68203
|
+
["prev_data", "trigger_record_prev"],
|
|
68204
|
+
["changes", "trigger_changes"]
|
|
68205
|
+
]);
|
|
67883
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
|
+
}
|
|
67884
68215
|
return wrapOptionalGuards(chain2, (segments) => {
|
|
67885
68216
|
const path8 = [];
|
|
67886
68217
|
for (const seg of segments) {
|
|
@@ -67907,10 +68238,13 @@ function readFromRuntime(chain2) {
|
|
|
67907
68238
|
if (first3.kind !== "dot" && first3.kind !== "bracket-string") {
|
|
67908
68239
|
fail(first3.node, "runtime is keyed by name (runtime.timezone, runtime.now, ...).");
|
|
67909
68240
|
}
|
|
67910
|
-
|
|
67911
|
-
|
|
68241
|
+
const key = first3.key ?? "";
|
|
68242
|
+
if (!isRuntimeKey(key)) {
|
|
68243
|
+
fail(first3.node, `Unknown runtime key "${key}". Allowed: ${READABLE_RUNTIME_KEYS.join(", ")}.`);
|
|
67912
68244
|
}
|
|
67913
|
-
const
|
|
68245
|
+
const parked = PARKED_RUNTIME_KEYS.get(key);
|
|
68246
|
+
if (parked) fail(first3.node, parked, parkedRuntimeKeyConstruct(key));
|
|
68247
|
+
const source = { from: "runtime", key };
|
|
67914
68248
|
const path8 = [];
|
|
67915
68249
|
for (const seg of segments.slice(1)) {
|
|
67916
68250
|
if (seg.kind === "bracket-number") {
|
|
@@ -68101,14 +68435,25 @@ function parseWorkflowJs(source, opts) {
|
|
|
68101
68435
|
}
|
|
68102
68436
|
}
|
|
68103
68437
|
function walkProgram(body, scope, stepIds, opts) {
|
|
68438
|
+
const functions = collectFunctionDeclarations(body, opts.toolNames ?? /* @__PURE__ */ new Set());
|
|
68439
|
+
const progScope = { ...scope, functions };
|
|
68104
68440
|
let synthetic = 0;
|
|
68105
68441
|
const steps = [];
|
|
68106
68442
|
for (const stmt of body) {
|
|
68107
|
-
|
|
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);
|
|
68108
68448
|
}
|
|
68109
68449
|
return steps;
|
|
68110
68450
|
}
|
|
68111
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
|
+
}
|
|
68112
68457
|
function walkStatement(stmt, scope, stepIds, opts, nextSyntheticId, depth) {
|
|
68113
68458
|
if (depth > MAX_STATEMENT_DEPTH) {
|
|
68114
68459
|
fail(stmt, `Workflow nesting is too deep (max ${MAX_STATEMENT_DEPTH} levels).`, "depth_limit");
|
|
@@ -68117,6 +68462,9 @@ function walkStatement(stmt, scope, stepIds, opts, nextSyntheticId, depth) {
|
|
|
68117
68462
|
}
|
|
68118
68463
|
function walkStatementInner(stmt, scope, stepIds, opts, nextSyntheticId, depth) {
|
|
68119
68464
|
const { id: explicitId, description } = readLeadingComments(stmt);
|
|
68465
|
+
if (explicitId !== void 0) {
|
|
68466
|
+
assertNoFunctionCollision(stmt, explicitId, scope, "Step id");
|
|
68467
|
+
}
|
|
68120
68468
|
switch (stmt.type) {
|
|
68121
68469
|
case "VariableDeclaration":
|
|
68122
68470
|
return walkConstBinding(stmt, scope, stepIds, opts, explicitId, description, nextSyntheticId);
|
|
@@ -68177,10 +68525,9 @@ function walkStatementInner(stmt, scope, stepIds, opts, nextSyntheticId, depth)
|
|
|
68177
68525
|
case "ReturnStatement":
|
|
68178
68526
|
return [walkReturnStatement(stmt, scope, explicitId ?? nextSyntheticId(), description)];
|
|
68179
68527
|
case "FunctionDeclaration":
|
|
68180
|
-
|
|
68528
|
+
failFunctionDeclaration(
|
|
68181
68529
|
stmt,
|
|
68182
|
-
|
|
68183
|
-
"function_declaration"
|
|
68530
|
+
"A function declared inside a block is not supported."
|
|
68184
68531
|
);
|
|
68185
68532
|
case "ClassDeclaration":
|
|
68186
68533
|
fail(stmt, "Class declarations are not allowed.");
|
|
@@ -68243,6 +68590,7 @@ function walkConstDeclarator(decl, scope, stepIds, opts, explicitId, description
|
|
|
68243
68590
|
if (opts.toolNames && opts.toolNames.has(id) || isHelper(id)) {
|
|
68244
68591
|
fail(decl.id, `Cannot bind to "${id}" \u2014 name collides with a tool or helper.`);
|
|
68245
68592
|
}
|
|
68593
|
+
assertNoFunctionCollision(decl.id, id, scope, "Binding");
|
|
68246
68594
|
if (stepIds.has(id)) {
|
|
68247
68595
|
fail(decl.id, `Step id "${id}" is already used.`);
|
|
68248
68596
|
}
|
|
@@ -68419,6 +68767,7 @@ function walkPatternBinding(decl, scope, stepIds, opts, explicitId, description,
|
|
|
68419
68767
|
if (opts.toolNames && opts.toolNames.has(entry.name) || isHelper(entry.name)) {
|
|
68420
68768
|
fail(entry.node, `Cannot bind to "${entry.name}" \u2014 name collides with a tool or helper.`);
|
|
68421
68769
|
}
|
|
68770
|
+
assertNoFunctionCollision(entry.node, entry.name, scope, "Binding");
|
|
68422
68771
|
if (stepIds.has(entry.name)) {
|
|
68423
68772
|
fail(entry.node, `Step id "${entry.name}" is already used.`);
|
|
68424
68773
|
}
|
|
@@ -68462,6 +68811,7 @@ function walkLetDeclaration(stmt, scope, stepIds, opts, explicitId, description)
|
|
|
68462
68811
|
if (opts.toolNames && opts.toolNames.has(name2) || isHelper(name2)) {
|
|
68463
68812
|
fail(entry.node, `let binding "${name2}" collides with a tool or helper.`);
|
|
68464
68813
|
}
|
|
68814
|
+
assertNoFunctionCollision(entry.node, name2, scope, "let binding");
|
|
68465
68815
|
if (stepIds.has(name2)) {
|
|
68466
68816
|
fail(entry.node, `let binding "${name2}" collides with a step id.`);
|
|
68467
68817
|
}
|
|
@@ -68492,6 +68842,7 @@ function walkLetDeclaration(stmt, scope, stepIds, opts, explicitId, description)
|
|
|
68492
68842
|
if (opts.toolNames && opts.toolNames.has(name) || isHelper(name)) {
|
|
68493
68843
|
fail(decl.id, `let binding "${name}" collides with a tool or helper.`);
|
|
68494
68844
|
}
|
|
68845
|
+
assertNoFunctionCollision(decl.id, name, scope, "let binding");
|
|
68495
68846
|
if (stepIds.has(name)) {
|
|
68496
68847
|
fail(decl.id, `let binding "${name}" collides with a step id.`);
|
|
68497
68848
|
}
|
|
@@ -68822,15 +69173,13 @@ function walkForOf(stmt, scope, stepIds, opts, stepId, description, nextSyntheti
|
|
|
68822
69173
|
if (opts.toolNames && opts.toolNames.has(bindName) || isHelper(bindName)) {
|
|
68823
69174
|
fail(decl.id, `Foreach bind name "${bindName}" collides with a tool or helper.`);
|
|
68824
69175
|
}
|
|
69176
|
+
assertNoFunctionCollision(decl.id, bindName, scope, "Foreach bind name");
|
|
68825
69177
|
const items = walkForOfRight(stmt.right, scope);
|
|
68826
69178
|
const innerScope = {
|
|
68827
|
-
|
|
69179
|
+
...scope,
|
|
68828
69180
|
foreachBinds: [...scope.foreachBinds, bindName],
|
|
68829
|
-
toolNames: scope.toolNames,
|
|
68830
|
-
lambdaParams: scope.lambdaParams,
|
|
68831
69181
|
loopDepth: scope.loopDepth + 1,
|
|
68832
|
-
lexicalBindings: [...scope.lexicalBindings, /* @__PURE__ */ new Set()]
|
|
68833
|
-
depth: scope.depth
|
|
69182
|
+
lexicalBindings: [...scope.lexicalBindings, /* @__PURE__ */ new Set()]
|
|
68834
69183
|
};
|
|
68835
69184
|
if (stmt.body.type !== "BlockStatement") {
|
|
68836
69185
|
fail(stmt.body, "for-of body must be a { ... } block.");
|
|
@@ -68844,6 +69193,7 @@ function walkForOf(stmt, scope, stepIds, opts, stepId, description, nextSyntheti
|
|
|
68844
69193
|
if (opts.toolNames && opts.toolNames.has(entry.name) || isHelper(entry.name)) {
|
|
68845
69194
|
fail(entry.node, `Cannot bind to "${entry.name}" \u2014 name collides with a tool or helper.`);
|
|
68846
69195
|
}
|
|
69196
|
+
assertNoFunctionCollision(entry.node, entry.name, scope, "Binding");
|
|
68847
69197
|
if (stepIds.has(entry.name)) {
|
|
68848
69198
|
fail(entry.node, `Step id "${entry.name}" is already used.`);
|
|
68849
69199
|
}
|
|
@@ -69026,6 +69376,7 @@ function walkTry(stmt, scope, stepIds, opts, stepId, description, nextSyntheticI
|
|
|
69026
69376
|
if (opts.toolNames && opts.toolNames.has(errorBinding) || isHelper(errorBinding)) {
|
|
69027
69377
|
fail(handler.param, `Catch binding "${errorBinding}" collides with a tool or helper.`);
|
|
69028
69378
|
}
|
|
69379
|
+
assertNoFunctionCollision(handler.param, errorBinding, scope, "Catch binding");
|
|
69029
69380
|
if (stepIds.has(errorBinding)) {
|
|
69030
69381
|
fail(handler.param, `Catch binding "${errorBinding}" collides with an existing step id.`);
|
|
69031
69382
|
}
|
|
@@ -69285,8 +69636,7 @@ function walkAgentInvocation(call, scope, stepId, description) {
|
|
|
69285
69636
|
if (!toolsNode) fail(arg, "agent requires `tools` (use [] for none).");
|
|
69286
69637
|
const tool_names = readStaticStringArray(toolsNode, "agent `tools`");
|
|
69287
69638
|
const modelNode = kw.get("model");
|
|
69288
|
-
|
|
69289
|
-
const model_id = readStaticString(modelNode, "agent `model`");
|
|
69639
|
+
const model_id = modelNode ? readStaticString(modelNode, "agent `model`") : void 0;
|
|
69290
69640
|
const outputNode = kw.get("output");
|
|
69291
69641
|
if (!outputNode) fail(arg, "agent requires `output`.");
|
|
69292
69642
|
const outputResult = agentOutputSpecSchema.safeParse(walkStaticJsonLiteral(outputNode));
|
|
@@ -69301,7 +69651,7 @@ function walkAgentInvocation(call, scope, stepId, description) {
|
|
|
69301
69651
|
instructions,
|
|
69302
69652
|
input,
|
|
69303
69653
|
tool_names,
|
|
69304
|
-
model_id,
|
|
69654
|
+
...model_id !== void 0 ? { model_id } : {},
|
|
69305
69655
|
output: outputResult.data
|
|
69306
69656
|
};
|
|
69307
69657
|
}
|
|
@@ -69402,6 +69752,133 @@ function readLeadingComments(stmt) {
|
|
|
69402
69752
|
return { id, description };
|
|
69403
69753
|
}
|
|
69404
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
|
+
|
|
69405
69882
|
// src/workflow_envelope.ts
|
|
69406
69883
|
var FALLBACK_ENVELOPE_PREFIX = "async function __workflow(): Promise<__WorkflowReturn | void> {\n";
|
|
69407
69884
|
var FALLBACK_ENVELOPE_SUFFIX = "\n}";
|
|
@@ -69489,9 +69966,16 @@ function checkOneWorkflowBody(tsApi, input) {
|
|
|
69489
69966
|
const offset = bodyLineOffset(wrapped);
|
|
69490
69967
|
const subset = subsetIssues(stripWorkflowHeader(wrapped), offset);
|
|
69491
69968
|
if (subset) return subset;
|
|
69969
|
+
const options = workflowCheckCompilerOptions(tsApi);
|
|
69492
69970
|
const program = tsApi.createProgram({
|
|
69493
69971
|
rootNames: [input.globalsPath, input.bodyPath],
|
|
69494
|
-
options
|
|
69972
|
+
options,
|
|
69973
|
+
host: createCheckedSourceHost(
|
|
69974
|
+
tsApi,
|
|
69975
|
+
options,
|
|
69976
|
+
input.bodyPath,
|
|
69977
|
+
rewriteAccumulatorAppends(tsApi, wrapped)
|
|
69978
|
+
)
|
|
69495
69979
|
});
|
|
69496
69980
|
const bodyFile = program.getSourceFile(input.bodyPath);
|
|
69497
69981
|
if (!bodyFile) {
|
|
@@ -69523,6 +70007,17 @@ function checkOneWorkflowBody(tsApi, input) {
|
|
|
69523
70007
|
}
|
|
69524
70008
|
return issues;
|
|
69525
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
|
+
}
|
|
69526
70021
|
function checkWorkflowBodies(tsApi, aliases) {
|
|
69527
70022
|
return aliases.map(({ alias, input }) => ({
|
|
69528
70023
|
alias,
|