@soleri/core 9.10.0 → 9.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/dist/adapters/types.d.ts +2 -0
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/brain/brain.d.ts +5 -1
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +97 -10
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +4 -0
- package/dist/brain/intelligence.js.map +1 -1
- package/dist/brain/types.d.ts +1 -1
- package/dist/brain/types.d.ts.map +1 -1
- package/dist/dream/cron-manager.d.ts +10 -0
- package/dist/dream/cron-manager.d.ts.map +1 -0
- package/dist/dream/cron-manager.js +122 -0
- package/dist/dream/cron-manager.js.map +1 -0
- package/dist/dream/dream-engine.d.ts +34 -0
- package/dist/dream/dream-engine.d.ts.map +1 -0
- package/dist/dream/dream-engine.js +88 -0
- package/dist/dream/dream-engine.js.map +1 -0
- package/dist/dream/dream-ops.d.ts +8 -0
- package/dist/dream/dream-ops.d.ts.map +1 -0
- package/dist/dream/dream-ops.js +49 -0
- package/dist/dream/dream-ops.js.map +1 -0
- package/dist/dream/index.d.ts +7 -0
- package/dist/dream/index.d.ts.map +1 -0
- package/dist/dream/index.js +5 -0
- package/dist/dream/index.js.map +1 -0
- package/dist/dream/schema.d.ts +3 -0
- package/dist/dream/schema.d.ts.map +1 -0
- package/dist/dream/schema.js +16 -0
- package/dist/dream/schema.js.map +1 -0
- package/dist/embeddings/index.d.ts +5 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +3 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/openai-provider.d.ts +31 -0
- package/dist/embeddings/openai-provider.d.ts.map +1 -0
- package/dist/embeddings/openai-provider.js +120 -0
- package/dist/embeddings/openai-provider.js.map +1 -0
- package/dist/embeddings/pipeline.d.ts +36 -0
- package/dist/embeddings/pipeline.d.ts.map +1 -0
- package/dist/embeddings/pipeline.js +78 -0
- package/dist/embeddings/pipeline.js.map +1 -0
- package/dist/embeddings/types.d.ts +62 -0
- package/dist/embeddings/types.d.ts.map +1 -0
- package/dist/embeddings/types.js +3 -0
- package/dist/embeddings/types.js.map +1 -0
- package/dist/engine/bin/soleri-engine.js +4 -1
- package/dist/engine/bin/soleri-engine.js.map +1 -1
- package/dist/engine/module-manifest.d.ts.map +1 -1
- package/dist/engine/module-manifest.js +20 -0
- package/dist/engine/module-manifest.js.map +1 -1
- package/dist/engine/register-engine.d.ts.map +1 -1
- package/dist/engine/register-engine.js +12 -0
- package/dist/engine/register-engine.js.map +1 -1
- package/dist/flows/chain-types.d.ts +8 -8
- package/dist/flows/dispatch-registry.d.ts +15 -1
- package/dist/flows/dispatch-registry.d.ts.map +1 -1
- package/dist/flows/dispatch-registry.js +28 -1
- package/dist/flows/dispatch-registry.js.map +1 -1
- package/dist/flows/executor.d.ts +20 -2
- package/dist/flows/executor.d.ts.map +1 -1
- package/dist/flows/executor.js +79 -1
- package/dist/flows/executor.js.map +1 -1
- package/dist/flows/index.d.ts +2 -1
- package/dist/flows/index.d.ts.map +1 -1
- package/dist/flows/index.js.map +1 -1
- package/dist/flows/types.d.ts +43 -21
- package/dist/flows/types.d.ts.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/planning/plan-lifecycle.d.ts.map +1 -1
- package/dist/planning/plan-lifecycle.js +4 -2
- package/dist/planning/plan-lifecycle.js.map +1 -1
- package/dist/planning/planner-types.d.ts +1 -1
- package/dist/planning/planner-types.d.ts.map +1 -1
- package/dist/plugins/types.d.ts +31 -31
- package/dist/runtime/admin-ops.d.ts.map +1 -1
- package/dist/runtime/admin-ops.js +15 -0
- package/dist/runtime/admin-ops.js.map +1 -1
- package/dist/runtime/admin-setup-ops.js +2 -2
- package/dist/runtime/admin-setup-ops.js.map +1 -1
- package/dist/runtime/embedding-ops.d.ts +12 -0
- package/dist/runtime/embedding-ops.d.ts.map +1 -0
- package/dist/runtime/embedding-ops.js +96 -0
- package/dist/runtime/embedding-ops.js.map +1 -0
- package/dist/runtime/facades/embedding-facade.d.ts +7 -0
- package/dist/runtime/facades/embedding-facade.d.ts.map +1 -0
- package/dist/runtime/facades/embedding-facade.js +8 -0
- package/dist/runtime/facades/embedding-facade.js.map +1 -0
- package/dist/runtime/facades/index.d.ts.map +1 -1
- package/dist/runtime/facades/index.js +12 -0
- package/dist/runtime/facades/index.js.map +1 -1
- package/dist/runtime/facades/orchestrate-facade.d.ts.map +1 -1
- package/dist/runtime/facades/orchestrate-facade.js +120 -0
- package/dist/runtime/facades/orchestrate-facade.js.map +1 -1
- package/dist/runtime/feature-flags.d.ts.map +1 -1
- package/dist/runtime/feature-flags.js +4 -0
- package/dist/runtime/feature-flags.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +146 -12
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
- package/dist/runtime/planning-extra-ops.js +51 -0
- package/dist/runtime/planning-extra-ops.js.map +1 -1
- package/dist/runtime/preflight.d.ts +32 -0
- package/dist/runtime/preflight.d.ts.map +1 -0
- package/dist/runtime/preflight.js +29 -0
- package/dist/runtime/preflight.js.map +1 -0
- package/dist/runtime/quality-signals.d.ts +6 -1
- package/dist/runtime/quality-signals.d.ts.map +1 -1
- package/dist/runtime/quality-signals.js +41 -5
- package/dist/runtime/quality-signals.js.map +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +33 -2
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +27 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/skills/step-tracker.d.ts +39 -0
- package/dist/skills/step-tracker.d.ts.map +1 -0
- package/dist/skills/step-tracker.js +105 -0
- package/dist/skills/step-tracker.js.map +1 -0
- package/dist/skills/sync-skills.d.ts +3 -2
- package/dist/skills/sync-skills.d.ts.map +1 -1
- package/dist/skills/sync-skills.js +42 -8
- package/dist/skills/sync-skills.js.map +1 -1
- package/dist/subagent/dispatcher.d.ts +4 -3
- package/dist/subagent/dispatcher.d.ts.map +1 -1
- package/dist/subagent/dispatcher.js +57 -35
- package/dist/subagent/dispatcher.js.map +1 -1
- package/dist/subagent/index.d.ts +1 -0
- package/dist/subagent/index.d.ts.map +1 -1
- package/dist/subagent/index.js.map +1 -1
- package/dist/subagent/orphan-reaper.d.ts +51 -4
- package/dist/subagent/orphan-reaper.d.ts.map +1 -1
- package/dist/subagent/orphan-reaper.js +103 -3
- package/dist/subagent/orphan-reaper.js.map +1 -1
- package/dist/subagent/types.d.ts +7 -0
- package/dist/subagent/types.d.ts.map +1 -1
- package/dist/subagent/workspace-resolver.d.ts +2 -0
- package/dist/subagent/workspace-resolver.d.ts.map +1 -1
- package/dist/subagent/workspace-resolver.js +3 -1
- package/dist/subagent/workspace-resolver.js.map +1 -1
- package/dist/vault/vault-entries.d.ts +18 -0
- package/dist/vault/vault-entries.d.ts.map +1 -1
- package/dist/vault/vault-entries.js +73 -0
- package/dist/vault/vault-entries.js.map +1 -1
- package/dist/vault/vault-manager.d.ts.map +1 -1
- package/dist/vault/vault-manager.js +1 -0
- package/dist/vault/vault-manager.js.map +1 -1
- package/dist/vault/vault-schema.d.ts.map +1 -1
- package/dist/vault/vault-schema.js +14 -0
- package/dist/vault/vault-schema.js.map +1 -1
- package/dist/vault/vault.d.ts +1 -0
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js.map +1 -1
- package/package.json +3 -5
- package/src/__tests__/cron-manager.test.ts +132 -0
- package/src/__tests__/deviation-detection.test.ts +234 -0
- package/src/__tests__/embeddings.test.ts +536 -0
- package/src/__tests__/preflight.test.ts +97 -0
- package/src/__tests__/step-persistence.test.ts +324 -0
- package/src/__tests__/step-tracker.test.ts +260 -0
- package/src/__tests__/subagent/dispatcher.test.ts +122 -4
- package/src/__tests__/subagent/orphan-reaper.test.ts +148 -12
- package/src/__tests__/subagent/process-lifecycle.test.ts +422 -0
- package/src/__tests__/subagent/workspace-resolver.test.ts +6 -1
- package/src/adapters/types.ts +2 -0
- package/src/brain/brain.ts +117 -9
- package/src/brain/intelligence.ts +4 -0
- package/src/brain/types.ts +6 -1
- package/src/dream/cron-manager.ts +137 -0
- package/src/dream/dream-engine.ts +119 -0
- package/src/dream/dream-ops.ts +56 -0
- package/src/dream/dream.test.ts +182 -0
- package/src/dream/index.ts +6 -0
- package/src/dream/schema.ts +17 -0
- package/src/embeddings/openai-provider.ts +158 -0
- package/src/embeddings/pipeline.ts +126 -0
- package/src/embeddings/types.ts +67 -0
- package/src/engine/bin/soleri-engine.ts +4 -1
- package/src/engine/module-manifest.test.ts +4 -4
- package/src/engine/module-manifest.ts +20 -0
- package/src/engine/register-engine.ts +12 -0
- package/src/flows/dispatch-registry.ts +44 -1
- package/src/flows/executor.ts +93 -2
- package/src/flows/index.ts +2 -0
- package/src/flows/types.ts +39 -1
- package/src/index.ts +11 -0
- package/src/planning/goal-ancestry.test.ts +3 -5
- package/src/planning/plan-lifecycle.ts +5 -2
- package/src/planning/planner-types.ts +1 -1
- package/src/planning/planner.test.ts +73 -3
- package/src/runtime/admin-ops.test.ts +2 -2
- package/src/runtime/admin-ops.ts +17 -0
- package/src/runtime/admin-setup-ops.ts +2 -2
- package/src/runtime/embedding-ops.ts +116 -0
- package/src/runtime/facades/admin-facade.test.ts +31 -0
- package/src/runtime/facades/embedding-facade.ts +11 -0
- package/src/runtime/facades/index.ts +12 -0
- package/src/runtime/facades/orchestrate-facade.test.ts +16 -0
- package/src/runtime/facades/orchestrate-facade.ts +146 -0
- package/src/runtime/feature-flags.ts +4 -0
- package/src/runtime/orchestrate-ops.test.ts +182 -2
- package/src/runtime/orchestrate-ops.ts +170 -13
- package/src/runtime/planning-extra-ops.ts +77 -0
- package/src/runtime/preflight.ts +53 -0
- package/src/runtime/quality-signals.test.ts +182 -8
- package/src/runtime/quality-signals.ts +44 -5
- package/src/runtime/runtime.ts +41 -2
- package/src/runtime/types.ts +20 -0
- package/src/skills/__tests__/sync-skills.test.ts +132 -0
- package/src/skills/step-tracker.ts +162 -0
- package/src/skills/sync-skills.ts +54 -9
- package/src/subagent/dispatcher.ts +62 -39
- package/src/subagent/index.ts +1 -0
- package/src/subagent/orphan-reaper.test.ts +135 -0
- package/src/subagent/orphan-reaper.ts +130 -7
- package/src/subagent/types.ts +10 -0
- package/src/subagent/workspace-resolver.ts +3 -1
- package/src/vault/vault-entries.ts +112 -0
- package/src/vault/vault-manager.ts +1 -0
- package/src/vault/vault-scaling.test.ts +3 -2
- package/src/vault/vault-schema.ts +15 -0
- package/src/vault/vault.ts +1 -0
- package/vitest.config.ts +2 -1
- package/dist/brain/strength-scorer.d.ts +0 -31
- package/dist/brain/strength-scorer.d.ts.map +0 -1
- package/dist/brain/strength-scorer.js +0 -264
- package/dist/brain/strength-scorer.js.map +0 -1
- package/dist/engine/index.d.ts +0 -21
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js +0 -18
- package/dist/engine/index.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -2
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/index.js +0 -2
- package/dist/hooks/index.js.map +0 -1
- package/dist/persona/index.d.ts +0 -5
- package/dist/persona/index.d.ts.map +0 -1
- package/dist/persona/index.js +0 -4
- package/dist/persona/index.js.map +0 -1
- package/dist/vault/vault-interfaces.d.ts +0 -153
- package/dist/vault/vault-interfaces.d.ts.map +0 -1
- package/dist/vault/vault-interfaces.js +0 -2
- package/dist/vault/vault-interfaces.js.map +0 -1
|
@@ -245,6 +245,137 @@ describe('createOrchestrateOps', () => {
|
|
|
245
245
|
expect(rt.contextHealth.track).toHaveBeenCalled();
|
|
246
246
|
expect(rt.contextHealth.check).toHaveBeenCalled();
|
|
247
247
|
});
|
|
248
|
+
|
|
249
|
+
// ─── Post-dispatch cleanup (subagent path) ─────────────────
|
|
250
|
+
describe('post-dispatch cleanup', () => {
|
|
251
|
+
function addSubagentDispatcher(
|
|
252
|
+
runtime: AgentRuntime,
|
|
253
|
+
opts: {
|
|
254
|
+
dispatchResult?: Record<string, unknown>;
|
|
255
|
+
dispatchError?: Error;
|
|
256
|
+
reapResult?: string[];
|
|
257
|
+
reapThrows?: boolean;
|
|
258
|
+
} = {},
|
|
259
|
+
) {
|
|
260
|
+
const dispatchMock = opts.dispatchError
|
|
261
|
+
? vi.fn().mockRejectedValue(opts.dispatchError)
|
|
262
|
+
: vi.fn().mockResolvedValue(
|
|
263
|
+
opts.dispatchResult ?? {
|
|
264
|
+
status: 'completed',
|
|
265
|
+
totalTasks: 1,
|
|
266
|
+
completed: 1,
|
|
267
|
+
failed: 0,
|
|
268
|
+
durationMs: 100,
|
|
269
|
+
totalUsage: {},
|
|
270
|
+
},
|
|
271
|
+
);
|
|
272
|
+
const reapMock = opts.reapThrows
|
|
273
|
+
? vi.fn().mockImplementation(() => {
|
|
274
|
+
throw new Error('reap failed');
|
|
275
|
+
})
|
|
276
|
+
: vi.fn().mockReturnValue({
|
|
277
|
+
reaped: opts.reapResult ?? [],
|
|
278
|
+
alive: [],
|
|
279
|
+
});
|
|
280
|
+
const cleanupMock = vi.fn();
|
|
281
|
+
|
|
282
|
+
(runtime as Record<string, unknown>).subagentDispatcher = {
|
|
283
|
+
dispatch: dispatchMock,
|
|
284
|
+
reapOrphans: reapMock,
|
|
285
|
+
cleanup: cleanupMock,
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
return { dispatchMock, reapMock, cleanupMock };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
it('calls reapOrphans after successful subagent dispatch', async () => {
|
|
292
|
+
const { reapMock } = addSubagentDispatcher(rt);
|
|
293
|
+
ops = createOrchestrateOps(rt);
|
|
294
|
+
|
|
295
|
+
const op = findOp(ops, 'orchestrate_execute');
|
|
296
|
+
await op.handler({ planId: 'legacy-plan', subagent: true });
|
|
297
|
+
|
|
298
|
+
expect(reapMock).toHaveBeenCalledTimes(1);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('calls reapOrphans even when dispatch fails', async () => {
|
|
302
|
+
const { reapMock } = addSubagentDispatcher(rt, {
|
|
303
|
+
dispatchError: new Error('dispatch boom'),
|
|
304
|
+
});
|
|
305
|
+
ops = createOrchestrateOps(rt);
|
|
306
|
+
|
|
307
|
+
const op = findOp(ops, 'orchestrate_execute');
|
|
308
|
+
await expect(op.handler({ planId: 'legacy-plan', subagent: true })).rejects.toThrow(
|
|
309
|
+
'dispatch boom',
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
expect(reapMock).toHaveBeenCalledTimes(1);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('includes reapedOrphans in result when orphans found', async () => {
|
|
316
|
+
addSubagentDispatcher(rt, {
|
|
317
|
+
reapResult: ['t1'],
|
|
318
|
+
});
|
|
319
|
+
ops = createOrchestrateOps(rt);
|
|
320
|
+
|
|
321
|
+
const op = findOp(ops, 'orchestrate_execute');
|
|
322
|
+
const result = (await op.handler({
|
|
323
|
+
planId: 'legacy-plan',
|
|
324
|
+
subagent: true,
|
|
325
|
+
})) as Record<string, unknown>;
|
|
326
|
+
|
|
327
|
+
expect(result).toHaveProperty('reapedOrphans');
|
|
328
|
+
const reaped = result.reapedOrphans as Array<{ taskId: string }>;
|
|
329
|
+
expect(reaped).toHaveLength(1);
|
|
330
|
+
expect(reaped[0].taskId).toBe('t1');
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('omits reapedOrphans from result when none found', async () => {
|
|
334
|
+
addSubagentDispatcher(rt, { reapResult: [] });
|
|
335
|
+
ops = createOrchestrateOps(rt);
|
|
336
|
+
|
|
337
|
+
const op = findOp(ops, 'orchestrate_execute');
|
|
338
|
+
const result = (await op.handler({
|
|
339
|
+
planId: 'legacy-plan',
|
|
340
|
+
subagent: true,
|
|
341
|
+
})) as Record<string, unknown>;
|
|
342
|
+
|
|
343
|
+
expect(result).not.toHaveProperty('reapedOrphans');
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('does not throw when reapOrphans fails', async () => {
|
|
347
|
+
addSubagentDispatcher(rt, { reapThrows: true });
|
|
348
|
+
ops = createOrchestrateOps(rt);
|
|
349
|
+
|
|
350
|
+
const op = findOp(ops, 'orchestrate_execute');
|
|
351
|
+
// Should complete successfully despite reap failure
|
|
352
|
+
const result = (await op.handler({
|
|
353
|
+
planId: 'legacy-plan',
|
|
354
|
+
subagent: true,
|
|
355
|
+
})) as Record<string, unknown>;
|
|
356
|
+
|
|
357
|
+
expect(result).toHaveProperty('subagent');
|
|
358
|
+
expect(result).not.toHaveProperty('reapedOrphans');
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('logs reaped orphans to stderr', async () => {
|
|
362
|
+
const stderrSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
363
|
+
addSubagentDispatcher(rt, {
|
|
364
|
+
reapResult: ['t1', 't2'],
|
|
365
|
+
});
|
|
366
|
+
ops = createOrchestrateOps(rt);
|
|
367
|
+
|
|
368
|
+
const op = findOp(ops, 'orchestrate_execute');
|
|
369
|
+
await op.handler({ planId: 'legacy-plan', subagent: true });
|
|
370
|
+
|
|
371
|
+
expect(stderrSpy).toHaveBeenCalledWith(
|
|
372
|
+
expect.stringContaining('Reaped 2 orphaned subagent(s)'),
|
|
373
|
+
);
|
|
374
|
+
expect(stderrSpy).toHaveBeenCalledWith(expect.stringContaining('t1'));
|
|
375
|
+
expect(stderrSpy).toHaveBeenCalledWith(expect.stringContaining('t2'));
|
|
376
|
+
stderrSpy.mockRestore();
|
|
377
|
+
});
|
|
378
|
+
});
|
|
248
379
|
});
|
|
249
380
|
|
|
250
381
|
// ─── orchestrate_complete ─────────────────────────────────────
|
|
@@ -363,10 +494,10 @@ describe('createOrchestrateOps', () => {
|
|
|
363
494
|
outcome: 'completed',
|
|
364
495
|
})) as Record<string, unknown>;
|
|
365
496
|
|
|
366
|
-
// Should complete successfully
|
|
497
|
+
// Should complete successfully with evidenceReport: null
|
|
367
498
|
expect(result).toHaveProperty('plan');
|
|
368
499
|
expect(result).toHaveProperty('session');
|
|
369
|
-
expect(result).
|
|
500
|
+
expect(result.evidenceReport).toBeNull();
|
|
370
501
|
});
|
|
371
502
|
|
|
372
503
|
it('adds warning when evidence accuracy is below 50%', async () => {
|
|
@@ -395,6 +526,55 @@ describe('createOrchestrateOps', () => {
|
|
|
395
526
|
const warnings = result.warnings as string[];
|
|
396
527
|
expect(warnings.some((w) => w.includes('Low evidence accuracy (30%)'))).toBe(true);
|
|
397
528
|
});
|
|
529
|
+
|
|
530
|
+
it('runs evidence collection for abandoned plans too', async () => {
|
|
531
|
+
const { collectGitEvidence } = await import('../planning/evidence-collector.js');
|
|
532
|
+
vi.mocked(collectGitEvidence).mockReturnValueOnce({
|
|
533
|
+
planId: 'plan-1',
|
|
534
|
+
planObjective: 'test',
|
|
535
|
+
accuracy: 60,
|
|
536
|
+
evidenceSources: ['git'],
|
|
537
|
+
taskEvidence: [
|
|
538
|
+
{
|
|
539
|
+
taskId: 't1',
|
|
540
|
+
taskTitle: 'Task 1',
|
|
541
|
+
plannedStatus: 'pending',
|
|
542
|
+
matchedFiles: [],
|
|
543
|
+
verdict: 'MISSING',
|
|
544
|
+
},
|
|
545
|
+
],
|
|
546
|
+
unplannedChanges: [],
|
|
547
|
+
missingWork: [],
|
|
548
|
+
verificationGaps: [],
|
|
549
|
+
summary: '0/1 tasks verified by git evidence',
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
const op = findOp(ops, 'orchestrate_complete');
|
|
553
|
+
const result = (await op.handler({
|
|
554
|
+
planId: 'plan-1',
|
|
555
|
+
sessionId: 'session-1',
|
|
556
|
+
outcome: 'abandoned',
|
|
557
|
+
projectPath: '.',
|
|
558
|
+
})) as Record<string, unknown>;
|
|
559
|
+
|
|
560
|
+
expect(collectGitEvidence).toHaveBeenCalled();
|
|
561
|
+
expect(result).toHaveProperty('evidenceReport');
|
|
562
|
+
const report = result.evidenceReport as Record<string, unknown>;
|
|
563
|
+
expect(report.accuracy).toBe(60);
|
|
564
|
+
expect(Array.isArray(report.taskEvidence)).toBe(true);
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
it('returns evidenceReport as null when no plan is provided', async () => {
|
|
568
|
+
const op = findOp(ops, 'orchestrate_complete');
|
|
569
|
+
const result = (await op.handler({
|
|
570
|
+
sessionId: 'session-1',
|
|
571
|
+
outcome: 'completed',
|
|
572
|
+
summary: 'Direct task without a plan',
|
|
573
|
+
})) as Record<string, unknown>;
|
|
574
|
+
|
|
575
|
+
expect(result).toHaveProperty('evidenceReport');
|
|
576
|
+
expect(result.evidenceReport).toBeNull();
|
|
577
|
+
});
|
|
398
578
|
});
|
|
399
579
|
|
|
400
580
|
// ─── orchestrate_status ───────────────────────────────────────
|
|
@@ -15,10 +15,10 @@ import { z } from 'zod';
|
|
|
15
15
|
import type { OpDefinition, FacadeConfig } from '../facades/types.js';
|
|
16
16
|
import type { AgentRuntime } from './types.js';
|
|
17
17
|
import { buildPlan } from '../flows/plan-builder.js';
|
|
18
|
-
import { FlowExecutor } from '../flows/executor.js';
|
|
18
|
+
import { FlowExecutor, getPlanRunDir, loadManifest, saveManifest } from '../flows/executor.js';
|
|
19
19
|
import { createDispatcher } from '../flows/dispatch-registry.js';
|
|
20
20
|
import { runEpilogue } from '../flows/epilogue.js';
|
|
21
|
-
import type { OrchestrationPlan, ExecutionResult } from '../flows/types.js';
|
|
21
|
+
import type { OrchestrationPlan, ExecutionResult, PlanRunManifest } from '../flows/types.js';
|
|
22
22
|
import type { ContextHealthStatus } from './context-health.js';
|
|
23
23
|
import type { OperatorSignals } from '../operator/operator-context-types.js';
|
|
24
24
|
import { loadAgentWorkflows, getWorkflowForIntent } from '../workflows/workflow-loader.js';
|
|
@@ -43,7 +43,11 @@ import type { ImpactReport } from '../planning/impact-analyzer.js';
|
|
|
43
43
|
import { collectGitEvidence } from '../planning/evidence-collector.js';
|
|
44
44
|
import type { EvidenceReport } from '../planning/evidence-collector.js';
|
|
45
45
|
import { recordPlanFeedback } from './plan-feedback-helper.js';
|
|
46
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
analyzeQualitySignals,
|
|
48
|
+
captureQualitySignals,
|
|
49
|
+
buildFixTrailSummary,
|
|
50
|
+
} from './quality-signals.js';
|
|
47
51
|
|
|
48
52
|
// ---------------------------------------------------------------------------
|
|
49
53
|
// Intent detection — keyword-based mapping from prompt to intent
|
|
@@ -125,6 +129,13 @@ export function applyWorkflowOverride(plan: OrchestrationPlan, override: Workflo
|
|
|
125
129
|
plan.estimatedTools = plan.steps.reduce((acc, s) => acc + s.tools.length, 0);
|
|
126
130
|
}
|
|
127
131
|
|
|
132
|
+
// Set allowedTools from the merged tool set
|
|
133
|
+
for (const step of plan.steps) {
|
|
134
|
+
if (step.tools.length > 0) {
|
|
135
|
+
step.allowedTools = [...new Set(step.tools)];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
128
139
|
// Add workflow info to warnings for visibility
|
|
129
140
|
plan.warnings.push(
|
|
130
141
|
`Workflow override "${override.name}" applied (${override.gates.length} gate(s), ${override.tools.length} tool(s)).`,
|
|
@@ -152,9 +163,14 @@ const planStore = new Map<string, PlanEntry>();
|
|
|
152
163
|
* If facades are provided, uses the full dispatch registry.
|
|
153
164
|
* Otherwise, falls back to a simple runtime-based dispatcher.
|
|
154
165
|
*/
|
|
155
|
-
function buildDispatch(
|
|
166
|
+
function buildDispatch(
|
|
167
|
+
agentId: string,
|
|
168
|
+
runtime: AgentRuntime,
|
|
169
|
+
facades?: FacadeConfig[],
|
|
170
|
+
activePlan?: import('../flows/dispatch-registry.js').ActivePlanRef,
|
|
171
|
+
) {
|
|
156
172
|
if (facades && facades.length > 0) {
|
|
157
|
-
return createDispatcher(agentId, facades);
|
|
173
|
+
return createDispatcher(agentId, facades, activePlan);
|
|
158
174
|
}
|
|
159
175
|
|
|
160
176
|
// Fallback: runtime-based dispatch for known tool patterns
|
|
@@ -530,10 +546,27 @@ export function createOrchestrateOps(
|
|
|
530
546
|
})) ??
|
|
531
547
|
[];
|
|
532
548
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
549
|
+
let aggregated;
|
|
550
|
+
let reapedOrphans: { taskId: string; pid?: number }[] = [];
|
|
551
|
+
try {
|
|
552
|
+
aggregated = await runtime.subagentDispatcher.dispatch(tasks, {
|
|
553
|
+
parallel: parallelMode ?? true,
|
|
554
|
+
maxConcurrent: maxConcurrentParam ?? 3,
|
|
555
|
+
});
|
|
556
|
+
} finally {
|
|
557
|
+
// Post-dispatch cleanup: reap orphaned subagent processes
|
|
558
|
+
try {
|
|
559
|
+
const reapResult = runtime.subagentDispatcher.reapOrphans();
|
|
560
|
+
if (reapResult.reaped.length > 0) {
|
|
561
|
+
reapedOrphans = reapResult.reaped.map((taskId) => ({ taskId }));
|
|
562
|
+
console.error(
|
|
563
|
+
`[soleri] Reaped ${reapResult.reaped.length} orphaned subagent(s): ${reapResult.reaped.join(', ')}`,
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
} catch {
|
|
567
|
+
// Orphan reaping is best-effort — never blocks dispatch result
|
|
568
|
+
}
|
|
569
|
+
}
|
|
537
570
|
|
|
538
571
|
// Track in brain session
|
|
539
572
|
const existingSession = brainIntelligence.getSessionByPlanId(planId);
|
|
@@ -565,6 +598,7 @@ export function createOrchestrateOps(
|
|
|
565
598
|
durationMs: aggregated.durationMs,
|
|
566
599
|
totalUsage: aggregated.totalUsage,
|
|
567
600
|
},
|
|
601
|
+
...(reapedOrphans.length > 0 ? { reapedOrphans } : {}),
|
|
568
602
|
...(healthWarning ? { contextHealth: healthWarning } : {}),
|
|
569
603
|
};
|
|
570
604
|
}
|
|
@@ -622,8 +656,17 @@ export function createOrchestrateOps(
|
|
|
622
656
|
|
|
623
657
|
if (entry) {
|
|
624
658
|
// Flow-engine execution path
|
|
625
|
-
const
|
|
626
|
-
|
|
659
|
+
const activePlanRef = {
|
|
660
|
+
steps: entry.plan.steps.map((s) => ({
|
|
661
|
+
id: s.id,
|
|
662
|
+
allowedTools: s.allowedTools,
|
|
663
|
+
status: s.status,
|
|
664
|
+
})),
|
|
665
|
+
deviations: entry.plan.deviations,
|
|
666
|
+
};
|
|
667
|
+
const dispatch = buildDispatch(agentId, runtime, facades, activePlanRef);
|
|
668
|
+
const projectPath = (params.projectPath as string) ?? '.';
|
|
669
|
+
const executor = new FlowExecutor(dispatch, projectPath);
|
|
627
670
|
const executionResult = await executor.execute(entry.plan);
|
|
628
671
|
|
|
629
672
|
// Store result
|
|
@@ -832,7 +875,7 @@ export function createOrchestrateOps(
|
|
|
832
875
|
|
|
833
876
|
// Evidence-based reconciliation: cross-reference plan tasks against git diff
|
|
834
877
|
let evidenceReport: EvidenceReport | null = null;
|
|
835
|
-
if (planObj
|
|
878
|
+
if (planObj) {
|
|
836
879
|
try {
|
|
837
880
|
evidenceReport = collectGitEvidence(
|
|
838
881
|
planObj,
|
|
@@ -840,6 +883,9 @@ export function createOrchestrateOps(
|
|
|
840
883
|
'main',
|
|
841
884
|
);
|
|
842
885
|
if (evidenceReport.accuracy < 50) {
|
|
886
|
+
console.error(
|
|
887
|
+
`[soleri] Evidence accuracy ${evidenceReport.accuracy}% — significant drift detected between plan and git state`,
|
|
888
|
+
);
|
|
843
889
|
warnings.push(
|
|
844
890
|
`Low evidence accuracy (${evidenceReport.accuracy}%) — plan tasks may not match git changes.`,
|
|
845
891
|
);
|
|
@@ -873,6 +919,7 @@ export function createOrchestrateOps(
|
|
|
873
919
|
}
|
|
874
920
|
|
|
875
921
|
// End brain session — runs regardless of plan existence
|
|
922
|
+
const fixTrail = evidenceReport ? buildFixTrailSummary(evidenceReport) : undefined;
|
|
876
923
|
const session = brainIntelligence.lifecycle({
|
|
877
924
|
action: 'end',
|
|
878
925
|
sessionId,
|
|
@@ -880,6 +927,7 @@ export function createOrchestrateOps(
|
|
|
880
927
|
planOutcome: outcome,
|
|
881
928
|
toolsUsed,
|
|
882
929
|
filesModified,
|
|
930
|
+
...(fixTrail ? { context: `Fix trail: ${fixTrail}` } : {}),
|
|
883
931
|
});
|
|
884
932
|
|
|
885
933
|
// Record brain feedback for vault entries referenced in plan decisions
|
|
@@ -974,7 +1022,7 @@ export function createOrchestrateOps(
|
|
|
974
1022
|
extraction,
|
|
975
1023
|
epilogue: epilogueResult,
|
|
976
1024
|
...(impactReport ? { impactAnalysis: impactReport } : {}),
|
|
977
|
-
|
|
1025
|
+
evidenceReport,
|
|
978
1026
|
...(warnings.length > 0 ? { warnings } : {}),
|
|
979
1027
|
};
|
|
980
1028
|
},
|
|
@@ -1317,5 +1365,114 @@ export function createOrchestrateOps(
|
|
|
1317
1365
|
};
|
|
1318
1366
|
},
|
|
1319
1367
|
},
|
|
1368
|
+
|
|
1369
|
+
// ─── orchestrate_rerun_step ──────────────────────────────────────
|
|
1370
|
+
{
|
|
1371
|
+
name: 'orchestrate_rerun_step',
|
|
1372
|
+
description:
|
|
1373
|
+
'Re-execute a single plan step without full restart. Marks the target step as invalidated then rerun, ' +
|
|
1374
|
+
'marks downstream steps as stale (or rerun if within cascadeTo range). ' +
|
|
1375
|
+
'Reads and writes the plan-run manifest on disk.',
|
|
1376
|
+
auth: 'write',
|
|
1377
|
+
schema: z.object({
|
|
1378
|
+
planId: z.string().describe('Plan ID'),
|
|
1379
|
+
stepNumber: z.number().describe('0-based step index to re-run'),
|
|
1380
|
+
reason: z.string().describe('Why the step is being re-run'),
|
|
1381
|
+
projectPath: z
|
|
1382
|
+
.string()
|
|
1383
|
+
.optional()
|
|
1384
|
+
.default('.')
|
|
1385
|
+
.describe('Project root (for manifest location)'),
|
|
1386
|
+
cascadeTo: z
|
|
1387
|
+
.number()
|
|
1388
|
+
.optional()
|
|
1389
|
+
.describe(
|
|
1390
|
+
'If set, also mark steps up to this index (exclusive) as rerun instead of just stale',
|
|
1391
|
+
),
|
|
1392
|
+
}),
|
|
1393
|
+
handler: async (params) => {
|
|
1394
|
+
const planId = params.planId as string;
|
|
1395
|
+
const stepNumber = params.stepNumber as number;
|
|
1396
|
+
const reason = params.reason as string;
|
|
1397
|
+
const projectPath = params.projectPath as string;
|
|
1398
|
+
const cascadeTo = params.cascadeTo as number | undefined;
|
|
1399
|
+
|
|
1400
|
+
const runDir = getPlanRunDir(projectPath, planId);
|
|
1401
|
+
let manifest: PlanRunManifest;
|
|
1402
|
+
try {
|
|
1403
|
+
manifest = loadManifest(runDir, planId);
|
|
1404
|
+
} catch (err) {
|
|
1405
|
+
return { error: `Failed to load manifest: ${(err as Error).message}` };
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
const stepKeys = Object.keys(manifest.steps);
|
|
1409
|
+
if (stepKeys.length === 0 && stepNumber > 0) {
|
|
1410
|
+
return {
|
|
1411
|
+
error:
|
|
1412
|
+
'No step data in manifest — the plan may not have been executed with persistence enabled.',
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
// Find the step key at the target index by checking all steps
|
|
1417
|
+
// Steps are keyed by stepId — we match by position in the plan
|
|
1418
|
+
const allStepIds = Object.keys(manifest.steps);
|
|
1419
|
+
const targetStepId = allStepIds[stepNumber];
|
|
1420
|
+
|
|
1421
|
+
if (!targetStepId && !manifest.steps[String(stepNumber)]) {
|
|
1422
|
+
return {
|
|
1423
|
+
error: `Step ${stepNumber} not found in manifest. Available steps: ${allStepIds.join(', ') || '(none)'}`,
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
const now = new Date().toISOString();
|
|
1428
|
+
const affected: { stepId: string; status: string }[] = [];
|
|
1429
|
+
|
|
1430
|
+
// Mark all steps by their position
|
|
1431
|
+
const sortedStepIds = Object.keys(manifest.steps);
|
|
1432
|
+
for (let i = 0; i < sortedStepIds.length; i++) {
|
|
1433
|
+
const sid = sortedStepIds[i];
|
|
1434
|
+
const state = manifest.steps[sid];
|
|
1435
|
+
|
|
1436
|
+
if (i === stepNumber) {
|
|
1437
|
+
// Target step: invalidated → rerun
|
|
1438
|
+
state.status = 'rerun';
|
|
1439
|
+
state.rerunCount += 1;
|
|
1440
|
+
state.rerunReason = reason;
|
|
1441
|
+
state.timestamp = now;
|
|
1442
|
+
affected.push({ stepId: sid, status: 'rerun' });
|
|
1443
|
+
} else if (i > stepNumber) {
|
|
1444
|
+
// Downstream step
|
|
1445
|
+
if (cascadeTo !== undefined && i < cascadeTo) {
|
|
1446
|
+
state.status = 'rerun';
|
|
1447
|
+
state.rerunCount += 1;
|
|
1448
|
+
state.rerunReason = `Cascade from step ${stepNumber}: ${reason}`;
|
|
1449
|
+
state.timestamp = now;
|
|
1450
|
+
affected.push({ stepId: sid, status: 'rerun' });
|
|
1451
|
+
} else {
|
|
1452
|
+
state.status = 'stale';
|
|
1453
|
+
state.timestamp = now;
|
|
1454
|
+
affected.push({ stepId: sid, status: 'stale' });
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
manifest.lastRun = now;
|
|
1460
|
+
|
|
1461
|
+
try {
|
|
1462
|
+
saveManifest(runDir, manifest);
|
|
1463
|
+
} catch (err) {
|
|
1464
|
+
return { error: `Failed to save manifest: ${(err as Error).message}` };
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
return {
|
|
1468
|
+
planId,
|
|
1469
|
+
stepNumber,
|
|
1470
|
+
reason,
|
|
1471
|
+
cascadeTo: cascadeTo ?? null,
|
|
1472
|
+
affected,
|
|
1473
|
+
manifestPath: runDir,
|
|
1474
|
+
};
|
|
1475
|
+
},
|
|
1476
|
+
},
|
|
1320
1477
|
];
|
|
1321
1478
|
}
|
|
@@ -9,10 +9,13 @@
|
|
|
9
9
|
* deliverable submission, and deliverable verification.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import fs from 'node:fs';
|
|
12
13
|
import { z } from 'zod';
|
|
13
14
|
import type { OpDefinition } from '../facades/types.js';
|
|
14
15
|
import type { AgentRuntime } from './types.js';
|
|
15
16
|
import type { DriftItem, TaskEvidence } from '../planning/planner.js';
|
|
17
|
+
import type { PlanRunManifest } from '../flows/types.js';
|
|
18
|
+
import { getPlanRunDir } from '../flows/executor.js';
|
|
16
19
|
import { collectGitEvidence } from '../planning/evidence-collector.js';
|
|
17
20
|
import { matchPlaybooks, type PlaybookMatchResult } from '../playbooks/index.js';
|
|
18
21
|
import { entryToPlaybookDefinition } from '../playbooks/index.js';
|
|
@@ -178,6 +181,11 @@ export function createPlanningExtraOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
178
181
|
schema: z.object({
|
|
179
182
|
planId: z.string().describe('Plan ID to reconcile'),
|
|
180
183
|
actualOutcome: z.string().describe('Description of what actually happened'),
|
|
184
|
+
projectPath: z
|
|
185
|
+
.string()
|
|
186
|
+
.optional()
|
|
187
|
+
.default('.')
|
|
188
|
+
.describe('Project root (for reading rerun manifest)'),
|
|
181
189
|
driftItems: z
|
|
182
190
|
.array(
|
|
183
191
|
z.object({
|
|
@@ -195,10 +203,79 @@ export function createPlanningExtraOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
195
203
|
actualOutcome: params.actualOutcome as string,
|
|
196
204
|
driftItems: params.driftItems as DriftItem[] | undefined,
|
|
197
205
|
});
|
|
206
|
+
|
|
207
|
+
// Add tool deviations to drift report
|
|
208
|
+
const deviations =
|
|
209
|
+
(
|
|
210
|
+
plan as unknown as {
|
|
211
|
+
deviations?: Array<{
|
|
212
|
+
stepId: string;
|
|
213
|
+
expectedTools: string[];
|
|
214
|
+
actualTool: string;
|
|
215
|
+
timestamp: string;
|
|
216
|
+
}>;
|
|
217
|
+
}
|
|
218
|
+
).deviations ?? [];
|
|
219
|
+
const toolDeviations =
|
|
220
|
+
deviations.length > 0
|
|
221
|
+
? {
|
|
222
|
+
count: deviations.length,
|
|
223
|
+
byStep: Object.entries(
|
|
224
|
+
deviations.reduce(
|
|
225
|
+
(acc, d) => {
|
|
226
|
+
(acc[d.stepId] = acc[d.stepId] || []).push(d);
|
|
227
|
+
return acc;
|
|
228
|
+
},
|
|
229
|
+
{} as Record<string, typeof deviations>,
|
|
230
|
+
),
|
|
231
|
+
).map(([stepId, devs]) => ({
|
|
232
|
+
stepId,
|
|
233
|
+
deviationCount: devs.length,
|
|
234
|
+
unexpectedTools: [...new Set(devs.map((d) => d.actualTool))],
|
|
235
|
+
})),
|
|
236
|
+
}
|
|
237
|
+
: undefined;
|
|
238
|
+
|
|
239
|
+
// Attach rerun report if a plan-run manifest exists
|
|
240
|
+
let rerunReport: {
|
|
241
|
+
totalReruns: number;
|
|
242
|
+
staleSteps: number;
|
|
243
|
+
steps: { stepId: string; status: string; rerunCount: number; rerunReason?: string }[];
|
|
244
|
+
} | null = null;
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
const projectPath = params.projectPath as string;
|
|
248
|
+
const runDir = getPlanRunDir(projectPath, params.planId as string);
|
|
249
|
+
const manifestPath = `${runDir}/manifest.json`;
|
|
250
|
+
if (fs.existsSync(manifestPath)) {
|
|
251
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) as PlanRunManifest;
|
|
252
|
+
const entries = Object.entries(manifest.steps);
|
|
253
|
+
const rerunSteps = entries.filter(
|
|
254
|
+
([, s]) => s.rerunCount > 0 || s.status === 'rerun' || s.status === 'stale',
|
|
255
|
+
);
|
|
256
|
+
if (rerunSteps.length > 0) {
|
|
257
|
+
rerunReport = {
|
|
258
|
+
totalReruns: rerunSteps.reduce((sum, [, s]) => sum + s.rerunCount, 0),
|
|
259
|
+
staleSteps: entries.filter(([, s]) => s.status === 'stale').length,
|
|
260
|
+
steps: rerunSteps.map(([id, s]) => ({
|
|
261
|
+
stepId: id,
|
|
262
|
+
status: s.status,
|
|
263
|
+
rerunCount: s.rerunCount,
|
|
264
|
+
rerunReason: s.rerunReason,
|
|
265
|
+
})),
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
} catch {
|
|
270
|
+
// Manifest reading is best-effort
|
|
271
|
+
}
|
|
272
|
+
|
|
198
273
|
return {
|
|
199
274
|
reconciled: true,
|
|
200
275
|
accuracy: plan.reconciliation!.accuracy,
|
|
201
276
|
driftCount: plan.reconciliation!.driftItems.length,
|
|
277
|
+
toolDeviations,
|
|
278
|
+
rerunReport,
|
|
202
279
|
plan,
|
|
203
280
|
};
|
|
204
281
|
},
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the pre-flight manifest for session_start responses.
|
|
3
|
+
* Extracted as a pure function for testability.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { PreflightManifest } from './types.js';
|
|
7
|
+
|
|
8
|
+
export interface PreflightInput {
|
|
9
|
+
/** Facade definitions: array of { name, ops: Array<{ name, description }> } */
|
|
10
|
+
facades: Array<{
|
|
11
|
+
name: string;
|
|
12
|
+
ops: Array<{ name: string; description: string }>;
|
|
13
|
+
}>;
|
|
14
|
+
/** Skill names installed for this agent */
|
|
15
|
+
skills: string[];
|
|
16
|
+
/** Plans currently in executing state */
|
|
17
|
+
executingPlans: Array<{ id: string; objective: string; status: string }>;
|
|
18
|
+
/** Whether the vault is connected */
|
|
19
|
+
vaultConnected?: boolean;
|
|
20
|
+
/** Vault stats */
|
|
21
|
+
vaultStats: {
|
|
22
|
+
totalEntries: number;
|
|
23
|
+
byDomain: Record<string, number>;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function buildPreflightManifest(input: PreflightInput): PreflightManifest {
|
|
28
|
+
const tools: PreflightManifest['tools'] = [];
|
|
29
|
+
for (const facade of input.facades) {
|
|
30
|
+
for (const op of facade.ops) {
|
|
31
|
+
tools.push({ facade: facade.name, op: op.name, description: op.description });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const activePlans = input.executingPlans.map((p) => ({
|
|
36
|
+
planId: p.id,
|
|
37
|
+
title: p.objective,
|
|
38
|
+
status: p.status,
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
const domains = Object.keys(input.vaultStats.byDomain);
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
tools,
|
|
45
|
+
skills: input.skills,
|
|
46
|
+
activePlans,
|
|
47
|
+
vaultSummary: {
|
|
48
|
+
entryCount: input.vaultStats.totalEntries,
|
|
49
|
+
connected: input.vaultConnected ?? true,
|
|
50
|
+
domains,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|