@xemahq/kernel-contracts 0.40.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-composition/lib/model-resolution-matrix.d.ts +5 -2
- package/dist/agent-composition/lib/model-resolution-matrix.d.ts.map +1 -1
- package/dist/agent-composition/lib/model-resolution-matrix.js +1 -0
- package/dist/agent-composition/lib/model-resolution-matrix.js.map +1 -1
- package/dist/connector/lib/canonical-operation-binding.d.ts +1 -1
- package/dist/connector/lib/canonical-operation-binding.d.ts.map +1 -1
- package/dist/connector/lib/canonical-operation-binding.js +2 -2
- package/dist/connector/lib/canonical-operation-binding.js.map +1 -1
- package/dist/connector/lib/integration-provider-contribution.d.ts +2 -2
- package/dist/contribution/lib/contribution-kind.d.ts +2 -1
- package/dist/contribution/lib/contribution-kind.d.ts.map +1 -1
- package/dist/contribution/lib/contribution-kind.js +1 -0
- package/dist/contribution/lib/contribution-kind.js.map +1 -1
- package/dist/enterprise-context/index.d.ts +5 -0
- package/dist/enterprise-context/index.d.ts.map +1 -0
- package/dist/enterprise-context/index.js +21 -0
- package/dist/enterprise-context/index.js.map +1 -0
- package/dist/enterprise-context/lib/context-bundle.d.ts +13 -0
- package/dist/enterprise-context/lib/context-bundle.d.ts.map +1 -0
- package/dist/enterprise-context/lib/context-bundle.js +3 -0
- package/dist/enterprise-context/lib/context-bundle.js.map +1 -0
- package/dist/enterprise-context/lib/context-document.d.ts +9 -0
- package/dist/enterprise-context/lib/context-document.d.ts.map +1 -0
- package/dist/enterprise-context/lib/context-document.js +3 -0
- package/dist/enterprise-context/lib/context-document.js.map +1 -0
- package/dist/enterprise-context/lib/context-mount-entry.d.ts +8 -0
- package/dist/enterprise-context/lib/context-mount-entry.d.ts.map +1 -0
- package/dist/enterprise-context/lib/context-mount-entry.js +5 -0
- package/dist/enterprise-context/lib/context-mount-entry.js.map +1 -0
- package/dist/enterprise-context/lib/dropped-context.d.ts +9 -0
- package/dist/enterprise-context/lib/dropped-context.d.ts.map +1 -0
- package/dist/enterprise-context/lib/dropped-context.js +8 -0
- package/dist/enterprise-context/lib/dropped-context.js.map +1 -0
- package/dist/ingestion-source/index.d.ts +2 -0
- package/dist/ingestion-source/index.d.ts.map +1 -0
- package/dist/ingestion-source/index.js +18 -0
- package/dist/ingestion-source/index.js.map +1 -0
- package/dist/ingestion-source/lib/ingestion-source-contribution.d.ts +62 -0
- package/dist/ingestion-source/lib/ingestion-source-contribution.d.ts.map +1 -0
- package/dist/ingestion-source/lib/ingestion-source-contribution.js +78 -0
- package/dist/ingestion-source/lib/ingestion-source-contribution.js.map +1 -0
- package/dist/llm-gateway/lib/errors.d.ts +5 -0
- package/dist/llm-gateway/lib/errors.d.ts.map +1 -1
- package/dist/llm-gateway/lib/errors.js +21 -1
- package/dist/llm-gateway/lib/errors.js.map +1 -1
- package/dist/output/index.d.ts +1 -0
- package/dist/output/index.d.ts.map +1 -1
- package/dist/output/index.js +1 -0
- package/dist/output/index.js.map +1 -1
- package/dist/output/lib/output-version-dto.d.ts +2 -0
- package/dist/output/lib/output-version-dto.d.ts.map +1 -1
- package/dist/output/lib/point-of-attention.d.ts +19 -0
- package/dist/output/lib/point-of-attention.d.ts.map +1 -0
- package/dist/output/lib/point-of-attention.js +24 -0
- package/dist/output/lib/point-of-attention.js.map +1 -0
- package/dist/worker-runtime/index.d.ts +1 -0
- package/dist/worker-runtime/index.d.ts.map +1 -1
- package/dist/worker-runtime/index.js +1 -0
- package/dist/worker-runtime/index.js.map +1 -1
- package/dist/worker-runtime/lib/enums.d.ts +5 -0
- package/dist/worker-runtime/lib/enums.d.ts.map +1 -1
- package/dist/worker-runtime/lib/enums.js +7 -1
- package/dist/worker-runtime/lib/enums.js.map +1 -1
- package/dist/worker-runtime/lib/runtime-layout-profile.d.ts +4 -1
- package/dist/worker-runtime/lib/runtime-layout-profile.d.ts.map +1 -1
- package/dist/worker-runtime/lib/runtime-layout-profile.js +12 -0
- package/dist/worker-runtime/lib/runtime-layout-profile.js.map +1 -1
- package/dist/worker-runtime/lib/workspace-sandbox.d.ts +7 -0
- package/dist/worker-runtime/lib/workspace-sandbox.d.ts.map +1 -0
- package/dist/worker-runtime/lib/workspace-sandbox.js +36 -0
- package/dist/worker-runtime/lib/workspace-sandbox.js.map +1 -0
- package/dist/workflow/index.d.ts +1 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +1 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/lib/enums.d.ts +1 -0
- package/dist/workflow/lib/enums.d.ts.map +1 -1
- package/dist/workflow/lib/enums.js +1 -0
- package/dist/workflow/lib/enums.js.map +1 -1
- package/dist/workflow/lib/model-ref.d.ts +17 -0
- package/dist/workflow/lib/model-ref.d.ts.map +1 -1
- package/dist/workflow/lib/model-ref.js +19 -1
- package/dist/workflow/lib/model-ref.js.map +1 -1
- package/dist/workflow/lib/workflow-control.d.ts +91 -0
- package/dist/workflow/lib/workflow-control.d.ts.map +1 -0
- package/dist/workflow/lib/workflow-control.js +84 -0
- package/dist/workflow/lib/workflow-control.js.map +1 -0
- package/package.json +1 -1
- package/src/agent-composition/lib/model-resolution-matrix.ts +16 -1
- package/src/connector/lib/canonical-operation-binding.ts +1 -1
- package/src/contribution/lib/contribution-kind.ts +18 -0
- package/src/enterprise-context/index.ts +4 -0
- package/src/enterprise-context/lib/context-bundle.ts +35 -0
- package/src/enterprise-context/lib/context-document.ts +23 -0
- package/src/enterprise-context/lib/context-mount-entry.ts +29 -0
- package/src/enterprise-context/lib/dropped-context.ts +24 -0
- package/src/ingestion-source/index.ts +20 -0
- package/src/ingestion-source/lib/ingestion-source-contribution.ts +238 -0
- package/src/llm-gateway/lib/errors.ts +46 -0
- package/src/output/index.ts +1 -0
- package/src/output/lib/output-version-dto.ts +9 -0
- package/src/output/lib/point-of-attention.ts +69 -0
- package/src/worker-runtime/index.ts +1 -0
- package/src/worker-runtime/lib/enums.ts +29 -0
- package/src/worker-runtime/lib/runtime-layout-profile.ts +61 -1
- package/src/worker-runtime/lib/workspace-sandbox.ts +129 -0
- package/src/workflow/index.ts +1 -0
- package/src/workflow/lib/enums.ts +8 -0
- package/src/workflow/lib/model-ref.ts +45 -0
- package/src/workflow/lib/workflow-control.ts +198 -0
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// is where the platform renders the system overlay per WS7.
|
|
21
21
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
22
22
|
|
|
23
|
-
import { WorkerRuntimeKind } from './enums';
|
|
23
|
+
import { RuntimeCommandFormat, WorkerRuntimeKind } from './enums';
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* The workspace slots whose on-disk location differs per orchestrator. The
|
|
@@ -48,6 +48,43 @@ export interface RuntimeLayoutProfile {
|
|
|
48
48
|
* fail-fast contract as `agentBundlesDir`.
|
|
49
49
|
*/
|
|
50
50
|
readonly commandsDir: string | null;
|
|
51
|
+
/**
|
|
52
|
+
* How this runtime consumes a per-skill `/`-launch command — the capability
|
|
53
|
+
* the composer/proxy switch on to decide WHETHER (and in which format) to
|
|
54
|
+
* materialise a launcher file. `Markdown` runtimes get a launcher written at
|
|
55
|
+
* `commandsDir`; `Toml`/`None` runtimes get NOTHING (skills still mount as
|
|
56
|
+
* bundles and launch via the runtime's own `/skills` surface / skill tool),
|
|
57
|
+
* so no stray file lands where the runtime would silently ignore it.
|
|
58
|
+
*
|
|
59
|
+
* INVARIANT: `commandFormat === Markdown` ⟺ `commandsDir !== null`. A runtime
|
|
60
|
+
* with no command surface (`commandsDir: null`) MUST declare `None`.
|
|
61
|
+
*/
|
|
62
|
+
readonly commandFormat: RuntimeCommandFormat;
|
|
63
|
+
/**
|
|
64
|
+
* Absolute path to the runtime's PRIMARY config file — the file the
|
|
65
|
+
* orchestrator's bundle composer renders and the supervisor materialises on
|
|
66
|
+
* the PVC. It carries the LLM-gateway credential (OpenCode reads
|
|
67
|
+
* `.opencode/opencode.jsonc`; Qwen `.qwen/settings.json` → `env.OPENAI_API_KEY`;
|
|
68
|
+
* Claude `.claude/settings.json` → `env.ANTHROPIC_AUTH_TOKEN`; Hermes
|
|
69
|
+
* `.hermes/config.json` → `provider.api_key`) and/or merged MCP bearer headers.
|
|
70
|
+
*
|
|
71
|
+
* This is the single source of truth for the config-file location: both the
|
|
72
|
+
* proxy's on-disk layout and the workspace-sandbox protected-path contract
|
|
73
|
+
* (`deriveWorkspaceSandboxProtection`) source it here rather than each
|
|
74
|
+
* re-deriving a per-runtime basename literal.
|
|
75
|
+
*/
|
|
76
|
+
readonly runtimeConfigFile: string;
|
|
77
|
+
/**
|
|
78
|
+
* Absolute path to a DEDICATED MCP-server file the runtime reads separately
|
|
79
|
+
* from its primary config (Claude Code: `.mcp.json` at the workspace root),
|
|
80
|
+
* or `null` when the runtime merges MCP into {@link runtimeConfigFile}
|
|
81
|
+
* (OpenCode/Qwen/Hermes: `InConfig`). When non-null the file carries per-server
|
|
82
|
+
* bearer headers and is therefore part of the secret read-deny set.
|
|
83
|
+
*
|
|
84
|
+
* INVARIANT: `mcpConfigFile !== null` ⟺ the runtime places MCP in a separate
|
|
85
|
+
* file. A runtime that folds MCP into its primary config MUST declare `null`.
|
|
86
|
+
*/
|
|
87
|
+
readonly mcpConfigFile: string | null;
|
|
51
88
|
}
|
|
52
89
|
|
|
53
90
|
/**
|
|
@@ -63,6 +100,11 @@ export const RUNTIME_LAYOUT_PROFILES: Readonly<
|
|
|
63
100
|
contextFile: '/workspace/AGENTS.md',
|
|
64
101
|
agentBundlesDir: '/workspace/.opencode/agents',
|
|
65
102
|
commandsDir: '/workspace/.opencode/command',
|
|
103
|
+
// OpenCode reads markdown commands from `{command,commands}/**.md`.
|
|
104
|
+
commandFormat: RuntimeCommandFormat.Markdown,
|
|
105
|
+
runtimeConfigFile: '/workspace/.opencode/opencode.jsonc',
|
|
106
|
+
// OpenCode pre-merges MCP into opencode.jsonc (`InConfig`) — no separate file.
|
|
107
|
+
mcpConfigFile: null,
|
|
66
108
|
},
|
|
67
109
|
[WorkerRuntimeKind.Qwen]: {
|
|
68
110
|
kind: WorkerRuntimeKind.Qwen,
|
|
@@ -70,6 +112,13 @@ export const RUNTIME_LAYOUT_PROFILES: Readonly<
|
|
|
70
112
|
contextFile: '/workspace/AGENTS.md',
|
|
71
113
|
agentBundlesDir: '/workspace/.qwen/agents',
|
|
72
114
|
commandsDir: '/workspace/.qwen/commands',
|
|
115
|
+
// Qwen custom commands are TOML, not markdown; skills launch via Qwen's
|
|
116
|
+
// own `/skills <name>` surface. A synthesised markdown `.md` under
|
|
117
|
+
// `.qwen/commands` would be silently ignored — so Xema emits NOTHING.
|
|
118
|
+
commandFormat: RuntimeCommandFormat.Toml,
|
|
119
|
+
runtimeConfigFile: '/workspace/.qwen/settings.json',
|
|
120
|
+
// Qwen reads its `mcpServers` map INSIDE `.qwen/settings.json` (`InConfig`).
|
|
121
|
+
mcpConfigFile: null,
|
|
73
122
|
},
|
|
74
123
|
[WorkerRuntimeKind.ClaudeCode]: {
|
|
75
124
|
kind: WorkerRuntimeKind.ClaudeCode,
|
|
@@ -77,6 +126,12 @@ export const RUNTIME_LAYOUT_PROFILES: Readonly<
|
|
|
77
126
|
contextFile: '/workspace/CLAUDE.md',
|
|
78
127
|
agentBundlesDir: '/workspace/.claude/agents',
|
|
79
128
|
commandsDir: '/workspace/.claude/commands',
|
|
129
|
+
// Claude Code custom slash-commands ARE markdown, under `.claude/commands`.
|
|
130
|
+
commandFormat: RuntimeCommandFormat.Markdown,
|
|
131
|
+
runtimeConfigFile: '/workspace/.claude/settings.json',
|
|
132
|
+
// Claude Code reads project-scoped MCP from a DEDICATED `.mcp.json` at the
|
|
133
|
+
// workspace root (SeparateFile) — NOT from `.claude/settings.json`.
|
|
134
|
+
mcpConfigFile: '/workspace/.mcp.json',
|
|
80
135
|
},
|
|
81
136
|
[WorkerRuntimeKind.Hermes]: {
|
|
82
137
|
kind: WorkerRuntimeKind.Hermes,
|
|
@@ -89,6 +144,11 @@ export const RUNTIME_LAYOUT_PROFILES: Readonly<
|
|
|
89
144
|
// is a portable `.md` surface, so the composer must not mount them as files.
|
|
90
145
|
agentBundlesDir: null,
|
|
91
146
|
commandsDir: null,
|
|
147
|
+
// No file-based slash-command surface at all — emit nothing.
|
|
148
|
+
commandFormat: RuntimeCommandFormat.None,
|
|
149
|
+
runtimeConfigFile: '/workspace/.hermes/config.json',
|
|
150
|
+
// Hermes reads its `mcpServers` map INSIDE `.hermes/config.json` (`InConfig`).
|
|
151
|
+
mcpConfigFile: null,
|
|
92
152
|
},
|
|
93
153
|
};
|
|
94
154
|
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// ── Workspace-sandbox protection contract ──
|
|
3
|
+
//
|
|
4
|
+
// A FIRST-CLASS, per-runtime security invariant for the agent-workspace sandbox.
|
|
5
|
+
// The agent runs a code CLI (OpenCode/Qwen/Claude/Hermes) inside the workspace
|
|
6
|
+
// pod with its OWN file tools (read/edit/write). Those tools MUST NOT be able to:
|
|
7
|
+
//
|
|
8
|
+
// (a) READ the mounted platform secrets — the runtime config file carries the
|
|
9
|
+
// LLM-gateway credential (a signed JWT) and/or merged MCP bearer headers,
|
|
10
|
+
// and a dedicated `.mcp.json` (Claude) carries per-server bearer headers.
|
|
11
|
+
// (b) MUTATE the agent's OWN system-instructions / agent-definition bundles —
|
|
12
|
+
// the auto-loaded context file (`AGENTS.md`/`CLAUDE.md`), the file-based
|
|
13
|
+
// sub-agent definitions, the Xema-private system overlay + injected
|
|
14
|
+
// instruction sections, and the `context.json` capability-authority spec
|
|
15
|
+
// (rewriting it would let the agent self-grant tools).
|
|
16
|
+
//
|
|
17
|
+
// This module DECLARES that protected-path set ONCE, derived from the kernel
|
|
18
|
+
// `RuntimeLayoutProfile` (the SSOT for every per-runtime path). Each runtime's
|
|
19
|
+
// config builder translates the neutral glob set into its NATIVE permission
|
|
20
|
+
// vocabulary (OpenCode `permission.read`/`permission.edit` globs; Claude Code
|
|
21
|
+
// `permissions.deny: ["Read(...)","Edit(...)"]`). Runtimes with no per-file
|
|
22
|
+
// permission surface (Qwen/Hermes) cannot consume the deny set and fall back to
|
|
23
|
+
// the pod's uid + per-org mount confinement — see the residual note in each
|
|
24
|
+
// builder. The contract is declared here regardless so the invariant is
|
|
25
|
+
// single-sourced, not scattered across builders.
|
|
26
|
+
//
|
|
27
|
+
// Globs are runtime-syntax-NEUTRAL absolute paths anchored at `/workspace`
|
|
28
|
+
// (`*` matches any character; `<dir>/**` covers a whole slot). Callers adapt
|
|
29
|
+
// them to their runtime's matcher.
|
|
30
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
31
|
+
|
|
32
|
+
import {
|
|
33
|
+
WorkspaceMount,
|
|
34
|
+
} from '../../agent-workspace/lib/workspace-layout';
|
|
35
|
+
|
|
36
|
+
import type { RuntimeLayoutProfile } from './runtime-layout-profile';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The resolved protected-path set for one runtime's workspace sandbox.
|
|
40
|
+
*
|
|
41
|
+
* - `secretReadDenyGlobs` — the agent's READ tool must DENY these (secrets). The
|
|
42
|
+
* agent may still read its own instructions/code; only credential-bearing
|
|
43
|
+
* files are denied.
|
|
44
|
+
* - `protectedEditDenyGlobs` — the agent's EDIT/WRITE tool must DENY these
|
|
45
|
+
* (own system-instructions, agent-definitions, authority spec, and — defense
|
|
46
|
+
* in depth — its own credential config). The agent may still edit workspace
|
|
47
|
+
* code, deliverables, repos, inputs, uploads, and tmp.
|
|
48
|
+
*
|
|
49
|
+
* Every entry is an absolute `/workspace/...` glob. Both sets are de-duplicated
|
|
50
|
+
* and stable-ordered.
|
|
51
|
+
*/
|
|
52
|
+
export interface WorkspaceSandboxProtection {
|
|
53
|
+
readonly secretReadDenyGlobs: readonly string[];
|
|
54
|
+
readonly protectedEditDenyGlobs: readonly string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Constant, runtime-AGNOSTIC Xema-private system-instruction + authority slots.
|
|
59
|
+
* These are excluded from the per-runtime `RuntimeLayoutProfile` on purpose (the
|
|
60
|
+
* profile only carries the slots whose ON-DISK path differs per runtime), so
|
|
61
|
+
* they are sourced from the `WorkspaceMount` SSOT here — never re-literalled.
|
|
62
|
+
*
|
|
63
|
+
* All are EDIT-denied (never mutate the injected system overlay / instructions /
|
|
64
|
+
* capability-authority). They are NOT read-denied: the runtime legitimately
|
|
65
|
+
* loads the overlay + instructions as context, and the plugin reads
|
|
66
|
+
* `context.json`.
|
|
67
|
+
*/
|
|
68
|
+
const XEMA_PRIVATE_INSTRUCTION_EDIT_GLOBS: readonly string[] = [
|
|
69
|
+
// /workspace/.xema/system-overlay.md — the injected system prompt overlay.
|
|
70
|
+
WorkspaceMount.SystemOverlay,
|
|
71
|
+
// /workspace/.xema/instructions/** — injected rule/policy instruction sections.
|
|
72
|
+
`${WorkspaceMount.Instructions}/**`,
|
|
73
|
+
// /workspace/context.json — carries authority.allowedXemaTools; rewriting it
|
|
74
|
+
// would let the agent escalate its own tool grants.
|
|
75
|
+
WorkspaceMount.ContextJson,
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
function dedupeStable(globs: readonly string[]): string[] {
|
|
79
|
+
return [...new Set(globs)];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Derive the workspace-sandbox protected-path set for a runtime, from its
|
|
84
|
+
* kernel `RuntimeLayoutProfile`. Pure — no I/O, no defaults that mask input.
|
|
85
|
+
*
|
|
86
|
+
* The set is CLOSED and fail-closed: a config file always exists (every runtime
|
|
87
|
+
* declares `runtimeConfigFile`), and every credential-bearing / instruction path
|
|
88
|
+
* the profile exposes is protected. A runtime with no file-based sub-agent
|
|
89
|
+
* surface (`agentBundlesDir === null`, e.g. Hermes) contributes no agent-def
|
|
90
|
+
* glob — there is no such directory to protect.
|
|
91
|
+
*/
|
|
92
|
+
export function deriveWorkspaceSandboxProtection(
|
|
93
|
+
profile: RuntimeLayoutProfile,
|
|
94
|
+
): WorkspaceSandboxProtection {
|
|
95
|
+
const configFile = profile.runtimeConfigFile;
|
|
96
|
+
const mcpFile = profile.mcpConfigFile;
|
|
97
|
+
|
|
98
|
+
// ── (a) Secret READ deny ─────────────────────────────────────────────────
|
|
99
|
+
// The credential-bearing config file + (if separate) the bearer-header MCP
|
|
100
|
+
// file. Nothing else is read-denied — the agent must still read its own
|
|
101
|
+
// instructions + workspace code.
|
|
102
|
+
const secretReadDenyGlobs: string[] = [configFile];
|
|
103
|
+
if (mcpFile !== null) {
|
|
104
|
+
secretReadDenyGlobs.push(mcpFile);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ── (b) Own-instruction / agent-def EDIT deny ────────────────────────────
|
|
108
|
+
const protectedEditDenyGlobs: string[] = [
|
|
109
|
+
// The auto-loaded context file (AGENTS.md / CLAUDE.md) IS the agent's own
|
|
110
|
+
// system instructions.
|
|
111
|
+
profile.contextFile,
|
|
112
|
+
// Defense in depth: never let the agent rewrite its own credential config
|
|
113
|
+
// (e.g. to strip its own deny rules or exfiltrate the token).
|
|
114
|
+
configFile,
|
|
115
|
+
...XEMA_PRIVATE_INSTRUCTION_EDIT_GLOBS,
|
|
116
|
+
];
|
|
117
|
+
if (mcpFile !== null) {
|
|
118
|
+
protectedEditDenyGlobs.push(mcpFile);
|
|
119
|
+
}
|
|
120
|
+
if (profile.agentBundlesDir !== null) {
|
|
121
|
+
// The whole file-based sub-agent-definition slot.
|
|
122
|
+
protectedEditDenyGlobs.push(`${profile.agentBundlesDir}/**`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
secretReadDenyGlobs: dedupeStable(secretReadDenyGlobs),
|
|
127
|
+
protectedEditDenyGlobs: dedupeStable(protectedEditDenyGlobs),
|
|
128
|
+
};
|
|
129
|
+
}
|
package/src/workflow/index.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
9
9
|
|
|
10
10
|
export * from './lib/enums';
|
|
11
|
+
export * from './lib/workflow-control';
|
|
11
12
|
export * from './lib/temporal-namespace';
|
|
12
13
|
export * from './lib/platform-task-queue';
|
|
13
14
|
export * from './lib/errors';
|
|
@@ -263,6 +263,14 @@ export enum BuiltInActionId {
|
|
|
263
263
|
HTTP = 'xema/http',
|
|
264
264
|
WAIT = 'xema/wait',
|
|
265
265
|
EMIT_ARTIFACT = 'xema/emit-artifact',
|
|
266
|
+
/**
|
|
267
|
+
* Pull a declared external ingestion source into its local catalog — the
|
|
268
|
+
* generic runtime counterpart of a `ContributionKind.IngestionSource`
|
|
269
|
+
* declaration. Any sub-app that materializes an external dataset on a cadence
|
|
270
|
+
* dispatches this action; the caller resolves which source to pull. The
|
|
271
|
+
* implementation ships later in workflow-runtime-worker.
|
|
272
|
+
*/
|
|
273
|
+
INGEST_SOURCE = 'xema/ingest-source',
|
|
266
274
|
ENDPOINT_FETCH = 'xema/endpoint-fetch',
|
|
267
275
|
SCM_POST_REVIEW = 'software-dev/scm-post-review',
|
|
268
276
|
DISPATCH_WORKFLOW = 'xema/dispatch-workflow',
|
|
@@ -64,6 +64,51 @@ export const ModelClassSchema = z.enum(
|
|
|
64
64
|
MODEL_CLASS_VALUES as readonly [ModelClass, ...ModelClass[]],
|
|
65
65
|
);
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* A model's INTRINSIC capability / modality — what a concrete model can do
|
|
69
|
+
* regardless of which routing tier ({@link ModelClass}) selected it. The
|
|
70
|
+
* SINGLE source of truth for the closed set
|
|
71
|
+
* `vision | long_context | reasoning | function_calling | audio | embedding`.
|
|
72
|
+
*
|
|
73
|
+
* Consumed as a first-class dimension by the Model Resolution Matrix
|
|
74
|
+
* (`ResolutionDimension.ModelCapability` in `@xemahq/kernel-contracts/agent-composition`),
|
|
75
|
+
* so an org can route "any invocation that needs vision" to a capable model
|
|
76
|
+
* without pinning a provider. Domain-agnostic: describes the model, never a
|
|
77
|
+
* business use-case.
|
|
78
|
+
*/
|
|
79
|
+
export enum ModelCapability {
|
|
80
|
+
/** Accepts image input (multimodal vision). */
|
|
81
|
+
VISION = 'vision',
|
|
82
|
+
/** Supports an extended context window beyond the baseline tier. */
|
|
83
|
+
LONG_CONTEXT = 'long_context',
|
|
84
|
+
/** Exposes explicit step-by-step / extended-thinking reasoning. */
|
|
85
|
+
REASONING = 'reasoning',
|
|
86
|
+
/** Supports structured tool / function calling. */
|
|
87
|
+
FUNCTION_CALLING = 'function_calling',
|
|
88
|
+
/** Accepts or produces audio. */
|
|
89
|
+
AUDIO = 'audio',
|
|
90
|
+
/** Produces embedding vectors (retrieval / similarity). */
|
|
91
|
+
EMBEDDING = 'embedding',
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const MODEL_CAPABILITY_VALUES: readonly ModelCapability[] = [
|
|
95
|
+
ModelCapability.VISION,
|
|
96
|
+
ModelCapability.LONG_CONTEXT,
|
|
97
|
+
ModelCapability.REASONING,
|
|
98
|
+
ModelCapability.FUNCTION_CALLING,
|
|
99
|
+
ModelCapability.AUDIO,
|
|
100
|
+
ModelCapability.EMBEDDING,
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Zod schema for {@link ModelCapability}. Built from the enum so the literal
|
|
105
|
+
* list lives in exactly one place. Use this in any YAML/wire schema that
|
|
106
|
+
* needs to validate a model-capability field.
|
|
107
|
+
*/
|
|
108
|
+
export const ModelCapabilitySchema = z.enum(
|
|
109
|
+
MODEL_CAPABILITY_VALUES as readonly [ModelCapability, ...ModelCapability[]],
|
|
110
|
+
);
|
|
111
|
+
|
|
67
112
|
export enum ModelRefKind {
|
|
68
113
|
CONCRETE = 'concrete',
|
|
69
114
|
STRATEGY = 'strategy',
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// ── Governed Workflow-Control Contracts ──
|
|
3
|
+
//
|
|
4
|
+
// Wire contracts for the least-privilege "workflow-control" surface: an
|
|
5
|
+
// in-session agent invoking its OWN governing workflow run's control signals
|
|
6
|
+
// (decide a decision gate, answer a pending inquiry, change run status) as a
|
|
7
|
+
// governed tool.
|
|
8
|
+
//
|
|
9
|
+
// The agent NEVER supplies the target run or org — those are server-derived
|
|
10
|
+
// from the verified session/token and injected by the mcp-gateway bridge into
|
|
11
|
+
// `runId` / `orgId`. This file carries the closed action set, the settable
|
|
12
|
+
// run-status subset, the typed rejection codes, the engine-internal request
|
|
13
|
+
// shape, and the first-class structured accepted/rejected RESULT.
|
|
14
|
+
//
|
|
15
|
+
// The RESULT is deliberately a structured discriminated union (never an opaque
|
|
16
|
+
// 4xx) so the calling model reads a typed outcome rather than mis-parsing an
|
|
17
|
+
// HTTP error. The engine-internal control endpoint returns HTTP 200 carrying
|
|
18
|
+
// this shape for every BUSINESS outcome (accepted or rejected); only genuine
|
|
19
|
+
// server faults surface as non-2xx.
|
|
20
|
+
//
|
|
21
|
+
// Shared between workflow-engine-api (the control surface) and mcp-gateway-api
|
|
22
|
+
// (the governed tool). Runtime-framework-agnostic — no NestJS, no Prisma.
|
|
23
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
24
|
+
|
|
25
|
+
import { z } from 'zod';
|
|
26
|
+
|
|
27
|
+
import { DecisionVerdict } from '../../decision-gate/lib/decision-gate';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The closed set of control actions an agent may take against its own
|
|
31
|
+
* governing run. Reuses the platform's existing signal/gate/inquiry
|
|
32
|
+
* mechanisms — no second control path.
|
|
33
|
+
*
|
|
34
|
+
* - `DECIDE_GATE` — submit a decision-gate verdict (approve/reject/abstain)
|
|
35
|
+
* on a `DECISION_GATE` inquiry belonging to the run.
|
|
36
|
+
* - `ANSWER_INQUIRY` — submit a reply to a pending non-gate inquiry
|
|
37
|
+
* (`AGENT_TOOL_INQUIRY` / `REVIEW`) belonging to the run.
|
|
38
|
+
* - `SET_RUN_STATUS` — request a run-status transition (pause / resume /
|
|
39
|
+
* cancel) via the existing run signal.
|
|
40
|
+
*
|
|
41
|
+
* Phase refine/restart is intentionally absent: the engine ships no
|
|
42
|
+
* server-side phase-refinement mechanism to reuse today (the run "redo"
|
|
43
|
+
* affordance is UI-metadata only). Adding it is a coordinated engine+worker
|
|
44
|
+
* change plus a member here — not a silent no-op action.
|
|
45
|
+
*/
|
|
46
|
+
export enum WorkflowControlAction {
|
|
47
|
+
DECIDE_GATE = 'decide_gate',
|
|
48
|
+
ANSWER_INQUIRY = 'answer_inquiry',
|
|
49
|
+
SET_RUN_STATUS = 'set_run_status',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The settable run-status transitions an agent may request. A closed subset of
|
|
54
|
+
* `WorkflowRunStatus` — only the values that map 1:1 onto an existing run
|
|
55
|
+
* signal. The engine translates each to the corresponding signal:
|
|
56
|
+
*
|
|
57
|
+
* PAUSED → `pauseRun`
|
|
58
|
+
* RUNNING → `resumeRun`
|
|
59
|
+
* CANCELLED → `cancelRun`
|
|
60
|
+
*
|
|
61
|
+
* Terminal statuses (succeeded/failed/…) are NOT settable — a run reaches
|
|
62
|
+
* those only through its own execution, never an agent control signal.
|
|
63
|
+
*/
|
|
64
|
+
export enum WorkflowRunStatusTarget {
|
|
65
|
+
PAUSED = 'paused',
|
|
66
|
+
RUNNING = 'running',
|
|
67
|
+
CANCELLED = 'cancelled',
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Typed rejection codes for the structured control RESULT. Closed set — the
|
|
72
|
+
* calling model branches on these, so free-form strings are forbidden. Each
|
|
73
|
+
* corresponds to a pre-validation against live run/gate/inquiry state.
|
|
74
|
+
*/
|
|
75
|
+
export enum WorkflowControlRejectionCode {
|
|
76
|
+
/** The calling session is not bound to any governing workflow run. */
|
|
77
|
+
NO_GOVERNING_RUN = 'NO_GOVERNING_RUN',
|
|
78
|
+
/** The request failed contract validation before any state was touched. */
|
|
79
|
+
INVALID_REQUEST = 'INVALID_REQUEST',
|
|
80
|
+
/** The bound run does not exist for the session's org (uniform anti-probe). */
|
|
81
|
+
RUN_NOT_FOUND = 'RUN_NOT_FOUND',
|
|
82
|
+
/** The run is in a terminal state and accepts no control signal. */
|
|
83
|
+
RUN_ALREADY_TERMINAL = 'RUN_ALREADY_TERMINAL',
|
|
84
|
+
/** The run's execution rejected the requested signal. */
|
|
85
|
+
RUN_SIGNAL_REJECTED = 'RUN_SIGNAL_REJECTED',
|
|
86
|
+
/** The referenced inquiry does not exist for the session's org. */
|
|
87
|
+
INQUIRY_NOT_FOUND = 'INQUIRY_NOT_FOUND',
|
|
88
|
+
/** The referenced inquiry does not belong to the session's governing run. */
|
|
89
|
+
INQUIRY_NOT_ON_RUN = 'INQUIRY_NOT_ON_RUN',
|
|
90
|
+
/** The inquiry kind does not match the action (gate vs non-gate). */
|
|
91
|
+
INQUIRY_WRONG_KIND = 'INQUIRY_WRONG_KIND',
|
|
92
|
+
/** The gate is no longer pending and accepts no further verdicts. */
|
|
93
|
+
GATE_NOT_PENDING = 'GATE_NOT_PENDING',
|
|
94
|
+
/** The inquiry is no longer pending and accepts no further replies. */
|
|
95
|
+
INQUIRY_NOT_PENDING = 'INQUIRY_NOT_PENDING',
|
|
96
|
+
/** No recipient with the given id exists on the inquiry. */
|
|
97
|
+
RECIPIENT_NOT_FOUND = 'RECIPIENT_NOT_FOUND',
|
|
98
|
+
/** The targeted recipient is not an AGENT recipient — an agent tool may
|
|
99
|
+
* only reply as an agent recipient, never forge a human/endpoint verdict. */
|
|
100
|
+
RECIPIENT_NOT_AGENT = 'RECIPIENT_NOT_AGENT',
|
|
101
|
+
/** The targeted recipient has already replied (idempotent-safe rejection). */
|
|
102
|
+
RECIPIENT_ALREADY_REPLIED = 'RECIPIENT_ALREADY_REPLIED',
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ─── Request (engine-internal wire shape) ────────────────────────────────────
|
|
106
|
+
//
|
|
107
|
+
// `runId` + `orgId` are the SERVER-derived tenancy/subject binding injected by
|
|
108
|
+
// the gateway from the verified session token — they are part of the wire
|
|
109
|
+
// shape the engine validates, but the AGENT never supplies them.
|
|
110
|
+
|
|
111
|
+
const RunBindingShape = {
|
|
112
|
+
/** The governing run, injected server-side from the session — not agent input. */
|
|
113
|
+
runId: z.string().min(1),
|
|
114
|
+
/** The session's org, injected server-side from the token — not agent input. */
|
|
115
|
+
orgId: z.string().min(1),
|
|
116
|
+
} as const;
|
|
117
|
+
|
|
118
|
+
const DecideGateRequestSchema = z.object({
|
|
119
|
+
...RunBindingShape,
|
|
120
|
+
action: z.literal(WorkflowControlAction.DECIDE_GATE),
|
|
121
|
+
/** The DECISION_GATE inquiry to decide (must belong to the governing run). */
|
|
122
|
+
inquiryId: z.string().min(1),
|
|
123
|
+
/** The agent recipient row on the inquiry the caller replies AS. */
|
|
124
|
+
recipientId: z.string().min(1),
|
|
125
|
+
verdict: z.nativeEnum(DecisionVerdict),
|
|
126
|
+
/** Required (non-empty) when verdict is REJECT — enforced downstream by the
|
|
127
|
+
* reused `DecisionGateReplySchema`. */
|
|
128
|
+
reason: z.string().optional(),
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const AnswerInquiryRequestSchema = z.object({
|
|
132
|
+
...RunBindingShape,
|
|
133
|
+
action: z.literal(WorkflowControlAction.ANSWER_INQUIRY),
|
|
134
|
+
/** The non-gate inquiry to answer (must belong to the governing run). */
|
|
135
|
+
inquiryId: z.string().min(1),
|
|
136
|
+
/** The agent recipient row on the inquiry the caller replies AS. */
|
|
137
|
+
recipientId: z.string().min(1),
|
|
138
|
+
/** Kind-specific reply payload — validated by the inquiry kind's own schema. */
|
|
139
|
+
payload: z.record(z.string(), z.unknown()),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const SetRunStatusRequestSchema = z.object({
|
|
143
|
+
...RunBindingShape,
|
|
144
|
+
action: z.literal(WorkflowControlAction.SET_RUN_STATUS),
|
|
145
|
+
status: z.nativeEnum(WorkflowRunStatusTarget),
|
|
146
|
+
reason: z.string().optional(),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The engine-internal control request. Discriminated on `action`; every member
|
|
151
|
+
* carries the server-derived `runId` + `orgId` binding.
|
|
152
|
+
*/
|
|
153
|
+
export const WorkflowControlRequestSchema = z.discriminatedUnion('action', [
|
|
154
|
+
DecideGateRequestSchema,
|
|
155
|
+
AnswerInquiryRequestSchema,
|
|
156
|
+
SetRunStatusRequestSchema,
|
|
157
|
+
]);
|
|
158
|
+
export type WorkflowControlRequest = z.infer<typeof WorkflowControlRequestSchema>;
|
|
159
|
+
|
|
160
|
+
export type DecideGateRequest = z.infer<typeof DecideGateRequestSchema>;
|
|
161
|
+
export type AnswerInquiryRequest = z.infer<typeof AnswerInquiryRequestSchema>;
|
|
162
|
+
export type SetRunStatusRequest = z.infer<typeof SetRunStatusRequestSchema>;
|
|
163
|
+
|
|
164
|
+
// ─── Result (first-class structured accepted/rejected outcome) ───────────────
|
|
165
|
+
|
|
166
|
+
export const WorkflowControlResultSchema = z.discriminatedUnion('ok', [
|
|
167
|
+
z.object({
|
|
168
|
+
ok: z.literal(true),
|
|
169
|
+
/** Optional human-readable note (e.g. which signal was sent). */
|
|
170
|
+
detail: z.string().optional(),
|
|
171
|
+
}),
|
|
172
|
+
z.object({
|
|
173
|
+
ok: z.literal(false),
|
|
174
|
+
code: z.nativeEnum(WorkflowControlRejectionCode),
|
|
175
|
+
message: z.string().min(1),
|
|
176
|
+
}),
|
|
177
|
+
]);
|
|
178
|
+
export type WorkflowControlResult = z.infer<typeof WorkflowControlResultSchema>;
|
|
179
|
+
|
|
180
|
+
/** Construct an accepted result. */
|
|
181
|
+
export function workflowControlAccepted(detail?: string): WorkflowControlResult {
|
|
182
|
+
return detail === undefined ? { ok: true } : { ok: true, detail };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Construct a typed rejection result. */
|
|
186
|
+
export function workflowControlRejected(
|
|
187
|
+
code: WorkflowControlRejectionCode,
|
|
188
|
+
message: string,
|
|
189
|
+
): WorkflowControlResult {
|
|
190
|
+
return { ok: false, code, message };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The canonical capability ref governing this surface. `workflow` is an
|
|
195
|
+
* existing first-party capability domain; the router dispatches this ref to the
|
|
196
|
+
* engine's internal control endpoint and authorization-api gates it by grant.
|
|
197
|
+
*/
|
|
198
|
+
export const WORKFLOW_CONTROL_CAPABILITY_REF = 'workflow:control@1';
|