@defai.digital/ability-domain 13.0.3
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/LICENSE +214 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +38 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +171 -0
- package/dist/loader.js.map +1 -0
- package/dist/manager.d.ts +44 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +132 -0
- package/dist/manager.js.map +1 -0
- package/dist/registry.d.ts +24 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +64 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +111 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -0
- package/package.json +48 -0
- package/src/index.ts +50 -0
- package/src/loader.ts +209 -0
- package/src/manager.ts +184 -0
- package/src/registry.ts +84 -0
- package/src/types.ts +149 -0
package/src/manager.ts
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ability Manager
|
|
3
|
+
*
|
|
4
|
+
* Coordinates ability loading and injection into agent contexts.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Ability, AbilityInjectionResult } from '@defai.digital/contracts';
|
|
8
|
+
import type {
|
|
9
|
+
AbilityRegistry,
|
|
10
|
+
AbilityManager,
|
|
11
|
+
AbilityInjectionOptions,
|
|
12
|
+
AbilityDomainConfig,
|
|
13
|
+
} from './types.js';
|
|
14
|
+
import { DEFAULT_ABILITY_DOMAIN_CONFIG } from './types.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Default ability manager implementation
|
|
18
|
+
*/
|
|
19
|
+
export class DefaultAbilityManager implements AbilityManager {
|
|
20
|
+
constructor(
|
|
21
|
+
private readonly registry: AbilityRegistry,
|
|
22
|
+
private readonly config: AbilityDomainConfig = DEFAULT_ABILITY_DOMAIN_CONFIG
|
|
23
|
+
) {}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get abilities for an agent based on task keywords
|
|
27
|
+
*/
|
|
28
|
+
async getAbilitiesForTask(
|
|
29
|
+
agentId: string,
|
|
30
|
+
task: string,
|
|
31
|
+
coreAbilities?: string[],
|
|
32
|
+
maxAbilities?: number
|
|
33
|
+
): Promise<Ability[]> {
|
|
34
|
+
const max = maxAbilities ?? this.config.maxAbilitiesPerAgent;
|
|
35
|
+
const taskLower = task.toLowerCase();
|
|
36
|
+
const taskWords = this.extractKeywords(taskLower);
|
|
37
|
+
|
|
38
|
+
// Get all applicable abilities
|
|
39
|
+
const applicable = await this.getApplicableAbilities(agentId);
|
|
40
|
+
|
|
41
|
+
// Score abilities by relevance to task
|
|
42
|
+
const scored = applicable.map((ability) => ({
|
|
43
|
+
ability,
|
|
44
|
+
score: this.scoreAbility(ability, taskWords, coreAbilities ?? []),
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
// Sort by score descending
|
|
48
|
+
scored.sort((a, b) => b.score - a.score);
|
|
49
|
+
|
|
50
|
+
// Return top N abilities
|
|
51
|
+
return scored.slice(0, max).map((s) => s.ability);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Inject abilities into agent context
|
|
56
|
+
*/
|
|
57
|
+
async injectAbilities(
|
|
58
|
+
agentId: string,
|
|
59
|
+
task: string,
|
|
60
|
+
coreAbilities?: string[],
|
|
61
|
+
options?: AbilityInjectionOptions
|
|
62
|
+
): Promise<AbilityInjectionResult> {
|
|
63
|
+
const maxAbilities = options?.maxAbilities ?? this.config.maxAbilitiesPerAgent;
|
|
64
|
+
const maxTokens = options?.maxTokens ?? this.config.maxTokensPerInjection;
|
|
65
|
+
|
|
66
|
+
// Get relevant abilities
|
|
67
|
+
const abilities = await this.getAbilitiesForTask(
|
|
68
|
+
agentId,
|
|
69
|
+
task,
|
|
70
|
+
coreAbilities,
|
|
71
|
+
maxAbilities
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Build combined content
|
|
75
|
+
const injectedAbilities: string[] = [];
|
|
76
|
+
const contentParts: string[] = [];
|
|
77
|
+
let currentTokens = 0;
|
|
78
|
+
let truncated = false;
|
|
79
|
+
|
|
80
|
+
for (const ability of abilities) {
|
|
81
|
+
const abilityTokens = this.estimateTokens(ability.content);
|
|
82
|
+
|
|
83
|
+
if (currentTokens + abilityTokens > maxTokens) {
|
|
84
|
+
truncated = true;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const header = options?.includeMetadata
|
|
89
|
+
? `## ${ability.displayName ?? ability.abilityId}\n\n`
|
|
90
|
+
: '';
|
|
91
|
+
|
|
92
|
+
contentParts.push(header + ability.content);
|
|
93
|
+
injectedAbilities.push(ability.abilityId);
|
|
94
|
+
currentTokens += abilityTokens;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
agentId,
|
|
99
|
+
injectedAbilities,
|
|
100
|
+
combinedContent: contentParts.join('\n\n---\n\n'),
|
|
101
|
+
tokenCount: currentTokens,
|
|
102
|
+
truncated,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get all abilities applicable to an agent
|
|
108
|
+
*/
|
|
109
|
+
async getApplicableAbilities(agentId: string): Promise<Ability[]> {
|
|
110
|
+
return this.registry.list({
|
|
111
|
+
enabled: true,
|
|
112
|
+
applicableTo: agentId,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Score an ability's relevance to a task
|
|
118
|
+
*/
|
|
119
|
+
private scoreAbility(
|
|
120
|
+
ability: Ability,
|
|
121
|
+
taskWords: string[],
|
|
122
|
+
coreAbilities: string[]
|
|
123
|
+
): number {
|
|
124
|
+
let score = 0;
|
|
125
|
+
|
|
126
|
+
// Core ability bonus
|
|
127
|
+
if (coreAbilities.includes(ability.abilityId)) {
|
|
128
|
+
score += 100;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Tag matches
|
|
132
|
+
for (const tag of ability.tags ?? []) {
|
|
133
|
+
if (taskWords.includes(tag.toLowerCase())) {
|
|
134
|
+
score += 10;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Category match
|
|
139
|
+
if (ability.category && taskWords.includes(ability.category.toLowerCase())) {
|
|
140
|
+
score += 15;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Content keyword matches (limited)
|
|
144
|
+
const contentWords = this.extractKeywords(ability.content.toLowerCase());
|
|
145
|
+
const contentMatches = taskWords.filter((w) => contentWords.includes(w)).length;
|
|
146
|
+
score += Math.min(contentMatches * 2, 20);
|
|
147
|
+
|
|
148
|
+
// Priority bonus
|
|
149
|
+
score += (ability.priority ?? 50) / 10;
|
|
150
|
+
|
|
151
|
+
return score;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Extract keywords from text
|
|
156
|
+
*/
|
|
157
|
+
private extractKeywords(text: string): string[] {
|
|
158
|
+
return text
|
|
159
|
+
.split(/[\s\-_,./]+/)
|
|
160
|
+
.filter((w) => w.length > 2)
|
|
161
|
+
.map((w) => w.toLowerCase());
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Estimate token count (rough approximation)
|
|
166
|
+
*/
|
|
167
|
+
private estimateTokens(text: string): number {
|
|
168
|
+
// Rough estimate: ~4 characters per token
|
|
169
|
+
return Math.ceil(text.length / 4);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Creates an ability manager
|
|
175
|
+
*/
|
|
176
|
+
export function createAbilityManager(
|
|
177
|
+
registry: AbilityRegistry,
|
|
178
|
+
config?: Partial<AbilityDomainConfig>
|
|
179
|
+
): AbilityManager {
|
|
180
|
+
return new DefaultAbilityManager(registry, {
|
|
181
|
+
...DEFAULT_ABILITY_DOMAIN_CONFIG,
|
|
182
|
+
...config,
|
|
183
|
+
});
|
|
184
|
+
}
|
package/src/registry.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ability Registry
|
|
3
|
+
*
|
|
4
|
+
* In-memory storage for abilities with filtering support.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Ability } from '@defai.digital/contracts';
|
|
8
|
+
import type { AbilityRegistry, AbilityFilter } from './types.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* In-memory ability registry implementation
|
|
12
|
+
*/
|
|
13
|
+
export class InMemoryAbilityRegistry implements AbilityRegistry {
|
|
14
|
+
private abilities = new Map<string, Ability>();
|
|
15
|
+
|
|
16
|
+
async register(ability: Ability): Promise<void> {
|
|
17
|
+
this.abilities.set(ability.abilityId, ability);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async get(abilityId: string): Promise<Ability | undefined> {
|
|
21
|
+
return this.abilities.get(abilityId);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async list(filter?: AbilityFilter): Promise<Ability[]> {
|
|
25
|
+
let results = Array.from(this.abilities.values());
|
|
26
|
+
|
|
27
|
+
if (filter) {
|
|
28
|
+
if (filter.category !== undefined) {
|
|
29
|
+
results = results.filter((a) => a.category === filter.category);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (filter.tags !== undefined && filter.tags.length > 0) {
|
|
33
|
+
results = results.filter((a) =>
|
|
34
|
+
filter.tags!.some((tag) => a.tags?.includes(tag))
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (filter.enabled !== undefined) {
|
|
39
|
+
results = results.filter((a) => a.enabled === filter.enabled);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (filter.applicableTo !== undefined) {
|
|
43
|
+
results = results.filter((a) => {
|
|
44
|
+
// Check if excluded
|
|
45
|
+
if (a.excludeFrom?.includes(filter.applicableTo!)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
// Check if applicable (empty means all)
|
|
49
|
+
if (!a.applicableTo || a.applicableTo.length === 0) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
return (
|
|
53
|
+
a.applicableTo.includes('*') ||
|
|
54
|
+
a.applicableTo.includes(filter.applicableTo!)
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Sort by priority (higher first)
|
|
61
|
+
results.sort((a, b) => (b.priority ?? 50) - (a.priority ?? 50));
|
|
62
|
+
|
|
63
|
+
return results;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async remove(abilityId: string): Promise<void> {
|
|
67
|
+
this.abilities.delete(abilityId);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async exists(abilityId: string): Promise<boolean> {
|
|
71
|
+
return this.abilities.has(abilityId);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async clear(): Promise<void> {
|
|
75
|
+
this.abilities.clear();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Creates an in-memory ability registry
|
|
81
|
+
*/
|
|
82
|
+
export function createAbilityRegistry(): AbilityRegistry {
|
|
83
|
+
return new InMemoryAbilityRegistry();
|
|
84
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ability Domain Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
type Ability,
|
|
7
|
+
type AbilityInjectionResult,
|
|
8
|
+
LIMIT_ABILITY_TOKENS,
|
|
9
|
+
LIMIT_ABILITIES_INJECT,
|
|
10
|
+
} from '@defai.digital/contracts';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Ability registry interface
|
|
14
|
+
*/
|
|
15
|
+
export interface AbilityRegistry {
|
|
16
|
+
/**
|
|
17
|
+
* Register an ability
|
|
18
|
+
*/
|
|
19
|
+
register(ability: Ability): Promise<void>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get an ability by ID
|
|
23
|
+
*/
|
|
24
|
+
get(abilityId: string): Promise<Ability | undefined>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* List all abilities
|
|
28
|
+
*/
|
|
29
|
+
list(filter?: AbilityFilter): Promise<Ability[]>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Remove an ability
|
|
33
|
+
*/
|
|
34
|
+
remove(abilityId: string): Promise<void>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check if ability exists
|
|
38
|
+
*/
|
|
39
|
+
exists(abilityId: string): Promise<boolean>;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Clear all abilities
|
|
43
|
+
*/
|
|
44
|
+
clear(): Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Filter for listing abilities
|
|
49
|
+
*/
|
|
50
|
+
export interface AbilityFilter {
|
|
51
|
+
category?: string;
|
|
52
|
+
tags?: string[];
|
|
53
|
+
enabled?: boolean;
|
|
54
|
+
applicableTo?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Ability loader interface
|
|
59
|
+
*/
|
|
60
|
+
export interface AbilityLoader {
|
|
61
|
+
/**
|
|
62
|
+
* Load an ability by ID
|
|
63
|
+
*/
|
|
64
|
+
load(abilityId: string): Promise<Ability | undefined>;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Load all abilities from the source
|
|
68
|
+
*/
|
|
69
|
+
loadAll(): Promise<Ability[]>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if ability exists
|
|
73
|
+
*/
|
|
74
|
+
exists(abilityId: string): Promise<boolean>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Reload abilities from source
|
|
78
|
+
*/
|
|
79
|
+
reload(): Promise<void>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Ability loader configuration
|
|
84
|
+
*/
|
|
85
|
+
export interface AbilityLoaderConfig {
|
|
86
|
+
/** Directory to load abilities from */
|
|
87
|
+
abilitiesDir: string;
|
|
88
|
+
/** File extensions to load */
|
|
89
|
+
extensions?: string[];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Ability manager interface - coordinates loading and injection
|
|
94
|
+
*/
|
|
95
|
+
export interface AbilityManager {
|
|
96
|
+
/**
|
|
97
|
+
* Get abilities for an agent based on task
|
|
98
|
+
*/
|
|
99
|
+
getAbilitiesForTask(
|
|
100
|
+
agentId: string,
|
|
101
|
+
task: string,
|
|
102
|
+
coreAbilities?: string[],
|
|
103
|
+
maxAbilities?: number
|
|
104
|
+
): Promise<Ability[]>;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Inject abilities into agent context
|
|
108
|
+
*/
|
|
109
|
+
injectAbilities(
|
|
110
|
+
agentId: string,
|
|
111
|
+
task: string,
|
|
112
|
+
coreAbilities?: string[],
|
|
113
|
+
options?: AbilityInjectionOptions
|
|
114
|
+
): Promise<AbilityInjectionResult>;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get all abilities applicable to an agent
|
|
118
|
+
*/
|
|
119
|
+
getApplicableAbilities(agentId: string): Promise<Ability[]>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Options for ability injection
|
|
124
|
+
*/
|
|
125
|
+
export interface AbilityInjectionOptions {
|
|
126
|
+
maxAbilities?: number;
|
|
127
|
+
maxTokens?: number;
|
|
128
|
+
includeMetadata?: boolean;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Ability domain configuration
|
|
133
|
+
*/
|
|
134
|
+
export interface AbilityDomainConfig {
|
|
135
|
+
abilitiesDir: string;
|
|
136
|
+
maxAbilitiesPerAgent: number;
|
|
137
|
+
maxTokensPerInjection: number;
|
|
138
|
+
cacheEnabled: boolean;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Default ability domain configuration
|
|
143
|
+
*/
|
|
144
|
+
export const DEFAULT_ABILITY_DOMAIN_CONFIG: AbilityDomainConfig = {
|
|
145
|
+
abilitiesDir: 'examples/abilities',
|
|
146
|
+
maxAbilitiesPerAgent: LIMIT_ABILITIES_INJECT,
|
|
147
|
+
maxTokensPerInjection: LIMIT_ABILITY_TOKENS,
|
|
148
|
+
cacheEnabled: true,
|
|
149
|
+
};
|