qlogicagent 0.2.1 → 0.3.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/agent.js +1 -0
- package/dist/cli.js +9 -0
- package/dist/contracts.js +1 -0
- package/dist/index.js +5 -15
- package/dist/orchestration.js +118 -0
- package/package.json +56 -42
- package/dist/agent/agent.js +0 -113
- package/dist/agent/tool-loop.js +0 -575
- package/dist/agent/types.js +0 -14
- package/dist/cli/main.js +0 -23
- package/dist/cli/stdio-server.js +0 -463
- package/dist/config/config.js +0 -21
- package/dist/contracts/hooks.js +0 -7
- package/dist/contracts/index.js +0 -10
- package/dist/contracts/planner.js +0 -2
- package/dist/contracts/skill-candidate.js +0 -195
- package/dist/contracts/todo.js +0 -9
- package/dist/llm/builtin-providers.js +0 -531
- package/dist/llm/index.js +0 -14
- package/dist/llm/llm-client.js +0 -67
- package/dist/llm/model-catalog.js +0 -191
- package/dist/llm/provider-def.js +0 -12
- package/dist/llm/provider-registry.js +0 -147
- package/dist/llm/transport.js +0 -27
- package/dist/llm/transports/anthropic-messages.js +0 -293
- package/dist/llm/transports/openai-chat.js +0 -165
- package/dist/orchestration/agent-registry.js +0 -116
- package/dist/orchestration/approval-aware-tool-plan.js +0 -87
- package/dist/orchestration/context-compression.js +0 -583
- package/dist/orchestration/conversation-repair.js +0 -429
- package/dist/orchestration/curator-scheduler.js +0 -135
- package/dist/orchestration/embedded-failover-policy.js +0 -168
- package/dist/orchestration/error-classification.js +0 -77
- package/dist/orchestration/failover-classification.js +0 -381
- package/dist/orchestration/failover-error.js +0 -198
- package/dist/orchestration/fork-subagent.js +0 -98
- package/dist/orchestration/index.js +0 -267
- package/dist/orchestration/memory-flush-policy.js +0 -85
- package/dist/orchestration/memory-provider.js +0 -2
- package/dist/orchestration/parallel-tool-calls.js +0 -59
- package/dist/orchestration/prompt-cache-strategy.js +0 -228
- package/dist/orchestration/reactive-compact.js +0 -78
- package/dist/orchestration/retry-loop.js +0 -24
- package/dist/orchestration/skill-candidate.js +0 -141
- package/dist/orchestration/skill-consolidation.js +0 -220
- package/dist/orchestration/skill-improvement.js +0 -66
- package/dist/orchestration/skill-similarity.js +0 -131
- package/dist/orchestration/streaming-tool-executor.js +0 -96
- package/dist/orchestration/team-orchestration.js +0 -369
- package/dist/orchestration/team-tool-loop-wiring.js +0 -147
- package/dist/orchestration/tool-choice-policy.js +0 -164
- package/dist/orchestration/tool-loop-state.js +0 -133
- package/dist/orchestration/tool-schema.js +0 -297
- package/dist/orchestration/transcript-repair.js +0 -426
- package/dist/orchestration/turn-loop-guard.js +0 -92
- package/dist/orchestration/web-browser-policy.js +0 -39
- package/dist/runtime/context-compression.js +0 -274
- package/dist/runtime/hook-registry.js +0 -53
- package/dist/runtime/memory-hooks.js +0 -65
- package/dist/runtime/tool-eligibility.js +0 -111
- package/dist/skills/index.js +0 -82
- package/dist/skills/memory-extractor.js +0 -173
- package/dist/skills/memory-query-tool.js +0 -127
- package/dist/skills/memory-store.js +0 -228
- package/dist/skills/memory-tool.js +0 -192
- package/dist/skills/portable-tool.js +0 -14
- package/dist/skills/qmemory-adapter.js +0 -165
- package/dist/skills/skill-frontmatter.js +0 -344
- package/dist/skills/skill-guard.js +0 -229
- package/dist/skills/skill-loader.js +0 -303
- package/dist/skills/skill-source.js +0 -126
- package/dist/skills/skill-types.js +0 -6
- package/dist/skills/think-tool.js +0 -59
- package/dist/skills/todo-tool.js +0 -114
- package/dist/skills/tools/agent-tool.js +0 -142
- package/dist/skills/tools/apply-patch-tool.js +0 -184
- package/dist/skills/tools/ask-user-tool.js +0 -121
- package/dist/skills/tools/brief-tool.js +0 -95
- package/dist/skills/tools/browser-tool.js +0 -155
- package/dist/skills/tools/checkpoint-tool.js +0 -102
- package/dist/skills/tools/config-tool.js +0 -143
- package/dist/skills/tools/cron-tool.js +0 -175
- package/dist/skills/tools/edit-tool.js +0 -70
- package/dist/skills/tools/exec-tool.js +0 -133
- package/dist/skills/tools/image-generate-tool.js +0 -67
- package/dist/skills/tools/instructions-tool.js +0 -187
- package/dist/skills/tools/lsp-tool.js +0 -227
- package/dist/skills/tools/mcp-client-types.js +0 -53
- package/dist/skills/tools/mcp-tool.js +0 -503
- package/dist/skills/tools/memory-tool.js +0 -88
- package/dist/skills/tools/monitor-tool.js +0 -131
- package/dist/skills/tools/music-generate-tool.js +0 -62
- package/dist/skills/tools/notify-tool.js +0 -62
- package/dist/skills/tools/patch-tool.js +0 -505
- package/dist/skills/tools/pdf-tool.js +0 -88
- package/dist/skills/tools/plan-mode-tool.js +0 -122
- package/dist/skills/tools/read-tool.js +0 -84
- package/dist/skills/tools/repl-tool.js +0 -69
- package/dist/skills/tools/search-tool.js +0 -225
- package/dist/skills/tools/send-message-tool.js +0 -76
- package/dist/skills/tools/skill-list-tool.js +0 -54
- package/dist/skills/tools/skill-manage-tool.js +0 -153
- package/dist/skills/tools/skill-view-tool.js +0 -72
- package/dist/skills/tools/sleep-tool.js +0 -81
- package/dist/skills/tools/structured-output-tool.js +0 -176
- package/dist/skills/tools/task-tool.js +0 -161
- package/dist/skills/tools/team-tool.js +0 -105
- package/dist/skills/tools/tool-search-tool.js +0 -110
- package/dist/skills/tools/tts-tool.js +0 -45
- package/dist/skills/tools/video-edit-tool.js +0 -74
- package/dist/skills/tools/video-generate-tool.js +0 -66
- package/dist/skills/tools/video-merge-tool.js +0 -92
- package/dist/skills/tools/video-upscale-tool.js +0 -52
- package/dist/skills/tools/web-fetch-tool.js +0 -92
- package/dist/skills/tools/web-search-tool.js +0 -86
- package/dist/skills/tools/worktree-tool.js +0 -147
- package/dist/skills/tools/write-tool.js +0 -81
- /package/dist/{agent → types/agent}/agent.d.ts +0 -0
- /package/dist/{agent → types/agent}/tool-loop.d.ts +0 -0
- /package/dist/{agent → types/agent}/types.d.ts +0 -0
- /package/dist/{cli → types/cli}/main.d.ts +0 -0
- /package/dist/{cli → types/cli}/stdio-server.d.ts +0 -0
- /package/dist/{config → types/config}/config.d.ts +0 -0
- /package/dist/{contracts → types/contracts}/hooks.d.ts +0 -0
- /package/dist/{contracts → types/contracts}/index.d.ts +0 -0
- /package/dist/{contracts → types/contracts}/planner.d.ts +0 -0
- /package/dist/{contracts → types/contracts}/skill-candidate.d.ts +0 -0
- /package/dist/{contracts → types/contracts}/todo.d.ts +0 -0
- /package/dist/{index.d.ts → types/index.d.ts} +0 -0
- /package/dist/{llm → types/llm}/builtin-providers.d.ts +0 -0
- /package/dist/{llm → types/llm}/index.d.ts +0 -0
- /package/dist/{llm → types/llm}/llm-client.d.ts +0 -0
- /package/dist/{llm → types/llm}/model-catalog.d.ts +0 -0
- /package/dist/{llm → types/llm}/provider-def.d.ts +0 -0
- /package/dist/{llm → types/llm}/provider-registry.d.ts +0 -0
- /package/dist/{llm → types/llm}/transport.d.ts +0 -0
- /package/dist/{llm → types/llm}/transports/anthropic-messages.d.ts +0 -0
- /package/dist/{llm → types/llm}/transports/openai-chat.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/agent-registry.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/approval-aware-tool-plan.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/context-compression.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/conversation-repair.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/curator-scheduler.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/embedded-failover-policy.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/error-classification.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/failover-classification.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/failover-error.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/fork-subagent.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/index.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/memory-flush-policy.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/memory-provider.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/parallel-tool-calls.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/prompt-cache-strategy.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/reactive-compact.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/retry-loop.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/skill-candidate.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/skill-consolidation.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/skill-improvement.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/skill-similarity.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/streaming-tool-executor.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/team-orchestration.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/team-tool-loop-wiring.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/tool-choice-policy.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/tool-loop-state.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/tool-schema.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/transcript-repair.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/turn-loop-guard.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/web-browser-policy.d.ts +0 -0
- /package/dist/{runtime → types/runtime}/context-compression.d.ts +0 -0
- /package/dist/{runtime → types/runtime}/hook-registry.d.ts +0 -0
- /package/dist/{runtime → types/runtime}/memory-hooks.d.ts +0 -0
- /package/dist/{runtime → types/runtime}/tool-eligibility.d.ts +0 -0
- /package/dist/{skills → types/skills}/index.d.ts +0 -0
- /package/dist/{skills → types/skills}/memory-extractor.d.ts +0 -0
- /package/dist/{skills → types/skills}/memory-query-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/memory-store.d.ts +0 -0
- /package/dist/{skills → types/skills}/memory-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/portable-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/qmemory-adapter.d.ts +0 -0
- /package/dist/{skills → types/skills}/skill-frontmatter.d.ts +0 -0
- /package/dist/{skills → types/skills}/skill-guard.d.ts +0 -0
- /package/dist/{skills → types/skills}/skill-loader.d.ts +0 -0
- /package/dist/{skills → types/skills}/skill-source.d.ts +0 -0
- /package/dist/{skills → types/skills}/skill-types.d.ts +0 -0
- /package/dist/{skills → types/skills}/think-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/todo-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/agent-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/apply-patch-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/ask-user-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/brief-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/browser-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/checkpoint-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/config-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/cron-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/edit-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/exec-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/image-generate-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/instructions-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/lsp-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/mcp-client-types.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/mcp-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/memory-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/monitor-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/music-generate-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/notify-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/patch-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/pdf-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/plan-mode-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/read-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/repl-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/search-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/send-message-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/skill-list-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/skill-manage-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/skill-view-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/sleep-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/structured-output-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/task-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/team-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/tool-search-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/tts-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/video-edit-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/video-generate-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/video-merge-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/video-upscale-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/web-fetch-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/web-search-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/worktree-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/write-tool.d.ts +0 -0
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Team Orchestration Strategy — multi-agent coordination layer.
|
|
3
|
-
*
|
|
4
|
-
* Extends the existing sidechain foundation with team/swarm semantics:
|
|
5
|
-
* - Parallel agent spawning (multiple sidechains at once)
|
|
6
|
-
* - Role-based agent assignment (planner / executor / reviewer)
|
|
7
|
-
* - Shared knowledge pool via merge policies
|
|
8
|
-
* - Budget-aware concurrency limits
|
|
9
|
-
* - Lifecycle state machine for team coordination
|
|
10
|
-
*
|
|
11
|
-
* Architecture:
|
|
12
|
-
* Gateway ─── Hub ─── orchestration (this module, pure strategy)
|
|
13
|
-
* │
|
|
14
|
-
* ┌───────────┼───────────┐
|
|
15
|
-
* │ TeamCoordinator │ ← Hub runtime (multi-sidechain-coordinator.ts)
|
|
16
|
-
* │ │ │
|
|
17
|
-
* │ ├── Agent #1 (planner)
|
|
18
|
-
* │ ├── Agent #2 (executor)
|
|
19
|
-
* │ └── Agent #3 (reviewer)
|
|
20
|
-
* └───────────────────────┘
|
|
21
|
-
*
|
|
22
|
-
* This module provides:
|
|
23
|
-
* 1. Type definitions (TeamPlan, AgentRole, TeamLifecycle)
|
|
24
|
-
* 2. Pure strategy functions (plan resolution, role assignment, budget allocation)
|
|
25
|
-
* 3. Merge/aggregation policies (how agent results combine)
|
|
26
|
-
*
|
|
27
|
-
* Hub consumes these types and strategy functions in its coordinator runtime.
|
|
28
|
-
* Gateway consumes TeamPlan to spawn/manage multiple sidechain executions.
|
|
29
|
-
*/
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
|
-
// Strategy Functions — Pure, no I/O
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
33
|
-
/** Budget allocation weights per role (relative). */
|
|
34
|
-
const ROLE_BUDGET_WEIGHTS = {
|
|
35
|
-
planner: 3,
|
|
36
|
-
executor: 4,
|
|
37
|
-
reviewer: 2,
|
|
38
|
-
researcher: 3,
|
|
39
|
-
custom: 2,
|
|
40
|
-
};
|
|
41
|
-
/** Default concurrency limits by execution mode. */
|
|
42
|
-
const MODE_CONCURRENCY_DEFAULTS = {
|
|
43
|
-
parallel: 4,
|
|
44
|
-
sequential: 1,
|
|
45
|
-
pipeline: 1,
|
|
46
|
-
adaptive: 3,
|
|
47
|
-
};
|
|
48
|
-
/** Maximum agents allowed in a team (safety bound). */
|
|
49
|
-
export const TEAM_MAX_AGENTS = 8;
|
|
50
|
-
/** Maximum recursion depth (teams spawning teams). */
|
|
51
|
-
export const TEAM_MAX_DEPTH = 2;
|
|
52
|
-
/**
|
|
53
|
-
* Resolve a team plan from a high-level task decomposition.
|
|
54
|
-
*
|
|
55
|
-
* This is the main entry point: given a set of subtasks and constraints,
|
|
56
|
-
* produce a concrete TeamPlan that the coordinator can execute.
|
|
57
|
-
*/
|
|
58
|
-
export function resolveTeamPlan(params) {
|
|
59
|
-
const agents = params.subtasks.slice(0, TEAM_MAX_AGENTS).map((sub) => {
|
|
60
|
-
const budgetTier = resolveAgentBudgetTier(sub.role);
|
|
61
|
-
return {
|
|
62
|
-
agentId: sub.id,
|
|
63
|
-
role: sub.role,
|
|
64
|
-
label: sub.label,
|
|
65
|
-
sidechainType: mapRoleToSidechainType(sub.role),
|
|
66
|
-
taskDescription: sub.task,
|
|
67
|
-
toolAccessMode: resolveAgentToolAccess(sub.role),
|
|
68
|
-
budgetTier,
|
|
69
|
-
dependsOn: sub.dependsOn ?? [],
|
|
70
|
-
maxTokenBudget: 0, // Will be allocated below
|
|
71
|
-
maxToolCalls: resolveMaxToolCalls(sub.role),
|
|
72
|
-
timeoutMs: 0,
|
|
73
|
-
};
|
|
74
|
-
});
|
|
75
|
-
const mode = params.mode ?? inferExecutionMode(agents);
|
|
76
|
-
const totalBudget = params.totalTokenBudget ?? 100_000;
|
|
77
|
-
const allocatedAgents = allocateTokenBudgets(agents, totalBudget);
|
|
78
|
-
return {
|
|
79
|
-
teamId: params.teamId,
|
|
80
|
-
mode,
|
|
81
|
-
aggregationPolicy: params.aggregationPolicy ?? inferAggregationPolicy(mode, agents),
|
|
82
|
-
agents: allocatedAgents,
|
|
83
|
-
maxConcurrency: MODE_CONCURRENCY_DEFAULTS[mode],
|
|
84
|
-
timeoutMs: params.timeoutMs ?? 300_000, // 5 min default
|
|
85
|
-
totalTokenBudget: totalBudget,
|
|
86
|
-
failFast: params.failFast ?? (mode === "pipeline"),
|
|
87
|
-
sharedContext: params.sharedContext ?? "",
|
|
88
|
-
mergePolicy: params.mergePolicy ?? "summary-only",
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Map agent role to sidechain type for existing Hub plumbing compatibility.
|
|
93
|
-
*/
|
|
94
|
-
export function mapRoleToSidechainType(role) {
|
|
95
|
-
switch (role) {
|
|
96
|
-
case "planner": return "planner";
|
|
97
|
-
case "executor": return "code-repair";
|
|
98
|
-
case "reviewer": return "research";
|
|
99
|
-
case "researcher": return "research";
|
|
100
|
-
case "custom": return "research";
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Resolve tool access mode for a given role.
|
|
105
|
-
*/
|
|
106
|
-
export function resolveAgentToolAccess(role) {
|
|
107
|
-
switch (role) {
|
|
108
|
-
case "planner": return "none"; // Planner thinks, doesn't act
|
|
109
|
-
case "executor": return "full"; // Executor needs full tool access
|
|
110
|
-
case "reviewer": return "read-only"; // Reviewer inspects, doesn't modify
|
|
111
|
-
case "researcher": return "read-only";
|
|
112
|
-
case "custom": return "read-only";
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Resolve maximum tool calls for a given role.
|
|
117
|
-
*/
|
|
118
|
-
function resolveMaxToolCalls(role) {
|
|
119
|
-
switch (role) {
|
|
120
|
-
case "planner": return 0;
|
|
121
|
-
case "executor": return 30;
|
|
122
|
-
case "reviewer": return 10;
|
|
123
|
-
case "researcher": return 15;
|
|
124
|
-
case "custom": return 10;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Resolve budget tier for a given role.
|
|
129
|
-
*/
|
|
130
|
-
function resolveAgentBudgetTier(role) {
|
|
131
|
-
switch (role) {
|
|
132
|
-
case "planner": return "high";
|
|
133
|
-
case "executor": return "high";
|
|
134
|
-
case "reviewer": return "medium";
|
|
135
|
-
case "researcher": return "medium";
|
|
136
|
-
case "custom": return "low";
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Allocate token budgets proportionally by role weight.
|
|
141
|
-
*/
|
|
142
|
-
export function allocateTokenBudgets(agents, totalBudget) {
|
|
143
|
-
const totalWeight = agents.reduce((sum, a) => sum + ROLE_BUDGET_WEIGHTS[a.role], 0);
|
|
144
|
-
if (totalWeight === 0)
|
|
145
|
-
return agents;
|
|
146
|
-
return agents.map((agent) => ({
|
|
147
|
-
...agent,
|
|
148
|
-
maxTokenBudget: Math.floor((ROLE_BUDGET_WEIGHTS[agent.role] / totalWeight) * totalBudget),
|
|
149
|
-
}));
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Infer execution mode from agent dependency graph.
|
|
153
|
-
*/
|
|
154
|
-
export function inferExecutionMode(agents) {
|
|
155
|
-
const hasDeps = agents.some((a) => a.dependsOn.length > 0);
|
|
156
|
-
if (!hasDeps)
|
|
157
|
-
return "parallel";
|
|
158
|
-
// If every agent depends on the previous → pipeline
|
|
159
|
-
const isLinearChain = agents.every((a, i) => i === 0 ? a.dependsOn.length === 0 : a.dependsOn.includes(agents[i - 1].agentId));
|
|
160
|
-
if (isLinearChain)
|
|
161
|
-
return "pipeline";
|
|
162
|
-
return "sequential";
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Infer aggregation policy from mode and agent composition.
|
|
166
|
-
*/
|
|
167
|
-
export function inferAggregationPolicy(mode, agents) {
|
|
168
|
-
const hasReviewer = agents.some((a) => a.role === "reviewer");
|
|
169
|
-
const hasPlanner = agents.some((a) => a.role === "planner");
|
|
170
|
-
if (hasReviewer)
|
|
171
|
-
return "reviewer-gate";
|
|
172
|
-
if (hasPlanner && mode === "adaptive")
|
|
173
|
-
return "planner-sync";
|
|
174
|
-
if (mode === "parallel" && agents.length >= 3)
|
|
175
|
-
return "majority-vote";
|
|
176
|
-
return "merge-all";
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Determine the execution order for agents given their dependency graph.
|
|
180
|
-
* Returns groups of agent IDs that can execute concurrently.
|
|
181
|
-
*
|
|
182
|
-
* Example: [[planner], [executor-a, executor-b], [reviewer]]
|
|
183
|
-
*/
|
|
184
|
-
export function resolveExecutionOrder(agents) {
|
|
185
|
-
const idSet = new Set(agents.map((a) => a.agentId));
|
|
186
|
-
const depMap = new Map(agents.map((a) => [a.agentId, a.dependsOn.filter((d) => idSet.has(d))]));
|
|
187
|
-
const completed = new Set();
|
|
188
|
-
const groups = [];
|
|
189
|
-
let remaining = new Set(idSet);
|
|
190
|
-
let iterations = 0;
|
|
191
|
-
const maxIterations = agents.length + 1;
|
|
192
|
-
while (remaining.size > 0 && iterations < maxIterations) {
|
|
193
|
-
const ready = [];
|
|
194
|
-
for (const id of remaining) {
|
|
195
|
-
const deps = depMap.get(id) ?? [];
|
|
196
|
-
if (deps.every((d) => completed.has(d))) {
|
|
197
|
-
ready.push(id);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
if (ready.length === 0) {
|
|
201
|
-
// Circular dependency — break by picking first remaining
|
|
202
|
-
const first = remaining.values().next().value;
|
|
203
|
-
if (first)
|
|
204
|
-
ready.push(first);
|
|
205
|
-
}
|
|
206
|
-
for (const id of ready) {
|
|
207
|
-
remaining.delete(id);
|
|
208
|
-
completed.add(id);
|
|
209
|
-
}
|
|
210
|
-
groups.push(ready);
|
|
211
|
-
iterations++;
|
|
212
|
-
}
|
|
213
|
-
return groups;
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Aggregate team results into a single output string.
|
|
217
|
-
*/
|
|
218
|
-
export function aggregateTeamResults(results, policy) {
|
|
219
|
-
const completed = results.filter((r) => r.status === "completed");
|
|
220
|
-
switch (policy) {
|
|
221
|
-
case "first-success":
|
|
222
|
-
return completed[0]?.output ?? "[No successful agent output]";
|
|
223
|
-
case "majority-vote":
|
|
224
|
-
// Simple: return the most common non-empty output
|
|
225
|
-
if (completed.length === 0)
|
|
226
|
-
return "[No successful agent output]";
|
|
227
|
-
const counts = new Map();
|
|
228
|
-
for (const r of completed) {
|
|
229
|
-
const key = r.output.trim();
|
|
230
|
-
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
231
|
-
}
|
|
232
|
-
let best = "";
|
|
233
|
-
let bestCount = 0;
|
|
234
|
-
for (const [output, count] of counts) {
|
|
235
|
-
if (count > bestCount) {
|
|
236
|
-
best = output;
|
|
237
|
-
bestCount = count;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return best || completed[0].output;
|
|
241
|
-
case "merge-all":
|
|
242
|
-
return completed
|
|
243
|
-
.map((r) => `[${r.role}:${r.agentId}]\n${r.output}`)
|
|
244
|
-
.join("\n\n---\n\n");
|
|
245
|
-
case "reviewer-gate": {
|
|
246
|
-
const reviewer = completed.find((r) => r.role === "reviewer");
|
|
247
|
-
const executors = completed.filter((r) => r.role !== "reviewer");
|
|
248
|
-
if (reviewer) {
|
|
249
|
-
return `[Review]\n${reviewer.output}\n\n[Execution]\n${executors.map((e) => e.output).join("\n")}`;
|
|
250
|
-
}
|
|
251
|
-
return executors.map((e) => e.output).join("\n\n");
|
|
252
|
-
}
|
|
253
|
-
case "planner-sync": {
|
|
254
|
-
const planner = completed.find((r) => r.role === "planner");
|
|
255
|
-
const others = completed.filter((r) => r.role !== "planner");
|
|
256
|
-
if (planner) {
|
|
257
|
-
return `[Plan]\n${planner.output}\n\n[Results]\n${others.map((o) => `- ${o.agentId}: ${o.output.slice(0, 500)}`).join("\n")}`;
|
|
258
|
-
}
|
|
259
|
-
return others.map((o) => o.output).join("\n\n");
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Validate a team plan before execution.
|
|
265
|
-
* Returns a list of issues (empty = valid).
|
|
266
|
-
*/
|
|
267
|
-
export function validateTeamPlan(plan) {
|
|
268
|
-
const issues = [];
|
|
269
|
-
if (plan.agents.length === 0) {
|
|
270
|
-
issues.push("Team plan has no agents");
|
|
271
|
-
}
|
|
272
|
-
if (plan.agents.length > TEAM_MAX_AGENTS) {
|
|
273
|
-
issues.push(`Too many agents: ${plan.agents.length} > ${TEAM_MAX_AGENTS}`);
|
|
274
|
-
}
|
|
275
|
-
if (plan.totalTokenBudget <= 0) {
|
|
276
|
-
issues.push("Total token budget must be positive");
|
|
277
|
-
}
|
|
278
|
-
// Check for duplicate IDs
|
|
279
|
-
const ids = plan.agents.map((a) => a.agentId);
|
|
280
|
-
const uniqueIds = new Set(ids);
|
|
281
|
-
if (uniqueIds.size !== ids.length) {
|
|
282
|
-
issues.push("Duplicate agent IDs in team plan");
|
|
283
|
-
}
|
|
284
|
-
// Check dependency references
|
|
285
|
-
for (const agent of plan.agents) {
|
|
286
|
-
for (const dep of agent.dependsOn) {
|
|
287
|
-
if (!uniqueIds.has(dep)) {
|
|
288
|
-
issues.push(`Agent "${agent.agentId}" depends on non-existent agent "${dep}"`);
|
|
289
|
-
}
|
|
290
|
-
if (dep === agent.agentId) {
|
|
291
|
-
issues.push(`Agent "${agent.agentId}" depends on itself`);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
// Pipeline mode must have linear dependencies
|
|
296
|
-
if (plan.mode === "pipeline") {
|
|
297
|
-
for (let i = 1; i < plan.agents.length; i++) {
|
|
298
|
-
if (!plan.agents[i].dependsOn.includes(plan.agents[i - 1].agentId)) {
|
|
299
|
-
issues.push(`Pipeline mode requires linear dependencies; agent "${plan.agents[i].agentId}" doesn't depend on previous`);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return issues;
|
|
304
|
-
}
|
|
305
|
-
/**
|
|
306
|
-
* Determine if a team execution should be cancelled early based on current state.
|
|
307
|
-
*/
|
|
308
|
-
export function shouldCancelTeam(params) {
|
|
309
|
-
const { plan, results, elapsedMs } = params;
|
|
310
|
-
// Timeout check
|
|
311
|
-
if (plan.timeoutMs > 0 && elapsedMs > plan.timeoutMs) {
|
|
312
|
-
return { cancel: true, reason: "team_timeout" };
|
|
313
|
-
}
|
|
314
|
-
// Fail-fast: any failure causes cancel
|
|
315
|
-
if (plan.failFast && results.some((r) => r.status === "failed")) {
|
|
316
|
-
return { cancel: true, reason: "fail_fast" };
|
|
317
|
-
}
|
|
318
|
-
// All agents completed or failed — no need to cancel, just aggregate
|
|
319
|
-
if (results.length >= plan.agents.length) {
|
|
320
|
-
return { cancel: false, reason: "" };
|
|
321
|
-
}
|
|
322
|
-
// Budget exhaustion
|
|
323
|
-
const usedTokens = results.reduce((sum, r) => sum + r.tokenUsage.prompt + r.tokenUsage.completion, 0);
|
|
324
|
-
if (usedTokens >= plan.totalTokenBudget * 0.95) {
|
|
325
|
-
return { cancel: true, reason: "budget_exhausted" };
|
|
326
|
-
}
|
|
327
|
-
return { cancel: false, reason: "" };
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Build the task prompt for a specific agent, incorporating shared context
|
|
331
|
-
* and outputs from dependency agents.
|
|
332
|
-
*/
|
|
333
|
-
export function buildAgentTaskPrompt(params) {
|
|
334
|
-
const { agent, plan, dependencyResults } = params;
|
|
335
|
-
const parts = [];
|
|
336
|
-
// Shared context
|
|
337
|
-
if (plan.sharedContext) {
|
|
338
|
-
parts.push(`[Shared Context]\n${plan.sharedContext}`);
|
|
339
|
-
}
|
|
340
|
-
// Dependency outputs
|
|
341
|
-
if (dependencyResults.length > 0) {
|
|
342
|
-
const depSection = dependencyResults
|
|
343
|
-
.filter((r) => r.status === "completed")
|
|
344
|
-
.map((r) => `[From ${r.agentId} (${r.role})]\n${r.output}`)
|
|
345
|
-
.join("\n\n");
|
|
346
|
-
if (depSection) {
|
|
347
|
-
parts.push(`[Prior Results]\n${depSection}`);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
// Agent-specific task
|
|
351
|
-
parts.push(`[Your Task]\n${agent.taskDescription}`);
|
|
352
|
-
// Role-specific guidance
|
|
353
|
-
parts.push(`[Your Role: ${agent.role}]\n${getRoleGuidance(agent.role)}`);
|
|
354
|
-
return parts.join("\n\n---\n\n");
|
|
355
|
-
}
|
|
356
|
-
function getRoleGuidance(role) {
|
|
357
|
-
switch (role) {
|
|
358
|
-
case "planner":
|
|
359
|
-
return "You are the planner. Break down the task, identify dependencies, and create a clear execution plan. Do NOT execute actions yourself.";
|
|
360
|
-
case "executor":
|
|
361
|
-
return "You are the executor. Follow the plan and use tools to accomplish the task. Report what you did and any issues encountered.";
|
|
362
|
-
case "reviewer":
|
|
363
|
-
return "You are the reviewer. Examine the execution results for correctness, completeness, and quality. Point out any issues or improvements needed.";
|
|
364
|
-
case "researcher":
|
|
365
|
-
return "You are the researcher. Gather information, search for relevant context, and provide evidence-based findings. Do NOT modify any files.";
|
|
366
|
-
case "custom":
|
|
367
|
-
return "Complete the assigned task to the best of your ability.";
|
|
368
|
-
}
|
|
369
|
-
}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Team Tool Loop Wiring — Connects team orchestration to the tool loop.
|
|
3
|
-
*
|
|
4
|
-
* When the LLM invokes `team_create`, this module:
|
|
5
|
-
* 1. Validates the team plan
|
|
6
|
-
* 2. Maps team agents to fork contexts
|
|
7
|
-
* 3. Coordinates execution according to the team execution mode
|
|
8
|
-
* 4. Aggregates results according to the aggregation policy
|
|
9
|
-
* 5. Returns the aggregated result as a tool_result
|
|
10
|
-
*
|
|
11
|
-
* This bridges the gap between the pure strategy layer (team-orchestration.ts)
|
|
12
|
-
* and the Hub's tool loop.
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* Resolve the execution plan for a team request.
|
|
16
|
-
* Validates the request, resolves dependencies into ordered batches,
|
|
17
|
-
* and determines the execution topology.
|
|
18
|
-
*/
|
|
19
|
-
export function resolveTeamExecutionPlan(request) {
|
|
20
|
-
const agents = request.agents;
|
|
21
|
-
if (agents.length === 0) {
|
|
22
|
-
return { batches: [], totalBudget: 0, state: "planning" };
|
|
23
|
-
}
|
|
24
|
-
if (agents.length > 8) {
|
|
25
|
-
throw new Error(`Team cannot have more than 8 agents (got ${agents.length})`);
|
|
26
|
-
}
|
|
27
|
-
// For "parallel" mode — single batch with all agents
|
|
28
|
-
if (request.mode === "parallel") {
|
|
29
|
-
return {
|
|
30
|
-
batches: [{ agents, mode: "parallel" }],
|
|
31
|
-
totalBudget: agents.length * 50000,
|
|
32
|
-
state: "spawning",
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
// For "sequential" or "pipeline" — one agent per batch
|
|
36
|
-
if (request.mode === "sequential" || request.mode === "pipeline") {
|
|
37
|
-
return {
|
|
38
|
-
batches: agents.map((a) => ({ agents: [a], mode: "serial" })),
|
|
39
|
-
totalBudget: agents.length * 50000,
|
|
40
|
-
state: "spawning",
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
// "adaptive" — planner first, then remaining in parallel
|
|
44
|
-
const planners = agents.filter((a) => a.role === "planner");
|
|
45
|
-
const rest = agents.filter((a) => a.role !== "planner");
|
|
46
|
-
const batches = [];
|
|
47
|
-
if (planners.length > 0) {
|
|
48
|
-
batches.push({ agents: planners, mode: "serial" });
|
|
49
|
-
}
|
|
50
|
-
if (rest.length > 0) {
|
|
51
|
-
batches.push({ agents: rest, mode: "parallel" });
|
|
52
|
-
}
|
|
53
|
-
return {
|
|
54
|
-
batches,
|
|
55
|
-
totalBudget: agents.length * 50000,
|
|
56
|
-
state: "spawning",
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Aggregate results from team agents according to the aggregation policy.
|
|
61
|
-
*/
|
|
62
|
-
export function aggregateTeamToolResults(results, policy) {
|
|
63
|
-
const totalTokens = results.reduce((sum, r) => sum + r.result.tokensUsed, 0);
|
|
64
|
-
const allOk = results.every((r) => r.result.ok);
|
|
65
|
-
const anyOk = results.some((r) => r.result.ok);
|
|
66
|
-
switch (policy) {
|
|
67
|
-
case "first-success": {
|
|
68
|
-
const first = results.find((r) => r.result.ok);
|
|
69
|
-
return {
|
|
70
|
-
ok: !!first,
|
|
71
|
-
output: first?.result.output || "(No agent succeeded)",
|
|
72
|
-
agentResults: results,
|
|
73
|
-
totalTokensUsed: totalTokens,
|
|
74
|
-
finalState: first ? "completed" : "failed",
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
case "merge-all": {
|
|
78
|
-
const outputs = results.map((r) => `### ${r.role} (${r.agentId})\n${r.result.output || "(no output)"}`);
|
|
79
|
-
return {
|
|
80
|
-
ok: anyOk,
|
|
81
|
-
output: outputs.join("\n\n---\n\n"),
|
|
82
|
-
agentResults: results,
|
|
83
|
-
totalTokensUsed: totalTokens,
|
|
84
|
-
finalState: anyOk ? "completed" : "failed",
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
case "reviewer-gate": {
|
|
88
|
-
const reviewer = results.find((r) => r.role === "reviewer");
|
|
89
|
-
const executors = results.filter((r) => r.role !== "reviewer");
|
|
90
|
-
const executorOutput = executors.map((r) => r.result.output).filter(Boolean).join("\n\n");
|
|
91
|
-
if (reviewer && reviewer.result.ok) {
|
|
92
|
-
return {
|
|
93
|
-
ok: true,
|
|
94
|
-
output: `## Reviewed Output\n${executorOutput}\n\n## Review\n${reviewer.result.output}`,
|
|
95
|
-
agentResults: results,
|
|
96
|
-
totalTokensUsed: totalTokens,
|
|
97
|
-
finalState: "completed",
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
return {
|
|
101
|
-
ok: false,
|
|
102
|
-
output: `Review failed: ${reviewer?.result.output || "no reviewer result"}`,
|
|
103
|
-
agentResults: results,
|
|
104
|
-
totalTokensUsed: totalTokens,
|
|
105
|
-
finalState: "failed",
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
case "planner-sync": {
|
|
109
|
-
// Planner agent's output is the consolidated result
|
|
110
|
-
const planner = results.find((r) => r.role === "planner");
|
|
111
|
-
if (planner) {
|
|
112
|
-
return {
|
|
113
|
-
ok: planner.result.ok,
|
|
114
|
-
output: planner.result.output,
|
|
115
|
-
agentResults: results,
|
|
116
|
-
totalTokensUsed: totalTokens,
|
|
117
|
-
finalState: planner.result.ok ? "completed" : "failed",
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
// Fallback to merge-all
|
|
121
|
-
return aggregateTeamToolResults(results, "merge-all");
|
|
122
|
-
}
|
|
123
|
-
case "majority-vote":
|
|
124
|
-
default: {
|
|
125
|
-
// Simple: all outputs merged
|
|
126
|
-
return aggregateTeamToolResults(results, "merge-all");
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Map a team tool_call into the format needed for the tool loop to dispatch.
|
|
132
|
-
* Returns the list of fork configs the tool loop should execute.
|
|
133
|
-
*/
|
|
134
|
-
export function mapTeamRequestToForkConfigs(request, agentRegistry) {
|
|
135
|
-
return request.agents.map((agent) => {
|
|
136
|
-
const def = agentRegistry(agent.agentType);
|
|
137
|
-
if (!def) {
|
|
138
|
-
throw new Error(`Unknown agent type "${agent.agentType}" in team request`);
|
|
139
|
-
}
|
|
140
|
-
return {
|
|
141
|
-
agentDef: def,
|
|
142
|
-
prompt: agent.prompt,
|
|
143
|
-
maxTurns: agent.maxTurns,
|
|
144
|
-
role: agent.role,
|
|
145
|
-
};
|
|
146
|
-
});
|
|
147
|
-
}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
function resolveToolName(tool) {
|
|
2
|
-
if (tool.function && typeof tool.function === "object" && typeof tool.function.name === "string") {
|
|
3
|
-
return tool.function.name.trim();
|
|
4
|
-
}
|
|
5
|
-
if (typeof tool.name === "string") {
|
|
6
|
-
return tool.name.trim();
|
|
7
|
-
}
|
|
8
|
-
return "";
|
|
9
|
-
}
|
|
10
|
-
function isEligibleStatus(status) {
|
|
11
|
-
return status === "enabled-eligible" || status === "installed-awaiting-approval";
|
|
12
|
-
}
|
|
13
|
-
function buildEligibilityMap(eligibility) {
|
|
14
|
-
return new Map((eligibility ?? []).map((entry) => [entry.toolName, entry]));
|
|
15
|
-
}
|
|
16
|
-
export function filterEligibleTools(params) {
|
|
17
|
-
if (!params.eligibility?.length) {
|
|
18
|
-
return [...params.tools];
|
|
19
|
-
}
|
|
20
|
-
const eligibilityMap = buildEligibilityMap(params.eligibility);
|
|
21
|
-
return params.tools.filter((tool) => {
|
|
22
|
-
const name = resolveToolName(tool);
|
|
23
|
-
if (!name) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
const eligibility = eligibilityMap.get(name);
|
|
27
|
-
return !eligibility || isEligibleStatus(eligibility.status);
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
export function normalizeAnthropicFunctionToolDefinition(tool) {
|
|
31
|
-
if (!tool || typeof tool !== "object" || Array.isArray(tool)) {
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
|
-
const toolRecord = tool;
|
|
35
|
-
if (toolRecord.function && typeof toolRecord.function === "object") {
|
|
36
|
-
return toolRecord;
|
|
37
|
-
}
|
|
38
|
-
const name = typeof toolRecord.name === "string" ? toolRecord.name.trim() : "";
|
|
39
|
-
if (!name) {
|
|
40
|
-
return toolRecord;
|
|
41
|
-
}
|
|
42
|
-
const functionSpec = {
|
|
43
|
-
name,
|
|
44
|
-
parameters: toolRecord.input_schema && typeof toolRecord.input_schema === "object"
|
|
45
|
-
? toolRecord.input_schema
|
|
46
|
-
: toolRecord.parameters && typeof toolRecord.parameters === "object"
|
|
47
|
-
? toolRecord.parameters
|
|
48
|
-
: { type: "object", properties: {} },
|
|
49
|
-
};
|
|
50
|
-
if (typeof toolRecord.description === "string" && toolRecord.description.trim()) {
|
|
51
|
-
functionSpec.description = toolRecord.description;
|
|
52
|
-
}
|
|
53
|
-
if (typeof toolRecord.strict === "boolean") {
|
|
54
|
-
functionSpec.strict = toolRecord.strict;
|
|
55
|
-
}
|
|
56
|
-
return {
|
|
57
|
-
type: "function",
|
|
58
|
-
function: functionSpec,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
export function normalizeAnthropicToolChoice(toolChoice) {
|
|
62
|
-
if (!toolChoice || typeof toolChoice !== "object" || Array.isArray(toolChoice)) {
|
|
63
|
-
return toolChoice;
|
|
64
|
-
}
|
|
65
|
-
const choice = toolChoice;
|
|
66
|
-
if (choice.type === "auto") {
|
|
67
|
-
return "auto";
|
|
68
|
-
}
|
|
69
|
-
if (choice.type === "none") {
|
|
70
|
-
return "none";
|
|
71
|
-
}
|
|
72
|
-
if (choice.type === "required" || choice.type === "any") {
|
|
73
|
-
return "required";
|
|
74
|
-
}
|
|
75
|
-
if (choice.type === "tool" && typeof choice.name === "string" && choice.name.trim()) {
|
|
76
|
-
return {
|
|
77
|
-
type: "function",
|
|
78
|
-
function: { name: choice.name.trim() },
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
return toolChoice;
|
|
82
|
-
}
|
|
83
|
-
export function applyToolChoiceCompatibility(params) {
|
|
84
|
-
const warnings = [];
|
|
85
|
-
const compatibility = params.compatibility ?? {};
|
|
86
|
-
let normalizedToolChoice = params.toolChoice;
|
|
87
|
-
if (params.thinkingEnabled && compatibility.requireAutoWhenThinking) {
|
|
88
|
-
const type = typeof normalizedToolChoice === "object" && normalizedToolChoice && !Array.isArray(normalizedToolChoice)
|
|
89
|
-
? String(normalizedToolChoice.type ?? "")
|
|
90
|
-
: normalizedToolChoice;
|
|
91
|
-
if (type && type !== "auto" && type !== "none") {
|
|
92
|
-
warnings.push("tool_choice downgraded to auto because thinking mode requires auto/none compatibility.");
|
|
93
|
-
normalizedToolChoice = "auto";
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
if (normalizedToolChoice === "required" && compatibility.allowRequiredToolChoice === false) {
|
|
97
|
-
const fallback = compatibility.requiredFallback ?? "auto";
|
|
98
|
-
warnings.push(`tool_choice=required is not supported by this provider; downgraded to ${fallback}.`);
|
|
99
|
-
normalizedToolChoice = fallback;
|
|
100
|
-
}
|
|
101
|
-
if (normalizedToolChoice
|
|
102
|
-
&& typeof normalizedToolChoice === "object"
|
|
103
|
-
&& !Array.isArray(normalizedToolChoice)
|
|
104
|
-
&& normalizedToolChoice.type === "function"
|
|
105
|
-
&& compatibility.allowNamedToolChoice === false) {
|
|
106
|
-
const fallback = compatibility.namedFallback ?? "required";
|
|
107
|
-
warnings.push(`named tool_choice is not supported by this provider; downgraded to ${fallback}.`);
|
|
108
|
-
normalizedToolChoice = fallback;
|
|
109
|
-
}
|
|
110
|
-
return { normalizedToolChoice, warnings };
|
|
111
|
-
}
|
|
112
|
-
export function applyToolChoicePolicy(params) {
|
|
113
|
-
const compatibilityResult = applyToolChoiceCompatibility({
|
|
114
|
-
toolChoice: params.toolChoice,
|
|
115
|
-
thinkingEnabled: params.thinkingEnabled,
|
|
116
|
-
compatibility: params.compatibility,
|
|
117
|
-
});
|
|
118
|
-
const toolChoice = compatibilityResult.normalizedToolChoice;
|
|
119
|
-
const warnings = [...compatibilityResult.warnings];
|
|
120
|
-
const tools = filterEligibleTools({
|
|
121
|
-
tools: params.tools,
|
|
122
|
-
eligibility: params.eligibility,
|
|
123
|
-
});
|
|
124
|
-
if (!toolChoice || toolChoice === "auto") {
|
|
125
|
-
return { tools, normalizedToolChoice: toolChoice, warnings };
|
|
126
|
-
}
|
|
127
|
-
if (toolChoice === "none") {
|
|
128
|
-
return { tools: [], normalizedToolChoice: "none", warnings };
|
|
129
|
-
}
|
|
130
|
-
if (toolChoice === "required") {
|
|
131
|
-
if (tools.length === 0) {
|
|
132
|
-
throw new Error("tool_choice=required but no tools were provided");
|
|
133
|
-
}
|
|
134
|
-
return {
|
|
135
|
-
tools,
|
|
136
|
-
normalizedToolChoice: "required",
|
|
137
|
-
extraSystemPrompt: "You must call one of the available tools before responding.",
|
|
138
|
-
warnings,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
if (typeof toolChoice === "object" && !Array.isArray(toolChoice) && toolChoice.type === "function") {
|
|
142
|
-
const functionChoice = (toolChoice.function ?? undefined);
|
|
143
|
-
const targetName = typeof functionChoice?.name === "string"
|
|
144
|
-
? functionChoice.name.trim()
|
|
145
|
-
: "";
|
|
146
|
-
if (!targetName) {
|
|
147
|
-
throw new Error("tool_choice.function.name is required");
|
|
148
|
-
}
|
|
149
|
-
const matchedTools = tools.filter((tool) => resolveToolName(tool) === targetName);
|
|
150
|
-
if (matchedTools.length === 0) {
|
|
151
|
-
throw new Error(`tool_choice requested unknown tool: ${targetName}`);
|
|
152
|
-
}
|
|
153
|
-
return {
|
|
154
|
-
tools: matchedTools,
|
|
155
|
-
normalizedToolChoice: {
|
|
156
|
-
type: "function",
|
|
157
|
-
function: { name: targetName },
|
|
158
|
-
},
|
|
159
|
-
extraSystemPrompt: `You must call the ${targetName} tool before responding.`,
|
|
160
|
-
warnings,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
return { tools, normalizedToolChoice: toolChoice, warnings };
|
|
164
|
-
}
|