@dotdrelle/wiki-manager 0.11.10 → 0.12.1
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 +32 -7
- package/agents.docker-compose.yml +3 -0
- package/docker-compose.yml +1 -0
- package/package.json +3 -2
- package/src/activity/activityAggregator.js +109 -0
- package/src/activity/activityAggregator.test.js +60 -0
- package/src/activity/activityDeduplicator.js +50 -0
- package/src/activity/progressCalculator.js +61 -0
- package/src/activity/runSynthesis.js +15 -0
- package/src/agent/graph.js +101 -38
- package/src/agent/graph.test.js +75 -5
- package/src/cli/wiki-manager.js +22 -0
- package/src/contracts/schemas.js +249 -13
- package/src/contracts/schemas.test.js +147 -2
- package/src/core/activity.js +8 -5
- package/src/core/agentEvents.js +260 -23
- package/src/core/agentEvents.test.js +53 -0
- package/src/core/compose.js +6 -1
- package/src/core/dockerCompose.test.js +12 -0
- package/src/core/documentIntake.js +33 -38
- package/src/core/documentIntake.test.js +25 -0
- package/src/core/env.js +11 -1
- package/src/core/jobQueue.js +26 -2
- package/src/core/mcp.js +30 -3
- package/src/core/mcp.test.js +69 -1
- package/src/core/plan.js +46 -4
- package/src/core/plan.test.js +16 -1
- package/src/core/planPatch.js +64 -3
- package/src/core/planPatch.test.js +110 -1
- package/src/core/queueStore.test.js +21 -0
- package/src/core/runtimeLog.js +119 -0
- package/src/core/runtimeLog.test.js +84 -0
- package/src/core/wikirc.js +22 -0
- package/src/core/wikirc.test.js +49 -1
- package/src/core/workflow.js +14 -3
- package/src/graph/graphAggregator.js +5 -0
- package/src/graph/graphPatch.js +8 -0
- package/src/graph/graphSnapshot.js +14 -0
- package/src/graph/graphVisibilityPolicy.js +40 -0
- package/src/graph/runGraphProjector.js +87 -0
- package/src/graph/runGraphProjector.test.js +111 -0
- package/src/orchestrator/agentRegistry.js +172 -0
- package/src/orchestrator/agentRegistry.test.js +114 -0
- package/src/orchestrator/approvalPolicy.js +126 -0
- package/src/orchestrator/approvalPolicy.test.js +78 -0
- package/src/orchestrator/assignmentManager.js +80 -0
- package/src/orchestrator/attemptManager.js +168 -0
- package/src/orchestrator/attemptManager.test.js +160 -0
- package/src/orchestrator/budgetManager.js +127 -0
- package/src/orchestrator/capabilityRegistry.js +64 -0
- package/src/orchestrator/capabilityRegistry.test.js +59 -0
- package/src/orchestrator/capabilityResolver.js +151 -0
- package/src/orchestrator/capabilityResolver.test.js +127 -0
- package/src/orchestrator/dependencyResolver.js +112 -0
- package/src/orchestrator/dispatcher.js +302 -0
- package/src/orchestrator/lockManager.js +51 -0
- package/src/orchestrator/planIntegrator.js +268 -0
- package/src/orchestrator/planIntegrator.test.js +213 -0
- package/src/orchestrator/planValidator.js +534 -0
- package/src/orchestrator/planValidator.test.js +262 -0
- package/src/orchestrator/resultAggregator.js +248 -0
- package/src/orchestrator/resultAggregator.test.js +211 -0
- package/src/orchestrator/scheduler.js +103 -0
- package/src/orchestrator/scheduler.test.js +138 -0
- package/src/runtime/approvals.js +130 -1
- package/src/runtime/controlMessages.js +43 -0
- package/src/runtime/controlMessages.test.js +21 -0
- package/src/runtime/donna-contract.test.js +296 -33
- package/src/runtime/recoveryManager.js +176 -0
- package/src/runtime/recoveryManager.test.js +162 -0
- package/src/runtime/runner.e2e.test.js +98 -12
- package/src/runtime/runner.js +261 -227
- package/src/runtime/runner.test.js +249 -452
- package/src/runtime/server.js +142 -24
- package/src/runtime/server.test.js +192 -62
- package/src/runtime/store.js +837 -2
- package/src/runtime/store.test.js +269 -28
- package/src/runtime/supervisor.js +33 -1
- package/src/runtime/supervisor.test.js +47 -1
- package/src/shell/RightPane.tsx +8 -5
- package/src/shell/repl.js +7 -11
- package/src/shell/repl.test.js +26 -5
- package/src/shell/tui.tsx +1 -0
- package/src/shell/useAgent.ts +5 -1
- package/src/shell/useSession.ts +75 -19
package/src/contracts/schemas.js
CHANGED
|
@@ -3,6 +3,7 @@ const STATUS_VALUES = [
|
|
|
3
3
|
'queued',
|
|
4
4
|
'running',
|
|
5
5
|
'waiting',
|
|
6
|
+
'waiting_approval',
|
|
6
7
|
'pending_approval',
|
|
7
8
|
'done',
|
|
8
9
|
'failed',
|
|
@@ -18,6 +19,7 @@ const STATUS_VALUES = [
|
|
|
18
19
|
|
|
19
20
|
const nullableString = { type: ['string', 'null'] };
|
|
20
21
|
const nullableObject = { type: ['object', 'null'], additionalProperties: true };
|
|
22
|
+
const stringArraySchema = { type: 'array', items: { type: 'string' } };
|
|
21
23
|
const outputReferenceSchema = {
|
|
22
24
|
$id: 'https://dotdrelle.dev/wiki-manager/contracts/output-reference/v1',
|
|
23
25
|
title: 'OutputReference',
|
|
@@ -38,6 +40,241 @@ const outputReferenceSchema = {
|
|
|
38
40
|
],
|
|
39
41
|
};
|
|
40
42
|
|
|
43
|
+
const retryPolicySchema = {
|
|
44
|
+
$id: 'https://dotdrelle.dev/wiki-manager/contracts/retry-policy/v1',
|
|
45
|
+
title: 'RetryPolicy',
|
|
46
|
+
schemaVersion: '1',
|
|
47
|
+
type: 'object',
|
|
48
|
+
required: ['maxAttempts', 'retryableErrors', 'allowAgentFallback'],
|
|
49
|
+
additionalProperties: false,
|
|
50
|
+
properties: {
|
|
51
|
+
maxAttempts: { type: 'integer', minimum: 1 },
|
|
52
|
+
retryableErrors: stringArraySchema,
|
|
53
|
+
allowAgentFallback: { type: 'boolean' },
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const jsonSchemaSchema = { type: 'object', additionalProperties: true };
|
|
58
|
+
const capabilityDescriptionSchema = {
|
|
59
|
+
$id: 'https://dotdrelle.dev/wiki-manager/contracts/capability-description/v1',
|
|
60
|
+
title: 'CapabilityDescription',
|
|
61
|
+
schemaVersion: '1',
|
|
62
|
+
type: 'object',
|
|
63
|
+
required: ['id', 'version', 'description', 'inputSchema', 'outputSchema', 'supportedOperations'],
|
|
64
|
+
additionalProperties: true,
|
|
65
|
+
properties: {
|
|
66
|
+
id: { type: 'string', minLength: 1 },
|
|
67
|
+
version: { type: 'string', minLength: 1 },
|
|
68
|
+
description: { type: 'string' },
|
|
69
|
+
inputSchema: jsonSchemaSchema,
|
|
70
|
+
outputSchema: jsonSchemaSchema,
|
|
71
|
+
supportedOperations: stringArraySchema,
|
|
72
|
+
mutationClass: { type: 'string' },
|
|
73
|
+
defaultRequiresApproval: { type: 'boolean' },
|
|
74
|
+
estimatedCost: {
|
|
75
|
+
type: 'object',
|
|
76
|
+
additionalProperties: false,
|
|
77
|
+
properties: {
|
|
78
|
+
llmCalls: { type: 'number', minimum: 0 },
|
|
79
|
+
tokenRange: { type: 'array', items: { type: 'number' } },
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const agentDescriptionSchema = {
|
|
86
|
+
$id: 'https://dotdrelle.dev/wiki-manager/contracts/agent-description/v1',
|
|
87
|
+
title: 'AgentDescription',
|
|
88
|
+
schemaVersion: '1',
|
|
89
|
+
type: 'object',
|
|
90
|
+
required: ['contractVersion', 'agentType', 'agentInstanceId', 'displayName', 'capabilities', 'orchestration', 'limits', 'health'],
|
|
91
|
+
additionalProperties: true,
|
|
92
|
+
properties: {
|
|
93
|
+
contractVersion: { type: 'string', minLength: 1 },
|
|
94
|
+
agentType: { type: 'string', minLength: 1 },
|
|
95
|
+
agentInstanceId: { type: 'string', minLength: 1 },
|
|
96
|
+
displayName: { type: 'string', minLength: 1 },
|
|
97
|
+
capabilities: { type: 'array', items: capabilityDescriptionSchema },
|
|
98
|
+
orchestration: {
|
|
99
|
+
type: 'object',
|
|
100
|
+
required: ['canPlan', 'canExpandPlan', 'canExecute', 'canCancel', 'canResume', 'supportsIdempotency', 'supportsParallelWorkers'],
|
|
101
|
+
additionalProperties: true,
|
|
102
|
+
properties: {
|
|
103
|
+
canPlan: { type: 'boolean' },
|
|
104
|
+
canExpandPlan: { type: 'boolean' },
|
|
105
|
+
canExecute: { type: 'boolean' },
|
|
106
|
+
canCancel: { type: 'boolean' },
|
|
107
|
+
canResume: { type: 'boolean' },
|
|
108
|
+
supportsIdempotency: { type: 'boolean' },
|
|
109
|
+
supportsParallelWorkers: { type: 'boolean' },
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
limits: {
|
|
113
|
+
type: 'object',
|
|
114
|
+
required: ['recommendedConcurrency', 'maxConcurrency'],
|
|
115
|
+
additionalProperties: true,
|
|
116
|
+
properties: {
|
|
117
|
+
recommendedConcurrency: { type: 'number', minimum: 0 },
|
|
118
|
+
maxConcurrency: { type: 'number', minimum: 0 },
|
|
119
|
+
maxTasksPerPlan: { type: 'number', minimum: 0 },
|
|
120
|
+
maxTaskDurationMs: { type: 'number', minimum: 0 },
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
health: {
|
|
124
|
+
type: 'object',
|
|
125
|
+
required: ['status'],
|
|
126
|
+
additionalProperties: false,
|
|
127
|
+
properties: {
|
|
128
|
+
status: { type: 'string', enum: ['available', 'degraded', 'unavailable'] },
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const taskGroupSchema = {
|
|
135
|
+
$id: 'https://dotdrelle.dev/wiki-manager/contracts/task-group/v1',
|
|
136
|
+
title: 'TaskGroup',
|
|
137
|
+
schemaVersion: '1',
|
|
138
|
+
type: 'object',
|
|
139
|
+
required: ['id', 'label'],
|
|
140
|
+
additionalProperties: false,
|
|
141
|
+
properties: {
|
|
142
|
+
id: { type: 'string', minLength: 1 },
|
|
143
|
+
label: { type: 'string', minLength: 1 },
|
|
144
|
+
recommendedConcurrency: { type: 'integer', minimum: 1 },
|
|
145
|
+
progressWeight: { type: 'number', minimum: 0 },
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const plannedTaskSchema = {
|
|
150
|
+
$id: 'https://dotdrelle.dev/wiki-manager/contracts/planned-task/v1',
|
|
151
|
+
title: 'PlannedTask',
|
|
152
|
+
schemaVersion: '1',
|
|
153
|
+
type: 'object',
|
|
154
|
+
required: [
|
|
155
|
+
'id',
|
|
156
|
+
'label',
|
|
157
|
+
'requiredCapability',
|
|
158
|
+
'operation',
|
|
159
|
+
'dependsOn',
|
|
160
|
+
'parallelizable',
|
|
161
|
+
'inputRefs',
|
|
162
|
+
'locks',
|
|
163
|
+
'requiresApproval',
|
|
164
|
+
'idempotencyKey',
|
|
165
|
+
'progressWeight',
|
|
166
|
+
],
|
|
167
|
+
additionalProperties: false,
|
|
168
|
+
properties: {
|
|
169
|
+
id: { type: 'string', minLength: 1 },
|
|
170
|
+
label: { type: 'string', minLength: 1 },
|
|
171
|
+
requiredCapability: { type: 'string', minLength: 1 },
|
|
172
|
+
operation: { type: 'string', minLength: 1 },
|
|
173
|
+
arguments: { type: 'object', additionalProperties: true },
|
|
174
|
+
groupId: { type: 'string' },
|
|
175
|
+
dependsOn: stringArraySchema,
|
|
176
|
+
dependsOnGroup: { type: 'string' },
|
|
177
|
+
barrier: { type: 'boolean' },
|
|
178
|
+
parallelizable: { type: 'boolean' },
|
|
179
|
+
recommendedConcurrency: { type: 'integer', minimum: 1 },
|
|
180
|
+
inputRefs: { type: 'array', items: outputReferenceSchema },
|
|
181
|
+
expectedOutputRefs: { type: 'array', items: outputReferenceSchema },
|
|
182
|
+
locks: stringArraySchema,
|
|
183
|
+
requiresApproval: { type: 'boolean' },
|
|
184
|
+
approvalClass: { type: 'string' },
|
|
185
|
+
approvalSummary: { type: 'string' },
|
|
186
|
+
idempotencyKey: { type: ['string', 'null'] },
|
|
187
|
+
progressWeight: { type: 'number', minimum: 0 },
|
|
188
|
+
priority: { type: 'number' },
|
|
189
|
+
retryPolicy: retryPolicySchema,
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const taskGraphFragmentSchema = {
|
|
194
|
+
$id: 'https://dotdrelle.dev/wiki-manager/contracts/task-graph-fragment/v1',
|
|
195
|
+
title: 'TaskGraphFragment',
|
|
196
|
+
schemaVersion: '1',
|
|
197
|
+
type: 'object',
|
|
198
|
+
required: ['contractVersion', 'agentInstanceId', 'capability', 'summary', 'groups', 'tasks'],
|
|
199
|
+
additionalProperties: false,
|
|
200
|
+
properties: {
|
|
201
|
+
contractVersion: { type: 'string', minLength: 1 },
|
|
202
|
+
agentInstanceId: { type: 'string', minLength: 1 },
|
|
203
|
+
capability: { type: 'string', minLength: 1 },
|
|
204
|
+
summary: {
|
|
205
|
+
type: 'object',
|
|
206
|
+
required: ['label', 'initialSynthesis', 'estimatedTasks'],
|
|
207
|
+
additionalProperties: false,
|
|
208
|
+
properties: {
|
|
209
|
+
label: { type: 'string', minLength: 1 },
|
|
210
|
+
initialSynthesis: stringArraySchema,
|
|
211
|
+
estimatedTasks: { type: 'integer', minimum: 0 },
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
groups: { type: 'array', items: taskGroupSchema },
|
|
215
|
+
tasks: { type: 'array', items: plannedTaskSchema },
|
|
216
|
+
expectedOutputs: { type: 'array', items: outputReferenceSchema },
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const planExpansionRequestSchema = {
|
|
221
|
+
$id: 'https://dotdrelle.dev/wiki-manager/contracts/plan-expansion-request/v1',
|
|
222
|
+
title: 'PlanExpansionRequest',
|
|
223
|
+
schemaVersion: '1',
|
|
224
|
+
type: 'object',
|
|
225
|
+
required: ['capability'],
|
|
226
|
+
additionalProperties: true,
|
|
227
|
+
properties: {
|
|
228
|
+
capability: { type: 'string', minLength: 1 },
|
|
229
|
+
operation: nullableString,
|
|
230
|
+
objective: nullableString,
|
|
231
|
+
reason: nullableString,
|
|
232
|
+
arguments: { type: 'object', additionalProperties: true },
|
|
233
|
+
workspace: { type: 'object', additionalProperties: true },
|
|
234
|
+
constraints: {
|
|
235
|
+
type: 'object',
|
|
236
|
+
additionalProperties: true,
|
|
237
|
+
properties: {
|
|
238
|
+
maxTasks: { type: 'integer', minimum: 1 },
|
|
239
|
+
maxConcurrency: { type: 'integer', minimum: 1 },
|
|
240
|
+
maxDepth: { type: 'integer', minimum: 1 },
|
|
241
|
+
requireApprovalForMutations: { type: 'boolean' },
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
insertBeforeTasks: stringArraySchema,
|
|
245
|
+
insertAfterTasks: stringArraySchema,
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// Shared by planStepSchema and patchTaskSchema, which differ only in their
|
|
250
|
+
// `id`/`description` requiredness — every other field is identical.
|
|
251
|
+
const taskFieldsSchema = {
|
|
252
|
+
label: { type: 'string' },
|
|
253
|
+
requiredCapability: { type: ['string', 'null'] },
|
|
254
|
+
operation: { type: ['string', 'null'] },
|
|
255
|
+
arguments: { type: 'object', additionalProperties: true },
|
|
256
|
+
groupId: nullableString,
|
|
257
|
+
status: { type: 'string' },
|
|
258
|
+
dependsOn: { type: 'array', items: { type: 'string' } },
|
|
259
|
+
dependsOnGroup: nullableString,
|
|
260
|
+
barrier: { type: 'boolean' },
|
|
261
|
+
parallelizable: { type: 'boolean' },
|
|
262
|
+
recommendedConcurrency: { type: 'integer', minimum: 1 },
|
|
263
|
+
executor: nullableString,
|
|
264
|
+
executorQuery: nullableObject,
|
|
265
|
+
inputRefs: { type: 'array', items: outputReferenceSchema },
|
|
266
|
+
expectedOutputRefs: { type: 'array', items: outputReferenceSchema },
|
|
267
|
+
locks: stringArraySchema,
|
|
268
|
+
requiresApproval: { type: 'boolean' },
|
|
269
|
+
approvalClass: nullableString,
|
|
270
|
+
approvalSummary: nullableString,
|
|
271
|
+
idempotencyKey: { type: ['string', 'null'] },
|
|
272
|
+
progressWeight: { type: 'number', minimum: 0 },
|
|
273
|
+
priority: { type: 'number' },
|
|
274
|
+
retryPolicy: retryPolicySchema,
|
|
275
|
+
outputRefs: { type: 'array', items: outputReferenceSchema },
|
|
276
|
+
};
|
|
277
|
+
|
|
41
278
|
const planStepSchema = {
|
|
42
279
|
type: 'object',
|
|
43
280
|
required: ['description', 'status', 'dependsOn', 'outputRefs'],
|
|
@@ -46,11 +283,7 @@ const planStepSchema = {
|
|
|
46
283
|
step: { type: 'number' },
|
|
47
284
|
id: nullableString,
|
|
48
285
|
description: { type: 'string' },
|
|
49
|
-
|
|
50
|
-
dependsOn: { type: 'array', items: { type: 'string' } },
|
|
51
|
-
executor: nullableString,
|
|
52
|
-
executorQuery: nullableObject,
|
|
53
|
-
outputRefs: { type: 'array', items: outputReferenceSchema },
|
|
286
|
+
...taskFieldsSchema,
|
|
54
287
|
},
|
|
55
288
|
};
|
|
56
289
|
|
|
@@ -61,11 +294,7 @@ const patchTaskSchema = {
|
|
|
61
294
|
properties: {
|
|
62
295
|
id: { type: 'string' },
|
|
63
296
|
description: { type: 'string' },
|
|
64
|
-
|
|
65
|
-
dependsOn: { type: 'array', items: { type: 'string' } },
|
|
66
|
-
executor: nullableString,
|
|
67
|
-
executorQuery: nullableObject,
|
|
68
|
-
outputRefs: { type: 'array', items: outputReferenceSchema },
|
|
297
|
+
...taskFieldsSchema,
|
|
69
298
|
},
|
|
70
299
|
};
|
|
71
300
|
|
|
@@ -145,7 +374,7 @@ export const contractSchemas = {
|
|
|
145
374
|
message: { type: 'string' },
|
|
146
375
|
prompt: { type: 'string' },
|
|
147
376
|
request: { type: 'string' },
|
|
148
|
-
intent: { type: 'string', enum: ['observe', 'converse', 'mutate', 'enqueue'] },
|
|
377
|
+
intent: { type: 'string', enum: ['observe', 'converse', 'mutate', 'enqueue', 'approve', 'modify_run', 'enqueue_run', 'cancel', 'ambiguous'] },
|
|
149
378
|
workspace: nullableString,
|
|
150
379
|
patchId: { type: 'string' },
|
|
151
380
|
id: { type: 'string' },
|
|
@@ -157,7 +386,7 @@ export const contractSchemas = {
|
|
|
157
386
|
title: 'StructuredPlan',
|
|
158
387
|
schemaVersion: '1',
|
|
159
388
|
type: 'array',
|
|
160
|
-
items: planStepSchema,
|
|
389
|
+
items: { anyOf: [planStepSchema, plannedTaskSchema] },
|
|
161
390
|
},
|
|
162
391
|
planPatch: {
|
|
163
392
|
$id: 'https://dotdrelle.dev/wiki-manager/contracts/plan-patch/v1',
|
|
@@ -179,7 +408,7 @@ export const contractSchemas = {
|
|
|
179
408
|
additionalProperties: true,
|
|
180
409
|
properties: {
|
|
181
410
|
op: { type: 'string', enum: ['add_task', 'add_dependency', 'remove_dependency', 'cancel_task', 'replace_executor', 'request_approval'] },
|
|
182
|
-
task: patchTaskSchema,
|
|
411
|
+
task: { anyOf: [patchTaskSchema, plannedTaskSchema] },
|
|
183
412
|
taskId: { type: 'string' },
|
|
184
413
|
targetTaskId: { type: 'string' },
|
|
185
414
|
dependencyId: { type: 'string' },
|
|
@@ -193,6 +422,13 @@ export const contractSchemas = {
|
|
|
193
422
|
},
|
|
194
423
|
},
|
|
195
424
|
outputReference: outputReferenceSchema,
|
|
425
|
+
capabilityDescription: capabilityDescriptionSchema,
|
|
426
|
+
agentDescription: agentDescriptionSchema,
|
|
427
|
+
retryPolicy: retryPolicySchema,
|
|
428
|
+
taskGroup: taskGroupSchema,
|
|
429
|
+
plannedTask: plannedTaskSchema,
|
|
430
|
+
taskGraphFragment: taskGraphFragmentSchema,
|
|
431
|
+
planExpansionRequest: planExpansionRequestSchema,
|
|
196
432
|
};
|
|
197
433
|
|
|
198
434
|
export function validateContract(name, value) {
|
|
@@ -50,7 +50,7 @@ test('agent event contract carries the unified audit identity fields', () => {
|
|
|
50
50
|
runId: 'run-1',
|
|
51
51
|
turnId: 'turn-1',
|
|
52
52
|
taskId: 'task-1',
|
|
53
|
-
workspace: '
|
|
53
|
+
workspace: 'acme',
|
|
54
54
|
payload: { toolCallId: 'call-1', ok: true },
|
|
55
55
|
};
|
|
56
56
|
|
|
@@ -58,7 +58,7 @@ test('agent event contract carries the unified audit identity fields', () => {
|
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
test('run and control request contracts reject empty run input but accept explicit controls', () => {
|
|
61
|
-
assert.equal(validateContract('runRequest', { input: 'Build docs', workspace: '
|
|
61
|
+
assert.equal(validateContract('runRequest', { input: 'Build docs', workspace: 'acme' }).ok, true);
|
|
62
62
|
assert.equal(validateContract('runRequest', { input: '' }).ok, false);
|
|
63
63
|
assert.equal(validateContract('controlMessage', { action: 'message', input: 'Ou en est le build ?', intent: 'observe' }).ok, true);
|
|
64
64
|
});
|
|
@@ -91,3 +91,148 @@ test('plan and patch contracts cover structured dependencies and output refs', (
|
|
|
91
91
|
assert.equal(validateContract('plan', plan).ok, true);
|
|
92
92
|
assert.equal(validateContract('planPatch', patch).ok, true);
|
|
93
93
|
});
|
|
94
|
+
|
|
95
|
+
test('task graph fragment contract carries planned tasks and groups round-trip', () => {
|
|
96
|
+
const fragment = {
|
|
97
|
+
contractVersion: '1',
|
|
98
|
+
agentInstanceId: 'production-main',
|
|
99
|
+
capability: 'knowledge.update',
|
|
100
|
+
summary: {
|
|
101
|
+
label: 'Update knowledge',
|
|
102
|
+
initialSynthesis: ['Two files will be ingested.'],
|
|
103
|
+
estimatedTasks: 1,
|
|
104
|
+
},
|
|
105
|
+
groups: [{
|
|
106
|
+
id: 'ingest',
|
|
107
|
+
label: 'Ingest sources',
|
|
108
|
+
recommendedConcurrency: 2,
|
|
109
|
+
progressWeight: 2,
|
|
110
|
+
}],
|
|
111
|
+
tasks: [{
|
|
112
|
+
id: 'ingest-a',
|
|
113
|
+
label: 'Ingest source A',
|
|
114
|
+
requiredCapability: 'knowledge.update',
|
|
115
|
+
operation: 'ingest',
|
|
116
|
+
arguments: { files: ['raw/untracked/a.md'] },
|
|
117
|
+
groupId: 'ingest',
|
|
118
|
+
dependsOn: [],
|
|
119
|
+
parallelizable: true,
|
|
120
|
+
recommendedConcurrency: 2,
|
|
121
|
+
inputRefs: [{ type: 'file', ref: 'raw/untracked/a.md', label: 'A' }],
|
|
122
|
+
expectedOutputRefs: ['wiki/a.md'],
|
|
123
|
+
locks: ['workspace:wiki'],
|
|
124
|
+
requiresApproval: false,
|
|
125
|
+
idempotencyKey: null,
|
|
126
|
+
progressWeight: 1,
|
|
127
|
+
priority: 10,
|
|
128
|
+
retryPolicy: {
|
|
129
|
+
maxAttempts: 2,
|
|
130
|
+
retryableErrors: ['timeout'],
|
|
131
|
+
allowAgentFallback: false,
|
|
132
|
+
},
|
|
133
|
+
}],
|
|
134
|
+
expectedOutputs: ['wiki/a.md'],
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const roundTrip = JSON.parse(JSON.stringify(fragment));
|
|
138
|
+
assert.deepEqual(roundTrip, fragment);
|
|
139
|
+
assert.equal(validateContract('taskGraphFragment', roundTrip).ok, true);
|
|
140
|
+
assert.equal(validateContract('plannedTask', roundTrip.tasks[0]).ok, true);
|
|
141
|
+
assert.equal(validateContract('taskGroup', roundTrip.groups[0]).ok, true);
|
|
142
|
+
assert.equal(validateContract('retryPolicy', roundTrip.tasks[0].retryPolicy).ok, true);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('strict planned task contract rejects invalid fields and types', () => {
|
|
146
|
+
const invalid = {
|
|
147
|
+
id: 'bad',
|
|
148
|
+
label: 'Bad task',
|
|
149
|
+
requiredCapability: 'knowledge.update',
|
|
150
|
+
operation: 'ingest',
|
|
151
|
+
dependsOn: [],
|
|
152
|
+
parallelizable: false,
|
|
153
|
+
inputRefs: [],
|
|
154
|
+
locks: [],
|
|
155
|
+
requiresApproval: false,
|
|
156
|
+
idempotencyKey: null,
|
|
157
|
+
progressWeight: 1,
|
|
158
|
+
executor: 'legacy-tool',
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const result = validateContract('plannedTask', invalid);
|
|
162
|
+
assert.equal(result.ok, false);
|
|
163
|
+
assert.ok(result.errors.some((error) => error.includes('executor is not allowed')));
|
|
164
|
+
assert.throws(() => assertContract('plannedTask', invalid), /executor is not allowed/);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('plan and plan patch contracts accept new planned task fields without rejecting legacy tasks', () => {
|
|
168
|
+
const plannedTask = {
|
|
169
|
+
id: 'publish',
|
|
170
|
+
label: 'Publish deliverable',
|
|
171
|
+
requiredCapability: 'document.publish',
|
|
172
|
+
operation: 'publish',
|
|
173
|
+
arguments: { target: 'deliverables/report.md' },
|
|
174
|
+
dependsOn: ['build'],
|
|
175
|
+
parallelizable: false,
|
|
176
|
+
inputRefs: ['deliverables/report.md'],
|
|
177
|
+
locks: ['deliverable:report.md'],
|
|
178
|
+
requiresApproval: true,
|
|
179
|
+
approvalClass: 'mutation',
|
|
180
|
+
approvalSummary: 'Publish report',
|
|
181
|
+
idempotencyKey: 'idem-1',
|
|
182
|
+
progressWeight: 1,
|
|
183
|
+
};
|
|
184
|
+
const legacyTask = {
|
|
185
|
+
step: 1,
|
|
186
|
+
id: 'build',
|
|
187
|
+
description: 'Build report',
|
|
188
|
+
status: 'done',
|
|
189
|
+
dependsOn: [],
|
|
190
|
+
outputRefs: ['deliverables/report.md'],
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
assert.equal(validateContract('plan', [legacyTask, plannedTask]).ok, true);
|
|
194
|
+
assert.equal(validateContract('planPatch', {
|
|
195
|
+
basePlanRevision: 1,
|
|
196
|
+
operations: [{ op: 'add_task', task: plannedTask }],
|
|
197
|
+
}).ok, true);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('agent description contract validates orchestrable agent capabilities', () => {
|
|
201
|
+
const description = {
|
|
202
|
+
contractVersion: '1',
|
|
203
|
+
agentType: 'production',
|
|
204
|
+
agentInstanceId: 'production-main',
|
|
205
|
+
displayName: 'Production',
|
|
206
|
+
capabilities: [{
|
|
207
|
+
id: 'knowledge.update',
|
|
208
|
+
version: '1',
|
|
209
|
+
description: 'Update wiki knowledge',
|
|
210
|
+
inputSchema: { type: 'object' },
|
|
211
|
+
outputSchema: { type: 'object' },
|
|
212
|
+
supportedOperations: ['ingest', 'pipeline'],
|
|
213
|
+
mutationClass: 'workspace',
|
|
214
|
+
defaultRequiresApproval: true,
|
|
215
|
+
estimatedCost: { llmCalls: 2, tokenRange: [1000, 3000] },
|
|
216
|
+
}],
|
|
217
|
+
orchestration: {
|
|
218
|
+
canPlan: true,
|
|
219
|
+
canExpandPlan: false,
|
|
220
|
+
canExecute: true,
|
|
221
|
+
canCancel: true,
|
|
222
|
+
canResume: false,
|
|
223
|
+
supportsIdempotency: false,
|
|
224
|
+
supportsParallelWorkers: true,
|
|
225
|
+
},
|
|
226
|
+
limits: {
|
|
227
|
+
recommendedConcurrency: 2,
|
|
228
|
+
maxConcurrency: 4,
|
|
229
|
+
maxTasksPerPlan: 50,
|
|
230
|
+
maxTaskDurationMs: 600000,
|
|
231
|
+
},
|
|
232
|
+
health: { status: 'available' },
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
assert.equal(validateContract('agentDescription', description).ok, true);
|
|
236
|
+
assert.equal(validateContract('capabilityDescription', description.capabilities[0]).ok, true);
|
|
237
|
+
assert.equal(validateContract('agentDescription', { ...description, health: { status: 'offline' } }).ok, false);
|
|
238
|
+
});
|
package/src/core/activity.js
CHANGED
|
@@ -100,11 +100,12 @@ export function normalizeActivity(activity, fallback = {}) {
|
|
|
100
100
|
return normalized;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
function productionActivityFromPayload(payload) {
|
|
103
|
+
function productionActivityFromPayload(payload, context = {}) {
|
|
104
104
|
const progress = payload?.progress;
|
|
105
105
|
const job = payload?.job;
|
|
106
106
|
const jobId = payload?.jobId ?? job?.jobId;
|
|
107
107
|
if (!progress && !job && !jobId) return null;
|
|
108
|
+
const result = payload?.result && typeof payload.result === 'object' ? payload.result : null;
|
|
108
109
|
const status = job?.status ?? payload?.status ?? progress?.status ?? 'running';
|
|
109
110
|
const percent = Number.isFinite(Number(progress?.percent)) ? Number(progress.percent) : null;
|
|
110
111
|
const sourceCount = Number(progress?.sourceCount);
|
|
@@ -139,21 +140,23 @@ function productionActivityFromPayload(payload) {
|
|
|
139
140
|
return normalizeActivity({
|
|
140
141
|
id: jobId,
|
|
141
142
|
source: 'production',
|
|
142
|
-
kind: job?.type ?? payload?.type ?? progress?.phase ?? progress?.currentStep ?? 'job',
|
|
143
|
+
kind: job?.type ?? payload?.operation ?? payload?.type ?? progress?.phase ?? progress?.currentStep ?? 'job',
|
|
143
144
|
label: detail ? `Production: ${detail}` : `Production: ${status}`,
|
|
144
145
|
status,
|
|
145
146
|
progress: {
|
|
146
147
|
...(progress ?? {}),
|
|
147
148
|
...(percent !== null ? { percent } : {}),
|
|
148
149
|
step,
|
|
150
|
+
...(payload?.taskId ? { stepId: String(payload.taskId) } : {}),
|
|
149
151
|
},
|
|
150
152
|
poll: jobId ? {
|
|
151
153
|
server: 'production',
|
|
152
|
-
tool: 'production_job_status',
|
|
154
|
+
tool: context.tool === 'agent_status' ? 'agent_status' : 'production_job_status',
|
|
153
155
|
args: { jobId },
|
|
154
156
|
intervalMs: 2500,
|
|
155
157
|
} : null,
|
|
156
|
-
|
|
158
|
+
outputRefs: Array.isArray(result?.outputRefs) ? result.outputRefs : [],
|
|
159
|
+
error: job?.error ?? result?.error?.message ?? payload?.error ?? null,
|
|
157
160
|
});
|
|
158
161
|
}
|
|
159
162
|
|
|
@@ -163,7 +166,7 @@ export function extractActivity(payload, context = {}) {
|
|
|
163
166
|
return normalizeActivity(payload._activity, { source: context.server });
|
|
164
167
|
}
|
|
165
168
|
if (context.server === 'production') {
|
|
166
|
-
return productionActivityFromPayload(payload);
|
|
169
|
+
return productionActivityFromPayload(payload, context);
|
|
167
170
|
}
|
|
168
171
|
return null;
|
|
169
172
|
}
|