|
|
@@ -5,6 +5,7 @@ import { createTaskSessionManagerHook } from './index';
|
|
|
|
|
|
function createHook(options?: {
|
|
|
shouldManageSession?: (sessionID: string) => boolean;
|
|
|
+ registerSessionAsOrchestrator?: (sessionID: string) => void;
|
|
|
readContextMinLines?: number;
|
|
|
readContextMaxFiles?: number;
|
|
|
backgroundJobBoard?: BackgroundJobBoard;
|
|
|
@@ -28,6 +29,8 @@ function createHook(options?: {
|
|
|
readContextMaxFiles: options?.readContextMaxFiles,
|
|
|
backgroundJobBoard: options?.backgroundJobBoard,
|
|
|
shouldManageSession: options?.shouldManageSession ?? (() => true),
|
|
|
+ registerSessionAsOrchestrator:
|
|
|
+ options?.registerSessionAsOrchestrator,
|
|
|
isFallbackInProgress: options?.isFallbackInProgress,
|
|
|
coordinator: options?.coordinator,
|
|
|
},
|
|
|
@@ -2045,4 +2048,64 @@ describe('task-session-manager hook', () => {
|
|
|
|
|
|
expect(board.list('parent-1')).toHaveLength(0);
|
|
|
});
|
|
|
+
|
|
|
+ test('recovers stale orchestrator mapping in executeTool.before', async () => {
|
|
|
+ const agentMap = new Map<string, string>();
|
|
|
+ agentMap.set('orchestrator-1', 'explorer'); // stale non-orchestrator value
|
|
|
+
|
|
|
+ const board = new BackgroundJobBoard();
|
|
|
+
|
|
|
+ const { hook } = createHook({
|
|
|
+ backgroundJobBoard: board,
|
|
|
+ shouldManageSession: (id) => agentMap.get(id) === 'orchestrator',
|
|
|
+ registerSessionAsOrchestrator: (id) => {
|
|
|
+ agentMap.set(id, 'orchestrator');
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ // Before recovery: stale mapping blocks pending call creation
|
|
|
+ await hook['tool.execute.before'](
|
|
|
+ {
|
|
|
+ tool: 'task',
|
|
|
+ sessionID: 'orchestrator-1',
|
|
|
+ callID: 'call-recovery',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ args: {
|
|
|
+ subagent_type: 'explorer',
|
|
|
+ description: 'test recovery',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ );
|
|
|
+
|
|
|
+ // After recovery: agentMap now has 'orchestrator' for this session
|
|
|
+ expect(agentMap.get('orchestrator-1')).toBe('orchestrator');
|
|
|
+
|
|
|
+ // executeTool.after finds the pending call and registers the board entry
|
|
|
+ await hook['tool.execute.after'](
|
|
|
+ {
|
|
|
+ tool: 'task',
|
|
|
+ sessionID: 'orchestrator-1',
|
|
|
+ callID: 'call-recovery',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ output: [
|
|
|
+ 'task_id: child-recovery-1',
|
|
|
+ 'state: running',
|
|
|
+ '',
|
|
|
+ '<task_result>',
|
|
|
+ 'Background task started.',
|
|
|
+ '</task_result>',
|
|
|
+ ].join('\n'),
|
|
|
+ },
|
|
|
+ );
|
|
|
+
|
|
|
+ const jobs = board.list('orchestrator-1');
|
|
|
+ expect(jobs).toHaveLength(1);
|
|
|
+ expect(jobs[0]).toMatchObject({
|
|
|
+ taskID: 'child-recovery-1',
|
|
|
+ parentSessionID: 'orchestrator-1',
|
|
|
+ state: 'running',
|
|
|
+ });
|
|
|
+ });
|
|
|
});
|