@sensigo/realm 0.12.1 → 0.14.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/engine/execution-loop.d.ts +0 -4
- package/dist/engine/execution-loop.d.ts.map +1 -1
- package/dist/engine/execution-loop.js +58 -7
- package/dist/engine/execution-loop.js.map +1 -1
- package/dist/extensions/default-registry.d.ts +6 -13
- package/dist/extensions/default-registry.d.ts.map +1 -1
- package/dist/extensions/default-registry.js +13 -20
- package/dist/extensions/default-registry.js.map +1 -1
- package/dist/extensions/manifest.d.ts +26 -0
- package/dist/extensions/manifest.d.ts.map +1 -0
- package/dist/extensions/manifest.js +7 -0
- package/dist/extensions/manifest.js.map +1 -0
- package/dist/extensions/registry.d.ts +14 -0
- package/dist/extensions/registry.d.ts.map +1 -1
- package/dist/extensions/registry.js +32 -0
- package/dist/extensions/registry.js.map +1 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/manifest/deployment-manifest.d.ts +197 -0
- package/dist/manifest/deployment-manifest.d.ts.map +1 -0
- package/dist/manifest/deployment-manifest.js +174 -0
- package/dist/manifest/deployment-manifest.js.map +1 -0
- package/dist/manifest/secret-refs.d.ts +44 -0
- package/dist/manifest/secret-refs.d.ts.map +1 -0
- package/dist/manifest/secret-refs.js +126 -0
- package/dist/manifest/secret-refs.js.map +1 -0
- package/dist/types/extension-identity.d.ts +74 -0
- package/dist/types/extension-identity.d.ts.map +1 -0
- package/dist/types/extension-identity.js +28 -0
- package/dist/types/extension-identity.js.map +1 -0
- package/dist/types/run-record.d.ts +9 -0
- package/dist/types/run-record.d.ts.map +1 -1
- package/dist/types/workflow-definition.d.ts +23 -3
- package/dist/types/workflow-definition.d.ts.map +1 -1
- package/dist/workflow/yaml-loader.d.ts +2 -0
- package/dist/workflow/yaml-loader.d.ts.map +1 -1
- package/dist/workflow/yaml-loader.js +123 -3
- package/dist/workflow/yaml-loader.js.map +1 -1
- package/package.json +1 -1
- package/dist/config/secrets.d.ts +0 -15
- package/dist/config/secrets.d.ts.map +0 -1
- package/dist/config/secrets.js +0 -33
- package/dist/config/secrets.js.map +0 -1
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Secret-reference grammar for the deployment manifest (v0.14): `${secret:NAME}`.
|
|
2
|
+
//
|
|
3
|
+
// Pure string/tree functions — core owns the GRAMMAR only. Reading secret sources
|
|
4
|
+
// (dotenv files, process.env) and resolving values is the CLI's job.
|
|
5
|
+
//
|
|
6
|
+
// Grammar:
|
|
7
|
+
// - a reference is exactly `${secret:NAME}` with NAME = [A-Z0-9_]+
|
|
8
|
+
// - references are legal ONLY inside string values under `config` trees
|
|
9
|
+
// - composite/embedded references are allowed: "${secret:A}:${secret:B}"
|
|
10
|
+
// - `$$` escapes a literal `$` (so `$${secret:X}` interpolates to the literal text
|
|
11
|
+
// `${secret:X}`); any other `${secret:` occurrence that does not match the full
|
|
12
|
+
// grammar is MALFORMED and must be rejected loudly (typos never pass silently).
|
|
13
|
+
const REF_PATTERN = /^\$\{secret:([A-Z0-9_]+)\}/;
|
|
14
|
+
/**
|
|
15
|
+
* Scans a string left-to-right honoring the `$$` escape. Well-formed references are
|
|
16
|
+
* collected; anything starting like a reference but not matching the grammar is malformed.
|
|
17
|
+
*/
|
|
18
|
+
export function scanSecretString(value) {
|
|
19
|
+
const refs = [];
|
|
20
|
+
const malformed = [];
|
|
21
|
+
let i = 0;
|
|
22
|
+
while (i < value.length) {
|
|
23
|
+
if (value[i] === '$') {
|
|
24
|
+
if (value[i + 1] === '$') {
|
|
25
|
+
i += 2; // escaped literal '$'
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const rest = value.slice(i);
|
|
29
|
+
const match = REF_PATTERN.exec(rest);
|
|
30
|
+
if (match !== null) {
|
|
31
|
+
refs.push(match[1]);
|
|
32
|
+
i += match[0].length;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (rest.startsWith('${secret:') || rest.startsWith('${secret')) {
|
|
36
|
+
// Looks like a reference but is not one — surface the fragment for the error.
|
|
37
|
+
const end = rest.indexOf('}');
|
|
38
|
+
malformed.push(end === -1 ? rest.slice(0, 24) : rest.slice(0, end + 1));
|
|
39
|
+
i += end === -1 ? rest.length : end + 1;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
i += 1;
|
|
44
|
+
}
|
|
45
|
+
return { refs, malformed };
|
|
46
|
+
}
|
|
47
|
+
/** True when the string contains at least one well-formed secret reference. */
|
|
48
|
+
export function stringHasSecretRefs(value) {
|
|
49
|
+
return scanSecretString(value).refs.length > 0;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Interpolates a string: well-formed references are replaced via `resolve(name)`,
|
|
53
|
+
* `$$` becomes a literal `$`. The caller guarantees every referenced name resolves
|
|
54
|
+
* (the CLI aggregates unresolved references into one loud error BEFORE interpolating).
|
|
55
|
+
*/
|
|
56
|
+
export function interpolateSecretString(value, resolve) {
|
|
57
|
+
let out = '';
|
|
58
|
+
let i = 0;
|
|
59
|
+
while (i < value.length) {
|
|
60
|
+
if (value[i] === '$') {
|
|
61
|
+
if (value[i + 1] === '$') {
|
|
62
|
+
out += '$';
|
|
63
|
+
i += 2;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const match = REF_PATTERN.exec(value.slice(i));
|
|
67
|
+
if (match !== null) {
|
|
68
|
+
out += resolve(match[1]);
|
|
69
|
+
i += match[0].length;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
out += value[i];
|
|
74
|
+
i += 1;
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Walks a config tree (objects/arrays/strings) collecting every well-formed secret
|
|
80
|
+
* reference with its binding site. Malformed fragments are collected separately.
|
|
81
|
+
*/
|
|
82
|
+
export function findSecretRefSites(node, basePath) {
|
|
83
|
+
const sites = [];
|
|
84
|
+
const malformed = [];
|
|
85
|
+
const walk = (value, path) => {
|
|
86
|
+
if (typeof value === 'string') {
|
|
87
|
+
const scan = scanSecretString(value);
|
|
88
|
+
for (const name of scan.refs)
|
|
89
|
+
sites.push({ name, path });
|
|
90
|
+
for (const fragment of scan.malformed)
|
|
91
|
+
malformed.push({ path, fragment });
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (Array.isArray(value)) {
|
|
95
|
+
value.forEach((item, idx) => walk(item, `${path}[${idx}]`));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (typeof value === 'object' && value !== null) {
|
|
99
|
+
for (const [key, child] of Object.entries(value))
|
|
100
|
+
walk(child, `${path}.${key}`);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
walk(node, basePath);
|
|
104
|
+
return { sites, malformed };
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Interpolates every string inside a config tree, returning a deep copy.
|
|
108
|
+
* Non-string leaves pass through untouched.
|
|
109
|
+
*/
|
|
110
|
+
export function interpolateConfigTree(node, resolve) {
|
|
111
|
+
if (typeof node === 'string') {
|
|
112
|
+
return interpolateSecretString(node, resolve);
|
|
113
|
+
}
|
|
114
|
+
if (Array.isArray(node)) {
|
|
115
|
+
return node.map((item) => interpolateConfigTree(item, resolve));
|
|
116
|
+
}
|
|
117
|
+
if (typeof node === 'object' && node !== null) {
|
|
118
|
+
const out = {};
|
|
119
|
+
for (const [key, child] of Object.entries(node)) {
|
|
120
|
+
out[key] = interpolateConfigTree(child, resolve);
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
return node;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=secret-refs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-refs.js","sourceRoot":"","sources":["../../src/manifest/secret-refs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** One loaded extension module's identity. */
|
|
2
|
+
export interface ExtensionIdentityModule {
|
|
3
|
+
/** The authored path (workflow.yaml `extensions:` entry or the --extensions-module value). */
|
|
4
|
+
declared: string;
|
|
5
|
+
/** Absolute realpath-resolved module path that was imported. */
|
|
6
|
+
resolved: string;
|
|
7
|
+
/** sha256 hex of the entry module file's bytes at load time. */
|
|
8
|
+
entry_hash: string;
|
|
9
|
+
/** How the module was loaded. Descriptive metadata — not part of the drift comparison. */
|
|
10
|
+
format: 'esm' | 'cjs' | 'ts-jiti';
|
|
11
|
+
}
|
|
12
|
+
/** Versioned deterministic directory-tree fingerprint over the module roots. */
|
|
13
|
+
export interface ExtensionIdentityTree {
|
|
14
|
+
/** Deduped, sorted parent directories of the resolved entry modules. */
|
|
15
|
+
roots: string[];
|
|
16
|
+
/** The sweep rule string the fingerprint was computed under (governs recomputation). */
|
|
17
|
+
rules: string;
|
|
18
|
+
/** Number of files included in the fingerprint. */
|
|
19
|
+
file_count: number;
|
|
20
|
+
/** Total bytes of the included files. */
|
|
21
|
+
total_bytes: number;
|
|
22
|
+
/** sha256 hex over the sorted `relpath\0filehash` lines. */
|
|
23
|
+
tree_hash: string;
|
|
24
|
+
/** True when the sweep hit its caps — the fingerprint covers a deterministic prefix only. */
|
|
25
|
+
truncated: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** Labeled advisory signals — each independently fail-soft; never part of the comparison. */
|
|
28
|
+
export interface ExtensionIdentitySignals {
|
|
29
|
+
/** `version` from the trust root's package.json, when readable. */
|
|
30
|
+
package_version?: string;
|
|
31
|
+
/** Commit hash from .git/HEAD (one ref-file dereference; no child processes), when readable. */
|
|
32
|
+
git_head?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* One point-in-time identity of the extension code executing a run. Appended to
|
|
36
|
+
* `RunRecord.extension_identity` on change (history, newest last). The shape is
|
|
37
|
+
* load-bearing evidence — never collapsible to a boolean.
|
|
38
|
+
*/
|
|
39
|
+
export interface ExtensionIdentityEntry {
|
|
40
|
+
/** ISO timestamp of module-LOAD-time capture (what was in memory), not of the append. */
|
|
41
|
+
captured_at: string;
|
|
42
|
+
/** PID of the capturing process. */
|
|
43
|
+
pid?: number;
|
|
44
|
+
modules: ExtensionIdentityModule[];
|
|
45
|
+
tree: ExtensionIdentityTree;
|
|
46
|
+
signals?: ExtensionIdentitySignals;
|
|
47
|
+
/** Coverage label of the sweep rules — never 'complete'. */
|
|
48
|
+
coverage: 'dir_tree_v1';
|
|
49
|
+
/**
|
|
50
|
+
* Deployment-manifest identity: the manifest file's path and its raw-bytes sha256
|
|
51
|
+
* (placeholders included — an edited binding IS drift; a rotated secret VALUE is not).
|
|
52
|
+
* The manifest file never contributes a sweep root — only this content hash.
|
|
53
|
+
*/
|
|
54
|
+
manifest?: {
|
|
55
|
+
path: string;
|
|
56
|
+
content_hash: string;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Secret NAMES referenced by the manifest at load time. Recorded, never compared —
|
|
60
|
+
* and never values or value hashes.
|
|
61
|
+
*/
|
|
62
|
+
secret_names?: string[];
|
|
63
|
+
/** Present when --extensions-module replaced the declared modules — omitting it would lie. */
|
|
64
|
+
override_active?: true;
|
|
65
|
+
/** Present when capture (or the extension load itself) failed — the failure is itself a record. */
|
|
66
|
+
error?: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Returns true when two identity entries denote DIFFERENT extension code:
|
|
70
|
+
* tree_hash, the per-module entry hashes (as an ordered resolved→hash list), or the
|
|
71
|
+
* override flag differ. Signals, timestamps, and pid are advisory — never compared.
|
|
72
|
+
*/
|
|
73
|
+
export declare function extensionIdentityDiffers(a: ExtensionIdentityEntry, b: ExtensionIdentityEntry): boolean;
|
|
74
|
+
//# sourceMappingURL=extension-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension-identity.d.ts","sourceRoot":"","sources":["../../src/types/extension-identity.ts"],"names":[],"mappings":"AASA,8CAA8C;AAC9C,MAAM,WAAW,uBAAuB;IACtC,8FAA8F;IAC9F,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,MAAM,EAAE,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC;CACnC;AAED,gFAAgF;AAChF,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,wFAAwF;IACxF,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,6FAA6F;AAC7F,MAAM,WAAW,wBAAwB;IACvC,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gGAAgG;IAChG,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,yFAAyF;IACzF,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC,IAAI,EAAE,qBAAqB,CAAC;IAC5B,OAAO,CAAC,EAAE,wBAAwB,CAAC;IACnC,4DAA4D;IAC5D,QAAQ,EAAE,aAAa,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,8FAA8F;IAC9F,eAAe,CAAC,EAAE,IAAI,CAAC;IACvB,mGAAmG;IACnG,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,CAAC,EAAE,sBAAsB,EACzB,CAAC,EAAE,sBAAsB,GACxB,OAAO,CAST"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Extension-code drift evidence types (issue #119) — pure data shapes carried on RunRecord.
|
|
2
|
+
//
|
|
3
|
+
// An ExtensionIdentityEntry records the identity of the project extension code that was in
|
|
4
|
+
// memory when a run executed: per-module entry hashes plus a versioned deterministic
|
|
5
|
+
// directory-tree fingerprint (dir_tree_v1). Core only defines and compares these shapes —
|
|
6
|
+
// identity is COMPUTED by the CLI (extension-identity.ts, pure fs+crypto) at module-load
|
|
7
|
+
// time, attached to the ExtensionRegistry, and appended to the run by the execution loop's
|
|
8
|
+
// lazy append-on-change site. Never a gate; advisory evidence only.
|
|
9
|
+
/**
|
|
10
|
+
* Returns true when two identity entries denote DIFFERENT extension code:
|
|
11
|
+
* tree_hash, the per-module entry hashes (as an ordered resolved→hash list), or the
|
|
12
|
+
* override flag differ. Signals, timestamps, and pid are advisory — never compared.
|
|
13
|
+
*/
|
|
14
|
+
export function extensionIdentityDiffers(a, b) {
|
|
15
|
+
if (a.tree.tree_hash !== b.tree.tree_hash)
|
|
16
|
+
return true;
|
|
17
|
+
if (a.manifest?.content_hash !== b.manifest?.content_hash)
|
|
18
|
+
return true;
|
|
19
|
+
if ((a.override_active === true) !== (b.override_active === true))
|
|
20
|
+
return true;
|
|
21
|
+
if (a.modules.length !== b.modules.length)
|
|
22
|
+
return true;
|
|
23
|
+
const key = (m) => `${m.resolved}\u0000${m.entry_hash}`;
|
|
24
|
+
const aModules = a.modules.map(key).sort();
|
|
25
|
+
const bModules = b.modules.map(key).sort();
|
|
26
|
+
return aModules.some((line, i) => line !== bModules[i]);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=extension-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension-identity.js","sourceRoot":"","sources":["../../src/types/extension-identity.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,EAAE;AACF,2FAA2F;AAC3F,qFAAqF;AACrF,0FAA0F;AAC1F,yFAAyF;AACzF,2FAA2F;AAC3F,oEAAoE;AAsEpE;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CACtC,CAAyB,EACzB,CAAyB;IAEzB,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IACvD,IAAI,CAAC,CAAC,QAAQ,EAAE,YAAY,KAAK,CAAC,CAAC,QAAQ,EAAE,YAAY;QAAE,OAAO,IAAI,CAAC;IACvE,IAAI,CAAC,CAAC,CAAC,eAAe,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe,KAAK,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/E,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACvD,MAAM,GAAG,GAAG,CAAC,CAA0B,EAAU,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,SAAS,CAAC,CAAC,UAAU,EAAE,CAAC;IACzF,MAAM,QAAQ,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3C,MAAM,QAAQ,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3C,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ToolCallRecord } from './mcp-types.js';
|
|
2
|
+
import type { ExtensionIdentityEntry } from './extension-identity.js';
|
|
2
3
|
/**
|
|
3
4
|
* A single trace entry submitted by the agent. Submitted as-is; the engine
|
|
4
5
|
* canonicalizes and assigns seq before persisting.
|
|
@@ -183,6 +184,14 @@ export interface RunRecord {
|
|
|
183
184
|
* Keyed by entry name. Separate from step evidence — not in evidence[].
|
|
184
185
|
*/
|
|
185
186
|
workflow_context_snapshots?: Record<string, WorkflowContextSnapshot>;
|
|
187
|
+
/**
|
|
188
|
+
* Append-on-change history of the project-extension code identity that executed this run
|
|
189
|
+
* (issue #119). Each entry is captured at module-LOAD time by the CLI and appended lazily
|
|
190
|
+
* by the execution loop when it differs from the last recorded entry. Advisory evidence —
|
|
191
|
+
* WARN-never-gate. Absent for extension-free runs. JSON-file-store-only until external
|
|
192
|
+
* stores round-trip unknown optional RunRecord fields through update().
|
|
193
|
+
*/
|
|
194
|
+
extension_identity?: ExtensionIdentityEntry[];
|
|
186
195
|
/**
|
|
187
196
|
* PID of the agent process spawned by 'realm listen' for this run.
|
|
188
197
|
* Set after a successful spawn. Absent for runs not started by a webhook trigger.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-record.d.ts","sourceRoot":"","sources":["../../src/types/run-record.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"run-record.d.ts","sourceRoot":"","sources":["../../src/types/run-record.ts"],"names":[],"mappings":"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"}
|
|
@@ -42,9 +42,6 @@ export interface RateLimitConfig {
|
|
|
42
42
|
}
|
|
43
43
|
export interface ServiceDefinition {
|
|
44
44
|
adapter: string;
|
|
45
|
-
auth?: {
|
|
46
|
-
token_from: string;
|
|
47
|
-
};
|
|
48
45
|
trust: ServiceTrust;
|
|
49
46
|
rate_limit?: RateLimitConfig;
|
|
50
47
|
}
|
|
@@ -287,6 +284,29 @@ export interface WorkflowDefinition {
|
|
|
287
284
|
* Resolved relative to the workflow YAML file at load time.
|
|
288
285
|
* Falls back to <workflow-dir>/profiles/ if omitted. */
|
|
289
286
|
profiles_dir?: string;
|
|
287
|
+
/**
|
|
288
|
+
* Optional project extension module path(s) — RELATIVE to the workflow directory.
|
|
289
|
+
* Authored as `string | string[]`; loadWorkflowFromFile normalizes to `string[]` and the
|
|
290
|
+
* authored relative paths are stored untouched. Absolute paths and empty strings/arrays
|
|
291
|
+
* are rejected at load time. Requires file-based loading — loadWorkflowFromString rejects
|
|
292
|
+
* a declared `extensions` key (no directory context). Core resolves and stores PATHS only;
|
|
293
|
+
* module loading lives in the CLI composition layer (loadProjectExtensions).
|
|
294
|
+
*/
|
|
295
|
+
extensions?: string | string[];
|
|
296
|
+
/**
|
|
297
|
+
* Absolutized workflow directory, stamped by loadWorkflowFromFile when `extensions` is
|
|
298
|
+
* declared (precedent: workflow_context path absolutization). Extension paths resolve
|
|
299
|
+
* against this directory. Runtime-only — never write to workflow YAML.
|
|
300
|
+
*/
|
|
301
|
+
source_dir?: string;
|
|
302
|
+
/**
|
|
303
|
+
* Containment root for extension resolution: nearest ancestor of source_dir (inclusive)
|
|
304
|
+
* containing package.json or .git; fallback source_dir itself. Stamped at registration
|
|
305
|
+
* time from an operator-given path — this is NOT execution-time discovery-walking.
|
|
306
|
+
* The loader refuses any extension module resolving outside this root (realpath-compared).
|
|
307
|
+
* Runtime-only — never write to workflow YAML.
|
|
308
|
+
*/
|
|
309
|
+
trust_root?: string;
|
|
290
310
|
/**
|
|
291
311
|
* Map of resolved profile content keyed by profile name.
|
|
292
312
|
* Populated by loadWorkflowFromFile — absent when loaded from string.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-definition.d.ts","sourceRoot":"","sources":["../../src/types/workflow-definition.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAE1F,MAAM,MAAM,YAAY,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;AAEpF,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACxC,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,
|
|
1
|
+
{"version":3,"file":"workflow-definition.d.ts","sourceRoot":"","sources":["../../src/types/workflow-definition.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,6 +10,8 @@ export declare function loadWorkflowFromFile(filePath: string, registry?: Extens
|
|
|
10
10
|
/**
|
|
11
11
|
* Loads a WorkflowDefinition from a YAML string.
|
|
12
12
|
* Validates structure and DAG dependency references.
|
|
13
|
+
* A declared `extensions` key is a hard load error — extensions require file-based loading
|
|
14
|
+
* (no directory context exists to resolve relative extension module paths against).
|
|
13
15
|
* @throws WorkflowError on parse failure or structural validation errors.
|
|
14
16
|
*/
|
|
15
17
|
export declare function loadWorkflowFromString(content: string, registry?: ExtensionRegistry): WorkflowDefinition;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yaml-loader.d.ts","sourceRoot":"","sources":["../../src/workflow/yaml-loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,kBAAkB,EAGnB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AA6FnE,iFAAiF;AACjF,eAAO,MAAM,+BAA+B,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"yaml-loader.d.ts","sourceRoot":"","sources":["../../src/workflow/yaml-loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,kBAAkB,EAGnB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AA6FnE,iFAAiF;AACjF,eAAO,MAAM,+BAA+B,IAAI,CAAC;AAuFjD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,kBAAkB,CA0IpB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,kBAAkB,CAEpB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Workflow YAML loader — parses workflow.yaml files into typed WorkflowDefinition objects.
|
|
2
2
|
import { readFileSync, existsSync } from 'node:fs';
|
|
3
|
-
import { dirname, resolve, join } from 'node:path';
|
|
3
|
+
import { dirname, resolve, join, isAbsolute } from 'node:path';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
5
|
import { load } from 'js-yaml';
|
|
6
6
|
import { Ajv } from 'ajv';
|
|
@@ -71,6 +71,22 @@ function validateWhenReference(path, stepName, dependsOn, errors) {
|
|
|
71
71
|
}
|
|
72
72
|
/** Bumped on every breaking change to WorkflowDefinition's serialized format. */
|
|
73
73
|
export const CURRENT_WORKFLOW_SCHEMA_VERSION = 1;
|
|
74
|
+
/**
|
|
75
|
+
* Ajv-strict schema for one `services:` entry — CLOSED key set (`adapter`, `trust`,
|
|
76
|
+
* `rate_limit`). `auth`/`token_from` get a targeted migration rejection BEFORE this schema
|
|
77
|
+
* runs (removed in v0.14.0 — credentials bind in the deployment manifest). rate_limit's
|
|
78
|
+
* field-level numeric rules keep their existing dedicated checks below.
|
|
79
|
+
*/
|
|
80
|
+
const SERVICE_ENTRY_JSON_SCHEMA = {
|
|
81
|
+
type: 'object',
|
|
82
|
+
additionalProperties: false,
|
|
83
|
+
required: ['adapter'],
|
|
84
|
+
properties: {
|
|
85
|
+
adapter: { type: 'string', minLength: 1 },
|
|
86
|
+
trust: { enum: ['engine_delivered', 'engine_managed', 'agent_provided'] },
|
|
87
|
+
rate_limit: { type: 'object' },
|
|
88
|
+
},
|
|
89
|
+
};
|
|
74
90
|
const VALID_EXECUTIONS = new Set(['auto', 'agent', 'guard']);
|
|
75
91
|
const VALID_SERVICE_METHODS = new Set(['fetch', 'create', 'update', 'delete']);
|
|
76
92
|
const VALID_TRIGGER_RULES = new Set([
|
|
@@ -81,6 +97,58 @@ const VALID_TRIGGER_RULES = new Set([
|
|
|
81
97
|
'one_success',
|
|
82
98
|
'none_failed',
|
|
83
99
|
]);
|
|
100
|
+
/**
|
|
101
|
+
* JSON Schema (Ajv strict) for the top-level `extensions` key: a non-empty module path or a
|
|
102
|
+
* non-empty array of them. Relative-path enforcement is a separate explicit check (isAbsolute)
|
|
103
|
+
* so the error message can be actionable.
|
|
104
|
+
*/
|
|
105
|
+
const EXTENSIONS_JSON_SCHEMA = {
|
|
106
|
+
anyOf: [
|
|
107
|
+
{ type: 'string', minLength: 1 },
|
|
108
|
+
{ type: 'array', minItems: 1, items: { type: 'string', minLength: 1 } },
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Validates the authored `extensions` value: `string | string[]`, every entry a non-empty
|
|
113
|
+
* RELATIVE path. Returns actionable error strings (empty = valid).
|
|
114
|
+
*/
|
|
115
|
+
function validateExtensionsDeclaration(rawExtensions) {
|
|
116
|
+
const errors = [];
|
|
117
|
+
const ajv = new Ajv({ strict: true });
|
|
118
|
+
if (!ajv.validate(EXTENSIONS_JSON_SCHEMA, rawExtensions)) {
|
|
119
|
+
errors.push(`'extensions' must be a non-empty module path or a non-empty array of module paths ` +
|
|
120
|
+
`(e.g. extensions: ./dist/registry.js)`);
|
|
121
|
+
return errors;
|
|
122
|
+
}
|
|
123
|
+
const entries = typeof rawExtensions === 'string' ? [rawExtensions] : rawExtensions;
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
if (entry.trim() === '') {
|
|
126
|
+
errors.push(`'extensions' entries must be non-empty module paths`);
|
|
127
|
+
}
|
|
128
|
+
else if (isAbsolute(entry)) {
|
|
129
|
+
errors.push(`'extensions' entry '${entry}' is an absolute path — extension modules must be declared ` +
|
|
130
|
+
`RELATIVE to the workflow directory (e.g. ../dist/registry.js)`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return errors;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Finds the trust root for extension resolution: the nearest ancestor of `dir` (inclusive)
|
|
137
|
+
* containing `package.json` or `.git`; falls back to `dir` itself when no such ancestor exists.
|
|
138
|
+
* Derived once, at registration time, from an operator-given path — never at execution time.
|
|
139
|
+
*/
|
|
140
|
+
function findTrustRoot(dir) {
|
|
141
|
+
let current = dir;
|
|
142
|
+
for (;;) {
|
|
143
|
+
if (existsSync(join(current, 'package.json')) || existsSync(join(current, '.git'))) {
|
|
144
|
+
return current;
|
|
145
|
+
}
|
|
146
|
+
const parent = dirname(current);
|
|
147
|
+
if (parent === current)
|
|
148
|
+
return dir;
|
|
149
|
+
current = parent;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
84
152
|
/**
|
|
85
153
|
* Loads a WorkflowDefinition from a YAML file on disk.
|
|
86
154
|
* @throws WorkflowError on read failure or structural validation errors.
|
|
@@ -99,7 +167,7 @@ export function loadWorkflowFromFile(filePath, registry) {
|
|
|
99
167
|
retryable: false,
|
|
100
168
|
});
|
|
101
169
|
}
|
|
102
|
-
const definition =
|
|
170
|
+
const definition = parseWorkflowString(content, registry, { allowExtensions: true });
|
|
103
171
|
// Resolve agent profiles — only possible when we have a file path.
|
|
104
172
|
const workflowDir = dirname(resolve(filePath));
|
|
105
173
|
const profilesDir = definition.profiles_dir !== undefined
|
|
@@ -190,15 +258,36 @@ export function loadWorkflowFromFile(filePath, registry) {
|
|
|
190
258
|
description: 'Auto-registered schema.json from workflow directory',
|
|
191
259
|
};
|
|
192
260
|
}
|
|
261
|
+
// Resolution metadata: stamped for EVERY file-loaded definition (v0.14) — trust_root is
|
|
262
|
+
// the deployment-manifest anchor (`<trust_root>/realm.yaml`), needed by extension-free
|
|
263
|
+
// workflows that consume manifest-constructed adapters by name. Core resolves/stores
|
|
264
|
+
// PATHS only — it never imports modules or reads the manifest; that is the CLI's job.
|
|
265
|
+
definition.source_dir = workflowDir;
|
|
266
|
+
definition.trust_root = findTrustRoot(workflowDir);
|
|
267
|
+
if (definition.extensions !== undefined) {
|
|
268
|
+
definition.extensions =
|
|
269
|
+
typeof definition.extensions === 'string' ? [definition.extensions] : definition.extensions;
|
|
270
|
+
}
|
|
193
271
|
definition.origin = 'human';
|
|
194
272
|
return definition;
|
|
195
273
|
}
|
|
196
274
|
/**
|
|
197
275
|
* Loads a WorkflowDefinition from a YAML string.
|
|
198
276
|
* Validates structure and DAG dependency references.
|
|
277
|
+
* A declared `extensions` key is a hard load error — extensions require file-based loading
|
|
278
|
+
* (no directory context exists to resolve relative extension module paths against).
|
|
199
279
|
* @throws WorkflowError on parse failure or structural validation errors.
|
|
200
280
|
*/
|
|
201
281
|
export function loadWorkflowFromString(content, registry) {
|
|
282
|
+
return parseWorkflowString(content, registry, { allowExtensions: false });
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Shared YAML → WorkflowDefinition parser. `allowExtensions` distinguishes file-based loading
|
|
286
|
+
* (extensions permitted — a directory context exists) from string-based loading (hard error,
|
|
287
|
+
* fired before any other processing).
|
|
288
|
+
* @throws WorkflowError on parse failure or structural validation errors.
|
|
289
|
+
*/
|
|
290
|
+
function parseWorkflowString(content, registry, opts) {
|
|
202
291
|
// Step 1: Parse YAML
|
|
203
292
|
let raw;
|
|
204
293
|
try {
|
|
@@ -223,6 +312,21 @@ export function loadWorkflowFromString(content, registry) {
|
|
|
223
312
|
});
|
|
224
313
|
}
|
|
225
314
|
const doc = raw;
|
|
315
|
+
// Project extensions: hard error for string-based loading (fires before any other
|
|
316
|
+
// processing); shape validation (string | string[], relative-only) for file-based loading.
|
|
317
|
+
if ('extensions' in doc && doc['extensions'] !== undefined) {
|
|
318
|
+
if (!opts.allowExtensions) {
|
|
319
|
+
throw new WorkflowError(`Invalid workflow: 'extensions' requires file-based loading — no directory context is ` +
|
|
320
|
+
`available to resolve extension module paths. Register this workflow from its YAML ` +
|
|
321
|
+
`file (realm workflow register <path>).`, {
|
|
322
|
+
code: 'VALIDATION_WORKFLOW_SCHEMA',
|
|
323
|
+
category: 'VALIDATION',
|
|
324
|
+
agentAction: 'report_to_user',
|
|
325
|
+
retryable: false,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
errors.push(...validateExtensionsDeclaration(doc['extensions']));
|
|
329
|
+
}
|
|
226
330
|
const REQUIRED_TOP_LEVEL = ['id', 'name', 'version', 'steps'];
|
|
227
331
|
for (const field of REQUIRED_TOP_LEVEL) {
|
|
228
332
|
if (!(field in doc)) {
|
|
@@ -565,12 +669,28 @@ export function loadWorkflowFromString(content, registry) {
|
|
|
565
669
|
seen.add(server.id);
|
|
566
670
|
}
|
|
567
671
|
}
|
|
568
|
-
// Validate services: rate_limit fields.
|
|
672
|
+
// Validate services: Ajv-strict entry schema (closed key set) + rate_limit fields.
|
|
569
673
|
if (typeof doc['services'] === 'object' && doc['services'] !== null) {
|
|
570
674
|
for (const [serviceName, serviceRaw] of Object.entries(doc['services'])) {
|
|
571
675
|
if (typeof serviceRaw !== 'object' || serviceRaw === null)
|
|
572
676
|
continue;
|
|
573
677
|
const service = serviceRaw;
|
|
678
|
+
// PERMANENT targeted rejection — must win over the generic unknown-key error.
|
|
679
|
+
if ('auth' in service || 'token_from' in service) {
|
|
680
|
+
errors.push(`Service '${serviceName}': 'auth.token_from' was removed in v0.14.0 — bind ` +
|
|
681
|
+
`credentials in your deployment manifest (realm.yaml); see the migration note.`);
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
const serviceAjv = new Ajv({ strict: true, allErrors: true });
|
|
685
|
+
if (!serviceAjv.validate(SERVICE_ENTRY_JSON_SCHEMA, service)) {
|
|
686
|
+
for (const err of serviceAjv.errors ?? []) {
|
|
687
|
+
const detail = err.keyword === 'additionalProperties'
|
|
688
|
+
? `unknown key '${String(err.params.additionalProperty)}'`
|
|
689
|
+
: `${err.instancePath.replace(/^\//, '').replace(/\//g, '.') || 'entry'} ${err.message ?? 'invalid'}`;
|
|
690
|
+
errors.push(`Service '${serviceName}': ${detail}`);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
574
694
|
const rateLimit = service['rate_limit'];
|
|
575
695
|
if (rateLimit === undefined)
|
|
576
696
|
continue;
|