@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/runtime/server.js
CHANGED
|
@@ -5,6 +5,7 @@ import { activeCacertPath } from '../core/cacert.js';
|
|
|
5
5
|
import { normalizePlanPatch, rebasePlanPatch } from '../core/planPatch.js';
|
|
6
6
|
import { validateContractInDev } from '../contracts/schemas.js';
|
|
7
7
|
import { runtimeTokenFromEnv } from './auth.js';
|
|
8
|
+
import { controlMessage } from './controlMessages.js';
|
|
8
9
|
|
|
9
10
|
export function startRuntimeServer({
|
|
10
11
|
host = '127.0.0.1',
|
|
@@ -85,6 +86,35 @@ export function startRuntimeServer({
|
|
|
85
86
|
});
|
|
86
87
|
return;
|
|
87
88
|
}
|
|
89
|
+
if (request.method === 'GET') {
|
|
90
|
+
const taskRead = readTaskEndpoint(url);
|
|
91
|
+
if (taskRead) {
|
|
92
|
+
if (taskRead.kind === 'run_tasks') {
|
|
93
|
+
sendJson(response, 200, {
|
|
94
|
+
ok: true,
|
|
95
|
+
runId: taskRead.runId,
|
|
96
|
+
tasks: typeof store.listTasks === 'function' ? store.listTasks({ runId: taskRead.runId }) : [],
|
|
97
|
+
});
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (taskRead.kind === 'task_attempts') {
|
|
101
|
+
sendJson(response, 200, {
|
|
102
|
+
ok: true,
|
|
103
|
+
taskId: taskRead.taskId,
|
|
104
|
+
attempts: typeof store.listTaskAttempts === 'function' ? store.listTaskAttempts({ taskId: taskRead.taskId }) : [],
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (taskRead.kind === 'task_result') {
|
|
109
|
+
sendJson(response, 200, {
|
|
110
|
+
ok: true,
|
|
111
|
+
taskId: taskRead.taskId,
|
|
112
|
+
result: typeof store.getTaskResult === 'function' ? store.getTaskResult({ taskId: taskRead.taskId }) : null,
|
|
113
|
+
});
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
88
118
|
if (request.method === 'GET' && url.pathname === '/control') {
|
|
89
119
|
const workspace = workspaceFromUrl(url);
|
|
90
120
|
const context = await resolveContext({ workspace });
|
|
@@ -112,9 +142,11 @@ export function startRuntimeServer({
|
|
|
112
142
|
return;
|
|
113
143
|
}
|
|
114
144
|
validateContractInDev('controlMessage', { ...body, action, input });
|
|
115
|
-
const result = handleControlMessage(context, store, input, {
|
|
145
|
+
const result = await handleControlMessage(context, store, input, {
|
|
116
146
|
intent: body.intent,
|
|
117
147
|
startNextControlRequest,
|
|
148
|
+
cancel,
|
|
149
|
+
approve,
|
|
118
150
|
});
|
|
119
151
|
sendJson(response, result.statusCode, result.body);
|
|
120
152
|
return;
|
|
@@ -202,10 +234,6 @@ export function startRuntimeServer({
|
|
|
202
234
|
}
|
|
203
235
|
if (request.method === 'POST' && url.pathname === '/run') {
|
|
204
236
|
const { body, context } = await resolveBodyContext(request, url);
|
|
205
|
-
if (context.running) {
|
|
206
|
-
sendJson(response, 409, { error: 'A runtime run is already active.' });
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
237
|
try {
|
|
210
238
|
const input = String(body.input ?? body.prompt ?? '').trim();
|
|
211
239
|
if (!input) {
|
|
@@ -213,6 +241,18 @@ export function startRuntimeServer({
|
|
|
213
241
|
return;
|
|
214
242
|
}
|
|
215
243
|
validateContractInDev('runRequest', { ...body, input });
|
|
244
|
+
if (context.running) {
|
|
245
|
+
const item = enqueueControlRequest(context, input);
|
|
246
|
+
sendJson(response, 202, {
|
|
247
|
+
accepted: true,
|
|
248
|
+
queued: true,
|
|
249
|
+
kind: 'enqueue_run',
|
|
250
|
+
item,
|
|
251
|
+
...controlStatus(context, store),
|
|
252
|
+
explanation: controlMessage(context?.session, 'queued_for_future_run'),
|
|
253
|
+
});
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
216
256
|
const accepted = startRuntimeRun(context, body);
|
|
217
257
|
sendJson(response, 202, accepted);
|
|
218
258
|
} catch (err) {
|
|
@@ -262,9 +302,15 @@ export function startRuntimeServer({
|
|
|
262
302
|
const workspace = workspaceFromBody(body) ?? workspaceFromUrl(url);
|
|
263
303
|
const result = await approve?.({
|
|
264
304
|
workspace,
|
|
305
|
+
workspaceId: workspace,
|
|
265
306
|
runId: url.searchParams.get('runId') ?? body.runId ?? null,
|
|
266
307
|
itemId: url.searchParams.get('itemId') ?? body.itemId ?? null,
|
|
267
308
|
approvalId: url.searchParams.get('approvalId') ?? body.approvalId ?? null,
|
|
309
|
+
scope: url.searchParams.get('scope') ?? body.scope ?? null,
|
|
310
|
+
taskId: url.searchParams.get('taskId') ?? body.taskId ?? null,
|
|
311
|
+
groupId: url.searchParams.get('groupId') ?? body.groupId ?? null,
|
|
312
|
+
planRevision: readOptionalNumber(url.searchParams.get('planRevision') ?? body.planRevision),
|
|
313
|
+
approvalClasses: readOptionalList(body.approvalClasses ?? body.approvalClass ?? url.searchParams.get('approvalClass')),
|
|
268
314
|
});
|
|
269
315
|
sendJson(response, result?.approved ? 202 : 404, result ?? { approved: false });
|
|
270
316
|
return;
|
|
@@ -363,6 +409,20 @@ function workspaceFromBody(body) {
|
|
|
363
409
|
return workspace == null ? null : String(workspace).trim() || null;
|
|
364
410
|
}
|
|
365
411
|
|
|
412
|
+
function readTaskEndpoint(url) {
|
|
413
|
+
const parts = url.pathname.split('/').filter(Boolean).map((part) => decodeURIComponent(part));
|
|
414
|
+
if (parts.length === 3 && parts[0] === 'runs' && parts[2] === 'tasks') {
|
|
415
|
+
return { kind: 'run_tasks', runId: parts[1] };
|
|
416
|
+
}
|
|
417
|
+
if (parts.length === 3 && parts[0] === 'tasks' && parts[2] === 'attempts') {
|
|
418
|
+
return { kind: 'task_attempts', taskId: parts[1] };
|
|
419
|
+
}
|
|
420
|
+
if (parts.length === 3 && parts[0] === 'tasks' && parts[2] === 'result') {
|
|
421
|
+
return { kind: 'task_result', taskId: parts[1] };
|
|
422
|
+
}
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
|
|
366
426
|
function controlStatus(context, store) {
|
|
367
427
|
const workspace = context?.workspace ?? context?.session?.workspace ?? null;
|
|
368
428
|
const state = runtimeState(context, store, { workspace });
|
|
@@ -419,29 +479,57 @@ function readOnlyControlResponse(kind, classification, status, explanation, { ac
|
|
|
419
479
|
};
|
|
420
480
|
}
|
|
421
481
|
|
|
422
|
-
function
|
|
482
|
+
function approvalRequestFromStatus(status) {
|
|
483
|
+
const runId = status.runId ?? status.runs?.find((run) => run.status === 'running' || run.status === 'pending_approval')?.id ?? null;
|
|
484
|
+
const pending = (status.approvals ?? []).filter((approval) => approval.status === 'pending_approval');
|
|
485
|
+
const classes = [...new Set(pending.flatMap((approval) => readOptionalList(approval.approvalClasses ?? approval.approvalClass)))];
|
|
486
|
+
return {
|
|
487
|
+
workspace: status.workspace ?? null,
|
|
488
|
+
workspaceId: status.workspace ?? null,
|
|
489
|
+
runId,
|
|
490
|
+
scope: 'run',
|
|
491
|
+
planRevision: status.planRevision ?? null,
|
|
492
|
+
approvalClasses: classes.length > 0 ? classes : ['default'],
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async function handleControlMessage(context, store, input, { intent = null, startNextControlRequest = () => false, cancel = null, approve = null } = {}) {
|
|
423
497
|
const status = controlStatus(context, store);
|
|
424
498
|
const classification = classifyControlMessage(input, status, intent);
|
|
425
499
|
if (classification.kind === 'observe') {
|
|
426
500
|
return readOnlyControlResponse('observe', classification, status, explainControlState(status));
|
|
427
501
|
}
|
|
428
|
-
if (classification.kind === '
|
|
502
|
+
if (classification.kind === 'cancel') {
|
|
503
|
+
if (context.running && context.currentAbortController) {
|
|
504
|
+
context.currentAbortController.abort();
|
|
505
|
+
await cancel?.(context);
|
|
506
|
+
return readOnlyControlResponse('cancel', classification, controlStatus(context, store), 'Runtime cancellation requested.', { accepted: true });
|
|
507
|
+
}
|
|
508
|
+
return readOnlyControlResponse('cancel', classification, status, 'No active run to cancel.', { accepted: false });
|
|
509
|
+
}
|
|
510
|
+
if (classification.kind === 'approve') {
|
|
511
|
+
const result = await approve?.(approvalRequestFromStatus(status));
|
|
512
|
+
return readOnlyControlResponse('approve', classification, controlStatus(context, store), result?.approved
|
|
513
|
+
? 'Approval grant recorded for the current run revision.'
|
|
514
|
+
: 'Approval intent recorded; no pending approval was available to grant.', { accepted: result?.approved === true, extra: { approval: result ?? null } });
|
|
515
|
+
}
|
|
516
|
+
if (classification.kind === 'modify_run') {
|
|
429
517
|
const proposal = storeControlProposal(context, input, classification, status);
|
|
430
518
|
return {
|
|
431
519
|
statusCode: 202,
|
|
432
520
|
body: {
|
|
433
521
|
accepted: true,
|
|
434
|
-
kind: '
|
|
522
|
+
kind: 'modify_run',
|
|
435
523
|
classification,
|
|
436
524
|
proposal,
|
|
437
525
|
...controlStatus(context, store),
|
|
438
|
-
explanation:
|
|
526
|
+
explanation: controlMessage(context?.session, 'plan_patch_proposed'),
|
|
439
527
|
},
|
|
440
528
|
};
|
|
441
529
|
}
|
|
442
|
-
if (classification.kind === '
|
|
530
|
+
if (classification.kind === 'enqueue_run') {
|
|
443
531
|
const item = enqueueControlRequest(context, input);
|
|
444
|
-
// Unlike `
|
|
532
|
+
// Unlike `modify_run`, this may synchronously start a queued run (see
|
|
445
533
|
// startNextControlRequest), which can change running/plan/status — a full
|
|
446
534
|
// controlStatus() recompute is required here, not just controlQueue.
|
|
447
535
|
void startNextControlRequest(context);
|
|
@@ -449,28 +537,29 @@ function handleControlMessage(context, store, input, { intent = null, startNextC
|
|
|
449
537
|
statusCode: 202,
|
|
450
538
|
body: {
|
|
451
539
|
accepted: true,
|
|
452
|
-
kind: '
|
|
540
|
+
kind: 'enqueue_run',
|
|
453
541
|
classification,
|
|
454
542
|
item,
|
|
455
543
|
...controlStatus(context, store),
|
|
544
|
+
explanation: controlMessage(context?.session, 'queued_for_future_run'),
|
|
456
545
|
},
|
|
457
546
|
};
|
|
458
547
|
}
|
|
459
548
|
if (classification.kind === 'ambiguous') {
|
|
460
|
-
return readOnlyControlResponse('ambiguous', classification, status,
|
|
549
|
+
return readOnlyControlResponse('ambiguous', classification, status, controlMessage(context?.session, 'ambiguous_control'), {
|
|
461
550
|
accepted: false,
|
|
462
551
|
extra: {
|
|
463
552
|
choices: [
|
|
464
553
|
{ action: 'message', intent: 'observe', label: 'Ask about this run' },
|
|
465
|
-
{ action: 'message', intent: '
|
|
466
|
-
{ action: 'enqueue', intent: '
|
|
554
|
+
{ action: 'message', intent: 'modify_run', label: 'Propose a change to this run' },
|
|
555
|
+
{ action: 'enqueue', intent: 'enqueue_run', label: 'Queue as a future run' },
|
|
467
556
|
],
|
|
468
557
|
},
|
|
469
558
|
});
|
|
470
559
|
}
|
|
471
560
|
return readOnlyControlResponse('converse', classification, status, status.running
|
|
472
|
-
?
|
|
473
|
-
:
|
|
561
|
+
? controlMessage(context?.session, 'converse_while_running')
|
|
562
|
+
: controlMessage(context?.session, 'converse_while_idle'));
|
|
474
563
|
}
|
|
475
564
|
|
|
476
565
|
function enqueueControlRequest(context, input) {
|
|
@@ -510,7 +599,7 @@ function storeControlProposal(context, input, classification, status) {
|
|
|
510
599
|
origin: 'runtime',
|
|
511
600
|
runId: context.currentRunId ?? status.runId ?? null,
|
|
512
601
|
workspace: proposal.workspace,
|
|
513
|
-
payload: { input, intent: '
|
|
602
|
+
payload: { input, intent: 'modify_run', classification },
|
|
514
603
|
}));
|
|
515
604
|
dispatchAgentEvent(context.session, createAgentEvent('plan_patch_proposed', {
|
|
516
605
|
origin: 'runtime',
|
|
@@ -636,17 +725,34 @@ function classifyControlMessage(input, status, forcedIntent = null) {
|
|
|
636
725
|
// Caller (the /control message route) already trims and rejects empty input.
|
|
637
726
|
const lower = String(input ?? '').toLowerCase();
|
|
638
727
|
const intent = forcedIntent ? String(forcedIntent).toLowerCase() : null;
|
|
639
|
-
|
|
640
|
-
|
|
728
|
+
const explicit = {
|
|
729
|
+
observe: 'observe',
|
|
730
|
+
converse: 'converse',
|
|
731
|
+
approve: 'approve',
|
|
732
|
+
modify_run: 'modify_run',
|
|
733
|
+
mutate: 'modify_run',
|
|
734
|
+
enqueue_run: 'enqueue_run',
|
|
735
|
+
enqueue: 'enqueue_run',
|
|
736
|
+
cancel: 'cancel',
|
|
737
|
+
ambiguous: 'ambiguous',
|
|
738
|
+
}[intent];
|
|
739
|
+
if (explicit) {
|
|
740
|
+
return { kind: explicit, confidence: 1, reason: 'explicit_intent' };
|
|
741
|
+
}
|
|
742
|
+
if (/\b(valide tout|approve all|approve|approuve|valid[eé]|ok pour tout|go pour tout)\b/i.test(lower)) {
|
|
743
|
+
return { kind: 'approve', confidence: 0.86, reason: 'approval_request' };
|
|
744
|
+
}
|
|
745
|
+
if (/\b(cancel|annule|stop|arr[eê]te|interromps|abort)\b/i.test(lower)) {
|
|
746
|
+
return { kind: 'cancel', confidence: 0.86, reason: 'cancel_request' };
|
|
747
|
+
}
|
|
748
|
+
if (/\b(plus tard|later|ensuite|apr[eè]s ce run|enqueue|mets en file|met en file|futur|next run|future run)\b/i.test(lower)) {
|
|
749
|
+
return { kind: 'enqueue_run', confidence: 0.8, reason: 'future_run_request' };
|
|
641
750
|
}
|
|
642
751
|
if (/\b(o[uù] en es[t-]|status|statut|progress|progression|build|run|job|queue|file|logs?|explique|explain|inspect|show|montre|quoi de neuf)\b/i.test(lower)) {
|
|
643
752
|
return { kind: 'observe', confidence: 0.86, reason: 'status_or_explanation_request' };
|
|
644
753
|
}
|
|
645
754
|
if (status.running && /\b(ajoute|add|change|modifie|modify|remplace|replace|retire|remove|skip|ignore|apr[eè]s|before|after|chaque|each|plan|step|t[aâ]che)\b/i.test(lower)) {
|
|
646
|
-
return { kind: '
|
|
647
|
-
}
|
|
648
|
-
if (/\b(plus tard|later|ensuite|apr[eè]s ce run|apr[eè]s|enqueue|queue|mets en file|met en file|futur|next run|future run)\b/i.test(lower)) {
|
|
649
|
-
return { kind: 'enqueue', confidence: 0.8, reason: 'future_run_request' };
|
|
755
|
+
return { kind: 'modify_run', confidence: 0.78, reason: 'active_run_change_request' };
|
|
650
756
|
}
|
|
651
757
|
if (status.running && /\b(lance|run|g[eé]n[eè]re|build|export|cr[eé]e|create|send|envoie|ingest|convert|importe|import)\b/i.test(lower)) {
|
|
652
758
|
return { kind: 'ambiguous', confidence: 0.45, reason: 'active_run_action_is_ambiguous' };
|
|
@@ -688,6 +794,18 @@ function readRequiredPatchId(body, response) {
|
|
|
688
794
|
return patchId;
|
|
689
795
|
}
|
|
690
796
|
|
|
797
|
+
function readOptionalNumber(value) {
|
|
798
|
+
if (value == null || value === '') return null;
|
|
799
|
+
const number = Number(value);
|
|
800
|
+
return Number.isFinite(number) ? number : null;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
function readOptionalList(value) {
|
|
804
|
+
if (value == null || value === '') return [];
|
|
805
|
+
if (Array.isArray(value)) return value.map(String).map((item) => item.trim()).filter(Boolean);
|
|
806
|
+
return String(value).split(',').map((item) => item.trim()).filter(Boolean);
|
|
807
|
+
}
|
|
808
|
+
|
|
691
809
|
function readJson(request) {
|
|
692
810
|
return new Promise((resolve, reject) => {
|
|
693
811
|
let raw = '';
|