@xemahq/kernel-contracts 0.3.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/agent-composition/index.d.ts +1 -0
  2. package/dist/agent-composition/index.d.ts.map +1 -1
  3. package/dist/agent-composition/index.js +1 -0
  4. package/dist/agent-composition/index.js.map +1 -1
  5. package/dist/agent-composition/lib/composition.d.ts +3 -10
  6. package/dist/agent-composition/lib/composition.d.ts.map +1 -1
  7. package/dist/agent-composition/lib/composition.js.map +1 -1
  8. package/dist/agent-composition/lib/invocation-overlay.d.ts +17 -0
  9. package/dist/agent-composition/lib/invocation-overlay.d.ts.map +1 -0
  10. package/dist/agent-composition/lib/invocation-overlay.js +3 -0
  11. package/dist/agent-composition/lib/invocation-overlay.js.map +1 -0
  12. package/dist/agent-permission/index.d.ts +2 -0
  13. package/dist/agent-permission/index.d.ts.map +1 -0
  14. package/dist/agent-permission/index.js +18 -0
  15. package/dist/agent-permission/index.js.map +1 -0
  16. package/dist/agent-permission/lib/permission-map.d.ts +6 -0
  17. package/dist/agent-permission/lib/permission-map.d.ts.map +1 -0
  18. package/dist/agent-permission/lib/permission-map.js +27 -0
  19. package/dist/agent-permission/lib/permission-map.js.map +1 -0
  20. package/dist/agent-workspace/awp-spec.json +1 -1
  21. package/dist/agent-workspace/lib/agent-tool-defaults.d.ts +2 -5
  22. package/dist/agent-workspace/lib/agent-tool-defaults.d.ts.map +1 -1
  23. package/dist/agent-workspace/lib/agent-tool-defaults.js +5 -25
  24. package/dist/agent-workspace/lib/agent-tool-defaults.js.map +1 -1
  25. package/dist/reference-resolution/index.d.ts +2 -0
  26. package/dist/reference-resolution/index.d.ts.map +1 -0
  27. package/dist/reference-resolution/index.js +18 -0
  28. package/dist/reference-resolution/index.js.map +1 -0
  29. package/dist/reference-resolution/lib/reference-resolution.d.ts +40 -0
  30. package/dist/reference-resolution/lib/reference-resolution.d.ts.map +1 -0
  31. package/dist/reference-resolution/lib/reference-resolution.js +42 -0
  32. package/dist/reference-resolution/lib/reference-resolution.js.map +1 -0
  33. package/dist/workflow/lib/compiled-workspace-manifest.d.ts +1 -1
  34. package/dist/workflow/lib/compiled-workspace-manifest.d.ts.map +1 -1
  35. package/dist/workflow/lib/model-ref.d.ts +18 -8
  36. package/dist/workflow/lib/model-ref.d.ts.map +1 -1
  37. package/dist/workflow/lib/model-ref.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/agent-composition/index.ts +1 -0
  40. package/src/agent-composition/lib/composition.ts +11 -57
  41. package/src/agent-composition/lib/invocation-overlay.ts +75 -0
  42. package/src/agent-permission/index.ts +12 -0
  43. package/src/agent-permission/lib/permission-map.ts +71 -0
  44. package/src/agent-workspace/lib/agent-tool-defaults.ts +20 -56
  45. package/src/reference-resolution/index.ts +10 -0
  46. package/src/reference-resolution/lib/reference-resolution.ts +175 -0
  47. package/src/workflow/lib/compiled-workspace-manifest.ts +1 -1
  48. package/src/workflow/lib/model-ref.ts +81 -28
@@ -13,9 +13,8 @@
13
13
  // the agent section of `WorkspaceManifest` into this primitive.
14
14
  // ═══════════════════════════════════════════════════════════════════════════
15
15
 
16
- import type { PermissionMap } from '../../agent-workspace';
17
16
  import type { ToolSelectionEntry } from '../../mcp-tool';
18
- import type { ModelRef } from '../../workflow';
17
+ import type { NodeOverlayFragment } from '../../workflow';
19
18
  import type { SkillRef } from '../../skill';
20
19
  import type { CapabilityLayer } from './capability-layer';
21
20
  import type { CompositionWorkspace } from './composition-workspace';
@@ -100,7 +99,7 @@ export interface CompositionLimits {
100
99
  * `skills` / `tools` / `modelOverride` attach AT this node — they extend or
101
100
  * override what the referenced agent definition declares intrinsically.
102
101
  */
103
- export interface CompositionNode {
102
+ export interface CompositionNode extends NodeOverlayFragment {
104
103
  /** The agent definition this node runs. */
105
104
  readonly agent: AgentRef;
106
105
  /** Optional alias when the same agent appears more than once in the tree. */
@@ -109,42 +108,10 @@ export interface CompositionNode {
109
108
  readonly skills: readonly SkillRef[];
110
109
  /** Tools attached at this node. */
111
110
  readonly tools: readonly ToolSelectionEntry[];
112
- /**
113
- * Optional model override for this node. When omitted the Model
114
- * Resolution Matrix decides at the invocation boundary. Besides the
115
- * concrete/strategy model choice, `ModelRef` carries the per-node
116
- * `temperature` and (for `StrategyModelRef`) the `modelClass` — so a
117
- * node specializes BOTH which model and how it samples here, without a
118
- * dedicated parallel field. The bundle renderer reads
119
- * `modelOverride.temperature` to stamp the agent's sampling temperature.
120
- */
121
- readonly modelOverride?: ModelRef;
122
- /**
123
- * Node-level instructions — a prompt fragment layered ONTO the referenced
124
- * agent definition's intrinsic system prompt. This is the "instructions"
125
- * lever that lets a handful of base agents (generic/coder/explorer/planner)
126
- * be specialized per node without authoring a new agent definition.
127
- *
128
- * Composite ordering (resolver-enforced): `agentDefinition.systemPrompt`
129
- * THEN these `instructions`. The platform `system-overlay.md` (AWP base +
130
- * deliverable contract + authority) is injected LATER by the runtime — it
131
- * is NOT part of this field. Absent/blank = the base prompt passes through
132
- * unchanged (never an empty append).
133
- */
134
- readonly instructions?: string;
135
- /**
136
- * Node-level permission override — a partial `PermissionMap` deep-merged
137
- * (per top-level key) OVER the referenced agent definition's authored
138
- * `permission` block, BEFORE the system tiers are applied by
139
- * `resolveAgentPermissions`. This is what lets a generic base agent
140
- * (e.g. `planner`) be specialized into a read-only, no-network reviewer
141
- * at a node — e.g. `{ edit: 'deny', webfetch: 'deny', task: { '*':
142
- * 'deny', 'kb-researcher': 'allow' } }` — without authoring a new agent
143
- * definition. A present key fully replaces the base agent's value for
144
- * that key; absent keys fall through to the base. Omitted = the base
145
- * agent's permissions pass through unchanged.
146
- */
147
- readonly permission?: PermissionMap;
111
+ // modelOverride / instructions / permission — the INVOCATION-overlay trio —
112
+ // are inherited from NodeOverlayFragment (single declaration site). They
113
+ // OVERRIDE the model, APPEND to the base prompt, and RESTRICT-merge the
114
+ // permission for this node only.
148
115
  /** Sub-agents — themselves fully-armed composition nodes (recursive). */
149
116
  readonly children: readonly CompositionNode[];
150
117
  /**
@@ -192,7 +159,7 @@ export interface Composition {
192
159
  * Distinct from `CompositionNode` (the AUTHORED form): a resolved node
193
160
  * has its agent reference fully version-pinned, never `latest`.
194
161
  */
195
- export interface ResolvedCompositionNode {
162
+ export interface ResolvedCompositionNode extends NodeOverlayFragment {
196
163
  /** Fully version-pinned agent reference. */
197
164
  readonly agent: Required<AgentRef>;
198
165
  readonly alias?: string;
@@ -200,23 +167,10 @@ export interface ResolvedCompositionNode {
200
167
  readonly skills: readonly SkillRef[];
201
168
  /** Tool references attached at this node. */
202
169
  readonly tools: readonly ToolSelectionEntry[];
203
- /** Concrete model override, when the node pinned one. */
204
- readonly modelOverride?: ModelRef;
205
- /**
206
- * Resolved node-level instructions, copied verbatim from the source
207
- * `CompositionNode.instructions`. The composer/runtime layers this onto
208
- * the referenced agent definition's `systemPrompt` (base prompt first,
209
- * then instructions). Absent = no node-level override.
210
- */
211
- readonly instructions?: string;
212
- /**
213
- * Resolved node-level permission override, copied verbatim from the
214
- * source `CompositionNode.permission`. The bundle renderer deep-merges
215
- * it (per top-level key) over the referenced agent definition's authored
216
- * permission before the system tiers run. Absent = no node-level
217
- * permission override.
218
- */
219
- readonly permission?: PermissionMap;
170
+ // modelOverride / instructions / permission inherited verbatim from the
171
+ // source node via NodeOverlayFragment (single declaration site). The
172
+ // composer layers instructions onto the base systemPrompt and deep-merges
173
+ // permission over the agent's authored permission before the system tiers.
220
174
  /** Resolved sub-agent nodes (recursive). */
221
175
  readonly children: readonly ResolvedCompositionNode[];
222
176
  /**
@@ -0,0 +1,75 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // ── Invocation Overlay ──
3
+ //
4
+ // The INVOCATION-axis overlay: per-call shaping of a resolved agent that is
5
+ // NEVER persisted into identity. ONE shared shape for both call sites —
6
+ // a workflow step's `with:` block AND a session/thread override — so the
7
+ // platform has a single overlay contract, not two divergent ones.
8
+ //
9
+ // Semantics (add-or-restrict ONLY — an overlay can extend context and NARROW
10
+ // capability, but NEVER widen beyond the agent's own + org/project policy):
11
+ // - modelOverride : OVERRIDE (inherited from NodeOverlayFragment)
12
+ // - instructions : APPEND (inherited; appended AFTER the resolved
13
+ // agent prompt — same field, same append
14
+ // semantics as a node; NO separate
15
+ // `instructionsAppend` name — one concept)
16
+ // - permission : RESTRICT (inherited; deep-merge, may only narrow)
17
+ // - tools : ADD/REMOVE (still policy-checked at resolve time)
18
+ // - skills : ADD-only (identity skills are the floor)
19
+ //
20
+ // Agent SELECTION (which agent runs) is deliberately NOT part of this overlay:
21
+ // in a workflow that is `agentSlug`/`agentRef`, and `compositionRef` selects a
22
+ // workspace SOURCE, not the agent (see plan audit A5). The overlay only SHAPES
23
+ // the already-selected agent.
24
+ // ═══════════════════════════════════════════════════════════════════════════
25
+
26
+ import type { ToolSelectionEntry } from '../../mcp-tool';
27
+ import type { SkillRef } from '../../skill';
28
+ import type { NodeOverlayFragment } from '../../workflow';
29
+
30
+ /**
31
+ * Tools requested for this invocation. `add` requests extra tools, `remove`
32
+ * drops tools by ref. Both are still validated against the agent's resolved
33
+ * permission AND the step/session policy at resolve time — a request can
34
+ * never force-enable a tool the agent is not allowed to use (fail-fast).
35
+ */
36
+ export interface InvocationToolOverlay {
37
+ readonly add?: readonly ToolSelectionEntry[];
38
+ /** Tool refs to drop for this invocation. */
39
+ readonly remove?: readonly string[];
40
+ }
41
+
42
+ /**
43
+ * Skills requested for this invocation. ADD-only: the resolved agent's own
44
+ * skills are the floor and cannot be removed by an overlay (they are
45
+ * governed, registered identity). Removal/disable is a separate, later
46
+ * governed capability — not part of the per-call overlay.
47
+ */
48
+ export interface InvocationSkillOverlay {
49
+ readonly add?: readonly SkillRef[];
50
+ }
51
+
52
+ /**
53
+ * The canonical per-call overlay. Extends {@link NodeOverlayFragment}
54
+ * (model / instructions / permission) with tools + skills. Both the
55
+ * workflow-step overlay and the session overlay ARE this shape.
56
+ */
57
+ export interface InvocationOverlay extends NodeOverlayFragment {
58
+ readonly tools?: InvocationToolOverlay;
59
+ readonly skills?: InvocationSkillOverlay;
60
+ }
61
+
62
+ /**
63
+ * A workflow step's typed `with:` overlay. Structurally an
64
+ * {@link InvocationOverlay}; named for the call site. The agent action
65
+ * manifest validates it via its `spec.inputs` JSON Schema (the existing
66
+ * fail-fast validation lane — NOT a new validation system).
67
+ */
68
+ export type AgentStepWithOverlay = InvocationOverlay;
69
+
70
+ /**
71
+ * A session/thread invocation overlay — the SAME shape as the workflow
72
+ * step overlay so sessions and workflows shape an agent identically
73
+ * (no second, looser session override surface).
74
+ */
75
+ export type SessionInvocationOverlay = InvocationOverlay;
@@ -0,0 +1,12 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // ── Agent Permission Contracts — Barrel Export ──
3
+ //
4
+ // Kernel (Layer-0) leaf: the pure permission TYPES (PermissionMap,
5
+ // PermissionRule, PermissionAction, PermissionKey, KNOWN_PERMISSION_KEYS).
6
+ // Zero deps. Imported by `workflow`, `agent-workspace`, and any consumer
7
+ // that needs the permission shape — breaks the prior workflow↔agent-workspace
8
+ // cycle. The resolve LOGIC + system tiers live in
9
+ // `agent-workspace/lib/agent-tool-defaults.ts`.
10
+ // ═══════════════════════════════════════════════════════════════════════════
11
+
12
+ export * from './lib/permission-map';
@@ -0,0 +1,71 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // ── Agent Permission types — single source of truth (Layer-0 leaf) ──
3
+ //
4
+ // The PURE permission TYPES, extracted to their own dependency-free subpath
5
+ // so BOTH `workflow` (model-ref / compiled-workspace-manifest) and
6
+ // `agent-workspace` (the resolve logic) can import them WITHOUT creating a
7
+ // `workflow ↔ agent-workspace` import cycle. The merge logic + system tiers
8
+ // (`resolveAgentPermissions`, `SYSTEM_*_PERMISSIONS`,
9
+ // `assertNoRequiredPermissionsDenied`) stay in
10
+ // `agent-workspace/lib/agent-tool-defaults.ts` and import these types.
11
+ //
12
+ // Mirrors OpenCode's unified `permission` surface (`packages/opencode/src/
13
+ // config/permission.ts`).
14
+ // ═══════════════════════════════════════════════════════════════════════════
15
+
16
+ /**
17
+ * Permission keys that take a `Rule` shape — either a scalar action or
18
+ * a per-target map (e.g. `bash: { '*': 'allow', 'rm -rf *': 'deny' }`,
19
+ * `task: { 'html-builder': 'allow' }`, `external_directory: { '/**':
20
+ * 'allow' }`). Mirrors OpenCode's `permission.ts` `InputObject`.
21
+ */
22
+ const KNOWN_RULE_PERMISSION_KEYS = [
23
+ 'read',
24
+ 'edit',
25
+ 'glob',
26
+ 'grep',
27
+ 'list',
28
+ 'bash',
29
+ 'task',
30
+ 'external_directory',
31
+ 'lsp',
32
+ 'skill',
33
+ ] as const;
34
+
35
+ /**
36
+ * Permission keys that take a scalar `Action` only (no per-target map).
37
+ * Mirrors OpenCode's `permission.ts` `InputObject`.
38
+ */
39
+ const KNOWN_ACTION_PERMISSION_KEYS = [
40
+ 'todowrite',
41
+ 'question',
42
+ 'webfetch',
43
+ 'websearch',
44
+ 'doom_loop',
45
+ ] as const;
46
+
47
+ export const KNOWN_PERMISSION_KEYS = [
48
+ ...KNOWN_RULE_PERMISSION_KEYS,
49
+ ...KNOWN_ACTION_PERMISSION_KEYS,
50
+ ] as const;
51
+
52
+ export type PermissionKey = (typeof KNOWN_PERMISSION_KEYS)[number];
53
+
54
+ export type PermissionAction = 'allow' | 'ask' | 'deny';
55
+
56
+ /**
57
+ * A permission entry is either a scalar action or a per-target map. The
58
+ * map form is supported only for the keys in `KNOWN_RULE_PERMISSION_KEYS`
59
+ * — the action-only keys reject map shapes at the OpenCode schema layer.
60
+ * Catch-all entries (MCP tool names, biome-emitted globs) live alongside
61
+ * the closed set; their shape is validated by OpenCode at config load.
62
+ */
63
+ export type PermissionRule = PermissionAction | Readonly<Record<string, PermissionAction>>;
64
+
65
+ /**
66
+ * Full permission map. Closed-set keys carry typed rules; the index
67
+ * signature accommodates MCP tool names and biome-emitted glob patterns
68
+ * (e.g. `xema_*`, `emit*`) that OpenCode resolves via its catch-all
69
+ * `Schema.Record(Schema.String, Rule)` in `permission.ts`.
70
+ */
71
+ export type PermissionMap = Readonly<Record<string, PermissionRule>>;
@@ -29,62 +29,26 @@
29
29
  // pipelines, and authoring previews all merge through this function.
30
30
  // ═══════════════════════════════════════════════════════════════════════════
31
31
 
32
- /**
33
- * Permission keys that take a `Rule` shape — either a scalar action or
34
- * a per-target map (e.g. `bash: { '*': 'allow', 'rm -rf *': 'deny' }`,
35
- * `task: { 'html-builder': 'allow' }`, `external_directory: { '/**':
36
- * 'allow' }`). Mirrors OpenCode's `permission.ts` `InputObject`.
37
- */
38
- const KNOWN_RULE_PERMISSION_KEYS = [
39
- 'read',
40
- 'edit',
41
- 'glob',
42
- 'grep',
43
- 'list',
44
- 'bash',
45
- 'task',
46
- 'external_directory',
47
- 'lsp',
48
- 'skill',
49
- ] as const;
50
-
51
- /**
52
- * Permission keys that take a scalar `Action` only (no per-target map).
53
- * Mirrors OpenCode's `permission.ts` `InputObject`.
54
- */
55
- const KNOWN_ACTION_PERMISSION_KEYS = [
56
- 'todowrite',
57
- 'question',
58
- 'webfetch',
59
- 'websearch',
60
- 'doom_loop',
61
- ] as const;
62
-
63
- export const KNOWN_PERMISSION_KEYS = [
64
- ...KNOWN_RULE_PERMISSION_KEYS,
65
- ...KNOWN_ACTION_PERMISSION_KEYS,
66
- ] as const;
67
-
68
- export type PermissionKey = (typeof KNOWN_PERMISSION_KEYS)[number];
69
-
70
- export type PermissionAction = 'allow' | 'ask' | 'deny';
71
-
72
- /**
73
- * A permission entry is either a scalar action or a per-target map. The
74
- * map form is supported only for the keys in `KNOWN_RULE_PERMISSION_KEYS`
75
- * — the action-only keys reject map shapes at the OpenCode schema layer.
76
- * Catch-all entries (MCP tool names, biome-emitted globs) live alongside
77
- * the closed set; their shape is validated by OpenCode at config load.
78
- */
79
- export type PermissionRule = PermissionAction | Readonly<Record<string, PermissionAction>>;
80
-
81
- /**
82
- * Full permission map. Closed-set keys carry typed rules; the index
83
- * signature accommodates MCP tool names and biome-emitted glob patterns
84
- * (e.g. `xema_*`, `emit*`) that OpenCode resolves via its catch-all
85
- * `Schema.Record(Schema.String, Rule)` in `permission.ts`.
86
- */
87
- export type PermissionMap = Readonly<Record<string, PermissionRule>>;
32
+ // The pure permission TYPES now live in the dependency-free `agent-permission`
33
+ // leaf subpath (breaking the prior workflow↔agent-workspace cycle). Import the
34
+ // value (`KNOWN_PERMISSION_KEYS`) for the resolve logic below + the types, and
35
+ // re-export all of them so the `agent-workspace` barrel surface stays stable
36
+ // for every existing consumer.
37
+ import { KNOWN_PERMISSION_KEYS } from '../../agent-permission';
38
+ import type {
39
+ PermissionAction,
40
+ PermissionKey,
41
+ PermissionMap,
42
+ PermissionRule,
43
+ } from '../../agent-permission';
44
+
45
+ export {
46
+ KNOWN_PERMISSION_KEYS,
47
+ type PermissionAction,
48
+ type PermissionKey,
49
+ type PermissionMap,
50
+ type PermissionRule,
51
+ } from '../../agent-permission';
88
52
 
89
53
  /**
90
54
  * Permissions every agent gets — manifests cannot deny them. Removing
@@ -0,0 +1,10 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // ── Reference Resolution Contracts — Barrel Export ──
3
+ //
4
+ // Kernel (Layer-0) leaf: the shared graceful-degradation decision (DBM.1) used
5
+ // by every resolver that walks a definition's declared references. Zero
6
+ // cross-subpath deps so agent-composition / skill / capability / workflow /
7
+ // action resolvers can all import it without a cycle.
8
+ // ═══════════════════════════════════════════════════════════════════════════
9
+
10
+ export * from './lib/reference-resolution';
@@ -0,0 +1,175 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // ── Reference Resolution — graceful-degradation decision (DBM.1) ──
3
+ //
4
+ // The ONE shared contract every resolver uses when it walks a definition's
5
+ // declared references (an Agent node's `agentRef`, a skill's sub-skill, an
6
+ // action's capability, a workflow's `uses:`) and asks "is this reference
7
+ // available right now, and what do I do if it isn't?".
8
+ //
9
+ // It exists because of DYNAMIC BIOME MODULARITY: a definition authored while
10
+ // biome X was installed keeps referencing X's contributions after X is
11
+ // uninstalled/disabled. Today every resolver hard-fails uniformly
12
+ // (AGENT_NOT_REGISTERED / MOUNT_SOURCE_NOT_FOUND aborts the whole run). The
13
+ // shared policy below lets a reference be declared `optional` so its absence
14
+ // degrades to a typed, observable skip instead of aborting — while the DEFAULT
15
+ // stays fail-fast/fail-closed (no silent fallback).
16
+ //
17
+ // Modeled on `ResolvedModelDecision` (the Model Matrix's traced outcome): a
18
+ // resolver returns one {@link ResolvedReferenceDecision} per reference, carrying
19
+ // the outcome + a human-readable `reason` for the Studio debugger. Distinct
20
+ // from model resolution on purpose — the Model Matrix always has a DEFAULT rule
21
+ // so it can never be "unavailable"; references genuinely can.
22
+ //
23
+ // Pure Layer-0 leaf: zero cross-subpath imports so EVERY resolver
24
+ // (agent-composition / skill / capability / workflow / action) can import it
25
+ // without creating a cycle.
26
+ // ═══════════════════════════════════════════════════════════════════════════
27
+
28
+ /**
29
+ * How a resolver must treat a reference whose backing contribution is not
30
+ * currently available. Closed set; the DEFAULT is {@link Required}.
31
+ *
32
+ * This is the per-reference knob that makes biome add/remove non-breaking:
33
+ * authors mark the references they can live without as {@link Optional}; every
34
+ * other reference keeps the fail-fast contract.
35
+ */
36
+ export enum MissingReferencePolicy {
37
+ /**
38
+ * Default. An unavailable reference is fatal — the resolver throws and the
39
+ * whole resolution/run fails fast. Never degrades silently.
40
+ */
41
+ Required = 'required',
42
+ /**
43
+ * An unavailable reference is skipped with a typed warning
44
+ * ({@link ReferenceResolutionState.OptionalSkipped}) and resolution
45
+ * continues without it. The only sanctioned degradation path.
46
+ */
47
+ Optional = 'optional',
48
+ }
49
+
50
+ /**
51
+ * The four — and only four — outcomes of resolving a single reference.
52
+ * Closed set; this is the "4-state ResolverDecision" of DBM.1.
53
+ */
54
+ export enum ReferenceResolutionState {
55
+ /** Resolved to a live, available contribution. */
56
+ Present = 'present',
57
+ /**
58
+ * The reference is declared on the definition but its backing contribution
59
+ * is not currently available — typically because the owning biome was
60
+ * uninstalled or disabled. Fatal under {@link MissingReferencePolicy.Required};
61
+ * degrades to {@link OptionalSkipped} under {@link MissingReferencePolicy.Optional}.
62
+ */
63
+ DeclaredButUnavailable = 'declared-but-unavailable',
64
+ /**
65
+ * An {@link MissingReferencePolicy.Optional} reference that was
66
+ * {@link DeclaredButUnavailable} and was therefore skipped; resolution
67
+ * continued without it. This is the graceful-degradation terminal state.
68
+ */
69
+ OptionalSkipped = 'optional-skipped',
70
+ /**
71
+ * The reference resolves to a contribution the caller is NOT permitted to
72
+ * use (governance / scope / visibility denied it). Always fatal regardless
73
+ * of policy — fail-closed, never downgraded to a skip.
74
+ */
75
+ Denied = 'denied',
76
+ }
77
+
78
+ /**
79
+ * The closed set of reference KINDS a definition can declare and a resolver
80
+ * can resolve. Adding a new resolvable reference type = a one-line enum
81
+ * extension here.
82
+ */
83
+ export enum ReferenceKind {
84
+ /** An Agent node's `agentRef` (a child agent / sub-agent). */
85
+ Agent = 'agent',
86
+ /** A skill bundle slug (intrinsic, node-attached, or a sub-skill). */
87
+ Skill = 'skill',
88
+ /** An MCP tool selection entry (`<provider>:<tool>@<v>`). */
89
+ Tool = 'tool',
90
+ /** A capability ref (`document:pdf-to-markdown@1`) reached via the router. */
91
+ Capability = 'capability',
92
+ /** A workflow action ref. */
93
+ Action = 'action',
94
+ /** A sub-workflow `uses:` ref. */
95
+ Workflow = 'workflow',
96
+ }
97
+
98
+ /**
99
+ * Declared-vs-enforced verification of a {@link ReferenceResolutionState.Present}
100
+ * reference — adopted from NemoClaw's verification model. Answers "the registry
101
+ * SAYS this exists, but is it actually reachable at the gateway/runtime?".
102
+ * Surfaced for diagnostics; it does NOT change fail-fast semantics (an
103
+ * unreachable-but-required reference still fails).
104
+ */
105
+ export enum ReferenceVerification {
106
+ /** Declared in the registry AND confirmed reachable at the gateway/runtime. */
107
+ Verified = 'verified',
108
+ /** Declared in the registry but reachability at runtime is unconfirmed. */
109
+ RegistryOnly = 'registry-only',
110
+ /** Reachable at the gateway/runtime but with no registry declaration. */
111
+ GatewayOnly = 'gateway-only',
112
+ /** Neither declared nor reachable — pairs with {@link ReferenceResolutionState.DeclaredButUnavailable}. */
113
+ Unavailable = 'unavailable',
114
+ }
115
+
116
+ /**
117
+ * Owner attribution for a reference — which biome contributes it (NemoClaw's
118
+ * owner-attribution shape). The key signal behind a
119
+ * {@link ReferenceResolutionState.DeclaredButUnavailable} outcome: it names the
120
+ * biome whose install/enable would make the reference {@link ReferenceResolutionState.Present}
121
+ * again, so the UI can say "install biome X to restore `media:transcribe@1`".
122
+ *
123
+ * Both fields optional — System/kernel-shipped references have no owning biome.
124
+ */
125
+ export interface ReferenceOwner {
126
+ /** The contributing biome's id; absent for System/kernel-shipped references. */
127
+ readonly biomeId?: string;
128
+ /** The contributing biome's version at resolution time, when known. */
129
+ readonly biomeVersion?: string;
130
+ }
131
+
132
+ /**
133
+ * The traced outcome of resolving ONE declared reference — the per-reference
134
+ * sibling of {@link ResolvedModelDecision}. A resolver emits one of these per
135
+ * reference it walks; the set is the audit trail the Studio debugger renders
136
+ * and the basis for any fail-fast throw.
137
+ */
138
+ export interface ResolvedReferenceDecision {
139
+ /** What kind of reference this is. */
140
+ readonly kind: ReferenceKind;
141
+ /** The ref/slug exactly as authored on the definition. */
142
+ readonly ref: string;
143
+ /** The pinned version the definition requested, when it pinned one. */
144
+ readonly version?: string;
145
+ /** The policy that governed this reference (default {@link MissingReferencePolicy.Required}). */
146
+ readonly policy: MissingReferencePolicy;
147
+ /** The resolution outcome. */
148
+ readonly state: ReferenceResolutionState;
149
+ /** Declared-vs-enforced verification, when the resolver could determine it. */
150
+ readonly verification?: ReferenceVerification;
151
+ /** Which biome contributes the reference, for "install X to restore" UX. */
152
+ readonly owner?: ReferenceOwner;
153
+ /** Human-readable reason for the Studio debugger; mirrors `ResolvedModelDecision.reason`. */
154
+ readonly reason: string;
155
+ }
156
+
157
+ /**
158
+ * `true` when the decision must abort resolution fail-fast — every
159
+ * {@link ReferenceResolutionState.Denied}, plus any
160
+ * {@link ReferenceResolutionState.DeclaredButUnavailable} that was NOT marked
161
+ * {@link MissingReferencePolicy.Optional}. The single predicate every resolver
162
+ * checks so the fail-fast rule is implemented in exactly one place (no
163
+ * per-resolver re-derivation, no silent divergence).
164
+ */
165
+ export function isFatalReferenceDecision(
166
+ decision: ResolvedReferenceDecision,
167
+ ): boolean {
168
+ if (decision.state === ReferenceResolutionState.Denied) {
169
+ return true;
170
+ }
171
+ if (decision.state === ReferenceResolutionState.DeclaredButUnavailable) {
172
+ return decision.policy !== MissingReferencePolicy.Optional;
173
+ }
174
+ return false;
175
+ }
@@ -8,7 +8,7 @@
8
8
  * → workflow-contracts → workspace-manifest-dsl.
9
9
  */
10
10
 
11
- import type { PermissionMap } from '../../agent-workspace/lib/agent-tool-defaults';
11
+ import type { PermissionMap } from '../../agent-permission';
12
12
  import type { ToolSelectionEntry } from '../../mcp-tool';
13
13
 
14
14
  import type { AgentRunRole } from './agent-role';