beth-copilot 1.0.15 → 1.0.16
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 +197 -77
- package/bin/cli.js +47 -0
- package/dist/core/context.d.ts +171 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +353 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/context.test.d.ts +8 -0
- package/dist/core/context.test.d.ts.map +1 -0
- package/dist/core/context.test.js +253 -0
- package/dist/core/context.test.js.map +1 -0
- package/dist/core/handoffs.d.ts +151 -0
- package/dist/core/handoffs.d.ts.map +1 -0
- package/dist/core/handoffs.js +220 -0
- package/dist/core/handoffs.js.map +1 -0
- package/dist/core/handoffs.test.d.ts +8 -0
- package/dist/core/handoffs.test.d.ts.map +1 -0
- package/dist/core/handoffs.test.js +231 -0
- package/dist/core/handoffs.test.js.map +1 -0
- package/dist/core/orchestrator.d.ts +246 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js +514 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/orchestrator.test.d.ts +8 -0
- package/dist/core/orchestrator.test.d.ts.map +1 -0
- package/dist/core/orchestrator.test.js +517 -0
- package/dist/core/orchestrator.test.js.map +1 -0
- package/dist/core/router.d.ts +102 -0
- package/dist/core/router.d.ts.map +1 -0
- package/dist/core/router.js +178 -0
- package/dist/core/router.js.map +1 -0
- package/dist/core/router.test.d.ts +8 -0
- package/dist/core/router.test.d.ts.map +1 -0
- package/dist/core/router.test.js +215 -0
- package/dist/core/router.test.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/tools/cli/beads.d.ts +27 -0
- package/dist/tools/cli/beads.d.ts.map +1 -0
- package/dist/tools/cli/beads.js +172 -0
- package/dist/tools/cli/beads.js.map +1 -0
- package/dist/tools/cli/beads.test.d.ts +8 -0
- package/dist/tools/cli/beads.test.d.ts.map +1 -0
- package/dist/tools/cli/beads.test.js +264 -0
- package/dist/tools/cli/beads.test.js.map +1 -0
- package/dist/tools/cli/editFile.d.ts +17 -0
- package/dist/tools/cli/editFile.d.ts.map +1 -0
- package/dist/tools/cli/editFile.js +125 -0
- package/dist/tools/cli/editFile.js.map +1 -0
- package/dist/tools/cli/editFile.test.d.ts +8 -0
- package/dist/tools/cli/editFile.test.d.ts.map +1 -0
- package/dist/tools/cli/editFile.test.js +177 -0
- package/dist/tools/cli/editFile.test.js.map +1 -0
- package/dist/tools/cli/readFile.d.ts +25 -0
- package/dist/tools/cli/readFile.d.ts.map +1 -0
- package/dist/tools/cli/readFile.js +118 -0
- package/dist/tools/cli/readFile.js.map +1 -0
- package/dist/tools/cli/readFile.test.d.ts +8 -0
- package/dist/tools/cli/readFile.test.d.ts.map +1 -0
- package/dist/tools/cli/readFile.test.js +194 -0
- package/dist/tools/cli/readFile.test.js.map +1 -0
- package/dist/tools/cli/search.d.ts +16 -0
- package/dist/tools/cli/search.d.ts.map +1 -0
- package/dist/tools/cli/search.js +261 -0
- package/dist/tools/cli/search.js.map +1 -0
- package/dist/tools/cli/search.test.d.ts +8 -0
- package/dist/tools/cli/search.test.d.ts.map +1 -0
- package/dist/tools/cli/search.test.js +172 -0
- package/dist/tools/cli/search.test.js.map +1 -0
- package/dist/tools/cli/subagent.d.ts +43 -0
- package/dist/tools/cli/subagent.d.ts.map +1 -0
- package/dist/tools/cli/subagent.js +99 -0
- package/dist/tools/cli/subagent.js.map +1 -0
- package/dist/tools/cli/subagent.test.d.ts +8 -0
- package/dist/tools/cli/subagent.test.d.ts.map +1 -0
- package/dist/tools/cli/subagent.test.js +190 -0
- package/dist/tools/cli/subagent.test.js.map +1 -0
- package/dist/tools/cli/terminal.d.ts +19 -0
- package/dist/tools/cli/terminal.d.ts.map +1 -0
- package/dist/tools/cli/terminal.js +164 -0
- package/dist/tools/cli/terminal.js.map +1 -0
- package/dist/tools/cli/terminal.test.d.ts +8 -0
- package/dist/tools/cli/terminal.test.d.ts.map +1 -0
- package/dist/tools/cli/terminal.test.js +161 -0
- package/dist/tools/cli/terminal.test.js.map +1 -0
- package/dist/tools/index.d.ts +25 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +41 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/interface.d.ts +64 -0
- package/dist/tools/interface.d.ts.map +1 -0
- package/dist/tools/interface.js +37 -0
- package/dist/tools/interface.js.map +1 -0
- package/dist/tools/interface.test.d.ts +7 -0
- package/dist/tools/interface.test.d.ts.map +1 -0
- package/dist/tools/interface.test.js +179 -0
- package/dist/tools/interface.test.js.map +1 -0
- package/dist/tools/mcp/bridge.d.ts +48 -0
- package/dist/tools/mcp/bridge.d.ts.map +1 -0
- package/dist/tools/mcp/bridge.js +128 -0
- package/dist/tools/mcp/bridge.js.map +1 -0
- package/dist/tools/mcp/bridge.test.d.ts +8 -0
- package/dist/tools/mcp/bridge.test.d.ts.map +1 -0
- package/dist/tools/mcp/bridge.test.js +300 -0
- package/dist/tools/mcp/bridge.test.js.map +1 -0
- package/dist/tools/mcp/client.d.ts +135 -0
- package/dist/tools/mcp/client.d.ts.map +1 -0
- package/dist/tools/mcp/client.js +263 -0
- package/dist/tools/mcp/client.js.map +1 -0
- package/dist/tools/mcp/client.test.d.ts +8 -0
- package/dist/tools/mcp/client.test.d.ts.map +1 -0
- package/dist/tools/mcp/client.test.js +390 -0
- package/dist/tools/mcp/client.test.js.map +1 -0
- package/dist/tools/registry.d.ts +82 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +99 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/registry.test.d.ts +7 -0
- package/dist/tools/registry.test.d.ts.map +1 -0
- package/dist/tools/registry.test.js +199 -0
- package/dist/tools/registry.test.js.map +1 -0
- package/dist/tools/suite.test.d.ts +11 -0
- package/dist/tools/suite.test.d.ts.map +1 -0
- package/dist/tools/suite.test.js +119 -0
- package/dist/tools/suite.test.js.map +1 -0
- package/dist/tools/types.d.ts +75 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +30 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/types.test.d.ts +7 -0
- package/dist/tools/types.test.d.ts.map +1 -0
- package/dist/tools/types.test.js +178 -0
- package/dist/tools/types.test.js.map +1 -0
- package/package.json +2 -2
- package/sbom.json +209 -209
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handoff Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages agent-to-agent transfers, including:
|
|
5
|
+
* - Interactive handoffs (user reviews before transfer)
|
|
6
|
+
* - Autonomous handoffs (subagent spawning — orchestrator decides)
|
|
7
|
+
* - Context transfer (conversation summary passed to new agent)
|
|
8
|
+
* - Handoff history tracking
|
|
9
|
+
*
|
|
10
|
+
* Handoffs are defined in agent frontmatter:
|
|
11
|
+
* ```yaml
|
|
12
|
+
* handoffs:
|
|
13
|
+
* - label: "Development"
|
|
14
|
+
* agent: developer
|
|
15
|
+
* prompt: "Implement this feature"
|
|
16
|
+
* send: true # auto-send vs. prepare for review
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
import type { AgentDefinition, AgentHandoff } from './agents/types.js';
|
|
20
|
+
import type { ConversationContext, ConversationSummary } from './context.js';
|
|
21
|
+
import { AgentRouter } from './router.js';
|
|
22
|
+
/**
|
|
23
|
+
* A handoff request — the intent to transfer from one agent to another.
|
|
24
|
+
*/
|
|
25
|
+
export interface HandoffRequest {
|
|
26
|
+
/** Source agent initiating the handoff */
|
|
27
|
+
fromAgentId: string;
|
|
28
|
+
/** Target agent receiving the handoff */
|
|
29
|
+
toAgentId: string;
|
|
30
|
+
/** The prompt/message to send to the target agent */
|
|
31
|
+
prompt: string;
|
|
32
|
+
/** Label for this handoff (from agent definition) */
|
|
33
|
+
label: string;
|
|
34
|
+
/** Whether to auto-send or wait for user review */
|
|
35
|
+
autoSend: boolean;
|
|
36
|
+
/** Summary of the conversation being handed off */
|
|
37
|
+
conversationSummary?: ConversationSummary;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The result of executing a handoff.
|
|
41
|
+
*/
|
|
42
|
+
export interface HandoffResult {
|
|
43
|
+
/** Whether the handoff was executed */
|
|
44
|
+
executed: boolean;
|
|
45
|
+
/** The target agent (resolved) */
|
|
46
|
+
targetAgent?: AgentDefinition;
|
|
47
|
+
/** Reason if handoff was not executed */
|
|
48
|
+
reason?: string;
|
|
49
|
+
/** The handoff request that was processed */
|
|
50
|
+
request: HandoffRequest;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Record of a completed handoff, for history tracking.
|
|
54
|
+
*/
|
|
55
|
+
export interface HandoffRecord {
|
|
56
|
+
/** Handoff request */
|
|
57
|
+
request: HandoffRequest;
|
|
58
|
+
/** When the handoff occurred */
|
|
59
|
+
timestamp: number;
|
|
60
|
+
/** Whether it was executed */
|
|
61
|
+
executed: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Mode for handoff execution.
|
|
65
|
+
*/
|
|
66
|
+
export type HandoffMode = 'interactive' | 'autonomous';
|
|
67
|
+
/**
|
|
68
|
+
* Manages agent handoffs and context transfer.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const manager = new HandoffManager(router);
|
|
73
|
+
*
|
|
74
|
+
* // Get available handoffs for the current agent
|
|
75
|
+
* const available = manager.getAvailableHandoffs(bethAgent);
|
|
76
|
+
*
|
|
77
|
+
* // Prepare a handoff
|
|
78
|
+
* const request = manager.prepareHandoff(bethAgent, 'developer', 'Implement login');
|
|
79
|
+
*
|
|
80
|
+
* // Execute the handoff
|
|
81
|
+
* const result = manager.executeHandoff(request, currentContext);
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare class HandoffManager {
|
|
85
|
+
/** Router for resolving agent references */
|
|
86
|
+
private readonly router;
|
|
87
|
+
/** History of handoffs in this session */
|
|
88
|
+
private readonly history;
|
|
89
|
+
/** Maximum depth of handoff chains to prevent infinite loops */
|
|
90
|
+
private readonly maxDepth;
|
|
91
|
+
constructor(router: AgentRouter, options?: {
|
|
92
|
+
maxDepth?: number;
|
|
93
|
+
});
|
|
94
|
+
/**
|
|
95
|
+
* Get the handoff definitions available from an agent.
|
|
96
|
+
*/
|
|
97
|
+
getAvailableHandoffs(agent: AgentDefinition): AgentHandoff[];
|
|
98
|
+
/**
|
|
99
|
+
* Find a handoff definition by target agent name.
|
|
100
|
+
*/
|
|
101
|
+
findHandoff(agent: AgentDefinition, targetAgentId: string): AgentHandoff | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* Prepare a handoff request from the current agent to a target.
|
|
104
|
+
*
|
|
105
|
+
* @param fromAgent - The agent initiating the handoff
|
|
106
|
+
* @param toAgentId - The target agent's ID or name
|
|
107
|
+
* @param prompt - The message to send (overrides handoff default)
|
|
108
|
+
* @param context - Optional conversation context for summary
|
|
109
|
+
* @returns The prepared handoff request, or null if the target agent isn't found
|
|
110
|
+
*/
|
|
111
|
+
prepareHandoff(fromAgent: AgentDefinition, toAgentId: string, prompt?: string, context?: ConversationContext): HandoffRequest | null;
|
|
112
|
+
/**
|
|
113
|
+
* Execute a handoff — validate and prepare the transfer.
|
|
114
|
+
*
|
|
115
|
+
* The actual agent switch happens in the orchestrator. This method
|
|
116
|
+
* validates the handoff, checks depth limits, and records it.
|
|
117
|
+
*
|
|
118
|
+
* @param request - The handoff request to execute
|
|
119
|
+
* @returns The handoff result
|
|
120
|
+
*/
|
|
121
|
+
executeHandoff(request: HandoffRequest): HandoffResult;
|
|
122
|
+
/**
|
|
123
|
+
* Build the context injection string for the target agent.
|
|
124
|
+
*
|
|
125
|
+
* Creates a summary of the previous conversation that gets injected
|
|
126
|
+
* into the new agent's system prompt.
|
|
127
|
+
*/
|
|
128
|
+
buildHandoffContext(request: HandoffRequest): string;
|
|
129
|
+
/**
|
|
130
|
+
* Get the handoff history for this session.
|
|
131
|
+
*/
|
|
132
|
+
getHistory(): HandoffRecord[];
|
|
133
|
+
/**
|
|
134
|
+
* Get the current handoff chain depth for a target agent.
|
|
135
|
+
* Counts consecutive handoffs in recent history.
|
|
136
|
+
*/
|
|
137
|
+
getDepth(): number;
|
|
138
|
+
/**
|
|
139
|
+
* Clear handoff history (e.g., when starting a new conversation).
|
|
140
|
+
*/
|
|
141
|
+
clearHistory(): void;
|
|
142
|
+
/**
|
|
143
|
+
* Check if handing off to a specific agent would exceed the depth limit.
|
|
144
|
+
*/
|
|
145
|
+
private isDepthExceeded;
|
|
146
|
+
/**
|
|
147
|
+
* Record a handoff in history.
|
|
148
|
+
*/
|
|
149
|
+
private recordHandoff;
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=handoffs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoffs.d.ts","sourceRoot":"","sources":["../../src/core/handoffs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM1C;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IAEpB,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAElB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IAEf,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IAEd,mDAAmD;IACnD,QAAQ,EAAE,OAAO,CAAC;IAElB,mDAAmD;IACnD,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,uCAAuC;IACvC,QAAQ,EAAE,OAAO,CAAC;IAElB,kCAAkC;IAClC,WAAW,CAAC,EAAE,eAAe,CAAC;IAE9B,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,6CAA6C;IAC7C,OAAO,EAAE,cAAc,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,sBAAsB;IACtB,OAAO,EAAE,cAAc,CAAC;IAExB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAElB,8BAA8B;IAC9B,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,YAAY,CAAC;AAMvD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,cAAc;IACzB,4CAA4C;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IAErC,0CAA0C;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAE/C,gEAAgE;IAChE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE;IAShE;;OAEG;IACH,oBAAoB,CAAC,KAAK,EAAE,eAAe,GAAG,YAAY,EAAE;IAI5D;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAWpF;;;;;;;;OAQG;IACH,cAAc,CACZ,SAAS,EAAE,eAAe,EAC1B,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,mBAAmB,GAC5B,cAAc,GAAG,IAAI;IA0BxB;;;;;;;;OAQG;IACH,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa;IAsCtD;;;;;OAKG;IACH,mBAAmB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM;IA2BpD;;OAEG;IACH,UAAU,IAAI,aAAa,EAAE;IAI7B;;;OAGG;IACH,QAAQ,IAAI,MAAM;IAIlB;;OAEG;IACH,YAAY,IAAI,IAAI;IAIpB;;OAEG;IACH,OAAO,CAAC,eAAe;IA2BvB;;OAEG;IACH,OAAO,CAAC,aAAa;CAOtB"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handoff Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages agent-to-agent transfers, including:
|
|
5
|
+
* - Interactive handoffs (user reviews before transfer)
|
|
6
|
+
* - Autonomous handoffs (subagent spawning — orchestrator decides)
|
|
7
|
+
* - Context transfer (conversation summary passed to new agent)
|
|
8
|
+
* - Handoff history tracking
|
|
9
|
+
*
|
|
10
|
+
* Handoffs are defined in agent frontmatter:
|
|
11
|
+
* ```yaml
|
|
12
|
+
* handoffs:
|
|
13
|
+
* - label: "Development"
|
|
14
|
+
* agent: developer
|
|
15
|
+
* prompt: "Implement this feature"
|
|
16
|
+
* send: true # auto-send vs. prepare for review
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
// =============================================================================
|
|
20
|
+
// HandoffManager
|
|
21
|
+
// =============================================================================
|
|
22
|
+
/**
|
|
23
|
+
* Manages agent handoffs and context transfer.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* const manager = new HandoffManager(router);
|
|
28
|
+
*
|
|
29
|
+
* // Get available handoffs for the current agent
|
|
30
|
+
* const available = manager.getAvailableHandoffs(bethAgent);
|
|
31
|
+
*
|
|
32
|
+
* // Prepare a handoff
|
|
33
|
+
* const request = manager.prepareHandoff(bethAgent, 'developer', 'Implement login');
|
|
34
|
+
*
|
|
35
|
+
* // Execute the handoff
|
|
36
|
+
* const result = manager.executeHandoff(request, currentContext);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export class HandoffManager {
|
|
40
|
+
/** Router for resolving agent references */
|
|
41
|
+
router;
|
|
42
|
+
/** History of handoffs in this session */
|
|
43
|
+
history = [];
|
|
44
|
+
/** Maximum depth of handoff chains to prevent infinite loops */
|
|
45
|
+
maxDepth;
|
|
46
|
+
constructor(router, options) {
|
|
47
|
+
this.router = router;
|
|
48
|
+
this.maxDepth = options?.maxDepth ?? 10;
|
|
49
|
+
}
|
|
50
|
+
// ===========================================================================
|
|
51
|
+
// Handoff Discovery
|
|
52
|
+
// ===========================================================================
|
|
53
|
+
/**
|
|
54
|
+
* Get the handoff definitions available from an agent.
|
|
55
|
+
*/
|
|
56
|
+
getAvailableHandoffs(agent) {
|
|
57
|
+
return agent.frontmatter.handoffs ?? [];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Find a handoff definition by target agent name.
|
|
61
|
+
*/
|
|
62
|
+
findHandoff(agent, targetAgentId) {
|
|
63
|
+
const handoffs = this.getAvailableHandoffs(agent);
|
|
64
|
+
return handoffs.find((h) => h.agent.toLowerCase() === targetAgentId.toLowerCase());
|
|
65
|
+
}
|
|
66
|
+
// ===========================================================================
|
|
67
|
+
// Handoff Preparation
|
|
68
|
+
// ===========================================================================
|
|
69
|
+
/**
|
|
70
|
+
* Prepare a handoff request from the current agent to a target.
|
|
71
|
+
*
|
|
72
|
+
* @param fromAgent - The agent initiating the handoff
|
|
73
|
+
* @param toAgentId - The target agent's ID or name
|
|
74
|
+
* @param prompt - The message to send (overrides handoff default)
|
|
75
|
+
* @param context - Optional conversation context for summary
|
|
76
|
+
* @returns The prepared handoff request, or null if the target agent isn't found
|
|
77
|
+
*/
|
|
78
|
+
prepareHandoff(fromAgent, toAgentId, prompt, context) {
|
|
79
|
+
// Resolve the target agent
|
|
80
|
+
const targetAgent = this.router.resolveAgent(toAgentId);
|
|
81
|
+
if (!targetAgent) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
// Look up the handoff definition for defaults
|
|
85
|
+
const handoffDef = this.findHandoff(fromAgent, toAgentId);
|
|
86
|
+
const request = {
|
|
87
|
+
fromAgentId: fromAgent.id,
|
|
88
|
+
toAgentId: targetAgent.id,
|
|
89
|
+
prompt: prompt ?? handoffDef?.prompt ?? '',
|
|
90
|
+
label: handoffDef?.label ?? `Handoff to ${targetAgent.frontmatter.name}`,
|
|
91
|
+
autoSend: handoffDef?.send ?? false,
|
|
92
|
+
conversationSummary: context?.getSummary(),
|
|
93
|
+
};
|
|
94
|
+
return request;
|
|
95
|
+
}
|
|
96
|
+
// ===========================================================================
|
|
97
|
+
// Handoff Execution
|
|
98
|
+
// ===========================================================================
|
|
99
|
+
/**
|
|
100
|
+
* Execute a handoff — validate and prepare the transfer.
|
|
101
|
+
*
|
|
102
|
+
* The actual agent switch happens in the orchestrator. This method
|
|
103
|
+
* validates the handoff, checks depth limits, and records it.
|
|
104
|
+
*
|
|
105
|
+
* @param request - The handoff request to execute
|
|
106
|
+
* @returns The handoff result
|
|
107
|
+
*/
|
|
108
|
+
executeHandoff(request) {
|
|
109
|
+
// Check depth limit to prevent infinite handoff chains
|
|
110
|
+
if (this.isDepthExceeded(request.toAgentId)) {
|
|
111
|
+
const result = {
|
|
112
|
+
executed: false,
|
|
113
|
+
reason: `Handoff depth limit (${this.maxDepth}) exceeded — possible loop detected`,
|
|
114
|
+
request,
|
|
115
|
+
};
|
|
116
|
+
this.recordHandoff(request, false);
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
// Resolve the target agent
|
|
120
|
+
const targetAgent = this.router.resolveAgent(request.toAgentId);
|
|
121
|
+
if (!targetAgent) {
|
|
122
|
+
const result = {
|
|
123
|
+
executed: false,
|
|
124
|
+
reason: `Agent "${request.toAgentId}" not found`,
|
|
125
|
+
request,
|
|
126
|
+
};
|
|
127
|
+
this.recordHandoff(request, false);
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
// Record and succeed
|
|
131
|
+
this.recordHandoff(request, true);
|
|
132
|
+
return {
|
|
133
|
+
executed: true,
|
|
134
|
+
targetAgent,
|
|
135
|
+
request,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// ===========================================================================
|
|
139
|
+
// Context Transfer
|
|
140
|
+
// ===========================================================================
|
|
141
|
+
/**
|
|
142
|
+
* Build the context injection string for the target agent.
|
|
143
|
+
*
|
|
144
|
+
* Creates a summary of the previous conversation that gets injected
|
|
145
|
+
* into the new agent's system prompt.
|
|
146
|
+
*/
|
|
147
|
+
buildHandoffContext(request) {
|
|
148
|
+
const parts = [];
|
|
149
|
+
parts.push(`## Handoff from ${request.fromAgentId}`);
|
|
150
|
+
parts.push(`**Reason:** ${request.label}`);
|
|
151
|
+
if (request.conversationSummary) {
|
|
152
|
+
const summary = request.conversationSummary;
|
|
153
|
+
parts.push(`**Previous conversation** (${summary.turnCount} turns with ${summary.agentId}):`);
|
|
154
|
+
parts.push(summary.summary);
|
|
155
|
+
if (summary.toolCallSummary.length > 0) {
|
|
156
|
+
parts.push(`**Tools used:** ${summary.toolCallSummary.join(', ')}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (request.prompt) {
|
|
160
|
+
parts.push(`\n**Task:** ${request.prompt}`);
|
|
161
|
+
}
|
|
162
|
+
return parts.join('\n');
|
|
163
|
+
}
|
|
164
|
+
// ===========================================================================
|
|
165
|
+
// History & Safety
|
|
166
|
+
// ===========================================================================
|
|
167
|
+
/**
|
|
168
|
+
* Get the handoff history for this session.
|
|
169
|
+
*/
|
|
170
|
+
getHistory() {
|
|
171
|
+
return [...this.history];
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get the current handoff chain depth for a target agent.
|
|
175
|
+
* Counts consecutive handoffs in recent history.
|
|
176
|
+
*/
|
|
177
|
+
getDepth() {
|
|
178
|
+
return this.history.filter((h) => h.executed).length;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Clear handoff history (e.g., when starting a new conversation).
|
|
182
|
+
*/
|
|
183
|
+
clearHistory() {
|
|
184
|
+
this.history.length = 0;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Check if handing off to a specific agent would exceed the depth limit.
|
|
188
|
+
*/
|
|
189
|
+
isDepthExceeded(_targetAgentId) {
|
|
190
|
+
const executedCount = this.history.filter((h) => h.executed).length;
|
|
191
|
+
if (executedCount >= this.maxDepth) {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
// Also check for direct ping-pong loops (A→B→A→B...)
|
|
195
|
+
const recentExecuted = this.history
|
|
196
|
+
.filter((h) => h.executed)
|
|
197
|
+
.slice(-4);
|
|
198
|
+
if (recentExecuted.length >= 4) {
|
|
199
|
+
const pattern = recentExecuted.map((h) => h.request.toAgentId);
|
|
200
|
+
// Check for A-B-A-B pattern
|
|
201
|
+
if (pattern[0] === pattern[2] &&
|
|
202
|
+
pattern[1] === pattern[3] &&
|
|
203
|
+
pattern[0] !== pattern[1]) {
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Record a handoff in history.
|
|
211
|
+
*/
|
|
212
|
+
recordHandoff(request, executed) {
|
|
213
|
+
this.history.push({
|
|
214
|
+
request,
|
|
215
|
+
timestamp: Date.now(),
|
|
216
|
+
executed,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=handoffs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoffs.js","sourceRoot":"","sources":["../../src/core/handoffs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoffs.test.d.ts","sourceRoot":"","sources":["../../src/core/handoffs.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HandoffManager Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for agent-to-agent transfers: preparation, execution,
|
|
5
|
+
* context building, depth limits, and loop detection.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it } from 'node:test';
|
|
8
|
+
import assert from 'node:assert';
|
|
9
|
+
import { HandoffManager } from './handoffs.js';
|
|
10
|
+
import { AgentRouter } from './router.js';
|
|
11
|
+
import { ConversationContext } from './context.js';
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// Test Fixtures
|
|
14
|
+
// =============================================================================
|
|
15
|
+
function createAgent(id, name, handoffs) {
|
|
16
|
+
return {
|
|
17
|
+
id,
|
|
18
|
+
frontmatter: {
|
|
19
|
+
name,
|
|
20
|
+
description: `${name} agent`,
|
|
21
|
+
tools: ['readFile'],
|
|
22
|
+
handoffs: handoffs ?? [],
|
|
23
|
+
},
|
|
24
|
+
body: `# ${name}`,
|
|
25
|
+
sourcePath: `/agents/${id}.agent.md`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function createTestSetup(maxDepth) {
|
|
29
|
+
const beth = createAgent('beth', 'Beth', [
|
|
30
|
+
{ label: 'Development', agent: 'developer', prompt: 'Implement this', send: false },
|
|
31
|
+
{ label: 'Testing', agent: 'tester', prompt: 'Test this', send: true },
|
|
32
|
+
{ label: 'Design', agent: 'ux-designer', prompt: 'Design this' },
|
|
33
|
+
]);
|
|
34
|
+
const developer = createAgent('developer', 'developer', [
|
|
35
|
+
{ label: 'QA', agent: 'tester', prompt: 'Test the implementation', send: false },
|
|
36
|
+
{ label: 'Review', agent: 'beth', prompt: 'Review complete' },
|
|
37
|
+
]);
|
|
38
|
+
const tester = createAgent('tester', 'tester', [
|
|
39
|
+
{ label: 'Fix Issue', agent: 'developer', prompt: 'Fix this bug', send: false },
|
|
40
|
+
]);
|
|
41
|
+
const designer = createAgent('ux-designer', 'ux-designer', []);
|
|
42
|
+
const agentResult = {
|
|
43
|
+
agents: [beth, developer, tester, designer],
|
|
44
|
+
errors: [],
|
|
45
|
+
};
|
|
46
|
+
const skillResult = { skills: [], errors: [] };
|
|
47
|
+
const router = new AgentRouter(agentResult, skillResult, 'beth');
|
|
48
|
+
const manager = new HandoffManager(router, { maxDepth: maxDepth ?? 10 });
|
|
49
|
+
return { router, manager, beth, developer, tester, designer };
|
|
50
|
+
}
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Tests
|
|
53
|
+
// =============================================================================
|
|
54
|
+
describe('HandoffManager', () => {
|
|
55
|
+
describe('Handoff Discovery', () => {
|
|
56
|
+
it('should list available handoffs for an agent', () => {
|
|
57
|
+
const { manager, beth } = createTestSetup();
|
|
58
|
+
const handoffs = manager.getAvailableHandoffs(beth);
|
|
59
|
+
assert.strictEqual(handoffs.length, 3);
|
|
60
|
+
assert.strictEqual(handoffs[0].label, 'Development');
|
|
61
|
+
assert.strictEqual(handoffs[0].agent, 'developer');
|
|
62
|
+
});
|
|
63
|
+
it('should return empty array for agent with no handoffs', () => {
|
|
64
|
+
const { manager, designer } = createTestSetup();
|
|
65
|
+
const handoffs = manager.getAvailableHandoffs(designer);
|
|
66
|
+
assert.strictEqual(handoffs.length, 0);
|
|
67
|
+
});
|
|
68
|
+
it('should find a specific handoff by target', () => {
|
|
69
|
+
const { manager, beth } = createTestSetup();
|
|
70
|
+
const handoff = manager.findHandoff(beth, 'developer');
|
|
71
|
+
assert.ok(handoff);
|
|
72
|
+
assert.strictEqual(handoff.label, 'Development');
|
|
73
|
+
assert.strictEqual(handoff.prompt, 'Implement this');
|
|
74
|
+
});
|
|
75
|
+
it('should return undefined for unknown target', () => {
|
|
76
|
+
const { manager, beth } = createTestSetup();
|
|
77
|
+
const handoff = manager.findHandoff(beth, 'nonexistent');
|
|
78
|
+
assert.strictEqual(handoff, undefined);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe('Handoff Preparation', () => {
|
|
82
|
+
it('should prepare a handoff with defaults from definition', () => {
|
|
83
|
+
const { manager, beth } = createTestSetup();
|
|
84
|
+
const request = manager.prepareHandoff(beth, 'developer');
|
|
85
|
+
assert.ok(request);
|
|
86
|
+
assert.strictEqual(request.fromAgentId, 'beth');
|
|
87
|
+
assert.strictEqual(request.toAgentId, 'developer');
|
|
88
|
+
assert.strictEqual(request.prompt, 'Implement this');
|
|
89
|
+
assert.strictEqual(request.label, 'Development');
|
|
90
|
+
assert.strictEqual(request.autoSend, false);
|
|
91
|
+
});
|
|
92
|
+
it('should override prompt when provided', () => {
|
|
93
|
+
const { manager, beth } = createTestSetup();
|
|
94
|
+
const request = manager.prepareHandoff(beth, 'developer', 'Build the login page');
|
|
95
|
+
assert.ok(request);
|
|
96
|
+
assert.strictEqual(request.prompt, 'Build the login page');
|
|
97
|
+
});
|
|
98
|
+
it('should include conversation summary when context provided', () => {
|
|
99
|
+
const { manager, beth } = createTestSetup();
|
|
100
|
+
const ctx = new ConversationContext(beth);
|
|
101
|
+
ctx.addUserMessage('Plan authentication');
|
|
102
|
+
ctx.addAssistantMessage('I recommend JWT tokens.');
|
|
103
|
+
const request = manager.prepareHandoff(beth, 'developer', 'Implement auth', ctx);
|
|
104
|
+
assert.ok(request);
|
|
105
|
+
assert.ok(request.conversationSummary);
|
|
106
|
+
assert.strictEqual(request.conversationSummary.agentId, 'beth');
|
|
107
|
+
assert.strictEqual(request.conversationSummary.turnCount, 1);
|
|
108
|
+
});
|
|
109
|
+
it('should return null for unknown target agent', () => {
|
|
110
|
+
const { manager, beth } = createTestSetup();
|
|
111
|
+
const request = manager.prepareHandoff(beth, 'nonexistent');
|
|
112
|
+
assert.strictEqual(request, null);
|
|
113
|
+
});
|
|
114
|
+
it('should use autoSend from handoff definition', () => {
|
|
115
|
+
const { manager, beth } = createTestSetup();
|
|
116
|
+
// Tester handoff has send: true
|
|
117
|
+
const request = manager.prepareHandoff(beth, 'tester');
|
|
118
|
+
assert.ok(request);
|
|
119
|
+
assert.strictEqual(request.autoSend, true);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe('Handoff Execution', () => {
|
|
123
|
+
it('should execute a valid handoff', () => {
|
|
124
|
+
const { manager, beth } = createTestSetup();
|
|
125
|
+
const request = manager.prepareHandoff(beth, 'developer');
|
|
126
|
+
const result = manager.executeHandoff(request);
|
|
127
|
+
assert.strictEqual(result.executed, true);
|
|
128
|
+
assert.ok(result.targetAgent);
|
|
129
|
+
assert.strictEqual(result.targetAgent.id, 'developer');
|
|
130
|
+
});
|
|
131
|
+
it('should fail for unknown target agent', () => {
|
|
132
|
+
const { manager } = createTestSetup();
|
|
133
|
+
const request = {
|
|
134
|
+
fromAgentId: 'beth',
|
|
135
|
+
toAgentId: 'nonexistent',
|
|
136
|
+
prompt: 'Do something',
|
|
137
|
+
label: 'Test',
|
|
138
|
+
autoSend: false,
|
|
139
|
+
};
|
|
140
|
+
const result = manager.executeHandoff(request);
|
|
141
|
+
assert.strictEqual(result.executed, false);
|
|
142
|
+
assert.ok(result.reason?.includes('not found'));
|
|
143
|
+
});
|
|
144
|
+
it('should record handoff in history', () => {
|
|
145
|
+
const { manager, beth } = createTestSetup();
|
|
146
|
+
const request = manager.prepareHandoff(beth, 'developer');
|
|
147
|
+
manager.executeHandoff(request);
|
|
148
|
+
const history = manager.getHistory();
|
|
149
|
+
assert.strictEqual(history.length, 1);
|
|
150
|
+
assert.strictEqual(history[0].executed, true);
|
|
151
|
+
assert.strictEqual(history[0].request.toAgentId, 'developer');
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
describe('Depth Limits', () => {
|
|
155
|
+
it('should enforce maximum handoff depth', () => {
|
|
156
|
+
const { manager, beth, developer, tester } = createTestSetup(3);
|
|
157
|
+
// Execute 3 handoffs
|
|
158
|
+
manager.executeHandoff(manager.prepareHandoff(beth, 'developer'));
|
|
159
|
+
manager.executeHandoff(manager.prepareHandoff(developer, 'tester'));
|
|
160
|
+
manager.executeHandoff(manager.prepareHandoff(tester, 'developer'));
|
|
161
|
+
// 4th should fail
|
|
162
|
+
const request = manager.prepareHandoff(developer, 'tester');
|
|
163
|
+
const result = manager.executeHandoff(request);
|
|
164
|
+
assert.strictEqual(result.executed, false);
|
|
165
|
+
assert.ok(result.reason?.includes('depth limit'));
|
|
166
|
+
});
|
|
167
|
+
it('should detect ping-pong loops (A→B→A→B)', () => {
|
|
168
|
+
const { manager, beth, developer } = createTestSetup(20);
|
|
169
|
+
// Create a ping-pong pattern
|
|
170
|
+
manager.executeHandoff(manager.prepareHandoff(beth, 'developer'));
|
|
171
|
+
manager.executeHandoff(manager.prepareHandoff(developer, 'beth'));
|
|
172
|
+
manager.executeHandoff(manager.prepareHandoff(beth, 'developer'));
|
|
173
|
+
manager.executeHandoff(manager.prepareHandoff(developer, 'beth'));
|
|
174
|
+
// 5th should be caught as a loop
|
|
175
|
+
const request = manager.prepareHandoff(beth, 'developer');
|
|
176
|
+
const result = manager.executeHandoff(request);
|
|
177
|
+
assert.strictEqual(result.executed, false);
|
|
178
|
+
});
|
|
179
|
+
it('should track depth correctly', () => {
|
|
180
|
+
const { manager, beth } = createTestSetup();
|
|
181
|
+
assert.strictEqual(manager.getDepth(), 0);
|
|
182
|
+
manager.executeHandoff(manager.prepareHandoff(beth, 'developer'));
|
|
183
|
+
assert.strictEqual(manager.getDepth(), 1);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
describe('Context Transfer', () => {
|
|
187
|
+
it('should build handoff context string', () => {
|
|
188
|
+
const { manager, beth } = createTestSetup();
|
|
189
|
+
const ctx = new ConversationContext(beth);
|
|
190
|
+
ctx.addUserMessage('Build auth');
|
|
191
|
+
ctx.addAssistantMessage('I recommend JWT.');
|
|
192
|
+
const request = manager.prepareHandoff(beth, 'developer', 'Implement auth', ctx);
|
|
193
|
+
const context = manager.buildHandoffContext(request);
|
|
194
|
+
assert.ok(context.includes('Handoff from beth'));
|
|
195
|
+
assert.ok(context.includes('Development'));
|
|
196
|
+
assert.ok(context.includes('Implement auth'));
|
|
197
|
+
assert.ok(context.includes('JWT'));
|
|
198
|
+
});
|
|
199
|
+
it('should include tool call summary in context', () => {
|
|
200
|
+
const { manager, beth } = createTestSetup();
|
|
201
|
+
const ctx = new ConversationContext(beth);
|
|
202
|
+
ctx.addUserMessage('Read the config');
|
|
203
|
+
ctx.addAssistantToolCalls([{ id: 'tc1', type: 'function', function: { name: 'readFile', arguments: '{"path":"config.ts"}' } }]);
|
|
204
|
+
ctx.addToolResult('tc1', 'export const config = {};');
|
|
205
|
+
ctx.addAssistantMessage('Config is empty.');
|
|
206
|
+
const request = manager.prepareHandoff(beth, 'developer', 'Update config', ctx);
|
|
207
|
+
const context = manager.buildHandoffContext(request);
|
|
208
|
+
assert.ok(context.includes('Tools used'));
|
|
209
|
+
assert.ok(context.includes('readFile'));
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
describe('History Management', () => {
|
|
213
|
+
it('should clear history', () => {
|
|
214
|
+
const { manager, beth } = createTestSetup();
|
|
215
|
+
manager.executeHandoff(manager.prepareHandoff(beth, 'developer'));
|
|
216
|
+
assert.strictEqual(manager.getHistory().length, 1);
|
|
217
|
+
manager.clearHistory();
|
|
218
|
+
assert.strictEqual(manager.getHistory().length, 0);
|
|
219
|
+
assert.strictEqual(manager.getDepth(), 0);
|
|
220
|
+
});
|
|
221
|
+
it('should return a copy of history', () => {
|
|
222
|
+
const { manager, beth } = createTestSetup();
|
|
223
|
+
manager.executeHandoff(manager.prepareHandoff(beth, 'developer'));
|
|
224
|
+
const history1 = manager.getHistory();
|
|
225
|
+
const history2 = manager.getHistory();
|
|
226
|
+
assert.deepStrictEqual(history1, history2);
|
|
227
|
+
assert.notStrictEqual(history1, history2); // Different array instances
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
//# sourceMappingURL=handoffs.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoffs.test.js","sourceRoot":"","sources":["../../src/core/handoffs.test.ts"],"names":[],"mappings":"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"}
|