@compilr-dev/sdk 0.2.16 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.js +130 -6
- package/dist/capabilities/auto-detect.d.ts +22 -0
- package/dist/capabilities/auto-detect.js +49 -0
- package/dist/capabilities/context.d.ts +46 -0
- package/dist/capabilities/context.js +50 -0
- package/dist/capabilities/hook.d.ts +52 -0
- package/dist/capabilities/hook.js +110 -0
- package/dist/capabilities/index.d.ts +7 -0
- package/dist/capabilities/index.js +8 -0
- package/dist/capabilities/profile-resolver.d.ts +17 -0
- package/dist/capabilities/profile-resolver.js +38 -0
- package/dist/config.d.ts +59 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +8 -0
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CompilrAgent — high-level wrapper around @compilr-dev/agents Agent
|
|
3
3
|
*/
|
|
4
|
-
import { Agent, ContextManager, } from '@compilr-dev/agents';
|
|
4
|
+
import { Agent, ContextManager, createSuggestTool, } from '@compilr-dev/agents';
|
|
5
5
|
import { resolveProvider } from './provider.js';
|
|
6
6
|
import { resolvePreset } from './presets/index.js';
|
|
7
7
|
import { assembleTools, deduplicateTools } from './tools.js';
|
|
8
8
|
import { getContextWindow } from './models.js';
|
|
9
|
+
// Capability loading imports
|
|
10
|
+
import { TOOL_GROUPS } from './team/tool-config.js';
|
|
11
|
+
import { CAPABILITY_PACKS } from './capabilities/packs.js';
|
|
12
|
+
import { CapabilityManager } from './capabilities/manager.js';
|
|
13
|
+
import { CapabilityContext } from './capabilities/context.js';
|
|
14
|
+
import { createCapabilityHook } from './capabilities/hook.js';
|
|
15
|
+
import { createLoadCapabilityTool } from './capabilities/load-tool.js';
|
|
16
|
+
import { resolveProfileGroups, resolveUpfrontGroups } from './capabilities/profile-resolver.js';
|
|
17
|
+
import { MetaToolsRegistry, createMetaTools } from './meta-tools/registry.js';
|
|
9
18
|
/**
|
|
10
19
|
* Convert an AgentRunResult to our simplified RunResult
|
|
11
20
|
*/
|
|
@@ -77,6 +86,8 @@ class CompilrAgentImpl {
|
|
|
77
86
|
agent;
|
|
78
87
|
abortController;
|
|
79
88
|
totalUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
89
|
+
/** Meta-tools fallback handler (set when capabilities.enabled) */
|
|
90
|
+
_metaToolsFallback;
|
|
80
91
|
constructor(config) {
|
|
81
92
|
this.abortController = new AbortController();
|
|
82
93
|
// Resolve provider
|
|
@@ -98,8 +109,13 @@ class CompilrAgentImpl {
|
|
|
98
109
|
else {
|
|
99
110
|
systemPrompt = preset.systemPrompt;
|
|
100
111
|
}
|
|
101
|
-
// Assemble tools
|
|
102
|
-
|
|
112
|
+
// Assemble all tools from preset + config
|
|
113
|
+
let allTools = deduplicateTools(assembleTools(preset, config?.tools));
|
|
114
|
+
// Replace default suggest tool with callback-wired version when onSuggest is provided
|
|
115
|
+
if (config?.onSuggest) {
|
|
116
|
+
const wiredSuggest = createSuggestTool({ onSuggest: config.onSuggest });
|
|
117
|
+
allTools = allTools.map((t) => t.definition.name === 'suggest' ? wiredSuggest : t);
|
|
118
|
+
}
|
|
103
119
|
// Build context manager if configured
|
|
104
120
|
let contextManager;
|
|
105
121
|
if (config?.context) {
|
|
@@ -114,6 +130,110 @@ class CompilrAgentImpl {
|
|
|
114
130
|
// Build agent config
|
|
115
131
|
const permissionsConfig = buildPermissions(config?.permissions, preset.defaultPermissions, config?.permissionRules, config?.includeDefaultRules);
|
|
116
132
|
const guardrailsConfig = buildGuardrails(config?.guardrails);
|
|
133
|
+
// Merge hooks: user-provided + capability hook (if enabled)
|
|
134
|
+
const capabilityHooks = [];
|
|
135
|
+
let finalTools = allTools;
|
|
136
|
+
// --- Dynamic Capability Loading ---
|
|
137
|
+
if (config?.capabilities?.enabled) {
|
|
138
|
+
const capConfig = config.capabilities;
|
|
139
|
+
// 1. Resolve profile → groups → upfront groups
|
|
140
|
+
const profileGroups = resolveProfileGroups(capConfig.profile ?? 'full');
|
|
141
|
+
const upfrontGroups = resolveUpfrontGroups(profileGroups);
|
|
142
|
+
// 2. Create capability manager
|
|
143
|
+
const manager = new CapabilityManager({
|
|
144
|
+
profileGroups,
|
|
145
|
+
packs: CAPABILITY_PACKS,
|
|
146
|
+
upfrontGroups,
|
|
147
|
+
});
|
|
148
|
+
// 3. Build direct-tier tool name set (tools in upfront groups)
|
|
149
|
+
const directToolNames = new Set();
|
|
150
|
+
for (const groupId of upfrontGroups) {
|
|
151
|
+
const group = TOOL_GROUPS[groupId];
|
|
152
|
+
for (const toolName of group.tools) {
|
|
153
|
+
directToolNames.add(toolName);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// 4. Split tools into direct (registered on Agent) and meta (registry)
|
|
157
|
+
const directTools = [];
|
|
158
|
+
const metaRegistryTools = [];
|
|
159
|
+
for (const tool of allTools) {
|
|
160
|
+
const t = tool;
|
|
161
|
+
if (directToolNames.has(t.definition.name)) {
|
|
162
|
+
directTools.push(t);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
metaRegistryTools.push(t);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// 5. Initialize meta-tools registry
|
|
169
|
+
const registry = new MetaToolsRegistry();
|
|
170
|
+
const additionalTools = capConfig.additionalTools ?? [];
|
|
171
|
+
registry.initialize([...metaRegistryTools, ...additionalTools]);
|
|
172
|
+
// 6. Compute orphan tools (in registry but not in any capability pack)
|
|
173
|
+
const packedToolNames = new Set();
|
|
174
|
+
for (const pack of Object.values(CAPABILITY_PACKS)) {
|
|
175
|
+
for (const toolName of pack.tools) {
|
|
176
|
+
packedToolNames.add(toolName);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const orphanTools = registry
|
|
180
|
+
.getRegisteredTools()
|
|
181
|
+
.map((t) => t.definition.name)
|
|
182
|
+
.filter((name) => !packedToolNames.has(name));
|
|
183
|
+
// 7. Create capability context with filter sync
|
|
184
|
+
const capCtx = new CapabilityContext({
|
|
185
|
+
manager,
|
|
186
|
+
orphanTools,
|
|
187
|
+
onFilterUpdate: (allowed) => {
|
|
188
|
+
registry.setFilter(allowed);
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
capCtx.syncFilter();
|
|
192
|
+
// 8. Create meta-tools (get_tool_info) + fallback handler
|
|
193
|
+
const metaTools = createMetaTools(registry, {
|
|
194
|
+
onFilteredTool: (name) => capCtx.onFilteredTool(name),
|
|
195
|
+
});
|
|
196
|
+
// 9. Assemble final tool list: direct + get_tool_info + load_capability
|
|
197
|
+
finalTools = [...directTools, metaTools.getToolInfoTool];
|
|
198
|
+
if (manager.hasLoadablePacks()) {
|
|
199
|
+
finalTools.push(createLoadCapabilityTool(manager));
|
|
200
|
+
}
|
|
201
|
+
// 10. Create BeforeLLM hook for dynamic prompt assembly
|
|
202
|
+
const basePrompt = systemPrompt ?? '';
|
|
203
|
+
const capHook = createCapabilityHook(capCtx, basePrompt, {
|
|
204
|
+
maxAutoLoadsPerTurn: capConfig.maxAutoLoadsPerTurn,
|
|
205
|
+
conditionalModules: capConfig.conditionalModules,
|
|
206
|
+
staticSections: capConfig.staticSections,
|
|
207
|
+
getToolIndex: (allowedNames) => {
|
|
208
|
+
registry.setFilter(allowedNames);
|
|
209
|
+
return registry.getToolIndexForSystemPrompt();
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
capabilityHooks.push(capHook);
|
|
213
|
+
// Store registry for fallback handler setup (after Agent creation)
|
|
214
|
+
this._metaToolsFallback = metaTools.createFallback({
|
|
215
|
+
onFilteredTool: (name) => capCtx.onFilteredTool(name),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
// Legacy mode: register all tools directly (no capability loading)
|
|
220
|
+
finalTools = allTools;
|
|
221
|
+
// Include additional tools if provided (even without capabilities)
|
|
222
|
+
if (config?.capabilities?.additionalTools) {
|
|
223
|
+
finalTools = [...finalTools, ...config.capabilities.additionalTools];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Merge hooks: capability hook + user-provided hooks
|
|
227
|
+
const mergedHooks = { ...config?.hooks };
|
|
228
|
+
if (capabilityHooks.length > 0) {
|
|
229
|
+
const existingBeforeLLM = config?.hooks?.beforeLLM;
|
|
230
|
+
const existingHooks = existingBeforeLLM
|
|
231
|
+
? Array.isArray(existingBeforeLLM)
|
|
232
|
+
? existingBeforeLLM
|
|
233
|
+
: [existingBeforeLLM]
|
|
234
|
+
: [];
|
|
235
|
+
mergedHooks.beforeLLM = [...existingHooks, ...capabilityHooks];
|
|
236
|
+
}
|
|
117
237
|
this.agent = new Agent({
|
|
118
238
|
provider,
|
|
119
239
|
systemPrompt,
|
|
@@ -121,7 +241,7 @@ class CompilrAgentImpl {
|
|
|
121
241
|
toolTimeoutMs: config?.toolTimeoutMs,
|
|
122
242
|
contextManager,
|
|
123
243
|
autoContextManagement: contextManager !== undefined,
|
|
124
|
-
hooks:
|
|
244
|
+
hooks: mergedHooks,
|
|
125
245
|
pins: {},
|
|
126
246
|
permissions: {
|
|
127
247
|
defaultLevel: permissionsConfig.defaultLevel,
|
|
@@ -142,8 +262,12 @@ class CompilrAgentImpl {
|
|
|
142
262
|
}
|
|
143
263
|
},
|
|
144
264
|
});
|
|
145
|
-
// Register tools
|
|
146
|
-
this.agent.registerTools(
|
|
265
|
+
// Register tools
|
|
266
|
+
this.agent.registerTools(finalTools);
|
|
267
|
+
// Set fallback handler for meta-tools (if capabilities enabled)
|
|
268
|
+
if (this._metaToolsFallback) {
|
|
269
|
+
this.agent.getToolRegistry().setFallbackHandler(this._metaToolsFallback);
|
|
270
|
+
}
|
|
147
271
|
}
|
|
148
272
|
async run(message, options) {
|
|
149
273
|
const result = await this.agent.run(message, {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-Detect Capabilities — scans agent messages for "Tool not found"
|
|
3
|
+
* errors and auto-loads the corresponding capability packs.
|
|
4
|
+
*/
|
|
5
|
+
import type { Message } from '@compilr-dev/agents';
|
|
6
|
+
import type { CapabilityManager } from './manager.js';
|
|
7
|
+
export interface AutoDetectResult {
|
|
8
|
+
/** Pack IDs that were auto-loaded */
|
|
9
|
+
loaded: string[];
|
|
10
|
+
/** Forbidden tool names with suggested agents */
|
|
11
|
+
forbidden: Array<{
|
|
12
|
+
toolName: string;
|
|
13
|
+
packId: string;
|
|
14
|
+
suggestedAgent?: string;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Scan recent messages for "Tool not found: X" errors and auto-load
|
|
19
|
+
* the corresponding capability packs (max N per turn, loadable only).
|
|
20
|
+
* Also detects forbidden tool access for handoff suggestions.
|
|
21
|
+
*/
|
|
22
|
+
export declare function autoDetectCapabilities(manager: CapabilityManager, messages: Message[], maxLoads?: number): AutoDetectResult;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-Detect Capabilities — scans agent messages for "Tool not found"
|
|
3
|
+
* errors and auto-loads the corresponding capability packs.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Scan recent messages for "Tool not found: X" errors and auto-load
|
|
7
|
+
* the corresponding capability packs (max N per turn, loadable only).
|
|
8
|
+
* Also detects forbidden tool access for handoff suggestions.
|
|
9
|
+
*/
|
|
10
|
+
export function autoDetectCapabilities(manager, messages, maxLoads = 2) {
|
|
11
|
+
const result = { loaded: [], forbidden: [] };
|
|
12
|
+
const toolNotFoundPattern = /Tool not found: (\w+)/;
|
|
13
|
+
// Find the most recent user message (contains the last turn's tool results)
|
|
14
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
15
|
+
const msg = messages[i];
|
|
16
|
+
if (msg.role !== 'user' || typeof msg.content === 'string')
|
|
17
|
+
continue;
|
|
18
|
+
// Scan this message's tool_result blocks for "Tool not found" errors
|
|
19
|
+
for (const block of msg.content) {
|
|
20
|
+
if (block.type !== 'tool_result' || !block.isError || !block.content)
|
|
21
|
+
continue;
|
|
22
|
+
// Extract text from tool_result content (may be string or structured)
|
|
23
|
+
const rawContent = block.content;
|
|
24
|
+
const content = typeof rawContent === 'string' ? rawContent : String(rawContent);
|
|
25
|
+
const match = toolNotFoundPattern.exec(content);
|
|
26
|
+
if (!match)
|
|
27
|
+
continue;
|
|
28
|
+
const toolName = match[1];
|
|
29
|
+
const packId = manager.findPackForTool(toolName);
|
|
30
|
+
if (!packId || manager.isLoaded(packId))
|
|
31
|
+
continue;
|
|
32
|
+
const tier = manager.getTier(packId);
|
|
33
|
+
if (tier === 'loadable' && result.loaded.length < maxLoads) {
|
|
34
|
+
manager.load(packId, 'auto-detect');
|
|
35
|
+
result.loaded.push(packId);
|
|
36
|
+
}
|
|
37
|
+
else if (tier === 'forbidden') {
|
|
38
|
+
result.forbidden.push({
|
|
39
|
+
toolName,
|
|
40
|
+
packId,
|
|
41
|
+
suggestedAgent: manager.getSuggestedAgent(packId),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Only scan the most recent user message
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CapabilityContext — encapsulates the CapabilityManager + meta-tool
|
|
3
|
+
* filter synchronization for a single agent.
|
|
4
|
+
*
|
|
5
|
+
* Replaces the CLI's global setCapabilityManager/getCapabilityManager
|
|
6
|
+
* pattern with a per-agent instance.
|
|
7
|
+
*/
|
|
8
|
+
import type { CapabilityManager } from './manager.js';
|
|
9
|
+
export interface CapabilityContextConfig {
|
|
10
|
+
manager: CapabilityManager;
|
|
11
|
+
/**
|
|
12
|
+
* Tools in the meta-registry that aren't in any capability pack.
|
|
13
|
+
* These are always visible regardless of which packs are loaded.
|
|
14
|
+
*/
|
|
15
|
+
orphanTools: string[];
|
|
16
|
+
/**
|
|
17
|
+
* Callback to update the meta-tool filter with allowed tool names.
|
|
18
|
+
* Called whenever loaded packs change (auto-detect, auto-load, manual load).
|
|
19
|
+
*/
|
|
20
|
+
onFilterUpdate: (allowedTools: string[]) => void;
|
|
21
|
+
}
|
|
22
|
+
export declare class CapabilityContext {
|
|
23
|
+
readonly manager: CapabilityManager;
|
|
24
|
+
private readonly orphanTools;
|
|
25
|
+
private readonly onFilterUpdate;
|
|
26
|
+
constructor(config: CapabilityContextConfig);
|
|
27
|
+
/**
|
|
28
|
+
* Update the meta-tool filter to match current loaded state.
|
|
29
|
+
* Call after loading packs or during BeforeLLM hook.
|
|
30
|
+
*/
|
|
31
|
+
syncFilter(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Get all currently allowed tool names (loaded packs + orphans).
|
|
34
|
+
*/
|
|
35
|
+
getAllowedToolNames(): string[];
|
|
36
|
+
/**
|
|
37
|
+
* Auto-load callback for meta-registry fallback.
|
|
38
|
+
*
|
|
39
|
+
* When the agent tries to call a tool that's in the meta-registry
|
|
40
|
+
* but filtered out, this callback loads the corresponding pack
|
|
41
|
+
* and updates the filter so the tool can be retried.
|
|
42
|
+
*
|
|
43
|
+
* @returns true if the tool was successfully loaded (retry the call)
|
|
44
|
+
*/
|
|
45
|
+
onFilteredTool(toolName: string): boolean;
|
|
46
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CapabilityContext — encapsulates the CapabilityManager + meta-tool
|
|
3
|
+
* filter synchronization for a single agent.
|
|
4
|
+
*
|
|
5
|
+
* Replaces the CLI's global setCapabilityManager/getCapabilityManager
|
|
6
|
+
* pattern with a per-agent instance.
|
|
7
|
+
*/
|
|
8
|
+
export class CapabilityContext {
|
|
9
|
+
manager;
|
|
10
|
+
orphanTools;
|
|
11
|
+
onFilterUpdate;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.manager = config.manager;
|
|
14
|
+
this.orphanTools = config.orphanTools;
|
|
15
|
+
this.onFilterUpdate = config.onFilterUpdate;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Update the meta-tool filter to match current loaded state.
|
|
19
|
+
* Call after loading packs or during BeforeLLM hook.
|
|
20
|
+
*/
|
|
21
|
+
syncFilter() {
|
|
22
|
+
const activeTools = this.manager.getActiveToolNames();
|
|
23
|
+
this.onFilterUpdate([...activeTools, ...this.orphanTools]);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get all currently allowed tool names (loaded packs + orphans).
|
|
27
|
+
*/
|
|
28
|
+
getAllowedToolNames() {
|
|
29
|
+
return [...this.manager.getActiveToolNames(), ...this.orphanTools];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Auto-load callback for meta-registry fallback.
|
|
33
|
+
*
|
|
34
|
+
* When the agent tries to call a tool that's in the meta-registry
|
|
35
|
+
* but filtered out, this callback loads the corresponding pack
|
|
36
|
+
* and updates the filter so the tool can be retried.
|
|
37
|
+
*
|
|
38
|
+
* @returns true if the tool was successfully loaded (retry the call)
|
|
39
|
+
*/
|
|
40
|
+
onFilteredTool(toolName) {
|
|
41
|
+
const packId = this.manager.findPackForTool(toolName);
|
|
42
|
+
if (!packId || this.manager.isLoaded(packId))
|
|
43
|
+
return false;
|
|
44
|
+
if (this.manager.getTier(packId) !== 'loadable')
|
|
45
|
+
return false;
|
|
46
|
+
this.manager.load(packId, 'auto-load');
|
|
47
|
+
this.syncFilter();
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Hook — BeforeLLM hook that dynamically rebuilds the
|
|
3
|
+
* system prompt based on loaded capability packs.
|
|
4
|
+
*
|
|
5
|
+
* Responsibilities:
|
|
6
|
+
* 1. Auto-detect "Tool not found" in recent messages → auto-load packs
|
|
7
|
+
* 2. Sync meta-tool filter with loaded state
|
|
8
|
+
* 3. Rebuild system prompt with dynamic sections (modules, snippets, catalog)
|
|
9
|
+
* 4. Preserve anchor blocks from agent library injection
|
|
10
|
+
* 5. Inject forbidden tool boundary hints as messages
|
|
11
|
+
*/
|
|
12
|
+
import type { Message, BeforeLLMHookResult } from '@compilr-dev/agents';
|
|
13
|
+
import type { CapabilityContext } from './context.js';
|
|
14
|
+
/**
|
|
15
|
+
* A conditional system prompt module that gets included
|
|
16
|
+
* when certain capability packs are loaded.
|
|
17
|
+
*/
|
|
18
|
+
export interface ConditionalModule {
|
|
19
|
+
/** Module content to append to the system prompt */
|
|
20
|
+
content: string;
|
|
21
|
+
/** Include when ANY of these prompt module IDs are active */
|
|
22
|
+
whenModuleActive: string[];
|
|
23
|
+
}
|
|
24
|
+
export interface CapabilityHookConfig {
|
|
25
|
+
/** Max packs to auto-load per turn from "Tool not found" detection */
|
|
26
|
+
maxAutoLoadsPerTurn?: number;
|
|
27
|
+
/** Conditional prompt modules to include based on loaded packs */
|
|
28
|
+
conditionalModules?: ConditionalModule[];
|
|
29
|
+
/**
|
|
30
|
+
* Callback to generate the filtered tool index section.
|
|
31
|
+
* Receives the list of currently allowed tool names.
|
|
32
|
+
* Should return a string to append to the system prompt (e.g., meta-tool index).
|
|
33
|
+
*/
|
|
34
|
+
getToolIndex?: (allowedToolNames: string[]) => string;
|
|
35
|
+
/**
|
|
36
|
+
* Static prompt section to always include (e.g., meta-tools protocol).
|
|
37
|
+
* Appended before conditional modules.
|
|
38
|
+
*/
|
|
39
|
+
staticSections?: string[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create a BeforeLLM hook that dynamically manages the system prompt
|
|
43
|
+
* based on loaded capabilities.
|
|
44
|
+
*
|
|
45
|
+
* @param ctx - CapabilityContext for this agent
|
|
46
|
+
* @param basePrompt - Base system prompt (without dynamic sections)
|
|
47
|
+
* @param config - Optional configuration
|
|
48
|
+
*/
|
|
49
|
+
export declare function createCapabilityHook(ctx: CapabilityContext, basePrompt: string, config?: CapabilityHookConfig): (hookCtx: {
|
|
50
|
+
messages: Message[];
|
|
51
|
+
systemPrompt: string;
|
|
52
|
+
}) => BeforeLLMHookResult;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Hook — BeforeLLM hook that dynamically rebuilds the
|
|
3
|
+
* system prompt based on loaded capability packs.
|
|
4
|
+
*
|
|
5
|
+
* Responsibilities:
|
|
6
|
+
* 1. Auto-detect "Tool not found" in recent messages → auto-load packs
|
|
7
|
+
* 2. Sync meta-tool filter with loaded state
|
|
8
|
+
* 3. Rebuild system prompt with dynamic sections (modules, snippets, catalog)
|
|
9
|
+
* 4. Preserve anchor blocks from agent library injection
|
|
10
|
+
* 5. Inject forbidden tool boundary hints as messages
|
|
11
|
+
*/
|
|
12
|
+
import { autoDetectCapabilities } from './auto-detect.js';
|
|
13
|
+
import { generateCapabilityCatalog } from './catalog.js';
|
|
14
|
+
/**
|
|
15
|
+
* Create a BeforeLLM hook that dynamically manages the system prompt
|
|
16
|
+
* based on loaded capabilities.
|
|
17
|
+
*
|
|
18
|
+
* @param ctx - CapabilityContext for this agent
|
|
19
|
+
* @param basePrompt - Base system prompt (without dynamic sections)
|
|
20
|
+
* @param config - Optional configuration
|
|
21
|
+
*/
|
|
22
|
+
export function createCapabilityHook(ctx, basePrompt, config) {
|
|
23
|
+
const maxAutoLoads = config?.maxAutoLoadsPerTurn ?? 2;
|
|
24
|
+
const conditionalModules = config?.conditionalModules ?? [];
|
|
25
|
+
const staticSections = config?.staticSections ?? [];
|
|
26
|
+
const getToolIndex = config?.getToolIndex;
|
|
27
|
+
return (hookCtx) => {
|
|
28
|
+
const manager = ctx.manager;
|
|
29
|
+
// 1. Auto-detect capability needs from tool failures (safety net)
|
|
30
|
+
let forbiddenHints = [];
|
|
31
|
+
if (manager.hasLoadablePacks() || hookCtx.messages.length > 1) {
|
|
32
|
+
const detected = autoDetectCapabilities(manager, hookCtx.messages, maxAutoLoads);
|
|
33
|
+
if (detected.forbidden.length > 0) {
|
|
34
|
+
forbiddenHints = detected.forbidden.map((f) => {
|
|
35
|
+
const agentHint = f.suggestedAgent
|
|
36
|
+
? ` Consider using handoff("${f.suggestedAgent}", "Need ${f.packId} access for ${f.toolName}").`
|
|
37
|
+
: '';
|
|
38
|
+
return `[System] Tool "${f.toolName}" requires ${f.packId} capability which is outside your tool profile.${agentHint}`;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// 2. Sync meta-tool filter with current loaded state
|
|
43
|
+
ctx.syncFilter();
|
|
44
|
+
// 3. Build dynamic system prompt sections
|
|
45
|
+
const dynamicSections = [];
|
|
46
|
+
// Static sections (e.g., meta-tools protocol)
|
|
47
|
+
for (const section of staticSections) {
|
|
48
|
+
dynamicSections.push(section);
|
|
49
|
+
}
|
|
50
|
+
// Conditional modules based on loaded packs
|
|
51
|
+
const activeModuleIds = manager.getActivePromptModuleIds();
|
|
52
|
+
for (const mod of conditionalModules) {
|
|
53
|
+
if (mod.whenModuleActive.some((id) => activeModuleIds.has(id))) {
|
|
54
|
+
dynamicSections.push(mod.content);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Prompt snippets from loaded packs
|
|
58
|
+
const snippets = manager.getActivePromptSnippets();
|
|
59
|
+
if (snippets.length > 0) {
|
|
60
|
+
dynamicSections.push('## Active Tool Capabilities\n' + snippets.join('\n'));
|
|
61
|
+
}
|
|
62
|
+
// Filtered tool index
|
|
63
|
+
if (getToolIndex) {
|
|
64
|
+
const allowedTools = ctx.getAllowedToolNames();
|
|
65
|
+
dynamicSections.push(getToolIndex(allowedTools));
|
|
66
|
+
}
|
|
67
|
+
// Capability catalog (if loadable packs remain)
|
|
68
|
+
if (manager.hasLoadablePacks()) {
|
|
69
|
+
const catalog = manager.getCatalog();
|
|
70
|
+
const catalogSection = generateCapabilityCatalog(catalog, manager.getLoadedPackIds());
|
|
71
|
+
if (catalogSection) {
|
|
72
|
+
dynamicSections.push(catalogSection);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// 4. Preserve anchors from the current system prompt.
|
|
76
|
+
// The agents library injects anchors into systemContent BEFORE this hook runs.
|
|
77
|
+
// We must extract and re-append them since we rebuild from basePrompt.
|
|
78
|
+
const anchorsBlock = extractAnchorsBlock(hookCtx.systemPrompt);
|
|
79
|
+
const hookResult = {
|
|
80
|
+
systemPrompt: basePrompt + '\n\n' + dynamicSections.join('\n\n') + anchorsBlock,
|
|
81
|
+
};
|
|
82
|
+
// 5. Inject forbidden boundary messages (handoff suggestions)
|
|
83
|
+
if (forbiddenHints.length > 0) {
|
|
84
|
+
const hintContent = forbiddenHints.join('\n');
|
|
85
|
+
const hintMessage = { role: 'user', content: hintContent };
|
|
86
|
+
hookResult.messages = [...hookCtx.messages, hintMessage];
|
|
87
|
+
}
|
|
88
|
+
return hookResult;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Extract the anchors block from a system prompt.
|
|
93
|
+
* The agents library injects "## Active Anchors (Critical Information)"
|
|
94
|
+
* before BeforeLLM hooks run. We need to preserve it when rebuilding.
|
|
95
|
+
*/
|
|
96
|
+
function extractAnchorsBlock(systemPrompt) {
|
|
97
|
+
const anchorMarker = '## Active Anchors (Critical Information)';
|
|
98
|
+
const anchorIdx = systemPrompt.indexOf(anchorMarker);
|
|
99
|
+
if (anchorIdx <= 0)
|
|
100
|
+
return '';
|
|
101
|
+
// Find the preceding "---" separator
|
|
102
|
+
const separatorBefore = systemPrompt.lastIndexOf('---', anchorIdx);
|
|
103
|
+
const startIdx = separatorBefore > 0 ? separatorBefore : anchorIdx;
|
|
104
|
+
// Find the end: either the next "---" after the anchor block, or end of string
|
|
105
|
+
const afterAnchor = systemPrompt.indexOf('\n\n---\n\n', anchorIdx);
|
|
106
|
+
if (afterAnchor > 0) {
|
|
107
|
+
return '\n\n' + systemPrompt.substring(startIdx, afterAnchor + 7);
|
|
108
|
+
}
|
|
109
|
+
return '\n\n' + systemPrompt.substring(startIdx);
|
|
110
|
+
}
|
|
@@ -12,3 +12,10 @@ export { CapabilityManager } from './manager.js';
|
|
|
12
12
|
export type { CapabilityManagerConfig } from './manager.js';
|
|
13
13
|
export { createLoadCapabilityTool } from './load-tool.js';
|
|
14
14
|
export { generateCapabilityCatalog } from './catalog.js';
|
|
15
|
+
export { CapabilityContext } from './context.js';
|
|
16
|
+
export type { CapabilityContextConfig } from './context.js';
|
|
17
|
+
export { createCapabilityHook } from './hook.js';
|
|
18
|
+
export type { CapabilityHookConfig, ConditionalModule } from './hook.js';
|
|
19
|
+
export { autoDetectCapabilities } from './auto-detect.js';
|
|
20
|
+
export type { AutoDetectResult } from './auto-detect.js';
|
|
21
|
+
export { resolveProfileGroups, resolveUpfrontGroups } from './profile-resolver.js';
|
|
@@ -14,3 +14,11 @@ export { CapabilityManager } from './manager.js';
|
|
|
14
14
|
export { createLoadCapabilityTool } from './load-tool.js';
|
|
15
15
|
// Catalog generator
|
|
16
16
|
export { generateCapabilityCatalog } from './catalog.js';
|
|
17
|
+
// Context — per-agent capability state + filter sync
|
|
18
|
+
export { CapabilityContext } from './context.js';
|
|
19
|
+
// Hook — BeforeLLM hook for dynamic prompt assembly
|
|
20
|
+
export { createCapabilityHook } from './hook.js';
|
|
21
|
+
// Auto-detection — scan messages for "Tool not found" errors
|
|
22
|
+
export { autoDetectCapabilities } from './auto-detect.js';
|
|
23
|
+
// Profile resolver — convert profiles/tool lists to group IDs
|
|
24
|
+
export { resolveProfileGroups, resolveUpfrontGroups } from './profile-resolver.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile Resolver — converts tool profiles or custom tool lists
|
|
3
|
+
* into capability group IDs for the CapabilityManager.
|
|
4
|
+
*/
|
|
5
|
+
import { type ToolProfile } from '../team/tool-config.js';
|
|
6
|
+
/**
|
|
7
|
+
* Convert a profile name or custom tool list into capability group IDs.
|
|
8
|
+
*
|
|
9
|
+
* @param profile - A named profile ('full', 'developer', etc.) or a custom tool name list
|
|
10
|
+
* @returns Array of group IDs that the profile allows
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveProfileGroups(profile: ToolProfile | string[]): string[];
|
|
13
|
+
/**
|
|
14
|
+
* Determine which groups should be loaded upfront (direct tier).
|
|
15
|
+
* Upfront = groups marked as 'direct' tier AND in the agent's profile.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveUpfrontGroups(profileGroups: string[]): string[];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile Resolver — converts tool profiles or custom tool lists
|
|
3
|
+
* into capability group IDs for the CapabilityManager.
|
|
4
|
+
*/
|
|
5
|
+
import { TOOL_GROUPS, TOOL_PROFILES } from '../team/tool-config.js';
|
|
6
|
+
/**
|
|
7
|
+
* Convert a profile name or custom tool list into capability group IDs.
|
|
8
|
+
*
|
|
9
|
+
* @param profile - A named profile ('full', 'developer', etc.) or a custom tool name list
|
|
10
|
+
* @returns Array of group IDs that the profile allows
|
|
11
|
+
*/
|
|
12
|
+
export function resolveProfileGroups(profile) {
|
|
13
|
+
if (typeof profile === 'string') {
|
|
14
|
+
// Named profile — look up in TOOL_PROFILES
|
|
15
|
+
const groups = TOOL_PROFILES[profile];
|
|
16
|
+
return [...groups];
|
|
17
|
+
}
|
|
18
|
+
// Custom tool list — find groups that contain at least one of these tools
|
|
19
|
+
const toolSet = new Set(profile);
|
|
20
|
+
const groups = [];
|
|
21
|
+
for (const [groupId, group] of Object.entries(TOOL_GROUPS)) {
|
|
22
|
+
if (group.tools.some((t) => toolSet.has(t))) {
|
|
23
|
+
groups.push(groupId);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return groups;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Determine which groups should be loaded upfront (direct tier).
|
|
30
|
+
* Upfront = groups marked as 'direct' tier AND in the agent's profile.
|
|
31
|
+
*/
|
|
32
|
+
export function resolveUpfrontGroups(profileGroups) {
|
|
33
|
+
const profileSet = new Set(profileGroups);
|
|
34
|
+
return Object.entries(TOOL_GROUPS)
|
|
35
|
+
.filter(([, group]) => group.tier === 'direct')
|
|
36
|
+
.map(([id]) => id)
|
|
37
|
+
.filter((id) => profileSet.has(id));
|
|
38
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -3,6 +3,49 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { LLMProvider, Message, Tool, ToolPermission, HooksConfig, AnchorInput, AgentEvent, ToolExecutionResult } from '@compilr-dev/agents';
|
|
5
5
|
import type { Preset } from './presets/types.js';
|
|
6
|
+
import type { ToolProfile } from './team/tool-config.js';
|
|
7
|
+
import type { ConditionalModule } from './capabilities/hook.js';
|
|
8
|
+
/**
|
|
9
|
+
* Dynamic capability loading configuration.
|
|
10
|
+
*
|
|
11
|
+
* When enabled, tools are loaded on-demand instead of all upfront,
|
|
12
|
+
* reducing token usage by ~50-60% on initial turns.
|
|
13
|
+
*/
|
|
14
|
+
export interface CapabilitiesConfig {
|
|
15
|
+
/** Enable dynamic capability loading. Default: false (all tools upfront) */
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Tool profile — determines which capability packs are available.
|
|
19
|
+
* Named profile ('full', 'developer', etc.) or custom tool name list.
|
|
20
|
+
* Default: 'full'
|
|
21
|
+
*/
|
|
22
|
+
profile?: ToolProfile | string[];
|
|
23
|
+
/**
|
|
24
|
+
* Whether to enable meta-tools mode (get_tool_info + registry fallback).
|
|
25
|
+
* Loadable tools go through the meta-registry instead of being declared directly.
|
|
26
|
+
* Default: true when capabilities.enabled is true
|
|
27
|
+
*/
|
|
28
|
+
metaTools?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Max packs to auto-load per turn when "Tool not found" detected.
|
|
31
|
+
* Default: 2
|
|
32
|
+
*/
|
|
33
|
+
maxAutoLoadsPerTurn?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Additional tools to include that aren't in any capability pack
|
|
36
|
+
* (e.g., MCP tools, platform tools). Always available.
|
|
37
|
+
*/
|
|
38
|
+
additionalTools?: Tool[];
|
|
39
|
+
/**
|
|
40
|
+
* Conditional prompt modules to include based on loaded packs.
|
|
41
|
+
* Default: git-safety, platform-tool-hints, factory-tool-hints
|
|
42
|
+
*/
|
|
43
|
+
conditionalModules?: ConditionalModule[];
|
|
44
|
+
/**
|
|
45
|
+
* Static prompt sections to always include (e.g., meta-tools protocol).
|
|
46
|
+
*/
|
|
47
|
+
staticSections?: string[];
|
|
48
|
+
}
|
|
6
49
|
/**
|
|
7
50
|
* Supported provider types for auto-detection
|
|
8
51
|
*/
|
|
@@ -147,6 +190,22 @@ export interface CompilrAgentConfig {
|
|
|
147
190
|
hooks?: HooksConfig;
|
|
148
191
|
/** Context management configuration */
|
|
149
192
|
context?: ContextConfig;
|
|
193
|
+
/**
|
|
194
|
+
* Callback for the suggest tool. When provided, the default no-op suggest
|
|
195
|
+
* tool is replaced with one that calls this callback.
|
|
196
|
+
* The suggestion appears as ghost text in the UI input area.
|
|
197
|
+
*/
|
|
198
|
+
onSuggest?: (event: {
|
|
199
|
+
type: 'suggest';
|
|
200
|
+
action: string;
|
|
201
|
+
reason?: string;
|
|
202
|
+
}) => void;
|
|
203
|
+
/**
|
|
204
|
+
* Dynamic capability loading configuration.
|
|
205
|
+
* When enabled, tools are loaded on-demand for token efficiency.
|
|
206
|
+
* Omit or set enabled: false for the legacy all-tools-upfront mode.
|
|
207
|
+
*/
|
|
208
|
+
capabilities?: CapabilitiesConfig;
|
|
150
209
|
}
|
|
151
210
|
/**
|
|
152
211
|
* High-level agent interface
|
package/dist/index.d.ts
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
36
|
export { createCompilrAgent } from './agent.js';
|
|
37
|
-
export type { CompilrAgentConfig, CompilrAgent, RunOptions, RunResult, ToolCallRecord, ToolConfig, UsageInfo, ProviderType, PermissionCallback, GuardrailConfig, ContextConfig, } from './config.js';
|
|
37
|
+
export type { CompilrAgentConfig, CompilrAgent, RunOptions, RunResult, ToolCallRecord, ToolConfig, UsageInfo, ProviderType, PermissionCallback, GuardrailConfig, ContextConfig, CapabilitiesConfig, } from './config.js';
|
|
38
38
|
export { AgentTeam, TeamAgent, SharedContextManager, ArtifactStore, DelegationTracker, ContextResolver, } from './team/index.js';
|
|
39
39
|
export type { AgentTeamConfig, TeamAgentConfig, ITeamPersistence, IArtifactStorage, ISessionRegistry, CustomAgentDefinition, ToolConfig as TeamToolConfig, ToolTier, ToolGroup, ProfileInfo, } from './team/index.js';
|
|
40
40
|
export type { AgentRole, RoleMetadata, ToolProfile, MascotExpression, BackgroundSessionInfo, SerializedTeam, SerializedTeamAgent, TeamMetadata, TeamEvent, TeamEventType, TeamEventHandler, Artifact, ArtifactType as TeamArtifactType, ArtifactSummary as TeamArtifactSummary, CreateArtifactOptions, UpdateArtifactOptions, SerializedArtifact, SharedContext, SharedProjectInfo, SharedTeamInfo, TeamRosterEntry, TeamActivity, TeamActivityType, SharedDecision, TokenBudget, SerializedSharedContext, ParsedMention, ParsedInput, ResolvedMention, ResolveOptions, ResolutionSource, Delegation, DelegationStatus, DelegationResult, CompletionEvent, CreateDelegationOptions, DelegationStats, DelegationTrackerEvents, SkillToolRequirement, } from './team/index.js';
|
|
@@ -49,8 +49,8 @@ export { type ModelTier, type TierInfo, type ProviderModelMap, MODEL_TIERS, TIER
|
|
|
49
49
|
export { assembleTools, deduplicateTools } from './tools.js';
|
|
50
50
|
export { MetaToolsRegistry, createMetaTools, META_TOOLS_SYSTEM_PROMPT_PREFIX, } from './meta-tools/index.js';
|
|
51
51
|
export type { MetaToolStats, MetaTools, FallbackOptions } from './meta-tools/index.js';
|
|
52
|
-
export { CapabilityManager, CAPABILITY_PACKS, FORBIDDEN_PACK_SUGGESTIONS, getPackCount, getAllPackIds, createLoadCapabilityTool, generateCapabilityCatalog, } from './capabilities/index.js';
|
|
53
|
-
export type { CapabilityPack, CapabilityTier, LoadedCapability, CapabilityCatalogEntry, CapabilityLoadResult, CapabilityManagerConfig, } from './capabilities/index.js';
|
|
52
|
+
export { CapabilityManager, CapabilityContext, createCapabilityHook, autoDetectCapabilities, resolveProfileGroups, resolveUpfrontGroups, CAPABILITY_PACKS, FORBIDDEN_PACK_SUGGESTIONS, getPackCount, getAllPackIds, createLoadCapabilityTool, generateCapabilityCatalog, } from './capabilities/index.js';
|
|
53
|
+
export type { CapabilityPack, CapabilityTier, LoadedCapability, CapabilityCatalogEntry, CapabilityLoadResult, CapabilityManagerConfig, CapabilityContextConfig, CapabilityHookConfig, ConditionalModule, AutoDetectResult, } from './capabilities/index.js';
|
|
54
54
|
export { SystemPromptBuilder, buildSystemPrompt, detectGitRepository, getModuleStats, ALL_MODULES, IDENTITY_MODULE, STYLE_MODULE, TASK_EXECUTION_MODULE, TODO_MANAGEMENT_MODULE, TOOL_USAGE_DIRECT_MODULE, TOOL_USAGE_HINTS_MODULE, PLATFORM_TOOL_HINTS_MODULE, FACTORY_TOOL_HINTS_MODULE, TOOL_USAGE_META_MODULE, DELEGATION_MODULE, GIT_SAFETY_MODULE, SUGGEST_MODULE, IMPORTANT_RULES_MODULE, VISUAL_OUTPUT_MODULE, ENVIRONMENT_MODULE, shouldIncludeModule, getEstimatedTokensForConditions, getTotalEstimatedTokens, } from './system-prompt/index.js';
|
|
55
55
|
export type { SystemPromptContext, BuildResult, SystemPromptModule, ModuleConditions, } from './system-prompt/index.js';
|
|
56
56
|
export type { ProjectType, ProjectStatus, RepoPattern, WorkflowMode, LifecycleState, WorkItemType, WorkItemStatus, WorkItemPriority, GuidedStep, DocumentType, PlanStatus, Project, WorkItem, ProjectDocument, Plan, PlanSummary, PlanWithWorkItem, HistoryEntry, CreateProjectInput, UpdateProjectInput, ProjectListOptions, CreateWorkItemInput, UpdateWorkItemInput, QueryWorkItemsInput, CreateDocumentInput, UpdateDocumentInput, CreatePlanInput, UpdatePlanInput, ListPlansOptions, WorkItemQueryResult, ProjectListResult, BulkCreateItem, WorkItemComment, CreateCommentInput, UpdateCommentInput, IProjectRepository, IWorkItemRepository, IDocumentRepository, IPlanRepository, ICommentRepository, IAnchorService, IArtifactService, IEpisodeService, AnchorData, ArtifactType, ArtifactData, ArtifactSummaryData, WorkEpisode, ProjectWorkSummary, PlatformContext, PlatformToolsConfig, PlatformHooks, StepCriteria, } from './platform/index.js';
|
package/dist/index.js
CHANGED
|
@@ -96,6 +96,14 @@ export { MetaToolsRegistry, createMetaTools, META_TOOLS_SYSTEM_PROMPT_PREFIX, }
|
|
|
96
96
|
export {
|
|
97
97
|
// Manager
|
|
98
98
|
CapabilityManager,
|
|
99
|
+
// Context (per-agent state + filter sync)
|
|
100
|
+
CapabilityContext,
|
|
101
|
+
// Hook (BeforeLLM dynamic prompt assembly)
|
|
102
|
+
createCapabilityHook,
|
|
103
|
+
// Auto-detection (tool-not-found scanning)
|
|
104
|
+
autoDetectCapabilities,
|
|
105
|
+
// Profile resolution
|
|
106
|
+
resolveProfileGroups, resolveUpfrontGroups,
|
|
99
107
|
// Pack definitions
|
|
100
108
|
CAPABILITY_PACKS, FORBIDDEN_PACK_SUGGESTIONS, getPackCount, getAllPackIds,
|
|
101
109
|
// Self-loading tool
|