@agntk/core 0.1.2 → 0.3.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 +251 -280
- package/dist/agent.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +16 -4
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/loader.d.ts +14 -6
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +38 -16
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +52 -52
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +1 -1
- package/dist/config/schema.js.map +1 -1
- 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/guardrails/runner.d.ts.map +1 -1
- package/dist/guardrails/runner.js +4 -0
- package/dist/guardrails/runner.js.map +1 -1
- package/dist/index.d.ts +9 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -10
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +24 -1
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +50 -4
- package/dist/models.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 +3 -3
- package/dist/provider-resolver.d.ts +38 -0
- package/dist/provider-resolver.d.ts.map +1 -0
- package/dist/provider-resolver.js +142 -0
- package/dist/provider-resolver.js.map +1 -0
- package/dist/reflection.d.ts +5 -2
- package/dist/reflection.d.ts.map +1 -1
- package/dist/reflection.js +8 -3
- package/dist/reflection.js.map +1 -1
- package/dist/skills/loader.d.ts +18 -0
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +58 -2
- package/dist/skills/loader.js.map +1 -1
- package/dist/system-detect.d.ts +59 -0
- package/dist/system-detect.d.ts.map +1 -0
- package/dist/system-detect.js +193 -0
- package/dist/system-detect.js.map +1 -0
- package/dist/tools/file/tools.d.ts.map +1 -1
- package/dist/tools/file/tools.js +30 -1
- package/dist/tools/file/tools.js.map +1 -1
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +0 -2
- package/dist/tools/index.js.map +1 -1
- 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 +1 -1
- package/dist/tools/shell/background.d.ts.map +1 -1
- package/dist/tools/shell/background.js +45 -3
- package/dist/tools/shell/background.js.map +1 -1
- package/dist/tools/shell/tools.d.ts.map +1 -1
- package/dist/tools/shell/tools.js +9 -2
- package/dist/tools/shell/tools.js.map +1 -1
- 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/tools/utils/shell.d.ts +14 -0
- package/dist/tools/utils/shell.d.ts.map +1 -1
- package/dist/tools/utils/shell.js +171 -12
- package/dist/tools/utils/shell.js.map +1 -1
- package/dist/types/agent.d.ts +75 -195
- 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/tools/factory.d.ts +0 -109
- package/dist/tools/factory.d.ts.map +0 -1
- package/dist/tools/factory.js +0 -166
- package/dist/tools/factory.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,157 @@
|
|
|
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';
|
|
32
|
+
import { runGuardrails, handleGuardrailResults } from './guardrails/runner.js';
|
|
33
|
+
import { contentFilter } from './guardrails/built-ins.js';
|
|
19
34
|
import { applyApproval, resolveApprovalConfig } from './tools/approval.js';
|
|
20
|
-
import { wrapWithGuardrails } from './guardrails/runner.js';
|
|
21
35
|
import { MarkdownMemoryStore } from './memory/store.js';
|
|
22
36
|
import { loadMemoryContext } from './memory/loader.js';
|
|
23
37
|
import { createMemoryTools } from './memory/tools.js';
|
|
24
38
|
import { initObservability, createTelemetrySettings } from './observability/index.js';
|
|
39
|
+
import { buildDynamicSystemPrompt } from './prompts/context.js';
|
|
25
40
|
// ============================================================================
|
|
26
41
|
// Logger
|
|
27
42
|
// ============================================================================
|
|
28
43
|
const log = createLogger('@agntk/core:agent');
|
|
29
44
|
// ============================================================================
|
|
30
|
-
//
|
|
45
|
+
// Constants
|
|
31
46
|
// ============================================================================
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
const DEFAULT_MAX_STEPS = 25;
|
|
48
|
+
const SUB_AGENT_MAX_STEPS = 15;
|
|
49
|
+
const DEFAULT_MAX_SPAWN_DEPTH = 2;
|
|
50
|
+
const AGENT_STATE_BASE = '.agntk/agents';
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Helpers
|
|
53
|
+
// ============================================================================
|
|
54
|
+
/**
|
|
55
|
+
* Resolve the persistent state directory for a named agent.
|
|
56
|
+
* ~/.agntk/agents/{name}/
|
|
57
|
+
*/
|
|
58
|
+
function resolveAgentStatePath(name) {
|
|
59
|
+
const safeName = name.replace(/[^a-zA-Z0-9_-]/g, '_').toLowerCase();
|
|
60
|
+
return resolve(homedir(), AGENT_STATE_BASE, safeName);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Detect if telemetry should be enabled from env vars.
|
|
64
|
+
*/
|
|
65
|
+
function detectTelemetry() {
|
|
66
|
+
return !!(process.env.LANGFUSE_PUBLIC_KEY &&
|
|
67
|
+
process.env.LANGFUSE_SECRET_KEY);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Build the base instructions for the agent.
|
|
71
|
+
*/
|
|
72
|
+
function buildBaseInstructions(name, userInstructions, skillsPrompt) {
|
|
73
|
+
const parts = [];
|
|
74
|
+
parts.push(`You are ${name}, a capable AI agent.`);
|
|
75
|
+
if (userInstructions) {
|
|
76
|
+
parts.push('');
|
|
77
|
+
parts.push(userInstructions);
|
|
45
78
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
79
|
+
parts.push('');
|
|
80
|
+
parts.push('You have access to a full suite of tools including file operations, ' +
|
|
81
|
+
'shell commands, code search (grep, glob, ast-grep), a browser, ' +
|
|
82
|
+
'deep reasoning, planning, and persistent memory. ' +
|
|
83
|
+
'You can spawn sub-agents for complex tasks that benefit from delegation. ' +
|
|
84
|
+
'Use the remember tool to persist important findings across sessions. ' +
|
|
85
|
+
'Use the recall tool to search your memory for relevant context. ' +
|
|
86
|
+
'If the user\'s request is vague or conversational (e.g., greetings, ' +
|
|
87
|
+
'"whats up", "hello"), respond conversationally without using tools.');
|
|
88
|
+
if (skillsPrompt) {
|
|
89
|
+
parts.push('');
|
|
90
|
+
parts.push(skillsPrompt);
|
|
51
91
|
}
|
|
52
|
-
|
|
53
|
-
return allTools;
|
|
92
|
+
return parts.join('\n');
|
|
54
93
|
}
|
|
55
94
|
// ============================================================================
|
|
56
|
-
//
|
|
95
|
+
// Agent Factory
|
|
57
96
|
// ============================================================================
|
|
58
97
|
/**
|
|
59
|
-
*
|
|
98
|
+
* Create an agent — fully equipped, zero config.
|
|
60
99
|
*
|
|
61
100
|
* @example
|
|
62
101
|
* ```typescript
|
|
63
102
|
* const agent = createAgent({
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* toolPreset: 'standard',
|
|
103
|
+
* name: 'deploy-bot',
|
|
104
|
+
* instructions: 'You manage deployments for our k8s cluster.',
|
|
67
105
|
* });
|
|
68
106
|
*
|
|
69
|
-
* const result = await agent.
|
|
70
|
-
*
|
|
107
|
+
* const result = await agent.stream({ prompt: 'Roll back staging to yesterday' });
|
|
108
|
+
* for await (const chunk of result.fullStream) {
|
|
109
|
+
* if (chunk.type === 'text-delta') process.stdout.write(chunk.text ?? '');
|
|
110
|
+
* }
|
|
71
111
|
* ```
|
|
72
112
|
*/
|
|
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
|
-
}
|
|
113
|
+
export function createAgent(options, _internal = {}) {
|
|
114
|
+
const { name, instructions, workspaceRoot = process.cwd(), } = options;
|
|
115
|
+
const spawnDepth = _internal._spawnDepth ?? 0;
|
|
116
|
+
const isSubAgent = spawnDepth > 0;
|
|
117
|
+
const maxSteps = options.maxSteps ?? (isSubAgent ? SUB_AGENT_MAX_STEPS : DEFAULT_MAX_STEPS);
|
|
118
|
+
log.info('Creating agent', { name, maxSteps, workspaceRoot, spawnDepth });
|
|
119
|
+
// ── 1. Resolve model ──────────────────────────────────────────────────
|
|
120
|
+
const model = options.model ?? resolveModel({ tier: 'standard' });
|
|
121
|
+
log.debug('Model resolved', { hasExplicitModel: !!options.model });
|
|
122
|
+
// ── 2. Build ALL tools ────────────────────────────────────────────────
|
|
123
|
+
let tools = createToolPreset('full', { workspaceRoot });
|
|
124
|
+
// Merge user-provided tools (escape hatch for testing / custom tools)
|
|
125
|
+
if (options.tools) {
|
|
126
|
+
Object.assign(tools, options.tools);
|
|
93
127
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
tier: (roleConfig.recommendedModel ?? 'standard'),
|
|
102
|
-
provider: options.modelProvider,
|
|
103
|
-
modelName: options.modelName,
|
|
128
|
+
log.debug('Base tools built', { count: Object.keys(tools).length });
|
|
129
|
+
// ── 3. Memory — always on ─────────────────────────────────────────────
|
|
130
|
+
const agentStatePath = resolveAgentStatePath(name);
|
|
131
|
+
const memoryStore = new MarkdownMemoryStore({
|
|
132
|
+
projectDir: agentStatePath,
|
|
133
|
+
globalDir: '.agntk',
|
|
134
|
+
workspaceRoot,
|
|
104
135
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
136
|
+
const memoryTools = createMemoryTools({ store: memoryStore, model });
|
|
137
|
+
Object.assign(tools, memoryTools);
|
|
138
|
+
log.info('Memory enabled', { agentStatePath });
|
|
139
|
+
// ── 4. Sub-agents — recursive creation ────────────────────────────────
|
|
140
|
+
if (spawnDepth < DEFAULT_MAX_SPAWN_DEPTH) {
|
|
110
141
|
const spawnTool = createSpawnAgentTool({
|
|
111
|
-
maxSpawnDepth:
|
|
112
|
-
currentDepth:
|
|
142
|
+
maxSpawnDepth: DEFAULT_MAX_SPAWN_DEPTH,
|
|
143
|
+
currentDepth: spawnDepth,
|
|
113
144
|
createAgent: (subAgentOptions) => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
role: subAgentOptions.role,
|
|
117
|
-
});
|
|
145
|
+
const subName = `${name}/${subAgentOptions.role}`;
|
|
146
|
+
log.info('Spawning sub-agent', { parentName: name, subName });
|
|
118
147
|
const subAgent = createAgent({
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
enableSubAgents: false, // Prevent recursion
|
|
148
|
+
name: subName,
|
|
149
|
+
instructions: subAgentOptions.instructions,
|
|
122
150
|
workspaceRoot,
|
|
123
|
-
maxSteps:
|
|
151
|
+
maxSteps: SUB_AGENT_MAX_STEPS,
|
|
152
|
+
model: options.model,
|
|
153
|
+
}, {
|
|
154
|
+
_spawnDepth: spawnDepth + 1,
|
|
124
155
|
});
|
|
125
156
|
return {
|
|
126
157
|
stream: (input) => {
|
|
@@ -132,7 +163,7 @@ export function createAgent(options = {}) {
|
|
|
132
163
|
yield chunk;
|
|
133
164
|
}
|
|
134
165
|
})(),
|
|
135
|
-
text: streamPromise.then(r => r.text),
|
|
166
|
+
text: streamPromise.then((r) => r.text),
|
|
136
167
|
};
|
|
137
168
|
},
|
|
138
169
|
};
|
|
@@ -140,241 +171,181 @@ export function createAgent(options = {}) {
|
|
|
140
171
|
});
|
|
141
172
|
tools = { ...tools, spawn_agent: spawnTool };
|
|
142
173
|
}
|
|
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(() => { });
|
|
174
|
+
// ── 5. ModelRetry — always on ─────────────────────────────────────────
|
|
175
|
+
tools = wrapAllToolsWithRetry(tools, 3);
|
|
176
|
+
// ── 6. Auto-discover skills ───────────────────────────────────────────
|
|
177
|
+
let skillsPrompt = '';
|
|
178
|
+
try {
|
|
179
|
+
const discovered = discoverSkills(undefined, workspaceRoot);
|
|
180
|
+
const eligible = filterEligibleSkills(discovered);
|
|
181
|
+
if (eligible.length > 0) {
|
|
182
|
+
const loaded = eligible.map((s) => loadSkillContent(s));
|
|
183
|
+
skillsPrompt = buildSkillsSystemPrompt(loaded);
|
|
184
|
+
log.info('Skills discovered', { count: eligible.length });
|
|
185
|
+
}
|
|
185
186
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (maxToolRetries !== 0) {
|
|
189
|
-
tools = wrapAllToolsWithRetry(tools, maxToolRetries);
|
|
190
|
-
log.debug('Tools wrapped with ModelRetry handling', { maxToolRetries: maxToolRetries ?? 3 });
|
|
187
|
+
catch (err) {
|
|
188
|
+
log.warn('Skill discovery failed', { error: err instanceof Error ? err.message : String(err) });
|
|
191
189
|
}
|
|
192
|
-
// Build
|
|
190
|
+
// ── 7. Build system prompt ────────────────────────────────────────────
|
|
191
|
+
let augmentedSystemPrompt = buildBaseInstructions(name, instructions, skillsPrompt);
|
|
192
|
+
// ── 8. Stop conditions ────────────────────────────────────────────────
|
|
193
193
|
const stopConditions = [
|
|
194
194
|
stepCountIs(maxSteps),
|
|
195
195
|
];
|
|
196
196
|
if (options.usageLimits) {
|
|
197
|
-
log.debug('Usage limits configured', { limits: options.usageLimits });
|
|
198
197
|
stopConditions.push(usageLimitStop(options.usageLimits));
|
|
199
198
|
}
|
|
200
|
-
//
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
:
|
|
205
|
-
|
|
206
|
-
|
|
199
|
+
// ── 9. Reflection — always on (reflact strategy) ──────────────────────
|
|
200
|
+
// Pass a getter so reflection always reads the current augmentedSystemPrompt,
|
|
201
|
+
// even after memory/context is injected during ensureInit() (fixes DESIGN-001).
|
|
202
|
+
const prepareStep = createReflectionPrepareStep(() => augmentedSystemPrompt, {
|
|
203
|
+
strategy: 'reflact',
|
|
204
|
+
});
|
|
205
|
+
// ── 10. Guardrails — always on (output: PII content filter) ───────────
|
|
206
|
+
const outputGuardrails = [contentFilter()];
|
|
207
|
+
// ── 11. Apply approval wrapping if requested ───────────────────────────
|
|
208
|
+
const approvalConfig = resolveApprovalConfig(options.approval);
|
|
209
|
+
if (approvalConfig) {
|
|
210
|
+
tools = applyApproval(tools, approvalConfig);
|
|
211
|
+
log.info('Approval system enabled', { tools: approvalConfig.tools ?? 'default dangerous tools' });
|
|
207
212
|
}
|
|
208
|
-
//
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
metadata: telemetry.metadata,
|
|
213
|
-
})
|
|
213
|
+
// ── 12. Telemetry — auto-detect ──────────────────────────────────────
|
|
214
|
+
const telemetryEnabled = detectTelemetry();
|
|
215
|
+
const telemetrySettings = telemetryEnabled
|
|
216
|
+
? createTelemetrySettings({ functionId: `agent:${name}` })
|
|
214
217
|
: undefined;
|
|
215
|
-
//
|
|
216
|
-
log.debug('Creating ToolLoopAgent', {
|
|
217
|
-
promptLength: augmentedSystemPrompt.length,
|
|
218
|
-
toolCount: Object.keys(tools).length,
|
|
219
|
-
telemetry: !!telemetrySettings,
|
|
220
|
-
});
|
|
218
|
+
// ── 13. Build the ToolLoopAgent ──────────────────────────────────────
|
|
221
219
|
const toolLoopAgent = new ToolLoopAgent({
|
|
222
220
|
model,
|
|
223
221
|
instructions: augmentedSystemPrompt,
|
|
224
222
|
tools,
|
|
225
223
|
stopWhen: stopConditions,
|
|
226
|
-
// prepareCall: dynamically inject the current system prompt (may be updated by memory loading)
|
|
227
224
|
prepareCall: (opts) => ({ ...opts, instructions: augmentedSystemPrompt }),
|
|
228
|
-
|
|
225
|
+
prepareStep,
|
|
229
226
|
...(telemetrySettings ? { experimental_telemetry: telemetrySettings } : {}),
|
|
230
227
|
});
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if (
|
|
241
|
-
return
|
|
242
|
-
|
|
228
|
+
log.debug('ToolLoopAgent created', {
|
|
229
|
+
toolCount: Object.keys(tools).length,
|
|
230
|
+
telemetry: !!telemetrySettings,
|
|
231
|
+
});
|
|
232
|
+
// ── Lazy initializers ─────────────────────────────────────────────────
|
|
233
|
+
const agentLog = log.child({ agent: name });
|
|
234
|
+
let initialized = false;
|
|
235
|
+
let initPromise = null;
|
|
236
|
+
async function ensureInit() {
|
|
237
|
+
if (initialized)
|
|
238
|
+
return;
|
|
239
|
+
if (initPromise)
|
|
240
|
+
return initPromise;
|
|
241
|
+
initPromise = (async () => {
|
|
243
242
|
try {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
243
|
+
// Load memory context into system prompt
|
|
244
|
+
try {
|
|
245
|
+
const memoryContext = await loadMemoryContext(memoryStore);
|
|
246
|
+
if (memoryContext) {
|
|
247
|
+
augmentedSystemPrompt = memoryContext + '\n\n' + augmentedSystemPrompt;
|
|
248
|
+
agentLog.debug('Memory context injected', { chars: memoryContext.length });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
agentLog.warn('Memory context loading failed', {
|
|
253
|
+
error: err instanceof Error ? err.message : String(err),
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
// Inject dynamic environment context
|
|
257
|
+
try {
|
|
258
|
+
augmentedSystemPrompt = await buildDynamicSystemPrompt(augmentedSystemPrompt, {
|
|
259
|
+
workspaceRoot,
|
|
260
|
+
includeWorkspaceMap: true,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
agentLog.warn('Dynamic context injection failed', {
|
|
265
|
+
error: err instanceof Error ? err.message : String(err),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
// Apply durable wrapping if workflow runtime is available
|
|
269
|
+
try {
|
|
270
|
+
const workflowAvailable = await checkWorkflowAvailability();
|
|
271
|
+
if (workflowAvailable) {
|
|
272
|
+
tools = wrapToolsAsDurable(tools, { retryCount: 3 });
|
|
273
|
+
agentLog.info('Durable tool wrapping active');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
agentLog.debug('Workflow detection failed — skipping durable wrapping');
|
|
249
278
|
}
|
|
279
|
+
// Initialize telemetry if detected
|
|
280
|
+
if (telemetryEnabled) {
|
|
281
|
+
try {
|
|
282
|
+
await initObservability({ provider: 'langfuse' });
|
|
283
|
+
agentLog.info('Telemetry initialized');
|
|
284
|
+
}
|
|
285
|
+
catch (err) {
|
|
286
|
+
agentLog.warn('Telemetry initialization failed', {
|
|
287
|
+
error: err instanceof Error ? err.message : String(err),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
initialized = true;
|
|
250
292
|
}
|
|
251
293
|
catch (err) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
finally {
|
|
257
|
-
memoryContextLoaded = true;
|
|
294
|
+
// E-5: Reset so callers can retry instead of permanently failing
|
|
295
|
+
initPromise = null;
|
|
296
|
+
throw err;
|
|
258
297
|
}
|
|
259
298
|
})();
|
|
260
|
-
return
|
|
299
|
+
return initPromise;
|
|
261
300
|
}
|
|
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 });
|
|
273
|
-
}
|
|
274
|
-
})
|
|
275
|
-
.catch((err) => {
|
|
276
|
-
agentLog.warn('Telemetry initialization failed', {
|
|
277
|
-
error: err instanceof Error ? err.message : String(err),
|
|
278
|
-
});
|
|
279
|
-
});
|
|
280
|
-
return telemetryInitPromise;
|
|
281
|
-
}
|
|
282
|
-
// Create the agent instance
|
|
301
|
+
// ── Build the agent instance ──────────────────────────────────────────
|
|
283
302
|
const agent = {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
init: async () => {
|
|
287
|
-
await Promise.all([ensureMemoryContext(), ensureTelemetryInit()]);
|
|
288
|
-
},
|
|
289
|
-
getToolLoopAgent: () => toolLoopAgent,
|
|
303
|
+
name,
|
|
304
|
+
init: ensureInit,
|
|
290
305
|
getSystemPrompt: () => augmentedSystemPrompt,
|
|
306
|
+
getToolNames: () => Object.keys(tools),
|
|
291
307
|
stream: async (input) => {
|
|
292
|
-
await
|
|
308
|
+
await ensureInit();
|
|
293
309
|
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,
|
|
310
|
+
const result = await toolLoopAgent.stream({ prompt: input.prompt });
|
|
311
|
+
// Apply output guardrails to the final text
|
|
312
|
+
const guardedText = result.text.then(async (text) => {
|
|
313
|
+
if (!text || outputGuardrails.length === 0)
|
|
314
|
+
return text;
|
|
315
|
+
try {
|
|
316
|
+
const { results, filteredText } = await runGuardrails(outputGuardrails, text, {
|
|
317
|
+
prompt: input.prompt,
|
|
318
|
+
phase: 'output',
|
|
319
|
+
});
|
|
320
|
+
const check = handleGuardrailResults(results, text, filteredText, 'output', 'filter');
|
|
321
|
+
if (check.blocked) {
|
|
322
|
+
agentLog.info('Output guardrails filtered content', {
|
|
323
|
+
guards: results.filter((r) => !r.passed).map((r) => r.name),
|
|
337
324
|
});
|
|
325
|
+
return check.text;
|
|
338
326
|
}
|
|
339
327
|
}
|
|
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,
|
|
328
|
+
catch (err) {
|
|
329
|
+
agentLog.warn('Output guardrails failed', {
|
|
330
|
+
error: err instanceof Error ? err.message : String(err),
|
|
348
331
|
});
|
|
349
332
|
}
|
|
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
|
-
}
|
|
333
|
+
return text;
|
|
334
|
+
});
|
|
335
|
+
return {
|
|
336
|
+
fullStream: result.fullStream,
|
|
337
|
+
text: guardedText,
|
|
338
|
+
usage: result.totalUsage,
|
|
339
|
+
};
|
|
365
340
|
},
|
|
366
341
|
};
|
|
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 });
|
|
342
|
+
log.info('Agent created', {
|
|
343
|
+
name,
|
|
344
|
+
spawnDepth,
|
|
345
|
+
toolCount: Object.keys(tools).length,
|
|
346
|
+
memoryPath: agentStatePath,
|
|
347
|
+
telemetry: telemetryEnabled,
|
|
348
|
+
});
|
|
378
349
|
return agent;
|
|
379
350
|
}
|
|
380
351
|
export default createAgent;
|