@agntk/core 0.1.2 → 0.2.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/README.md +56 -90
- package/dist/advanced/index.d.ts +9 -2
- package/dist/advanced/index.d.ts.map +1 -1
- package/dist/advanced/index.js +13 -2
- package/dist/advanced/index.js.map +1 -1
- package/dist/agent.d.ts +27 -31
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +228 -275
- package/dist/agent.js.map +1 -1
- package/dist/config/schema.d.ts +38 -38
- package/dist/evals/runner.js +16 -9
- package/dist/evals/runner.js.map +1 -1
- package/dist/evals/types.d.ts +1 -1
- package/dist/evals/types.d.ts.map +1 -1
- package/dist/index.d.ts +4 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -9
- package/dist/index.js.map +1 -1
- package/dist/observability/langfuse.d.ts +2 -2
- package/dist/observability/langfuse.d.ts.map +1 -1
- package/dist/observability/langfuse.js +39 -17
- package/dist/observability/langfuse.js.map +1 -1
- package/dist/presets/sub-agent-configs.d.ts +11 -3
- package/dist/presets/sub-agent-configs.d.ts.map +1 -1
- package/dist/presets/sub-agent-configs.js +5 -10
- package/dist/presets/sub-agent-configs.js.map +1 -1
- package/dist/presets/tools.d.ts +5 -5
- package/dist/tools/browser/tool.d.ts +2 -2
- package/dist/tools/browser/types.d.ts +2 -2
- package/dist/tools/plan/tools.d.ts +1 -1
- package/dist/tools/plan/types.d.ts +2 -2
- package/dist/tools/progress/index.d.ts +2 -2
- package/dist/tools/spawn-agent/index.d.ts +7 -9
- package/dist/tools/spawn-agent/index.d.ts.map +1 -1
- package/dist/tools/spawn-agent/index.js +2 -4
- package/dist/tools/spawn-agent/index.js.map +1 -1
- package/dist/types/agent.d.ts +69 -199
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/agent.js +4 -2
- package/dist/types/agent.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/usage-limits.d.ts +1 -1
- package/dist/usage-limits.js +1 -1
- package/dist/workflow/index.d.ts +1 -5
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +1 -9
- package/dist/workflow/index.js.map +1 -1
- package/dist/wrappers/best-of-n.d.ts +1 -1
- package/dist/wrappers/best-of-n.d.ts.map +1 -1
- package/dist/wrappers/best-of-n.js +11 -6
- package/dist/wrappers/best-of-n.js.map +1 -1
- package/package.json +4 -14
- package/dist/pool/index.d.ts +0 -7
- package/dist/pool/index.d.ts.map +0 -1
- package/dist/pool/index.js +0 -6
- package/dist/pool/index.js.map +0 -1
- package/dist/pool/specialist-pool.d.ts +0 -59
- package/dist/pool/specialist-pool.d.ts.map +0 -1
- package/dist/pool/specialist-pool.js +0 -224
- package/dist/pool/specialist-pool.js.map +0 -1
- package/dist/pool/tools.d.ts +0 -63
- package/dist/pool/tools.d.ts.map +0 -1
- package/dist/pool/tools.js +0 -83
- package/dist/pool/tools.js.map +0 -1
- package/dist/pool/types.d.ts +0 -79
- package/dist/pool/types.d.ts.map +0 -1
- package/dist/pool/types.js +0 -5
- package/dist/pool/types.js.map +0 -1
- package/dist/presets/index.d.ts +0 -5
- package/dist/presets/index.d.ts.map +0 -1
- package/dist/presets/index.js +0 -5
- package/dist/presets/index.js.map +0 -1
- package/dist/presets/role-registry.d.ts +0 -41
- package/dist/presets/role-registry.d.ts.map +0 -1
- package/dist/presets/role-registry.js +0 -213
- package/dist/presets/role-registry.js.map +0 -1
- package/dist/presets/roles.d.ts +0 -105
- package/dist/presets/roles.d.ts.map +0 -1
- package/dist/presets/roles.js +0 -207
- package/dist/presets/roles.js.map +0 -1
- package/dist/workflow/builders/adapt.d.ts +0 -20
- package/dist/workflow/builders/adapt.d.ts.map +0 -1
- package/dist/workflow/builders/adapt.js +0 -33
- package/dist/workflow/builders/adapt.js.map +0 -1
- package/dist/workflow/builders/index.d.ts +0 -8
- package/dist/workflow/builders/index.d.ts.map +0 -1
- package/dist/workflow/builders/index.js +0 -7
- package/dist/workflow/builders/index.js.map +0 -1
- package/dist/workflow/builders/parallel.d.ts +0 -25
- package/dist/workflow/builders/parallel.d.ts.map +0 -1
- package/dist/workflow/builders/parallel.js +0 -60
- package/dist/workflow/builders/parallel.js.map +0 -1
- package/dist/workflow/builders/pipeline.d.ts +0 -22
- package/dist/workflow/builders/pipeline.d.ts.map +0 -1
- package/dist/workflow/builders/pipeline.js +0 -48
- package/dist/workflow/builders/pipeline.js.map +0 -1
- package/dist/workflow/builders/types.d.ts +0 -54
- package/dist/workflow/builders/types.d.ts.map +0 -1
- package/dist/workflow/builders/types.js +0 -5
- package/dist/workflow/builders/types.js.map +0 -1
- package/dist/workflow/schedulers.d.ts +0 -231
- package/dist/workflow/schedulers.d.ts.map +0 -1
- package/dist/workflow/schedulers.js +0 -250
- package/dist/workflow/schedulers.js.map +0 -1
- package/dist/workflow/team/create-team.d.ts +0 -34
- package/dist/workflow/team/create-team.d.ts.map +0 -1
- package/dist/workflow/team/create-team.js +0 -242
- package/dist/workflow/team/create-team.js.map +0 -1
- package/dist/workflow/team/index.d.ts +0 -9
- package/dist/workflow/team/index.d.ts.map +0 -1
- package/dist/workflow/team/index.js +0 -8
- package/dist/workflow/team/index.js.map +0 -1
- package/dist/workflow/team/machines.d.ts +0 -152
- package/dist/workflow/team/machines.d.ts.map +0 -1
- package/dist/workflow/team/machines.js +0 -197
- package/dist/workflow/team/machines.js.map +0 -1
- package/dist/workflow/team/task-board.d.ts +0 -47
- package/dist/workflow/team/task-board.d.ts.map +0 -1
- package/dist/workflow/team/task-board.js +0 -111
- package/dist/workflow/team/task-board.js.map +0 -1
- package/dist/workflow/team/tools.d.ts +0 -66
- package/dist/workflow/team/tools.d.ts.map +0 -1
- package/dist/workflow/team/tools.js +0 -100
- package/dist/workflow/team/tools.js.map +0 -1
- package/dist/workflow/team/types.d.ts +0 -109
- package/dist/workflow/team/types.d.ts.map +0 -1
- package/dist/workflow/team/types.js +0 -5
- package/dist/workflow/team/types.js.map +0 -1
- package/dist/workflow/templates.d.ts +0 -71
- package/dist/workflow/templates.d.ts.map +0 -1
- package/dist/workflow/templates.js +0 -132
- package/dist/workflow/templates.js.map +0 -1
package/dist/agent.js
CHANGED
|
@@ -1,126 +1,154 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @agntk/core -
|
|
2
|
+
* @agntk/core - Agent Factory
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* A fully-equipped agent that shows up ready. No roles, no tool presets,
|
|
5
|
+
* no feature flags. You give it a name, tell it what it's doing, and
|
|
6
|
+
* point it at a task. It figures out the rest.
|
|
7
|
+
*
|
|
8
|
+
* Every capability is auto-detected from the environment:
|
|
9
|
+
* - Tools: ALL tools, always (plus any custom tools you pass)
|
|
10
|
+
* - Memory: always on, stored at ~/.agntk/agents/{name}/
|
|
11
|
+
* - Durability: auto-detected (workflow package installed → on)
|
|
12
|
+
* - Telemetry: auto-detected (LANGFUSE_PUBLIC_KEY set → on)
|
|
13
|
+
* - Skills: auto-discovered from standard directories
|
|
14
|
+
* - Sub-agents: always enabled with team coordination
|
|
15
|
+
* - Reflection: always on (reflact strategy)
|
|
16
|
+
* - Guardrails: always on (output: PII content filter)
|
|
17
|
+
* - Model: auto-selected from available API keys
|
|
6
18
|
*/
|
|
7
|
-
import {
|
|
19
|
+
import { resolve } from 'node:path';
|
|
20
|
+
import { homedir } from 'node:os';
|
|
21
|
+
import { ToolLoopAgent, stepCountIs } from 'ai';
|
|
8
22
|
import { createLogger } from '@agntk/logger';
|
|
9
23
|
import { usageLimitStop } from './usage-limits.js';
|
|
10
24
|
import { resolveModel } from './models.js';
|
|
11
|
-
import { getRole } from './presets/role-registry.js';
|
|
12
25
|
import { createToolPreset } from './presets/tools.js';
|
|
13
26
|
import { createSpawnAgentTool } from './tools/spawn-agent/index.js';
|
|
14
27
|
import { wrapAllToolsWithRetry } from './tools/model-retry.js';
|
|
15
|
-
import {
|
|
28
|
+
import { discoverSkills, filterEligibleSkills, buildSkillsSystemPrompt, loadSkillContent } from './skills/index.js';
|
|
16
29
|
import { checkWorkflowAvailability } from './workflow/utils.js';
|
|
17
30
|
import { wrapToolsAsDurable } from './workflow/durable-tool.js';
|
|
18
31
|
import { createReflectionPrepareStep } from './reflection.js';
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
32
|
+
import { runGuardrails, handleGuardrailResults } from './guardrails/runner.js';
|
|
33
|
+
import { contentFilter } from './guardrails/built-ins.js';
|
|
21
34
|
import { MarkdownMemoryStore } from './memory/store.js';
|
|
22
35
|
import { loadMemoryContext } from './memory/loader.js';
|
|
23
36
|
import { createMemoryTools } from './memory/tools.js';
|
|
24
37
|
import { initObservability, createTelemetrySettings } from './observability/index.js';
|
|
38
|
+
import { buildDynamicSystemPrompt } from './prompts/context.js';
|
|
25
39
|
// ============================================================================
|
|
26
40
|
// Logger
|
|
27
41
|
// ============================================================================
|
|
28
42
|
const log = createLogger('@agntk/core:agent');
|
|
29
43
|
// ============================================================================
|
|
30
|
-
//
|
|
44
|
+
// Constants
|
|
31
45
|
// ============================================================================
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
const DEFAULT_MAX_STEPS = 25;
|
|
47
|
+
const SUB_AGENT_MAX_STEPS = 15;
|
|
48
|
+
const DEFAULT_MAX_SPAWN_DEPTH = 2;
|
|
49
|
+
const AGENT_STATE_BASE = '.agntk/agents';
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Helpers
|
|
52
|
+
// ============================================================================
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the persistent state directory for a named agent.
|
|
55
|
+
* ~/.agntk/agents/{name}/
|
|
56
|
+
*/
|
|
57
|
+
function resolveAgentStatePath(name) {
|
|
58
|
+
const safeName = name.replace(/[^a-zA-Z0-9_-]/g, '_').toLowerCase();
|
|
59
|
+
return resolve(homedir(), AGENT_STATE_BASE, safeName);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Detect if telemetry should be enabled from env vars.
|
|
63
|
+
*/
|
|
64
|
+
function detectTelemetry() {
|
|
65
|
+
return !!(process.env.LANGFUSE_PUBLIC_KEY &&
|
|
66
|
+
process.env.LANGFUSE_SECRET_KEY);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build the base instructions for the agent.
|
|
70
|
+
*/
|
|
71
|
+
function buildBaseInstructions(name, userInstructions, skillsPrompt) {
|
|
72
|
+
const parts = [];
|
|
73
|
+
parts.push(`You are ${name}, a capable AI agent.`);
|
|
74
|
+
if (userInstructions) {
|
|
75
|
+
parts.push('');
|
|
76
|
+
parts.push(userInstructions);
|
|
45
77
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
78
|
+
parts.push('');
|
|
79
|
+
parts.push('You have access to a full suite of tools including file operations, ' +
|
|
80
|
+
'shell commands, code search (grep, glob, ast-grep), a browser, ' +
|
|
81
|
+
'deep reasoning, planning, and persistent memory. ' +
|
|
82
|
+
'You can spawn sub-agents for complex tasks that benefit from delegation. ' +
|
|
83
|
+
'Use the remember tool to persist important findings across sessions. ' +
|
|
84
|
+
'Use the recall tool to search your memory for relevant context.');
|
|
85
|
+
if (skillsPrompt) {
|
|
86
|
+
parts.push('');
|
|
87
|
+
parts.push(skillsPrompt);
|
|
51
88
|
}
|
|
52
|
-
|
|
53
|
-
return allTools;
|
|
89
|
+
return parts.join('\n');
|
|
54
90
|
}
|
|
55
91
|
// ============================================================================
|
|
56
|
-
//
|
|
92
|
+
// Agent Factory
|
|
57
93
|
// ============================================================================
|
|
58
94
|
/**
|
|
59
|
-
*
|
|
95
|
+
* Create an agent — fully equipped, zero config.
|
|
60
96
|
*
|
|
61
97
|
* @example
|
|
62
98
|
* ```typescript
|
|
63
99
|
* const agent = createAgent({
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* toolPreset: 'standard',
|
|
100
|
+
* name: 'deploy-bot',
|
|
101
|
+
* instructions: 'You manage deployments for our k8s cluster.',
|
|
67
102
|
* });
|
|
68
103
|
*
|
|
69
|
-
* const result = await agent.
|
|
70
|
-
*
|
|
104
|
+
* const result = await agent.stream({ prompt: 'Roll back staging to yesterday' });
|
|
105
|
+
* for await (const chunk of result.fullStream) {
|
|
106
|
+
* if (chunk.type === 'text-delta') process.stdout.write(chunk.text ?? '');
|
|
107
|
+
* }
|
|
71
108
|
* ```
|
|
72
109
|
*/
|
|
73
|
-
export function createAgent(options = {}) {
|
|
74
|
-
const {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (options.
|
|
86
|
-
|
|
87
|
-
const skills = loadSkills(options.skills, workspaceRoot);
|
|
88
|
-
if (skills.length > 0) {
|
|
89
|
-
const skillsPrompt = buildSkillsSystemPrompt(skills);
|
|
90
|
-
augmentedSystemPrompt += skillsPrompt;
|
|
91
|
-
log.info('Skills injected', { count: skills.length, names: skills.map(s => s.name) });
|
|
92
|
-
}
|
|
110
|
+
export function createAgent(options, _internal = {}) {
|
|
111
|
+
const { name, instructions, workspaceRoot = process.cwd(), } = options;
|
|
112
|
+
const spawnDepth = _internal._spawnDepth ?? 0;
|
|
113
|
+
const isSubAgent = spawnDepth > 0;
|
|
114
|
+
const maxSteps = options.maxSteps ?? (isSubAgent ? SUB_AGENT_MAX_STEPS : DEFAULT_MAX_STEPS);
|
|
115
|
+
log.info('Creating agent', { name, maxSteps, workspaceRoot, spawnDepth });
|
|
116
|
+
// ── 1. Resolve model ──────────────────────────────────────────────────
|
|
117
|
+
const model = options.model ?? resolveModel({ tier: 'standard' });
|
|
118
|
+
log.debug('Model resolved', { hasExplicitModel: !!options.model });
|
|
119
|
+
// ── 2. Build ALL tools ────────────────────────────────────────────────
|
|
120
|
+
let tools = createToolPreset('full', { workspaceRoot });
|
|
121
|
+
// Merge user-provided tools (escape hatch for testing / custom tools)
|
|
122
|
+
if (options.tools) {
|
|
123
|
+
Object.assign(tools, options.tools);
|
|
93
124
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
tier: (roleConfig.recommendedModel ?? 'standard'),
|
|
102
|
-
provider: options.modelProvider,
|
|
103
|
-
modelName: options.modelName,
|
|
125
|
+
log.debug('Base tools built', { count: Object.keys(tools).length });
|
|
126
|
+
// ── 3. Memory — always on ─────────────────────────────────────────────
|
|
127
|
+
const agentStatePath = resolveAgentStatePath(name);
|
|
128
|
+
const memoryStore = new MarkdownMemoryStore({
|
|
129
|
+
projectDir: agentStatePath,
|
|
130
|
+
globalDir: '.agntk',
|
|
131
|
+
workspaceRoot,
|
|
104
132
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
133
|
+
const memoryTools = createMemoryTools({ store: memoryStore, model });
|
|
134
|
+
Object.assign(tools, memoryTools);
|
|
135
|
+
log.info('Memory enabled', { agentStatePath });
|
|
136
|
+
// ── 4. Sub-agents — recursive creation ────────────────────────────────
|
|
137
|
+
if (spawnDepth < DEFAULT_MAX_SPAWN_DEPTH) {
|
|
110
138
|
const spawnTool = createSpawnAgentTool({
|
|
111
|
-
maxSpawnDepth:
|
|
112
|
-
currentDepth:
|
|
139
|
+
maxSpawnDepth: DEFAULT_MAX_SPAWN_DEPTH,
|
|
140
|
+
currentDepth: spawnDepth,
|
|
113
141
|
createAgent: (subAgentOptions) => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
role: subAgentOptions.role,
|
|
117
|
-
});
|
|
142
|
+
const subName = `${name}/${subAgentOptions.role}`;
|
|
143
|
+
log.info('Spawning sub-agent', { parentName: name, subName });
|
|
118
144
|
const subAgent = createAgent({
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
enableSubAgents: false, // Prevent recursion
|
|
145
|
+
name: subName,
|
|
146
|
+
instructions: subAgentOptions.instructions,
|
|
122
147
|
workspaceRoot,
|
|
123
|
-
maxSteps:
|
|
148
|
+
maxSteps: SUB_AGENT_MAX_STEPS,
|
|
149
|
+
model: options.model,
|
|
150
|
+
}, {
|
|
151
|
+
_spawnDepth: spawnDepth + 1,
|
|
124
152
|
});
|
|
125
153
|
return {
|
|
126
154
|
stream: (input) => {
|
|
@@ -132,7 +160,7 @@ export function createAgent(options = {}) {
|
|
|
132
160
|
yield chunk;
|
|
133
161
|
}
|
|
134
162
|
})(),
|
|
135
|
-
text: streamPromise.then(r => r.text),
|
|
163
|
+
text: streamPromise.then((r) => r.text),
|
|
136
164
|
};
|
|
137
165
|
},
|
|
138
166
|
};
|
|
@@ -140,241 +168,166 @@ export function createAgent(options = {}) {
|
|
|
140
168
|
});
|
|
141
169
|
tools = { ...tools, spawn_agent: spawnTool };
|
|
142
170
|
}
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
Object.assign(tools, memoryTools);
|
|
156
|
-
log.info('Memory enabled', {
|
|
157
|
-
projectPath: memoryStore.getProjectPath(),
|
|
158
|
-
globalPath: memoryStore.getGlobalPath(),
|
|
159
|
-
tools: Object.keys(memoryTools),
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
// Apply approval to dangerous tools if configured
|
|
163
|
-
const approvalConfig = resolveApprovalConfig(options.approval);
|
|
164
|
-
if (approvalConfig?.enabled) {
|
|
165
|
-
log.debug('Applying tool approval', { tools: approvalConfig.tools ?? 'default dangerous set' });
|
|
166
|
-
tools = applyApproval(tools, approvalConfig);
|
|
167
|
-
}
|
|
168
|
-
// Wrap tools as durable steps if configured
|
|
169
|
-
if (options.durable) {
|
|
170
|
-
log.debug('Wrapping tools as durable steps', {
|
|
171
|
-
toolCount: Object.keys(tools).length,
|
|
172
|
-
workflowOptions: options.workflowOptions,
|
|
173
|
-
});
|
|
174
|
-
const durableConfig = {
|
|
175
|
-
retryCount: options.workflowOptions?.defaultRetryCount ?? 3,
|
|
176
|
-
};
|
|
177
|
-
tools = wrapToolsAsDurable(tools, durableConfig);
|
|
178
|
-
// Eagerly check workflow availability
|
|
179
|
-
checkWorkflowAvailability().then((available) => {
|
|
180
|
-
if (!available) {
|
|
181
|
-
log.warn('Workflow package not installed. Durable tool wrapping is inert without the runtime. ' +
|
|
182
|
-
'Install with: npm install workflow');
|
|
183
|
-
}
|
|
184
|
-
}).catch(() => { });
|
|
171
|
+
// ── 5. ModelRetry — always on ─────────────────────────────────────────
|
|
172
|
+
tools = wrapAllToolsWithRetry(tools, 3);
|
|
173
|
+
// ── 6. Auto-discover skills ───────────────────────────────────────────
|
|
174
|
+
let skillsPrompt = '';
|
|
175
|
+
try {
|
|
176
|
+
const discovered = discoverSkills(undefined, workspaceRoot);
|
|
177
|
+
const eligible = filterEligibleSkills(discovered);
|
|
178
|
+
if (eligible.length > 0) {
|
|
179
|
+
const loaded = eligible.map((s) => loadSkillContent(s));
|
|
180
|
+
skillsPrompt = buildSkillsSystemPrompt(loaded);
|
|
181
|
+
log.info('Skills discovered', { count: eligible.length });
|
|
182
|
+
}
|
|
185
183
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (maxToolRetries !== 0) {
|
|
189
|
-
tools = wrapAllToolsWithRetry(tools, maxToolRetries);
|
|
190
|
-
log.debug('Tools wrapped with ModelRetry handling', { maxToolRetries: maxToolRetries ?? 3 });
|
|
184
|
+
catch (err) {
|
|
185
|
+
log.warn('Skill discovery failed', { error: err instanceof Error ? err.message : String(err) });
|
|
191
186
|
}
|
|
192
|
-
// Build
|
|
187
|
+
// ── 7. Build system prompt ────────────────────────────────────────────
|
|
188
|
+
let augmentedSystemPrompt = buildBaseInstructions(name, instructions, skillsPrompt);
|
|
189
|
+
// ── 8. Stop conditions ────────────────────────────────────────────────
|
|
193
190
|
const stopConditions = [
|
|
194
191
|
stepCountIs(maxSteps),
|
|
195
192
|
];
|
|
196
193
|
if (options.usageLimits) {
|
|
197
|
-
log.debug('Usage limits configured', { limits: options.usageLimits });
|
|
198
194
|
stopConditions.push(usageLimitStop(options.usageLimits));
|
|
199
195
|
}
|
|
200
|
-
//
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
? createReflectionPrepareStep(augmentedSystemPrompt, reflectionConfig)
|
|
204
|
-
: undefined;
|
|
205
|
-
if (prepareStep) {
|
|
206
|
-
log.debug('Reflection enabled', { strategy: reflectionConfig.strategy });
|
|
207
|
-
}
|
|
208
|
-
// Build telemetry settings (sync — just creates the config object)
|
|
209
|
-
const telemetrySettings = telemetry
|
|
210
|
-
? createTelemetrySettings({
|
|
211
|
-
functionId: telemetry.functionId ?? `agent:${agentId}`,
|
|
212
|
-
metadata: telemetry.metadata,
|
|
213
|
-
})
|
|
214
|
-
: undefined;
|
|
215
|
-
// Create the ToolLoopAgent
|
|
216
|
-
log.debug('Creating ToolLoopAgent', {
|
|
217
|
-
promptLength: augmentedSystemPrompt.length,
|
|
218
|
-
toolCount: Object.keys(tools).length,
|
|
219
|
-
telemetry: !!telemetrySettings,
|
|
196
|
+
// ── 9. Reflection — always on (reflact strategy) ──────────────────────
|
|
197
|
+
const prepareStep = createReflectionPrepareStep(augmentedSystemPrompt, {
|
|
198
|
+
strategy: 'reflact',
|
|
220
199
|
});
|
|
200
|
+
// ── 10. Guardrails — always on (output: PII content filter) ───────────
|
|
201
|
+
const outputGuardrails = [contentFilter()];
|
|
202
|
+
// ── 11. Telemetry — auto-detect ───────────────────────────────────────
|
|
203
|
+
const telemetryEnabled = detectTelemetry();
|
|
204
|
+
const telemetrySettings = telemetryEnabled
|
|
205
|
+
? createTelemetrySettings({ functionId: `agent:${name}` })
|
|
206
|
+
: undefined;
|
|
207
|
+
// ── 12. Build the ToolLoopAgent ───────────────────────────────────────
|
|
221
208
|
const toolLoopAgent = new ToolLoopAgent({
|
|
222
209
|
model,
|
|
223
210
|
instructions: augmentedSystemPrompt,
|
|
224
211
|
tools,
|
|
225
212
|
stopWhen: stopConditions,
|
|
226
|
-
// prepareCall: dynamically inject the current system prompt (may be updated by memory loading)
|
|
227
213
|
prepareCall: (opts) => ({ ...opts, instructions: augmentedSystemPrompt }),
|
|
228
|
-
|
|
214
|
+
prepareStep,
|
|
229
215
|
...(telemetrySettings ? { experimental_telemetry: telemetrySettings } : {}),
|
|
230
216
|
});
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if (
|
|
241
|
-
return
|
|
242
|
-
|
|
217
|
+
log.debug('ToolLoopAgent created', {
|
|
218
|
+
toolCount: Object.keys(tools).length,
|
|
219
|
+
telemetry: !!telemetrySettings,
|
|
220
|
+
});
|
|
221
|
+
// ── Lazy initializers ─────────────────────────────────────────────────
|
|
222
|
+
const agentLog = log.child({ agent: name });
|
|
223
|
+
let initialized = false;
|
|
224
|
+
let initPromise = null;
|
|
225
|
+
async function ensureInit() {
|
|
226
|
+
if (initialized)
|
|
227
|
+
return;
|
|
228
|
+
if (initPromise)
|
|
229
|
+
return initPromise;
|
|
230
|
+
initPromise = (async () => {
|
|
231
|
+
// Load memory context into system prompt
|
|
243
232
|
try {
|
|
244
233
|
const memoryContext = await loadMemoryContext(memoryStore);
|
|
245
234
|
if (memoryContext) {
|
|
246
235
|
augmentedSystemPrompt = memoryContext + '\n\n' + augmentedSystemPrompt;
|
|
247
|
-
// prepareCall() will pick up the updated augmentedSystemPrompt on next generate/stream
|
|
248
236
|
agentLog.debug('Memory context injected', { chars: memoryContext.length });
|
|
249
237
|
}
|
|
250
238
|
}
|
|
251
239
|
catch (err) {
|
|
252
|
-
agentLog.warn('
|
|
240
|
+
agentLog.warn('Memory context loading failed', {
|
|
253
241
|
error: err instanceof Error ? err.message : String(err),
|
|
254
242
|
});
|
|
255
243
|
}
|
|
256
|
-
|
|
257
|
-
|
|
244
|
+
// Inject dynamic environment context
|
|
245
|
+
try {
|
|
246
|
+
augmentedSystemPrompt = await buildDynamicSystemPrompt(augmentedSystemPrompt, {
|
|
247
|
+
workspaceRoot,
|
|
248
|
+
includeWorkspaceMap: true,
|
|
249
|
+
});
|
|
258
250
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
let telemetryInitPromise = null;
|
|
264
|
-
function ensureTelemetryInit() {
|
|
265
|
-
if (!telemetry?.provider)
|
|
266
|
-
return Promise.resolve();
|
|
267
|
-
if (telemetryInitPromise)
|
|
268
|
-
return telemetryInitPromise;
|
|
269
|
-
telemetryInitPromise = initObservability(telemetry.provider)
|
|
270
|
-
.then((ok) => {
|
|
271
|
-
if (ok) {
|
|
272
|
-
agentLog.info('Telemetry initialized', { provider: telemetry.provider.provider });
|
|
251
|
+
catch (err) {
|
|
252
|
+
agentLog.warn('Dynamic context injection failed', {
|
|
253
|
+
error: err instanceof Error ? err.message : String(err),
|
|
254
|
+
});
|
|
273
255
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
256
|
+
// Apply durable wrapping if workflow runtime is available
|
|
257
|
+
try {
|
|
258
|
+
const workflowAvailable = await checkWorkflowAvailability();
|
|
259
|
+
if (workflowAvailable) {
|
|
260
|
+
tools = wrapToolsAsDurable(tools, { retryCount: 3 });
|
|
261
|
+
agentLog.info('Durable tool wrapping active');
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
agentLog.debug('Workflow detection failed — skipping durable wrapping');
|
|
266
|
+
}
|
|
267
|
+
// Initialize telemetry if detected
|
|
268
|
+
if (telemetryEnabled) {
|
|
269
|
+
try {
|
|
270
|
+
await initObservability({ provider: 'langfuse' });
|
|
271
|
+
agentLog.info('Telemetry initialized');
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
agentLog.warn('Telemetry initialization failed', {
|
|
275
|
+
error: err instanceof Error ? err.message : String(err),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
initialized = true;
|
|
280
|
+
})();
|
|
281
|
+
return initPromise;
|
|
281
282
|
}
|
|
282
|
-
//
|
|
283
|
+
// ── Build the agent instance ──────────────────────────────────────────
|
|
283
284
|
const agent = {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
init: async () => {
|
|
287
|
-
await Promise.all([ensureMemoryContext(), ensureTelemetryInit()]);
|
|
288
|
-
},
|
|
289
|
-
getToolLoopAgent: () => toolLoopAgent,
|
|
285
|
+
name,
|
|
286
|
+
init: ensureInit,
|
|
290
287
|
getSystemPrompt: () => augmentedSystemPrompt,
|
|
288
|
+
getToolNames: () => Object.keys(tools),
|
|
291
289
|
stream: async (input) => {
|
|
292
|
-
await
|
|
290
|
+
await ensureInit();
|
|
293
291
|
agentLog.info('stream() called', { promptLength: input.prompt.length });
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
// Log each step with tool calls
|
|
309
|
-
if (result.steps) {
|
|
310
|
-
for (let i = 0; i < result.steps.length; i++) {
|
|
311
|
-
const step = result.steps[i];
|
|
312
|
-
// Log each tool call individually at trace level (full details)
|
|
313
|
-
if (step.toolCalls) {
|
|
314
|
-
for (const tc of step.toolCalls) {
|
|
315
|
-
agentLog.trace(`Tool call: ${tc.toolName}`, {
|
|
316
|
-
tool: tc.toolName,
|
|
317
|
-
input: tc.args ?? tc.input,
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
// Log each tool result individually at trace level (full output)
|
|
322
|
-
if (step.toolResults) {
|
|
323
|
-
for (const tr of step.toolResults) {
|
|
324
|
-
const output = tr.result ?? tr.output ?? '';
|
|
325
|
-
agentLog.trace(`Tool result: ${tr.toolName}`, {
|
|
326
|
-
tool: tr.toolName,
|
|
327
|
-
output: typeof output === 'string' ? output.slice(0, 1000) : output,
|
|
328
|
-
outputLength: typeof output === 'string' ? output.length : undefined,
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
// Summary log for step
|
|
333
|
-
agentLog.debug(`Step ${i + 1}/${result.steps.length}`, {
|
|
334
|
-
toolCalls: step.toolCalls?.map(tc => tc.toolName) ?? [],
|
|
335
|
-
toolResults: step.toolResults?.map(tr => tr.toolName) ?? [],
|
|
336
|
-
textLength: step.text?.length ?? 0,
|
|
292
|
+
const result = await toolLoopAgent.stream({ prompt: input.prompt });
|
|
293
|
+
// Apply output guardrails to the final text
|
|
294
|
+
const guardedText = result.text.then(async (text) => {
|
|
295
|
+
if (!text || outputGuardrails.length === 0)
|
|
296
|
+
return text;
|
|
297
|
+
try {
|
|
298
|
+
const { results, filteredText } = await runGuardrails(outputGuardrails, text, {
|
|
299
|
+
prompt: input.prompt,
|
|
300
|
+
phase: 'output',
|
|
301
|
+
});
|
|
302
|
+
const check = handleGuardrailResults(results, text, filteredText, 'output', 'filter');
|
|
303
|
+
if (check.blocked) {
|
|
304
|
+
agentLog.info('Output guardrails filtered content', {
|
|
305
|
+
guards: results.filter((r) => !r.passed).map((r) => r.name),
|
|
337
306
|
});
|
|
307
|
+
return check.text;
|
|
338
308
|
}
|
|
339
309
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
inputTokens: result.totalUsage.inputTokens,
|
|
344
|
-
outputTokens: result.totalUsage.outputTokens,
|
|
345
|
-
totalTokens: result.totalUsage.totalTokens,
|
|
346
|
-
reasoningTokens: result.totalUsage.reasoningTokens ?? 0,
|
|
347
|
-
cachedInputTokens: result.totalUsage.cachedInputTokens ?? 0,
|
|
310
|
+
catch (err) {
|
|
311
|
+
agentLog.warn('Output guardrails failed', {
|
|
312
|
+
error: err instanceof Error ? err.message : String(err),
|
|
348
313
|
});
|
|
349
314
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
357
|
-
catch (error) {
|
|
358
|
-
done();
|
|
359
|
-
agentLog.error('generate() failed', {
|
|
360
|
-
error: error instanceof Error ? error.message : String(error),
|
|
361
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
362
|
-
});
|
|
363
|
-
throw error;
|
|
364
|
-
}
|
|
315
|
+
return text;
|
|
316
|
+
});
|
|
317
|
+
return {
|
|
318
|
+
fullStream: result.fullStream,
|
|
319
|
+
text: guardedText,
|
|
320
|
+
usage: result.totalUsage,
|
|
321
|
+
};
|
|
365
322
|
},
|
|
366
323
|
};
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const originalGenerate = agent.generate.bind(agent);
|
|
375
|
-
agent.generate = wrapWithGuardrails(originalGenerate, options.guardrails);
|
|
376
|
-
}
|
|
377
|
-
log.info('Agent created', { agentId, role, durable: !!options.durable });
|
|
324
|
+
log.info('Agent created', {
|
|
325
|
+
name,
|
|
326
|
+
spawnDepth,
|
|
327
|
+
toolCount: Object.keys(tools).length,
|
|
328
|
+
memoryPath: agentStatePath,
|
|
329
|
+
telemetry: telemetryEnabled,
|
|
330
|
+
});
|
|
378
331
|
return agent;
|
|
379
332
|
}
|
|
380
333
|
export default createAgent;
|