@xemahq/dsl 0.1.1
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 +201 -0
- package/dist/deliverable-spec/index.d.ts +3 -0
- package/dist/deliverable-spec/index.d.ts.map +1 -0
- package/dist/deliverable-spec/index.js +19 -0
- package/dist/deliverable-spec/index.js.map +1 -0
- package/dist/deliverable-spec/lib/schema.d.ts +151 -0
- package/dist/deliverable-spec/lib/schema.d.ts.map +1 -0
- package/dist/deliverable-spec/lib/schema.js +139 -0
- package/dist/deliverable-spec/lib/schema.js.map +1 -0
- package/dist/deliverable-spec/lib/types.d.ts +8 -0
- package/dist/deliverable-spec/lib/types.d.ts.map +1 -0
- package/dist/deliverable-spec/lib/types.js +3 -0
- package/dist/deliverable-spec/lib/types.js.map +1 -0
- package/dist/payload-codec/index.d.ts +8 -0
- package/dist/payload-codec/index.d.ts.map +1 -0
- package/dist/payload-codec/index.js +27 -0
- package/dist/payload-codec/index.js.map +1 -0
- package/dist/payload-codec/lib/blob-store.d.ts +37 -0
- package/dist/payload-codec/lib/blob-store.d.ts.map +1 -0
- package/dist/payload-codec/lib/blob-store.js +0 -0
- package/dist/payload-codec/lib/blob-store.js.map +1 -0
- package/dist/payload-codec/lib/codec-context.d.ts +6 -0
- package/dist/payload-codec/lib/codec-context.d.ts.map +1 -0
- package/dist/payload-codec/lib/codec-context.js +16 -0
- package/dist/payload-codec/lib/codec-context.js.map +1 -0
- package/dist/payload-codec/lib/codec.d.ts +51 -0
- package/dist/payload-codec/lib/codec.d.ts.map +1 -0
- package/dist/payload-codec/lib/codec.js +330 -0
- package/dist/payload-codec/lib/codec.js.map +1 -0
- package/dist/payload-codec/lib/enums.d.ts +18 -0
- package/dist/payload-codec/lib/enums.d.ts.map +1 -0
- package/dist/payload-codec/lib/enums.js +23 -0
- package/dist/payload-codec/lib/enums.js.map +1 -0
- package/dist/payload-codec/lib/errors.d.ts +18 -0
- package/dist/payload-codec/lib/errors.d.ts.map +1 -0
- package/dist/payload-codec/lib/errors.js +39 -0
- package/dist/payload-codec/lib/errors.js.map +1 -0
- package/dist/payload-codec/lib/http-blob-store.d.ts +21 -0
- package/dist/payload-codec/lib/http-blob-store.d.ts.map +1 -0
- package/dist/payload-codec/lib/http-blob-store.js +139 -0
- package/dist/payload-codec/lib/http-blob-store.js.map +1 -0
- package/dist/payload-codec/lib/lru-cache.d.ts +12 -0
- package/dist/payload-codec/lib/lru-cache.d.ts.map +1 -0
- package/dist/payload-codec/lib/lru-cache.js +59 -0
- package/dist/payload-codec/lib/lru-cache.js.map +1 -0
- package/dist/schema/action.schema.json +181 -0
- package/dist/schema/reusable-workflow.schema.json +46 -0
- package/dist/schema/workflow.schema.json +373 -0
- package/dist/workflow/index.d.ts +14 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +49 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflow/lib/action-input-validator.d.ts +10 -0
- package/dist/workflow/lib/action-input-validator.d.ts.map +1 -0
- package/dist/workflow/lib/action-input-validator.js +69 -0
- package/dist/workflow/lib/action-input-validator.js.map +1 -0
- package/dist/workflow/lib/compiler/action-shape.d.ts +5 -0
- package/dist/workflow/lib/compiler/action-shape.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/action-shape.js +43 -0
- package/dist/workflow/lib/compiler/action-shape.js.map +1 -0
- package/dist/workflow/lib/compiler/canonical-json.d.ts +3 -0
- package/dist/workflow/lib/compiler/canonical-json.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/canonical-json.js +45 -0
- package/dist/workflow/lib/compiler/canonical-json.js.map +1 -0
- package/dist/workflow/lib/compiler/compile.d.ts +4 -0
- package/dist/workflow/lib/compiler/compile.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/compile.js +794 -0
- package/dist/workflow/lib/compiler/compile.js.map +1 -0
- package/dist/workflow/lib/compiler/concurrency.d.ts +5 -0
- package/dist/workflow/lib/compiler/concurrency.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/concurrency.js +104 -0
- package/dist/workflow/lib/compiler/concurrency.js.map +1 -0
- package/dist/workflow/lib/compiler/dag.d.ts +10 -0
- package/dist/workflow/lib/compiler/dag.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/dag.js +74 -0
- package/dist/workflow/lib/compiler/dag.js.map +1 -0
- package/dist/workflow/lib/compiler/index.d.ts +6 -0
- package/dist/workflow/lib/compiler/index.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/index.js +14 -0
- package/dist/workflow/lib/compiler/index.js.map +1 -0
- package/dist/workflow/lib/compiler/inputs.d.ts +4 -0
- package/dist/workflow/lib/compiler/inputs.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/inputs.js +108 -0
- package/dist/workflow/lib/compiler/inputs.js.map +1 -0
- package/dist/workflow/lib/compiler/installation-resource-validator.d.ts +9 -0
- package/dist/workflow/lib/compiler/installation-resource-validator.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/installation-resource-validator.js +76 -0
- package/dist/workflow/lib/compiler/installation-resource-validator.js.map +1 -0
- package/dist/workflow/lib/compiler/manifest-source.d.ts +4 -0
- package/dist/workflow/lib/compiler/manifest-source.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/manifest-source.js +100 -0
- package/dist/workflow/lib/compiler/manifest-source.js.map +1 -0
- package/dist/workflow/lib/compiler/matrix.d.ts +4 -0
- package/dist/workflow/lib/compiler/matrix.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/matrix.js +76 -0
- package/dist/workflow/lib/compiler/matrix.js.map +1 -0
- package/dist/workflow/lib/compiler/mount-plan.d.ts +4 -0
- package/dist/workflow/lib/compiler/mount-plan.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/mount-plan.js +96 -0
- package/dist/workflow/lib/compiler/mount-plan.js.map +1 -0
- package/dist/workflow/lib/compiler/payload-reach-in.d.ts +14 -0
- package/dist/workflow/lib/compiler/payload-reach-in.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/payload-reach-in.js +273 -0
- package/dist/workflow/lib/compiler/payload-reach-in.js.map +1 -0
- package/dist/workflow/lib/compiler/permissions.d.ts +6 -0
- package/dist/workflow/lib/compiler/permissions.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/permissions.js +43 -0
- package/dist/workflow/lib/compiler/permissions.js.map +1 -0
- package/dist/workflow/lib/compiler/retry-timeout.d.ts +6 -0
- package/dist/workflow/lib/compiler/retry-timeout.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/retry-timeout.js +64 -0
- package/dist/workflow/lib/compiler/retry-timeout.js.map +1 -0
- package/dist/workflow/lib/compiler/review-step.d.ts +18 -0
- package/dist/workflow/lib/compiler/review-step.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/review-step.js +247 -0
- package/dist/workflow/lib/compiler/review-step.js.map +1 -0
- package/dist/workflow/lib/compiler/types.d.ts +42 -0
- package/dist/workflow/lib/compiler/types.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/types.js +3 -0
- package/dist/workflow/lib/compiler/types.js.map +1 -0
- package/dist/workflow/lib/compiler/variable-requirements.d.ts +5 -0
- package/dist/workflow/lib/compiler/variable-requirements.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/variable-requirements.js +119 -0
- package/dist/workflow/lib/compiler/variable-requirements.js.map +1 -0
- package/dist/workflow/lib/deliverable-spec-keys.d.ts +3 -0
- package/dist/workflow/lib/deliverable-spec-keys.d.ts.map +1 -0
- package/dist/workflow/lib/deliverable-spec-keys.js +90 -0
- package/dist/workflow/lib/deliverable-spec-keys.js.map +1 -0
- package/dist/workflow/lib/dispatch-inputs/index.d.ts +23 -0
- package/dist/workflow/lib/dispatch-inputs/index.d.ts.map +1 -0
- package/dist/workflow/lib/dispatch-inputs/index.js +106 -0
- package/dist/workflow/lib/dispatch-inputs/index.js.map +1 -0
- package/dist/workflow/lib/dispatch-inputs/to-json-schema.d.ts +3 -0
- package/dist/workflow/lib/dispatch-inputs/to-json-schema.d.ts.map +1 -0
- package/dist/workflow/lib/dispatch-inputs/to-json-schema.js +43 -0
- package/dist/workflow/lib/dispatch-inputs/to-json-schema.js.map +1 -0
- package/dist/workflow/lib/duration.d.ts +2 -0
- package/dist/workflow/lib/duration.d.ts.map +1 -0
- package/dist/workflow/lib/duration.js +26 -0
- package/dist/workflow/lib/duration.js.map +1 -0
- package/dist/workflow/lib/errors.d.ts +9 -0
- package/dist/workflow/lib/errors.d.ts.map +1 -0
- package/dist/workflow/lib/errors.js +28 -0
- package/dist/workflow/lib/errors.js.map +1 -0
- package/dist/workflow/lib/expression/ast.d.ts +61 -0
- package/dist/workflow/lib/expression/ast.d.ts.map +1 -0
- package/dist/workflow/lib/expression/ast.js +34 -0
- package/dist/workflow/lib/expression/ast.js.map +1 -0
- package/dist/workflow/lib/expression/context.d.ts +63 -0
- package/dist/workflow/lib/expression/context.d.ts.map +1 -0
- package/dist/workflow/lib/expression/context.js +32 -0
- package/dist/workflow/lib/expression/context.js.map +1 -0
- package/dist/workflow/lib/expression/evaluator.d.ts +5 -0
- package/dist/workflow/lib/expression/evaluator.d.ts.map +1 -0
- package/dist/workflow/lib/expression/evaluator.js +291 -0
- package/dist/workflow/lib/expression/evaluator.js.map +1 -0
- package/dist/workflow/lib/expression/index.d.ts +9 -0
- package/dist/workflow/lib/expression/index.d.ts.map +1 -0
- package/dist/workflow/lib/expression/index.js +26 -0
- package/dist/workflow/lib/expression/index.js.map +1 -0
- package/dist/workflow/lib/expression/interpolation.d.ts +9 -0
- package/dist/workflow/lib/expression/interpolation.d.ts.map +1 -0
- package/dist/workflow/lib/expression/interpolation.js +51 -0
- package/dist/workflow/lib/expression/interpolation.js.map +1 -0
- package/dist/workflow/lib/expression/parser.d.ts +4 -0
- package/dist/workflow/lib/expression/parser.d.ts.map +1 -0
- package/dist/workflow/lib/expression/parser.js +203 -0
- package/dist/workflow/lib/expression/parser.js.map +1 -0
- package/dist/workflow/lib/expression/template.d.ts +18 -0
- package/dist/workflow/lib/expression/template.d.ts.map +1 -0
- package/dist/workflow/lib/expression/template.js +63 -0
- package/dist/workflow/lib/expression/template.js.map +1 -0
- package/dist/workflow/lib/expression/tokenizer.d.ts +3 -0
- package/dist/workflow/lib/expression/tokenizer.d.ts.map +1 -0
- package/dist/workflow/lib/expression/tokenizer.js +153 -0
- package/dist/workflow/lib/expression/tokenizer.js.map +1 -0
- package/dist/workflow/lib/expression/tokens.d.ts +25 -0
- package/dist/workflow/lib/expression/tokens.d.ts.map +1 -0
- package/dist/workflow/lib/expression/tokens.js +24 -0
- package/dist/workflow/lib/expression/tokens.js.map +1 -0
- package/dist/workflow/lib/expression/walk-artifact-refs.d.ts +5 -0
- package/dist/workflow/lib/expression/walk-artifact-refs.d.ts.map +1 -0
- package/dist/workflow/lib/expression/walk-artifact-refs.js +138 -0
- package/dist/workflow/lib/expression/walk-artifact-refs.js.map +1 -0
- package/dist/workflow/lib/installation-resource-kind.d.ts +14 -0
- package/dist/workflow/lib/installation-resource-kind.d.ts.map +1 -0
- package/dist/workflow/lib/installation-resource-kind.js +59 -0
- package/dist/workflow/lib/installation-resource-kind.js.map +1 -0
- package/dist/workflow/lib/schemas-loader.d.ts +4 -0
- package/dist/workflow/lib/schemas-loader.d.ts.map +1 -0
- package/dist/workflow/lib/schemas-loader.js +36 -0
- package/dist/workflow/lib/schemas-loader.js.map +1 -0
- package/dist/workflow/lib/serializer.d.ts +3 -0
- package/dist/workflow/lib/serializer.d.ts.map +1 -0
- package/dist/workflow/lib/serializer.js +15 -0
- package/dist/workflow/lib/serializer.js.map +1 -0
- package/dist/workflow/lib/types.d.ts +179 -0
- package/dist/workflow/lib/types.d.ts.map +1 -0
- package/dist/workflow/lib/types.js +3 -0
- package/dist/workflow/lib/types.js.map +1 -0
- package/dist/workflow/lib/validate.d.ts +8 -0
- package/dist/workflow/lib/validate.d.ts.map +1 -0
- package/dist/workflow/lib/validate.js +119 -0
- package/dist/workflow/lib/validate.js.map +1 -0
- package/dist/workspace-manifest/index.d.ts +6 -0
- package/dist/workspace-manifest/index.d.ts.map +1 -0
- package/dist/workspace-manifest/index.js +22 -0
- package/dist/workspace-manifest/index.js.map +1 -0
- package/dist/workspace-manifest/lib/compile.d.ts +8 -0
- package/dist/workspace-manifest/lib/compile.d.ts.map +1 -0
- package/dist/workspace-manifest/lib/compile.js +439 -0
- package/dist/workspace-manifest/lib/compile.js.map +1 -0
- package/dist/workspace-manifest/lib/interpolate.d.ts +12 -0
- package/dist/workspace-manifest/lib/interpolate.d.ts.map +1 -0
- package/dist/workspace-manifest/lib/interpolate.js +81 -0
- package/dist/workspace-manifest/lib/interpolate.js.map +1 -0
- package/dist/workspace-manifest/lib/resolve-extends.d.ts +10 -0
- package/dist/workspace-manifest/lib/resolve-extends.d.ts.map +1 -0
- package/dist/workspace-manifest/lib/resolve-extends.js +108 -0
- package/dist/workspace-manifest/lib/resolve-extends.js.map +1 -0
- package/dist/workspace-manifest/lib/schema.d.ts +710 -0
- package/dist/workspace-manifest/lib/schema.d.ts.map +1 -0
- package/dist/workspace-manifest/lib/schema.js +355 -0
- package/dist/workspace-manifest/lib/schema.js.map +1 -0
- package/dist/workspace-manifest/lib/types.d.ts +153 -0
- package/dist/workspace-manifest/lib/types.d.ts.map +1 -0
- package/dist/workspace-manifest/lib/types.js +10 -0
- package/dist/workspace-manifest/lib/types.js.map +1 -0
- package/package.json +79 -0
- package/schema/action.schema.json +181 -0
- package/schema/reusable-workflow.schema.json +46 -0
- package/schema/workflow.schema.json +373 -0
- package/src/deliverable-spec/index.ts +19 -0
- package/src/deliverable-spec/lib/schema.ts +248 -0
- package/src/deliverable-spec/lib/types.ts +26 -0
- package/src/payload-codec/index.ts +40 -0
- package/src/payload-codec/lib/blob-store.ts +0 -0
- package/src/payload-codec/lib/codec-context.ts +38 -0
- package/src/payload-codec/lib/codec.ts +593 -0
- package/src/payload-codec/lib/enums.ts +58 -0
- package/src/payload-codec/lib/errors.ts +54 -0
- package/src/payload-codec/lib/http-blob-store.ts +257 -0
- package/src/payload-codec/lib/lru-cache.ts +81 -0
- package/src/workflow/index.ts +98 -0
- package/src/workflow/lib/action-input-validator.ts +160 -0
- package/src/workflow/lib/compiler/action-shape.ts +71 -0
- package/src/workflow/lib/compiler/canonical-json.ts +53 -0
- package/src/workflow/lib/compiler/compile.ts +1518 -0
- package/src/workflow/lib/compiler/concurrency.ts +223 -0
- package/src/workflow/lib/compiler/dag.ts +108 -0
- package/src/workflow/lib/compiler/index.ts +10 -0
- package/src/workflow/lib/compiler/inputs.ts +199 -0
- package/src/workflow/lib/compiler/installation-resource-validator.ts +114 -0
- package/src/workflow/lib/compiler/manifest-source.ts +176 -0
- package/src/workflow/lib/compiler/matrix.ts +135 -0
- package/src/workflow/lib/compiler/mount-plan.ts +202 -0
- package/src/workflow/lib/compiler/payload-reach-in.ts +497 -0
- package/src/workflow/lib/compiler/permissions.ts +64 -0
- package/src/workflow/lib/compiler/retry-timeout.ts +105 -0
- package/src/workflow/lib/compiler/review-step.ts +517 -0
- package/src/workflow/lib/compiler/types.ts +170 -0
- package/src/workflow/lib/compiler/variable-requirements.ts +208 -0
- package/src/workflow/lib/deliverable-spec-keys.ts +109 -0
- package/src/workflow/lib/dispatch-inputs/index.ts +160 -0
- package/src/workflow/lib/dispatch-inputs/to-json-schema.ts +60 -0
- package/src/workflow/lib/duration.ts +48 -0
- package/src/workflow/lib/errors.ts +37 -0
- package/src/workflow/lib/expression/ast.ts +108 -0
- package/src/workflow/lib/expression/context.ts +148 -0
- package/src/workflow/lib/expression/evaluator.ts +492 -0
- package/src/workflow/lib/expression/index.ts +28 -0
- package/src/workflow/lib/expression/interpolation.ts +84 -0
- package/src/workflow/lib/expression/parser.ts +264 -0
- package/src/workflow/lib/expression/template.ts +117 -0
- package/src/workflow/lib/expression/tokenizer.ts +200 -0
- package/src/workflow/lib/expression/tokens.ts +30 -0
- package/src/workflow/lib/expression/walk-artifact-refs.ts +232 -0
- package/src/workflow/lib/installation-resource-kind.ts +107 -0
- package/src/workflow/lib/schemas-loader.ts +64 -0
- package/src/workflow/lib/serializer.ts +30 -0
- package/src/workflow/lib/types.ts +361 -0
- package/src/workflow/lib/validate.ts +199 -0
- package/src/workspace-manifest/index.ts +27 -0
- package/src/workspace-manifest/lib/compile.ts +608 -0
- package/src/workspace-manifest/lib/interpolate.ts +140 -0
- package/src/workspace-manifest/lib/resolve-extends.ts +260 -0
- package/src/workspace-manifest/lib/schema.ts +612 -0
- package/src/workspace-manifest/lib/types.ts +392 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WorkflowErrorCode,
|
|
3
|
+
isAgentRunRole,
|
|
4
|
+
type CompiledManifestSource,
|
|
5
|
+
} from '@xemahq/kernel-contracts/workflow';
|
|
6
|
+
import { compileManifest } from '../../../workspace-manifest';
|
|
7
|
+
|
|
8
|
+
import { WorkflowDslError } from '../errors';
|
|
9
|
+
import { ANY_INTERPOLATION_RE } from '../expression/interpolation';
|
|
10
|
+
import type { ActionManifest } from '../types';
|
|
11
|
+
import { isAgentShapedAction } from './action-shape';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* `metadata.version` on the synthesized envelope must satisfy the
|
|
15
|
+
* workspace-manifest-dsl semver regex. We use a static placeholder
|
|
16
|
+
* since the inline manifest never enters the manifests-api catalog —
|
|
17
|
+
* it lives in-process for the lifetime of the snapshot.
|
|
18
|
+
*/
|
|
19
|
+
const INLINE_MANIFEST_VERSION = '0.0.0';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Compute the compile-time `manifestSource` discriminator for one job.
|
|
23
|
+
*
|
|
24
|
+
* Three terminal shapes:
|
|
25
|
+
* • `null` — non-agent action (the action manifest doesn't expose
|
|
26
|
+
* `compositionRef` + `mounts` on its `inputs:` schema).
|
|
27
|
+
* • `{ kind: 'ref', ref }` — long-form ref is a literal string. The
|
|
28
|
+
* activity resolves the Agent Composition it names at boot.
|
|
29
|
+
* • `{ kind: 'inline', compiled }` — short-form mounts and every
|
|
30
|
+
* value the inline manifest depends on (`agentSlug`, `phaseKey`
|
|
31
|
+
* (YAML field — mapped onto kernel `groupKey`), `deliverableSpecRef`,
|
|
32
|
+
* every entry of `mounts`) are YAML literals.
|
|
33
|
+
* We synthesize a `WorkspaceManifest` envelope and feed it through
|
|
34
|
+
* `compileManifest()` once at workflow compile so the worker skips
|
|
35
|
+
* both the DB round-trip and the runtime synthesis.
|
|
36
|
+
* • `{ kind: 'inline-deferred' }` — at least one of the inputs above
|
|
37
|
+
* is a `${{ … }}` expression that must be evaluated per dispatch.
|
|
38
|
+
* The worker keeps today's runtime-synthesis path for these.
|
|
39
|
+
*
|
|
40
|
+
* The `oneOf` invariant (exactly one of `compositionRef` / `mounts`) is
|
|
41
|
+
* enforced upstream by `enforceManifestSourceDiscriminator` in
|
|
42
|
+
* `mount-plan.ts`. By the time we get here that pair is already known
|
|
43
|
+
* to be well-formed, so this function only branches between
|
|
44
|
+
* literal-vs-expression.
|
|
45
|
+
*/
|
|
46
|
+
export function compileManifestSource(
|
|
47
|
+
jobKey: string,
|
|
48
|
+
withBlock: Readonly<Record<string, unknown>> | undefined,
|
|
49
|
+
actionManifest: ActionManifest | null,
|
|
50
|
+
): CompiledManifestSource | null {
|
|
51
|
+
if (!isAgentShapedAction(actionManifest)) return null;
|
|
52
|
+
const block = withBlock ?? {};
|
|
53
|
+
|
|
54
|
+
const ref = block['compositionRef'];
|
|
55
|
+
if (typeof ref === 'string' && ref.length > 0) {
|
|
56
|
+
if (isLiteralString(ref)) {
|
|
57
|
+
return Object.freeze({ kind: 'ref' as const, ref });
|
|
58
|
+
}
|
|
59
|
+
return Object.freeze({ kind: 'inline-deferred' as const });
|
|
60
|
+
}
|
|
61
|
+
if (ref !== undefined) {
|
|
62
|
+
// ref is present but not a literal string → expression.
|
|
63
|
+
return Object.freeze({ kind: 'inline-deferred' as const });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Mounts path. Caller has already asserted `mounts` is set.
|
|
67
|
+
const mounts = block['mounts'];
|
|
68
|
+
if (containsExpressions(mounts)) {
|
|
69
|
+
return Object.freeze({ kind: 'inline-deferred' as const });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const agentSlug = block['agentSlug'];
|
|
73
|
+
const phaseKey = block['phaseKey'];
|
|
74
|
+
const deliverableSpecRef = block['deliverableSpecRef'];
|
|
75
|
+
const roleRaw = block['role'];
|
|
76
|
+
if (
|
|
77
|
+
!isLiteralString(agentSlug) ||
|
|
78
|
+
!isLiteralStringOrUndefined(phaseKey) ||
|
|
79
|
+
!isLiteralStringOrNullOrUndefined(deliverableSpecRef) ||
|
|
80
|
+
!isLiteralStringOrUndefined(roleRaw)
|
|
81
|
+
) {
|
|
82
|
+
return Object.freeze({ kind: 'inline-deferred' as const });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (typeof roleRaw !== 'string' || !isAgentRunRole(roleRaw)) {
|
|
86
|
+
throw new WorkflowDslError(
|
|
87
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
88
|
+
`Job '${jobKey}' uses inline 'mounts:' but its 'with.role' is missing or not a canonical AgentRunRole. ` +
|
|
89
|
+
`Declare 'role' explicitly (e.g. 'unit-worker', 'coordinator', 'gate-reviewer'); the action schema enforces this at compile time.`,
|
|
90
|
+
{ jobKey, actionId: actionManifest.metadata.id },
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
const role = roleRaw;
|
|
94
|
+
const envelope = synthesizeInlineManifestEnvelope({
|
|
95
|
+
agentSlug,
|
|
96
|
+
phaseKey: typeof phaseKey === 'string' ? phaseKey : null,
|
|
97
|
+
role,
|
|
98
|
+
deliverableSpecRef:
|
|
99
|
+
typeof deliverableSpecRef === 'string' ? deliverableSpecRef : null,
|
|
100
|
+
mounts: mounts as Readonly<Record<string, unknown>>,
|
|
101
|
+
});
|
|
102
|
+
const result = compileManifest(envelope, {});
|
|
103
|
+
if (!result.ok) {
|
|
104
|
+
throw new WorkflowDslError(
|
|
105
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
106
|
+
`Job '${jobKey}' inline workspace manifest failed to compile: ${result.errors
|
|
107
|
+
.map((e) => `${e.path}: ${e.message}`)
|
|
108
|
+
.join('; ')
|
|
109
|
+
.slice(0, 400)}`,
|
|
110
|
+
{ jobKey, actionId: actionManifest.metadata.id },
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
return Object.freeze({ kind: 'inline' as const, compiled: result.compiled });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
interface SynthesizeArgs {
|
|
117
|
+
readonly agentSlug: string;
|
|
118
|
+
readonly phaseKey: string | null;
|
|
119
|
+
readonly role: string;
|
|
120
|
+
readonly deliverableSpecRef: string | null;
|
|
121
|
+
readonly mounts: Readonly<Record<string, unknown>>;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Build the YAML-equivalent envelope object that `compileManifest()`
|
|
126
|
+
* accepts. We never round-trip through YAML — the kernel's compiler
|
|
127
|
+
* accepts a parsed object too.
|
|
128
|
+
*/
|
|
129
|
+
function synthesizeInlineManifestEnvelope(args: SynthesizeArgs): unknown {
|
|
130
|
+
return {
|
|
131
|
+
apiVersion: 'xema.dev/workspace/v1',
|
|
132
|
+
kind: 'WorkspaceManifest',
|
|
133
|
+
metadata: {
|
|
134
|
+
slug: 'inline',
|
|
135
|
+
version: INLINE_MANIFEST_VERSION,
|
|
136
|
+
// Inline-synthesized manifests only ever boot from a workflow
|
|
137
|
+
// step — interactive sessions reach the resolver through the
|
|
138
|
+
// long-form ref path. Pinning this here means
|
|
139
|
+
// `EnvironmentResolver` can fail fast if a workflow ever points
|
|
140
|
+
// an agent-session-only consumer at one of these.
|
|
141
|
+
surfaceCompat: ['workflow'],
|
|
142
|
+
},
|
|
143
|
+
spec: {
|
|
144
|
+
agent: {
|
|
145
|
+
slug: args.agentSlug,
|
|
146
|
+
phase: args.phaseKey ?? 'default',
|
|
147
|
+
role: args.role,
|
|
148
|
+
...(args.deliverableSpecRef === null
|
|
149
|
+
? {}
|
|
150
|
+
: { deliverableSpecRef: args.deliverableSpecRef }),
|
|
151
|
+
},
|
|
152
|
+
mounts: args.mounts,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function containsExpressions(node: unknown): boolean {
|
|
158
|
+
if (typeof node === 'string') return ANY_INTERPOLATION_RE.test(node);
|
|
159
|
+
if (Array.isArray(node)) return node.some(containsExpressions);
|
|
160
|
+
if (node !== null && typeof node === 'object') {
|
|
161
|
+
return Object.values(node as Record<string, unknown>).some(containsExpressions);
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function isLiteralString(value: unknown): value is string {
|
|
167
|
+
return typeof value === 'string' && !ANY_INTERPOLATION_RE.test(value);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function isLiteralStringOrUndefined(value: unknown): boolean {
|
|
171
|
+
return value === undefined || isLiteralString(value);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function isLiteralStringOrNullOrUndefined(value: unknown): boolean {
|
|
175
|
+
return value === null || value === undefined || isLiteralString(value);
|
|
176
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { MatrixStrategyKind, WorkflowErrorCode } from '@xemahq/kernel-contracts/workflow';
|
|
2
|
+
import type {
|
|
3
|
+
CompiledDynamicMatrixStrategy,
|
|
4
|
+
CompiledSingleStrategy,
|
|
5
|
+
CompiledStaticMatrixStrategy,
|
|
6
|
+
CompiledStrategy,
|
|
7
|
+
} from '@xemahq/kernel-contracts/workflow';
|
|
8
|
+
import { WorkflowDslError } from '../errors';
|
|
9
|
+
import type { WorkflowStrategyDeclaration } from '../types';
|
|
10
|
+
import { compileExpression } from '../expression';
|
|
11
|
+
import { stripInterpolation } from '../expression/interpolation';
|
|
12
|
+
|
|
13
|
+
/** Hard cap on matrix cardinality — static and dynamic both enforce this. */
|
|
14
|
+
const MATRIX_MAX_ENTRIES = 64;
|
|
15
|
+
const MATRIX_DEFAULT_MAX_PARALLEL = 8;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Compile an authored strategy declaration into a CompiledStrategy. Performs
|
|
19
|
+
* cartesian expansion for static matrices at compile time and pre-validates
|
|
20
|
+
* the dynamic `from:` expression.
|
|
21
|
+
*
|
|
22
|
+
* Static matrix cardinality is the product of each axis's length. The cap
|
|
23
|
+
* is applied to the product, not per-axis, so 8×8 (=64) is the ceiling.
|
|
24
|
+
*/
|
|
25
|
+
export function compileStrategy(
|
|
26
|
+
declaration: WorkflowStrategyDeclaration | undefined,
|
|
27
|
+
jobKey: string,
|
|
28
|
+
): CompiledStrategy {
|
|
29
|
+
if (!declaration) {
|
|
30
|
+
const single: CompiledSingleStrategy = { kind: MatrixStrategyKind.SINGLE };
|
|
31
|
+
return single;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if ('matrix' in declaration) {
|
|
35
|
+
return expandStaticMatrix(declaration.matrix, declaration.maxParallel, jobKey);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return compileDynamicStrategy(declaration.dynamic, declaration.maxParallel, jobKey);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function expandStaticMatrix(
|
|
42
|
+
axes: Readonly<Record<string, readonly unknown[]>>,
|
|
43
|
+
maxParallelRaw: number | undefined,
|
|
44
|
+
jobKey: string,
|
|
45
|
+
): CompiledStaticMatrixStrategy {
|
|
46
|
+
const axisKeys = Object.keys(axes).sort();
|
|
47
|
+
if (axisKeys.length === 0) {
|
|
48
|
+
throw new WorkflowDslError(
|
|
49
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
50
|
+
`Job '${jobKey}' declares strategy.matrix with no axes.`,
|
|
51
|
+
{ jobKey },
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Cartesian product in lexicographic order of axisKeys for determinism.
|
|
56
|
+
let entries: Record<string, unknown>[] = [{}];
|
|
57
|
+
for (const axis of axisKeys) {
|
|
58
|
+
const values = axes[axis]!;
|
|
59
|
+
const next: Record<string, unknown>[] = [];
|
|
60
|
+
for (const partial of entries) {
|
|
61
|
+
for (const value of values) {
|
|
62
|
+
next.push({ ...partial, [axis]: value });
|
|
63
|
+
}
|
|
64
|
+
if (next.length > MATRIX_MAX_ENTRIES) break;
|
|
65
|
+
}
|
|
66
|
+
entries = next;
|
|
67
|
+
if (entries.length > MATRIX_MAX_ENTRIES) break;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (entries.length > MATRIX_MAX_ENTRIES) {
|
|
71
|
+
throw new WorkflowDslError(
|
|
72
|
+
WorkflowErrorCode.DSL_MATRIX_CARDINALITY_EXCEEDED,
|
|
73
|
+
`Job '${jobKey}' static matrix expansion produces ${entries.length} entries; cap is ${MATRIX_MAX_ENTRIES}.`,
|
|
74
|
+
{ jobKey, cap: MATRIX_MAX_ENTRIES, cardinality: entries.length },
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const maxParallel = resolveMaxParallel(maxParallelRaw, entries.length);
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
kind: MatrixStrategyKind.STATIC,
|
|
82
|
+
entries: entries.map(Object.freeze) as readonly Readonly<Record<string, unknown>>[],
|
|
83
|
+
maxParallel,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function compileDynamicStrategy(
|
|
88
|
+
declaration: {
|
|
89
|
+
readonly from: string;
|
|
90
|
+
readonly as: string;
|
|
91
|
+
readonly maxEntries?: number;
|
|
92
|
+
readonly keyBy?: string;
|
|
93
|
+
},
|
|
94
|
+
maxParallelRaw: number | undefined,
|
|
95
|
+
jobKey: string,
|
|
96
|
+
): CompiledDynamicMatrixStrategy {
|
|
97
|
+
const body = stripInterpolation(declaration.from);
|
|
98
|
+
// Compile expression at compile time — this rejects unknown roots and
|
|
99
|
+
// bad syntax now, so the runtime evaluator can only fail on an unknown
|
|
100
|
+
// property during real evaluation.
|
|
101
|
+
compileExpression(body);
|
|
102
|
+
|
|
103
|
+
const maxEntries = declaration.maxEntries ?? MATRIX_MAX_ENTRIES;
|
|
104
|
+
if (maxEntries < 1 || maxEntries > MATRIX_MAX_ENTRIES) {
|
|
105
|
+
throw new WorkflowDslError(
|
|
106
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
107
|
+
`Job '${jobKey}' strategy.dynamic.maxEntries must be in [1, ${MATRIX_MAX_ENTRIES}]; got ${maxEntries}.`,
|
|
108
|
+
{ jobKey, maxEntries, cap: MATRIX_MAX_ENTRIES },
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
const maxParallel = resolveMaxParallel(maxParallelRaw, maxEntries);
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
kind: MatrixStrategyKind.DYNAMIC,
|
|
115
|
+
fromExpression: body,
|
|
116
|
+
bindingName: declaration.as,
|
|
117
|
+
maxEntries,
|
|
118
|
+
maxParallel,
|
|
119
|
+
keyBy: declaration.keyBy ?? null,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function resolveMaxParallel(raw: number | undefined, cardinality: number): number {
|
|
124
|
+
if (raw === undefined) {
|
|
125
|
+
return Math.min(MATRIX_DEFAULT_MAX_PARALLEL, Math.max(1, cardinality));
|
|
126
|
+
}
|
|
127
|
+
if (raw < 1) {
|
|
128
|
+
throw new WorkflowDslError(
|
|
129
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
130
|
+
`strategy.maxParallel must be >= 1 (got ${raw}).`,
|
|
131
|
+
{ raw },
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return Math.min(raw, MATRIX_MAX_ENTRIES);
|
|
135
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AWP_V1_SPEC,
|
|
3
|
+
findSlot,
|
|
4
|
+
isKnownSlotKey,
|
|
5
|
+
SlotMode,
|
|
6
|
+
type WorkspaceSpec,
|
|
7
|
+
} from '@xemahq/kernel-contracts/agent-workspace';
|
|
8
|
+
import { WorkflowErrorCode } from '@xemahq/kernel-contracts/workflow';
|
|
9
|
+
import type { MountPlan } from '@xemahq/kernel-contracts/workflow';
|
|
10
|
+
import { WorkflowDslError } from '../errors';
|
|
11
|
+
import type { ActionManifest } from '../types';
|
|
12
|
+
import { isAgentShapedAction } from './action-shape';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Compile a job's `with:` block into a compile-time {@link MountPlan} —
|
|
16
|
+
* a declarative list of mount keys grouped by access, validated against
|
|
17
|
+
* (a) the workspace spec's closed slot set and (b) the action manifest's
|
|
18
|
+
* `allowedMounts`.
|
|
19
|
+
*
|
|
20
|
+
* Read/write classification is per-mount with a slot-default fallback:
|
|
21
|
+
* - `with.mounts.<key>: true` uses the slot's `defaultMode` from the
|
|
22
|
+
* {@link WorkspaceSpec}.
|
|
23
|
+
* - `with.mounts.<key>: { mode: 'read-only' | 'read-write' }`
|
|
24
|
+
* overrides the slot default. The value is the closed `SlotMode`
|
|
25
|
+
* enum; any other string is rejected fail-fast.
|
|
26
|
+
*
|
|
27
|
+
* The legacy `with.mountKeys: []` / `with.writableMountKeys: []` array
|
|
28
|
+
* form is gone — the action manifest's input schema rejects it before
|
|
29
|
+
* this compiler runs, but we also explicitly reject those keys here so
|
|
30
|
+
* a misconfigured manifest can't sneak the legacy shape through.
|
|
31
|
+
*
|
|
32
|
+
* Concrete {@link MountSource} resolution is NOT the compiler's job: it
|
|
33
|
+
* requires access to the engine's org-scoped resource registry, which is
|
|
34
|
+
* only available at dispatch time.
|
|
35
|
+
*
|
|
36
|
+
* Rules:
|
|
37
|
+
* - `with.mounts` absent → empty plan (no implicit mounts).
|
|
38
|
+
* - Unknown slot keys (per the spec) fail fast.
|
|
39
|
+
* - If the action manifest declares `allowedMounts`, keys outside it
|
|
40
|
+
* also fail fast.
|
|
41
|
+
* - Legacy `mountKeys` / `writableMountKeys` keys on `with:` → fail fast
|
|
42
|
+
* with a migration hint.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
const SPEC: WorkspaceSpec = AWP_V1_SPEC;
|
|
46
|
+
|
|
47
|
+
const LEGACY_MOUNT_KEYS = ['mountKeys', 'writableMountKeys'] as const;
|
|
48
|
+
|
|
49
|
+
export function compileMountPlan(
|
|
50
|
+
jobKey: string,
|
|
51
|
+
withBlock: Readonly<Record<string, unknown>> | undefined,
|
|
52
|
+
actionManifest: ActionManifest | null,
|
|
53
|
+
): MountPlan {
|
|
54
|
+
rejectLegacyShape(jobKey, withBlock);
|
|
55
|
+
|
|
56
|
+
enforceManifestSourceDiscriminator(jobKey, withBlock, actionManifest);
|
|
57
|
+
|
|
58
|
+
const mounts = extractMountsBlock(withBlock);
|
|
59
|
+
if (!mounts) {
|
|
60
|
+
return { readOnly: [], readWrite: [] };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const allowed =
|
|
64
|
+
actionManifest?.spec.allowedMounts !== undefined
|
|
65
|
+
? new Set(actionManifest.spec.allowedMounts)
|
|
66
|
+
: null;
|
|
67
|
+
|
|
68
|
+
const readOnly: { key: string }[] = [];
|
|
69
|
+
const readWrite: { key: string }[] = [];
|
|
70
|
+
|
|
71
|
+
const sortedKeys = Object.keys(mounts).sort();
|
|
72
|
+
for (const key of sortedKeys) {
|
|
73
|
+
const mode = parseMountValue(jobKey, key, mounts[key]);
|
|
74
|
+
if (!isKnownSlotKey(SPEC, key)) {
|
|
75
|
+
throw new WorkflowDslError(
|
|
76
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
77
|
+
`Job '${jobKey}' requests mount '${key}' which is not a known slot in WorkspaceSpec '${SPEC.id}'.`,
|
|
78
|
+
{ jobKey, mountKey: key, knownSlots: SPEC.slots.map((s) => s.key) },
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (allowed !== null && !allowed.has(key)) {
|
|
82
|
+
throw new WorkflowDslError(
|
|
83
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
84
|
+
`Job '${jobKey}' requests mount '${key}' but action manifest does not list it in allowedMounts.`,
|
|
85
|
+
{ jobKey, mountKey: key, allowedMounts: [...allowed] },
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
const slot = findSlot(SPEC, key);
|
|
89
|
+
const slotDefault = slot?.defaultMode ?? SlotMode.ReadOnly;
|
|
90
|
+
const effective = mode ?? slotDefault;
|
|
91
|
+
if (slot && !slot.allowedModes.includes(effective)) {
|
|
92
|
+
throw new WorkflowDslError(
|
|
93
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
94
|
+
`Job '${jobKey}' requests mount '${key}' with mode '${effective}', which is not in slot allowedModes [${slot.allowedModes.join(', ')}].`,
|
|
95
|
+
{ jobKey, mountKey: key, requestedMode: effective, allowedModes: [...slot.allowedModes] },
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
if (effective === SlotMode.ReadWrite) {
|
|
99
|
+
readWrite.push({ key });
|
|
100
|
+
} else {
|
|
101
|
+
readOnly.push({ key });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { readOnly, readWrite };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function parseMountValue(
|
|
109
|
+
jobKey: string,
|
|
110
|
+
mountKey: string,
|
|
111
|
+
value: unknown,
|
|
112
|
+
): SlotMode | null {
|
|
113
|
+
if (value === true) return null;
|
|
114
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
115
|
+
const obj = value as Record<string, unknown>;
|
|
116
|
+
const mode = obj['mode'];
|
|
117
|
+
if (mode === SlotMode.ReadOnly || mode === SlotMode.ReadWrite) {
|
|
118
|
+
return mode;
|
|
119
|
+
}
|
|
120
|
+
throw new WorkflowDslError(
|
|
121
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
122
|
+
`Job '${jobKey}' mount '${mountKey}' has invalid 'mode': must be '${SlotMode.ReadOnly}' or '${SlotMode.ReadWrite}'.`,
|
|
123
|
+
{ jobKey, mountKey, mode: typeof mode === 'string' ? mode : typeof mode },
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
throw new WorkflowDslError(
|
|
127
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
128
|
+
`Job '${jobKey}' mount '${mountKey}' value must be \`true\` or \`{ mode: 'read-only' | 'read-write' }\`.`,
|
|
129
|
+
{ jobKey, mountKey, valueKind: Array.isArray(value) ? 'array' : value === null ? 'null' : typeof value },
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function extractMountsBlock(
|
|
134
|
+
withBlock: Readonly<Record<string, unknown>> | undefined,
|
|
135
|
+
): Readonly<Record<string, unknown>> | null {
|
|
136
|
+
if (!withBlock) return null;
|
|
137
|
+
const raw = withBlock['mounts'];
|
|
138
|
+
if (raw === undefined) return null;
|
|
139
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
140
|
+
throw new WorkflowDslError(
|
|
141
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
142
|
+
`\`with.mounts\` must be an object mapping slot keys to \`true\` or \`{ mode }\`.`,
|
|
143
|
+
{ mountsKind: Array.isArray(raw) ? 'array' : raw === null ? 'null' : typeof raw },
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return raw as Readonly<Record<string, unknown>>;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function rejectLegacyShape(
|
|
150
|
+
jobKey: string,
|
|
151
|
+
withBlock: Readonly<Record<string, unknown>> | undefined,
|
|
152
|
+
): void {
|
|
153
|
+
if (!withBlock) return;
|
|
154
|
+
for (const legacyKey of LEGACY_MOUNT_KEYS) {
|
|
155
|
+
if (legacyKey in withBlock) {
|
|
156
|
+
throw new WorkflowDslError(
|
|
157
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
158
|
+
`Job '${jobKey}' uses legacy '${legacyKey}' on \`with:\`. Migrate to \`with.mounts\` (object form): \`mounts: { references: true, deliverables: { mode: 'read-write' } }\`.`,
|
|
159
|
+
{ jobKey, legacyKey },
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Manifest-source discriminator: when an action's `inputs:` schema
|
|
167
|
+
* declares both `compositionRef` and `mounts` as alternatives (via
|
|
168
|
+
* JSON-Schema `oneOf`), the workflow's `with:` block must specify
|
|
169
|
+
* exactly one of them. Detected generically so biome-shipped agent
|
|
170
|
+
* actions inherit the same compile-time check without a hardcoded
|
|
171
|
+
* action-id allowlist.
|
|
172
|
+
*/
|
|
173
|
+
function enforceManifestSourceDiscriminator(
|
|
174
|
+
jobKey: string,
|
|
175
|
+
withBlock: Readonly<Record<string, unknown>> | undefined,
|
|
176
|
+
actionManifest: ActionManifest | null,
|
|
177
|
+
): void {
|
|
178
|
+
if (!isAgentShapedAction(actionManifest)) return;
|
|
179
|
+
const hasRef =
|
|
180
|
+
withBlock !== undefined &&
|
|
181
|
+
'compositionRef' in withBlock &&
|
|
182
|
+
typeof withBlock['compositionRef'] === 'string' &&
|
|
183
|
+
(withBlock['compositionRef'] as string).length > 0;
|
|
184
|
+
const hasMounts =
|
|
185
|
+
withBlock !== undefined &&
|
|
186
|
+
'mounts' in withBlock &&
|
|
187
|
+
withBlock['mounts'] !== undefined;
|
|
188
|
+
if (hasRef && hasMounts) {
|
|
189
|
+
throw new WorkflowDslError(
|
|
190
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
191
|
+
`Job '${jobKey}' sets both \`compositionRef\` and \`mounts\` on \`with:\`. The action's manifest source is exactly one of the two.`,
|
|
192
|
+
{ jobKey, actionId: actionManifest.metadata.id },
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
if (!hasRef && !hasMounts) {
|
|
196
|
+
throw new WorkflowDslError(
|
|
197
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
198
|
+
`Job '${jobKey}' must set exactly one of \`compositionRef\` (long form) or \`mounts\` (short form) on \`with:\`.`,
|
|
199
|
+
{ jobKey, actionId: actionManifest.metadata.id },
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
}
|