skillscript-runtime 0.15.6 → 0.16.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/README.md +23 -24
- package/dist/compile.js +25 -71
- package/dist/compile.js.map +1 -1
- package/dist/errors.d.ts +2 -7
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -14
- package/dist/errors.js.map +1 -1
- package/dist/filters.d.ts.map +1 -1
- package/dist/filters.js +21 -1
- package/dist/filters.js.map +1 -1
- package/dist/help-content.d.ts.map +1 -1
- package/dist/help-content.js +74 -103
- package/dist/help-content.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +43 -214
- package/dist/lint.js.map +1 -1
- package/dist/mutation-gate.d.ts +7 -13
- package/dist/mutation-gate.d.ts.map +1 -1
- package/dist/mutation-gate.js +6 -11
- package/dist/mutation-gate.js.map +1 -1
- package/dist/parser.d.ts +25 -74
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +30 -264
- package/dist/parser.js.map +1 -1
- package/dist/runtime.d.ts +1 -10
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +76 -188
- package/dist/runtime.js.map +1 -1
- package/dist/skill-manager.js +1 -1
- package/dist/skill-manager.js.map +1 -1
- package/docs/language-reference.md +3 -24
- package/examples/skillscripts/hello-world.skill.provenance.json +1 -1
- package/package.json +1 -1
- package/examples/skillscripts/cut-release-tag.skill.md +0 -40
package/dist/parser.d.ts
CHANGED
|
@@ -1,52 +1,20 @@
|
|
|
1
|
-
export type OpKind = "$" | "$set" | "$append" | "?" | "
|
|
1
|
+
export type OpKind = "$" | "$set" | "$append" | "?" | "shell" | "emit" | "foreach" | "if" | "inline" | "file_read" | "file_write" | "notify";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Runtime-intrinsic function-call names. Closed set of ops the language
|
|
4
|
+
* implements directly (no MCP dispatch). Function-call grammar:
|
|
5
5
|
* `verb(kwarg=value, ...) [-> BINDING]`.
|
|
6
6
|
*
|
|
7
7
|
* Anything else with function-call shape is rejected by parser with a
|
|
8
8
|
* remediation pointing at `$ tool args -> R` for MCP dispatch.
|
|
9
9
|
*/
|
|
10
|
-
export declare const RUNTIME_INTRINSIC_FN_NAMES: readonly ["emit", "
|
|
10
|
+
export declare const RUNTIME_INTRINSIC_FN_NAMES: readonly ["emit", "inline", "execute_skill", "shell", "file_read", "file_write", "notify"];
|
|
11
11
|
export interface SkillOp {
|
|
12
12
|
kind: OpKind;
|
|
13
13
|
body: string;
|
|
14
14
|
outputVar?: string;
|
|
15
15
|
mcpConnector?: string;
|
|
16
|
-
retrievalParams?: {
|
|
17
|
-
mode: string;
|
|
18
|
-
query: string;
|
|
19
|
-
/** Integer literal OR a `$(VAR)`-style ref string. Runtime substitutes refs then parses to int. */
|
|
20
|
-
limit: number | string;
|
|
21
|
-
connector: string;
|
|
22
|
-
extra: Record<string, string>;
|
|
23
|
-
/**
|
|
24
|
-
* Op-level fallback (per language reference §9). When the retrieval
|
|
25
|
-
* throws or returns an empty array, runtime binds this value (string)
|
|
26
|
-
* to the output var instead of propagating the error.
|
|
27
|
-
*/
|
|
28
|
-
fallback?: string;
|
|
29
|
-
};
|
|
30
|
-
localModelParams?: {
|
|
31
|
-
prompt: string;
|
|
32
|
-
model?: string;
|
|
33
|
-
/** Integer literal OR a `$(VAR)`-style ref string. Runtime substitutes refs then parses to int. */
|
|
34
|
-
maxTokens?: number | string;
|
|
35
|
-
/**
|
|
36
|
-
* Per-op timeout override in SECONDS (per decision 7 resolution chain).
|
|
37
|
-
* Integer literal OR `$(VAR)` ref. Per-op wins over skill `# Timeout:`
|
|
38
|
-
* header, connector default, and built-in fallback.
|
|
39
|
-
*/
|
|
40
|
-
timeoutSeconds?: number | string;
|
|
41
|
-
/**
|
|
42
|
-
* Op-level fallback (per language reference §9). When the model call
|
|
43
|
-
* throws or returns an empty (trimmed) response, runtime binds this
|
|
44
|
-
* value to the output var instead of propagating the error.
|
|
45
|
-
*/
|
|
46
|
-
fallback?: string;
|
|
47
|
-
};
|
|
48
16
|
/**
|
|
49
|
-
*
|
|
17
|
+
* `inline` ops only: skill name + optional key=value args passed as inputs
|
|
50
18
|
* when the target is procedural (runtime invocation), ignored when the
|
|
51
19
|
* target is data-typed (compile-time inline). `outputVar` captures the
|
|
52
20
|
* result of procedural invocations; absent for data inlines.
|
|
@@ -56,23 +24,18 @@ export interface SkillOp {
|
|
|
56
24
|
args: Record<string, string>;
|
|
57
25
|
};
|
|
58
26
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* `
|
|
63
|
-
*
|
|
64
|
-
* decision 2 — one binary, no shell interpretation.
|
|
27
|
+
* `shell` ops only: when `unsafe=true` kwarg is set, the runtime routes
|
|
28
|
+
* through full-shell exec (vs default structured-spawn sandbox). Refused
|
|
29
|
+
* unless `runtime.enable_unsafe_shell = true` (default false). Default
|
|
30
|
+
* `shell(...)` ops route through structured spawn — one binary, no shell
|
|
31
|
+
* interpretation.
|
|
65
32
|
*/
|
|
66
33
|
policy?: "unsafe";
|
|
67
34
|
setName?: string;
|
|
68
35
|
setValue?: string;
|
|
69
36
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* runtime binds this value to the output var instead of propagating
|
|
73
|
-
* the error. `~` and `>` ops carry fallback on their params bag for
|
|
74
|
-
* type-specific coercion; `$` returns are heterogeneous (objects,
|
|
75
|
-
* arrays, strings) so it lives at the op level.
|
|
37
|
+
* Op-level fallback. On op throw or empty result, runtime binds this value
|
|
38
|
+
* to the output var instead of propagating the error.
|
|
76
39
|
*/
|
|
77
40
|
fallback?: string;
|
|
78
41
|
foreachIter?: string;
|
|
@@ -84,7 +47,7 @@ export interface SkillOp {
|
|
|
84
47
|
}>;
|
|
85
48
|
ifElseBody?: SkillOp[];
|
|
86
49
|
/**
|
|
87
|
-
*
|
|
50
|
+
* `file_read` / `file_write` op params. `path` is the filesystem path
|
|
88
51
|
* (may contain `${VAR}` substitutions resolved at runtime). `content` is
|
|
89
52
|
* the body to write (file_write only).
|
|
90
53
|
*/
|
|
@@ -93,41 +56,29 @@ export interface SkillOp {
|
|
|
93
56
|
content?: string;
|
|
94
57
|
};
|
|
95
58
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
59
|
+
* `notify` op params. `agent` is the target agent identifier (required,
|
|
60
|
+
* may contain `${VAR}` substitutions resolved at runtime). `message` is
|
|
61
|
+
* the explicit message body (optional — runtime defaults to the joined
|
|
62
|
+
* accumulated emissions when absent). `connectors` is an optional
|
|
63
|
+
* restriction list — when present, only AgentConnectors whose registered
|
|
64
|
+
* name is in this list are dispatched to.
|
|
102
65
|
*/
|
|
103
66
|
notifyParams?: {
|
|
104
67
|
agent: string;
|
|
105
68
|
message?: string;
|
|
106
69
|
connectors?: string[];
|
|
107
|
-
/**
|
|
70
|
+
/** Adopter-defined routing vocab; flows to `meta.event_type`. */
|
|
108
71
|
event_type?: string;
|
|
109
|
-
/**
|
|
72
|
+
/** Reply-correlation primitive; flows to `meta.correlation_id`. */
|
|
110
73
|
correlation_id?: string;
|
|
111
74
|
};
|
|
112
75
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
76
|
+
* Inline `approved="reason"` kwarg captured on mutation-class function-call
|
|
77
|
+
* ops. Author intent marker; lint's `unconfirmed-mutation` rule accepts
|
|
78
|
+
* presence (any non-empty string) as per-op authorization when
|
|
79
|
+
* `# Autonomous: true` is not declared.
|
|
117
80
|
*/
|
|
118
81
|
approved?: string;
|
|
119
|
-
/**
|
|
120
|
-
* v0.7.1 — source-form marker. The parser collapses canonical
|
|
121
|
-
* function-call ops (`emit(text="...")`, `shell(command="...")`, etc.)
|
|
122
|
-
* to the same AST `kind` as the legacy symbol form (`! ...`, `@ ...`,
|
|
123
|
-
* etc.) so runtime/render/lint share one dispatch path. This field
|
|
124
|
-
* preserves which surface the author wrote so the `deprecated-symbol-op`
|
|
125
|
-
* lint can fire only on legacy-form occurrences.
|
|
126
|
-
*
|
|
127
|
-
* `"function-call"` = author wrote `verb(kwargs)` shape.
|
|
128
|
-
* `undefined` = author wrote symbol form (legacy).
|
|
129
|
-
*/
|
|
130
|
-
sourceForm?: "function-call";
|
|
131
82
|
}
|
|
132
83
|
export interface SkillTarget {
|
|
133
84
|
name: string;
|
package/dist/parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,MAAM,GAAG,GAAG,GAAG,MAAM,GAAG,SAAS,GAAG,GAAG,GAAG,
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
|
package/dist/parser.js
CHANGED
|
@@ -2,22 +2,21 @@
|
|
|
2
2
|
// no resolution against external state. Semantic analysis (variable resolution,
|
|
3
3
|
// data-skill inlining, topo-sort) lives in compile.ts.
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Runtime-intrinsic function-call names. Closed set of ops the language
|
|
6
|
+
* implements directly (no MCP dispatch). Function-call grammar:
|
|
7
7
|
* `verb(kwarg=value, ...) [-> BINDING]`.
|
|
8
8
|
*
|
|
9
9
|
* Anything else with function-call shape is rejected by parser with a
|
|
10
10
|
* remediation pointing at `$ tool args -> R` for MCP dispatch.
|
|
11
11
|
*/
|
|
12
12
|
export const RUNTIME_INTRINSIC_FN_NAMES = [
|
|
13
|
-
"emit", //
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"shell", // → @ (local subprocess)
|
|
13
|
+
"emit", // output to skill consumer
|
|
14
|
+
"inline", // compile-time skill composition
|
|
15
|
+
"execute_skill", // runtime skill invocation (dispatches via $ execute_skill)
|
|
16
|
+
"shell", // local subprocess
|
|
18
17
|
"file_read", // read file contents at runtime
|
|
19
18
|
"file_write", // write file contents at runtime
|
|
20
|
-
"notify", //
|
|
19
|
+
"notify", // mid-skill synchronous agent alert via AgentConnector(s)
|
|
21
20
|
];
|
|
22
21
|
/**
|
|
23
22
|
* Case-insensitive accept, canonical-form return. The `allowed` list defines
|
|
@@ -37,8 +36,6 @@ function normalizeEnumValue(raw, allowed) {
|
|
|
37
36
|
const REQUIRES_LINE = /^(user-var|system-var):([A-Za-z0-9_-]+)\s*(?:→|->)\s*([A-Za-z_][\w-]*)\s*(?:\(\s*fallback\s*:\s*(.+?)\s*\)\s*)?$/;
|
|
38
37
|
/** Capability token: `connector_type.feature_flag`. Matches one space-separated token of a capability `# Requires:` line. */
|
|
39
38
|
const CAPABILITY_TOKEN = /^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_]*$/;
|
|
40
|
-
/** `&` op: `& skill-name [arg=value ...] [-> VARNAME]`. Skill names follow the same charset as filesystem-safe identifiers (alphanumeric, hyphen, underscore). */
|
|
41
|
-
const AMPERSAND_OP_REGEX = /^&\s+([A-Za-z0-9][\w-]*)\s*(.*?)(?:\s*->\s*([A-Za-z_]\w*))?(?:\s+\(fallback\s*:\s*(.+?)\))?\s*$/s;
|
|
42
39
|
// v0.7.2 — `(.*)` widened to `([\s\S]*)` so multi-line triple-quote
|
|
43
40
|
// (`"""..."""`) values fold into a single $set value capture. Without the
|
|
44
41
|
// dotall-equivalent, the `.` excludes newlines and the regex stops at the
|
|
@@ -75,24 +72,9 @@ function extractApprovedKwarg(body) {
|
|
|
75
72
|
return undefined;
|
|
76
73
|
return m[1] ?? m[2] ?? "";
|
|
77
74
|
}
|
|
78
|
-
//
|
|
79
|
-
//
|
|
75
|
+
// P0.5 detect missing-space dispatch: `$<word> args` where `<word>` isn't
|
|
76
|
+
// `set`/`append` (the only legitimate no-space `$`-prefix verbs).
|
|
80
77
|
const NO_SPACE_DISPATCH_RE = /^\$(?!set\b|append\b)\w+\s/;
|
|
81
|
-
/**
|
|
82
|
-
* `>` and `~` ops accept optional trailing `(fallback: <value>)` per
|
|
83
|
-
* language reference §9 (Error Handling, Layer 3). Fires when the op
|
|
84
|
-
* throws or returns empty — runtime binds the fallback value to the
|
|
85
|
-
* output var and continues without surfacing the error.
|
|
86
|
-
*
|
|
87
|
-
* Value is permissive (matching `# Requires:` cascade convention): bare
|
|
88
|
-
* identifiers (`ip-based`), quoted strings (`"weather unavailable"`),
|
|
89
|
-
* array literals (`[]`, `[a, b]`), and arbitrary text between the colon
|
|
90
|
-
* and the closing paren are all accepted. Parser stores the raw form;
|
|
91
|
-
* runtime applies `coerceLiteralValue` for `>` (binds array on `[...]`)
|
|
92
|
-
* and the raw string for `~` (model response shape).
|
|
93
|
-
*/
|
|
94
|
-
const RETRIEVAL_OP_REGEX = /^>\s+(.+?)\s+->\s+([A-Za-z_]\w*)(?:\s+\(fallback\s*:\s*(.+?)\))?\s*$/s;
|
|
95
|
-
const LOCAL_MODEL_OP_REGEX = /^~\s+(.+?)\s+->\s+([A-Za-z_]\w*)(?:\s+\(fallback\s*:\s*(.+?)\))?\s*$/s;
|
|
96
78
|
const MCP_CONNECTOR_PREFIX = /^([a-z_][a-z0-9_-]*)\.(?=[A-Za-z_])([\s\S]*)$/;
|
|
97
79
|
// Narrow v1 condition grammar.
|
|
98
80
|
// v0.3.4: filter chain support — each `(REF)(|filter)?` became `(REF)(|filter)*`
|
|
@@ -686,109 +668,6 @@ function splitMcpConnectorPrefix(body) {
|
|
|
686
668
|
return { connector: undefined, rest: body };
|
|
687
669
|
return { connector: m[1], rest: m[2] };
|
|
688
670
|
}
|
|
689
|
-
function parseRetrievalArgs(argsStr, targetName) {
|
|
690
|
-
const errors = [];
|
|
691
|
-
const map = {};
|
|
692
|
-
const tokens = tokenizeKeywordArgs(argsStr);
|
|
693
|
-
for (const tok of tokens) {
|
|
694
|
-
const eq = tok.indexOf("=");
|
|
695
|
-
if (eq === -1) {
|
|
696
|
-
errors.push(`Malformed \`>\` arg '${tok}' in target '${targetName}' — expected key=value`);
|
|
697
|
-
continue;
|
|
698
|
-
}
|
|
699
|
-
const key = tok.slice(0, eq).trim();
|
|
700
|
-
const rawValue = tok.slice(eq + 1);
|
|
701
|
-
map[key] = processSetValue(rawValue);
|
|
702
|
-
}
|
|
703
|
-
for (const required of ["mode", "query", "limit"]) {
|
|
704
|
-
if (!(required in map) || map[required] === "") {
|
|
705
|
-
errors.push(`\`>\` op in target '${targetName}' missing required param '${required}'`);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
// Defer integer validation when the value contains a `$(VAR)` ref — runtime
|
|
709
|
-
// substitutes + parses after the ref resolves. Literal numerics still
|
|
710
|
-
// validate at parse time.
|
|
711
|
-
let limit = 0;
|
|
712
|
-
const rawLimit = map["limit"] ?? "";
|
|
713
|
-
if (/\$[(\{]/.test(rawLimit)) {
|
|
714
|
-
limit = rawLimit;
|
|
715
|
-
}
|
|
716
|
-
else {
|
|
717
|
-
const n = parseInt(rawLimit, 10);
|
|
718
|
-
if (!Number.isFinite(n) || n <= 0) {
|
|
719
|
-
errors.push(`\`>\` op in target '${targetName}': 'limit' must be a positive integer or a \`$(VAR)\` ref (got '${rawLimit}')`);
|
|
720
|
-
}
|
|
721
|
-
else {
|
|
722
|
-
limit = n;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
const extra = {};
|
|
726
|
-
for (const [k, v] of Object.entries(map)) {
|
|
727
|
-
if (k === "mode" || k === "query" || k === "limit" || k === "connector")
|
|
728
|
-
continue;
|
|
729
|
-
extra[k] = v;
|
|
730
|
-
}
|
|
731
|
-
return {
|
|
732
|
-
params: {
|
|
733
|
-
mode: map["mode"] ?? "",
|
|
734
|
-
query: map["query"] ?? "",
|
|
735
|
-
limit,
|
|
736
|
-
connector: map["connector"] ?? "primary",
|
|
737
|
-
extra,
|
|
738
|
-
},
|
|
739
|
-
errors,
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
function parseLocalModelArgs(argsStr, targetName) {
|
|
743
|
-
const errors = [];
|
|
744
|
-
const map = {};
|
|
745
|
-
const tokens = tokenizeKeywordArgs(argsStr);
|
|
746
|
-
for (const tok of tokens) {
|
|
747
|
-
const eq = tok.indexOf("=");
|
|
748
|
-
if (eq === -1) {
|
|
749
|
-
errors.push(`Malformed \`~\` arg '${tok}' in target '${targetName}' — expected key=value`);
|
|
750
|
-
continue;
|
|
751
|
-
}
|
|
752
|
-
const key = tok.slice(0, eq).trim();
|
|
753
|
-
const rawValue = tok.slice(eq + 1);
|
|
754
|
-
map[key] = processSetValue(rawValue);
|
|
755
|
-
}
|
|
756
|
-
const recognized = new Set(["prompt", "model", "maxTokens", "timeoutSeconds"]);
|
|
757
|
-
for (const key of Object.keys(map)) {
|
|
758
|
-
if (!recognized.has(key)) {
|
|
759
|
-
errors.push(`\`~\` op in target '${targetName}': unrecognized param '${key}' — strict grammar allows prompt/model/maxTokens/timeoutSeconds only. Interpolate context into the prompt string via $(...) instead.`);
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
if (!("prompt" in map) || map["prompt"] === "") {
|
|
763
|
-
errors.push(`\`~\` op in target '${targetName}' missing required param 'prompt'`);
|
|
764
|
-
}
|
|
765
|
-
// Defer integer validation when the value contains a `$(VAR)` ref.
|
|
766
|
-
function deferInt(key) {
|
|
767
|
-
if (!(key in map))
|
|
768
|
-
return undefined;
|
|
769
|
-
const raw = map[key];
|
|
770
|
-
if (/\$[(\{]/.test(raw))
|
|
771
|
-
return raw;
|
|
772
|
-
const n = parseInt(raw, 10);
|
|
773
|
-
if (!Number.isFinite(n) || n <= 0) {
|
|
774
|
-
errors.push(`\`~\` op in target '${targetName}': '${key}' must be a positive integer or a \`$(VAR)\` ref (got '${raw}')`);
|
|
775
|
-
return undefined;
|
|
776
|
-
}
|
|
777
|
-
return n;
|
|
778
|
-
}
|
|
779
|
-
const maxTokens = deferInt("maxTokens");
|
|
780
|
-
const timeoutSeconds = deferInt("timeoutSeconds");
|
|
781
|
-
const params = {
|
|
782
|
-
prompt: map["prompt"] ?? "",
|
|
783
|
-
};
|
|
784
|
-
if ("model" in map && map["model"] !== "")
|
|
785
|
-
params.model = map["model"];
|
|
786
|
-
if (maxTokens !== undefined)
|
|
787
|
-
params.maxTokens = maxTokens;
|
|
788
|
-
if (timeoutSeconds !== undefined)
|
|
789
|
-
params.timeoutSeconds = timeoutSeconds;
|
|
790
|
-
return { params, errors };
|
|
791
|
-
}
|
|
792
671
|
function popToDepth(stack, targetDepth) {
|
|
793
672
|
while (stack.length > 0 && stack[stack.length - 1].depth > targetDepth) {
|
|
794
673
|
stack.pop();
|
|
@@ -1349,82 +1228,18 @@ export function parse(source) {
|
|
|
1349
1228
|
continue;
|
|
1350
1229
|
}
|
|
1351
1230
|
if (stripped0.startsWith("> ")) {
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
result.parseErrors.push(`Malformed \`>\` op in target '${currentTarget.name}' — expected \`> key=value ... -> VARNAME [(fallback: "value")]\``);
|
|
1355
|
-
continue;
|
|
1356
|
-
}
|
|
1357
|
-
const [, argsStr, outputVar, fallback] = match;
|
|
1358
|
-
const parsed = parseRetrievalArgs(argsStr, currentTarget.name);
|
|
1359
|
-
if (parsed.errors.length > 0) {
|
|
1360
|
-
for (const e of parsed.errors)
|
|
1361
|
-
result.parseErrors.push(e);
|
|
1362
|
-
continue;
|
|
1363
|
-
}
|
|
1364
|
-
if (fallback !== undefined)
|
|
1365
|
-
parsed.params.fallback = processSetValue(fallback);
|
|
1366
|
-
opBucket.push({
|
|
1367
|
-
kind: ">",
|
|
1368
|
-
body: stripped0,
|
|
1369
|
-
outputVar: outputVar,
|
|
1370
|
-
retrievalParams: parsed.params,
|
|
1371
|
-
});
|
|
1231
|
+
result.parseErrors.push(`Legacy \`>\` retrieval op in target '${currentTarget.name}' is no longer supported. ` +
|
|
1232
|
+
`Use \`$ data_read mode="fts|semantic|rerank" query="..." limit=N -> R\` (or qualify the connector: \`$ <connector>.data_read ...\`).`);
|
|
1372
1233
|
continue;
|
|
1373
1234
|
}
|
|
1374
1235
|
if (stripped0.startsWith("~ ")) {
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
result.parseErrors.push(`Malformed \`~\` op in target '${currentTarget.name}' — expected \`~ key=value ... -> VARNAME [(fallback: "value")]\``);
|
|
1378
|
-
continue;
|
|
1379
|
-
}
|
|
1380
|
-
const [, argsStr, outputVar, fallback] = match;
|
|
1381
|
-
const parsed = parseLocalModelArgs(argsStr, currentTarget.name);
|
|
1382
|
-
if (parsed.errors.length > 0) {
|
|
1383
|
-
for (const e of parsed.errors)
|
|
1384
|
-
result.parseErrors.push(e);
|
|
1385
|
-
continue;
|
|
1386
|
-
}
|
|
1387
|
-
if (fallback !== undefined)
|
|
1388
|
-
parsed.params.fallback = processSetValue(fallback);
|
|
1389
|
-
opBucket.push({
|
|
1390
|
-
kind: "~",
|
|
1391
|
-
body: stripped0,
|
|
1392
|
-
outputVar: outputVar,
|
|
1393
|
-
localModelParams: parsed.params,
|
|
1394
|
-
});
|
|
1236
|
+
result.parseErrors.push(`Legacy \`~\` LocalModel op in target '${currentTarget.name}' is no longer supported. ` +
|
|
1237
|
+
`Use \`$ llm prompt="..." [maxTokens=N] [model="..."] -> R\` (op-level \`timeout=N\` kwarg and trailing \`(fallback: "value")\` are honored).`);
|
|
1395
1238
|
continue;
|
|
1396
1239
|
}
|
|
1397
1240
|
if (stripped0.startsWith("& ")) {
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
result.parseErrors.push(`Malformed \`&\` op in target '${currentTarget.name}' — expected \`& skill-name [key=value ...] [-> VARNAME]\``);
|
|
1401
|
-
continue;
|
|
1402
|
-
}
|
|
1403
|
-
const [, skillName, argsStr, outputVar, ampFallback] = match;
|
|
1404
|
-
const args = {};
|
|
1405
|
-
const tokens = tokenizeKeywordArgs(argsStr ?? "");
|
|
1406
|
-
let argError = false;
|
|
1407
|
-
for (const tok of tokens) {
|
|
1408
|
-
const eq = tok.indexOf("=");
|
|
1409
|
-
if (eq === -1) {
|
|
1410
|
-
result.parseErrors.push(`Malformed \`&\` arg '${tok}' in target '${currentTarget.name}' — expected key=value`);
|
|
1411
|
-
argError = true;
|
|
1412
|
-
continue;
|
|
1413
|
-
}
|
|
1414
|
-
args[tok.slice(0, eq).trim()] = processSetValue(tok.slice(eq + 1));
|
|
1415
|
-
}
|
|
1416
|
-
if (argError)
|
|
1417
|
-
continue;
|
|
1418
|
-
const ampOp = {
|
|
1419
|
-
kind: "&",
|
|
1420
|
-
body: stripped0,
|
|
1421
|
-
ampParams: { skillName: skillName, args },
|
|
1422
|
-
};
|
|
1423
|
-
if (outputVar !== undefined)
|
|
1424
|
-
ampOp.outputVar = outputVar;
|
|
1425
|
-
if (ampFallback !== undefined)
|
|
1426
|
-
ampOp.fallback = processSetValue(ampFallback);
|
|
1427
|
-
opBucket.push(ampOp);
|
|
1241
|
+
result.parseErrors.push(`Legacy \`&\` inline op in target '${currentTarget.name}' is no longer supported. ` +
|
|
1242
|
+
`Use \`inline(skill="...")\` for compile-time data-skill inlines, or \`execute_skill(name="...", ...) -> R\` for runtime composition.`);
|
|
1428
1243
|
continue;
|
|
1429
1244
|
}
|
|
1430
1245
|
if (stripped0.startsWith("foreach ")) {
|
|
@@ -1515,34 +1330,21 @@ export function parse(source) {
|
|
|
1515
1330
|
}
|
|
1516
1331
|
const text = kwArgs["text"] ?? "";
|
|
1517
1332
|
opBucket.push({
|
|
1518
|
-
kind: "
|
|
1333
|
+
kind: "emit",
|
|
1519
1334
|
body: text,
|
|
1520
1335
|
...(approved !== undefined ? { approved } : {}),
|
|
1521
|
-
sourceForm: "function-call",
|
|
1522
|
-
});
|
|
1523
|
-
continue;
|
|
1524
|
-
}
|
|
1525
|
-
if (fnName === "ask") {
|
|
1526
|
-
const prompt = kwArgs["prompt"] ?? "";
|
|
1527
|
-
opBucket.push({
|
|
1528
|
-
kind: "??",
|
|
1529
|
-
body: prompt,
|
|
1530
|
-
...(outputVar !== undefined ? { outputVar } : {}),
|
|
1531
|
-
...(approved !== undefined ? { approved } : {}),
|
|
1532
|
-
sourceForm: "function-call",
|
|
1533
1336
|
});
|
|
1534
1337
|
continue;
|
|
1535
1338
|
}
|
|
1536
1339
|
if (fnName === "inline") {
|
|
1537
1340
|
const skill = kwArgs["skill"] ?? "";
|
|
1538
1341
|
opBucket.push({
|
|
1539
|
-
kind: "
|
|
1342
|
+
kind: "inline",
|
|
1540
1343
|
body: stripped0,
|
|
1541
1344
|
ampParams: { skillName: skill, args: {} },
|
|
1542
1345
|
...(outputVar !== undefined ? { outputVar } : {}),
|
|
1543
1346
|
...(fallback !== undefined ? { fallback } : {}),
|
|
1544
1347
|
...(approved !== undefined ? { approved } : {}),
|
|
1545
|
-
sourceForm: "function-call",
|
|
1546
1348
|
});
|
|
1547
1349
|
continue;
|
|
1548
1350
|
}
|
|
@@ -1568,7 +1370,6 @@ export function parse(source) {
|
|
|
1568
1370
|
...(outputVar !== undefined ? { outputVar } : {}),
|
|
1569
1371
|
...(fallback !== undefined ? { fallback } : {}),
|
|
1570
1372
|
...(approved !== undefined ? { approved } : {}),
|
|
1571
|
-
sourceForm: "function-call",
|
|
1572
1373
|
});
|
|
1573
1374
|
continue;
|
|
1574
1375
|
}
|
|
@@ -1576,13 +1377,12 @@ export function parse(source) {
|
|
|
1576
1377
|
const command = kwArgs["command"] ?? "";
|
|
1577
1378
|
const unsafe = kwArgs["unsafe"] === "true";
|
|
1578
1379
|
opBucket.push({
|
|
1579
|
-
kind: "
|
|
1380
|
+
kind: "shell",
|
|
1580
1381
|
body: command,
|
|
1581
1382
|
...(unsafe ? { policy: "unsafe" } : {}),
|
|
1582
1383
|
...(outputVar !== undefined ? { outputVar } : {}),
|
|
1583
1384
|
...(fallback !== undefined ? { fallback } : {}),
|
|
1584
1385
|
...(approved !== undefined ? { approved } : {}),
|
|
1585
|
-
sourceForm: "function-call",
|
|
1586
1386
|
});
|
|
1587
1387
|
continue;
|
|
1588
1388
|
}
|
|
@@ -1594,7 +1394,6 @@ export function parse(source) {
|
|
|
1594
1394
|
fileParams: { path },
|
|
1595
1395
|
...(outputVar !== undefined ? { outputVar } : {}),
|
|
1596
1396
|
...(fallback !== undefined ? { fallback } : {}),
|
|
1597
|
-
sourceForm: "function-call",
|
|
1598
1397
|
});
|
|
1599
1398
|
continue;
|
|
1600
1399
|
}
|
|
@@ -1607,7 +1406,6 @@ export function parse(source) {
|
|
|
1607
1406
|
fileParams: { path, content },
|
|
1608
1407
|
...(outputVar !== undefined ? { outputVar } : {}),
|
|
1609
1408
|
...(approved !== undefined ? { approved } : {}),
|
|
1610
|
-
sourceForm: "function-call",
|
|
1611
1409
|
});
|
|
1612
1410
|
continue;
|
|
1613
1411
|
}
|
|
@@ -1655,7 +1453,6 @@ export function parse(source) {
|
|
|
1655
1453
|
...(outputVar !== undefined ? { outputVar } : {}),
|
|
1656
1454
|
...(fallback !== undefined ? { fallback } : {}),
|
|
1657
1455
|
...(approved !== undefined ? { approved } : {}),
|
|
1658
|
-
sourceForm: "function-call",
|
|
1659
1456
|
});
|
|
1660
1457
|
continue;
|
|
1661
1458
|
}
|
|
@@ -1670,19 +1467,11 @@ export function parse(source) {
|
|
|
1670
1467
|
let kind = null;
|
|
1671
1468
|
let body = "";
|
|
1672
1469
|
let mcpConnectorForOp = undefined;
|
|
1673
|
-
|
|
1674
|
-
let atOutputVar = undefined;
|
|
1675
|
-
let atFallback = undefined;
|
|
1676
|
-
// Check `??` before `?`, `$set` before `$`.
|
|
1470
|
+
// Check `??`/`$set` before bare `?`/`$`.
|
|
1677
1471
|
if (stripped.startsWith("?? ") || stripped === "??") {
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
opBucket.push({ kind: "??", body: m[1].trim(), outputVar: m[2] });
|
|
1682
|
-
}
|
|
1683
|
-
else {
|
|
1684
|
-
opBucket.push({ kind: "??", body: tail });
|
|
1685
|
-
}
|
|
1472
|
+
result.parseErrors.push(`Legacy \`??\` ask op in target '${currentTarget.name}' is no longer supported. ` +
|
|
1473
|
+
`\`ask\` was removed in v0.16.0 — it conflated user-surfacing (which the runtime can't guarantee a channel for) with mutation-gating (already covered by \`approved="reason"\` per-op kwarg + \`# Autonomous: true\` skill flag). ` +
|
|
1474
|
+
`For input, use \`emit(text="...")\` and have the caller handle the round-trip. For mutation authorization, use \`approved=\` or \`# Autonomous:\`.`);
|
|
1686
1475
|
continue;
|
|
1687
1476
|
}
|
|
1688
1477
|
else if (stripped.startsWith("$set ") || stripped === "$set") {
|
|
@@ -1771,45 +1560,22 @@ export function parse(source) {
|
|
|
1771
1560
|
body = stripped.slice(2).trim();
|
|
1772
1561
|
}
|
|
1773
1562
|
else if (stripped.startsWith("@ ") || stripped === "@") {
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
// v0.2.4 Bug F: the trailing `(fallback: ...)` clause is now supported
|
|
1778
|
-
// for parity with $/~/> ops — cold authors reach for op-level fallback
|
|
1779
|
-
// as a defensive-coding posture and previously hit silent
|
|
1780
|
-
// outputVar-not-bound failures.
|
|
1781
|
-
const outMatch = /^(.+?)\s+->\s+([A-Za-z_]\w*)(?:\s+\(fallback\s*:\s*(.+?)\))?\s*$/.exec(tail);
|
|
1782
|
-
if (outMatch !== null) {
|
|
1783
|
-
atOutputVar = outMatch[2];
|
|
1784
|
-
if (outMatch[3] !== undefined)
|
|
1785
|
-
atFallback = processSetValue(outMatch[3]);
|
|
1786
|
-
tail = outMatch[1].trim();
|
|
1787
|
-
}
|
|
1788
|
-
// `@ unsafe <command>` — `unsafe` as literal first token signals
|
|
1789
|
-
// opt-in full-shell exec (vs default structured-spawn sandbox).
|
|
1790
|
-
const unsafeMatch = /^unsafe(?:\s+(.*))?$/.exec(tail);
|
|
1791
|
-
if (unsafeMatch !== null) {
|
|
1792
|
-
atPolicy = "unsafe";
|
|
1793
|
-
body = (unsafeMatch[1] ?? "").trim();
|
|
1794
|
-
}
|
|
1795
|
-
else {
|
|
1796
|
-
body = tail;
|
|
1797
|
-
}
|
|
1563
|
+
result.parseErrors.push(`Legacy \`@\` shell op in target '${currentTarget.name}' is no longer supported. ` +
|
|
1564
|
+
`Use \`shell(command="...") [-> R] [(fallback: "...")]\` (add \`unsafe=true\` kwarg for full-shell exec; runtime opt-in still required).`);
|
|
1565
|
+
continue;
|
|
1798
1566
|
}
|
|
1799
1567
|
else if (stripped.startsWith("! ") || stripped === "!") {
|
|
1800
|
-
|
|
1801
|
-
|
|
1568
|
+
result.parseErrors.push(`Legacy \`!\` emit op in target '${currentTarget.name}' is no longer supported. ` +
|
|
1569
|
+
`Use \`emit(text="...")\`.`);
|
|
1570
|
+
continue;
|
|
1802
1571
|
}
|
|
1803
1572
|
if (kind !== null) {
|
|
1804
|
-
//
|
|
1573
|
+
// N1: extract `approved=` for the no-binding `$` op path.
|
|
1805
1574
|
const approvedKwarg = kind === "$" ? extractApprovedKwarg(body) : undefined;
|
|
1806
1575
|
opBucket.push({
|
|
1807
1576
|
kind,
|
|
1808
1577
|
body,
|
|
1809
1578
|
...(mcpConnectorForOp !== undefined ? { mcpConnector: mcpConnectorForOp } : {}),
|
|
1810
|
-
...(atPolicy !== undefined ? { policy: atPolicy } : {}),
|
|
1811
|
-
...(atOutputVar !== undefined ? { outputVar: atOutputVar } : {}),
|
|
1812
|
-
...(atFallback !== undefined ? { fallback: atFallback } : {}),
|
|
1813
1579
|
...(approvedKwarg !== undefined ? { approved: approvedKwarg } : {}),
|
|
1814
1580
|
});
|
|
1815
1581
|
continue;
|
|
@@ -1826,7 +1592,7 @@ export function parse(source) {
|
|
|
1826
1592
|
result.parseErrors.push(`Unknown block-introducer '${keyword}:' in target '${currentTarget.name}'. ` +
|
|
1827
1593
|
`Skillscript recognizes \`if COND:\`, \`elif COND:\`, \`else:\`, and \`foreach IT in $(LIST):\` ` +
|
|
1828
1594
|
`at body scope (target-level \`else:\` is the error handler). ` +
|
|
1829
|
-
`Composition is via
|
|
1595
|
+
`Composition is via \`inline(skill="...")\` (data-skill inline) or \`execute_skill(name="...")\` (in-skill invocation), not block syntax.`);
|
|
1830
1596
|
scopeStack.push({
|
|
1831
1597
|
kind: "unknown-block",
|
|
1832
1598
|
target: currentTarget,
|