@soleri/core 9.4.0 → 9.6.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/dist/adapters/claude-code-adapter.d.ts +27 -0
- package/dist/adapters/claude-code-adapter.d.ts.map +1 -0
- package/dist/adapters/claude-code-adapter.js +111 -0
- package/dist/adapters/claude-code-adapter.js.map +1 -0
- package/dist/adapters/index.d.ts +9 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +10 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/registry.d.ts +21 -0
- package/dist/adapters/registry.d.ts.map +1 -0
- package/dist/adapters/registry.js +44 -0
- package/dist/adapters/registry.js.map +1 -0
- package/dist/adapters/types.d.ts +93 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +10 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/brain/brain.d.ts +12 -1
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +106 -44
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +36 -30
- package/dist/brain/intelligence.js.map +1 -1
- package/dist/chat/agent-loop.js +1 -1
- package/dist/chat/agent-loop.js.map +1 -1
- package/dist/chat/notifications.d.ts.map +1 -1
- package/dist/chat/notifications.js +4 -0
- package/dist/chat/notifications.js.map +1 -1
- package/dist/control/intent-router.d.ts +1 -0
- package/dist/control/intent-router.d.ts.map +1 -1
- package/dist/control/intent-router.js +11 -5
- package/dist/control/intent-router.js.map +1 -1
- package/dist/curator/curator.d.ts +4 -0
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +141 -27
- package/dist/curator/curator.js.map +1 -1
- package/dist/hooks/candidate-scorer.d.ts +28 -0
- package/dist/hooks/candidate-scorer.d.ts.map +1 -0
- package/dist/hooks/candidate-scorer.js +20 -0
- package/dist/hooks/candidate-scorer.js.map +1 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.d.ts +14 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/llm/llm-client.d.ts.map +1 -1
- package/dist/llm/llm-client.js +1 -0
- package/dist/llm/llm-client.js.map +1 -1
- package/dist/packs/index.d.ts +3 -2
- package/dist/packs/index.d.ts.map +1 -1
- package/dist/packs/index.js +3 -2
- package/dist/packs/index.js.map +1 -1
- package/dist/packs/lockfile.d.ts +23 -1
- package/dist/packs/lockfile.d.ts.map +1 -1
- package/dist/packs/lockfile.js +50 -4
- package/dist/packs/lockfile.js.map +1 -1
- package/dist/packs/pack-installer.d.ts +10 -0
- package/dist/packs/pack-installer.d.ts.map +1 -1
- package/dist/packs/pack-installer.js +69 -2
- package/dist/packs/pack-installer.js.map +1 -1
- package/dist/packs/pack-lifecycle.d.ts +50 -0
- package/dist/packs/pack-lifecycle.d.ts.map +1 -0
- package/dist/packs/pack-lifecycle.js +91 -0
- package/dist/packs/pack-lifecycle.js.map +1 -0
- package/dist/packs/types.d.ts +64 -44
- package/dist/packs/types.d.ts.map +1 -1
- package/dist/packs/types.js +9 -0
- package/dist/packs/types.js.map +1 -1
- package/dist/persistence/sqlite-provider.d.ts +5 -1
- package/dist/persistence/sqlite-provider.d.ts.map +1 -1
- package/dist/persistence/sqlite-provider.js +22 -2
- package/dist/persistence/sqlite-provider.js.map +1 -1
- package/dist/planning/github-projection.d.ts +8 -8
- package/dist/planning/github-projection.d.ts.map +1 -1
- package/dist/planning/github-projection.js +42 -42
- package/dist/planning/github-projection.js.map +1 -1
- package/dist/planning/plan-lifecycle.d.ts.map +1 -1
- package/dist/planning/plan-lifecycle.js +6 -1
- package/dist/planning/plan-lifecycle.js.map +1 -1
- package/dist/plugins/types.d.ts +21 -21
- package/dist/queue/pipeline-runner.d.ts.map +1 -1
- package/dist/queue/pipeline-runner.js +4 -0
- package/dist/queue/pipeline-runner.js.map +1 -1
- package/dist/runtime/curator-extra-ops.d.ts.map +1 -1
- package/dist/runtime/curator-extra-ops.js +9 -1
- package/dist/runtime/curator-extra-ops.js.map +1 -1
- package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
- package/dist/runtime/facades/memory-facade.js +169 -0
- package/dist/runtime/facades/memory-facade.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +133 -4
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +128 -90
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/session-briefing.d.ts.map +1 -1
- package/dist/runtime/session-briefing.js +44 -11
- package/dist/runtime/session-briefing.js.map +1 -1
- package/dist/runtime/shutdown-registry.d.ts +36 -0
- package/dist/runtime/shutdown-registry.d.ts.map +1 -0
- package/dist/runtime/shutdown-registry.js +74 -0
- package/dist/runtime/shutdown-registry.js.map +1 -0
- package/dist/runtime/types.d.ts +10 -1
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/subagent/concurrency-manager.d.ts +29 -0
- package/dist/subagent/concurrency-manager.d.ts.map +1 -0
- package/dist/subagent/concurrency-manager.js +73 -0
- package/dist/subagent/concurrency-manager.js.map +1 -0
- package/dist/subagent/dispatcher.d.ts +41 -0
- package/dist/subagent/dispatcher.d.ts.map +1 -0
- package/dist/subagent/dispatcher.js +259 -0
- package/dist/subagent/dispatcher.js.map +1 -0
- package/dist/subagent/index.d.ts +14 -0
- package/dist/subagent/index.d.ts.map +1 -0
- package/dist/subagent/index.js +15 -0
- package/dist/subagent/index.js.map +1 -0
- package/dist/subagent/orphan-reaper.d.ts +37 -0
- package/dist/subagent/orphan-reaper.d.ts.map +1 -0
- package/dist/subagent/orphan-reaper.js +71 -0
- package/dist/subagent/orphan-reaper.js.map +1 -0
- package/dist/subagent/result-aggregator.d.ts +7 -0
- package/dist/subagent/result-aggregator.d.ts.map +1 -0
- package/dist/subagent/result-aggregator.js +57 -0
- package/dist/subagent/result-aggregator.js.map +1 -0
- package/dist/subagent/task-checkout.d.ts +36 -0
- package/dist/subagent/task-checkout.d.ts.map +1 -0
- package/dist/subagent/task-checkout.js +52 -0
- package/dist/subagent/task-checkout.js.map +1 -0
- package/dist/subagent/types.d.ts +114 -0
- package/dist/subagent/types.d.ts.map +1 -0
- package/dist/subagent/types.js +9 -0
- package/dist/subagent/types.js.map +1 -0
- package/dist/subagent/workspace-resolver.d.ts +35 -0
- package/dist/subagent/workspace-resolver.d.ts.map +1 -0
- package/dist/subagent/workspace-resolver.js +99 -0
- package/dist/subagent/workspace-resolver.js.map +1 -0
- package/dist/transport/http-server.d.ts.map +1 -1
- package/dist/transport/http-server.js +49 -3
- package/dist/transport/http-server.js.map +1 -1
- package/dist/transport/ws-server.d.ts.map +1 -1
- package/dist/transport/ws-server.js +7 -0
- package/dist/transport/ws-server.js.map +1 -1
- package/dist/vault/linking.d.ts +3 -4
- package/dist/vault/linking.d.ts.map +1 -1
- package/dist/vault/linking.js +79 -32
- package/dist/vault/linking.js.map +1 -1
- package/dist/vault/vault-maintenance.d.ts.map +1 -1
- package/dist/vault/vault-maintenance.js +7 -14
- package/dist/vault/vault-maintenance.js.map +1 -1
- package/dist/vault/vault-memories.d.ts.map +1 -1
- package/dist/vault/vault-memories.js +19 -9
- package/dist/vault/vault-memories.js.map +1 -1
- package/dist/vault/vault-schema.d.ts +1 -0
- package/dist/vault/vault-schema.d.ts.map +1 -1
- package/dist/vault/vault-schema.js +20 -0
- package/dist/vault/vault-schema.js.map +1 -1
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +7 -3
- package/dist/vault/vault.js.map +1 -1
- package/package.json +8 -2
- package/src/__tests__/adapters/claude-code-adapter.test.ts +167 -0
- package/src/__tests__/adapters/registry.test.ts +100 -0
- package/src/__tests__/packs/pack-lifecycle.test.ts +379 -0
- package/src/__tests__/subagent/concurrency-manager.test.ts +132 -0
- package/src/__tests__/subagent/dispatcher.test.ts +195 -0
- package/src/__tests__/subagent/orphan-reaper.test.ts +141 -0
- package/src/__tests__/subagent/result-aggregator.test.ts +141 -0
- package/src/__tests__/subagent/task-checkout.test.ts +86 -0
- package/src/__tests__/subagent/workspace-resolver.test.ts +138 -0
- package/src/adapters/claude-code-adapter.ts +163 -0
- package/src/adapters/index.ts +22 -0
- package/src/adapters/registry.ts +53 -0
- package/src/adapters/types.ts +114 -0
- package/src/brain/brain.ts +120 -46
- package/src/brain/intelligence.ts +42 -34
- package/src/chat/agent-loop.ts +1 -1
- package/src/chat/notifications.ts +4 -0
- package/src/control/intent-router.ts +10 -8
- package/src/curator/curator.ts +146 -29
- package/src/hooks/candidate-scorer.test.ts +76 -0
- package/src/hooks/candidate-scorer.ts +39 -0
- package/src/index.ts +40 -1
- package/src/llm/llm-client.ts +1 -0
- package/src/packs/index.ts +5 -1
- package/src/packs/lockfile.ts +70 -5
- package/src/packs/pack-installer.ts +78 -2
- package/src/packs/pack-lifecycle.ts +115 -0
- package/src/packs/pack-lockfile.test.ts +1 -1
- package/src/packs/pack-system.test.ts +1 -1
- package/src/packs/types.ts +40 -2
- package/src/persistence/sqlite-provider.ts +27 -2
- package/src/planning/github-projection.ts +48 -44
- package/src/planning/plan-lifecycle.ts +14 -1
- package/src/queue/pipeline-runner.ts +4 -0
- package/src/runtime/admin-setup-ops.test.ts +9 -4
- package/src/runtime/curator-extra-ops.test.ts +7 -0
- package/src/runtime/curator-extra-ops.ts +10 -1
- package/src/runtime/facades/curator-facade.test.ts +7 -0
- package/src/runtime/facades/memory-facade.ts +187 -0
- package/src/runtime/orchestrate-ops.ts +156 -4
- package/src/runtime/runtime.test.ts +50 -2
- package/src/runtime/runtime.ts +132 -89
- package/src/runtime/session-briefing.test.ts +94 -2
- package/src/runtime/session-briefing.ts +48 -12
- package/src/runtime/shutdown-registry.test.ts +151 -0
- package/src/runtime/shutdown-registry.ts +85 -0
- package/src/runtime/types.ts +10 -1
- package/src/subagent/concurrency-manager.ts +89 -0
- package/src/subagent/dispatcher.ts +326 -0
- package/src/subagent/index.ts +28 -0
- package/src/subagent/orphan-reaper.ts +82 -0
- package/src/subagent/result-aggregator.ts +66 -0
- package/src/subagent/task-checkout.ts +60 -0
- package/src/subagent/types.ts +138 -0
- package/src/subagent/workspace-resolver.ts +117 -0
- package/src/transport/http-server.ts +50 -3
- package/src/transport/ws-server.ts +8 -0
- package/src/vault/linking.test.ts +12 -0
- package/src/vault/linking.ts +90 -44
- package/src/vault/vault-maintenance.ts +11 -18
- package/src/vault/vault-memories.ts +21 -13
- package/src/vault/vault-scaling.test.ts +3 -2
- package/src/vault/vault-schema.ts +21 -0
- package/src/vault/vault.ts +8 -3
- package/vitest.config.ts +2 -0
|
@@ -10,9 +10,18 @@ import type { OpDefinition } from '../facades/types.js';
|
|
|
10
10
|
import type { AgentRuntime } from './types.js';
|
|
11
11
|
|
|
12
12
|
export function createCuratorExtraOps(runtime: AgentRuntime): OpDefinition[] {
|
|
13
|
-
const { curator, jobQueue, pipelineRunner } = runtime;
|
|
13
|
+
const { curator, jobQueue, pipelineRunner, shutdownRegistry } = runtime;
|
|
14
14
|
let consolidationInterval: ReturnType<typeof setInterval> | null = null;
|
|
15
15
|
|
|
16
|
+
// Register cleanup for any consolidation interval started during this session
|
|
17
|
+
shutdownRegistry.register('curatorConsolidation', () => {
|
|
18
|
+
if (consolidationInterval) {
|
|
19
|
+
clearInterval(consolidationInterval);
|
|
20
|
+
consolidationInterval = null;
|
|
21
|
+
}
|
|
22
|
+
pipelineRunner.stop();
|
|
23
|
+
});
|
|
24
|
+
|
|
16
25
|
return [
|
|
17
26
|
// ─── Entry History ──────────────────────────────────────────────
|
|
18
27
|
{
|
|
@@ -30,6 +30,13 @@ function mockRuntime(): AgentRuntime {
|
|
|
30
30
|
start: vi.fn(),
|
|
31
31
|
stop: vi.fn(),
|
|
32
32
|
},
|
|
33
|
+
shutdownRegistry: {
|
|
34
|
+
register: vi.fn(),
|
|
35
|
+
closeAll: vi.fn(),
|
|
36
|
+
closeAllSync: vi.fn(),
|
|
37
|
+
size: 0,
|
|
38
|
+
isClosed: false,
|
|
39
|
+
},
|
|
33
40
|
} as unknown as AgentRuntime;
|
|
34
41
|
}
|
|
35
42
|
|
|
@@ -195,6 +195,193 @@ export function createMemoryFacadeOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
195
195
|
},
|
|
196
196
|
},
|
|
197
197
|
|
|
198
|
+
// ─── Handoff ────────────────────────────────────────────────
|
|
199
|
+
{
|
|
200
|
+
name: 'handoff_generate',
|
|
201
|
+
description:
|
|
202
|
+
'Generate a structured handoff document for context transitions. ' +
|
|
203
|
+
'Pulls from active plan (if any) and recent session memories to produce ' +
|
|
204
|
+
'a markdown document that can bootstrap a new context window. ' +
|
|
205
|
+
'Ephemeral — NOT persisted to vault.',
|
|
206
|
+
auth: 'read',
|
|
207
|
+
schema: z.object({
|
|
208
|
+
projectPath: z
|
|
209
|
+
.string()
|
|
210
|
+
.optional()
|
|
211
|
+
.default('.')
|
|
212
|
+
.describe('Project path for filtering memories'),
|
|
213
|
+
sessionLimit: z
|
|
214
|
+
.number()
|
|
215
|
+
.optional()
|
|
216
|
+
.default(3)
|
|
217
|
+
.describe('Number of recent session memories to include'),
|
|
218
|
+
}),
|
|
219
|
+
handler: async (params) => {
|
|
220
|
+
const { planner } = runtime;
|
|
221
|
+
const projectPath = params.projectPath as string;
|
|
222
|
+
const sessionLimit = (params.sessionLimit as number) ?? 3;
|
|
223
|
+
|
|
224
|
+
const sections: string[] = [];
|
|
225
|
+
const now = new Date().toISOString();
|
|
226
|
+
|
|
227
|
+
sections.push('# Handoff Document');
|
|
228
|
+
sections.push('');
|
|
229
|
+
sections.push(`Generated: ${now}`);
|
|
230
|
+
sections.push('');
|
|
231
|
+
|
|
232
|
+
// ─── Active Plan Context ───────────────────────────
|
|
233
|
+
const activePlans = planner.getActive();
|
|
234
|
+
if (activePlans.length > 0) {
|
|
235
|
+
const plan = activePlans[0]; // Most relevant active plan
|
|
236
|
+
sections.push('## Active Plan');
|
|
237
|
+
sections.push('');
|
|
238
|
+
sections.push(`| Field | Value |`);
|
|
239
|
+
sections.push(`|-------|-------|`);
|
|
240
|
+
sections.push(`| **Plan ID** | ${plan.id} |`);
|
|
241
|
+
sections.push(`| **Objective** | ${plan.objective} |`);
|
|
242
|
+
sections.push(`| **Status** | ${plan.status} |`);
|
|
243
|
+
sections.push(`| **Scope** | ${plan.scope} |`);
|
|
244
|
+
sections.push('');
|
|
245
|
+
|
|
246
|
+
// Decisions
|
|
247
|
+
if (plan.decisions.length > 0) {
|
|
248
|
+
sections.push('### Decisions');
|
|
249
|
+
sections.push('');
|
|
250
|
+
for (const d of plan.decisions) {
|
|
251
|
+
if (typeof d === 'string') {
|
|
252
|
+
sections.push(`- ${d}`);
|
|
253
|
+
} else {
|
|
254
|
+
sections.push(`- **${d.decision}** — ${d.rationale}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
sections.push('');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Task status summary
|
|
261
|
+
if (plan.tasks.length > 0) {
|
|
262
|
+
sections.push('### Tasks');
|
|
263
|
+
sections.push('');
|
|
264
|
+
sections.push('| # | Task | Status |');
|
|
265
|
+
sections.push('|---|------|--------|');
|
|
266
|
+
for (let i = 0; i < plan.tasks.length; i++) {
|
|
267
|
+
const t = plan.tasks[i];
|
|
268
|
+
sections.push(`| ${i + 1} | ${t.title} | ${t.status} |`);
|
|
269
|
+
}
|
|
270
|
+
sections.push('');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Approach
|
|
274
|
+
if (plan.approach) {
|
|
275
|
+
sections.push('### Approach');
|
|
276
|
+
sections.push('');
|
|
277
|
+
sections.push(plan.approach);
|
|
278
|
+
sections.push('');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Additional active plans (just IDs)
|
|
282
|
+
if (activePlans.length > 1) {
|
|
283
|
+
sections.push('### Other Active Plans');
|
|
284
|
+
sections.push('');
|
|
285
|
+
for (let i = 1; i < activePlans.length; i++) {
|
|
286
|
+
const p = activePlans[i];
|
|
287
|
+
sections.push(`- **${p.id}**: ${p.objective} (${p.status})`);
|
|
288
|
+
}
|
|
289
|
+
sections.push('');
|
|
290
|
+
}
|
|
291
|
+
} else {
|
|
292
|
+
sections.push('## Active Plan');
|
|
293
|
+
sections.push('');
|
|
294
|
+
sections.push('No active plans.');
|
|
295
|
+
sections.push('');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ─── Recent Session Context ────────────────────────
|
|
299
|
+
const recentSessions = vault.listMemories({
|
|
300
|
+
type: 'session',
|
|
301
|
+
projectPath,
|
|
302
|
+
limit: sessionLimit,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
if (recentSessions.length > 0) {
|
|
306
|
+
sections.push('## Recent Sessions');
|
|
307
|
+
sections.push('');
|
|
308
|
+
for (const session of recentSessions) {
|
|
309
|
+
sections.push(`### ${session.createdAt}`);
|
|
310
|
+
sections.push('');
|
|
311
|
+
if (session.summary) {
|
|
312
|
+
sections.push(session.summary);
|
|
313
|
+
sections.push('');
|
|
314
|
+
}
|
|
315
|
+
if (session.nextSteps && session.nextSteps.length > 0) {
|
|
316
|
+
sections.push('**Next steps:**');
|
|
317
|
+
for (const step of session.nextSteps) {
|
|
318
|
+
sections.push(`- ${step}`);
|
|
319
|
+
}
|
|
320
|
+
sections.push('');
|
|
321
|
+
}
|
|
322
|
+
if (session.decisions && session.decisions.length > 0) {
|
|
323
|
+
sections.push('**Decisions:**');
|
|
324
|
+
for (const d of session.decisions) {
|
|
325
|
+
sections.push(`- ${d}`);
|
|
326
|
+
}
|
|
327
|
+
sections.push('');
|
|
328
|
+
}
|
|
329
|
+
if (session.filesModified && session.filesModified.length > 0) {
|
|
330
|
+
sections.push(`**Files modified:** ${session.filesModified.join(', ')}`);
|
|
331
|
+
sections.push('');
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
sections.push('## Recent Sessions');
|
|
336
|
+
sections.push('');
|
|
337
|
+
sections.push('No recent session memories found.');
|
|
338
|
+
sections.push('');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ─── Resumption Hints ──────────────────────────────
|
|
342
|
+
sections.push('## Resumption');
|
|
343
|
+
sections.push('');
|
|
344
|
+
sections.push('Use this document to restore context after a context window transition.');
|
|
345
|
+
sections.push('');
|
|
346
|
+
if (activePlans.length > 0) {
|
|
347
|
+
const plan = activePlans[0];
|
|
348
|
+
const pendingTasks = plan.tasks.filter(
|
|
349
|
+
(t) => t.status === 'pending' || t.status === 'in_progress',
|
|
350
|
+
);
|
|
351
|
+
if (pendingTasks.length > 0) {
|
|
352
|
+
sections.push('**Immediate next actions:**');
|
|
353
|
+
for (const t of pendingTasks.slice(0, 5)) {
|
|
354
|
+
sections.push(
|
|
355
|
+
`- ${t.status === 'in_progress' ? '[IN PROGRESS]' : '[PENDING]'} ${t.title}`,
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
sections.push('');
|
|
359
|
+
}
|
|
360
|
+
if (plan.status === 'executing') {
|
|
361
|
+
sections.push(
|
|
362
|
+
'> Plan is in `executing` state. Continue with pending tasks or call `op:plan_reconcile` if complete.',
|
|
363
|
+
);
|
|
364
|
+
} else if (plan.status === 'reconciling') {
|
|
365
|
+
sections.push(
|
|
366
|
+
'> Plan is in `reconciling` state. Call `op:plan_complete_lifecycle` to finalize.',
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const markdown = sections.join('\n');
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
handoff: markdown,
|
|
375
|
+
meta: {
|
|
376
|
+
activePlanCount: activePlans.length,
|
|
377
|
+
activePlanId: activePlans.length > 0 ? activePlans[0].id : null,
|
|
378
|
+
recentSessionCount: recentSessions.length,
|
|
379
|
+
generatedAt: now,
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
|
|
198
385
|
// ─── Satellite ops ───────────────────────────────────────────
|
|
199
386
|
...createMemoryExtraOps(runtime),
|
|
200
387
|
...createMemoryCrossProjectOps(runtime),
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
import { detectRationalizations } from '../planning/rationalization-detector.js';
|
|
39
39
|
import { ImpactAnalyzer } from '../planning/impact-analyzer.js';
|
|
40
40
|
import type { ImpactReport } from '../planning/impact-analyzer.js';
|
|
41
|
+
import { recordPlanFeedback } from './plan-feedback-helper.js';
|
|
41
42
|
|
|
42
43
|
// ---------------------------------------------------------------------------
|
|
43
44
|
// Intent detection — keyword-based mapping from prompt to intent
|
|
@@ -235,7 +236,7 @@ export function createOrchestrateOps(
|
|
|
235
236
|
runtime: AgentRuntime,
|
|
236
237
|
facades?: FacadeConfig[],
|
|
237
238
|
): OpDefinition[] {
|
|
238
|
-
const { planner, brainIntelligence, vault, contextHealth } = runtime;
|
|
239
|
+
const { planner, brain, brainIntelligence, vault, contextHealth } = runtime;
|
|
239
240
|
const agentId = runtime.config.agentId;
|
|
240
241
|
|
|
241
242
|
return [
|
|
@@ -385,11 +386,149 @@ export function createOrchestrateOps(
|
|
|
385
386
|
planId: z.string().describe('ID of the plan to execute (flow planId or legacy planId)'),
|
|
386
387
|
domain: z.string().optional().describe('Domain for brain session tracking'),
|
|
387
388
|
context: z.string().optional().describe('Additional context for the brain session'),
|
|
389
|
+
runtime: z
|
|
390
|
+
.string()
|
|
391
|
+
.optional()
|
|
392
|
+
.describe(
|
|
393
|
+
'Runtime adapter type (e.g. "claude-code", "codex"). ' +
|
|
394
|
+
'When provided, dispatches via the adapter instead of the flow engine.',
|
|
395
|
+
),
|
|
396
|
+
subagent: z
|
|
397
|
+
.boolean()
|
|
398
|
+
.optional()
|
|
399
|
+
.describe(
|
|
400
|
+
'When true, dispatches plan tasks via SubagentDispatcher instead of FlowExecutor. ' +
|
|
401
|
+
'Each task runs as a separate subagent process.',
|
|
402
|
+
),
|
|
403
|
+
parallel: z
|
|
404
|
+
.boolean()
|
|
405
|
+
.optional()
|
|
406
|
+
.describe(
|
|
407
|
+
'Run subagent tasks in parallel (default: true). Only applies when subagent=true.',
|
|
408
|
+
),
|
|
409
|
+
maxConcurrent: z
|
|
410
|
+
.number()
|
|
411
|
+
.optional()
|
|
412
|
+
.describe('Max concurrent subagents (default: 3). Only applies when subagent=true.'),
|
|
388
413
|
}),
|
|
389
414
|
handler: async (params) => {
|
|
390
415
|
const planId = params.planId as string;
|
|
391
416
|
const domain = params.domain as string | undefined;
|
|
392
417
|
const context = params.context as string | undefined;
|
|
418
|
+
const runtimeType = params.runtime as string | undefined;
|
|
419
|
+
const useSubagent = params.subagent as boolean | undefined;
|
|
420
|
+
const parallelMode = params.parallel as boolean | undefined;
|
|
421
|
+
const maxConcurrentParam = params.maxConcurrent as number | undefined;
|
|
422
|
+
|
|
423
|
+
// ── Subagent dispatch path ───────────────────────────────────
|
|
424
|
+
// When subagent=true, dispatch plan tasks via SubagentDispatcher.
|
|
425
|
+
// Each task runs as a separate child process via the adapter layer.
|
|
426
|
+
if (useSubagent && runtime.subagentDispatcher) {
|
|
427
|
+
const entry = planStore.get(planId);
|
|
428
|
+
const legacyPlan = !entry ? planner.get(planId) : undefined;
|
|
429
|
+
const tasks =
|
|
430
|
+
entry?.plan.steps.map((s) => ({
|
|
431
|
+
taskId: s.id,
|
|
432
|
+
prompt: s.name,
|
|
433
|
+
workspace: process.cwd(),
|
|
434
|
+
runtime: runtimeType,
|
|
435
|
+
timeout: 300_000,
|
|
436
|
+
})) ??
|
|
437
|
+
legacyPlan?.tasks?.map((t) => ({
|
|
438
|
+
taskId: t.id,
|
|
439
|
+
prompt: t.title ?? t.description ?? '',
|
|
440
|
+
workspace: process.cwd(),
|
|
441
|
+
runtime: runtimeType,
|
|
442
|
+
timeout: 300_000,
|
|
443
|
+
})) ??
|
|
444
|
+
[];
|
|
445
|
+
|
|
446
|
+
const aggregated = await runtime.subagentDispatcher.dispatch(tasks, {
|
|
447
|
+
parallel: parallelMode ?? true,
|
|
448
|
+
maxConcurrent: maxConcurrentParam ?? 3,
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
// Track in brain session
|
|
452
|
+
const existingSession = brainIntelligence.getSessionByPlanId(planId);
|
|
453
|
+
const session =
|
|
454
|
+
existingSession && !existingSession.endedAt
|
|
455
|
+
? existingSession
|
|
456
|
+
: brainIntelligence.lifecycle({
|
|
457
|
+
action: 'start',
|
|
458
|
+
domain,
|
|
459
|
+
context,
|
|
460
|
+
planId,
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
contextHealth.track({
|
|
464
|
+
type: 'orchestrate_execute',
|
|
465
|
+
payloadSize: JSON.stringify(aggregated).length,
|
|
466
|
+
});
|
|
467
|
+
const healthStatus = contextHealth.check();
|
|
468
|
+
const healthWarning = buildHealthWarning(healthStatus, vault);
|
|
469
|
+
|
|
470
|
+
return {
|
|
471
|
+
plan: { id: planId, status: 'executing' },
|
|
472
|
+
session,
|
|
473
|
+
subagent: {
|
|
474
|
+
status: aggregated.status,
|
|
475
|
+
totalTasks: aggregated.totalTasks,
|
|
476
|
+
completed: aggregated.completed,
|
|
477
|
+
failed: aggregated.failed,
|
|
478
|
+
durationMs: aggregated.durationMs,
|
|
479
|
+
totalUsage: aggregated.totalUsage,
|
|
480
|
+
},
|
|
481
|
+
...(healthWarning ? { contextHealth: healthWarning } : {}),
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// ── Adapter dispatch path ────────────────────────────────────
|
|
486
|
+
// When a runtime is specified, dispatch the plan's prompt via the
|
|
487
|
+
// adapter instead of the flow engine. This is the integration point
|
|
488
|
+
// for multi-runtime support (GH #410).
|
|
489
|
+
if (runtimeType && runtime.adapterRegistry) {
|
|
490
|
+
const adapter = runtime.adapterRegistry.get(runtimeType);
|
|
491
|
+
const entry = planStore.get(planId);
|
|
492
|
+
const prompt = entry?.plan.summary ?? `Execute plan ${planId}`;
|
|
493
|
+
|
|
494
|
+
const adapterResult = await adapter.execute({
|
|
495
|
+
runId: `${planId}-${Date.now()}`,
|
|
496
|
+
prompt,
|
|
497
|
+
workspace: process.cwd(),
|
|
498
|
+
config: { planId, domain },
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// Track in brain session
|
|
502
|
+
const existingSession = brainIntelligence.getSessionByPlanId(planId);
|
|
503
|
+
const session =
|
|
504
|
+
existingSession && !existingSession.endedAt
|
|
505
|
+
? existingSession
|
|
506
|
+
: brainIntelligence.lifecycle({
|
|
507
|
+
action: 'start',
|
|
508
|
+
domain,
|
|
509
|
+
context,
|
|
510
|
+
planId,
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
contextHealth.track({
|
|
514
|
+
type: 'orchestrate_execute',
|
|
515
|
+
payloadSize: JSON.stringify(adapterResult).length,
|
|
516
|
+
});
|
|
517
|
+
const healthStatus = contextHealth.check();
|
|
518
|
+
const healthWarning = buildHealthWarning(healthStatus, vault);
|
|
519
|
+
|
|
520
|
+
return {
|
|
521
|
+
plan: { id: planId, status: 'executing' },
|
|
522
|
+
session,
|
|
523
|
+
adapter: {
|
|
524
|
+
type: runtimeType,
|
|
525
|
+
exitCode: adapterResult.exitCode,
|
|
526
|
+
summary: adapterResult.summary,
|
|
527
|
+
usage: adapterResult.usage,
|
|
528
|
+
},
|
|
529
|
+
...(healthWarning ? { contextHealth: healthWarning } : {}),
|
|
530
|
+
};
|
|
531
|
+
}
|
|
393
532
|
|
|
394
533
|
// Look up flow plan
|
|
395
534
|
const entry = planStore.get(planId);
|
|
@@ -636,6 +775,19 @@ export function createOrchestrateOps(
|
|
|
636
775
|
filesModified,
|
|
637
776
|
});
|
|
638
777
|
|
|
778
|
+
// Record brain feedback for vault entries referenced in plan decisions
|
|
779
|
+
if (planObj && planObj.decisions) {
|
|
780
|
+
try {
|
|
781
|
+
recordPlanFeedback(
|
|
782
|
+
{ objective: planObj.objective, decisions: planObj.decisions },
|
|
783
|
+
brain,
|
|
784
|
+
brainIntelligence,
|
|
785
|
+
);
|
|
786
|
+
} catch {
|
|
787
|
+
// Brain feedback is best-effort
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
639
791
|
// Extract knowledge — runs regardless of plan existence
|
|
640
792
|
let extraction = null;
|
|
641
793
|
try {
|
|
@@ -847,7 +999,7 @@ export function createOrchestrateOps(
|
|
|
847
999
|
}
|
|
848
1000
|
|
|
849
1001
|
// 2. Detect GitHub context
|
|
850
|
-
const ctx = detectGitHubContext(projectPath);
|
|
1002
|
+
const ctx = await detectGitHubContext(projectPath);
|
|
851
1003
|
if (!ctx) {
|
|
852
1004
|
return {
|
|
853
1005
|
status: 'skipped',
|
|
@@ -885,7 +1037,7 @@ export function createOrchestrateOps(
|
|
|
885
1037
|
};
|
|
886
1038
|
}
|
|
887
1039
|
|
|
888
|
-
const updated = updateGitHubIssueBody(ctx.repo, linkToIssue, body);
|
|
1040
|
+
const updated = await updateGitHubIssueBody(ctx.repo, linkToIssue, body);
|
|
889
1041
|
if (!updated) {
|
|
890
1042
|
return {
|
|
891
1043
|
status: 'error',
|
|
@@ -949,7 +1101,7 @@ export function createOrchestrateOps(
|
|
|
949
1101
|
continue;
|
|
950
1102
|
}
|
|
951
1103
|
|
|
952
|
-
const issueNumber = createGitHubIssue(ctx.repo, task.title, body, {
|
|
1104
|
+
const issueNumber = await createGitHubIssue(ctx.repo, task.title, body, {
|
|
953
1105
|
milestone: milestoneNumber,
|
|
954
1106
|
labels: labels.length > 0 ? labels : undefined,
|
|
955
1107
|
});
|
|
@@ -81,7 +81,10 @@ vi.mock('../governance/governance.js', () => ({
|
|
|
81
81
|
}));
|
|
82
82
|
|
|
83
83
|
vi.mock('../loop/loop-manager.js', () => ({
|
|
84
|
-
LoopManager:
|
|
84
|
+
LoopManager: vi.fn(function (this: Record<string, unknown>) {
|
|
85
|
+
this.isActive = vi.fn().mockReturnValue(false);
|
|
86
|
+
this.cancelLoop = vi.fn();
|
|
87
|
+
}),
|
|
85
88
|
}));
|
|
86
89
|
|
|
87
90
|
vi.mock('../control/identity-manager.js', () => ({
|
|
@@ -185,7 +188,9 @@ vi.mock('../context/context-engine.js', () => ({
|
|
|
185
188
|
}));
|
|
186
189
|
|
|
187
190
|
vi.mock('../agency/agency-manager.js', () => ({
|
|
188
|
-
AgencyManager:
|
|
191
|
+
AgencyManager: vi.fn(function (this: Record<string, unknown>) {
|
|
192
|
+
this.disable = vi.fn();
|
|
193
|
+
}),
|
|
189
194
|
}));
|
|
190
195
|
|
|
191
196
|
vi.mock('../vault/knowledge-review.js', () => ({
|
|
@@ -220,6 +225,7 @@ vi.mock('../queue/job-queue.js', () => ({
|
|
|
220
225
|
vi.mock('../queue/pipeline-runner.js', () => ({
|
|
221
226
|
PipelineRunner: vi.fn(function (this: Record<string, unknown>) {
|
|
222
227
|
this.registerHandler = vi.fn();
|
|
228
|
+
this.stop = vi.fn();
|
|
223
229
|
}),
|
|
224
230
|
}));
|
|
225
231
|
|
|
@@ -261,6 +267,18 @@ vi.mock('./context-health.js', () => ({
|
|
|
261
267
|
}),
|
|
262
268
|
}));
|
|
263
269
|
|
|
270
|
+
vi.mock('./shutdown-registry.js', () => ({
|
|
271
|
+
ShutdownRegistry: vi.fn(function (this: Record<string, unknown>) {
|
|
272
|
+
this.register = vi.fn();
|
|
273
|
+
this.closeAll = vi.fn().mockResolvedValue(undefined);
|
|
274
|
+
this.closeAllSync = vi.fn();
|
|
275
|
+
this.size = 0;
|
|
276
|
+
this.isClosed = false;
|
|
277
|
+
this.entries = [];
|
|
278
|
+
this.closed = false;
|
|
279
|
+
}),
|
|
280
|
+
}));
|
|
281
|
+
|
|
264
282
|
vi.mock('node:fs', () => ({
|
|
265
283
|
existsSync: vi.fn().mockReturnValue(false),
|
|
266
284
|
mkdirSync: vi.fn(),
|
|
@@ -360,4 +378,34 @@ describe('createAgentRuntime', () => {
|
|
|
360
378
|
it('initializes context health monitor', () => {
|
|
361
379
|
expect(runtime.contextHealth).toBeDefined();
|
|
362
380
|
});
|
|
381
|
+
|
|
382
|
+
it('initializes shutdown registry', () => {
|
|
383
|
+
expect(runtime.shutdownRegistry).toBeDefined();
|
|
384
|
+
expect(runtime.shutdownRegistry.register).toBeDefined();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('registers cleanup callbacks with shutdown registry', () => {
|
|
388
|
+
// vaultManager, pipelineRunner, agencyManager, loopManager
|
|
389
|
+
expect(runtime.shutdownRegistry.register).toHaveBeenCalledWith(
|
|
390
|
+
'vaultManager',
|
|
391
|
+
expect.any(Function),
|
|
392
|
+
);
|
|
393
|
+
expect(runtime.shutdownRegistry.register).toHaveBeenCalledWith(
|
|
394
|
+
'pipelineRunner',
|
|
395
|
+
expect.any(Function),
|
|
396
|
+
);
|
|
397
|
+
expect(runtime.shutdownRegistry.register).toHaveBeenCalledWith(
|
|
398
|
+
'agencyManager',
|
|
399
|
+
expect.any(Function),
|
|
400
|
+
);
|
|
401
|
+
expect(runtime.shutdownRegistry.register).toHaveBeenCalledWith(
|
|
402
|
+
'loopManager',
|
|
403
|
+
expect.any(Function),
|
|
404
|
+
);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('close() calls shutdownRegistry.closeAllSync()', () => {
|
|
408
|
+
runtime.close();
|
|
409
|
+
expect(runtime.shutdownRegistry.closeAllSync).toHaveBeenCalled();
|
|
410
|
+
});
|
|
363
411
|
});
|