@veil-runtime/core 0.1.1 → 0.1.3
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/README.md +103 -69
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -1
- package/dist/integrations/mcp/inbound/capability-schema.d.ts +3 -0
- package/dist/integrations/mcp/inbound/capability-schema.js +73 -0
- package/dist/integrations/mcp/inbound/mcp-adapter.d.ts +8 -0
- package/dist/integrations/mcp/inbound/mcp-adapter.js +97 -0
- package/dist/runtime/jobs/job-event.d.ts +1 -1
- package/dist/runtime/jobs/job-manager.d.ts +3 -2
- package/dist/runtime/jobs/job-manager.js +40 -15
- package/dist/runtime/operator-runtime.d.ts +6 -0
- package/dist/runtime/operator-runtime.js +8 -1
- package/dist/runtime/permissions/execution-authorizer.d.ts +23 -0
- package/dist/runtime/permissions/execution-authorizer.js +16 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -353,67 +353,101 @@ Optional:
|
|
|
353
353
|
|
|
354
354
|
---
|
|
355
355
|
|
|
356
|
-
## Installation
|
|
357
|
-
|
|
358
|
-
Install the public runtime package in an application:
|
|
359
|
-
|
|
360
|
-
```bash
|
|
361
|
-
npm install @veil-runtime/core
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
Create and execute a capability through the public API:
|
|
365
|
-
|
|
366
|
-
```ts
|
|
367
|
-
import {
|
|
368
|
-
createCapability,
|
|
369
|
-
OperatorRuntime,
|
|
370
|
-
type CapabilityModule,
|
|
371
|
-
type ExecutionPlan,
|
|
372
|
-
} from '@veil-runtime/core';
|
|
373
|
-
|
|
374
|
-
const echo = createCapability<{ value: string }, string>({
|
|
375
|
-
name: 'example.echo',
|
|
376
|
-
version: '1.0.0',
|
|
377
|
-
description: 'Return the provided value',
|
|
378
|
-
risk: 'read',
|
|
379
|
-
async execute({ input }) {
|
|
380
|
-
return input.value;
|
|
381
|
-
},
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
const module: CapabilityModule = {
|
|
385
|
-
manifest: {
|
|
386
|
-
name: 'example',
|
|
387
|
-
version: '1.0.0',
|
|
388
|
-
capabilities: [echo.name],
|
|
389
|
-
},
|
|
390
|
-
capabilities: [echo],
|
|
391
|
-
};
|
|
392
|
-
|
|
393
|
-
const plan: ExecutionPlan = {
|
|
394
|
-
version: '1.0',
|
|
395
|
-
steps: [{
|
|
396
|
-
id: 'echo',
|
|
397
|
-
capability: echo.name,
|
|
398
|
-
capabilityVersion: echo.version,
|
|
399
|
-
input: { value: 'Hello from Veil' },
|
|
400
|
-
}],
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
const runtime = new OperatorRuntime();
|
|
404
|
-
runtime.use(module);
|
|
405
|
-
|
|
406
|
-
const job = await runtime.executePlan(plan);
|
|
407
|
-
console.log(job.status, job.result);
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
This public-package usage is compile- and execution-verified by the release
|
|
411
|
-
consumer fixture. Internal `src`, `dist`, registry, provider, and storage
|
|
412
|
-
subpaths are intentionally unavailable.
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
356
|
+
## Installation
|
|
357
|
+
|
|
358
|
+
Install the public runtime package in an application:
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
npm install @veil-runtime/core
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Create and execute a capability through the public API:
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
import {
|
|
368
|
+
createCapability,
|
|
369
|
+
OperatorRuntime,
|
|
370
|
+
type CapabilityModule,
|
|
371
|
+
type ExecutionPlan,
|
|
372
|
+
} from '@veil-runtime/core';
|
|
373
|
+
|
|
374
|
+
const echo = createCapability<{ value: string }, string>({
|
|
375
|
+
name: 'example.echo',
|
|
376
|
+
version: '1.0.0',
|
|
377
|
+
description: 'Return the provided value',
|
|
378
|
+
risk: 'read',
|
|
379
|
+
async execute({ input }) {
|
|
380
|
+
return input.value;
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
const module: CapabilityModule = {
|
|
385
|
+
manifest: {
|
|
386
|
+
name: 'example',
|
|
387
|
+
version: '1.0.0',
|
|
388
|
+
capabilities: [echo.name],
|
|
389
|
+
},
|
|
390
|
+
capabilities: [echo],
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const plan: ExecutionPlan = {
|
|
394
|
+
version: '1.0',
|
|
395
|
+
steps: [{
|
|
396
|
+
id: 'echo',
|
|
397
|
+
capability: echo.name,
|
|
398
|
+
capabilityVersion: echo.version,
|
|
399
|
+
input: { value: 'Hello from Veil' },
|
|
400
|
+
}],
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
const runtime = new OperatorRuntime();
|
|
404
|
+
runtime.use(module);
|
|
405
|
+
|
|
406
|
+
const job = await runtime.executePlan(plan);
|
|
407
|
+
console.log(job.status, job.result);
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
This public-package usage is compile- and execution-verified by the release
|
|
411
|
+
consumer fixture. Internal `src`, `dist`, registry, provider, and storage
|
|
412
|
+
subpaths are intentionally unavailable.
|
|
413
|
+
|
|
414
|
+
### Contextual execution authorization
|
|
415
|
+
|
|
416
|
+
An application may supply a runtime-scoped authorizer. Veil resolves and
|
|
417
|
+
validates the step input before invoking it, and does not start a denied
|
|
418
|
+
capability:
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
import {
|
|
422
|
+
OperatorRuntime,
|
|
423
|
+
type ExecutionAuthorizer,
|
|
424
|
+
} from '@veil-runtime/core';
|
|
425
|
+
|
|
426
|
+
const authorizer: ExecutionAuthorizer = {
|
|
427
|
+
async authorize({ capability, input }) {
|
|
428
|
+
if (
|
|
429
|
+
capability.name === 'deploy.trigger' &&
|
|
430
|
+
typeof input === 'object' &&
|
|
431
|
+
input !== null &&
|
|
432
|
+
'environment' in input &&
|
|
433
|
+
input.environment === 'production'
|
|
434
|
+
) {
|
|
435
|
+
return {
|
|
436
|
+
decision: 'deny',
|
|
437
|
+
reason: 'Production deployment is not allowed.',
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return { decision: 'allow' };
|
|
442
|
+
},
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
const runtime = new OperatorRuntime({ authorizer });
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## Repository development
|
|
449
|
+
|
|
450
|
+
Clone the repository:
|
|
417
451
|
|
|
418
452
|
```bash
|
|
419
453
|
git clone git@github.com:veil-runtime/veil.git
|
|
@@ -478,11 +512,11 @@ Veil will:
|
|
|
478
512
|
3. Generate an `ExecutionPlan`.
|
|
479
513
|
4. Execute the plan through `OperatorRuntime`.
|
|
480
514
|
5. Return the completed job.
|
|
481
|
-
|
|
482
|
-
---
|
|
483
|
-
|
|
484
|
-
# License and branding
|
|
485
|
-
|
|
486
|
-
Veil Core is licensed under Apache-2.0. Copyright 2026 Mustapha Keraan.
|
|
487
|
-
Veil names, logos, and branding are governed separately; see
|
|
488
|
-
[TRADEMARKS.md](TRADEMARKS.md) for the brand policy.
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
# License and branding
|
|
519
|
+
|
|
520
|
+
Veil Core is licensed under Apache-2.0. Copyright 2026 Mustapha Keraan.
|
|
521
|
+
Veil names, logos, and branding are governed separately; see
|
|
522
|
+
[TRADEMARKS.md](TRADEMARKS.md) for the brand policy.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { OperatorRuntime, operatorRuntime } from './runtime/operator-runtime.js';
|
|
2
2
|
export type { ExecutePlanOptions, RunJobOptions } from './runtime/operator-runtime.js';
|
|
3
|
+
export type { OperatorRuntimeOptions } from './runtime/operator-runtime.js';
|
|
4
|
+
export type { CapabilityAuthorizationContext, CapabilityAuthorizationDecision, ExecutionAuthorizer, } from './runtime/permissions/execution-authorizer.js';
|
|
3
5
|
export type { ExecutionPlan, ExecutionStep, Planner, PlannerContext, ResultReference, } from './runtime/planner/planner.js';
|
|
4
6
|
export type { ExecutionCaller, ExecutionContext } from './runtime/execution/execution-context.js';
|
|
5
7
|
export type { Capability, CapabilityInputField, CapabilityRisk, } from './runtime/registry/capability.js';
|
|
@@ -10,4 +12,5 @@ export type { JobStep, JobStepStatus } from './runtime/jobs/job-step.js';
|
|
|
10
12
|
export type { JobStatus } from './runtime/jobs/job-status.js';
|
|
11
13
|
export type { JobEvent } from './runtime/jobs/job-event.js';
|
|
12
14
|
export type { RuntimeEvent } from './runtime/events/runtime-event.js';
|
|
15
|
+
export { McpAdapter } from './integrations/mcp/inbound/mcp-adapter.js';
|
|
13
16
|
export * from './sdk/index.js';
|
package/dist/index.js
CHANGED
|
@@ -14,8 +14,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.operatorRuntime = exports.OperatorRuntime = void 0;
|
|
17
|
+
exports.McpAdapter = exports.operatorRuntime = exports.OperatorRuntime = void 0;
|
|
18
18
|
var operator_runtime_js_1 = require("./runtime/operator-runtime.js");
|
|
19
19
|
Object.defineProperty(exports, "OperatorRuntime", { enumerable: true, get: function () { return operator_runtime_js_1.OperatorRuntime; } });
|
|
20
20
|
Object.defineProperty(exports, "operatorRuntime", { enumerable: true, get: function () { return operator_runtime_js_1.operatorRuntime; } });
|
|
21
|
+
var mcp_adapter_js_1 = require("./integrations/mcp/inbound/mcp-adapter.js");
|
|
22
|
+
Object.defineProperty(exports, "McpAdapter", { enumerable: true, get: function () { return mcp_adapter_js_1.McpAdapter; } });
|
|
21
23
|
__exportStar(require("./sdk/index.js"), exports);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.createMcpInputSchema = createMcpInputSchema;
|
|
37
|
+
const z = __importStar(require("zod/v4"));
|
|
38
|
+
function createFieldSchema(field) {
|
|
39
|
+
let schema;
|
|
40
|
+
switch (field.type) {
|
|
41
|
+
case 'string':
|
|
42
|
+
schema = z.string();
|
|
43
|
+
break;
|
|
44
|
+
case 'number':
|
|
45
|
+
schema = z.number();
|
|
46
|
+
break;
|
|
47
|
+
case 'boolean':
|
|
48
|
+
schema = z.boolean();
|
|
49
|
+
break;
|
|
50
|
+
case 'array':
|
|
51
|
+
schema = z.array(z.unknown());
|
|
52
|
+
break;
|
|
53
|
+
case 'object':
|
|
54
|
+
schema = z.record(z.string(), z.unknown());
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
schema = z.unknown();
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
if (field.description) {
|
|
61
|
+
schema = schema.describe(field.description);
|
|
62
|
+
}
|
|
63
|
+
if (!field.required) {
|
|
64
|
+
schema = schema.optional();
|
|
65
|
+
}
|
|
66
|
+
return schema;
|
|
67
|
+
}
|
|
68
|
+
function createMcpInputSchema(inputSchema) {
|
|
69
|
+
return Object.fromEntries(Object.entries(inputSchema).map(([name, field]) => [
|
|
70
|
+
name,
|
|
71
|
+
createFieldSchema(field),
|
|
72
|
+
]));
|
|
73
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { OperatorRuntime } from '../../../runtime/operator-runtime.js';
|
|
3
|
+
export declare class McpAdapter {
|
|
4
|
+
private readonly runtime;
|
|
5
|
+
readonly server: McpServer;
|
|
6
|
+
constructor(runtime: OperatorRuntime);
|
|
7
|
+
private registerCapabilities;
|
|
8
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.McpAdapter = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
6
|
+
const capability_schema_js_1 = require("./capability-schema.js");
|
|
7
|
+
class McpAdapter {
|
|
8
|
+
runtime;
|
|
9
|
+
server;
|
|
10
|
+
constructor(runtime) {
|
|
11
|
+
this.runtime = runtime;
|
|
12
|
+
this.server =
|
|
13
|
+
new mcp_js_1.McpServer({
|
|
14
|
+
name: 'veil',
|
|
15
|
+
version: '0.1.0',
|
|
16
|
+
});
|
|
17
|
+
this.registerCapabilities();
|
|
18
|
+
}
|
|
19
|
+
registerCapabilities() {
|
|
20
|
+
const capabilities = this.runtime.listCapabilities();
|
|
21
|
+
for (const capability of capabilities) {
|
|
22
|
+
this.server.registerTool(capability.name, {
|
|
23
|
+
description: capability.description,
|
|
24
|
+
inputSchema: (0, capability_schema_js_1.createMcpInputSchema)(capability.inputSchema),
|
|
25
|
+
}, async (input) => {
|
|
26
|
+
try {
|
|
27
|
+
const plan = {
|
|
28
|
+
version: '1.0',
|
|
29
|
+
goal: `Execute MCP tool ${capability.name}`,
|
|
30
|
+
metadata: {
|
|
31
|
+
source: 'mcp',
|
|
32
|
+
tool: capability.name,
|
|
33
|
+
},
|
|
34
|
+
steps: [
|
|
35
|
+
{
|
|
36
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
37
|
+
capability: capability.name,
|
|
38
|
+
input,
|
|
39
|
+
reason: 'Requested through MCP',
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
const job = await this.runtime.executePlan(plan);
|
|
44
|
+
const step = job.steps[0];
|
|
45
|
+
if (!step) {
|
|
46
|
+
return {
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: 'text',
|
|
50
|
+
text: 'Veil execution completed without a job step result',
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
isError: true,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (step.status ===
|
|
57
|
+
'failed') {
|
|
58
|
+
return {
|
|
59
|
+
content: [
|
|
60
|
+
{
|
|
61
|
+
type: 'text',
|
|
62
|
+
text: step.error ??
|
|
63
|
+
'Veil capability execution failed',
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
isError: true,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
content: [
|
|
71
|
+
{
|
|
72
|
+
type: 'text',
|
|
73
|
+
text: JSON.stringify(step.result ??
|
|
74
|
+
null, null, 2),
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
const message = error instanceof Error
|
|
81
|
+
? error.message
|
|
82
|
+
: 'Unknown Veil execution error';
|
|
83
|
+
return {
|
|
84
|
+
content: [
|
|
85
|
+
{
|
|
86
|
+
type: 'text',
|
|
87
|
+
text: message,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
isError: true,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.McpAdapter = McpAdapter;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type JobEventType = 'job.created' | 'planning.started' | 'planning.completed' | 'approval.requested' | 'approval.granted' | 'execution.started' | 'capability.started' | 'capability.completed' | 'capability.failed' | 'job.completed' | 'job.failed' | 'job.cancelled' | 'job.reviewed';
|
|
1
|
+
export type JobEventType = 'job.created' | 'planning.started' | 'planning.completed' | 'approval.requested' | 'approval.granted' | 'execution.started' | 'capability.denied' | 'capability.started' | 'capability.completed' | 'capability.failed' | 'job.completed' | 'job.failed' | 'job.cancelled' | 'job.reviewed';
|
|
2
2
|
export interface JobEvent {
|
|
3
3
|
id: string;
|
|
4
4
|
jobId: string;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
import { ExecutionAuthorizer } from '../permissions/execution-authorizer.js';
|
|
1
2
|
import { ExecutionPlan } from '../planner/planner.js';
|
|
2
3
|
import { ExecutionCaller } from '../execution/execution-context.js';
|
|
3
4
|
import { Job, JobOutcome } from './job.js';
|
|
4
5
|
import { JobStatus } from './job-status.js';
|
|
5
6
|
import { JobListFilter } from './job-store.js';
|
|
6
7
|
declare class JobManager {
|
|
7
|
-
executePlan(plan: ExecutionPlan, caller?: ExecutionCaller): Promise<Job>;
|
|
8
|
+
executePlan(plan: ExecutionPlan, caller?: ExecutionCaller, authorizer?: ExecutionAuthorizer): Promise<Job>;
|
|
8
9
|
create(goal: string, planner?: string): Promise<Job>;
|
|
9
10
|
get(id: string): Promise<Job | undefined>;
|
|
10
11
|
list(filter?: JobListFilter): Promise<Job[]>;
|
|
11
12
|
setStatus(id: string, status: JobStatus): Promise<Job>;
|
|
12
|
-
execute(id: string, caller?: ExecutionCaller): Promise<Job>;
|
|
13
|
+
execute(id: string, caller?: ExecutionCaller, authorizer?: ExecutionAuthorizer): Promise<Job>;
|
|
13
14
|
review(id: string, outcome: JobOutcome, notes?: string): Promise<Job>;
|
|
14
15
|
private addEvent;
|
|
15
16
|
}
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.jobManager = void 0;
|
|
4
4
|
const node_crypto_1 = require("node:crypto");
|
|
5
5
|
const registry_js_1 = require("../registry/registry.js");
|
|
6
|
-
const
|
|
6
|
+
const execution_authorizer_js_1 = require("../permissions/execution-authorizer.js");
|
|
7
7
|
const plan_validator_js_1 = require("../execution/plan-validator.js");
|
|
8
8
|
const result_reference_js_1 = require("../execution/result-reference.js");
|
|
9
9
|
const memory_event_bus_js_1 = require("../events/memory-event-bus.js");
|
|
@@ -13,7 +13,7 @@ const console_log_sink_js_1 = require("../logging/console-log-sink.js");
|
|
|
13
13
|
const composite_log_sink_js_1 = require("../logging/composite-log-sink.js");
|
|
14
14
|
const sqlite_log_sink_js_1 = require("../logging/sqlite-log-sink.js");
|
|
15
15
|
class JobManager {
|
|
16
|
-
async executePlan(plan, caller) {
|
|
16
|
+
async executePlan(plan, caller, authorizer = execution_authorizer_js_1.defaultExecutionAuthorizer) {
|
|
17
17
|
if (!plan.steps.length) {
|
|
18
18
|
throw new Error('Execution plan contains no steps');
|
|
19
19
|
}
|
|
@@ -35,7 +35,7 @@ class JobManager {
|
|
|
35
35
|
job.updatedAt =
|
|
36
36
|
new Date().toISOString();
|
|
37
37
|
await job_store_js_1.jobStore.update(job);
|
|
38
|
-
return this.execute(job.id, caller);
|
|
38
|
+
return this.execute(job.id, caller, authorizer);
|
|
39
39
|
}
|
|
40
40
|
async create(goal, planner) {
|
|
41
41
|
if (!goal?.trim()) {
|
|
@@ -74,7 +74,7 @@ class JobManager {
|
|
|
74
74
|
await job_store_js_1.jobStore.update(job);
|
|
75
75
|
return job;
|
|
76
76
|
}
|
|
77
|
-
async execute(id, caller) {
|
|
77
|
+
async execute(id, caller, authorizer = execution_authorizer_js_1.defaultExecutionAuthorizer) {
|
|
78
78
|
const job = await job_store_js_1.jobStore.get(id);
|
|
79
79
|
if (!job) {
|
|
80
80
|
throw new Error(`Job not found: ${id}`);
|
|
@@ -92,17 +92,6 @@ class JobManager {
|
|
|
92
92
|
if (!capability) {
|
|
93
93
|
throw new Error(`Capability not found: ${step.capability}`);
|
|
94
94
|
}
|
|
95
|
-
const permission = (0, permissions_js_1.checkPermission)(capability.risk);
|
|
96
|
-
if (!permission.allowed) {
|
|
97
|
-
throw new Error(permission.reason ??
|
|
98
|
-
`Capability not permitted: ${step.capability}`);
|
|
99
|
-
}
|
|
100
|
-
step.status = 'running';
|
|
101
|
-
step.startedAt = new Date().toISOString();
|
|
102
|
-
this.addEvent(job, 'capability.started', {
|
|
103
|
-
stepId: step.id,
|
|
104
|
-
capability: step.capability,
|
|
105
|
-
});
|
|
106
95
|
try {
|
|
107
96
|
const completedSteps = job.steps.slice(0, job.steps.indexOf(step));
|
|
108
97
|
const resolvedInput = (0, result_reference_js_1.resolveResultReferences)(step.input, completedSteps);
|
|
@@ -112,6 +101,37 @@ class JobManager {
|
|
|
112
101
|
.map((validationError) => validationError.message)
|
|
113
102
|
.join('; '));
|
|
114
103
|
}
|
|
104
|
+
const authorization = await authorizer.authorize({
|
|
105
|
+
jobId: job.id,
|
|
106
|
+
stepId: step.id,
|
|
107
|
+
capability: {
|
|
108
|
+
name: capability.name,
|
|
109
|
+
version: capability.version,
|
|
110
|
+
risk: capability.risk,
|
|
111
|
+
},
|
|
112
|
+
input: resolvedInput,
|
|
113
|
+
caller,
|
|
114
|
+
});
|
|
115
|
+
if (authorization.decision === 'deny') {
|
|
116
|
+
const message = authorization.reason ??
|
|
117
|
+
`Capability not permitted: ${step.capability}`;
|
|
118
|
+
step.status = 'failed';
|
|
119
|
+
step.error = message;
|
|
120
|
+
step.completedAt = new Date().toISOString();
|
|
121
|
+
this.addEvent(job, 'capability.denied', {
|
|
122
|
+
stepId: step.id,
|
|
123
|
+
capability: capability.name,
|
|
124
|
+
risk: capability.risk,
|
|
125
|
+
reason: authorization.reason,
|
|
126
|
+
});
|
|
127
|
+
throw new AuthorizationDeniedError(message);
|
|
128
|
+
}
|
|
129
|
+
step.status = 'running';
|
|
130
|
+
step.startedAt = new Date().toISOString();
|
|
131
|
+
this.addEvent(job, 'capability.started', {
|
|
132
|
+
stepId: step.id,
|
|
133
|
+
capability: step.capability,
|
|
134
|
+
});
|
|
115
135
|
const logger = new console_execution_logger_js_1.ConsoleExecutionLogger(job.id, step.id, new composite_log_sink_js_1.CompositeLogSink([
|
|
116
136
|
new console_log_sink_js_1.ConsoleLogSink(),
|
|
117
137
|
new sqlite_log_sink_js_1.SQLiteLogSink(),
|
|
@@ -131,6 +151,9 @@ class JobManager {
|
|
|
131
151
|
});
|
|
132
152
|
}
|
|
133
153
|
catch (error) {
|
|
154
|
+
if (error instanceof AuthorizationDeniedError) {
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
134
157
|
const message = error instanceof Error
|
|
135
158
|
? error.message
|
|
136
159
|
: 'Unknown capability execution error';
|
|
@@ -211,4 +234,6 @@ class JobManager {
|
|
|
211
234
|
return event;
|
|
212
235
|
}
|
|
213
236
|
}
|
|
237
|
+
class AuthorizationDeniedError extends Error {
|
|
238
|
+
}
|
|
214
239
|
exports.jobManager = new JobManager();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Job } from './jobs/job.js';
|
|
2
2
|
import { JobListFilter } from './jobs/job-store.js';
|
|
3
3
|
import { ExecutionCaller } from './execution/execution-context.js';
|
|
4
|
+
import { ExecutionAuthorizer } from './permissions/execution-authorizer.js';
|
|
4
5
|
import { CapabilityModule } from './modules/capability-module.js';
|
|
5
6
|
import { ExecutionPlan } from './planner/planner.js';
|
|
6
7
|
export interface RunJobOptions {
|
|
@@ -11,7 +12,12 @@ export interface RunJobOptions {
|
|
|
11
12
|
export interface ExecutePlanOptions {
|
|
12
13
|
caller?: ExecutionCaller;
|
|
13
14
|
}
|
|
15
|
+
export interface OperatorRuntimeOptions {
|
|
16
|
+
readonly authorizer?: ExecutionAuthorizer;
|
|
17
|
+
}
|
|
14
18
|
export declare class OperatorRuntime {
|
|
19
|
+
private readonly authorizer;
|
|
20
|
+
constructor(options?: OperatorRuntimeOptions);
|
|
15
21
|
use(module: CapabilityModule): void;
|
|
16
22
|
executePlan(plan: ExecutionPlan, options?: ExecutePlanOptions): Promise<Job>;
|
|
17
23
|
run(goal: string, options?: RunJobOptions): Promise<Job>;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.operatorRuntime = exports.OperatorRuntime = void 0;
|
|
4
4
|
const job_manager_js_1 = require("./jobs/job-manager.js");
|
|
5
5
|
const job_memory_js_1 = require("./jobs/job-memory.js");
|
|
6
|
+
const execution_authorizer_js_1 = require("./permissions/execution-authorizer.js");
|
|
6
7
|
const registry_js_1 = require("./registry/registry.js");
|
|
7
8
|
const planner_registry_js_1 = require("./planner/planner-registry.js");
|
|
8
9
|
const planner_router_registry_js_1 = require("./planner/planner-router-registry.js");
|
|
@@ -21,6 +22,12 @@ function immutableCaller(caller) {
|
|
|
21
22
|
});
|
|
22
23
|
}
|
|
23
24
|
class OperatorRuntime {
|
|
25
|
+
authorizer;
|
|
26
|
+
constructor(options = {}) {
|
|
27
|
+
this.authorizer =
|
|
28
|
+
options.authorizer ??
|
|
29
|
+
execution_authorizer_js_1.defaultExecutionAuthorizer;
|
|
30
|
+
}
|
|
24
31
|
use(module) {
|
|
25
32
|
const declared = new Set(module.manifest.capabilities);
|
|
26
33
|
for (const capability of module.capabilities) {
|
|
@@ -31,7 +38,7 @@ class OperatorRuntime {
|
|
|
31
38
|
}
|
|
32
39
|
}
|
|
33
40
|
async executePlan(plan, options = {}) {
|
|
34
|
-
return job_manager_js_1.jobManager.executePlan(plan, immutableCaller(options.caller));
|
|
41
|
+
return job_manager_js_1.jobManager.executePlan(plan, immutableCaller(options.caller), this.authorizer);
|
|
35
42
|
}
|
|
36
43
|
async run(goal, options = {}) {
|
|
37
44
|
const router = planner_router_registry_js_1.plannerRouterRegistry.getDefault();
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ExecutionCaller } from '../execution/execution-context.js';
|
|
2
|
+
import { CapabilityRisk } from '../registry/capability.js';
|
|
3
|
+
export interface CapabilityAuthorizationContext {
|
|
4
|
+
readonly jobId: string;
|
|
5
|
+
readonly stepId: string;
|
|
6
|
+
readonly capability: {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly version: string;
|
|
9
|
+
readonly risk: CapabilityRisk;
|
|
10
|
+
};
|
|
11
|
+
readonly input: unknown;
|
|
12
|
+
readonly caller?: ExecutionCaller;
|
|
13
|
+
}
|
|
14
|
+
export type CapabilityAuthorizationDecision = {
|
|
15
|
+
readonly decision: 'allow';
|
|
16
|
+
} | {
|
|
17
|
+
readonly decision: 'deny';
|
|
18
|
+
readonly reason?: string;
|
|
19
|
+
};
|
|
20
|
+
export interface ExecutionAuthorizer {
|
|
21
|
+
authorize(context: CapabilityAuthorizationContext): Promise<CapabilityAuthorizationDecision>;
|
|
22
|
+
}
|
|
23
|
+
export declare const defaultExecutionAuthorizer: ExecutionAuthorizer;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultExecutionAuthorizer = void 0;
|
|
4
|
+
const permissions_js_1 = require("./permissions.js");
|
|
5
|
+
exports.defaultExecutionAuthorizer = {
|
|
6
|
+
async authorize({ capability }) {
|
|
7
|
+
const permission = (0, permissions_js_1.checkPermission)(capability.risk);
|
|
8
|
+
if (permission.allowed) {
|
|
9
|
+
return { decision: 'allow' };
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
decision: 'deny',
|
|
13
|
+
reason: permission.reason,
|
|
14
|
+
};
|
|
15
|
+
},
|
|
16
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veil-runtime/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "A governed, capability-driven execution runtime",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -56,16 +56,16 @@
|
|
|
56
56
|
],
|
|
57
57
|
"author": "Mustapha",
|
|
58
58
|
"dependencies": {
|
|
59
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
60
|
+
"zod": "^4.4.3",
|
|
59
61
|
"better-sqlite3": "^13.0.3"
|
|
60
62
|
},
|
|
61
63
|
"devDependencies": {
|
|
62
|
-
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
63
64
|
"@types/better-sqlite3": "^9.6.0",
|
|
64
65
|
"@types/node": "^26.2.0",
|
|
65
66
|
"fastify": "^5.11.3",
|
|
66
67
|
"playwright": "^1.62.1",
|
|
67
68
|
"tsx": "^4.23.12",
|
|
68
|
-
"typescript": "^7.0.2"
|
|
69
|
-
"zod": "^4.4.3"
|
|
69
|
+
"typescript": "^7.0.2"
|
|
70
70
|
}
|
|
71
71
|
}
|