@veil-runtime/core 0.1.2 → 0.2.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/README.md +58 -522
- package/dist/index.d.ts +1 -1
- package/dist/runtime/events/memory-event-bus.js +8 -1
- package/dist/runtime/execution/plan-validator.js +8 -1
- package/dist/runtime/execution/result-reference.js +1 -1
- package/dist/runtime/jobs/job-manager.js +40 -8
- package/dist/runtime/operator-runtime.d.ts +3 -7
- package/dist/runtime/operator-runtime.js +3 -0
- package/dist/runtime/registry/capability.d.ts +9 -0
- package/dist/runtime/registry/registry.d.ts +3 -8
- package/dist/runtime/registry/registry.js +22 -7
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -1,522 +1,58 @@
|
|
|
1
|
-
# Veil
|
|
2
|
-
|
|
3
|
-
Veil is a capability-driven execution
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
The
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
---
|
|
60
|
-
|
|
61
|
-
# Architecture
|
|
62
|
-
|
|
63
|
-
```text
|
|
64
|
-
Goal
|
|
65
|
-
│
|
|
66
|
-
▼
|
|
67
|
-
Planner Router
|
|
68
|
-
│
|
|
69
|
-
selects strategy
|
|
70
|
-
│
|
|
71
|
-
▼
|
|
72
|
-
Planner Strategy
|
|
73
|
-
│
|
|
74
|
-
┌────────────┼────────────┐
|
|
75
|
-
│ │ │
|
|
76
|
-
▼ ▼ ▼
|
|
77
|
-
Planner Registry Runtime State Eligibility
|
|
78
|
-
│
|
|
79
|
-
└────────────┬────────────┘
|
|
80
|
-
│
|
|
81
|
-
▼
|
|
82
|
-
Planner Provider(s)
|
|
83
|
-
│
|
|
84
|
-
▼
|
|
85
|
-
ExecutionPlan
|
|
86
|
-
|
|
87
|
-
══════════════════════════════════════════════
|
|
88
|
-
VEIL
|
|
89
|
-
══════════════════════════════════════════════
|
|
90
|
-
|
|
91
|
-
│
|
|
92
|
-
▼
|
|
93
|
-
OperatorRuntime
|
|
94
|
-
│
|
|
95
|
-
┌───────────────┼───────────────┐
|
|
96
|
-
▼ ▼ ▼
|
|
97
|
-
Policy Job Manager Event Bus
|
|
98
|
-
│
|
|
99
|
-
▼
|
|
100
|
-
Execution Engine
|
|
101
|
-
│
|
|
102
|
-
▼
|
|
103
|
-
Capability Registry
|
|
104
|
-
│
|
|
105
|
-
▼
|
|
106
|
-
Capability Modules
|
|
107
|
-
│
|
|
108
|
-
▼
|
|
109
|
-
Providers
|
|
110
|
-
│
|
|
111
|
-
┌──────────────┬──────────────┬──────────────┬──────────────┐
|
|
112
|
-
▼ ▼ ▼ ▼
|
|
113
|
-
Browser Filesystem Shell HTTP
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
# Current Features
|
|
119
|
-
|
|
120
|
-
## Runtime
|
|
121
|
-
|
|
122
|
-
- Generic execution engine
|
|
123
|
-
- OperatorRuntime façade
|
|
124
|
-
- Planner router registry
|
|
125
|
-
- Planner registry
|
|
126
|
-
- Strategy registry
|
|
127
|
-
- Capability registry
|
|
128
|
-
- ExecutionPlan contract
|
|
129
|
-
- Execution context
|
|
130
|
-
- Event bus
|
|
131
|
-
- Runtime events
|
|
132
|
-
- Structured execution logging
|
|
133
|
-
- Job lifecycle management
|
|
134
|
-
- Persistent SQLite job store
|
|
135
|
-
- Human-reviewed outcomes
|
|
136
|
-
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
## Planning
|
|
140
|
-
|
|
141
|
-
- Planner router
|
|
142
|
-
- Planner registry
|
|
143
|
-
- Planner strategies
|
|
144
|
-
- Deterministic planner
|
|
145
|
-
- OpenAI-compatible planner support
|
|
146
|
-
- Example local and distributed planner configurations
|
|
147
|
-
- Planner health monitoring
|
|
148
|
-
- Planner runtime state
|
|
149
|
-
- Planner eligibility
|
|
150
|
-
- Historical context retrieval
|
|
151
|
-
- ExecutionPlan v1
|
|
152
|
-
|
|
153
|
-
---
|
|
154
|
-
|
|
155
|
-
## SDK
|
|
156
|
-
|
|
157
|
-
- Capability SDK
|
|
158
|
-
- Declarative capability authoring
|
|
159
|
-
- Middleware pipeline
|
|
160
|
-
- Lifecycle middleware
|
|
161
|
-
- Timeout middleware
|
|
162
|
-
- Runtime execution options
|
|
163
|
-
- Capability module support
|
|
164
|
-
|
|
165
|
-
---
|
|
166
|
-
|
|
167
|
-
## Memory
|
|
168
|
-
|
|
169
|
-
- Persistent job history
|
|
170
|
-
- Planner context retrieval
|
|
171
|
-
- Historical capability recall
|
|
172
|
-
- Outcome recording
|
|
173
|
-
- Human review workflow
|
|
174
|
-
|
|
175
|
-
---
|
|
176
|
-
|
|
177
|
-
## Capabilities
|
|
178
|
-
|
|
179
|
-
- LinkedIn authentication
|
|
180
|
-
- LinkedIn profile reader
|
|
181
|
-
- Generic web page reader
|
|
182
|
-
- HTTP request execution
|
|
183
|
-
- Filesystem reader
|
|
184
|
-
- Shell command execution
|
|
185
|
-
|
|
186
|
-
---
|
|
187
|
-
|
|
188
|
-
## Providers
|
|
189
|
-
|
|
190
|
-
- Browser provider
|
|
191
|
-
- Browser session manager
|
|
192
|
-
- HTTP provider
|
|
193
|
-
- Filesystem provider
|
|
194
|
-
- Shell provider
|
|
195
|
-
- SQLite provider
|
|
196
|
-
- OpenAI-compatible AI providers
|
|
197
|
-
|
|
198
|
-
---
|
|
199
|
-
|
|
200
|
-
# Design Principles
|
|
201
|
-
|
|
202
|
-
- Planner agnostic
|
|
203
|
-
- Strategy agnostic
|
|
204
|
-
- Provider agnostic
|
|
205
|
-
- Capability driven
|
|
206
|
-
- Runtime governed
|
|
207
|
-
- Explicit permissions
|
|
208
|
-
- Structured observability
|
|
209
|
-
- Human review before learning
|
|
210
|
-
- Small composable services
|
|
211
|
-
- Extension through composition
|
|
212
|
-
- Reusable by design
|
|
213
|
-
|
|
214
|
-
---
|
|
215
|
-
|
|
216
|
-
# What Makes Veil Different
|
|
217
|
-
|
|
218
|
-
Veil does not attempt to replace language models.
|
|
219
|
-
|
|
220
|
-
Instead, it provides the governed execution environment around them.
|
|
221
|
-
|
|
222
|
-
Reasoning remains completely replaceable.
|
|
223
|
-
|
|
224
|
-
Execution remains governed and deterministic at the capability boundary.
|
|
225
|
-
|
|
226
|
-
Applications can embed Veil without coupling themselves to specific planners, capability implementations or infrastructure providers.
|
|
227
|
-
|
|
228
|
-
Because these responsibilities remain independent:
|
|
229
|
-
|
|
230
|
-
- Routers choose strategies.
|
|
231
|
-
- Strategies orchestrate planners.
|
|
232
|
-
- Planners produce execution plans.
|
|
233
|
-
- OperatorRuntime governs execution.
|
|
234
|
-
- Capabilities perform the work.
|
|
235
|
-
- Providers interact with external systems.
|
|
236
|
-
- Runtime services remain independent of planners and capabilities.
|
|
237
|
-
|
|
238
|
-
This separation allows Veil to remain reusable across domains while ensuring execution remains governed, observable and consistent.
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
# Roadmap
|
|
243
|
-
|
|
244
|
-
## Runtime
|
|
245
|
-
|
|
246
|
-
- Dependency graph execution
|
|
247
|
-
- Parallel capability execution
|
|
248
|
-
- Live progress reporting
|
|
249
|
-
- Cancellation
|
|
250
|
-
- Retry policies
|
|
251
|
-
- Secrets service
|
|
252
|
-
- Metrics
|
|
253
|
-
- Audit services
|
|
254
|
-
- Resource management
|
|
255
|
-
|
|
256
|
-
---
|
|
257
|
-
|
|
258
|
-
## SDK
|
|
259
|
-
|
|
260
|
-
- Provider SDK
|
|
261
|
-
- Capability testing framework
|
|
262
|
-
- Middleware library
|
|
263
|
-
- Validation helpers
|
|
264
|
-
- Retry middleware
|
|
265
|
-
- Metrics middleware
|
|
266
|
-
- Audit middleware
|
|
267
|
-
- Execution decorators
|
|
268
|
-
|
|
269
|
-
---
|
|
270
|
-
|
|
271
|
-
## Intelligence
|
|
272
|
-
|
|
273
|
-
- Planner routing policies
|
|
274
|
-
- Planner evaluation
|
|
275
|
-
- Cost-aware routing
|
|
276
|
-
- Latency-aware routing
|
|
277
|
-
- Capability recommendations
|
|
278
|
-
- Historical plan optimisation
|
|
279
|
-
- Outcome-aware learning
|
|
280
|
-
- Runtime analytics
|
|
281
|
-
- Multi-agent planning pipelines
|
|
282
|
-
|
|
283
|
-
---
|
|
284
|
-
|
|
285
|
-
## Capabilities
|
|
286
|
-
|
|
287
|
-
- LinkedIn posting
|
|
288
|
-
- GitHub
|
|
289
|
-
- Docker
|
|
290
|
-
- SSH
|
|
291
|
-
- SQL
|
|
292
|
-
- Jira
|
|
293
|
-
- Confluence
|
|
294
|
-
- Kubernetes
|
|
295
|
-
- Cloud providers
|
|
296
|
-
- Generic REST integrations
|
|
297
|
-
|
|
298
|
-
---
|
|
299
|
-
|
|
300
|
-
# Long-Term Direction
|
|
301
|
-
|
|
302
|
-
Veil is evolving into a reusable execution platform capable of powering:
|
|
303
|
-
|
|
304
|
-
- AI assistants
|
|
305
|
-
- Operational intelligence platforms
|
|
306
|
-
- Enterprise automation
|
|
307
|
-
- Developer tooling
|
|
308
|
-
- Agentic systems
|
|
309
|
-
- Workflow orchestration
|
|
310
|
-
- Multi-agent collaboration
|
|
311
|
-
|
|
312
|
-
Reasoning remains modular through planners, strategies and routing.
|
|
313
|
-
|
|
314
|
-
Execution remains governed and deterministic at the capability boundary.
|
|
315
|
-
|
|
316
|
-
Veil defines the contract between them.
|
|
317
|
-
|
|
318
|
-
---
|
|
319
|
-
|
|
320
|
-
# Status
|
|
321
|
-
|
|
322
|
-
Veil has evolved beyond a proof of concept into a reusable execution platform.
|
|
323
|
-
|
|
324
|
-
The execution runtime, capability system, reasoning architecture, planner routing, planner strategies, provider model, event bus, persistent memory, module architecture and SDK foundation are now in place.
|
|
325
|
-
|
|
326
|
-
The current focus is strengthening the platform itself—its SDKs, runtime services, provider ecosystem, reasoning architecture and execution model—so that new planners, strategies, capabilities and integrations become progressively simpler to build while preserving governed, observable and reliable execution.
|
|
327
|
-
|
|
328
|
-
Every architectural improvement compounds across the platform, allowing Veil to grow in capability while keeping complexity contained behind stable contracts.
|
|
329
|
-
|
|
330
|
-
The guiding principles remain simple:
|
|
331
|
-
|
|
332
|
-
> **Veil owns the contract between reasoning and execution.**
|
|
333
|
-
|
|
334
|
-
> **Planners reason. Strategies orchestrate. OperatorRuntime governs execution. Veil owns the contract between them.**
|
|
335
|
-
|
|
336
|
-
> **Architecture evolves only when existing contracts can no longer express a real-world use case. Otherwise, Veil grows through extensions rather than changes to its core.**
|
|
337
|
-
|
|
338
|
-
---
|
|
339
|
-
---
|
|
340
|
-
|
|
341
|
-
# Getting Started
|
|
342
|
-
|
|
343
|
-
## Requirements
|
|
344
|
-
|
|
345
|
-
- Node.js 24+
|
|
346
|
-
- npm
|
|
347
|
-
- Git
|
|
348
|
-
|
|
349
|
-
Optional:
|
|
350
|
-
|
|
351
|
-
- Docker Model Runner or another OpenAI-compatible endpoint
|
|
352
|
-
- Playwright (for browser capabilities)
|
|
353
|
-
|
|
354
|
-
---
|
|
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
|
-
### 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:
|
|
451
|
-
|
|
452
|
-
```bash
|
|
453
|
-
git clone git@github.com:veil-runtime/veil.git
|
|
454
|
-
cd veil
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
Install dependencies:
|
|
458
|
-
|
|
459
|
-
```bash
|
|
460
|
-
npm install
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
---
|
|
464
|
-
|
|
465
|
-
## Running Veil
|
|
466
|
-
|
|
467
|
-
Start the runtime:
|
|
468
|
-
|
|
469
|
-
```bash
|
|
470
|
-
JOB_STORE=sqlite npm run dev
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
The runtime will start on:
|
|
474
|
-
|
|
475
|
-
```
|
|
476
|
-
http://127.0.0.1:3333
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
Verify it's running:
|
|
480
|
-
|
|
481
|
-
```bash
|
|
482
|
-
curl http://127.0.0.1:3333/health
|
|
483
|
-
```
|
|
484
|
-
|
|
485
|
-
Expected response:
|
|
486
|
-
|
|
487
|
-
```json
|
|
488
|
-
{
|
|
489
|
-
"status": "ok",
|
|
490
|
-
"service": "operator-runtime"
|
|
491
|
-
}
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
---
|
|
495
|
-
|
|
496
|
-
## Running Your First Job
|
|
497
|
-
|
|
498
|
-
Submit a goal:
|
|
499
|
-
|
|
500
|
-
```bash
|
|
501
|
-
curl -X POST http://127.0.0.1:3333/api/jobs/run \
|
|
502
|
-
-H "Content-Type: application/json" \
|
|
503
|
-
-d '{
|
|
504
|
-
"goal": "Read README.md"
|
|
505
|
-
}'
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
Veil will:
|
|
509
|
-
|
|
510
|
-
1. Route the request through a `PlannerRouter`.
|
|
511
|
-
2. Select a `PlannerStrategy`.
|
|
512
|
-
3. Generate an `ExecutionPlan`.
|
|
513
|
-
4. Execute the plan through `OperatorRuntime`.
|
|
514
|
-
5. Return the completed job.
|
|
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.
|
|
1
|
+
# Veil
|
|
2
|
+
|
|
3
|
+
Veil is a governed, capability-driven execution runtime for AI and software systems.
|
|
4
|
+
|
|
5
|
+
**Developer documentation:** [GitHub Pages portal](https://veil-runtime.github.io/veil/developer.html) | [repository docs](docs/developer.md)
|
|
6
|
+
|
|
7
|
+
## The idea
|
|
8
|
+
|
|
9
|
+
Veil separates reasoning from execution. A human, application, deterministic planner, or AI system produces an ExecutionPlan; OperatorRuntime validates and executes it through registered capabilities.
|
|
10
|
+
|
|
11
|
+
Reasoning / application -> ExecutionPlan -> OperatorRuntime
|
|
12
|
+
-> validation -> reference resolution -> authorization
|
|
13
|
+
-> capability -> provider -> external system
|
|
14
|
+
-> job history and events
|
|
15
|
+
|
|
16
|
+
Planners reason. Strategies orchestrate. Routers select strategies. Capabilities define work. Providers interact with external systems.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
Requires Node.js 24+.
|
|
21
|
+
|
|
22
|
+
npm install @veil-runtime/core
|
|
23
|
+
|
|
24
|
+
This installs the published package. To verify v0.2.0 before publication, see [candidate installation](docs/getting-started/installation.md).
|
|
25
|
+
|
|
26
|
+
## Smallest working example
|
|
27
|
+
|
|
28
|
+
import { createCapability, OperatorRuntime } from '@veil-runtime/core';
|
|
29
|
+
|
|
30
|
+
const echo = createCapability<{ value: string }, string>({
|
|
31
|
+
name: 'example.echo', version: '1.0.0', description: 'Return a value', risk: 'read',
|
|
32
|
+
inputSchema: { value: { type: 'string', required: true, description: 'Text' } },
|
|
33
|
+
async execute({ input }) { return input.value; },
|
|
34
|
+
});
|
|
35
|
+
const module = {
|
|
36
|
+
manifest: { name: 'example', version: '1.0.0', capabilities: [echo.name] },
|
|
37
|
+
capabilities: [echo],
|
|
38
|
+
};
|
|
39
|
+
const runtime = new OperatorRuntime();
|
|
40
|
+
runtime.use(module);
|
|
41
|
+
const job = await runtime.executePlan({ version: '1.0', steps: [{
|
|
42
|
+
id: 'echo', capability: echo.name, capabilityVersion: echo.version,
|
|
43
|
+
input: { value: 'Hello from Veil' },
|
|
44
|
+
}] });
|
|
45
|
+
|
|
46
|
+
The default authorizer permits reads and denies write/destructive capabilities. Supply a runtime-scoped authorizer to allow selected writes. See [authorization](docs/concepts/authorization.md).
|
|
47
|
+
|
|
48
|
+
## Status and roadmap
|
|
49
|
+
|
|
50
|
+
Release candidate: **v0.2.0**. See [release notes](docs/getting-started/v0.2.0.md). The latest published package remains **v0.1.3** until publication. ExecutionPlan v1 is linear. DAGs, parallel execution, conditionals, cancellation, and retry policies are roadmap work, not current behavior.
|
|
51
|
+
|
|
52
|
+
## Contributing
|
|
53
|
+
|
|
54
|
+
See [development setup](docs/contributing/development-setup.md), [testing](docs/contributing/testing.md), and the [architecture rules](docs/contributing/architecture-rules.md).
|
|
55
|
+
|
|
56
|
+
## License and branding
|
|
57
|
+
|
|
58
|
+
Veil Core is licensed under Apache-2.0. See [LICENSE](LICENSE), [NOTICE](NOTICE), and [TRADEMARKS.md](TRADEMARKS.md).
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export type { OperatorRuntimeOptions } from './runtime/operator-runtime.js';
|
|
|
4
4
|
export type { CapabilityAuthorizationContext, CapabilityAuthorizationDecision, ExecutionAuthorizer, } from './runtime/permissions/execution-authorizer.js';
|
|
5
5
|
export type { ExecutionPlan, ExecutionStep, Planner, PlannerContext, ResultReference, } from './runtime/planner/planner.js';
|
|
6
6
|
export type { ExecutionCaller, ExecutionContext } from './runtime/execution/execution-context.js';
|
|
7
|
-
export type { Capability, CapabilityInputField, CapabilityRisk, } from './runtime/registry/capability.js';
|
|
7
|
+
export type { Capability, CapabilityDescriptor, CapabilityInputField, CapabilityRisk, } from './runtime/registry/capability.js';
|
|
8
8
|
export type { CapabilityModule } from './runtime/modules/capability-module.js';
|
|
9
9
|
export type { CapabilityModuleManifest } from './runtime/modules/capability-module-manifest.js';
|
|
10
10
|
export type { Job, JobOutcome } from './runtime/jobs/job.js';
|
|
@@ -24,7 +24,14 @@ class MemoryEventBus {
|
|
|
24
24
|
...specific,
|
|
25
25
|
...wildcard,
|
|
26
26
|
];
|
|
27
|
-
await Promise.all(handlers.map((handler) =>
|
|
27
|
+
await Promise.all(handlers.map(async (handler) => {
|
|
28
|
+
try {
|
|
29
|
+
await handler(event);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// Subscriber failures are contained observer failures.
|
|
33
|
+
}
|
|
34
|
+
}));
|
|
28
35
|
}
|
|
29
36
|
}
|
|
30
37
|
exports.MemoryEventBus = MemoryEventBus;
|
|
@@ -79,6 +79,13 @@ function validatePlan(steps) {
|
|
|
79
79
|
const errors = [];
|
|
80
80
|
const seenStepIds = new Set();
|
|
81
81
|
for (const step of steps) {
|
|
82
|
+
if (seenStepIds.has(step.id)) {
|
|
83
|
+
errors.push({
|
|
84
|
+
stepId: step.id,
|
|
85
|
+
capability: step.capability,
|
|
86
|
+
message: `Duplicate step ID: ${step.id}`,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
82
89
|
const capability = registry_js_1.capabilityRegistry.get(step.capability);
|
|
83
90
|
if (!capability) {
|
|
84
91
|
errors.push({
|
|
@@ -89,7 +96,7 @@ function validatePlan(steps) {
|
|
|
89
96
|
seenStepIds.add(step.id);
|
|
90
97
|
continue;
|
|
91
98
|
}
|
|
92
|
-
if (step.capabilityVersion && step.capabilityVersion !== capability.version) {
|
|
99
|
+
if (step.capabilityVersion !== undefined && step.capabilityVersion !== capability.version) {
|
|
93
100
|
errors.push({
|
|
94
101
|
stepId: step.id,
|
|
95
102
|
capability: step.capability,
|
|
@@ -39,7 +39,7 @@ function resolveResultReferences(value, completedSteps) {
|
|
|
39
39
|
}
|
|
40
40
|
let resolved = step.result;
|
|
41
41
|
for (const segment of path) {
|
|
42
|
-
if (!resolved || typeof resolved !== 'object' || !(segment
|
|
42
|
+
if (!resolved || typeof resolved !== 'object' || !Object.hasOwn(resolved, segment)) {
|
|
43
43
|
throw new Error(`Result reference path not found: ${value.$ref}`);
|
|
44
44
|
}
|
|
45
45
|
resolved = resolved[segment];
|
|
@@ -14,20 +14,38 @@ 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
16
|
async executePlan(plan, caller, authorizer = execution_authorizer_js_1.defaultExecutionAuthorizer) {
|
|
17
|
-
|
|
17
|
+
// Own the structural envelope before admission; nested input remains shared.
|
|
18
|
+
const capturedGoal = plan.goal;
|
|
19
|
+
const idempotencyKey = plan.idempotencyKey;
|
|
20
|
+
const submittedSteps = plan.steps;
|
|
21
|
+
const steps = new Array(submittedSteps.length);
|
|
22
|
+
for (let index = 0; index < steps.length; index += 1) {
|
|
23
|
+
if (!(index in submittedSteps))
|
|
24
|
+
continue;
|
|
25
|
+
const step = submittedSteps[index];
|
|
26
|
+
steps[index] = {
|
|
27
|
+
id: step.id,
|
|
28
|
+
capability: step.capability,
|
|
29
|
+
capabilityVersion: step.capabilityVersion,
|
|
30
|
+
input: step.input,
|
|
31
|
+
reason: step.reason,
|
|
32
|
+
idempotencyKey: step.idempotencyKey,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (!steps.length) {
|
|
18
36
|
throw new Error('Execution plan contains no steps');
|
|
19
37
|
}
|
|
20
|
-
const validation = (0, plan_validator_js_1.validatePlan)(
|
|
38
|
+
const validation = (0, plan_validator_js_1.validatePlan)(steps);
|
|
21
39
|
if (!validation.valid) {
|
|
22
40
|
throw new Error(`Execution plan failed validation: ${validation.errors
|
|
23
41
|
.map((error) => error.message)
|
|
24
42
|
.join('; ')}`);
|
|
25
43
|
}
|
|
26
|
-
const goal =
|
|
44
|
+
const goal = capturedGoal?.trim() ||
|
|
27
45
|
'External execution plan';
|
|
28
46
|
const job = await this.create(goal);
|
|
29
|
-
job.idempotencyKey =
|
|
30
|
-
job.steps =
|
|
47
|
+
job.idempotencyKey = idempotencyKey;
|
|
48
|
+
job.steps = steps.map((step) => ({
|
|
31
49
|
...step,
|
|
32
50
|
status: 'pending',
|
|
33
51
|
createdAt: new Date().toISOString(),
|
|
@@ -112,8 +130,22 @@ class JobManager {
|
|
|
112
130
|
input: resolvedInput,
|
|
113
131
|
caller,
|
|
114
132
|
});
|
|
115
|
-
if (authorization
|
|
116
|
-
|
|
133
|
+
if (typeof authorization !== 'object' ||
|
|
134
|
+
authorization === null ||
|
|
135
|
+
Array.isArray(authorization) ||
|
|
136
|
+
!Object.hasOwn(authorization, 'decision')) {
|
|
137
|
+
throw new Error('Invalid authorization decision');
|
|
138
|
+
}
|
|
139
|
+
const decision = authorization.decision;
|
|
140
|
+
if (decision !== 'allow' && decision !== 'deny') {
|
|
141
|
+
throw new Error('Invalid authorization decision');
|
|
142
|
+
}
|
|
143
|
+
if (decision === 'deny') {
|
|
144
|
+
const reason = authorization.reason;
|
|
145
|
+
if (reason !== undefined && typeof reason !== 'string') {
|
|
146
|
+
throw new Error('Invalid authorization decision');
|
|
147
|
+
}
|
|
148
|
+
const message = reason ??
|
|
117
149
|
`Capability not permitted: ${step.capability}`;
|
|
118
150
|
step.status = 'failed';
|
|
119
151
|
step.error = message;
|
|
@@ -122,7 +154,7 @@ class JobManager {
|
|
|
122
154
|
stepId: step.id,
|
|
123
155
|
capability: capability.name,
|
|
124
156
|
risk: capability.risk,
|
|
125
|
-
reason
|
|
157
|
+
reason,
|
|
126
158
|
});
|
|
127
159
|
throw new AuthorizationDeniedError(message);
|
|
128
160
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CapabilityDescriptor } from './registry/capability.js';
|
|
1
2
|
import { Job } from './jobs/job.js';
|
|
2
3
|
import { JobListFilter } from './jobs/job-store.js';
|
|
3
4
|
import { ExecutionCaller } from './execution/execution-context.js';
|
|
@@ -23,13 +24,8 @@ export declare class OperatorRuntime {
|
|
|
23
24
|
run(goal: string, options?: RunJobOptions): Promise<Job>;
|
|
24
25
|
getJob(id: string): Promise<Job | undefined>;
|
|
25
26
|
listJobs(filter?: JobListFilter): Promise<Job[]>;
|
|
26
|
-
listCapabilities():
|
|
27
|
-
|
|
28
|
-
version: string;
|
|
29
|
-
description: string;
|
|
30
|
-
risk: import("./registry/capability.js").CapabilityRisk;
|
|
31
|
-
inputSchema: Record<string, import("./registry/capability.js").CapabilityInputField>;
|
|
32
|
-
}[];
|
|
27
|
+
listCapabilities(): CapabilityDescriptor[];
|
|
28
|
+
describeCapability(name: string, version?: string): CapabilityDescriptor | undefined;
|
|
33
29
|
listPlanners(): {
|
|
34
30
|
name: string;
|
|
35
31
|
}[];
|
|
@@ -70,6 +70,9 @@ class OperatorRuntime {
|
|
|
70
70
|
listCapabilities() {
|
|
71
71
|
return registry_js_1.capabilityRegistry.list();
|
|
72
72
|
}
|
|
73
|
+
describeCapability(name, version) {
|
|
74
|
+
return registry_js_1.capabilityRegistry.describe(name, version);
|
|
75
|
+
}
|
|
73
76
|
listPlanners() {
|
|
74
77
|
return planner_registry_js_1.plannerRegistry.list();
|
|
75
78
|
}
|
|
@@ -13,3 +13,12 @@ export interface Capability<TInput = unknown, TResult = unknown> {
|
|
|
13
13
|
inputSchema?: Record<string, CapabilityInputField>;
|
|
14
14
|
execute(input: TInput, context?: ExecutionContext): Promise<TResult>;
|
|
15
15
|
}
|
|
16
|
+
/** Detached execution metadata. Introspection is not authorization. */
|
|
17
|
+
export interface CapabilityDescriptor {
|
|
18
|
+
name: string;
|
|
19
|
+
version: string;
|
|
20
|
+
description: string;
|
|
21
|
+
risk: CapabilityRisk;
|
|
22
|
+
/** Limited Veil field contract, not full JSON Schema. */
|
|
23
|
+
inputSchema: Record<string, CapabilityInputField>;
|
|
24
|
+
}
|
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import { Capability } from './capability.js';
|
|
1
|
+
import { Capability, CapabilityDescriptor } from './capability.js';
|
|
2
2
|
declare class CapabilityRegistry {
|
|
3
3
|
private capabilities;
|
|
4
4
|
register(capability: Capability): void;
|
|
5
5
|
get(name: string): Capability | undefined;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
version: string;
|
|
9
|
-
description: string;
|
|
10
|
-
risk: import("./capability.js").CapabilityRisk;
|
|
11
|
-
inputSchema: Record<string, import("./capability.js").CapabilityInputField>;
|
|
12
|
-
}[];
|
|
6
|
+
describe(name: string, version?: string): CapabilityDescriptor | undefined;
|
|
7
|
+
list(): CapabilityDescriptor[];
|
|
13
8
|
}
|
|
14
9
|
export declare const capabilityRegistry: CapabilityRegistry;
|
|
15
10
|
export {};
|
|
@@ -12,14 +12,29 @@ class CapabilityRegistry {
|
|
|
12
12
|
get(name) {
|
|
13
13
|
return this.capabilities.get(name);
|
|
14
14
|
}
|
|
15
|
+
describe(name, version) {
|
|
16
|
+
const capability = this.get(name);
|
|
17
|
+
if (!capability || (version !== undefined && version !== capability.version)) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
return describeCapability(capability);
|
|
21
|
+
}
|
|
15
22
|
list() {
|
|
16
|
-
return Array.from(this.capabilities.values())
|
|
17
|
-
name: capability.name,
|
|
18
|
-
version: capability.version,
|
|
19
|
-
description: capability.description,
|
|
20
|
-
risk: capability.risk,
|
|
21
|
-
inputSchema: capability.inputSchema ?? {},
|
|
22
|
-
}));
|
|
23
|
+
return Array.from(this.capabilities.values(), describeCapability);
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
exports.capabilityRegistry = new CapabilityRegistry();
|
|
27
|
+
// Explicitly project passive metadata; never copy implementation properties.
|
|
28
|
+
function describeCapability(capability) {
|
|
29
|
+
return {
|
|
30
|
+
name: capability.name,
|
|
31
|
+
version: capability.version,
|
|
32
|
+
description: capability.description,
|
|
33
|
+
risk: capability.risk,
|
|
34
|
+
inputSchema: Object.fromEntries(Object.entries(capability.inputSchema ?? {}).map(([name, field]) => [name, {
|
|
35
|
+
type: field.type,
|
|
36
|
+
required: field.required,
|
|
37
|
+
description: field.description,
|
|
38
|
+
}])),
|
|
39
|
+
};
|
|
40
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veil-runtime/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A governed, capability-driven execution runtime",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -43,6 +43,8 @@
|
|
|
43
43
|
"test": "tsc --project tsconfig.test.json && node --test .tmp/test-build/test/*.test.js",
|
|
44
44
|
"verify:package": "npm run build && node tools/verify-package.mjs",
|
|
45
45
|
"check": "npm run typecheck && npm test && npm run verify:package",
|
|
46
|
+
"quality": "node tools/quality.mjs",
|
|
47
|
+
"test:quality": "node --test tools/quality.test.mjs tools/quality-governance.test.mjs",
|
|
46
48
|
"mcp:stdio": "tsx src/integrations/mcp/stdio-server.ts",
|
|
47
49
|
"mcp:test": "tsx src/integrations/mcp/test-client.ts",
|
|
48
50
|
"mcp:test:outbound": "tsx src/integrations/mcp/test-outbound.ts"
|
|
@@ -61,6 +63,7 @@
|
|
|
61
63
|
"better-sqlite3": "^13.0.3"
|
|
62
64
|
},
|
|
63
65
|
"devDependencies": {
|
|
66
|
+
"@babel/parser": "8.0.5",
|
|
64
67
|
"@types/better-sqlite3": "^9.6.0",
|
|
65
68
|
"@types/node": "^26.2.0",
|
|
66
69
|
"fastify": "^5.11.3",
|