@obversa/runtime 0.1.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/LICENSE +21 -0
- package/README.md +192 -0
- package/dist/api.d.ts +72 -0
- package/dist/api.js +9775 -0
- package/dist/api.js.map +1 -0
- package/dist/artifacts/conformance.d.ts +1 -0
- package/dist/artifacts/file-store.d.ts +8 -0
- package/dist/artifacts/store.d.ts +1 -0
- package/dist/callback/approval.d.ts +8 -0
- package/dist/callback/client.d.ts +38 -0
- package/dist/callback/gate.d.ts +1 -0
- package/dist/callback/stored-client.d.ts +5 -0
- package/dist/chunk-3L6YNPN6.js +3 -0
- package/dist/chunk-3L6YNPN6.js.map +1 -0
- package/dist/chunk-5GLEABOU.js +3 -0
- package/dist/chunk-5GLEABOU.js.map +1 -0
- package/dist/chunk-DEW5R23M.js +338 -0
- package/dist/chunk-DEW5R23M.js.map +1 -0
- package/dist/chunk-DV5P4QLI.js +3056 -0
- package/dist/chunk-DV5P4QLI.js.map +1 -0
- package/dist/chunk-NIBHM5I5.js +34 -0
- package/dist/chunk-NIBHM5I5.js.map +1 -0
- package/dist/chunk-RZLMX3IA.js +368 -0
- package/dist/chunk-RZLMX3IA.js.map +1 -0
- package/dist/core/agent-md.d.ts +36 -0
- package/dist/core/agent.d.ts +86 -0
- package/dist/core/approval-job.d.ts +43 -0
- package/dist/core/assert-graph.d.ts +34 -0
- package/dist/core/budget.d.ts +49 -0
- package/dist/core/concurrency.d.ts +2 -0
- package/dist/core/condition.d.ts +209 -0
- package/dist/core/context.d.ts +36 -0
- package/dist/core/cost.d.ts +59 -0
- package/dist/core/dag.d.ts +20 -0
- package/dist/core/decision.d.ts +29 -0
- package/dist/core/describe.d.ts +56 -0
- package/dist/core/engine-meta.d.ts +5 -0
- package/dist/core/env-overlay.d.ts +35 -0
- package/dist/core/errors.d.ts +46 -0
- package/dist/core/feedback.d.ts +68 -0
- package/dist/core/git.d.ts +152 -0
- package/dist/core/guards.d.ts +70 -0
- package/dist/core/isolated.d.ts +40 -0
- package/dist/core/job.d.ts +134 -0
- package/dist/core/limits.d.ts +22 -0
- package/dist/core/loop.d.ts +24 -0
- package/dist/core/merge.d.ts +30 -0
- package/dist/core/pipeline.d.ts +35 -0
- package/dist/core/process.d.ts +11 -0
- package/dist/core/progress.d.ts +82 -0
- package/dist/core/redact.d.ts +1 -0
- package/dist/core/stats.d.ts +63 -0
- package/dist/core/team.d.ts +34 -0
- package/dist/core/text.d.ts +8 -0
- package/dist/core/tournament.d.ts +25 -0
- package/dist/core/types.d.ts +650 -0
- package/dist/engines/command-runner.d.ts +1 -0
- package/dist/engines/conformance.d.ts +1 -0
- package/dist/engines/engine.d.ts +4 -0
- package/dist/engines/failure.d.ts +1 -0
- package/dist/engines/fallback.d.ts +35 -0
- package/dist/engines/message-map.d.ts +1 -0
- package/dist/engines/mock.d.ts +1 -0
- package/dist/engines/preflight.d.ts +36 -0
- package/dist/env/command.d.ts +50 -0
- package/dist/env/command.js +65 -0
- package/dist/env/command.js.map +1 -0
- package/dist/env/environment.d.ts +4 -0
- package/dist/env/mock.d.ts +24 -0
- package/dist/events/conformance.d.ts +1 -0
- package/dist/events/envelope.d.ts +1 -0
- package/dist/events/jsonl-store.d.ts +8 -0
- package/dist/events/store.d.ts +1 -0
- package/dist/graph/commands.d.ts +5 -0
- package/dist/graph/conformance.d.ts +32 -0
- package/dist/graph/kernel.d.ts +5 -0
- package/dist/graph/plan.d.ts +1 -0
- package/dist/graph/type.d.ts +7 -0
- package/dist/graph/value.d.ts +1 -0
- package/dist/graph-types/dag.d.ts +137 -0
- package/dist/graph-types/loop.d.ts +194 -0
- package/dist/graph-types/team.d.ts +68 -0
- package/dist/memory.d.ts +82 -0
- package/dist/memory.js +397 -0
- package/dist/memory.js.map +1 -0
- package/dist/proof/acceptance.d.ts +5 -0
- package/dist/proof/artifact.d.ts +6 -0
- package/dist/proof/cache.d.ts +4 -0
- package/dist/runtime/attempt.d.ts +19 -0
- package/dist/runtime/budget.d.ts +4 -0
- package/dist/runtime/engine-availability.d.ts +18 -0
- package/dist/runtime/graph-executor.d.ts +7 -0
- package/dist/runtime/monitor.d.ts +80 -0
- package/dist/runtime/node-lifecycle.d.ts +84 -0
- package/dist/runtime/paths.d.ts +2 -0
- package/dist/runtime/persist.d.ts +31 -0
- package/dist/runtime/preflight-record.d.ts +149 -0
- package/dist/runtime/process-tree.d.ts +1 -0
- package/dist/runtime/result-contract.d.ts +4 -0
- package/dist/runtime/result-parts.d.ts +1 -0
- package/dist/runtime/run-definition.d.ts +24 -0
- package/dist/runtime/run-event.d.ts +9 -0
- package/dist/runtime/runner.d.ts +139 -0
- package/dist/runtime/supervisor.d.ts +111 -0
- package/dist/runtime/team-rooms.d.ts +13 -0
- package/dist/runtime/workspace-policy.d.ts +25 -0
- package/dist/storage/error.d.ts +1 -0
- package/dist/storage/id.d.ts +1 -0
- package/dist/storage/local.d.ts +12 -0
- package/dist/storage/local.js +1492 -0
- package/dist/storage/local.js.map +1 -0
- package/dist/testing.d.ts +15 -0
- package/dist/testing.js +274 -0
- package/dist/testing.js.map +1 -0
- package/dist/workflow-agent-response.d.ts +3 -0
- package/dist/workflow-support.d.ts +36 -0
- package/dist/workflow-support.js +5 -0
- package/dist/workflow-support.js.map +1 -0
- package/dist/workflow.d.ts +73 -0
- package/dist/workspace/conformance.d.ts +1 -0
- package/dist/workspace/git-provider.d.ts +26 -0
- package/dist/workspace/provider.d.ts +2 -0
- package/package.json +91 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { runChild, RunChildError } from '@obversa/core';
|
|
2
|
+
|
|
3
|
+
// src/core/process.ts
|
|
4
|
+
var DEFAULT_PROCESS_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
5
|
+
var DEFAULT_PROCESS_GRACE_MS = 5 * 1e3;
|
|
6
|
+
var DEFAULT_PROCESS_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
7
|
+
async function runRuntimeProcess(options) {
|
|
8
|
+
try {
|
|
9
|
+
return await runChild({
|
|
10
|
+
...options,
|
|
11
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_PROCESS_TIMEOUT_MS,
|
|
12
|
+
killGraceMs: options.killGraceMs ?? DEFAULT_PROCESS_GRACE_MS,
|
|
13
|
+
maxOutputBytes: options.maxOutputBytes ?? DEFAULT_PROCESS_OUTPUT_BYTES
|
|
14
|
+
});
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error instanceof RunChildError && error.code === "SPAWN_FAILED") {
|
|
17
|
+
return Object.freeze({
|
|
18
|
+
exitCode: null,
|
|
19
|
+
stdout: error.stdout,
|
|
20
|
+
stderr: error.stderr,
|
|
21
|
+
timedOut: false,
|
|
22
|
+
aborted: false
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function processText(output) {
|
|
29
|
+
return new TextDecoder().decode(output);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { processText, runRuntimeProcess };
|
|
33
|
+
//# sourceMappingURL=chunk-NIBHM5I5.js.map
|
|
34
|
+
//# sourceMappingURL=chunk-NIBHM5I5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/process.ts"],"names":[],"mappings":";;;AAOO,IAAM,0BAAA,GAA6B,KAAK,EAAA,GAAK,GAAA;AAC7C,IAAM,2BAA2B,CAAA,GAAI,GAAA;AACrC,IAAM,4BAAA,GAA+B,KAAK,IAAA,GAAQ,IAAA;AAWzD,eAAsB,kBACpB,OAAA,EACyB;AACzB,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,QAAA,CAAS;AAAA,MACpB,GAAG,OAAA;AAAA,MACH,SAAA,EAAW,QAAQ,SAAA,IAAa,0BAAA;AAAA,MAChC,WAAA,EAAa,QAAQ,WAAA,IAAe,wBAAA;AAAA,MACpC,cAAA,EAAgB,QAAQ,cAAA,IAAkB;AAAA,KAC3C,CAAA;AAAA,EACH,SAAS,KAAA,EAAO;AACd,IAAA,IAAI,KAAA,YAAiB,aAAA,IAAiB,KAAA,CAAM,IAAA,KAAS,cAAA,EAAgB;AACnE,MAAA,OAAO,OAAO,MAAA,CAAO;AAAA,QACnB,QAAA,EAAU,IAAA;AAAA,QACV,QAAQ,KAAA,CAAM,MAAA;AAAA,QACd,QAAQ,KAAA,CAAM,MAAA;AAAA,QACd,QAAA,EAAU,KAAA;AAAA,QACV,OAAA,EAAS;AAAA,OACV,CAAA;AAAA,IACH;AACA,IAAA,MAAM,KAAA;AAAA,EACR;AACF;AAEO,SAAS,YAAY,MAAA,EAA4B;AACtD,EAAA,OAAO,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,MAAM,CAAA;AACxC","file":"chunk-NIBHM5I5.js","sourcesContent":["import {\n RunChildError,\n runChild,\n type RunChildOptions,\n type RunChildResult,\n} from '@obversa/core';\n\nexport const DEFAULT_PROCESS_TIMEOUT_MS = 10 * 60 * 1_000;\nexport const DEFAULT_PROCESS_GRACE_MS = 5 * 1_000;\nexport const DEFAULT_PROCESS_OUTPUT_BYTES = 16 * 1_024 * 1_024;\n\nexport type RuntimeProcessOptions = Omit<\n RunChildOptions,\n 'timeoutMs' | 'killGraceMs' | 'maxOutputBytes' | 'hooks'\n> & {\n readonly timeoutMs?: number;\n readonly killGraceMs?: number;\n readonly maxOutputBytes?: number;\n};\n\nexport async function runRuntimeProcess(\n options: RuntimeProcessOptions,\n): Promise<RunChildResult> {\n try {\n return await runChild({\n ...options,\n timeoutMs: options.timeoutMs ?? DEFAULT_PROCESS_TIMEOUT_MS,\n killGraceMs: options.killGraceMs ?? DEFAULT_PROCESS_GRACE_MS,\n maxOutputBytes: options.maxOutputBytes ?? DEFAULT_PROCESS_OUTPUT_BYTES,\n });\n } catch (error) {\n if (error instanceof RunChildError && error.code === 'SPAWN_FAILED') {\n return Object.freeze({\n exitCode: null,\n stdout: error.stdout,\n stderr: error.stderr,\n timedOut: false,\n aborted: false,\n });\n }\n throw error;\n }\n}\n\nexport function processText(output: Uint8Array): string {\n return new TextDecoder().decode(output);\n}\n"]}
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { validateResolvedPlan } from './chunk-3L6YNPN6.js';
|
|
2
|
+
import { canonicalJson, digestJson, GraphValidationError } from './chunk-5GLEABOU.js';
|
|
3
|
+
import { validateStorageId, validateNewArtifact, StorageError, validateRunDefinition, validateNewDomainEvent, validateRunStartRecord, validateRunStorageRecord, validateArtifactReference, validateDomainEventEnvelope, findKnownSecretInEvents } from '@obversa/api';
|
|
4
|
+
export { StorageError, findKnownSecretInEvents, validateArtifactReference, validateArtifactScope, validateDomainEventBatch, validateDomainEventEnvelope, validateDomainEventId, validateEventStreamRef, validateNewArtifact, validateNewDomainEvent, validateRunDefinition, validateRunStartRecord, validateRunStoragePolicy, validateRunStorageRecord, validateStorageId, validateStreamRevision } from '@obversa/api';
|
|
5
|
+
import { createHash } from 'crypto';
|
|
6
|
+
import { isDeepStrictEqual } from 'util';
|
|
7
|
+
import { runDefinitionSupport } from '@obversa/api/run-definition-support';
|
|
8
|
+
|
|
9
|
+
var fail = runDefinitionSupport.fail;
|
|
10
|
+
var { json, record, text, digest, validateCompiledDefinition } = runDefinitionSupport;
|
|
11
|
+
function validateResolvedPlanSnapshot(value) {
|
|
12
|
+
const plan = json(value.plan, "/resolvedPlan/plan");
|
|
13
|
+
if (plan === null || typeof plan !== "object" || Array.isArray(plan)) {
|
|
14
|
+
fail("/resolvedPlan/plan", "Resolved plan must be an object.");
|
|
15
|
+
}
|
|
16
|
+
const stable = canonicalJson(plan);
|
|
17
|
+
if (value.canonicalJson !== stable) {
|
|
18
|
+
fail("/resolvedPlan/canonicalJson", "Resolved plan bytes do not match its value.");
|
|
19
|
+
}
|
|
20
|
+
const planDigest = digestJson(plan);
|
|
21
|
+
if (value.digest !== planDigest) {
|
|
22
|
+
fail("/resolvedPlan/digest", "Resolved plan digest does not match its value.");
|
|
23
|
+
}
|
|
24
|
+
const validatedPlan = storedResolvedPlan(plan, "/resolvedPlan/plan");
|
|
25
|
+
return Object.freeze({
|
|
26
|
+
plan: validatedPlan,
|
|
27
|
+
canonicalJson: stable,
|
|
28
|
+
digest: planDigest
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function storedResolvedPlan(value, path) {
|
|
32
|
+
try {
|
|
33
|
+
return validateResolvedPlan(value);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (!(error instanceof GraphValidationError)) throw error;
|
|
36
|
+
const first = error.issues[0];
|
|
37
|
+
fail(`${path}${first?.path ?? ""}`, first?.message ?? error.message);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function validateResolvedPlanGraph(plan, definition) {
|
|
41
|
+
const graph = record(plan.graph, "/resolvedPlan/graph", "Resolved plan graph");
|
|
42
|
+
if (digest(graph.definitionDigest, "/resolvedPlan/graph/definitionDigest") !== definition.digest) {
|
|
43
|
+
fail("/resolvedPlan/graph/definitionDigest", "Resolved plan belongs to another graph definition.");
|
|
44
|
+
}
|
|
45
|
+
if (graph.id !== definition.value.id) {
|
|
46
|
+
fail("/resolvedPlan/graph/id", "Resolved plan belongs to another graph id.");
|
|
47
|
+
}
|
|
48
|
+
if (graph.definitionVersion !== definition.value.definitionVersion) {
|
|
49
|
+
fail("/resolvedPlan/graph/definitionVersion", "Resolved plan belongs to another graph definition version.");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function validateBinding(binding, storedRecord) {
|
|
53
|
+
const bindingRecord = validateRunStorageRecord(binding.record);
|
|
54
|
+
if (storedRecord !== void 0 && !isDeepStrictEqual(bindingRecord, storedRecord)) {
|
|
55
|
+
fail("/storage", "Stored and live storage records must match.");
|
|
56
|
+
}
|
|
57
|
+
const knownSecrets = json(binding.knownSecrets, "/binding/knownSecrets");
|
|
58
|
+
if (!Array.isArray(knownSecrets) || knownSecrets.some((secret) => typeof secret !== "string" || secret.length === 0)) {
|
|
59
|
+
fail("/binding/knownSecrets", "Known secrets must be non-empty strings.");
|
|
60
|
+
}
|
|
61
|
+
const secrets = knownSecrets;
|
|
62
|
+
const serializedRecord = canonicalJson(bindingRecord);
|
|
63
|
+
if (secrets.some((secret) => serializedRecord.includes(secret) || jsonValueContainsText(bindingRecord, secret))) {
|
|
64
|
+
throw new StorageError(
|
|
65
|
+
"KNOWN_SECRET",
|
|
66
|
+
"Storage binding record contains a configured known secret."
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
record: bindingRecord,
|
|
71
|
+
knownSecrets: secrets
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function jsonValueContainsText(value, textValue) {
|
|
75
|
+
if (typeof value === "string") return value.includes(textValue);
|
|
76
|
+
if (value === null || typeof value !== "object") return false;
|
|
77
|
+
if (Array.isArray(value)) {
|
|
78
|
+
return value.some((item) => jsonValueContainsText(item, textValue));
|
|
79
|
+
}
|
|
80
|
+
return Object.entries(value).some(([key, item]) => key.includes(textValue) || jsonValueContainsText(item, textValue));
|
|
81
|
+
}
|
|
82
|
+
function expectedArtifactReference(artifact) {
|
|
83
|
+
return validateArtifactReference({
|
|
84
|
+
schemaVersion: 1,
|
|
85
|
+
digest: `sha256:${createHash("sha256").update(artifact.bytes).digest("hex")}`,
|
|
86
|
+
byteLength: artifact.bytes.byteLength,
|
|
87
|
+
mediaType: artifact.mediaType,
|
|
88
|
+
purpose: artifact.purpose
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function preflightArtifacts(artifacts, policy, knownSecrets) {
|
|
92
|
+
const uniqueBytes = /* @__PURE__ */ new Map();
|
|
93
|
+
for (const artifact of artifacts) {
|
|
94
|
+
if (artifact.bytes.byteLength > policy.maxArtifactBytes) {
|
|
95
|
+
throw new StorageError(
|
|
96
|
+
"STORAGE_LIMIT_EXCEEDED",
|
|
97
|
+
"Run-start artifact exceeds maxArtifactBytes.",
|
|
98
|
+
{
|
|
99
|
+
purpose: artifact.purpose,
|
|
100
|
+
byteLength: artifact.bytes.byteLength,
|
|
101
|
+
maximumBytes: policy.maxArtifactBytes
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
const bytes = Buffer.from(artifact.bytes);
|
|
106
|
+
if (knownSecrets.some((secret) => bytes.includes(Buffer.from(secret, "utf8")))) {
|
|
107
|
+
throw new StorageError(
|
|
108
|
+
"KNOWN_SECRET",
|
|
109
|
+
"Exact run-start artifact contains a configured known secret.",
|
|
110
|
+
{ purpose: artifact.purpose }
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
const reference = expectedArtifactReference(artifact);
|
|
114
|
+
uniqueBytes.set(reference.digest, reference.byteLength);
|
|
115
|
+
}
|
|
116
|
+
const total = [...uniqueBytes.values()].reduce((sum, size) => sum + size, 0);
|
|
117
|
+
if (total > policy.maxTotalArtifactBytesPerRun) {
|
|
118
|
+
throw new StorageError(
|
|
119
|
+
"STORAGE_LIMIT_EXCEEDED",
|
|
120
|
+
"Run-start artifacts exceed maxTotalArtifactBytesPerRun.",
|
|
121
|
+
{ byteLength: total, maximumBytes: policy.maxTotalArtifactBytesPerRun }
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function preflightRunStartEvent(event, policy, knownSecrets) {
|
|
126
|
+
const payloadByteLength = Buffer.byteLength(canonicalJson(event.payload), "utf8");
|
|
127
|
+
if (payloadByteLength > policy.maxEventPayloadBytes) {
|
|
128
|
+
throw new StorageError(
|
|
129
|
+
"STORAGE_LIMIT_EXCEEDED",
|
|
130
|
+
"Run-start payload exceeds maxEventPayloadBytes.",
|
|
131
|
+
{ byteLength: payloadByteLength, maximumBytes: policy.maxEventPayloadBytes }
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
const envelope = validateDomainEventEnvelope({
|
|
135
|
+
...event,
|
|
136
|
+
envelopeVersion: 1,
|
|
137
|
+
streamId: event.correlationId,
|
|
138
|
+
revision: 1
|
|
139
|
+
});
|
|
140
|
+
const serializedEvent = `${JSON.stringify(envelope)}
|
|
141
|
+
`;
|
|
142
|
+
const appendByteLength = Buffer.byteLength(serializedEvent, "utf8");
|
|
143
|
+
if (appendByteLength > policy.maxAppendBatchBytes) {
|
|
144
|
+
throw new StorageError(
|
|
145
|
+
"STORAGE_LIMIT_EXCEEDED",
|
|
146
|
+
"Run-start append exceeds maxAppendBatchBytes.",
|
|
147
|
+
{ byteLength: appendByteLength, maximumBytes: policy.maxAppendBatchBytes }
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
const secret = findKnownSecretInEvents(
|
|
151
|
+
knownSecrets,
|
|
152
|
+
serializedEvent,
|
|
153
|
+
[event]
|
|
154
|
+
);
|
|
155
|
+
if (secret !== void 0) {
|
|
156
|
+
throw new StorageError(
|
|
157
|
+
"KNOWN_SECRET",
|
|
158
|
+
"Run-start event contains a configured known secret.",
|
|
159
|
+
{ eventId: event.eventId }
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function artifactError(error, record2, referencePath) {
|
|
164
|
+
return new StorageError(error.code, error.message, {
|
|
165
|
+
...error.details,
|
|
166
|
+
streamId: record2.streamId,
|
|
167
|
+
revision: record2.revision,
|
|
168
|
+
eventId: record2.eventId,
|
|
169
|
+
referencePath
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function readPlanBytes(bytes, referencePath) {
|
|
173
|
+
let textValue;
|
|
174
|
+
try {
|
|
175
|
+
textValue = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
176
|
+
} catch {
|
|
177
|
+
fail(referencePath, "Resolved plan bytes must be valid UTF-8.");
|
|
178
|
+
}
|
|
179
|
+
return textValue;
|
|
180
|
+
}
|
|
181
|
+
function parseStoredPlan(bytes, digestValue) {
|
|
182
|
+
const textValue = readPlanBytes(bytes, "/resolvedPlan");
|
|
183
|
+
let parsed;
|
|
184
|
+
try {
|
|
185
|
+
parsed = JSON.parse(textValue);
|
|
186
|
+
} catch {
|
|
187
|
+
fail("/resolvedPlan", "Resolved plan bytes must contain JSON.");
|
|
188
|
+
}
|
|
189
|
+
const plan = json(parsed, "/resolvedPlan");
|
|
190
|
+
if (plan === null || typeof plan !== "object" || Array.isArray(plan)) {
|
|
191
|
+
fail("/resolvedPlan", "Resolved plan must be an object.");
|
|
192
|
+
}
|
|
193
|
+
if (canonicalJson(plan) !== textValue) {
|
|
194
|
+
fail("/resolvedPlan", "Resolved plan bytes must use stable JSON encoding.");
|
|
195
|
+
}
|
|
196
|
+
if (digestJson(plan) !== digestValue) {
|
|
197
|
+
fail("/resolvedPlan", "Resolved plan digest does not match its bytes.");
|
|
198
|
+
}
|
|
199
|
+
const validatedPlan = storedResolvedPlan(plan, "/resolvedPlan");
|
|
200
|
+
return Object.freeze({
|
|
201
|
+
plan: validatedPlan,
|
|
202
|
+
canonicalJson: textValue,
|
|
203
|
+
digest: digestValue
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async function persistRunDefinition(binding, input) {
|
|
207
|
+
const { record: storage, knownSecrets } = validateBinding(binding);
|
|
208
|
+
const graphDefinition = validateCompiledDefinition(input.graphDefinition);
|
|
209
|
+
const resolvedPlan = validateResolvedPlanSnapshot(input.resolvedPlan);
|
|
210
|
+
validateResolvedPlanGraph(resolvedPlan.plan, graphDefinition);
|
|
211
|
+
const resolvedInputs = record(
|
|
212
|
+
input.resolvedInputs,
|
|
213
|
+
"/resolvedInputs",
|
|
214
|
+
"Resolved inputs"
|
|
215
|
+
);
|
|
216
|
+
const workspaceBinding = input.workspaceBinding === null ? null : record(input.workspaceBinding, "/workspaceBinding", "Workspace binding");
|
|
217
|
+
const hostBinding = input.hostBinding === null ? null : Object.freeze({
|
|
218
|
+
bytes: Uint8Array.from(input.hostBinding.bytes),
|
|
219
|
+
mediaType: text(input.hostBinding.mediaType, "/hostBinding/mediaType", "Host-binding media type")
|
|
220
|
+
});
|
|
221
|
+
const runId = validateStorageId(input.runId, "/runId");
|
|
222
|
+
const scope = { namespace: storage.namespace, runId };
|
|
223
|
+
const resolvedPlanArtifact = validateNewArtifact({
|
|
224
|
+
bytes: new TextEncoder().encode(resolvedPlan.canonicalJson),
|
|
225
|
+
mediaType: "application/json",
|
|
226
|
+
purpose: "resolved-plan",
|
|
227
|
+
contentMode: "state"
|
|
228
|
+
});
|
|
229
|
+
const hostBindingArtifact = hostBinding === null ? null : validateNewArtifact({
|
|
230
|
+
bytes: hostBinding.bytes,
|
|
231
|
+
mediaType: hostBinding.mediaType,
|
|
232
|
+
purpose: "host-binding",
|
|
233
|
+
contentMode: "exact"
|
|
234
|
+
});
|
|
235
|
+
preflightArtifacts(
|
|
236
|
+
hostBindingArtifact === null ? [resolvedPlanArtifact] : [resolvedPlanArtifact, hostBindingArtifact],
|
|
237
|
+
storage.policy,
|
|
238
|
+
knownSecrets
|
|
239
|
+
);
|
|
240
|
+
const artifacts = hostBindingArtifact === null ? [resolvedPlanArtifact] : [resolvedPlanArtifact, hostBindingArtifact];
|
|
241
|
+
const expectedPlanReference = expectedArtifactReference(
|
|
242
|
+
resolvedPlanArtifact
|
|
243
|
+
);
|
|
244
|
+
const expectedHostReference = hostBindingArtifact === null ? null : expectedArtifactReference(hostBindingArtifact);
|
|
245
|
+
const expectedReferences = expectedHostReference === null ? [expectedPlanReference] : [expectedPlanReference, expectedHostReference];
|
|
246
|
+
const predictedReferences = await binding.artifactStore.preflightWrite(
|
|
247
|
+
scope,
|
|
248
|
+
artifacts
|
|
249
|
+
);
|
|
250
|
+
if (!isDeepStrictEqual(predictedReferences, expectedReferences)) {
|
|
251
|
+
throw new StorageError(
|
|
252
|
+
"ARTIFACT_INTEGRITY",
|
|
253
|
+
"Artifact store predicted the wrong run-start references.",
|
|
254
|
+
{ expected: expectedReferences, actual: predictedReferences }
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
const definition = validateRunDefinition({
|
|
258
|
+
schemaVersion: 1,
|
|
259
|
+
runId,
|
|
260
|
+
graphDefinition,
|
|
261
|
+
resolvedPlan: expectedPlanReference,
|
|
262
|
+
resolvedInputs,
|
|
263
|
+
storage,
|
|
264
|
+
workspaceBinding,
|
|
265
|
+
hostBinding: expectedHostReference
|
|
266
|
+
});
|
|
267
|
+
const event = validateNewDomainEvent({
|
|
268
|
+
eventId: input.eventId,
|
|
269
|
+
type: "graph:run-started",
|
|
270
|
+
version: 1,
|
|
271
|
+
timestamp: input.timestamp,
|
|
272
|
+
correlationId: runId,
|
|
273
|
+
causationId: null,
|
|
274
|
+
payload: { definition }
|
|
275
|
+
});
|
|
276
|
+
preflightRunStartEvent(event, storage.policy, knownSecrets);
|
|
277
|
+
await binding.eventStore.preflightAppend(
|
|
278
|
+
{ namespace: storage.namespace, streamId: runId },
|
|
279
|
+
0,
|
|
280
|
+
[event]
|
|
281
|
+
);
|
|
282
|
+
const resolvedPlanReference = await binding.artifactStore.write(scope, {
|
|
283
|
+
...resolvedPlanArtifact
|
|
284
|
+
});
|
|
285
|
+
if (!isDeepStrictEqual(resolvedPlanReference, expectedPlanReference)) {
|
|
286
|
+
throw new StorageError(
|
|
287
|
+
"ARTIFACT_INTEGRITY",
|
|
288
|
+
"Resolved plan store returned the wrong reference.",
|
|
289
|
+
{ expected: expectedPlanReference, actual: resolvedPlanReference }
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
const hostBindingReference = hostBindingArtifact === null ? null : await binding.artifactStore.write(scope, {
|
|
293
|
+
...hostBindingArtifact
|
|
294
|
+
});
|
|
295
|
+
if (!isDeepStrictEqual(hostBindingReference, expectedHostReference)) {
|
|
296
|
+
throw new StorageError(
|
|
297
|
+
"ARTIFACT_INTEGRITY",
|
|
298
|
+
"Host-binding store returned the wrong reference.",
|
|
299
|
+
{ expected: expectedHostReference, actual: hostBindingReference }
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
const revision = await binding.eventStore.append(
|
|
303
|
+
{ namespace: storage.namespace, streamId: runId },
|
|
304
|
+
0,
|
|
305
|
+
[event]
|
|
306
|
+
);
|
|
307
|
+
return validateRunStartRecord({
|
|
308
|
+
envelopeVersion: 1,
|
|
309
|
+
streamId: runId,
|
|
310
|
+
revision,
|
|
311
|
+
...event
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
async function loadRunDefinition(binding, runId) {
|
|
315
|
+
const live = validateBinding(binding);
|
|
316
|
+
const streamId = validateStorageId(runId, "/runId");
|
|
317
|
+
let first;
|
|
318
|
+
for await (const event of binding.eventStore.read({
|
|
319
|
+
namespace: live.record.namespace,
|
|
320
|
+
streamId
|
|
321
|
+
})) {
|
|
322
|
+
first = event;
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
if (!first) {
|
|
326
|
+
fail("/runId", `Run "${streamId}" has no stored definition.`);
|
|
327
|
+
}
|
|
328
|
+
const record2 = validateRunStartRecord(first);
|
|
329
|
+
validateBinding(binding, record2.payload.definition.storage);
|
|
330
|
+
const scope = { namespace: live.record.namespace, runId: streamId };
|
|
331
|
+
let planBytes;
|
|
332
|
+
try {
|
|
333
|
+
planBytes = await binding.artifactStore.read(
|
|
334
|
+
scope,
|
|
335
|
+
record2.payload.definition.resolvedPlan
|
|
336
|
+
);
|
|
337
|
+
} catch (error) {
|
|
338
|
+
if (!(error instanceof StorageError)) throw error;
|
|
339
|
+
throw artifactError(error, record2, "/payload/definition/resolvedPlan");
|
|
340
|
+
}
|
|
341
|
+
const resolvedPlan = parseStoredPlan(
|
|
342
|
+
planBytes,
|
|
343
|
+
record2.payload.definition.resolvedPlan.digest
|
|
344
|
+
);
|
|
345
|
+
validateResolvedPlanGraph(
|
|
346
|
+
resolvedPlan.plan,
|
|
347
|
+
record2.payload.definition.graphDefinition
|
|
348
|
+
);
|
|
349
|
+
let hostBindingBytes = null;
|
|
350
|
+
const hostBinding = record2.payload.definition.hostBinding;
|
|
351
|
+
if (hostBinding !== null) {
|
|
352
|
+
try {
|
|
353
|
+
hostBindingBytes = await binding.artifactStore.read(scope, hostBinding);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
if (!(error instanceof StorageError)) throw error;
|
|
356
|
+
throw artifactError(error, record2, "/payload/definition/hostBinding");
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return Object.freeze({
|
|
360
|
+
record: record2,
|
|
361
|
+
resolvedPlan,
|
|
362
|
+
hostBindingBytes
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export { loadRunDefinition, persistRunDefinition };
|
|
367
|
+
//# sourceMappingURL=chunk-RZLMX3IA.js.map
|
|
368
|
+
//# sourceMappingURL=chunk-RZLMX3IA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/run-definition.ts"],"names":["record","validateStorageId"],"mappings":"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","file":"chunk-RZLMX3IA.js","sourcesContent":["import { createHash } from 'node:crypto';\nimport { isDeepStrictEqual } from 'node:util';\n\nimport {\n validateRunDefinition,\n validateRunStartRecord,\n validateRunStorageRecord,\n type CompiledGraphDefinition,\n type RunStorageBinding,\n type RunStorageRecord,\n type RunStoragePolicy,\n type RunStartRecord,\n type NewRunStartedEvent,\n} from '@obversa/api';\nimport { runDefinitionSupport } from '@obversa/api/run-definition-support';\nimport {\n canonicalJson,\n digestJson,\n GraphValidationError,\n type JsonObject,\n type JsonValue,\n type RunBrief,\n type Sha256Digest,\n} from '../graph/value.js';\nimport {\n validateResolvedPlan,\n type ResolvedPlan,\n type ResolvedPlanSnapshot,\n} from '../graph/plan.js';\nimport {\n validateArtifactReference,\n validateNewArtifact,\n type ArtifactBatch,\n type NewArtifact,\n type ArtifactReference,\n} from '../artifacts/store.js';\nimport {\n validateDomainEventEnvelope,\n validateNewDomainEvent,\n type DomainEventEnvelope,\n} from '../events/envelope.js';\nimport { findKnownSecretInEvents } from '../events/store.js';\nimport { StorageError } from '../storage/error.js';\nimport { validateStorageId } from '../storage/id.js';\n\nexport {\n validateRunDefinition,\n validateRunStartRecord,\n validateRunStoragePolicy,\n validateRunStorageRecord,\n type StorageProviderRecord,\n type SensitiveContentPolicy,\n type RunStoragePolicy,\n type RunStorageRecord,\n type RunDefinition,\n type RunStartedPayload,\n type NewRunStartedEvent,\n type RunStartRecord,\n type RunStorageBinding,\n} from '@obversa/api';\n\nconst fail: (path: string, message: string) => never = runDefinitionSupport.fail;\nconst { json, record, text, digest, validateCompiledDefinition } = runDefinitionSupport;\n\nexport interface PersistRunDefinitionInput {\n readonly runId: string;\n readonly eventId: string;\n readonly timestamp: string;\n readonly graphDefinition: CompiledGraphDefinition;\n readonly resolvedPlan: ResolvedPlanSnapshot;\n readonly resolvedInputs: RunBrief;\n readonly workspaceBinding: JsonObject | null;\n readonly hostBinding: {\n readonly bytes: Uint8Array;\n readonly mediaType: string;\n } | null;\n}\n\nexport interface LoadedRunDefinition {\n readonly record: RunStartRecord;\n readonly resolvedPlan: ResolvedPlanSnapshot;\n readonly hostBindingBytes: Uint8Array | null;\n}\n\nfunction validateResolvedPlanSnapshot(\n value: ResolvedPlanSnapshot,\n): ResolvedPlanSnapshot {\n const plan = json(value.plan, '/resolvedPlan/plan');\n if (plan === null || typeof plan !== 'object' || Array.isArray(plan)) {\n fail('/resolvedPlan/plan', 'Resolved plan must be an object.');\n }\n const stable = canonicalJson(plan);\n if (value.canonicalJson !== stable) {\n fail('/resolvedPlan/canonicalJson', 'Resolved plan bytes do not match its value.');\n }\n const planDigest = digestJson(plan);\n if (value.digest !== planDigest) {\n fail('/resolvedPlan/digest', 'Resolved plan digest does not match its value.');\n }\n const validatedPlan = storedResolvedPlan(plan, '/resolvedPlan/plan');\n return Object.freeze({\n plan: validatedPlan,\n canonicalJson: stable,\n digest: planDigest,\n });\n}\n\nfunction storedResolvedPlan(value: unknown, path: string): ResolvedPlan {\n try {\n return validateResolvedPlan(value);\n } catch (error) {\n if (!(error instanceof GraphValidationError)) throw error;\n const first = error.issues[0];\n fail(`${path}${first?.path ?? ''}`, first?.message ?? error.message);\n }\n}\n\nfunction validateResolvedPlanGraph(\n plan: ResolvedPlan,\n definition: CompiledGraphDefinition,\n): void {\n const graph = record(plan.graph, '/resolvedPlan/graph', 'Resolved plan graph');\n if (\n digest(graph.definitionDigest, '/resolvedPlan/graph/definitionDigest')\n !== definition.digest\n ) {\n fail('/resolvedPlan/graph/definitionDigest', 'Resolved plan belongs to another graph definition.');\n }\n if (graph.id !== definition.value.id) {\n fail('/resolvedPlan/graph/id', 'Resolved plan belongs to another graph id.');\n }\n if (graph.definitionVersion !== definition.value.definitionVersion) {\n fail('/resolvedPlan/graph/definitionVersion', 'Resolved plan belongs to another graph definition version.');\n }\n}\n\nfunction validateBinding(\n binding: RunStorageBinding,\n storedRecord?: RunStorageRecord,\n): {\n readonly record: RunStorageRecord;\n readonly knownSecrets: readonly string[];\n} {\n const bindingRecord = validateRunStorageRecord(binding.record);\n if (\n storedRecord !== undefined\n && !isDeepStrictEqual(bindingRecord, storedRecord)\n ) {\n fail('/storage', 'Stored and live storage records must match.');\n }\n const knownSecrets = json(binding.knownSecrets, '/binding/knownSecrets');\n if (\n !Array.isArray(knownSecrets)\n || knownSecrets.some((secret) => (\n typeof secret !== 'string' || secret.length === 0\n ))\n ) {\n fail('/binding/knownSecrets', 'Known secrets must be non-empty strings.');\n }\n const secrets = knownSecrets as readonly string[];\n const serializedRecord = canonicalJson(bindingRecord);\n if (secrets.some((secret) => (\n serializedRecord.includes(secret)\n || jsonValueContainsText(bindingRecord, secret)\n ))) {\n throw new StorageError(\n 'KNOWN_SECRET',\n 'Storage binding record contains a configured known secret.',\n );\n }\n return {\n record: bindingRecord,\n knownSecrets: secrets,\n };\n}\n\nfunction jsonValueContainsText(value: JsonValue, textValue: string): boolean {\n if (typeof value === 'string') return value.includes(textValue);\n if (value === null || typeof value !== 'object') return false;\n if (Array.isArray(value)) {\n return value.some((item) => jsonValueContainsText(item, textValue));\n }\n return Object.entries(value).some(([key, item]) => (\n key.includes(textValue) || jsonValueContainsText(item, textValue)\n ));\n}\n\nfunction expectedArtifactReference(\n artifact: NewArtifact,\n): ArtifactReference {\n return validateArtifactReference({\n schemaVersion: 1,\n digest: `sha256:${createHash('sha256').update(artifact.bytes).digest('hex')}`,\n byteLength: artifact.bytes.byteLength,\n mediaType: artifact.mediaType,\n purpose: artifact.purpose,\n });\n}\n\nfunction preflightArtifacts(\n artifacts: readonly NewArtifact[],\n policy: RunStoragePolicy,\n knownSecrets: readonly string[],\n): void {\n const uniqueBytes = new Map<string, number>();\n for (const artifact of artifacts) {\n if (artifact.bytes.byteLength > policy.maxArtifactBytes) {\n throw new StorageError(\n 'STORAGE_LIMIT_EXCEEDED',\n 'Run-start artifact exceeds maxArtifactBytes.',\n {\n purpose: artifact.purpose,\n byteLength: artifact.bytes.byteLength,\n maximumBytes: policy.maxArtifactBytes,\n },\n );\n }\n const bytes = Buffer.from(artifact.bytes);\n if (knownSecrets.some((secret) => bytes.includes(Buffer.from(secret, 'utf8')))) {\n throw new StorageError(\n 'KNOWN_SECRET',\n 'Exact run-start artifact contains a configured known secret.',\n { purpose: artifact.purpose },\n );\n }\n const reference = expectedArtifactReference(artifact);\n uniqueBytes.set(reference.digest, reference.byteLength);\n }\n const total = [...uniqueBytes.values()].reduce((sum, size) => sum + size, 0);\n if (total > policy.maxTotalArtifactBytesPerRun) {\n throw new StorageError(\n 'STORAGE_LIMIT_EXCEEDED',\n 'Run-start artifacts exceed maxTotalArtifactBytesPerRun.',\n { byteLength: total, maximumBytes: policy.maxTotalArtifactBytesPerRun },\n );\n }\n}\n\nfunction preflightRunStartEvent(\n event: NewRunStartedEvent,\n policy: RunStoragePolicy,\n knownSecrets: readonly string[],\n): void {\n const payloadByteLength = Buffer.byteLength(canonicalJson(event.payload), 'utf8');\n if (payloadByteLength > policy.maxEventPayloadBytes) {\n throw new StorageError(\n 'STORAGE_LIMIT_EXCEEDED',\n 'Run-start payload exceeds maxEventPayloadBytes.',\n { byteLength: payloadByteLength, maximumBytes: policy.maxEventPayloadBytes },\n );\n }\n const envelope = validateDomainEventEnvelope({\n ...event,\n envelopeVersion: 1,\n streamId: event.correlationId,\n revision: 1,\n });\n const serializedEvent = `${JSON.stringify(envelope)}\\n`;\n const appendByteLength = Buffer.byteLength(serializedEvent, 'utf8');\n if (appendByteLength > policy.maxAppendBatchBytes) {\n throw new StorageError(\n 'STORAGE_LIMIT_EXCEEDED',\n 'Run-start append exceeds maxAppendBatchBytes.',\n { byteLength: appendByteLength, maximumBytes: policy.maxAppendBatchBytes },\n );\n }\n const secret = findKnownSecretInEvents(\n knownSecrets,\n serializedEvent,\n [event],\n );\n if (secret !== undefined) {\n throw new StorageError(\n 'KNOWN_SECRET',\n 'Run-start event contains a configured known secret.',\n { eventId: event.eventId },\n );\n }\n}\n\nfunction artifactError(\n error: StorageError,\n record: RunStartRecord,\n referencePath: string,\n): StorageError {\n return new StorageError(error.code, error.message, {\n ...error.details,\n streamId: record.streamId,\n revision: record.revision,\n eventId: record.eventId,\n referencePath,\n });\n}\n\nfunction readPlanBytes(bytes: Uint8Array, referencePath: string): string {\n let textValue: string;\n try {\n textValue = new TextDecoder('utf-8', { fatal: true }).decode(bytes);\n } catch {\n fail(referencePath, 'Resolved plan bytes must be valid UTF-8.');\n }\n return textValue;\n}\n\nfunction parseStoredPlan(\n bytes: Uint8Array,\n digestValue: Sha256Digest,\n): ResolvedPlanSnapshot {\n const textValue = readPlanBytes(bytes, '/resolvedPlan');\n let parsed: unknown;\n try {\n parsed = JSON.parse(textValue);\n } catch {\n fail('/resolvedPlan', 'Resolved plan bytes must contain JSON.');\n }\n const plan = json(parsed, '/resolvedPlan');\n if (plan === null || typeof plan !== 'object' || Array.isArray(plan)) {\n fail('/resolvedPlan', 'Resolved plan must be an object.');\n }\n if (canonicalJson(plan) !== textValue) {\n fail('/resolvedPlan', 'Resolved plan bytes must use stable JSON encoding.');\n }\n if (digestJson(plan) !== digestValue) {\n fail('/resolvedPlan', 'Resolved plan digest does not match its bytes.');\n }\n const validatedPlan = storedResolvedPlan(plan, '/resolvedPlan');\n return Object.freeze({\n plan: validatedPlan,\n canonicalJson: textValue,\n digest: digestValue,\n });\n}\n\nexport async function persistRunDefinition(\n binding: RunStorageBinding,\n input: PersistRunDefinitionInput,\n): Promise<RunStartRecord> {\n const { record: storage, knownSecrets } = validateBinding(binding);\n const graphDefinition = validateCompiledDefinition(input.graphDefinition);\n const resolvedPlan = validateResolvedPlanSnapshot(input.resolvedPlan);\n validateResolvedPlanGraph(resolvedPlan.plan, graphDefinition);\n const resolvedInputs = record(\n input.resolvedInputs,\n '/resolvedInputs',\n 'Resolved inputs',\n ) as RunBrief;\n const workspaceBinding = input.workspaceBinding === null\n ? null\n : record(input.workspaceBinding, '/workspaceBinding', 'Workspace binding');\n const hostBinding = input.hostBinding === null\n ? null\n : Object.freeze({\n bytes: Uint8Array.from(input.hostBinding.bytes),\n mediaType: text(input.hostBinding.mediaType, '/hostBinding/mediaType', 'Host-binding media type'),\n });\n const runId = validateStorageId(input.runId, '/runId');\n const scope = { namespace: storage.namespace, runId };\n const resolvedPlanArtifact = validateNewArtifact({\n bytes: new TextEncoder().encode(resolvedPlan.canonicalJson),\n mediaType: 'application/json',\n purpose: 'resolved-plan',\n contentMode: 'state',\n });\n const hostBindingArtifact = hostBinding === null\n ? null\n : validateNewArtifact({\n bytes: hostBinding.bytes,\n mediaType: hostBinding.mediaType,\n purpose: 'host-binding',\n contentMode: 'exact',\n });\n preflightArtifacts(\n hostBindingArtifact === null\n ? [resolvedPlanArtifact]\n : [resolvedPlanArtifact, hostBindingArtifact],\n storage.policy,\n knownSecrets,\n );\n const artifacts: ArtifactBatch = hostBindingArtifact === null\n ? [resolvedPlanArtifact]\n : [resolvedPlanArtifact, hostBindingArtifact];\n const expectedPlanReference = expectedArtifactReference(\n resolvedPlanArtifact,\n ) as ArtifactReference<'resolved-plan'>;\n const expectedHostReference = hostBindingArtifact === null\n ? null\n : expectedArtifactReference(hostBindingArtifact) as ArtifactReference<'host-binding'>;\n const expectedReferences = expectedHostReference === null\n ? [expectedPlanReference]\n : [expectedPlanReference, expectedHostReference];\n const predictedReferences = await binding.artifactStore.preflightWrite(\n scope,\n artifacts,\n );\n if (!isDeepStrictEqual(predictedReferences, expectedReferences)) {\n throw new StorageError(\n 'ARTIFACT_INTEGRITY',\n 'Artifact store predicted the wrong run-start references.',\n { expected: expectedReferences, actual: predictedReferences },\n );\n }\n const definition = validateRunDefinition({\n schemaVersion: 1,\n runId,\n graphDefinition,\n resolvedPlan: expectedPlanReference,\n resolvedInputs,\n storage,\n workspaceBinding,\n hostBinding: expectedHostReference,\n });\n const event = validateNewDomainEvent({\n eventId: input.eventId,\n type: 'graph:run-started',\n version: 1,\n timestamp: input.timestamp,\n correlationId: runId,\n causationId: null,\n payload: { definition },\n }) as NewRunStartedEvent;\n preflightRunStartEvent(event, storage.policy, knownSecrets);\n await binding.eventStore.preflightAppend(\n { namespace: storage.namespace, streamId: runId },\n 0,\n [event],\n );\n\n const resolvedPlanReference = await binding.artifactStore.write(scope, {\n ...resolvedPlanArtifact,\n });\n if (!isDeepStrictEqual(resolvedPlanReference, expectedPlanReference)) {\n throw new StorageError(\n 'ARTIFACT_INTEGRITY',\n 'Resolved plan store returned the wrong reference.',\n { expected: expectedPlanReference, actual: resolvedPlanReference },\n );\n }\n\n const hostBindingReference = hostBindingArtifact === null\n ? null\n : await binding.artifactStore.write(scope, {\n ...hostBindingArtifact,\n });\n if (!isDeepStrictEqual(hostBindingReference, expectedHostReference)) {\n throw new StorageError(\n 'ARTIFACT_INTEGRITY',\n 'Host-binding store returned the wrong reference.',\n { expected: expectedHostReference, actual: hostBindingReference },\n );\n }\n const revision = await binding.eventStore.append(\n { namespace: storage.namespace, streamId: runId },\n 0,\n [event],\n );\n return validateRunStartRecord({\n envelopeVersion: 1,\n streamId: runId,\n revision,\n ...event,\n });\n}\n\nexport async function loadRunDefinition(\n binding: RunStorageBinding,\n runId: string,\n): Promise<LoadedRunDefinition> {\n const live = validateBinding(binding);\n const streamId = validateStorageId(runId, '/runId');\n let first: DomainEventEnvelope | undefined;\n for await (const event of binding.eventStore.read({\n namespace: live.record.namespace,\n streamId,\n })) {\n first = event;\n break;\n }\n if (!first) {\n fail('/runId', `Run \"${streamId}\" has no stored definition.`);\n }\n const record = validateRunStartRecord(first);\n validateBinding(binding, record.payload.definition.storage);\n const scope = { namespace: live.record.namespace, runId: streamId };\n\n let planBytes: Uint8Array;\n try {\n planBytes = await binding.artifactStore.read(\n scope,\n record.payload.definition.resolvedPlan,\n );\n } catch (error) {\n if (!(error instanceof StorageError)) throw error;\n throw artifactError(error, record, '/payload/definition/resolvedPlan');\n }\n const resolvedPlan = parseStoredPlan(\n planBytes,\n record.payload.definition.resolvedPlan.digest,\n );\n validateResolvedPlanGraph(\n resolvedPlan.plan,\n record.payload.definition.graphDefinition,\n );\n\n let hostBindingBytes: Uint8Array | null = null;\n const hostBinding = record.payload.definition.hostBinding;\n if (hostBinding !== null) {\n try {\n hostBindingBytes = await binding.artifactStore.read(scope, hostBinding);\n } catch (error) {\n if (!(error instanceof StorageError)) throw error;\n throw artifactError(error, record, '/payload/definition/hostBinding');\n }\n }\n\n return Object.freeze({\n record,\n resolvedPlan,\n hostBindingBytes,\n });\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `defineAgentFromMarkdown(path)` — load a Claude Code agent `.md` file into an
|
|
3
|
+
* `AgentDef`. The markdown body becomes `system`; the frontmatter maps onto the
|
|
4
|
+
* def's structured fields. The runtime has no yaml dependency, so the frontmatter
|
|
5
|
+
* parser is hand-rolled and deliberately scoped. The FULL supported grammar:
|
|
6
|
+
*
|
|
7
|
+
* - Frontmatter is an optional leading block delimited by `---` lines (the
|
|
8
|
+
* first line of the file opens it; the closing `---` must be unindented,
|
|
9
|
+
* so an indented `---` inside a block scalar stays block content). No
|
|
10
|
+
* closing `---` ⇒ no frontmatter; the whole file is the body.
|
|
11
|
+
* - Scalar: `key: value`. Surrounding single/double quotes are stripped.
|
|
12
|
+
* - Block scalar: `key: |` or `key: >`, then deeper-indented lines, each
|
|
13
|
+
* trimmed. The block ends at the first non-indented, non-blank line; blank
|
|
14
|
+
* lines inside it (followed by more indented content) are kept by `|` as
|
|
15
|
+
* paragraph breaks and folded away by `>`. `|` joins the lines with
|
|
16
|
+
* newlines; `>` folds them with spaces (YAML's basic folding, nothing
|
|
17
|
+
* more).
|
|
18
|
+
* - Sequence: `key:` alone, then `- item` lines (quotes stripped per item).
|
|
19
|
+
* - `tools` specifically: a scalar value is ALSO split on commas into a list
|
|
20
|
+
* (`tools: Read, Grep, Bash` ⇒ `['Read', 'Grep', 'Bash']`).
|
|
21
|
+
*
|
|
22
|
+
* Anything else (nested maps, anchors, multi-line flow, comments…) is out of
|
|
23
|
+
* scope BY DESIGN, not a bug: Claude Code agent files use exactly this shape.
|
|
24
|
+
* Unknown frontmatter keys are ignored (forward compat with CC's evolving
|
|
25
|
+
* frontmatter), not errors.
|
|
26
|
+
*/
|
|
27
|
+
import { type AgentDef } from './agent.js';
|
|
28
|
+
/**
|
|
29
|
+
* Load a Claude Code agent `.md` into an `AgentDef` (validated through
|
|
30
|
+
* `defineAgent`). The body is the `system`; `name` falls back to the file's
|
|
31
|
+
* basename; `model: inherit`/`default` map to undefined (inherit the run
|
|
32
|
+
* default). The result is always a leaf — a markdown-loaded agent has no
|
|
33
|
+
* sub-agents by construction, and dropping Task/Agent from `tools` is the
|
|
34
|
+
* same statement. `overrides` spread last, so the caller wins.
|
|
35
|
+
*/
|
|
36
|
+
export declare function defineAgentFromMarkdown(path: string | URL, overrides?: Partial<AgentDef>): AgentDef;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentDef — a reusable, job-specific agent definition. The persona and methodology
|
|
3
|
+
* (the prose: `system`, skill `instructions`) live in editable markdown files; the
|
|
4
|
+
* structure and types live here in TypeScript. The `.ts` is the strongly-typed wrapper
|
|
5
|
+
* around the `.md` — author the prompt as markdown, get type safety and validation in code.
|
|
6
|
+
*
|
|
7
|
+
* Adapted from an application-specific agent profile without duplicating the orchestration
|
|
8
|
+
* that the runtime already provides: `dag` is the dispatcher, `conditions`/`quorum` are the
|
|
9
|
+
* gates, and `Outcome` is the result channel. AgentDef captures who the agent is, what it
|
|
10
|
+
* may touch, and how it works; `agentJob` resolves that contract into an engine request.
|
|
11
|
+
*/
|
|
12
|
+
/** A skill is a METHODOLOGY (how to do the work — TDD, writing-plans), not a worker.
|
|
13
|
+
* An agent composes skills; a skill never dispatches an agent. */
|
|
14
|
+
export interface Skill {
|
|
15
|
+
name: string;
|
|
16
|
+
/** The methodology instructions — prepended to the agent's system when it applies them. */
|
|
17
|
+
instructions: string;
|
|
18
|
+
}
|
|
19
|
+
export type AgentTier = 'worker' | 'reviewer' | 'lead' | 'specialist' | 'utility' | (string & {});
|
|
20
|
+
export type AgentSkillRef = string | Skill;
|
|
21
|
+
export interface AgentOutputContract {
|
|
22
|
+
/** Stable output name, such as `patch`, `review`, or `test-report`. */
|
|
23
|
+
name: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
/** Optional structured schema owned by the loop author. The runtime stores it, it does not interpret it. */
|
|
26
|
+
schema?: unknown;
|
|
27
|
+
}
|
|
28
|
+
export interface AgentFailureMode {
|
|
29
|
+
mode: string;
|
|
30
|
+
recovery: string;
|
|
31
|
+
detection?: string;
|
|
32
|
+
severity?: 'block' | 'should-fix' | 'nice-to-have' | (string & {});
|
|
33
|
+
}
|
|
34
|
+
export interface AgentDef {
|
|
35
|
+
/** Identity (also the default job label). */
|
|
36
|
+
name: string;
|
|
37
|
+
/** What and why — for humans, docs, and package discovery. */
|
|
38
|
+
description?: string;
|
|
39
|
+
/** The system prompt: who the agent is and how it works. Use `fromFile('x.md')`. */
|
|
40
|
+
system: string;
|
|
41
|
+
/** Model id; omitted = inherit the run default. */
|
|
42
|
+
model?: string;
|
|
43
|
+
/** Allowed tool names — the permission boundary. */
|
|
44
|
+
tools?: string[];
|
|
45
|
+
/**
|
|
46
|
+
* Mark this agent a leaf: it may not spawn sub-agents / fan out (the engine disallows
|
|
47
|
+
* the sub-agent tool). Use it to control where a branch of the graph bottoms out — to
|
|
48
|
+
* stop a thorough agent from quietly expanding into a slow, expensive swarm.
|
|
49
|
+
*/
|
|
50
|
+
leaf?: boolean;
|
|
51
|
+
/** Contract tier for humans, describe output, and future discovery. No scheduling authority. */
|
|
52
|
+
tier?: AgentTier;
|
|
53
|
+
/** Structured job descriptions (not prose) — for discovery / docs. */
|
|
54
|
+
capabilities?: string[];
|
|
55
|
+
/** Structured outputs this agent is expected to produce. */
|
|
56
|
+
outputs?: AgentOutputContract[];
|
|
57
|
+
/** Methodologies the agent applies; their instructions are folded into the system. */
|
|
58
|
+
skills?: Skill[];
|
|
59
|
+
/** Skills the caller should supply before the turn. Metadata only unless also listed in `skills`. */
|
|
60
|
+
requiresSkills?: AgentSkillRef[];
|
|
61
|
+
/** Skills the agent is known to use. Metadata only unless also listed in `skills`. */
|
|
62
|
+
usesSkills?: AgentSkillRef[];
|
|
63
|
+
/** Named failure modes + their recovery — first-class contracts, not buried prose. */
|
|
64
|
+
failureModes?: AgentFailureMode[];
|
|
65
|
+
}
|
|
66
|
+
export interface AgentContractSummary {
|
|
67
|
+
tier?: string;
|
|
68
|
+
capabilities?: string[];
|
|
69
|
+
outputs?: string[];
|
|
70
|
+
requiresSkills?: string[];
|
|
71
|
+
usesSkills?: string[];
|
|
72
|
+
failureModes?: string[];
|
|
73
|
+
}
|
|
74
|
+
/** Read a markdown file as a string — for `system` or skill `instructions`. Pass an
|
|
75
|
+
* absolute path, or `new URL('./x.md', import.meta.url)` for a path relative to the file. */
|
|
76
|
+
export declare function fromFile(path: string | URL): string;
|
|
77
|
+
/** Define a skill (a methodology). Identity + validation; strongly typed. */
|
|
78
|
+
export declare function defineSkill(skill: Skill): Skill;
|
|
79
|
+
/** Define an agent. Identity + validation; strongly typed (the wrapper around the md). */
|
|
80
|
+
export declare function defineAgent(def: AgentDef): AgentDef;
|
|
81
|
+
export declare function agentContract(agent: AgentDef | undefined): AgentContractSummary | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* Resolve an agent's system prompt, folding in its skills' methodologies. This is what
|
|
84
|
+
* `agentJob` hands the engine as `system`.
|
|
85
|
+
*/
|
|
86
|
+
export declare function resolveSystem(agent: AgentDef): string;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A person's decision as a job. The step asks one question through the run's
|
|
3
|
+
* callbacks client and does one of three things: passes when the answer is
|
|
4
|
+
* yes, sends the work back (or fails) with the person's note when it is no,
|
|
5
|
+
* and pauses the run with the request pending when nobody has answered yet.
|
|
6
|
+
* A run started again with the same client, or the stored client over the
|
|
7
|
+
* same store, finds the answer and carries on.
|
|
8
|
+
*/
|
|
9
|
+
import { type CallbackRequest } from '../callback/gate.js';
|
|
10
|
+
import type { JsonValue } from '../graph/value.js';
|
|
11
|
+
import type { Job, JobContext, Outcome } from './types.js';
|
|
12
|
+
/** Bind only the actual node job; a condition receives an unbound context. */
|
|
13
|
+
export declare function nodeJobContext(ctx: JobContext, job: Job | undefined): JobContext;
|
|
14
|
+
/** A transparent wrapper may delegate only when it is itself the node's job. */
|
|
15
|
+
export declare function delegateNodeJob(ctx: JobContext, owner: Job, job: Job, child: JobContext): Promise<Outcome>;
|
|
16
|
+
/** The answer a person gives: yes or no, and a note when there is one. */
|
|
17
|
+
export interface ApprovalAnswer {
|
|
18
|
+
readonly approved: boolean;
|
|
19
|
+
readonly note?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ApprovalOptions {
|
|
22
|
+
/** The question, in plain words. */
|
|
23
|
+
question: string;
|
|
24
|
+
/**
|
|
25
|
+
* What the question is about. Default: the outcomes this step depends on
|
|
26
|
+
* (their summaries), so a change upstream after a kickback is a new
|
|
27
|
+
* question, and the same question about the same thing is the same request.
|
|
28
|
+
*/
|
|
29
|
+
input?: JsonValue;
|
|
30
|
+
/**
|
|
31
|
+
* The node that owns the fix when the person says no: the note goes back
|
|
32
|
+
* there as the finding. Without it a refusal fails the step with the note
|
|
33
|
+
* as its summary.
|
|
34
|
+
*/
|
|
35
|
+
target?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Answer in-process (a test, an example, a script that decides). Without
|
|
38
|
+
* it the step pauses the run with the request pending on the run's
|
|
39
|
+
* callbacks client until a router answers it there.
|
|
40
|
+
*/
|
|
41
|
+
answer?: (request: CallbackRequest) => ApprovalAnswer | Promise<ApprovalAnswer>;
|
|
42
|
+
}
|
|
43
|
+
export declare function approval(label: string, opts: ApprovalOptions): Job;
|