@compilr-dev/sdk 0.1.19 → 0.1.21
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/capabilities/catalog.d.ts +15 -0
- package/dist/capabilities/catalog.js +32 -0
- package/dist/capabilities/index.d.ts +14 -0
- package/dist/capabilities/index.js +16 -0
- package/dist/capabilities/load-tool.d.ts +21 -0
- package/dist/capabilities/load-tool.js +62 -0
- package/dist/capabilities/manager.d.ts +82 -0
- package/dist/capabilities/manager.js +223 -0
- package/dist/capabilities/packs.d.ts +29 -0
- package/dist/capabilities/packs.js +291 -0
- package/dist/capabilities/types.d.ts +67 -0
- package/dist/capabilities/types.js +7 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +12 -0
- package/dist/meta-tools/registry.js +69 -5
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Catalog Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates the system prompt section that lists available (loadable)
|
|
5
|
+
* capability packs for agent self-loading.
|
|
6
|
+
*/
|
|
7
|
+
import type { CapabilityCatalogEntry } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Generate a system prompt section listing loadable capability packs.
|
|
10
|
+
*
|
|
11
|
+
* @param catalog - Entries from CapabilityManager.getCatalog()
|
|
12
|
+
* @param loadedPackIds - Currently loaded pack IDs (for "Currently loaded" line)
|
|
13
|
+
* @returns System prompt section string, or empty string if catalog is empty
|
|
14
|
+
*/
|
|
15
|
+
export declare function generateCapabilityCatalog(catalog: CapabilityCatalogEntry[], loadedPackIds: string[]): string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Catalog Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates the system prompt section that lists available (loadable)
|
|
5
|
+
* capability packs for agent self-loading.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Generate a system prompt section listing loadable capability packs.
|
|
9
|
+
*
|
|
10
|
+
* @param catalog - Entries from CapabilityManager.getCatalog()
|
|
11
|
+
* @param loadedPackIds - Currently loaded pack IDs (for "Currently loaded" line)
|
|
12
|
+
* @returns System prompt section string, or empty string if catalog is empty
|
|
13
|
+
*/
|
|
14
|
+
export function generateCapabilityCatalog(catalog, loadedPackIds) {
|
|
15
|
+
if (catalog.length === 0) {
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
18
|
+
const lines = [
|
|
19
|
+
'## Available Capabilities',
|
|
20
|
+
'',
|
|
21
|
+
'Call `load_capability("name")` to activate additional tools:',
|
|
22
|
+
];
|
|
23
|
+
for (const entry of catalog) {
|
|
24
|
+
const readOnlySuffix = entry.readOnly ? ', read-only' : '';
|
|
25
|
+
lines.push(`- ${entry.id}: ${entry.label} (${String(entry.toolCount)} tools${readOnlySuffix})`);
|
|
26
|
+
}
|
|
27
|
+
if (loadedPackIds.length > 0) {
|
|
28
|
+
lines.push('');
|
|
29
|
+
lines.push(`Currently loaded: ${loadedPackIds.join(', ')}`);
|
|
30
|
+
}
|
|
31
|
+
return lines.join('\n');
|
|
32
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Capability Loading
|
|
3
|
+
*
|
|
4
|
+
* Provides three-tier capability management for agents:
|
|
5
|
+
* - Upfront: always active (HOT tools ∩ profile)
|
|
6
|
+
* - Loadable: on-demand activation (sticky)
|
|
7
|
+
* - Forbidden: outside profile (triggers handoff)
|
|
8
|
+
*/
|
|
9
|
+
export type { CapabilityPack, CapabilityTier, LoadedCapability, CapabilityCatalogEntry, CapabilityLoadResult, } from './types.js';
|
|
10
|
+
export { CAPABILITY_PACKS, FORBIDDEN_PACK_SUGGESTIONS, getPackCount, getAllPackIds, } from './packs.js';
|
|
11
|
+
export { CapabilityManager } from './manager.js';
|
|
12
|
+
export type { CapabilityManagerConfig } from './manager.js';
|
|
13
|
+
export { createLoadCapabilityTool } from './load-tool.js';
|
|
14
|
+
export { generateCapabilityCatalog } from './catalog.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Capability Loading
|
|
3
|
+
*
|
|
4
|
+
* Provides three-tier capability management for agents:
|
|
5
|
+
* - Upfront: always active (HOT tools ∩ profile)
|
|
6
|
+
* - Loadable: on-demand activation (sticky)
|
|
7
|
+
* - Forbidden: outside profile (triggers handoff)
|
|
8
|
+
*/
|
|
9
|
+
// Pack definitions
|
|
10
|
+
export { CAPABILITY_PACKS, FORBIDDEN_PACK_SUGGESTIONS, getPackCount, getAllPackIds, } from './packs.js';
|
|
11
|
+
// Manager
|
|
12
|
+
export { CapabilityManager } from './manager.js';
|
|
13
|
+
// Self-loading tool
|
|
14
|
+
export { createLoadCapabilityTool } from './load-tool.js';
|
|
15
|
+
// Catalog generator
|
|
16
|
+
export { generateCapabilityCatalog } from './catalog.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* load_capability Tool
|
|
3
|
+
*
|
|
4
|
+
* Allows agents to self-load capability packs at runtime.
|
|
5
|
+
* When called, sets a pending load flag that the BeforeLLM hook
|
|
6
|
+
* picks up on the next turn.
|
|
7
|
+
*/
|
|
8
|
+
import type { Tool } from '@compilr-dev/agents';
|
|
9
|
+
import type { CapabilityManager } from './manager.js';
|
|
10
|
+
interface LoadCapabilityInput {
|
|
11
|
+
capability: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a load_capability tool bound to a CapabilityManager.
|
|
15
|
+
*
|
|
16
|
+
* The tool immediately loads the pack into the manager.
|
|
17
|
+
* The BeforeLLM hook will see the updated state on the next turn
|
|
18
|
+
* and rebuild the system prompt + tool filter accordingly.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createLoadCapabilityTool(manager: CapabilityManager): Tool<LoadCapabilityInput>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* load_capability Tool
|
|
3
|
+
*
|
|
4
|
+
* Allows agents to self-load capability packs at runtime.
|
|
5
|
+
* When called, sets a pending load flag that the BeforeLLM hook
|
|
6
|
+
* picks up on the next turn.
|
|
7
|
+
*/
|
|
8
|
+
import { defineTool, createSuccessResult, createErrorResult } from '@compilr-dev/agents';
|
|
9
|
+
/**
|
|
10
|
+
* Create a load_capability tool bound to a CapabilityManager.
|
|
11
|
+
*
|
|
12
|
+
* The tool immediately loads the pack into the manager.
|
|
13
|
+
* The BeforeLLM hook will see the updated state on the next turn
|
|
14
|
+
* and rebuild the system prompt + tool filter accordingly.
|
|
15
|
+
*/
|
|
16
|
+
export function createLoadCapabilityTool(manager) {
|
|
17
|
+
return defineTool({
|
|
18
|
+
name: 'load_capability',
|
|
19
|
+
description: 'Load a capability pack to gain access to additional tools. ' +
|
|
20
|
+
'See the "Available Capabilities" section in your instructions for pack IDs.',
|
|
21
|
+
inputSchema: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
properties: {
|
|
24
|
+
capability: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: 'Pack ID from the capability catalog (e.g., "git_read", "runners", "backlog_write")',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
required: ['capability'],
|
|
30
|
+
},
|
|
31
|
+
silent: true,
|
|
32
|
+
execute: (input) => {
|
|
33
|
+
const { capability } = input;
|
|
34
|
+
// Check if already loaded
|
|
35
|
+
if (manager.isLoaded(capability)) {
|
|
36
|
+
return Promise.resolve(createSuccessResult({
|
|
37
|
+
status: 'already_loaded',
|
|
38
|
+
message: `Capability "${capability}" is already loaded.`,
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
// Attempt to load
|
|
42
|
+
const tier = manager.getTier(capability);
|
|
43
|
+
if (tier === 'forbidden') {
|
|
44
|
+
const suggested = manager.getSuggestedAgent(capability);
|
|
45
|
+
const handoffHint = suggested
|
|
46
|
+
? ` Try: handoff("${suggested}", "Need ${capability} access")`
|
|
47
|
+
: '';
|
|
48
|
+
return Promise.resolve(createErrorResult(`Capability "${capability}" is outside your tool profile.${handoffHint}`));
|
|
49
|
+
}
|
|
50
|
+
const success = manager.load(capability, 'agent-self');
|
|
51
|
+
if (success) {
|
|
52
|
+
return Promise.resolve(createSuccessResult({
|
|
53
|
+
status: 'loaded',
|
|
54
|
+
message: `Capability "${capability}" loaded. ` +
|
|
55
|
+
'The tools will be available on your next response.',
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
return Promise.resolve(createErrorResult(`Unknown capability pack: "${capability}". ` +
|
|
59
|
+
'Check the "Available Capabilities" section in your instructions.'));
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Capability Loading — CapabilityManager
|
|
3
|
+
*
|
|
4
|
+
* Manages the three-tier loading model for an agent:
|
|
5
|
+
* - Upfront: always active from the start
|
|
6
|
+
* - Loadable: within profile, activated on demand (sticky)
|
|
7
|
+
* - Forbidden: outside profile, triggers handoff suggestion
|
|
8
|
+
*/
|
|
9
|
+
import type { CapabilityPack, CapabilityTier, LoadedCapability, CapabilityCatalogEntry, CapabilityLoadResult } from './types.js';
|
|
10
|
+
export interface CapabilityManagerConfig {
|
|
11
|
+
/** All group IDs the agent's profile allows */
|
|
12
|
+
profileGroups: string[];
|
|
13
|
+
/** All pack definitions */
|
|
14
|
+
packs: Record<string, CapabilityPack>;
|
|
15
|
+
/** Group IDs to load upfront (HOT_TOOLS ∩ profile) */
|
|
16
|
+
upfrontGroups: string[];
|
|
17
|
+
}
|
|
18
|
+
export declare class CapabilityManager {
|
|
19
|
+
private readonly profileGroupSet;
|
|
20
|
+
private readonly packs;
|
|
21
|
+
private readonly loaded;
|
|
22
|
+
/** Reverse index: tool name → pack ID */
|
|
23
|
+
private readonly toolToPackIndex;
|
|
24
|
+
constructor(config: CapabilityManagerConfig);
|
|
25
|
+
/**
|
|
26
|
+
* Get the tier of a capability pack relative to this agent.
|
|
27
|
+
*/
|
|
28
|
+
getTier(packId: string): CapabilityTier;
|
|
29
|
+
/**
|
|
30
|
+
* Load a capability pack. Returns false if forbidden.
|
|
31
|
+
* Once loaded, packs stay loaded (sticky mode).
|
|
32
|
+
*/
|
|
33
|
+
load(packId: string, trigger: LoadedCapability['trigger']): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Load multiple packs for a skill invocation.
|
|
36
|
+
* Maps tool names to pack IDs, then loads each.
|
|
37
|
+
*/
|
|
38
|
+
loadForSkill(toolNames: string[], trigger: LoadedCapability['trigger']): CapabilityLoadResult;
|
|
39
|
+
/**
|
|
40
|
+
* Get all currently loaded tool names (deduplicated).
|
|
41
|
+
*/
|
|
42
|
+
getActiveToolNames(): string[];
|
|
43
|
+
/**
|
|
44
|
+
* Get prompt module IDs to include (deduplicated across loaded packs).
|
|
45
|
+
*/
|
|
46
|
+
getActivePromptModuleIds(): Set<string>;
|
|
47
|
+
/**
|
|
48
|
+
* Get prompt snippets from loaded packs.
|
|
49
|
+
*/
|
|
50
|
+
getActivePromptSnippets(): string[];
|
|
51
|
+
/**
|
|
52
|
+
* Get catalog of loadable (not yet loaded) packs for the system prompt.
|
|
53
|
+
*/
|
|
54
|
+
getCatalog(): CapabilityCatalogEntry[];
|
|
55
|
+
/**
|
|
56
|
+
* Check if any loadable packs remain (not yet loaded).
|
|
57
|
+
*/
|
|
58
|
+
hasLoadablePacks(): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Find which pack contains a given tool name.
|
|
61
|
+
*/
|
|
62
|
+
findPackForTool(toolName: string): string | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Get suggested agent for a forbidden pack.
|
|
65
|
+
*/
|
|
66
|
+
getSuggestedAgent(packId: string): string | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Get all loaded pack IDs.
|
|
69
|
+
*/
|
|
70
|
+
getLoadedPackIds(): string[];
|
|
71
|
+
/**
|
|
72
|
+
* Check if a specific pack is loaded.
|
|
73
|
+
*/
|
|
74
|
+
isLoaded(packId: string): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Token estimate for currently loaded state.
|
|
77
|
+
*/
|
|
78
|
+
getTokenEstimate(): {
|
|
79
|
+
promptTokens: number;
|
|
80
|
+
toolTokens: number;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Capability Loading — CapabilityManager
|
|
3
|
+
*
|
|
4
|
+
* Manages the three-tier loading model for an agent:
|
|
5
|
+
* - Upfront: always active from the start
|
|
6
|
+
* - Loadable: within profile, activated on demand (sticky)
|
|
7
|
+
* - Forbidden: outside profile, triggers handoff suggestion
|
|
8
|
+
*/
|
|
9
|
+
import { FORBIDDEN_PACK_SUGGESTIONS } from './packs.js';
|
|
10
|
+
export class CapabilityManager {
|
|
11
|
+
profileGroupSet;
|
|
12
|
+
packs;
|
|
13
|
+
loaded = new Map();
|
|
14
|
+
/** Reverse index: tool name → pack ID */
|
|
15
|
+
toolToPackIndex = new Map();
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.profileGroupSet = new Set(config.profileGroups);
|
|
18
|
+
this.packs = config.packs;
|
|
19
|
+
// Build tool → pack reverse index
|
|
20
|
+
for (const [packId, pack] of Object.entries(this.packs)) {
|
|
21
|
+
for (const tool of pack.tools) {
|
|
22
|
+
this.toolToPackIndex.set(tool, packId);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Load upfront packs
|
|
26
|
+
for (const groupId of config.upfrontGroups) {
|
|
27
|
+
if (groupId in this.packs && this.profileGroupSet.has(groupId)) {
|
|
28
|
+
this.loaded.set(groupId, {
|
|
29
|
+
packId: groupId,
|
|
30
|
+
loadedAt: Date.now(),
|
|
31
|
+
trigger: 'upfront',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get the tier of a capability pack relative to this agent.
|
|
38
|
+
*/
|
|
39
|
+
getTier(packId) {
|
|
40
|
+
if (this.loaded.has(packId)) {
|
|
41
|
+
return 'upfront'; // Already loaded (whether upfront or later)
|
|
42
|
+
}
|
|
43
|
+
if (this.profileGroupSet.has(packId)) {
|
|
44
|
+
return 'loadable';
|
|
45
|
+
}
|
|
46
|
+
return 'forbidden';
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Load a capability pack. Returns false if forbidden.
|
|
50
|
+
* Once loaded, packs stay loaded (sticky mode).
|
|
51
|
+
*/
|
|
52
|
+
load(packId, trigger) {
|
|
53
|
+
// Already loaded → no-op success
|
|
54
|
+
if (this.loaded.has(packId)) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
// Not in profile → forbidden
|
|
58
|
+
if (!this.profileGroupSet.has(packId)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
// Pack must exist
|
|
62
|
+
if (!(packId in this.packs)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
this.loaded.set(packId, {
|
|
66
|
+
packId,
|
|
67
|
+
loadedAt: Date.now(),
|
|
68
|
+
trigger,
|
|
69
|
+
});
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Load multiple packs for a skill invocation.
|
|
74
|
+
* Maps tool names to pack IDs, then loads each.
|
|
75
|
+
*/
|
|
76
|
+
loadForSkill(toolNames, trigger) {
|
|
77
|
+
const loaded = [];
|
|
78
|
+
const alreadyLoaded = [];
|
|
79
|
+
const forbidden = [];
|
|
80
|
+
// Deduplicate pack IDs from tool names
|
|
81
|
+
const packIds = new Set();
|
|
82
|
+
for (const toolName of toolNames) {
|
|
83
|
+
const packId = this.toolToPackIndex.get(toolName);
|
|
84
|
+
if (packId) {
|
|
85
|
+
packIds.add(packId);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
for (const packId of packIds) {
|
|
89
|
+
if (this.loaded.has(packId)) {
|
|
90
|
+
alreadyLoaded.push(packId);
|
|
91
|
+
}
|
|
92
|
+
else if (this.load(packId, trigger)) {
|
|
93
|
+
loaded.push(packId);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
forbidden.push({
|
|
97
|
+
packId,
|
|
98
|
+
suggestedAgent: this.getSuggestedAgent(packId),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
success: forbidden.length === 0,
|
|
104
|
+
loaded,
|
|
105
|
+
alreadyLoaded,
|
|
106
|
+
forbidden,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get all currently loaded tool names (deduplicated).
|
|
111
|
+
*/
|
|
112
|
+
getActiveToolNames() {
|
|
113
|
+
const names = new Set();
|
|
114
|
+
for (const [packId] of this.loaded) {
|
|
115
|
+
const pack = this.packs[packId];
|
|
116
|
+
for (const tool of pack.tools) {
|
|
117
|
+
names.add(tool);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return [...names];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get prompt module IDs to include (deduplicated across loaded packs).
|
|
124
|
+
*/
|
|
125
|
+
getActivePromptModuleIds() {
|
|
126
|
+
const ids = new Set();
|
|
127
|
+
for (const [packId] of this.loaded) {
|
|
128
|
+
const pack = this.packs[packId];
|
|
129
|
+
for (const moduleId of pack.promptModules) {
|
|
130
|
+
ids.add(moduleId);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return ids;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Get prompt snippets from loaded packs.
|
|
137
|
+
*/
|
|
138
|
+
getActivePromptSnippets() {
|
|
139
|
+
const snippets = [];
|
|
140
|
+
for (const [packId] of this.loaded) {
|
|
141
|
+
const pack = this.packs[packId];
|
|
142
|
+
if (pack.promptSnippet) {
|
|
143
|
+
snippets.push(pack.promptSnippet);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return snippets;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get catalog of loadable (not yet loaded) packs for the system prompt.
|
|
150
|
+
*/
|
|
151
|
+
getCatalog() {
|
|
152
|
+
const entries = [];
|
|
153
|
+
for (const [packId, pack] of Object.entries(this.packs)) {
|
|
154
|
+
// Only include packs that are loadable (in profile but not yet loaded)
|
|
155
|
+
if (this.profileGroupSet.has(packId) && !this.loaded.has(packId)) {
|
|
156
|
+
entries.push({
|
|
157
|
+
id: pack.id,
|
|
158
|
+
label: pack.label,
|
|
159
|
+
toolCount: pack.tools.length,
|
|
160
|
+
readOnly: pack.readOnly,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return entries;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Check if any loadable packs remain (not yet loaded).
|
|
168
|
+
*/
|
|
169
|
+
hasLoadablePacks() {
|
|
170
|
+
for (const packId of this.profileGroupSet) {
|
|
171
|
+
if (!this.loaded.has(packId)) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Find which pack contains a given tool name.
|
|
179
|
+
*/
|
|
180
|
+
findPackForTool(toolName) {
|
|
181
|
+
return this.toolToPackIndex.get(toolName);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get suggested agent for a forbidden pack.
|
|
185
|
+
*/
|
|
186
|
+
getSuggestedAgent(packId) {
|
|
187
|
+
return FORBIDDEN_PACK_SUGGESTIONS[packId];
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get all loaded pack IDs.
|
|
191
|
+
*/
|
|
192
|
+
getLoadedPackIds() {
|
|
193
|
+
return [...this.loaded.keys()];
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Check if a specific pack is loaded.
|
|
197
|
+
*/
|
|
198
|
+
isLoaded(packId) {
|
|
199
|
+
return this.loaded.has(packId);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Token estimate for currently loaded state.
|
|
203
|
+
*/
|
|
204
|
+
getTokenEstimate() {
|
|
205
|
+
let promptTokens = 0;
|
|
206
|
+
let toolTokens = 0;
|
|
207
|
+
// Track seen prompt module IDs to avoid double-counting shared modules
|
|
208
|
+
const seenModules = new Set();
|
|
209
|
+
for (const [packId] of this.loaded) {
|
|
210
|
+
const pack = this.packs[packId];
|
|
211
|
+
// Count prompt tokens only for new modules
|
|
212
|
+
for (const moduleId of pack.promptModules) {
|
|
213
|
+
if (!seenModules.has(moduleId)) {
|
|
214
|
+
seenModules.add(moduleId);
|
|
215
|
+
promptTokens += pack.estimatedPromptTokens;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// Always count tool tokens (tools are unique per pack)
|
|
219
|
+
toolTokens += pack.estimatedToolTokens;
|
|
220
|
+
}
|
|
221
|
+
return { promptTokens, toolTokens };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Capability Loading — Pack Definitions
|
|
3
|
+
*
|
|
4
|
+
* Maps each tool group (from tool-config.ts) to a capability pack with
|
|
5
|
+
* associated prompt modules and usage guidance.
|
|
6
|
+
*
|
|
7
|
+
* 24 packs total, matching 1:1 with TOOL_GROUPS keys.
|
|
8
|
+
*/
|
|
9
|
+
import type { CapabilityPack } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* All capability pack definitions.
|
|
12
|
+
*
|
|
13
|
+
* Tool names are string literals (not imported from TOOL_NAMES) to keep the SDK
|
|
14
|
+
* independent of CLI-specific tool-names.ts. The CLI validates consistency at startup.
|
|
15
|
+
*/
|
|
16
|
+
export declare const CAPABILITY_PACKS: Record<string, CapabilityPack>;
|
|
17
|
+
/**
|
|
18
|
+
* Mapping from forbidden pack IDs to suggested agent roles.
|
|
19
|
+
* Used for handoff suggestions when an agent tries to load a forbidden capability.
|
|
20
|
+
*/
|
|
21
|
+
export declare const FORBIDDEN_PACK_SUGGESTIONS: Record<string, string>;
|
|
22
|
+
/**
|
|
23
|
+
* Get the total number of defined capability packs.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getPackCount(): number;
|
|
26
|
+
/**
|
|
27
|
+
* Get all pack IDs.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getAllPackIds(): string[];
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Capability Loading — Pack Definitions
|
|
3
|
+
*
|
|
4
|
+
* Maps each tool group (from tool-config.ts) to a capability pack with
|
|
5
|
+
* associated prompt modules and usage guidance.
|
|
6
|
+
*
|
|
7
|
+
* 24 packs total, matching 1:1 with TOOL_GROUPS keys.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* All capability pack definitions.
|
|
11
|
+
*
|
|
12
|
+
* Tool names are string literals (not imported from TOOL_NAMES) to keep the SDK
|
|
13
|
+
* independent of CLI-specific tool-names.ts. The CLI validates consistency at startup.
|
|
14
|
+
*/
|
|
15
|
+
export const CAPABILITY_PACKS = {
|
|
16
|
+
// ============= DIRECT TOOL PACKS =============
|
|
17
|
+
file_read: {
|
|
18
|
+
id: 'file_read',
|
|
19
|
+
label: 'File Reading',
|
|
20
|
+
tools: ['read_file', 'glob', 'grep'],
|
|
21
|
+
readOnly: true,
|
|
22
|
+
promptModules: [],
|
|
23
|
+
estimatedPromptTokens: 0,
|
|
24
|
+
estimatedToolTokens: 900,
|
|
25
|
+
},
|
|
26
|
+
file_write: {
|
|
27
|
+
id: 'file_write',
|
|
28
|
+
label: 'File Writing',
|
|
29
|
+
tools: ['write_file', 'edit'],
|
|
30
|
+
readOnly: false,
|
|
31
|
+
promptModules: [],
|
|
32
|
+
estimatedPromptTokens: 0,
|
|
33
|
+
estimatedToolTokens: 600,
|
|
34
|
+
},
|
|
35
|
+
shell: {
|
|
36
|
+
id: 'shell',
|
|
37
|
+
label: 'Shell Commands',
|
|
38
|
+
tools: ['bash', 'bash_output', 'kill_shell'],
|
|
39
|
+
readOnly: false,
|
|
40
|
+
promptModules: [],
|
|
41
|
+
estimatedPromptTokens: 0,
|
|
42
|
+
estimatedToolTokens: 900,
|
|
43
|
+
},
|
|
44
|
+
tasks: {
|
|
45
|
+
id: 'tasks',
|
|
46
|
+
label: 'Task Tracking',
|
|
47
|
+
tools: ['todo_write', 'todo_read', 'todo_claim', 'todo_handoff'],
|
|
48
|
+
readOnly: false,
|
|
49
|
+
promptModules: [],
|
|
50
|
+
estimatedPromptTokens: 0,
|
|
51
|
+
estimatedToolTokens: 1200,
|
|
52
|
+
},
|
|
53
|
+
interaction: {
|
|
54
|
+
id: 'interaction',
|
|
55
|
+
label: 'User Interaction',
|
|
56
|
+
tools: ['ask_user', 'ask_user_simple', 'suggest'],
|
|
57
|
+
readOnly: true,
|
|
58
|
+
promptModules: [],
|
|
59
|
+
estimatedPromptTokens: 0,
|
|
60
|
+
estimatedToolTokens: 900,
|
|
61
|
+
},
|
|
62
|
+
coordinator: {
|
|
63
|
+
id: 'coordinator',
|
|
64
|
+
label: 'Coordination',
|
|
65
|
+
tools: ['delegate', 'delegate_background', 'delegation_status'],
|
|
66
|
+
readOnly: false,
|
|
67
|
+
promptModules: [],
|
|
68
|
+
estimatedPromptTokens: 0,
|
|
69
|
+
estimatedToolTokens: 900,
|
|
70
|
+
},
|
|
71
|
+
handoff: {
|
|
72
|
+
id: 'handoff',
|
|
73
|
+
label: 'Agent Handoff',
|
|
74
|
+
tools: ['handoff'],
|
|
75
|
+
readOnly: false,
|
|
76
|
+
promptModules: [],
|
|
77
|
+
estimatedPromptTokens: 0,
|
|
78
|
+
estimatedToolTokens: 300,
|
|
79
|
+
},
|
|
80
|
+
guide: {
|
|
81
|
+
id: 'guide',
|
|
82
|
+
label: 'CLI Documentation',
|
|
83
|
+
tools: ['compilr_guide'],
|
|
84
|
+
readOnly: true,
|
|
85
|
+
promptModules: [],
|
|
86
|
+
estimatedPromptTokens: 0,
|
|
87
|
+
estimatedToolTokens: 300,
|
|
88
|
+
},
|
|
89
|
+
meta: {
|
|
90
|
+
id: 'meta',
|
|
91
|
+
label: 'Tool Discovery',
|
|
92
|
+
tools: ['list_tools', 'get_tool_info', 'use_tool'],
|
|
93
|
+
readOnly: true,
|
|
94
|
+
promptModules: [],
|
|
95
|
+
estimatedPromptTokens: 0,
|
|
96
|
+
estimatedToolTokens: 900,
|
|
97
|
+
},
|
|
98
|
+
subagents: {
|
|
99
|
+
id: 'subagents',
|
|
100
|
+
label: 'Subagent Delegation',
|
|
101
|
+
tools: ['task'],
|
|
102
|
+
readOnly: false,
|
|
103
|
+
promptModules: [],
|
|
104
|
+
estimatedPromptTokens: 0,
|
|
105
|
+
estimatedToolTokens: 300,
|
|
106
|
+
},
|
|
107
|
+
// ============= META-REGISTRY TOOL PACKS =============
|
|
108
|
+
git_read: {
|
|
109
|
+
id: 'git_read',
|
|
110
|
+
label: 'Git (Read)',
|
|
111
|
+
tools: ['git_status', 'git_diff', 'git_log', 'git_blame', 'git_file_history'],
|
|
112
|
+
readOnly: true,
|
|
113
|
+
promptModules: ['git-safety'],
|
|
114
|
+
promptSnippet: 'Git read tools: git_status, git_diff, git_log, git_blame, git_file_history',
|
|
115
|
+
estimatedPromptTokens: 300,
|
|
116
|
+
estimatedToolTokens: 1500,
|
|
117
|
+
},
|
|
118
|
+
git_write: {
|
|
119
|
+
id: 'git_write',
|
|
120
|
+
label: 'Git (Write)',
|
|
121
|
+
tools: ['git_commit', 'git_branch', 'git_stash'],
|
|
122
|
+
readOnly: false,
|
|
123
|
+
promptModules: ['git-safety'],
|
|
124
|
+
promptSnippet: 'Git write tools: git_commit, git_branch, git_stash. Always check git_status before committing.',
|
|
125
|
+
estimatedPromptTokens: 300,
|
|
126
|
+
estimatedToolTokens: 900,
|
|
127
|
+
},
|
|
128
|
+
project: {
|
|
129
|
+
id: 'project',
|
|
130
|
+
label: 'Project Analysis',
|
|
131
|
+
tools: [
|
|
132
|
+
'detect_project',
|
|
133
|
+
'find_project_root',
|
|
134
|
+
'get_file_structure',
|
|
135
|
+
'get_complexity',
|
|
136
|
+
'read_function',
|
|
137
|
+
'read_class',
|
|
138
|
+
'read_type',
|
|
139
|
+
],
|
|
140
|
+
readOnly: true,
|
|
141
|
+
promptModules: [],
|
|
142
|
+
promptSnippet: 'Project analysis: detect_project, find_project_root, get_file_structure, get_complexity, read_function, read_class, read_type',
|
|
143
|
+
estimatedPromptTokens: 30,
|
|
144
|
+
estimatedToolTokens: 2100,
|
|
145
|
+
},
|
|
146
|
+
runners: {
|
|
147
|
+
id: 'runners',
|
|
148
|
+
label: 'Runners (Test/Build/Lint)',
|
|
149
|
+
tools: ['run_tests', 'run_lint', 'run_build', 'run_format'],
|
|
150
|
+
readOnly: false,
|
|
151
|
+
promptModules: [],
|
|
152
|
+
promptSnippet: 'Runner tools: run_tests, run_lint, run_build, run_format',
|
|
153
|
+
estimatedPromptTokens: 15,
|
|
154
|
+
estimatedToolTokens: 1200,
|
|
155
|
+
},
|
|
156
|
+
search: {
|
|
157
|
+
id: 'search',
|
|
158
|
+
label: 'Code Search',
|
|
159
|
+
tools: ['find_definition', 'find_references', 'find_todos'],
|
|
160
|
+
readOnly: true,
|
|
161
|
+
promptModules: [],
|
|
162
|
+
promptSnippet: 'Code search: find_definition, find_references, find_todos',
|
|
163
|
+
estimatedPromptTokens: 15,
|
|
164
|
+
estimatedToolTokens: 900,
|
|
165
|
+
},
|
|
166
|
+
dependencies: {
|
|
167
|
+
id: 'dependencies',
|
|
168
|
+
label: 'Dependency Analysis',
|
|
169
|
+
tools: ['check_outdated', 'find_vulnerabilities', 'analyze_test_coverage'],
|
|
170
|
+
readOnly: true,
|
|
171
|
+
promptModules: [],
|
|
172
|
+
promptSnippet: 'Dependencies: check_outdated, find_vulnerabilities, analyze_test_coverage',
|
|
173
|
+
estimatedPromptTokens: 15,
|
|
174
|
+
estimatedToolTokens: 900,
|
|
175
|
+
},
|
|
176
|
+
backlog_read: {
|
|
177
|
+
id: 'backlog_read',
|
|
178
|
+
label: 'Backlog (Read)',
|
|
179
|
+
tools: ['workitem_query', 'workitem_status_counts'],
|
|
180
|
+
readOnly: true,
|
|
181
|
+
promptModules: ['platform-tool-hints'],
|
|
182
|
+
promptSnippet: 'Backlog read: workitem_query, workitem_status_counts',
|
|
183
|
+
estimatedPromptTokens: 120,
|
|
184
|
+
estimatedToolTokens: 600,
|
|
185
|
+
},
|
|
186
|
+
backlog_write: {
|
|
187
|
+
id: 'backlog_write',
|
|
188
|
+
label: 'Backlog (Write)',
|
|
189
|
+
tools: [
|
|
190
|
+
'workitem_add',
|
|
191
|
+
'workitem_update',
|
|
192
|
+
'workitem_delete',
|
|
193
|
+
'workitem_next',
|
|
194
|
+
'workitem_advance_step',
|
|
195
|
+
'workitem_claim',
|
|
196
|
+
'workitem_handoff',
|
|
197
|
+
],
|
|
198
|
+
readOnly: false,
|
|
199
|
+
promptModules: ['platform-tool-hints'],
|
|
200
|
+
promptSnippet: 'Backlog write: workitem_add, workitem_update, workitem_delete, workitem_next, workitem_advance_step, workitem_claim, workitem_handoff. Use workitem_query before modifications.',
|
|
201
|
+
estimatedPromptTokens: 120,
|
|
202
|
+
estimatedToolTokens: 2100,
|
|
203
|
+
},
|
|
204
|
+
documents: {
|
|
205
|
+
id: 'documents',
|
|
206
|
+
label: 'Project Documents',
|
|
207
|
+
tools: [
|
|
208
|
+
'project_document_add',
|
|
209
|
+
'project_document_get',
|
|
210
|
+
'project_document_list',
|
|
211
|
+
'project_document_delete',
|
|
212
|
+
],
|
|
213
|
+
readOnly: false,
|
|
214
|
+
promptModules: ['platform-tool-hints'],
|
|
215
|
+
promptSnippet: 'Documents stored in database, not filesystem. Use project_document_add/get/list/delete. Valid doc_types: prd, architecture, design, notes, session-note',
|
|
216
|
+
estimatedPromptTokens: 120,
|
|
217
|
+
estimatedToolTokens: 1200,
|
|
218
|
+
},
|
|
219
|
+
plans: {
|
|
220
|
+
id: 'plans',
|
|
221
|
+
label: 'Planning',
|
|
222
|
+
tools: ['plan_create', 'plan_update', 'plan_get', 'plan_list', 'plan_delete'],
|
|
223
|
+
readOnly: false,
|
|
224
|
+
promptModules: ['platform-tool-hints'],
|
|
225
|
+
promptSnippet: 'Plan tools: plan_create, plan_get, plan_list, plan_update, plan_delete. Status flow: draft → approved → in_progress → completed',
|
|
226
|
+
estimatedPromptTokens: 120,
|
|
227
|
+
estimatedToolTokens: 1500,
|
|
228
|
+
},
|
|
229
|
+
artifacts: {
|
|
230
|
+
id: 'artifacts',
|
|
231
|
+
label: 'Team Artifacts',
|
|
232
|
+
tools: ['artifact_save', 'artifact_get', 'artifact_list', 'artifact_delete'],
|
|
233
|
+
readOnly: false,
|
|
234
|
+
promptModules: ['platform-tool-hints'],
|
|
235
|
+
promptSnippet: 'Artifacts: artifact_save, artifact_get, artifact_list, artifact_delete. Types: design, plan, review, decision, note. Names must be unique within team.',
|
|
236
|
+
estimatedPromptTokens: 120,
|
|
237
|
+
estimatedToolTokens: 1200,
|
|
238
|
+
},
|
|
239
|
+
anchors: {
|
|
240
|
+
id: 'anchors',
|
|
241
|
+
label: 'Context Anchors',
|
|
242
|
+
tools: ['anchor_add', 'anchor_list', 'anchor_remove'],
|
|
243
|
+
readOnly: false,
|
|
244
|
+
promptModules: ['platform-tool-hints'],
|
|
245
|
+
promptSnippet: 'Anchors survive context compaction. Use "critical" priority sparingly. Most anchors should be "info" priority with "project" scope.',
|
|
246
|
+
estimatedPromptTokens: 120,
|
|
247
|
+
estimatedToolTokens: 900,
|
|
248
|
+
},
|
|
249
|
+
project_db: {
|
|
250
|
+
id: 'project_db',
|
|
251
|
+
label: 'Project Database',
|
|
252
|
+
tools: ['project_get', 'project_list', 'project_update'],
|
|
253
|
+
readOnly: false,
|
|
254
|
+
promptModules: ['platform-tool-hints'],
|
|
255
|
+
promptSnippet: 'Project database: project_get, project_list, project_update. Default to active project unless targeting a different one.',
|
|
256
|
+
estimatedPromptTokens: 120,
|
|
257
|
+
estimatedToolTokens: 900,
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* Mapping from forbidden pack IDs to suggested agent roles.
|
|
262
|
+
* Used for handoff suggestions when an agent tries to load a forbidden capability.
|
|
263
|
+
*/
|
|
264
|
+
export const FORBIDDEN_PACK_SUGGESTIONS = {
|
|
265
|
+
file_write: '$dev',
|
|
266
|
+
shell: '$dev',
|
|
267
|
+
git_write: '$dev',
|
|
268
|
+
runners: '$dev',
|
|
269
|
+
backlog_write: '$pm',
|
|
270
|
+
backlog_read: '$pm',
|
|
271
|
+
documents: '$pm',
|
|
272
|
+
plans: '$pm',
|
|
273
|
+
search: '$arch',
|
|
274
|
+
dependencies: '$arch',
|
|
275
|
+
coordinator: '$default',
|
|
276
|
+
artifacts: '$pm',
|
|
277
|
+
anchors: '$pm',
|
|
278
|
+
project_db: '$pm',
|
|
279
|
+
};
|
|
280
|
+
/**
|
|
281
|
+
* Get the total number of defined capability packs.
|
|
282
|
+
*/
|
|
283
|
+
export function getPackCount() {
|
|
284
|
+
return Object.keys(CAPABILITY_PACKS).length;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Get all pack IDs.
|
|
288
|
+
*/
|
|
289
|
+
export function getAllPackIds() {
|
|
290
|
+
return Object.keys(CAPABILITY_PACKS);
|
|
291
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Capability Loading — Types
|
|
3
|
+
*
|
|
4
|
+
* Capability packs bundle tools, prompt modules, and usage guidance.
|
|
5
|
+
* Each pack maps 1:1 to an existing tool group from tool-config.ts.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* A capability pack bundles tools with their associated prompt modules and guidance.
|
|
9
|
+
*/
|
|
10
|
+
export interface CapabilityPack {
|
|
11
|
+
/** Matches a TOOL_GROUPS key (e.g., 'git_read', 'backlog_write') */
|
|
12
|
+
id: string;
|
|
13
|
+
/** Human-readable label (e.g., 'Git (Read)', 'Backlog Management') */
|
|
14
|
+
label: string;
|
|
15
|
+
/** Tool names in this pack */
|
|
16
|
+
tools: readonly string[];
|
|
17
|
+
/** Whether all tools in this pack are read-only */
|
|
18
|
+
readOnly: boolean;
|
|
19
|
+
/** System prompt module IDs to inject when loaded (from modules.ts) */
|
|
20
|
+
promptModules: readonly string[];
|
|
21
|
+
/** Optional inline prompt snippet for pack-specific guidance */
|
|
22
|
+
promptSnippet?: string;
|
|
23
|
+
/** Estimated token cost: prompt modules + snippet */
|
|
24
|
+
estimatedPromptTokens: number;
|
|
25
|
+
/** Estimated token cost: tool schemas in meta-registry index */
|
|
26
|
+
estimatedToolTokens: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Three-tier loading model for each capability pack relative to an agent.
|
|
30
|
+
*
|
|
31
|
+
* - upfront: Always loaded from the start (HOT_TOOLS ∩ profile)
|
|
32
|
+
* - loadable: Within profile but not yet active; loaded on demand
|
|
33
|
+
* - forbidden: Outside the agent's tool profile; triggers handoff suggestion
|
|
34
|
+
*/
|
|
35
|
+
export type CapabilityTier = 'upfront' | 'loadable' | 'forbidden';
|
|
36
|
+
/**
|
|
37
|
+
* Tracks when and how a capability was loaded.
|
|
38
|
+
*/
|
|
39
|
+
export interface LoadedCapability {
|
|
40
|
+
packId: string;
|
|
41
|
+
loadedAt: number;
|
|
42
|
+
trigger: 'upfront' | 'slash-command' | 'agent-self' | 'auto-detect';
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Catalog entry shown in the agent's system prompt for self-loading.
|
|
46
|
+
*/
|
|
47
|
+
export interface CapabilityCatalogEntry {
|
|
48
|
+
id: string;
|
|
49
|
+
label: string;
|
|
50
|
+
toolCount: number;
|
|
51
|
+
readOnly: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Result of attempting to load capabilities (e.g., for a skill invocation).
|
|
55
|
+
*/
|
|
56
|
+
export interface CapabilityLoadResult {
|
|
57
|
+
success: boolean;
|
|
58
|
+
/** Packs that were loaded */
|
|
59
|
+
loaded: string[];
|
|
60
|
+
/** Packs that were already loaded */
|
|
61
|
+
alreadyLoaded: string[];
|
|
62
|
+
/** Packs that are forbidden (with suggested agent) */
|
|
63
|
+
forbidden: Array<{
|
|
64
|
+
packId: string;
|
|
65
|
+
suggestedAgent?: string;
|
|
66
|
+
}>;
|
|
67
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export { type ModelTier, type TierInfo, type ProviderModelMap, MODEL_TIERS, TIER
|
|
|
45
45
|
export { assembleTools, deduplicateTools } from './tools.js';
|
|
46
46
|
export { MetaToolsRegistry, createMetaTools, META_TOOLS_SYSTEM_PROMPT_PREFIX, } from './meta-tools/index.js';
|
|
47
47
|
export type { MetaToolStats, MetaTools } from './meta-tools/index.js';
|
|
48
|
+
export { CapabilityManager, CAPABILITY_PACKS, FORBIDDEN_PACK_SUGGESTIONS, getPackCount, getAllPackIds, createLoadCapabilityTool, generateCapabilityCatalog, } from './capabilities/index.js';
|
|
49
|
+
export type { CapabilityPack, CapabilityTier, LoadedCapability, CapabilityCatalogEntry, CapabilityLoadResult, CapabilityManagerConfig, } from './capabilities/index.js';
|
|
48
50
|
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, ENVIRONMENT_MODULE, shouldIncludeModule, getEstimatedTokensForConditions, getTotalEstimatedTokens, } from './system-prompt/index.js';
|
|
49
51
|
export type { SystemPromptContext, BuildResult, SystemPromptModule, ModuleConditions, } from './system-prompt/index.js';
|
|
50
52
|
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, IProjectRepository, IWorkItemRepository, IDocumentRepository, IPlanRepository, IAnchorService, IArtifactService, IEpisodeService, AnchorData, ArtifactType, ArtifactData, ArtifactSummaryData, WorkEpisode, ProjectWorkSummary, PlatformContext, PlatformToolsConfig, PlatformHooks, StepCriteria, } from './platform/index.js';
|
package/dist/index.js
CHANGED
|
@@ -65,6 +65,18 @@ export { assembleTools, deduplicateTools } from './tools.js';
|
|
|
65
65
|
// =============================================================================
|
|
66
66
|
export { MetaToolsRegistry, createMetaTools, META_TOOLS_SYSTEM_PROMPT_PREFIX, } from './meta-tools/index.js';
|
|
67
67
|
// =============================================================================
|
|
68
|
+
// Dynamic Capability Loading
|
|
69
|
+
// =============================================================================
|
|
70
|
+
export {
|
|
71
|
+
// Manager
|
|
72
|
+
CapabilityManager,
|
|
73
|
+
// Pack definitions
|
|
74
|
+
CAPABILITY_PACKS, FORBIDDEN_PACK_SUGGESTIONS, getPackCount, getAllPackIds,
|
|
75
|
+
// Self-loading tool
|
|
76
|
+
createLoadCapabilityTool,
|
|
77
|
+
// Catalog generator
|
|
78
|
+
generateCapabilityCatalog, } from './capabilities/index.js';
|
|
79
|
+
// =============================================================================
|
|
68
80
|
// System Prompt Builder
|
|
69
81
|
// =============================================================================
|
|
70
82
|
export {
|
|
@@ -260,15 +260,17 @@ export function createMetaTools(registry) {
|
|
|
260
260
|
`Available tools include: ${availableTools.join(', ')}... ` +
|
|
261
261
|
`Check the Tool Index for the full list.`);
|
|
262
262
|
}
|
|
263
|
-
// 2.
|
|
264
|
-
const
|
|
263
|
+
// 2. Coerce + validate args
|
|
264
|
+
const toolSchema = tool.definition.inputSchema;
|
|
265
|
+
const coercedArgs = coerceArgs(toolSchema, args);
|
|
266
|
+
const validationError = registry.validateArgs(tool_name, coercedArgs);
|
|
265
267
|
if (validationError) {
|
|
266
268
|
return createErrorResult(`Invalid arguments for '${tool_name}': ${validationError}.\n` +
|
|
267
269
|
`Expected:\n${buildCompactSchema(tool)}`);
|
|
268
270
|
}
|
|
269
271
|
// 3. Execute the tool
|
|
270
272
|
try {
|
|
271
|
-
const result = await tool.execute(
|
|
273
|
+
const result = await tool.execute(coercedArgs);
|
|
272
274
|
// Enhance result with clear top-level message for smaller models
|
|
273
275
|
if (result.success && typeof result.result === 'object' && result.result !== null) {
|
|
274
276
|
const innerResult = result.result;
|
|
@@ -307,15 +309,18 @@ export function createMetaTools(registry) {
|
|
|
307
309
|
return createErrorResult(`Tool '${name}' is not available for this agent. ` +
|
|
308
310
|
`Available tools: ${allowedTools.slice(0, 10).join(', ')}${allowedTools.length > 10 ? '...' : ''}`);
|
|
309
311
|
}
|
|
312
|
+
// Coerce arguments before validation (handles LLM mistakes without guided decoding)
|
|
313
|
+
const toolSchema = tool.definition.inputSchema;
|
|
314
|
+
const coercedInput = coerceArgs(toolSchema, input);
|
|
310
315
|
// Validate args
|
|
311
|
-
const validationError = registry.validateArgs(name,
|
|
316
|
+
const validationError = registry.validateArgs(name, coercedInput);
|
|
312
317
|
if (validationError) {
|
|
313
318
|
return createErrorResult(`Invalid arguments for '${name}': ${validationError}. ` +
|
|
314
319
|
`Call get_tool_info("${name}") to get the exact parameter schema before retrying.`);
|
|
315
320
|
}
|
|
316
321
|
// Execute the tool
|
|
317
322
|
try {
|
|
318
|
-
return await tool.execute(
|
|
323
|
+
return await tool.execute(coercedInput);
|
|
319
324
|
}
|
|
320
325
|
catch (err) {
|
|
321
326
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -437,6 +442,26 @@ function buildCompactSchema(tool) {
|
|
|
437
442
|
detail += ' (required)';
|
|
438
443
|
}
|
|
439
444
|
paramDetails.push(detail);
|
|
445
|
+
// For array-of-objects, show item properties as indented sub-details
|
|
446
|
+
if (prop.type === 'array') {
|
|
447
|
+
const items = prop.items;
|
|
448
|
+
if (items?.type === 'object' && items.properties) {
|
|
449
|
+
const itemProps = items.properties;
|
|
450
|
+
const itemRequired = new Set(items.required ?? []);
|
|
451
|
+
for (const [itemName, itemProp] of Object.entries(itemProps)) {
|
|
452
|
+
const itemDesc = itemProp.description;
|
|
453
|
+
const itemEnum = itemProp.enum;
|
|
454
|
+
let itemDetail = ` - ${itemName}: ${resolveCompactType(itemProp)}`;
|
|
455
|
+
if (itemDesc)
|
|
456
|
+
itemDetail += ` — ${itemDesc}`;
|
|
457
|
+
if (itemEnum)
|
|
458
|
+
itemDetail += ` (${itemEnum.map((v) => `"${v}"`).join('|')})`;
|
|
459
|
+
if (itemRequired.has(itemName))
|
|
460
|
+
itemDetail += ' (required)';
|
|
461
|
+
paramDetails.push(itemDetail);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
440
465
|
}
|
|
441
466
|
}
|
|
442
467
|
const paramsStr = params.join(', ');
|
|
@@ -447,6 +472,45 @@ function buildCompactSchema(tool) {
|
|
|
447
472
|
}
|
|
448
473
|
return lines.join('\n');
|
|
449
474
|
}
|
|
475
|
+
/**
|
|
476
|
+
* Attempt to coerce arguments to match the tool schema.
|
|
477
|
+
* Handles common LLM mistakes when calling tools without guided decoding:
|
|
478
|
+
* - Single object where array expected → wraps in array
|
|
479
|
+
* - JSON string where array expected → parses it
|
|
480
|
+
*/
|
|
481
|
+
function coerceArgs(schema, args) {
|
|
482
|
+
const properties = schema.properties;
|
|
483
|
+
if (!properties)
|
|
484
|
+
return args;
|
|
485
|
+
let changed = false;
|
|
486
|
+
const coerced = { ...args };
|
|
487
|
+
for (const [name, prop] of Object.entries(properties)) {
|
|
488
|
+
if (prop.type === 'array' && name in coerced && !Array.isArray(coerced[name])) {
|
|
489
|
+
const value = coerced[name];
|
|
490
|
+
if (value !== null && value !== undefined) {
|
|
491
|
+
if (typeof value === 'object') {
|
|
492
|
+
// Single object → wrap in array
|
|
493
|
+
coerced[name] = [value];
|
|
494
|
+
changed = true;
|
|
495
|
+
}
|
|
496
|
+
else if (typeof value === 'string') {
|
|
497
|
+
// JSON string → try parsing
|
|
498
|
+
try {
|
|
499
|
+
const parsed = JSON.parse(value);
|
|
500
|
+
if (Array.isArray(parsed)) {
|
|
501
|
+
coerced[name] = parsed;
|
|
502
|
+
changed = true;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
catch {
|
|
506
|
+
// Leave as-is, will fail validation
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return changed ? coerced : args;
|
|
513
|
+
}
|
|
450
514
|
/**
|
|
451
515
|
* Format AJV validation errors into a readable string.
|
|
452
516
|
*/
|