skillscript-runtime 0.8.0 → 0.9.6
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/approval.d.ts +99 -0
- package/dist/approval.d.ts.map +1 -0
- package/dist/approval.js +224 -0
- package/dist/approval.js.map +1 -0
- package/dist/bootstrap.d.ts +21 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +19 -5
- package/dist/bootstrap.js.map +1 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +9 -1
- package/dist/compile.js.map +1 -1
- package/dist/composition.d.ts +29 -0
- package/dist/composition.d.ts.map +1 -1
- package/dist/composition.js +62 -1
- package/dist/composition.js.map +1 -1
- package/dist/connectors/agent-noop.d.ts +12 -7
- package/dist/connectors/agent-noop.d.ts.map +1 -1
- package/dist/connectors/agent-noop.js +31 -12
- package/dist/connectors/agent-noop.js.map +1 -1
- package/dist/connectors/agent.d.ts +150 -28
- package/dist/connectors/agent.d.ts.map +1 -1
- package/dist/connectors/agent.js +13 -10
- package/dist/connectors/agent.js.map +1 -1
- package/dist/connectors/local-model-mcp.d.ts +8 -0
- package/dist/connectors/local-model-mcp.d.ts.map +1 -1
- package/dist/connectors/local-model-mcp.js +10 -0
- package/dist/connectors/local-model-mcp.js.map +1 -1
- package/dist/connectors/memory-store-mcp.d.ts +9 -0
- package/dist/connectors/memory-store-mcp.d.ts.map +1 -1
- package/dist/connectors/memory-store-mcp.js +11 -0
- package/dist/connectors/memory-store-mcp.js.map +1 -1
- package/dist/connectors/registry.d.ts +15 -1
- package/dist/connectors/registry.d.ts.map +1 -1
- package/dist/connectors/registry.js +23 -1
- package/dist/connectors/registry.js.map +1 -1
- package/dist/connectors/skill-store.d.ts.map +1 -1
- package/dist/connectors/skill-store.js +43 -13
- package/dist/connectors/skill-store.js.map +1 -1
- package/dist/connectors/types.d.ts +12 -0
- package/dist/connectors/types.d.ts.map +1 -1
- package/dist/connectors/types.js.map +1 -1
- package/dist/dashboard/spa/app.js +37 -9
- package/dist/dispatch-validate.d.ts +64 -0
- package/dist/dispatch-validate.d.ts.map +1 -0
- package/dist/dispatch-validate.js +81 -0
- package/dist/dispatch-validate.js.map +1 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +16 -0
- package/dist/errors.js.map +1 -1
- package/dist/help-content.d.ts.map +1 -1
- package/dist/help-content.js +92 -12
- package/dist/help-content.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lint.d.ts +11 -0
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +560 -104
- package/dist/lint.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +53 -11
- package/dist/mcp-server.js.map +1 -1
- package/dist/parser.d.ts +27 -6
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +115 -11
- package/dist/parser.js.map +1 -1
- package/dist/runtime.d.ts +51 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +126 -35
- package/dist/runtime.js.map +1 -1
- package/dist/scheduler.d.ts +20 -1
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +40 -3
- package/dist/scheduler.js.map +1 -1
- package/dist/testing/conformance.d.ts.map +1 -1
- package/dist/testing/conformance.js +16 -6
- package/dist/testing/conformance.js.map +1 -1
- package/examples/classify-support-ticket.skill.md +1 -1
- package/examples/cut-release-tag.skill.md +1 -1
- package/examples/doc-qa-with-citations.skill.md +1 -1
- package/examples/feedback-sentiment-scan.skill.md +1 -1
- package/examples/hello.skill.provenance.json +1 -1
- package/examples/morning-brief.skill.md +1 -1
- package/examples/queue-length-monitor.skill.md +1 -1
- package/examples/service-health-watch.skill.md +1 -1
- package/examples/youtrack-morning-sweep.skill.md +1 -1
- package/package.json +1 -1
- package/scaffold/examples/hello.skill.md +3 -2
package/dist/runtime.d.ts
CHANGED
|
@@ -62,8 +62,11 @@ export interface ExecuteContext {
|
|
|
62
62
|
/** Persistence backend for trace records. Wires alongside `trace`. */
|
|
63
63
|
traceStore?: TraceStore;
|
|
64
64
|
/**
|
|
65
|
-
* Trigger context for trace identity + sampling
|
|
66
|
-
* fired trigger's metadata; direct callers can
|
|
65
|
+
* Trigger context for trace identity + sampling + DeliveryMeta.origin.
|
|
66
|
+
* Scheduler passes the fired trigger's metadata; direct callers can
|
|
67
|
+
* synthesize. v0.9.6 — `source` values are constrained to the Q8 enum
|
|
68
|
+
* (cron / session / webhook / agent / cli / dashboard / inline) when
|
|
69
|
+
* the value flows into DeliveryMeta.origin.trigger_kind.
|
|
67
70
|
*/
|
|
68
71
|
triggerCtx?: {
|
|
69
72
|
source: string;
|
|
@@ -71,6 +74,23 @@ export interface ExecuteContext {
|
|
|
71
74
|
fired_at_ms: number;
|
|
72
75
|
trigger_id?: string;
|
|
73
76
|
};
|
|
77
|
+
/**
|
|
78
|
+
* v0.9.6 — root entry-point skill name for composition chains. When
|
|
79
|
+
* skill A inlines B via `&`, A's execution sets this; B's nested
|
|
80
|
+
* execute() inherits it; B's deliver()-time meta gets
|
|
81
|
+
* `entry_skill_name = A`. Undefined for top-level execution (the
|
|
82
|
+
* emitter IS the entry skill).
|
|
83
|
+
*/
|
|
84
|
+
entrySkillName?: string;
|
|
85
|
+
/**
|
|
86
|
+
* v0.9.6 — current skill's name + frontmatter eventType, stashed by
|
|
87
|
+
* `execute()` at the top of the per-skill run so deep-stack op
|
|
88
|
+
* handlers (notify(), etc.) can build DeliveryMeta without threading
|
|
89
|
+
* `parsed` through every call hop. Internal-use; do not set from
|
|
90
|
+
* outside execute().
|
|
91
|
+
*/
|
|
92
|
+
_currentSkillName?: string;
|
|
93
|
+
_currentSkillEventType?: string | null;
|
|
74
94
|
/** Skill identity for trace records. Optional — falls back to parsed.name + version inference. */
|
|
75
95
|
skillVersion?: string;
|
|
76
96
|
/**
|
|
@@ -100,11 +120,29 @@ export interface ExecutionError {
|
|
|
100
120
|
remediation?: string;
|
|
101
121
|
innerCause?: string;
|
|
102
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* v0.9.2 — fallback-fire record per P1.4. When an op's `(fallback: ...)`
|
|
125
|
+
* trailer absorbs a dispatch failure, the runtime appends one record so
|
|
126
|
+
* callers can distinguish "real success" from "fallback substituted."
|
|
127
|
+
* Previously the caller saw `errors: []` either way and couldn't tell.
|
|
128
|
+
*/
|
|
129
|
+
export interface FallbackRecord {
|
|
130
|
+
target: string;
|
|
131
|
+
opKind: string;
|
|
132
|
+
value: unknown;
|
|
133
|
+
reason: string;
|
|
134
|
+
}
|
|
103
135
|
export interface ExecuteResult {
|
|
104
136
|
finalVars: Record<string, unknown>;
|
|
105
137
|
emissions: string[];
|
|
106
138
|
outputs: Record<string, unknown>;
|
|
107
139
|
errors: ExecutionError[];
|
|
140
|
+
/**
|
|
141
|
+
* v0.9.2 — fallback events. Populated when an op's `(fallback: ...)`
|
|
142
|
+
* trailer caught a dispatch failure. Empty array when no fallbacks
|
|
143
|
+
* fired. Inspect `length > 0` to detect partial-success runs.
|
|
144
|
+
*/
|
|
145
|
+
fallbacks: FallbackRecord[];
|
|
108
146
|
targetOrder: string[];
|
|
109
147
|
/**
|
|
110
148
|
* Delivery receipts from `AgentConnector.deliver` calls fired after the
|
|
@@ -121,6 +159,17 @@ export interface AgentDeliveryReceiptRecord {
|
|
|
121
159
|
agent_id: string;
|
|
122
160
|
output_kind: "agent" | "template";
|
|
123
161
|
receipt: DeliveryReceipt;
|
|
162
|
+
/**
|
|
163
|
+
* v0.9.2 — true when no real AgentConnector was wired and the
|
|
164
|
+
* NoOpAgentConnector fallback "handled" the dispatch (i.e. accepted
|
|
165
|
+
* the payload without delivering it anywhere). Cold authors writing
|
|
166
|
+
* `# Output: agent: oncall` skills against a runtime without an
|
|
167
|
+
* AgentConnector get a clear signal that the delivery didn't happen.
|
|
168
|
+
* Per P1.1 finding in `dec3ca8a`.
|
|
169
|
+
*/
|
|
170
|
+
delivery_skipped?: boolean;
|
|
171
|
+
/** Human-readable reason when `delivery_skipped: true`. */
|
|
172
|
+
reason?: string;
|
|
124
173
|
}
|
|
125
174
|
/**
|
|
126
175
|
* Execute a parsed skill against the live variable state. Walks targets in
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,aAAa,CAAC;AACpE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime.js
CHANGED
|
@@ -1,11 +1,52 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { tokenizeKeywordArgs } from "./parser.js";
|
|
2
3
|
import { applyFilter, parseFilterChain } from "./filters.js";
|
|
3
4
|
import { dispatchExecuteSkillIntercept } from "./composition.js";
|
|
5
|
+
import { validateQualifiedDispatch } from "./dispatch-validate.js";
|
|
4
6
|
import { spawn } from "node:child_process";
|
|
5
7
|
import { readFile as fsReadFile, writeFile as fsWriteFile, mkdir as fsMkdir } from "node:fs/promises";
|
|
6
8
|
import { dirname as pathDirname } from "node:path";
|
|
7
9
|
import { OpError, ConnectorNotFoundError, OpTimeoutError, InteractiveOpInAutonomousModeError, UnsafeShellDisabledError, UnresolvedVariableError, TypeMismatchError, MissingSkillReferenceError, } from "./errors.js";
|
|
8
10
|
import { TraceBuilder, shouldTraceFire } from "./trace.js";
|
|
11
|
+
/**
|
|
12
|
+
* v0.9.6 audit Q8 — build the `DeliveryMeta` envelope for an `AgentConnector.deliver()`
|
|
13
|
+
* call. Runtime is the sole producer of meta; adopters consume but never construct.
|
|
14
|
+
*
|
|
15
|
+
* - `dispatch_id` is one UUID per call to this helper. Lifecycle hook callers and
|
|
16
|
+
* notify() callers each call it once per emit; multi-connector broadcast (one
|
|
17
|
+
* notify(), N wired connectors) shares the same id across all N deliver() calls
|
|
18
|
+
* because we hold the same `meta` reference across the loop.
|
|
19
|
+
* - `sent_at` is the runtime emit-clock (NOT the substrate's delivered_at).
|
|
20
|
+
* - `origin.trigger_kind` maps from `ctx.triggerCtx.source` per Q8 enum.
|
|
21
|
+
* `inline` is the fallback when no triggerCtx is set.
|
|
22
|
+
* - `origin.entry_skill_name` propagates from `ctx.entrySkillName` (set when
|
|
23
|
+
* composition runs an inner skill).
|
|
24
|
+
* - `origin.caller_agent_id` propagates from `ctx.agentId` when the chain was
|
|
25
|
+
* started by an agent (cron/cli/dashboard/inline triggers leave it undefined).
|
|
26
|
+
* - `event_type` precedence: opOverrides.event_type ?? parsed.eventType ?? undefined.
|
|
27
|
+
* `notify(event_type=...)` kwarg wins; `# Event-type:` frontmatter is the fallback.
|
|
28
|
+
* - `correlation_id` only set when explicitly passed via opOverrides (notify()
|
|
29
|
+
* kwarg). Lifecycle hooks always emit with correlation_id undefined per Q8.
|
|
30
|
+
*/
|
|
31
|
+
function buildLifecycleMeta(skillName, frontmatterEventType, ctx, opOverrides) {
|
|
32
|
+
const triggerKind = (ctx.triggerCtx?.source ?? "inline");
|
|
33
|
+
const eventType = opOverrides?.event_type ?? frontmatterEventType ?? undefined;
|
|
34
|
+
const name = skillName ?? "(unnamed)";
|
|
35
|
+
return {
|
|
36
|
+
dispatch_id: randomUUID(),
|
|
37
|
+
sent_at: Date.now(),
|
|
38
|
+
origin: {
|
|
39
|
+
skill_name: name,
|
|
40
|
+
...(ctx.entrySkillName !== undefined && ctx.entrySkillName !== name
|
|
41
|
+
? { entry_skill_name: ctx.entrySkillName }
|
|
42
|
+
: {}),
|
|
43
|
+
trigger_kind: triggerKind,
|
|
44
|
+
...(ctx.agentId !== undefined ? { caller_agent_id: ctx.agentId } : {}),
|
|
45
|
+
},
|
|
46
|
+
...(eventType !== undefined ? { event_type: eventType } : {}),
|
|
47
|
+
...(opOverrides?.correlation_id !== undefined ? { correlation_id: opOverrides.correlation_id } : {}),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
9
50
|
/**
|
|
10
51
|
* Execute a parsed skill against the live variable state. Walks targets in
|
|
11
52
|
* the provided order. Each target's ops run sequentially; on failure the
|
|
@@ -28,7 +69,9 @@ export async function execute(parsed, initialVars, order, ctx) {
|
|
|
28
69
|
vars.set("NOW", new Date(nowMs).toISOString());
|
|
29
70
|
vars.set("USER", ctx.agentId ?? "unknown");
|
|
30
71
|
vars.set("SESSION_CONTEXT", "");
|
|
31
|
-
|
|
72
|
+
// v0.9.6 — "manual" enum value dropped per audit Q12; "inline" is the
|
|
73
|
+
// fallback for runtime-direct callers without explicit trigger context.
|
|
74
|
+
vars.set("TRIGGER_TYPE", "inline");
|
|
32
75
|
vars.set("TRIGGER_PAYLOAD", "");
|
|
33
76
|
vars.set("EVENT.fired_at", nowMs);
|
|
34
77
|
vars.set("EVENT.fired_at_unix", nowSec);
|
|
@@ -44,14 +87,21 @@ export async function execute(parsed, initialVars, order, ctx) {
|
|
|
44
87
|
}
|
|
45
88
|
const emissions = [];
|
|
46
89
|
const errors = [];
|
|
90
|
+
const fallbacks = [];
|
|
47
91
|
let lastBoundVar = null;
|
|
48
92
|
const absoluteTimeoutMs = ctx.absoluteTimeoutMs ?? DEFAULT_RUNTIME_ABSOLUTE_TIMEOUT_MS;
|
|
49
93
|
// Trace recording (per ERD §8). Build when shouldTraceFire returns true;
|
|
50
94
|
// skip entirely when off (the NFR-11 floor — errors still surface via
|
|
51
95
|
// `result.errors[]`).
|
|
52
|
-
|
|
96
|
+
// v0.9.6 — "manual" enum value dropped per audit Q12; "inline" is the
|
|
97
|
+
// fallback for callers without explicit trigger context.
|
|
98
|
+
const triggerCtx = ctx.triggerCtx ?? { source: "inline", name: "", fired_at_ms: nowMs };
|
|
53
99
|
const triggerId = triggerCtx.trigger_id ?? `${triggerCtx.source}:${triggerCtx.name}`;
|
|
54
100
|
const skillName = parsed.name ?? "(anonymous)";
|
|
101
|
+
// v0.9.6 — stash skill identity onto ctx so deep-stack op handlers
|
|
102
|
+
// (notify()) can read for DeliveryMeta without threading `parsed`
|
|
103
|
+
// through every call hop. Internal convention; do not set from outside.
|
|
104
|
+
ctx = { ...ctx, _currentSkillName: skillName, _currentSkillEventType: parsed.eventType };
|
|
55
105
|
const traceBuilder = shouldTraceFire(ctx.trace, triggerId, skillName)
|
|
56
106
|
? new TraceBuilder(skillName, ctx.skillVersion ?? "unknown", triggerCtx, { agent_id: ctx.agentId })
|
|
57
107
|
: null;
|
|
@@ -62,7 +112,7 @@ export async function execute(parsed, initialVars, order, ctx) {
|
|
|
62
112
|
let targetLastBound = null;
|
|
63
113
|
let targetLastValue = undefined;
|
|
64
114
|
try {
|
|
65
|
-
const r = await execOps(target.ops, vars, emissions, ctx, targetName, parsed.timeout, absoluteTimeoutMs, traceBuilder);
|
|
115
|
+
const r = await execOps(target.ops, vars, emissions, fallbacks, ctx, targetName, parsed.timeout, absoluteTimeoutMs, traceBuilder);
|
|
66
116
|
targetLastBound = r.lastBoundVar;
|
|
67
117
|
targetLastValue = r.lastBoundVar !== null ? vars.get(r.lastBoundVar) : r.lastValue;
|
|
68
118
|
}
|
|
@@ -70,7 +120,7 @@ export async function execute(parsed, initialVars, order, ctx) {
|
|
|
70
120
|
errors.push(buildExecutionError(err, targetName));
|
|
71
121
|
if (target.elseBlock !== undefined) {
|
|
72
122
|
try {
|
|
73
|
-
const r = await execOps(target.elseBlock, vars, emissions, ctx, targetName, parsed.timeout, absoluteTimeoutMs, traceBuilder);
|
|
123
|
+
const r = await execOps(target.elseBlock, vars, emissions, fallbacks, ctx, targetName, parsed.timeout, absoluteTimeoutMs, traceBuilder);
|
|
74
124
|
targetLastBound = r.lastBoundVar;
|
|
75
125
|
targetLastValue = r.lastBoundVar !== null ? vars.get(r.lastBoundVar) : r.lastValue;
|
|
76
126
|
}
|
|
@@ -153,28 +203,31 @@ export async function execute(parsed, initialVars, order, ctx) {
|
|
|
153
203
|
const key = `${decl.kind}:${decl.target}`;
|
|
154
204
|
const body = String(outputs[key] ?? emissions.join("\n"));
|
|
155
205
|
const agent = ctx.registry.getAgentConnector();
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
name: ctx.triggerCtx.name,
|
|
167
|
-
fired_at_ms: ctx.triggerCtx.fired_at_ms,
|
|
168
|
-
},
|
|
169
|
-
} : {}),
|
|
170
|
-
...(parsed.deliveryContext !== null ? { delivery_context: parsed.deliveryContext } : {}),
|
|
171
|
-
...(parsed.templates.length > 0 ? { templates: parsed.templates } : {}),
|
|
172
|
-
};
|
|
206
|
+
// v0.9.6 audit Q8 — build DeliveryMeta per the locked v1.0 shape.
|
|
207
|
+
// Runtime auto-fills dispatch_id, sent_at, origin; threads optional
|
|
208
|
+
// event_type from frontmatter (`# Event-type:` fallback per Q9).
|
|
209
|
+
// Lifecycle hook deliveries carry no correlation_id (Q8 design
|
|
210
|
+
// boundary — that's notify()-kwarg territory).
|
|
211
|
+
const meta = buildLifecycleMeta(parsed.name, parsed.eventType, ctx);
|
|
212
|
+
// v0.9.2 — P1.1 flag the no-op dispatch case. When no real
|
|
213
|
+
// AgentConnector is wired, the NoOp fallback accepts the deliver
|
|
214
|
+
// call without doing anything; cold authors get a clear signal.
|
|
215
|
+
const hasRealConnector = ctx.registry.hasAgentConnector();
|
|
173
216
|
try {
|
|
174
217
|
const receipt = decl.kind === "agent"
|
|
175
|
-
? await agent.deliver(decl.target, { kind: "augment", content: body,
|
|
176
|
-
: await agent.deliver(decl.target, { kind: "template", prompt: body,
|
|
177
|
-
|
|
218
|
+
? await agent.deliver(decl.target, { kind: "augment", content: body, meta })
|
|
219
|
+
: await agent.deliver(decl.target, { kind: "template", prompt: body, meta });
|
|
220
|
+
const record = { agent_id: decl.target, output_kind: decl.kind, receipt };
|
|
221
|
+
if (!hasRealConnector) {
|
|
222
|
+
record.delivery_skipped = true;
|
|
223
|
+
record.reason = `No AgentConnector wired for runtime; NoOpAgentConnector accepted but didn't deliver. Wire an AgentConnector via registry.registerAgentConnector('primary', <YourImpl>) to enable real delivery.`;
|
|
224
|
+
}
|
|
225
|
+
else if (receipt.delivery_skipped === true) {
|
|
226
|
+
// v0.9.6 Q7 — connector signaled accept-but-not-pushed (offline,
|
|
227
|
+
// rate-limit drop, etc.). Honor the contract-level signal.
|
|
228
|
+
record.delivery_skipped = true;
|
|
229
|
+
}
|
|
230
|
+
agentDeliveryReceipts.push(record);
|
|
178
231
|
}
|
|
179
232
|
catch (err) {
|
|
180
233
|
// Delivery failure is non-fatal — record alongside other errors so
|
|
@@ -203,15 +256,16 @@ export async function execute(parsed, initialVars, order, ctx) {
|
|
|
203
256
|
emissions,
|
|
204
257
|
outputs,
|
|
205
258
|
errors,
|
|
259
|
+
fallbacks,
|
|
206
260
|
targetOrder: order,
|
|
207
261
|
agentDeliveryReceipts,
|
|
208
262
|
};
|
|
209
263
|
}
|
|
210
|
-
async function execOps(ops, vars, emissions, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder) {
|
|
264
|
+
async function execOps(ops, vars, emissions, fallbacks, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder) {
|
|
211
265
|
let lastBoundVar = null;
|
|
212
266
|
let lastValue = undefined;
|
|
213
267
|
for (const op of ops) {
|
|
214
|
-
const r = await execOp(op, vars, emissions, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder);
|
|
268
|
+
const r = await execOp(op, vars, emissions, fallbacks, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder);
|
|
215
269
|
if (r.lastBoundVar !== null) {
|
|
216
270
|
lastBoundVar = r.lastBoundVar;
|
|
217
271
|
lastValue = r.lastValue;
|
|
@@ -222,11 +276,11 @@ async function execOps(ops, vars, emissions, ctx, targetName, skillTimeoutSec, a
|
|
|
222
276
|
}
|
|
223
277
|
return { lastBoundVar, lastValue };
|
|
224
278
|
}
|
|
225
|
-
async function execOp(op, vars, emissions, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder) {
|
|
279
|
+
async function execOp(op, vars, emissions, fallbacks, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder) {
|
|
226
280
|
const startMs = traceBuilder !== null ? Date.now() : 0;
|
|
227
281
|
let errored = false;
|
|
228
282
|
try {
|
|
229
|
-
return await execOpInner(op, vars, emissions, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder);
|
|
283
|
+
return await execOpInner(op, vars, emissions, fallbacks, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder);
|
|
230
284
|
}
|
|
231
285
|
catch (err) {
|
|
232
286
|
errored = true;
|
|
@@ -261,7 +315,7 @@ function extractOpConnector(op) {
|
|
|
261
315
|
default: return undefined;
|
|
262
316
|
}
|
|
263
317
|
}
|
|
264
|
-
async function execOpInner(op, vars, emissions, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder) {
|
|
318
|
+
async function execOpInner(op, vars, emissions, fallbacks, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder) {
|
|
265
319
|
switch (op.kind) {
|
|
266
320
|
case "$set": {
|
|
267
321
|
// v0.5.0 item 3 — `$set X = "...$(REF)..."` now resolves $(REF) at
|
|
@@ -429,6 +483,14 @@ async function execOpInner(op, vars, emissions, ctx, targetName, skillTimeoutSec
|
|
|
429
483
|
vars.set(flatKey, fallbackValue);
|
|
430
484
|
if (op.outputVar !== undefined)
|
|
431
485
|
vars.set(op.outputVar, fallbackValue);
|
|
486
|
+
// v0.9.2 — P1.4 record fallback firing so callers can distinguish
|
|
487
|
+
// "read succeeded" from "fallback substituted."
|
|
488
|
+
fallbacks.push({
|
|
489
|
+
target: targetName,
|
|
490
|
+
opKind: "file_read",
|
|
491
|
+
value: fallbackValue,
|
|
492
|
+
reason: `file_read failed: ${err.message}`,
|
|
493
|
+
});
|
|
432
494
|
return { lastBoundVar: op.outputVar ?? flatKey, lastValue: fallbackValue };
|
|
433
495
|
}
|
|
434
496
|
throw makeOpError("file_read", `\`file_read(path="${path}")\` in target '${targetName}' failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -456,6 +518,9 @@ async function execOpInner(op, vars, emissions, ctx, targetName, skillTimeoutSec
|
|
|
456
518
|
catch (err) {
|
|
457
519
|
throw makeOpError("file_write", `\`file_write(path="${path}")\` in target '${targetName}' failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
458
520
|
}
|
|
521
|
+
// v0.9.2 — P2.5 transcript line on successful write so cold authors
|
|
522
|
+
// can confirm side effects landed without reading the file back.
|
|
523
|
+
emissions.push(`[file_write] wrote ${Buffer.byteLength(content, "utf8")} bytes to ${path}`);
|
|
459
524
|
return { lastBoundVar: null, lastValue: undefined };
|
|
460
525
|
}
|
|
461
526
|
case "notify": {
|
|
@@ -486,6 +551,15 @@ async function execOpInner(op, vars, emissions, ctx, targetName, skillTimeoutSec
|
|
|
486
551
|
// connector failures are recorded in the ACK but don't propagate.
|
|
487
552
|
const allConnectors = ctx.registry.listAgentConnectors();
|
|
488
553
|
const dispatched = [];
|
|
554
|
+
// v0.9.6 audit Q8 — ONE notify() op invocation = ONE dispatch_id across
|
|
555
|
+
// every wired connector that gets the deliver() call. Same payload + meta
|
|
556
|
+
// for the whole fanout; receivers dedupe substrate retries by dispatch_id.
|
|
557
|
+
// event_type / correlation_id come from the notify() kwargs (per-emit
|
|
558
|
+
// override of any frontmatter fallback).
|
|
559
|
+
const notifyMeta = buildLifecycleMeta(ctx._currentSkillName ?? null, ctx._currentSkillEventType ?? null, ctx, {
|
|
560
|
+
event_type: op.notifyParams?.event_type,
|
|
561
|
+
correlation_id: op.notifyParams?.correlation_id,
|
|
562
|
+
});
|
|
489
563
|
for (const entry of allConnectors) {
|
|
490
564
|
if (restrictConnectors !== undefined && !restrictConnectors.includes(entry.name))
|
|
491
565
|
continue;
|
|
@@ -504,6 +578,7 @@ async function execOpInner(op, vars, emissions, ctx, targetName, skillTimeoutSec
|
|
|
504
578
|
await entry.instance.deliver(agent, {
|
|
505
579
|
kind: "augment",
|
|
506
580
|
content: message,
|
|
581
|
+
meta: notifyMeta,
|
|
507
582
|
});
|
|
508
583
|
dispatched.push({ connector: entry.name, ok: true });
|
|
509
584
|
}
|
|
@@ -606,10 +681,19 @@ async function execOpInner(op, vars, emissions, ctx, targetName, skillTimeoutSec
|
|
|
606
681
|
// an explicit connector name is set (op.mcpConnector !== undefined)
|
|
607
682
|
// — the implicit "primary" path is for embedder-wired connectors
|
|
608
683
|
// that don't go through connectors.json.
|
|
684
|
+
//
|
|
685
|
+
// v0.9.1 — extended to call the shared `validateQualifiedDispatch`
|
|
686
|
+
// helper so runtime and lint use the SAME source of truth. Catches
|
|
687
|
+
// unknown-tool-on-connector at runtime when lint was skipped.
|
|
609
688
|
if (op.mcpConnector !== undefined && ctx.registry.hasMcpConnector(connectorName)) {
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
689
|
+
const diagnostics = validateQualifiedDispatch({
|
|
690
|
+
toolName,
|
|
691
|
+
qualifiedConnector: connectorName,
|
|
692
|
+
registry: ctx.registry,
|
|
693
|
+
});
|
|
694
|
+
const blocking = diagnostics.find((d) => d.severity === "error");
|
|
695
|
+
if (blocking !== undefined) {
|
|
696
|
+
throw makeOpError("$", `${blocking.message} (Defense-in-depth: lint should have caught this earlier.)`);
|
|
613
697
|
}
|
|
614
698
|
}
|
|
615
699
|
let rawResult;
|
|
@@ -644,6 +728,13 @@ async function execOpInner(op, vars, emissions, ctx, targetName, skillTimeoutSec
|
|
|
644
728
|
vars.set(flatKey, dollarFallback);
|
|
645
729
|
if (op.outputVar !== undefined)
|
|
646
730
|
vars.set(op.outputVar, dollarFallback);
|
|
731
|
+
// v0.9.2 — P1.4 record the fallback substitution
|
|
732
|
+
fallbacks.push({
|
|
733
|
+
target: targetName,
|
|
734
|
+
opKind: "$",
|
|
735
|
+
value: dollarFallback,
|
|
736
|
+
reason: `$ ${connectorLabel}${toolName} failed: ${err.message}`,
|
|
737
|
+
});
|
|
647
738
|
return { lastBoundVar: op.outputVar ?? flatKey, lastValue: dollarFallback };
|
|
648
739
|
}
|
|
649
740
|
throw err;
|
|
@@ -781,7 +872,7 @@ async function execOpInner(op, vars, emissions, ctx, targetName, skillTimeoutSec
|
|
|
781
872
|
let last = { lastBoundVar: null, lastValue: undefined };
|
|
782
873
|
for (const item of listVal) {
|
|
783
874
|
vars.set(iterName, item);
|
|
784
|
-
last = await execOps(op.foreachBody, vars, emissions, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder);
|
|
875
|
+
last = await execOps(op.foreachBody, vars, emissions, fallbacks, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder);
|
|
785
876
|
}
|
|
786
877
|
for (const k of Array.from(vars.keys())) {
|
|
787
878
|
if (!before.has(k))
|
|
@@ -792,11 +883,11 @@ async function execOpInner(op, vars, emissions, ctx, targetName, skillTimeoutSec
|
|
|
792
883
|
case "if": {
|
|
793
884
|
for (const branch of op.ifBranches) {
|
|
794
885
|
if (evalCondition(branch.cond, vars)) {
|
|
795
|
-
return execOps(branch.body, vars, emissions, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder);
|
|
886
|
+
return execOps(branch.body, vars, emissions, fallbacks, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder);
|
|
796
887
|
}
|
|
797
888
|
}
|
|
798
889
|
if (op.ifElseBody !== undefined) {
|
|
799
|
-
return execOps(op.ifElseBody, vars, emissions, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder);
|
|
890
|
+
return execOps(op.ifElseBody, vars, emissions, fallbacks, ctx, targetName, skillTimeoutSec, absoluteTimeoutMs, traceBuilder);
|
|
800
891
|
}
|
|
801
892
|
return { lastBoundVar: null, lastValue: undefined };
|
|
802
893
|
}
|