@veil-runtime/core 0.1.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/LICENSE +201 -0
- package/README.md +480 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +21 -0
- package/dist/providers/storage/sqlite-job-store.d.ts +10 -0
- package/dist/providers/storage/sqlite-job-store.js +89 -0
- package/dist/runtime/events/event-bus.d.ts +6 -0
- package/dist/runtime/events/event-bus.js +2 -0
- package/dist/runtime/events/event-subscriber.d.ts +6 -0
- package/dist/runtime/events/event-subscriber.js +2 -0
- package/dist/runtime/events/memory-event-bus.d.ts +9 -0
- package/dist/runtime/events/memory-event-bus.js +31 -0
- package/dist/runtime/events/runtime-event.d.ts +10 -0
- package/dist/runtime/events/runtime-event.js +2 -0
- package/dist/runtime/execution/console-execution-logger.d.ts +13 -0
- package/dist/runtime/execution/console-execution-logger.js +37 -0
- package/dist/runtime/execution/execution-context.d.ts +13 -0
- package/dist/runtime/execution/execution-context.js +2 -0
- package/dist/runtime/execution/execution-logger.d.ts +15 -0
- package/dist/runtime/execution/execution-logger.js +2 -0
- package/dist/runtime/execution/plan-validator.d.ts +13 -0
- package/dist/runtime/execution/plan-validator.js +122 -0
- package/dist/runtime/execution/result-reference.d.ts +9 -0
- package/dist/runtime/execution/result-reference.js +59 -0
- package/dist/runtime/jobs/job-event.d.ts +8 -0
- package/dist/runtime/jobs/job-event.js +2 -0
- package/dist/runtime/jobs/job-manager.d.ts +17 -0
- package/dist/runtime/jobs/job-manager.js +214 -0
- package/dist/runtime/jobs/job-memory.d.ts +6 -0
- package/dist/runtime/jobs/job-memory.js +70 -0
- package/dist/runtime/jobs/job-status.d.ts +1 -0
- package/dist/runtime/jobs/job-status.js +2 -0
- package/dist/runtime/jobs/job-step.d.ts +10 -0
- package/dist/runtime/jobs/job-step.js +2 -0
- package/dist/runtime/jobs/job-store.d.ts +15 -0
- package/dist/runtime/jobs/job-store.js +52 -0
- package/dist/runtime/jobs/job.d.ts +20 -0
- package/dist/runtime/jobs/job.js +2 -0
- package/dist/runtime/logging/composite-log-sink.d.ts +7 -0
- package/dist/runtime/logging/composite-log-sink.js +13 -0
- package/dist/runtime/logging/console-log-sink.d.ts +6 -0
- package/dist/runtime/logging/console-log-sink.js +28 -0
- package/dist/runtime/logging/execution-log-sink.d.ts +4 -0
- package/dist/runtime/logging/execution-log-sink.js +2 -0
- package/dist/runtime/logging/sqlite-log-sink.d.ts +7 -0
- package/dist/runtime/logging/sqlite-log-sink.js +52 -0
- package/dist/runtime/modules/capability-module-manifest.d.ts +11 -0
- package/dist/runtime/modules/capability-module-manifest.js +2 -0
- package/dist/runtime/modules/capability-module.d.ts +6 -0
- package/dist/runtime/modules/capability-module.js +2 -0
- package/dist/runtime/operator-runtime.d.ts +31 -0
- package/dist/runtime/operator-runtime.js +71 -0
- package/dist/runtime/permissions/permissions.d.ts +7 -0
- package/dist/runtime/permissions/permissions.js +42 -0
- package/dist/runtime/planner/planner-definition.d.ts +9 -0
- package/dist/runtime/planner/planner-definition.js +2 -0
- package/dist/runtime/planner/planner-eligibility.d.ts +7 -0
- package/dist/runtime/planner/planner-eligibility.js +20 -0
- package/dist/runtime/planner/planner-provider.d.ts +11 -0
- package/dist/runtime/planner/planner-provider.js +2 -0
- package/dist/runtime/planner/planner-registry.d.ts +40 -0
- package/dist/runtime/planner/planner-registry.js +103 -0
- package/dist/runtime/planner/planner-router-registry.d.ts +17 -0
- package/dist/runtime/planner/planner-router-registry.js +37 -0
- package/dist/runtime/planner/planner-router.d.ts +14 -0
- package/dist/runtime/planner/planner-router.js +2 -0
- package/dist/runtime/planner/planner-runtime-state.d.ts +6 -0
- package/dist/runtime/planner/planner-runtime-state.js +2 -0
- package/dist/runtime/planner/planner-traits.d.ts +10 -0
- package/dist/runtime/planner/planner-traits.js +2 -0
- package/dist/runtime/planner/planner.d.ts +29 -0
- package/dist/runtime/planner/planner.js +2 -0
- package/dist/runtime/planner/strategies/planner-strategy-registry.d.ts +12 -0
- package/dist/runtime/planner/strategies/planner-strategy-registry.js +22 -0
- package/dist/runtime/planner/strategies/planner-strategy.d.ts +11 -0
- package/dist/runtime/planner/strategies/planner-strategy.js +2 -0
- package/dist/runtime/registry/capability.d.ts +15 -0
- package/dist/runtime/registry/capability.js +2 -0
- package/dist/runtime/registry/registry.d.ts +15 -0
- package/dist/runtime/registry/registry.js +25 -0
- package/dist/sdk/capability/capability-definition.d.ts +19 -0
- package/dist/sdk/capability/capability-definition.js +2 -0
- package/dist/sdk/capability/capability-middleware.d.ts +5 -0
- package/dist/sdk/capability/capability-middleware.js +2 -0
- package/dist/sdk/capability/create-capability.d.ts +3 -0
- package/dist/sdk/capability/create-capability.js +37 -0
- package/dist/sdk/capability/index.d.ts +5 -0
- package/dist/sdk/capability/index.js +9 -0
- package/dist/sdk/capability/lifecycle-logging-middleware.d.ts +7 -0
- package/dist/sdk/capability/lifecycle-logging-middleware.js +35 -0
- package/dist/sdk/capability/timeout-middleware.d.ts +7 -0
- package/dist/sdk/capability/timeout-middleware.js +43 -0
- package/dist/sdk/index.d.ts +1 -0
- package/dist/sdk/index.js +17 -0
- package/package.json +70 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jobManager = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const registry_js_1 = require("../registry/registry.js");
|
|
6
|
+
const permissions_js_1 = require("../permissions/permissions.js");
|
|
7
|
+
const plan_validator_js_1 = require("../execution/plan-validator.js");
|
|
8
|
+
const result_reference_js_1 = require("../execution/result-reference.js");
|
|
9
|
+
const memory_event_bus_js_1 = require("../events/memory-event-bus.js");
|
|
10
|
+
const job_store_js_1 = require("./job-store.js");
|
|
11
|
+
const console_execution_logger_js_1 = require("../execution/console-execution-logger.js");
|
|
12
|
+
const console_log_sink_js_1 = require("../logging/console-log-sink.js");
|
|
13
|
+
const composite_log_sink_js_1 = require("../logging/composite-log-sink.js");
|
|
14
|
+
const sqlite_log_sink_js_1 = require("../logging/sqlite-log-sink.js");
|
|
15
|
+
class JobManager {
|
|
16
|
+
async executePlan(plan, caller) {
|
|
17
|
+
if (!plan.steps.length) {
|
|
18
|
+
throw new Error('Execution plan contains no steps');
|
|
19
|
+
}
|
|
20
|
+
const validation = (0, plan_validator_js_1.validatePlan)(plan.steps);
|
|
21
|
+
if (!validation.valid) {
|
|
22
|
+
throw new Error(`Execution plan failed validation: ${validation.errors
|
|
23
|
+
.map((error) => error.message)
|
|
24
|
+
.join('; ')}`);
|
|
25
|
+
}
|
|
26
|
+
const goal = plan.goal?.trim() ||
|
|
27
|
+
'External execution plan';
|
|
28
|
+
const job = await this.create(goal);
|
|
29
|
+
job.idempotencyKey = plan.idempotencyKey;
|
|
30
|
+
job.steps = plan.steps.map((step) => ({
|
|
31
|
+
...step,
|
|
32
|
+
status: 'pending',
|
|
33
|
+
createdAt: new Date().toISOString(),
|
|
34
|
+
}));
|
|
35
|
+
job.updatedAt =
|
|
36
|
+
new Date().toISOString();
|
|
37
|
+
await job_store_js_1.jobStore.update(job);
|
|
38
|
+
return this.execute(job.id, caller);
|
|
39
|
+
}
|
|
40
|
+
async create(goal, planner) {
|
|
41
|
+
if (!goal?.trim()) {
|
|
42
|
+
throw new Error('Job goal is required');
|
|
43
|
+
}
|
|
44
|
+
const now = new Date().toISOString();
|
|
45
|
+
const job = {
|
|
46
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
47
|
+
goal: goal.trim(),
|
|
48
|
+
planner,
|
|
49
|
+
status: 'created',
|
|
50
|
+
createdAt: now,
|
|
51
|
+
updatedAt: now,
|
|
52
|
+
steps: [],
|
|
53
|
+
events: [],
|
|
54
|
+
};
|
|
55
|
+
this.addEvent(job, 'job.created', {
|
|
56
|
+
goal: job.goal,
|
|
57
|
+
});
|
|
58
|
+
await job_store_js_1.jobStore.create(job);
|
|
59
|
+
return job;
|
|
60
|
+
}
|
|
61
|
+
async get(id) {
|
|
62
|
+
return job_store_js_1.jobStore.get(id);
|
|
63
|
+
}
|
|
64
|
+
async list(filter) {
|
|
65
|
+
return job_store_js_1.jobStore.list(filter);
|
|
66
|
+
}
|
|
67
|
+
async setStatus(id, status) {
|
|
68
|
+
const job = await job_store_js_1.jobStore.get(id);
|
|
69
|
+
if (!job) {
|
|
70
|
+
throw new Error(`Job not found: ${id}`);
|
|
71
|
+
}
|
|
72
|
+
job.status = status;
|
|
73
|
+
job.updatedAt = new Date().toISOString();
|
|
74
|
+
await job_store_js_1.jobStore.update(job);
|
|
75
|
+
return job;
|
|
76
|
+
}
|
|
77
|
+
async execute(id, caller) {
|
|
78
|
+
const job = await job_store_js_1.jobStore.get(id);
|
|
79
|
+
if (!job) {
|
|
80
|
+
throw new Error(`Job not found: ${id}`);
|
|
81
|
+
}
|
|
82
|
+
if (job.steps.length === 0) {
|
|
83
|
+
throw new Error('Job has no planned steps');
|
|
84
|
+
}
|
|
85
|
+
job.status = 'executing';
|
|
86
|
+
job.startedAt = new Date().toISOString();
|
|
87
|
+
job.updatedAt = job.startedAt;
|
|
88
|
+
this.addEvent(job, 'execution.started');
|
|
89
|
+
try {
|
|
90
|
+
for (const step of job.steps) {
|
|
91
|
+
const capability = registry_js_1.capabilityRegistry.get(step.capability);
|
|
92
|
+
if (!capability) {
|
|
93
|
+
throw new Error(`Capability not found: ${step.capability}`);
|
|
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
|
+
try {
|
|
107
|
+
const completedSteps = job.steps.slice(0, job.steps.indexOf(step));
|
|
108
|
+
const resolvedInput = (0, result_reference_js_1.resolveResultReferences)(step.input, completedSteps);
|
|
109
|
+
const inputValidation = (0, plan_validator_js_1.validateStepInput)(step, resolvedInput);
|
|
110
|
+
if (!inputValidation.valid) {
|
|
111
|
+
throw new Error(inputValidation.errors
|
|
112
|
+
.map((validationError) => validationError.message)
|
|
113
|
+
.join('; '));
|
|
114
|
+
}
|
|
115
|
+
const logger = new console_execution_logger_js_1.ConsoleExecutionLogger(job.id, step.id, new composite_log_sink_js_1.CompositeLogSink([
|
|
116
|
+
new console_log_sink_js_1.ConsoleLogSink(),
|
|
117
|
+
new sqlite_log_sink_js_1.SQLiteLogSink(),
|
|
118
|
+
]));
|
|
119
|
+
const result = await capability.execute(resolvedInput, {
|
|
120
|
+
jobId: job.id,
|
|
121
|
+
stepId: step.id,
|
|
122
|
+
logger,
|
|
123
|
+
caller,
|
|
124
|
+
});
|
|
125
|
+
step.result = result;
|
|
126
|
+
step.status = 'completed';
|
|
127
|
+
step.completedAt = new Date().toISOString();
|
|
128
|
+
this.addEvent(job, 'capability.completed', {
|
|
129
|
+
stepId: step.id,
|
|
130
|
+
capability: step.capability,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
const message = error instanceof Error
|
|
135
|
+
? error.message
|
|
136
|
+
: 'Unknown capability execution error';
|
|
137
|
+
step.status = 'failed';
|
|
138
|
+
step.error = message;
|
|
139
|
+
step.completedAt = new Date().toISOString();
|
|
140
|
+
this.addEvent(job, 'capability.failed', {
|
|
141
|
+
stepId: step.id,
|
|
142
|
+
capability: step.capability,
|
|
143
|
+
error: message,
|
|
144
|
+
});
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
job.status = 'completed';
|
|
149
|
+
job.outcome = 'success';
|
|
150
|
+
job.completedAt = new Date().toISOString();
|
|
151
|
+
job.updatedAt = job.completedAt;
|
|
152
|
+
job.result =
|
|
153
|
+
job.steps.length === 1
|
|
154
|
+
? job.steps[0].result
|
|
155
|
+
: job.steps.map((step) => step.result);
|
|
156
|
+
this.addEvent(job, 'job.completed', {
|
|
157
|
+
stepCount: job.steps.length,
|
|
158
|
+
});
|
|
159
|
+
await job_store_js_1.jobStore.update(job);
|
|
160
|
+
return job;
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
const message = error instanceof Error
|
|
164
|
+
? error.message
|
|
165
|
+
: 'Unknown job execution error';
|
|
166
|
+
job.status = 'failed';
|
|
167
|
+
job.outcome = 'failed';
|
|
168
|
+
job.error = message;
|
|
169
|
+
job.completedAt = new Date().toISOString();
|
|
170
|
+
job.updatedAt = job.completedAt;
|
|
171
|
+
this.addEvent(job, 'job.failed', {
|
|
172
|
+
error: message,
|
|
173
|
+
});
|
|
174
|
+
await job_store_js_1.jobStore.update(job);
|
|
175
|
+
return job;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async review(id, outcome, notes) {
|
|
179
|
+
const job = await job_store_js_1.jobStore.get(id);
|
|
180
|
+
if (!job) {
|
|
181
|
+
throw new Error(`Job not found: ${id}`);
|
|
182
|
+
}
|
|
183
|
+
job.outcome = outcome;
|
|
184
|
+
job.updatedAt = new Date().toISOString();
|
|
185
|
+
this.addEvent(job, 'job.reviewed', {
|
|
186
|
+
outcome,
|
|
187
|
+
notes,
|
|
188
|
+
});
|
|
189
|
+
await job_store_js_1.jobStore.update(job);
|
|
190
|
+
return job;
|
|
191
|
+
}
|
|
192
|
+
addEvent(job, type, data) {
|
|
193
|
+
const event = {
|
|
194
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
195
|
+
jobId: job.id,
|
|
196
|
+
type,
|
|
197
|
+
timestamp: new Date().toISOString(),
|
|
198
|
+
data,
|
|
199
|
+
};
|
|
200
|
+
job.events.push(event);
|
|
201
|
+
void memory_event_bus_js_1.runtimeEventBus.publish({
|
|
202
|
+
id: event.id,
|
|
203
|
+
type: event.type,
|
|
204
|
+
timestamp: event.timestamp,
|
|
205
|
+
jobId: event.jobId,
|
|
206
|
+
stepId: typeof event.data?.stepId === 'string'
|
|
207
|
+
? event.data.stepId
|
|
208
|
+
: undefined,
|
|
209
|
+
data: event.data,
|
|
210
|
+
});
|
|
211
|
+
return event;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
exports.jobManager = new JobManager();
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jobMemory = void 0;
|
|
4
|
+
const job_store_js_1 = require("./job-store.js");
|
|
5
|
+
const STOP_WORDS = new Set([
|
|
6
|
+
'read',
|
|
7
|
+
'write',
|
|
8
|
+
'check',
|
|
9
|
+
'run',
|
|
10
|
+
'show',
|
|
11
|
+
'tell',
|
|
12
|
+
'current',
|
|
13
|
+
'from',
|
|
14
|
+
'with',
|
|
15
|
+
'that',
|
|
16
|
+
'this',
|
|
17
|
+
'file',
|
|
18
|
+
]);
|
|
19
|
+
function extractKeywords(goal) {
|
|
20
|
+
return goal
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
.split(/[^a-z0-9.]+/)
|
|
23
|
+
.filter((word) => word.length >= 4 &&
|
|
24
|
+
!STOP_WORDS.has(word))
|
|
25
|
+
.slice(0, 8);
|
|
26
|
+
}
|
|
27
|
+
function scoreJob(currentGoal, previousJob) {
|
|
28
|
+
const normalizedCurrentGoal = currentGoal.toLowerCase();
|
|
29
|
+
const previousGoal = previousJob.goal.toLowerCase();
|
|
30
|
+
const keywords = extractKeywords(currentGoal);
|
|
31
|
+
let score = keywords.reduce((total, keyword) => previousGoal.includes(keyword)
|
|
32
|
+
? total + 1
|
|
33
|
+
: total, 0);
|
|
34
|
+
if (normalizedCurrentGoal.includes('linkedin') &&
|
|
35
|
+
previousGoal.includes('linkedin')) {
|
|
36
|
+
score += 3;
|
|
37
|
+
}
|
|
38
|
+
if (normalizedCurrentGoal.includes('git') &&
|
|
39
|
+
previousGoal.includes('git')) {
|
|
40
|
+
score += 3;
|
|
41
|
+
}
|
|
42
|
+
if (normalizedCurrentGoal.includes('readme') &&
|
|
43
|
+
previousGoal.includes('readme')) {
|
|
44
|
+
score += 3;
|
|
45
|
+
}
|
|
46
|
+
return score;
|
|
47
|
+
}
|
|
48
|
+
class JobMemory {
|
|
49
|
+
async getPlannerContext(goal) {
|
|
50
|
+
const jobs = await job_store_js_1.jobStore.list({
|
|
51
|
+
status: 'completed',
|
|
52
|
+
});
|
|
53
|
+
const relevant = jobs
|
|
54
|
+
.map((job) => ({
|
|
55
|
+
job,
|
|
56
|
+
score: scoreJob(goal, job),
|
|
57
|
+
}))
|
|
58
|
+
.filter(({ score }) => score > 0)
|
|
59
|
+
.sort((a, b) => b.score - a.score)
|
|
60
|
+
.slice(0, 3);
|
|
61
|
+
return {
|
|
62
|
+
previousJobs: relevant.map(({ job }) => ({
|
|
63
|
+
goal: job.goal,
|
|
64
|
+
status: job.status,
|
|
65
|
+
capabilities: job.steps.map((step) => step.capability),
|
|
66
|
+
})),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.jobMemory = new JobMemory();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type JobStatus = 'created' | 'planning' | 'awaiting_approval' | 'executing' | 'completed' | 'failed' | 'cancelled';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type JobStepStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
|
|
2
|
+
export interface JobStep extends ExecutionStep {
|
|
3
|
+
status: JobStepStatus;
|
|
4
|
+
createdAt: string;
|
|
5
|
+
startedAt?: string;
|
|
6
|
+
completedAt?: string;
|
|
7
|
+
result?: unknown;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
import { ExecutionStep } from '../planner/planner.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Job } from './job.js';
|
|
2
|
+
export interface JobListFilter {
|
|
3
|
+
status?: string;
|
|
4
|
+
planner?: string;
|
|
5
|
+
capability?: string;
|
|
6
|
+
goal?: string;
|
|
7
|
+
limit?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface JobStore {
|
|
10
|
+
create(job: Job): Promise<Job>;
|
|
11
|
+
get(id: string): Promise<Job | undefined>;
|
|
12
|
+
list(filter?: JobListFilter): Promise<Job[]>;
|
|
13
|
+
update(job: Job): Promise<Job>;
|
|
14
|
+
}
|
|
15
|
+
export declare const jobStore: JobStore;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jobStore = void 0;
|
|
4
|
+
const sqlite_job_store_js_1 = require("../../providers/storage/sqlite-job-store.js");
|
|
5
|
+
class InMemoryJobStore {
|
|
6
|
+
jobs = new Map();
|
|
7
|
+
async create(job) {
|
|
8
|
+
if (this.jobs.has(job.id)) {
|
|
9
|
+
throw new Error(`Job already exists: ${job.id}`);
|
|
10
|
+
}
|
|
11
|
+
this.jobs.set(job.id, job);
|
|
12
|
+
return job;
|
|
13
|
+
}
|
|
14
|
+
async get(id) {
|
|
15
|
+
return this.jobs.get(id);
|
|
16
|
+
}
|
|
17
|
+
async list(filter) {
|
|
18
|
+
let jobs = Array.from(this.jobs.values());
|
|
19
|
+
if (filter?.status) {
|
|
20
|
+
jobs = jobs.filter((job) => job.status === filter.status);
|
|
21
|
+
}
|
|
22
|
+
if (filter?.planner) {
|
|
23
|
+
jobs = jobs.filter((job) => job.planner === filter.planner);
|
|
24
|
+
}
|
|
25
|
+
if (filter?.capability) {
|
|
26
|
+
jobs = jobs.filter((job) => job.steps.some((step) => step.capability === filter.capability));
|
|
27
|
+
}
|
|
28
|
+
if (filter?.goal) {
|
|
29
|
+
const goal = filter.goal.toLowerCase();
|
|
30
|
+
jobs = jobs.filter((job) => job.goal.toLowerCase().includes(goal));
|
|
31
|
+
}
|
|
32
|
+
if (filter?.limit && filter.limit > 0) {
|
|
33
|
+
jobs = jobs.slice(0, filter.limit);
|
|
34
|
+
}
|
|
35
|
+
return jobs;
|
|
36
|
+
}
|
|
37
|
+
async update(job) {
|
|
38
|
+
if (!this.jobs.has(job.id)) {
|
|
39
|
+
throw new Error(`Job not found: ${job.id}`);
|
|
40
|
+
}
|
|
41
|
+
this.jobs.set(job.id, job);
|
|
42
|
+
return job;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function createJobStore() {
|
|
46
|
+
const type = process.env.JOB_STORE?.toLowerCase() ?? 'memory';
|
|
47
|
+
if (type === 'sqlite') {
|
|
48
|
+
return new sqlite_job_store_js_1.SQLiteJobStore();
|
|
49
|
+
}
|
|
50
|
+
return new InMemoryJobStore();
|
|
51
|
+
}
|
|
52
|
+
exports.jobStore = createJobStore();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { JobStatus } from './job-status.js';
|
|
2
|
+
import { JobEvent } from './job-event.js';
|
|
3
|
+
import { JobStep } from './job-step.js';
|
|
4
|
+
export type JobOutcome = 'success' | 'inconclusive' | 'failed';
|
|
5
|
+
export interface Job {
|
|
6
|
+
id: string;
|
|
7
|
+
goal: string;
|
|
8
|
+
planner?: string;
|
|
9
|
+
idempotencyKey?: string;
|
|
10
|
+
status: JobStatus;
|
|
11
|
+
outcome?: JobOutcome;
|
|
12
|
+
createdAt: string;
|
|
13
|
+
updatedAt: string;
|
|
14
|
+
startedAt?: string;
|
|
15
|
+
completedAt?: string;
|
|
16
|
+
steps: JobStep[];
|
|
17
|
+
result?: unknown;
|
|
18
|
+
error?: string;
|
|
19
|
+
events: JobEvent[];
|
|
20
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ExecutionLogEntry } from '../execution/execution-logger.js';
|
|
2
|
+
import { ExecutionLogSink } from './execution-log-sink.js';
|
|
3
|
+
export declare class CompositeLogSink implements ExecutionLogSink {
|
|
4
|
+
private readonly sinks;
|
|
5
|
+
constructor(sinks: ExecutionLogSink[]);
|
|
6
|
+
write(entry: ExecutionLogEntry): Promise<void>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CompositeLogSink = void 0;
|
|
4
|
+
class CompositeLogSink {
|
|
5
|
+
sinks;
|
|
6
|
+
constructor(sinks) {
|
|
7
|
+
this.sinks = sinks;
|
|
8
|
+
}
|
|
9
|
+
async write(entry) {
|
|
10
|
+
await Promise.all(this.sinks.map((sink) => sink.write(entry)));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.CompositeLogSink = CompositeLogSink;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ExecutionLogEntry } from '../execution/execution-logger.js';
|
|
2
|
+
import { ExecutionLogSink } from './execution-log-sink.js';
|
|
3
|
+
export declare class ConsoleLogSink implements ExecutionLogSink {
|
|
4
|
+
write(entry: ExecutionLogEntry): void;
|
|
5
|
+
private writeToConsole;
|
|
6
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConsoleLogSink = void 0;
|
|
4
|
+
class ConsoleLogSink {
|
|
5
|
+
write(entry) {
|
|
6
|
+
const output = JSON.stringify({
|
|
7
|
+
type: 'execution_log',
|
|
8
|
+
...entry,
|
|
9
|
+
});
|
|
10
|
+
this.writeToConsole(entry.level, output);
|
|
11
|
+
}
|
|
12
|
+
writeToConsole(level, output) {
|
|
13
|
+
switch (level) {
|
|
14
|
+
case 'debug':
|
|
15
|
+
console.debug(output);
|
|
16
|
+
break;
|
|
17
|
+
case 'warn':
|
|
18
|
+
console.warn(output);
|
|
19
|
+
break;
|
|
20
|
+
case 'error':
|
|
21
|
+
console.error(output);
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
console.info(output);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.ConsoleLogSink = ConsoleLogSink;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ExecutionLogEntry } from '../execution/execution-logger.js';
|
|
2
|
+
import { ExecutionLogSink } from './execution-log-sink.js';
|
|
3
|
+
export declare class SQLiteLogSink implements ExecutionLogSink {
|
|
4
|
+
private readonly db;
|
|
5
|
+
constructor(path?: string);
|
|
6
|
+
write(entry: ExecutionLogEntry): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SQLiteLogSink = void 0;
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
10
|
+
class SQLiteLogSink {
|
|
11
|
+
db;
|
|
12
|
+
constructor(path = './data/operator.db') {
|
|
13
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)((0, node_path_1.resolve)(path)), { recursive: true });
|
|
14
|
+
this.db = new better_sqlite3_1.default(path);
|
|
15
|
+
this.db.exec(`
|
|
16
|
+
CREATE TABLE IF NOT EXISTS execution_logs (
|
|
17
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
18
|
+
timestamp TEXT NOT NULL,
|
|
19
|
+
level TEXT NOT NULL,
|
|
20
|
+
message TEXT NOT NULL,
|
|
21
|
+
job_id TEXT NOT NULL,
|
|
22
|
+
step_id TEXT NOT NULL,
|
|
23
|
+
metadata TEXT
|
|
24
|
+
)
|
|
25
|
+
`);
|
|
26
|
+
this.db.exec(`
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_execution_logs_job_id
|
|
28
|
+
ON execution_logs(job_id)
|
|
29
|
+
`);
|
|
30
|
+
this.db.exec(`
|
|
31
|
+
CREATE INDEX IF NOT EXISTS idx_execution_logs_step_id
|
|
32
|
+
ON execution_logs(step_id)
|
|
33
|
+
`);
|
|
34
|
+
}
|
|
35
|
+
write(entry) {
|
|
36
|
+
const statement = this.db.prepare(`
|
|
37
|
+
INSERT INTO execution_logs (
|
|
38
|
+
timestamp,
|
|
39
|
+
level,
|
|
40
|
+
message,
|
|
41
|
+
job_id,
|
|
42
|
+
step_id,
|
|
43
|
+
metadata
|
|
44
|
+
)
|
|
45
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
46
|
+
`);
|
|
47
|
+
statement.run(entry.timestamp, entry.level, entry.message, entry.jobId, entry.stepId, entry.metadata
|
|
48
|
+
? JSON.stringify(entry.metadata)
|
|
49
|
+
: null);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.SQLiteLogSink = SQLiteLogSink;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Job } from './jobs/job.js';
|
|
2
|
+
import { JobListFilter } from './jobs/job-store.js';
|
|
3
|
+
import { ExecutionCaller } from './execution/execution-context.js';
|
|
4
|
+
import { CapabilityModule } from './modules/capability-module.js';
|
|
5
|
+
import { ExecutionPlan } from './planner/planner.js';
|
|
6
|
+
export interface RunJobOptions {
|
|
7
|
+
planner?: string;
|
|
8
|
+
strategy?: string;
|
|
9
|
+
caller?: ExecutionCaller;
|
|
10
|
+
}
|
|
11
|
+
export interface ExecutePlanOptions {
|
|
12
|
+
caller?: ExecutionCaller;
|
|
13
|
+
}
|
|
14
|
+
export declare class OperatorRuntime {
|
|
15
|
+
use(module: CapabilityModule): void;
|
|
16
|
+
executePlan(plan: ExecutionPlan, options?: ExecutePlanOptions): Promise<Job>;
|
|
17
|
+
run(goal: string, options?: RunJobOptions): Promise<Job>;
|
|
18
|
+
getJob(id: string): Promise<Job | undefined>;
|
|
19
|
+
listJobs(filter?: JobListFilter): Promise<Job[]>;
|
|
20
|
+
listCapabilities(): {
|
|
21
|
+
name: string;
|
|
22
|
+
version: string;
|
|
23
|
+
description: string;
|
|
24
|
+
risk: import("./registry/capability.js").CapabilityRisk;
|
|
25
|
+
inputSchema: Record<string, import("./registry/capability.js").CapabilityInputField>;
|
|
26
|
+
}[];
|
|
27
|
+
listPlanners(): {
|
|
28
|
+
name: string;
|
|
29
|
+
}[];
|
|
30
|
+
}
|
|
31
|
+
export declare const operatorRuntime: OperatorRuntime;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.operatorRuntime = exports.OperatorRuntime = void 0;
|
|
4
|
+
const job_manager_js_1 = require("./jobs/job-manager.js");
|
|
5
|
+
const job_memory_js_1 = require("./jobs/job-memory.js");
|
|
6
|
+
const registry_js_1 = require("./registry/registry.js");
|
|
7
|
+
const planner_registry_js_1 = require("./planner/planner-registry.js");
|
|
8
|
+
const planner_router_registry_js_1 = require("./planner/planner-router-registry.js");
|
|
9
|
+
const planner_strategy_registry_js_1 = require("./planner/strategies/planner-strategy-registry.js");
|
|
10
|
+
function immutableCaller(caller) {
|
|
11
|
+
if (!caller)
|
|
12
|
+
return undefined;
|
|
13
|
+
return Object.freeze({
|
|
14
|
+
...caller,
|
|
15
|
+
scopes: caller.scopes
|
|
16
|
+
? Object.freeze([...caller.scopes])
|
|
17
|
+
: undefined,
|
|
18
|
+
metadata: caller.metadata
|
|
19
|
+
? Object.freeze({ ...caller.metadata })
|
|
20
|
+
: undefined,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
class OperatorRuntime {
|
|
24
|
+
use(module) {
|
|
25
|
+
const declared = new Set(module.manifest.capabilities);
|
|
26
|
+
for (const capability of module.capabilities) {
|
|
27
|
+
if (!declared.has(capability.name)) {
|
|
28
|
+
throw new Error(`Capability ${capability.name} is not declared in module manifest ${module.manifest.name}`);
|
|
29
|
+
}
|
|
30
|
+
registry_js_1.capabilityRegistry.register(capability);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async executePlan(plan, options = {}) {
|
|
34
|
+
return job_manager_js_1.jobManager.executePlan(plan, immutableCaller(options.caller));
|
|
35
|
+
}
|
|
36
|
+
async run(goal, options = {}) {
|
|
37
|
+
const router = planner_router_registry_js_1.plannerRouterRegistry.getDefault();
|
|
38
|
+
if (!router) {
|
|
39
|
+
throw new Error('No default planner router registered');
|
|
40
|
+
}
|
|
41
|
+
const selection = await router.select({
|
|
42
|
+
goal,
|
|
43
|
+
strategy: options.strategy,
|
|
44
|
+
planner: options.planner,
|
|
45
|
+
});
|
|
46
|
+
const strategy = planner_strategy_registry_js_1.plannerStrategyRegistry.get(selection.strategy);
|
|
47
|
+
if (!strategy) {
|
|
48
|
+
throw new Error(`Planner strategy not found: ${selection.strategy}`);
|
|
49
|
+
}
|
|
50
|
+
const plan = await strategy.execute({
|
|
51
|
+
goal,
|
|
52
|
+
planner: options.planner,
|
|
53
|
+
context: await job_memory_js_1.jobMemory.getPlannerContext(goal),
|
|
54
|
+
});
|
|
55
|
+
return this.executePlan(plan, { caller: options.caller });
|
|
56
|
+
}
|
|
57
|
+
async getJob(id) {
|
|
58
|
+
return job_manager_js_1.jobManager.get(id);
|
|
59
|
+
}
|
|
60
|
+
async listJobs(filter) {
|
|
61
|
+
return job_manager_js_1.jobManager.list(filter);
|
|
62
|
+
}
|
|
63
|
+
listCapabilities() {
|
|
64
|
+
return registry_js_1.capabilityRegistry.list();
|
|
65
|
+
}
|
|
66
|
+
listPlanners() {
|
|
67
|
+
return planner_registry_js_1.plannerRegistry.list();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.OperatorRuntime = OperatorRuntime;
|
|
71
|
+
exports.operatorRuntime = new OperatorRuntime();
|