@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/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
+ }
@@ -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
+ };