@oxygen-agent/cli 1.832.1 → 1.832.9
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/README.md +1 -1
- package/dist/index.js +1 -1
- package/node_modules/@oxygen/formula/dist/evaluate.d.ts +6 -0
- package/node_modules/@oxygen/formula/dist/evaluate.js +13 -5
- package/node_modules/@oxygen/formula/dist/formula-functions.js +10 -2
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/node_modules/@oxygen/workflows/dist/graph/expression.js +40 -0
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -2835,7 +2835,7 @@ export function createProgram() {
|
|
|
2835
2835
|
}));
|
|
2836
2836
|
program
|
|
2837
2837
|
.command("support")
|
|
2838
|
-
.description("Open and track Plain support conversations for the active OXYGEN organization. In the app,
|
|
2838
|
+
.description("Open and track Plain support conversations for the active OXYGEN organization. In the app, the support chat is the widget in the bottom-right corner of every page. Retry is the only recovery control; it returns after each failed connection attempt and never creates a Thread. CLI filing is a zero-credit write to the canonical Plain queue. Guide: https://oxygen-agent.com/docs/surfaces/support.")
|
|
2839
2839
|
.addCommand(new Command("file")
|
|
2840
2840
|
.description("Create a real, zero-credit Plain support Thread immediately. This is a separate filing action, not the in-app Retry, and can create a second Thread: check `support list --status open` first and reply to the existing Thread for the same issue. There is no preview: review the exact subject, body, category, and severity before running it. Use when you're stuck on an OXYGEN operation.")
|
|
2841
2841
|
.requiredOption("--subject <subject>", "One-line summary of the problem.")
|
|
@@ -14,6 +14,12 @@ export type FormulaScope = {
|
|
|
14
14
|
* reference must fail loudly rather than silently evaluate to null.
|
|
15
15
|
*/
|
|
16
16
|
resolve(identifier: string): unknown;
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a direct identifier argument to COALESCE. Hosts may return null
|
|
19
|
+
* for a recognized-but-absent optional path while keeping typos, unknown
|
|
20
|
+
* roots, and identifiers nested inside larger expressions strict.
|
|
21
|
+
*/
|
|
22
|
+
resolveCoalescible?(identifier: string): unknown;
|
|
17
23
|
/**
|
|
18
24
|
* Lenient read used only by cross-row functions, which take a column key
|
|
19
25
|
* rather than an in-scope identifier and treat an absent value as blank.
|
|
@@ -6,15 +6,20 @@ export function evaluateFormulaInScope(expression, scope, options = {}) {
|
|
|
6
6
|
return evaluateFormulaAst(parseFormulaExpression(expression, options), scope);
|
|
7
7
|
}
|
|
8
8
|
export function evaluateFormulaAst(node, scope) {
|
|
9
|
+
return evaluateFormulaAstWithMode(node, scope, "strict");
|
|
10
|
+
}
|
|
11
|
+
function evaluateFormulaAstWithMode(node, scope, resolutionMode) {
|
|
9
12
|
switch (node.type) { // skipcq: JS-0047
|
|
10
13
|
case "literal":
|
|
11
14
|
return node.value;
|
|
12
15
|
case "identifier":
|
|
13
|
-
return scope.
|
|
16
|
+
return resolutionMode === "coalescible" && scope.resolveCoalescible
|
|
17
|
+
? scope.resolveCoalescible(node.name)
|
|
18
|
+
: scope.resolve(node.name);
|
|
14
19
|
case "unary":
|
|
15
|
-
return evaluateUnary(node.operator,
|
|
20
|
+
return evaluateUnary(node.operator, evaluateFormulaAstWithMode(node.argument, scope, "strict"));
|
|
16
21
|
case "binary":
|
|
17
|
-
return evaluateBinary(node.operator, () =>
|
|
22
|
+
return evaluateBinary(node.operator, () => evaluateFormulaAstWithMode(node.left, scope, "strict"), () => evaluateFormulaAstWithMode(node.right, scope, "strict"));
|
|
18
23
|
case "call": {
|
|
19
24
|
const normalized = node.name.toLowerCase();
|
|
20
25
|
if (CROSS_ROW_FUNCTIONS.has(normalized)) {
|
|
@@ -23,11 +28,14 @@ export function evaluateFormulaAst(node, scope) {
|
|
|
23
28
|
const spec = FORMULA_FUNCTION_REGISTRY.get(normalized);
|
|
24
29
|
if (spec)
|
|
25
30
|
checkFormulaFunctionArity(spec, node.args.length);
|
|
31
|
+
const evaluateArgument = (arg) => evaluateFormulaAstWithMode(arg, scope, normalized === "coalesce" && arg.type === "identifier"
|
|
32
|
+
? "coalescible"
|
|
33
|
+
: "strict");
|
|
26
34
|
if (spec?.lazy && spec.evaluateLazy) {
|
|
27
|
-
return spec.evaluateLazy(node.args.map((arg) => () =>
|
|
35
|
+
return spec.evaluateLazy(node.args.map((arg) => () => evaluateArgument(arg)));
|
|
28
36
|
}
|
|
29
37
|
if (spec?.evaluate) {
|
|
30
|
-
return spec.evaluate(node.args.map(
|
|
38
|
+
return spec.evaluate(node.args.map(evaluateArgument));
|
|
31
39
|
}
|
|
32
40
|
throw formulaExpressionError("Unsupported formula function.", {
|
|
33
41
|
function: node.name,
|
|
@@ -1017,11 +1017,19 @@ const SPECS = [
|
|
|
1017
1017
|
name: "coalesce",
|
|
1018
1018
|
category: "null_handling",
|
|
1019
1019
|
signature: "coalesce(a, b, ...)",
|
|
1020
|
-
description: "First non-blank argument, or null when all are blank.",
|
|
1020
|
+
description: "First non-blank argument, or null when all are blank. In Workflow formulas, an absent descendant of a valid trigger, step, or loop binding counts as blank.",
|
|
1021
1021
|
minArgs: 0,
|
|
1022
1022
|
maxArgs: null,
|
|
1023
|
+
lazy: true,
|
|
1023
1024
|
examples: [{ expression: "coalesce(work_email, personal_email)", result: "jo@acme.com" }],
|
|
1024
|
-
|
|
1025
|
+
evaluateLazy: (thunks) => {
|
|
1026
|
+
for (const thunk of thunks) {
|
|
1027
|
+
const value = thunk();
|
|
1028
|
+
if (!isBlankFormulaValue(value))
|
|
1029
|
+
return value;
|
|
1030
|
+
}
|
|
1031
|
+
return null;
|
|
1032
|
+
},
|
|
1025
1033
|
}),
|
|
1026
1034
|
spec({
|
|
1027
1035
|
name: "default",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const OXYGEN_VERSION = "1.832.
|
|
1
|
+
export declare const OXYGEN_VERSION = "1.832.9";
|
|
2
2
|
export declare const OXYGEN_MINIMUM_CLI_VERSION = "1.181.0";
|
|
3
3
|
export declare const MANAGED_INBOX_MINIMUM_CLI_VERSION = "1.326.2";
|
|
4
4
|
export declare const SUPPORT_AGENT_REPLY_MINIMUM_CLI_VERSION = "1.747.0";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const OXYGEN_VERSION = "1.832.
|
|
1
|
+
export const OXYGEN_VERSION = "1.832.9";
|
|
2
2
|
// The GLOBAL CLI compatibility floor: the oldest CLI allowed to call any
|
|
3
3
|
// operational route. Raising it hard-rejects every older CLI from the entire
|
|
4
4
|
// product, so it obeys one law, enforced by scripts/ci/cli-min-version-gate.mjs:
|
|
@@ -559,6 +559,17 @@ function formulaScopeFor(scope) {
|
|
|
559
559
|
const found = lookupFormulaIdentifier(scope, identifier);
|
|
560
560
|
return found.found ? found.value : undefined;
|
|
561
561
|
},
|
|
562
|
+
resolveCoalescible(identifier) {
|
|
563
|
+
const found = lookupFormulaIdentifier(scope, identifier);
|
|
564
|
+
if (found.found)
|
|
565
|
+
return found.value;
|
|
566
|
+
if (isCoalescibleWorkflowFormulaPath(scope, identifier))
|
|
567
|
+
return null;
|
|
568
|
+
throw formulaExpressionError("Unknown identifier in workflow formula.", {
|
|
569
|
+
identifier,
|
|
570
|
+
hint: "Reference a workflow input, a node id, or a loop node id.",
|
|
571
|
+
});
|
|
572
|
+
},
|
|
562
573
|
};
|
|
563
574
|
}
|
|
564
575
|
function lookupFormulaIdentifier(scope, identifier) {
|
|
@@ -587,6 +598,35 @@ function lookupFormulaIdentifier(scope, identifier) {
|
|
|
587
598
|
return { found: true, value: readSegment(loop, "item") };
|
|
588
599
|
return { found: false };
|
|
589
600
|
}
|
|
601
|
+
/**
|
|
602
|
+
* COALESCE may absorb an optional value below a real workflow binding, but it
|
|
603
|
+
* must not turn a misspelled root, node id, or structural segment into blank.
|
|
604
|
+
*/
|
|
605
|
+
function isCoalescibleWorkflowFormulaPath(scope, identifier) {
|
|
606
|
+
const segments = parseScopePath(identifier);
|
|
607
|
+
if (!segments)
|
|
608
|
+
return false;
|
|
609
|
+
const [root, owner, slot] = segments;
|
|
610
|
+
if (root === "trigger") {
|
|
611
|
+
return segments.length >= 3
|
|
612
|
+
&& scope.trigger !== undefined
|
|
613
|
+
&& (owner === "input" || owner === "payload");
|
|
614
|
+
}
|
|
615
|
+
if (root === "steps") {
|
|
616
|
+
return segments.length >= 4
|
|
617
|
+
&& owner !== undefined
|
|
618
|
+
&& slot === "output"
|
|
619
|
+
&& Object.prototype.hasOwnProperty.call(scope.steps, owner);
|
|
620
|
+
}
|
|
621
|
+
if (root === "loop") {
|
|
622
|
+
return segments.length >= 4
|
|
623
|
+
&& owner !== undefined
|
|
624
|
+
&& slot === "item"
|
|
625
|
+
&& scope.loop !== undefined
|
|
626
|
+
&& Object.prototype.hasOwnProperty.call(scope.loop, owner);
|
|
627
|
+
}
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
590
630
|
function isPlainObject(value) {
|
|
591
631
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
592
632
|
}
|