@charming_groot/agent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-loop.d.ts +46 -0
- package/dist/agent-loop.d.ts.map +1 -0
- package/dist/agent-loop.js +139 -0
- package/dist/agent-loop.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/message-manager.d.ts +46 -0
- package/dist/message-manager.d.ts.map +1 -0
- package/dist/message-manager.js +152 -0
- package/dist/message-manager.js.map +1 -0
- package/dist/permission.d.ts +37 -0
- package/dist/permission.d.ts.map +1 -0
- package/dist/permission.js +62 -0
- package/dist/permission.js.map +1 -0
- package/dist/session-manager.d.ts +31 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +101 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/skill-tool.d.ts +38 -0
- package/dist/skill-tool.d.ts.map +1 -0
- package/dist/skill-tool.js +112 -0
- package/dist/skill-tool.js.map +1 -0
- package/dist/sub-agent-tool.d.ts +39 -0
- package/dist/sub-agent-tool.d.ts.map +1 -0
- package/dist/sub-agent-tool.js +80 -0
- package/dist/sub-agent-tool.js.map +1 -0
- package/dist/token-counter.d.ts +16 -0
- package/dist/token-counter.d.ts.map +1 -0
- package/dist/token-counter.js +69 -0
- package/dist/token-counter.js.map +1 -0
- package/dist/tool-dispatcher.d.ts +15 -0
- package/dist/tool-dispatcher.d.ts.map +1 -0
- package/dist/tool-dispatcher.js +94 -0
- package/dist/tool-dispatcher.js.map +1 -0
- package/package.json +34 -0
- package/src/agent-loop.ts +210 -0
- package/src/index.ts +19 -0
- package/src/message-manager.ts +184 -0
- package/src/permission.ts +104 -0
- package/src/session-manager.ts +121 -0
- package/src/skill-tool.ts +155 -0
- package/src/sub-agent-tool.ts +122 -0
- package/src/token-counter.ts +79 -0
- package/src/tool-dispatcher.ts +124 -0
- package/tests/agent-loop.test.ts +372 -0
- package/tests/message-manager-new.test.ts +204 -0
- package/tests/message-manager.test.ts +195 -0
- package/tests/permission.test.ts +148 -0
- package/tests/session-manager.test.ts +106 -0
- package/tests/skill-tool.test.ts +119 -0
- package/tests/sub-agent-tool.test.ts +198 -0
- package/tests/token-counter.test.ts +77 -0
- package/tests/tool-dispatcher.test.ts +181 -0
- package/tsconfig.json +9 -0
- package/vitest.config.ts +17 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { createChildLogger } from '@charming_groot/core';
|
|
4
|
+
/**
|
|
5
|
+
* Persists and restores agent conversations to/from disk.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const sm = new SessionManager('/path/to/sessions');
|
|
9
|
+
* await sm.save(sessionId, messageManager);
|
|
10
|
+
* await sm.load(sessionId, messageManager);
|
|
11
|
+
*/
|
|
12
|
+
export class SessionManager {
|
|
13
|
+
sessionsDir;
|
|
14
|
+
logger;
|
|
15
|
+
constructor(sessionsDir) {
|
|
16
|
+
this.sessionsDir = sessionsDir;
|
|
17
|
+
this.logger = createChildLogger('session-manager');
|
|
18
|
+
}
|
|
19
|
+
/** Save the current conversation to disk. */
|
|
20
|
+
async save(sessionId, manager) {
|
|
21
|
+
const filePath = this.sessionPath(sessionId);
|
|
22
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
23
|
+
let meta;
|
|
24
|
+
try {
|
|
25
|
+
const existing = await this.readSessionFile(filePath);
|
|
26
|
+
meta = {
|
|
27
|
+
sessionId,
|
|
28
|
+
createdAt: existing.meta.createdAt,
|
|
29
|
+
updatedAt: new Date().toISOString(),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
meta = {
|
|
34
|
+
sessionId,
|
|
35
|
+
createdAt: new Date().toISOString(),
|
|
36
|
+
updatedAt: new Date().toISOString(),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const data = {
|
|
40
|
+
meta,
|
|
41
|
+
messages: manager.serialize(),
|
|
42
|
+
};
|
|
43
|
+
await writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');
|
|
44
|
+
this.logger.info({ sessionId, messages: manager.messageCount }, 'Session saved');
|
|
45
|
+
}
|
|
46
|
+
/** Restore a conversation from disk into the given MessageManager. */
|
|
47
|
+
async load(sessionId, manager) {
|
|
48
|
+
const filePath = this.sessionPath(sessionId);
|
|
49
|
+
const data = await this.readSessionFile(filePath);
|
|
50
|
+
manager.restore(data.messages);
|
|
51
|
+
this.logger.info({ sessionId, messages: manager.messageCount }, 'Session loaded');
|
|
52
|
+
return data.meta;
|
|
53
|
+
}
|
|
54
|
+
/** Check if a session file exists. */
|
|
55
|
+
async exists(sessionId) {
|
|
56
|
+
try {
|
|
57
|
+
await this.readSessionFile(this.sessionPath(sessionId));
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** List all session IDs in the sessions directory. */
|
|
65
|
+
async list() {
|
|
66
|
+
const { readdir } = await import('node:fs/promises');
|
|
67
|
+
try {
|
|
68
|
+
const entries = await readdir(this.sessionsDir);
|
|
69
|
+
const metas = [];
|
|
70
|
+
for (const entry of entries) {
|
|
71
|
+
if (!entry.endsWith('.session.json'))
|
|
72
|
+
continue;
|
|
73
|
+
try {
|
|
74
|
+
const data = await this.readSessionFile(join(this.sessionsDir, entry));
|
|
75
|
+
metas.push(data.meta);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// skip corrupt files
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return metas.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
sessionPath(sessionId) {
|
|
88
|
+
// Sanitize sessionId to prevent path traversal
|
|
89
|
+
const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
90
|
+
return join(this.sessionsDir, `${safe}.session.json`);
|
|
91
|
+
}
|
|
92
|
+
async readSessionFile(filePath) {
|
|
93
|
+
const content = await readFile(filePath, 'utf-8');
|
|
94
|
+
const parsed = JSON.parse(content);
|
|
95
|
+
if (!parsed.meta || !parsed.messages) {
|
|
96
|
+
throw new Error('Invalid session file format');
|
|
97
|
+
}
|
|
98
|
+
return parsed;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=session-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ITool, ToolDescription, ToolResult, JsonObject } from '@charming_groot/core';
|
|
2
|
+
import type { RunContext } from '@charming_groot/core';
|
|
3
|
+
/** Minimal skill shape — avoids hard dependency on @core/types */
|
|
4
|
+
export interface SkillEntry {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly description: string;
|
|
7
|
+
readonly tools: readonly string[];
|
|
8
|
+
readonly prompt: string;
|
|
9
|
+
readonly rules: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
/** Minimal registry interface — compatible with @core/skill SkillRegistry */
|
|
12
|
+
export interface SkillProvider {
|
|
13
|
+
get(name: string): SkillEntry | undefined;
|
|
14
|
+
getAll(): readonly SkillEntry[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* An ITool that exposes the skill registry to the agent.
|
|
18
|
+
*
|
|
19
|
+
* Actions:
|
|
20
|
+
* - "list" → returns all available skill names and descriptions
|
|
21
|
+
* - "invoke" → returns the skill's prompt, tools, and rules so the
|
|
22
|
+
* agent can adopt that behavior for the current task
|
|
23
|
+
*
|
|
24
|
+
* This enables CLI-style `/skill` invocation: the agent discovers
|
|
25
|
+
* available skills and activates one by reading its prompt guidance.
|
|
26
|
+
*/
|
|
27
|
+
export declare class SkillTool implements ITool {
|
|
28
|
+
readonly name = "skill";
|
|
29
|
+
readonly requiresPermission = false;
|
|
30
|
+
private readonly registry;
|
|
31
|
+
private readonly logger;
|
|
32
|
+
constructor(registry: SkillProvider);
|
|
33
|
+
describe(): ToolDescription;
|
|
34
|
+
execute(params: JsonObject, _context: RunContext): Promise<ToolResult>;
|
|
35
|
+
private listSkills;
|
|
36
|
+
private invokeSkill;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=skill-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-tool.d.ts","sourceRoot":"","sources":["../src/skill-tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,KAAK,EACL,eAAe,EACf,UAAU,EACV,UAAU,EAEX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,kEAAkE;AAClE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IAC1C,MAAM,IAAI,SAAS,UAAU,EAAE,CAAC;CACjC;AAED;;;;;;;;;;GAUG;AACH,qBAAa,SAAU,YAAW,KAAK;IACrC,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,kBAAkB,SAAS;IAEpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,QAAQ,EAAE,aAAa;IAKnC,QAAQ,IAAI,eAAe;IA8BrB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAuB5E,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,WAAW;CAuCpB"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { createChildLogger } from '@charming_groot/core';
|
|
2
|
+
/**
|
|
3
|
+
* An ITool that exposes the skill registry to the agent.
|
|
4
|
+
*
|
|
5
|
+
* Actions:
|
|
6
|
+
* - "list" → returns all available skill names and descriptions
|
|
7
|
+
* - "invoke" → returns the skill's prompt, tools, and rules so the
|
|
8
|
+
* agent can adopt that behavior for the current task
|
|
9
|
+
*
|
|
10
|
+
* This enables CLI-style `/skill` invocation: the agent discovers
|
|
11
|
+
* available skills and activates one by reading its prompt guidance.
|
|
12
|
+
*/
|
|
13
|
+
export class SkillTool {
|
|
14
|
+
name = 'skill';
|
|
15
|
+
requiresPermission = false;
|
|
16
|
+
registry;
|
|
17
|
+
logger;
|
|
18
|
+
constructor(registry) {
|
|
19
|
+
this.registry = registry;
|
|
20
|
+
this.logger = createChildLogger('skill-tool');
|
|
21
|
+
}
|
|
22
|
+
describe() {
|
|
23
|
+
return {
|
|
24
|
+
name: this.name,
|
|
25
|
+
description: 'List or invoke predefined skills. ' +
|
|
26
|
+
'Use action="list" to see available skills, ' +
|
|
27
|
+
'or action="invoke" with name="<skill>" to activate a skill.',
|
|
28
|
+
parameters: [
|
|
29
|
+
{
|
|
30
|
+
name: 'action',
|
|
31
|
+
type: 'string',
|
|
32
|
+
description: 'Action to perform: "list" or "invoke"',
|
|
33
|
+
required: true,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'name',
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: 'Skill name (required for "invoke")',
|
|
39
|
+
required: false,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'input',
|
|
43
|
+
type: 'string',
|
|
44
|
+
description: 'Optional context/input to pass to the skill',
|
|
45
|
+
required: false,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async execute(params, _context) {
|
|
51
|
+
const action = params['action'];
|
|
52
|
+
if (action === 'list') {
|
|
53
|
+
return this.listSkills();
|
|
54
|
+
}
|
|
55
|
+
if (action === 'invoke') {
|
|
56
|
+
const name = params['name'];
|
|
57
|
+
if (typeof name !== 'string' || name.trim().length === 0) {
|
|
58
|
+
return { success: false, output: '', error: 'Missing "name" parameter for invoke action' };
|
|
59
|
+
}
|
|
60
|
+
const input = typeof params['input'] === 'string' ? params['input'] : undefined;
|
|
61
|
+
return this.invokeSkill(name, input);
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
success: false,
|
|
65
|
+
output: '',
|
|
66
|
+
error: `Unknown action: "${String(action)}". Use "list" or "invoke".`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
listSkills() {
|
|
70
|
+
const skills = this.registry.getAll();
|
|
71
|
+
if (skills.length === 0) {
|
|
72
|
+
return { success: true, output: 'No skills available.' };
|
|
73
|
+
}
|
|
74
|
+
const lines = skills.map((s) => `- ${s.name}: ${s.description} [tools: ${s.tools.join(', ')}]`);
|
|
75
|
+
this.logger.debug({ count: skills.length }, 'Listed skills');
|
|
76
|
+
return { success: true, output: `Available skills:\n${lines.join('\n')}` };
|
|
77
|
+
}
|
|
78
|
+
invokeSkill(name, input) {
|
|
79
|
+
const skill = this.registry.get(name);
|
|
80
|
+
if (!skill) {
|
|
81
|
+
return { success: false, output: '', error: `Skill "${name}" not found` };
|
|
82
|
+
}
|
|
83
|
+
this.logger.info({ skill: name }, 'Skill invoked');
|
|
84
|
+
const sections = [
|
|
85
|
+
`## Skill: ${skill.name}`,
|
|
86
|
+
'',
|
|
87
|
+
skill.description,
|
|
88
|
+
'',
|
|
89
|
+
'### Prompt',
|
|
90
|
+
skill.prompt,
|
|
91
|
+
];
|
|
92
|
+
if (skill.tools.length > 0) {
|
|
93
|
+
sections.push('', `### Available tools: ${skill.tools.join(', ')}`);
|
|
94
|
+
}
|
|
95
|
+
if (skill.rules.length > 0) {
|
|
96
|
+
sections.push('', `### Rules: ${skill.rules.join(', ')}`);
|
|
97
|
+
}
|
|
98
|
+
if (input) {
|
|
99
|
+
sections.push('', `### User input`, input);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
success: true,
|
|
103
|
+
output: sections.join('\n'),
|
|
104
|
+
metadata: {
|
|
105
|
+
skillName: skill.name,
|
|
106
|
+
tools: [...skill.tools],
|
|
107
|
+
rules: [...skill.rules],
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=skill-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-tool.js","sourceRoot":"","sources":["../src/skill-tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ITool, ILlmProvider, ToolDescription, ToolResult, JsonObject } from '@charming_groot/core';
|
|
2
|
+
import { Registry, RunContext } from '@charming_groot/core';
|
|
3
|
+
import type { SystemPromptBuilder } from './agent-loop.js';
|
|
4
|
+
import type { PermissionHandler } from './permission.js';
|
|
5
|
+
/** Configuration for creating a sub-agent tool. */
|
|
6
|
+
export interface SubAgentToolConfig {
|
|
7
|
+
/** Display name for this sub-agent tool */
|
|
8
|
+
readonly name: string;
|
|
9
|
+
/** Description shown to the parent agent */
|
|
10
|
+
readonly description: string;
|
|
11
|
+
/** LLM provider the sub-agent will use */
|
|
12
|
+
readonly provider: ILlmProvider;
|
|
13
|
+
/** Tools available to the sub-agent */
|
|
14
|
+
readonly toolRegistry: Registry<ITool>;
|
|
15
|
+
/** System prompt or dynamic builder for the sub-agent */
|
|
16
|
+
readonly systemPrompt?: string;
|
|
17
|
+
readonly systemPromptBuilder?: SystemPromptBuilder;
|
|
18
|
+
/** Max iterations for the sub-agent (default: 25) */
|
|
19
|
+
readonly maxIterations?: number;
|
|
20
|
+
/** Permission handler for the sub-agent's tools */
|
|
21
|
+
readonly permissionHandler?: PermissionHandler;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Wraps an AgentLoop as an ITool so a parent agent can delegate
|
|
25
|
+
* sub-tasks to a child agent via tool calls.
|
|
26
|
+
*
|
|
27
|
+
* The parent sends a "task" parameter; the sub-agent runs autonomously
|
|
28
|
+
* and returns the final result as tool output.
|
|
29
|
+
*/
|
|
30
|
+
export declare class SubAgentTool implements ITool {
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly requiresPermission = false;
|
|
33
|
+
private readonly config;
|
|
34
|
+
private readonly logger;
|
|
35
|
+
constructor(config: SubAgentToolConfig);
|
|
36
|
+
describe(): ToolDescription;
|
|
37
|
+
execute(params: JsonObject, context: RunContext): Promise<ToolResult>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=sub-agent-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sub-agent-tool.d.ts","sourceRoot":"","sources":["../src/sub-agent-tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,KAAK,EACL,YAAY,EACZ,eAAe,EACf,UAAU,EACV,UAAU,EAEX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAqB,MAAM,sBAAsB,CAAC;AAE/E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,mDAAmD;AACnD,MAAM,WAAW,kBAAkB;IACjC,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,0CAA0C;IAC1C,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,uCAAuC;IACvC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IACvC,yDAAyD;IACzD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IACnD,qDAAqD;IACrD,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,mDAAmD;IACnD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CAChD;AAID;;;;;;GAMG;AACH,qBAAa,YAAa,YAAW,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,kBAAkB,SAAS;IAEpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,MAAM,EAAE,kBAAkB;IAMtC,QAAQ,IAAI,eAAe;IAerB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;CAoD5E"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { createChildLogger } from '@charming_groot/core';
|
|
2
|
+
import { AgentLoop } from './agent-loop.js';
|
|
3
|
+
const DEFAULT_MAX_ITERATIONS = 25;
|
|
4
|
+
/**
|
|
5
|
+
* Wraps an AgentLoop as an ITool so a parent agent can delegate
|
|
6
|
+
* sub-tasks to a child agent via tool calls.
|
|
7
|
+
*
|
|
8
|
+
* The parent sends a "task" parameter; the sub-agent runs autonomously
|
|
9
|
+
* and returns the final result as tool output.
|
|
10
|
+
*/
|
|
11
|
+
export class SubAgentTool {
|
|
12
|
+
name;
|
|
13
|
+
requiresPermission = false;
|
|
14
|
+
config;
|
|
15
|
+
logger;
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.name = config.name;
|
|
18
|
+
this.config = config;
|
|
19
|
+
this.logger = createChildLogger(`sub-agent:${config.name}`);
|
|
20
|
+
}
|
|
21
|
+
describe() {
|
|
22
|
+
return {
|
|
23
|
+
name: this.name,
|
|
24
|
+
description: this.config.description,
|
|
25
|
+
parameters: [
|
|
26
|
+
{
|
|
27
|
+
name: 'task',
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: 'The task to delegate to the sub-agent',
|
|
30
|
+
required: true,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
async execute(params, context) {
|
|
36
|
+
const task = params['task'];
|
|
37
|
+
if (typeof task !== 'string' || task.trim().length === 0) {
|
|
38
|
+
return { success: false, output: '', error: 'Missing or empty "task" parameter' };
|
|
39
|
+
}
|
|
40
|
+
this.logger.info({ task: task.slice(0, 200) }, 'Sub-agent starting');
|
|
41
|
+
const childLoop = new AgentLoop({
|
|
42
|
+
provider: this.config.provider,
|
|
43
|
+
toolRegistry: this.config.toolRegistry,
|
|
44
|
+
config: {
|
|
45
|
+
provider: {
|
|
46
|
+
providerId: this.config.provider.providerId,
|
|
47
|
+
model: 'sub-agent',
|
|
48
|
+
auth: { type: 'api-key', apiKey: '' },
|
|
49
|
+
maxTokens: 4096,
|
|
50
|
+
temperature: 0.7,
|
|
51
|
+
},
|
|
52
|
+
maxIterations: this.config.maxIterations ?? DEFAULT_MAX_ITERATIONS,
|
|
53
|
+
workingDirectory: context.workingDirectory,
|
|
54
|
+
systemPrompt: this.config.systemPrompt,
|
|
55
|
+
},
|
|
56
|
+
permissionHandler: this.config.permissionHandler,
|
|
57
|
+
systemPromptBuilder: this.config.systemPromptBuilder,
|
|
58
|
+
});
|
|
59
|
+
// Propagate abort from parent context to child via AbortSignal
|
|
60
|
+
const onAbort = () => childLoop.abort('Parent aborted');
|
|
61
|
+
context.signal.addEventListener('abort', onAbort, { once: true });
|
|
62
|
+
try {
|
|
63
|
+
const result = await childLoop.run(task);
|
|
64
|
+
this.logger.info({ iterations: result.iterations, aborted: result.aborted }, 'Sub-agent completed');
|
|
65
|
+
if (result.aborted) {
|
|
66
|
+
return { success: false, output: result.content, error: 'Sub-agent was aborted' };
|
|
67
|
+
}
|
|
68
|
+
return { success: true, output: result.content };
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
72
|
+
this.logger.warn({ error: message }, 'Sub-agent failed');
|
|
73
|
+
return { success: false, output: '', error: `Sub-agent error: ${message}` };
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
context.signal.removeEventListener('abort', onAbort);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=sub-agent-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sub-agent-tool.js","sourceRoot":"","sources":["../src/sub-agent-tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Message } from '@charming_groot/core';
|
|
2
|
+
export declare function resolveEncoding(modelId: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Count tokens in a plain text string.
|
|
5
|
+
*/
|
|
6
|
+
export declare function countTextTokens(text: string): number;
|
|
7
|
+
/**
|
|
8
|
+
* Count tokens for a single Message (content + tool calls + tool results).
|
|
9
|
+
* Mirrors OpenAI's message token counting spec closely enough for Claude too.
|
|
10
|
+
*/
|
|
11
|
+
export declare function countMessageTokens(msg: Message): number;
|
|
12
|
+
/**
|
|
13
|
+
* Count total tokens across a message history.
|
|
14
|
+
*/
|
|
15
|
+
export declare function countHistoryTokens(messages: readonly Message[]): number;
|
|
16
|
+
//# sourceMappingURL=token-counter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-counter.d.ts","sourceRoot":"","sources":["../src/token-counter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AA2BpD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQpD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAqBvD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAEvE"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { getEncoding } from 'js-tiktoken';
|
|
2
|
+
// cl100k_base: Claude, GPT-4, GPT-3.5-turbo
|
|
3
|
+
// o200k_base: GPT-4o, o1, o3
|
|
4
|
+
const MODEL_ENCODING = {
|
|
5
|
+
'claude': 'cl100k_base',
|
|
6
|
+
'gpt-4': 'cl100k_base',
|
|
7
|
+
'gpt-3.5': 'cl100k_base',
|
|
8
|
+
'gpt-4o': 'o200k_base',
|
|
9
|
+
'o1': 'o200k_base',
|
|
10
|
+
'o3': 'o200k_base',
|
|
11
|
+
};
|
|
12
|
+
const MESSAGE_OVERHEAD = 4; // role + formatting tokens per message
|
|
13
|
+
const REPLY_OVERHEAD = 3; // assistant reply priming tokens
|
|
14
|
+
let enc = null;
|
|
15
|
+
function getEncoder() {
|
|
16
|
+
if (!enc) {
|
|
17
|
+
// cl100k_base works well enough for Claude and most OpenAI models.
|
|
18
|
+
// For o200k_base models the count differs by ~3% — acceptable for budget tracking.
|
|
19
|
+
enc = getEncoding('cl100k_base');
|
|
20
|
+
}
|
|
21
|
+
return enc;
|
|
22
|
+
}
|
|
23
|
+
export function resolveEncoding(modelId) {
|
|
24
|
+
const key = Object.keys(MODEL_ENCODING).find(k => modelId.toLowerCase().includes(k));
|
|
25
|
+
return key ? MODEL_ENCODING[key] : 'cl100k_base';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Count tokens in a plain text string.
|
|
29
|
+
*/
|
|
30
|
+
export function countTextTokens(text) {
|
|
31
|
+
if (!text)
|
|
32
|
+
return 0;
|
|
33
|
+
try {
|
|
34
|
+
return getEncoder().encode(text).length;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Fallback: character estimate
|
|
38
|
+
return Math.ceil(text.length / 4);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Count tokens for a single Message (content + tool calls + tool results).
|
|
43
|
+
* Mirrors OpenAI's message token counting spec closely enough for Claude too.
|
|
44
|
+
*/
|
|
45
|
+
export function countMessageTokens(msg) {
|
|
46
|
+
let tokens = MESSAGE_OVERHEAD;
|
|
47
|
+
tokens += countTextTokens(msg.content);
|
|
48
|
+
if (msg.toolCalls) {
|
|
49
|
+
for (const tc of msg.toolCalls) {
|
|
50
|
+
tokens += countTextTokens(tc.name);
|
|
51
|
+
tokens += countTextTokens(tc.arguments);
|
|
52
|
+
tokens += 3; // function_call framing
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (msg.toolResults) {
|
|
56
|
+
for (const tr of msg.toolResults) {
|
|
57
|
+
tokens += countTextTokens(tr.content);
|
|
58
|
+
tokens += 3; // tool result framing
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return tokens;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Count total tokens across a message history.
|
|
65
|
+
*/
|
|
66
|
+
export function countHistoryTokens(messages) {
|
|
67
|
+
return messages.reduce((sum, m) => sum + countMessageTokens(m), REPLY_OVERHEAD);
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=token-counter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-counter.js","sourceRoot":"","sources":["../src/token-counter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,aAAa,CAAC;AAGzD,4CAA4C;AAC5C,8BAA8B;AAC9B,MAAM,cAAc,GAA2B;IAC7C,QAAQ,EAAE,aAAa;IACvB,OAAO,EAAE,aAAa;IACtB,SAAS,EAAE,aAAa;IACxB,QAAQ,EAAE,YAAY;IACtB,IAAI,EAAE,YAAY;IAClB,IAAI,EAAE,YAAY;CACnB,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,CAAC,CAAG,uCAAuC;AACrE,MAAM,cAAc,GAAI,CAAC,CAAC,CAAG,iCAAiC;AAE9D,IAAI,GAAG,GAAoB,IAAI,CAAC;AAEhC,SAAS,UAAU;IACjB,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,mEAAmE;QACnE,mFAAmF;QACnF,GAAG,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACrF,OAAO,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;AACnD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,CAAC,IAAI;QAAE,OAAO,CAAC,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,UAAU,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;QAC/B,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACpC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC7C,IAAI,MAAM,GAAG,gBAAgB,CAAC;IAE9B,MAAM,IAAI,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAEvC,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QAClB,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YAC/B,MAAM,IAAI,eAAe,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;YACnC,MAAM,IAAI,eAAe,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;YACxC,MAAM,IAAI,CAAC,CAAC,CAAC,wBAAwB;QACvC,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QACpB,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;YACjC,MAAM,IAAI,eAAe,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;YACtC,MAAM,IAAI,CAAC,CAAC,CAAC,sBAAsB;QACrC,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAA4B;IAC7D,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,kBAAkB,CAAC,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;AAClF,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ITool, ToolCall, ToolResult, ToolDescription } from '@charming_groot/core';
|
|
2
|
+
import { Registry } from '@charming_groot/core';
|
|
3
|
+
import type { RunContext } from '@charming_groot/core';
|
|
4
|
+
import { PermissionManager } from './permission.js';
|
|
5
|
+
export declare class ToolDispatcher {
|
|
6
|
+
private readonly toolRegistry;
|
|
7
|
+
private readonly permissionManager;
|
|
8
|
+
private readonly logger;
|
|
9
|
+
constructor(toolRegistry: Registry<ITool>, permissionManager: PermissionManager);
|
|
10
|
+
dispatch(toolCall: ToolCall, context: RunContext): Promise<ToolResult>;
|
|
11
|
+
dispatchAll(toolCalls: readonly ToolCall[], context: RunContext): Promise<ReadonlyMap<string, ToolResult>>;
|
|
12
|
+
private truncateResult;
|
|
13
|
+
getToolDescriptions(): ToolDescription[];
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=tool-dispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-dispatcher.d.ts","sourceRoot":"","sources":["../src/tool-dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAc,MAAM,sBAAsB,CAAC;AACrG,OAAO,EACL,QAAQ,EAGT,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAMpD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkB;IAC/C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAoB;IACtD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,iBAAiB,EAAE,iBAAiB;IAMzE,QAAQ,CACZ,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,UAAU,CAAC;IAsDhB,WAAW,CACf,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAoB3C,OAAO,CAAC,cAAc;IAYtB,mBAAmB,IAAI,eAAe,EAAE;CAOzC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { PermissionDeniedError, createChildLogger, } from '@charming_groot/core';
|
|
2
|
+
/** Maximum characters in a single tool result output */
|
|
3
|
+
const MAX_OUTPUT_CHARS = 80_000;
|
|
4
|
+
const TRUNCATION_NOTICE = '\n\n... [output truncated — exceeded 80,000 characters]';
|
|
5
|
+
export class ToolDispatcher {
|
|
6
|
+
toolRegistry;
|
|
7
|
+
permissionManager;
|
|
8
|
+
logger;
|
|
9
|
+
constructor(toolRegistry, permissionManager) {
|
|
10
|
+
this.toolRegistry = toolRegistry;
|
|
11
|
+
this.permissionManager = permissionManager;
|
|
12
|
+
this.logger = createChildLogger('tool-dispatcher');
|
|
13
|
+
}
|
|
14
|
+
async dispatch(toolCall, context) {
|
|
15
|
+
const tool = this.toolRegistry.tryGet(toolCall.name);
|
|
16
|
+
if (!tool) {
|
|
17
|
+
this.logger.warn({ toolName: toolCall.name }, 'Unknown tool');
|
|
18
|
+
return {
|
|
19
|
+
success: false,
|
|
20
|
+
output: '',
|
|
21
|
+
error: `Unknown tool: ${toolCall.name}`,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
let params;
|
|
25
|
+
try {
|
|
26
|
+
params = JSON.parse(toolCall.arguments);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
this.logger.warn({ toolName: toolCall.name, arguments: toolCall.arguments.slice(0, 200) }, 'Invalid JSON in tool arguments');
|
|
30
|
+
return {
|
|
31
|
+
success: false,
|
|
32
|
+
output: '',
|
|
33
|
+
error: `Invalid tool arguments for "${toolCall.name}": ${toolCall.arguments.slice(0, 100)}`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const permitted = await this.permissionManager.checkPermission(tool, params);
|
|
37
|
+
if (!permitted) {
|
|
38
|
+
this.logger.info({ toolName: toolCall.name }, 'Permission denied');
|
|
39
|
+
throw new PermissionDeniedError(toolCall.name);
|
|
40
|
+
}
|
|
41
|
+
const toolStartedAt = Date.now();
|
|
42
|
+
context.eventBus.emit('tool:start', { runId: context.runId, toolCall, startedAt: toolStartedAt });
|
|
43
|
+
let result;
|
|
44
|
+
try {
|
|
45
|
+
result = await tool.execute(params, context);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (error instanceof PermissionDeniedError)
|
|
49
|
+
throw error;
|
|
50
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
51
|
+
result = { success: false, output: '', error: message };
|
|
52
|
+
}
|
|
53
|
+
const truncated = this.truncateResult(result);
|
|
54
|
+
context.eventBus.emit('tool:end', {
|
|
55
|
+
runId: context.runId,
|
|
56
|
+
toolCall,
|
|
57
|
+
result: truncated,
|
|
58
|
+
durationMs: Date.now() - toolStartedAt,
|
|
59
|
+
});
|
|
60
|
+
return truncated;
|
|
61
|
+
}
|
|
62
|
+
async dispatchAll(toolCalls, context) {
|
|
63
|
+
const abortedResult = {
|
|
64
|
+
success: false,
|
|
65
|
+
output: '',
|
|
66
|
+
error: 'Operation aborted',
|
|
67
|
+
};
|
|
68
|
+
const entries = await Promise.all(toolCalls.map(async (toolCall) => {
|
|
69
|
+
if (context.isAborted) {
|
|
70
|
+
return [toolCall.id, abortedResult];
|
|
71
|
+
}
|
|
72
|
+
const result = await this.dispatch(toolCall, context);
|
|
73
|
+
return [toolCall.id, result];
|
|
74
|
+
}));
|
|
75
|
+
return new Map(entries);
|
|
76
|
+
}
|
|
77
|
+
truncateResult(result) {
|
|
78
|
+
if (result.output.length <= MAX_OUTPUT_CHARS)
|
|
79
|
+
return result;
|
|
80
|
+
this.logger.info({ originalLength: result.output.length, limit: MAX_OUTPUT_CHARS }, 'Tool output truncated');
|
|
81
|
+
return {
|
|
82
|
+
...result,
|
|
83
|
+
output: result.output.slice(0, MAX_OUTPUT_CHARS) + TRUNCATION_NOTICE,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
getToolDescriptions() {
|
|
87
|
+
const descriptions = [];
|
|
88
|
+
for (const [, tool] of this.toolRegistry.getAll()) {
|
|
89
|
+
descriptions.push(tool.describe());
|
|
90
|
+
}
|
|
91
|
+
return descriptions;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=tool-dispatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-dispatcher.js","sourceRoot":"","sources":["../src/tool-dispatcher.ts"],"names":[],"mappings":"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"}
|