@build0.ai/agent-core 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/README.md +146 -0
- package/dist/core/log-types.d.ts +73 -0
- package/dist/core/log-types.d.ts.map +1 -0
- package/dist/core/log-types.js +8 -0
- package/dist/core/runner.d.ts +26 -0
- package/dist/core/runner.d.ts.map +1 -0
- package/dist/core/runner.js +214 -0
- package/dist/core/types.d.ts +29 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/services/credential-manager.d.ts +30 -0
- package/dist/services/credential-manager.d.ts.map +1 -0
- package/dist/services/credential-manager.js +94 -0
- package/dist/utils/agent.d.ts +11 -0
- package/dist/utils/agent.d.ts.map +1 -0
- package/dist/utils/agent.js +46 -0
- package/dist/utils/logger.d.ts +29 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +168 -0
- package/dist/utils/workflow.d.ts +12 -0
- package/dist/utils/workflow.d.ts.map +1 -0
- package/dist/utils/workflow.js +5 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# @build0.ai/agent-core
|
|
2
|
+
|
|
3
|
+
Core framework for Build0 autonomous coding agents. This package provides the workflow orchestration engine, plugin system, and utilities needed to build autonomous agents.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @build0.ai/agent-core
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @build0.ai/agent-core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { Runner, credentialManager } from "@build0.ai/agent-core";
|
|
17
|
+
import { myPlugin } from "./plugins/my-plugin.js";
|
|
18
|
+
|
|
19
|
+
async function main() {
|
|
20
|
+
// Fetch credentials from remote API
|
|
21
|
+
const credentials = await credentialManager.fetchCredentials();
|
|
22
|
+
|
|
23
|
+
// Create runner and register plugins
|
|
24
|
+
const runner = new Runner();
|
|
25
|
+
await runner.registerPlugin(myPlugin, {
|
|
26
|
+
API_KEY: credentials.MY_API_KEY!,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Run workflow
|
|
30
|
+
await runner.runWorkflow("./workflow.json");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
main();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- **Declarative Workflows**: Define agent behavior in JSON
|
|
39
|
+
- **Type-Safe Plugin System**: Build plugins with compile-time validation
|
|
40
|
+
- **MCP Tools Integration**: Tools are exposed via Model Context Protocol
|
|
41
|
+
- **Claude Agent SDK**: Leverages Claude for intelligent coding tasks
|
|
42
|
+
- **Credential Management**: Secure remote credential fetching and decryption
|
|
43
|
+
- **Structured Logging**: JSON-formatted logs for easy parsing
|
|
44
|
+
|
|
45
|
+
## Workflow Format
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"steps": [
|
|
50
|
+
{
|
|
51
|
+
"id": "step_1",
|
|
52
|
+
"type": "tool",
|
|
53
|
+
"tool": "my_tool",
|
|
54
|
+
"args": {
|
|
55
|
+
"param": "value"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"id": "step_2",
|
|
60
|
+
"type": "ai_agent",
|
|
61
|
+
"args": {
|
|
62
|
+
"prompt": "Analyze the output: {{ step_1.output }}",
|
|
63
|
+
"working_dir": "./workspace"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Creating Plugins
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { McpPlugin, BasePluginConfig, ToolDefinition } from "@build0.ai/agent-core";
|
|
74
|
+
import { z } from "zod";
|
|
75
|
+
|
|
76
|
+
interface MyPluginConfig extends BasePluginConfig {
|
|
77
|
+
API_KEY: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const myPlugin: McpPlugin<MyPluginConfig> = {
|
|
81
|
+
name: "my_plugin",
|
|
82
|
+
config: {} as MyPluginConfig,
|
|
83
|
+
|
|
84
|
+
async init(config: MyPluginConfig): Promise<void> {
|
|
85
|
+
if (!config.API_KEY) {
|
|
86
|
+
throw new Error("API_KEY is required");
|
|
87
|
+
}
|
|
88
|
+
this.config = config;
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
registerTools(): ToolDefinition[] {
|
|
92
|
+
return [
|
|
93
|
+
{
|
|
94
|
+
name: "my_tool",
|
|
95
|
+
description: "Does something useful",
|
|
96
|
+
zodSchema: z.object({
|
|
97
|
+
param: z.string().describe("A parameter"),
|
|
98
|
+
}),
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
async handleToolCall(name, args) {
|
|
104
|
+
if (name === "my_tool") {
|
|
105
|
+
// Implementation
|
|
106
|
+
return {
|
|
107
|
+
content: [{ type: "text", text: "Result" }],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Environment Variables
|
|
116
|
+
|
|
117
|
+
The framework uses these environment variables:
|
|
118
|
+
|
|
119
|
+
- `BUILD0_AGENT_CREDENTIALS_URL` - Remote credentials API endpoint
|
|
120
|
+
- `BUILD0_AGENT_AUTH_TOKEN` - Authentication token for credentials API
|
|
121
|
+
- `BUILD0_AGENT_ENCRYPTION_KEY` - AES-256 key for credential decryption (hex)
|
|
122
|
+
- `BUILD0_TRIGGER_PAYLOAD` - JSON payload from external triggers (auto-injected as `{{ input }}`)
|
|
123
|
+
- `ANTHROPIC_API_KEY` - API key for Claude (required by Claude Agent SDK)
|
|
124
|
+
|
|
125
|
+
## Exports
|
|
126
|
+
|
|
127
|
+
### Classes
|
|
128
|
+
- `Runner` - Main workflow orchestration engine
|
|
129
|
+
|
|
130
|
+
### Singletons
|
|
131
|
+
- `logger` - Structured JSON logger
|
|
132
|
+
- `credentialManager` - Credential fetching service
|
|
133
|
+
|
|
134
|
+
### Types
|
|
135
|
+
- `McpPlugin<TConfig>` - Plugin interface
|
|
136
|
+
- `BasePluginConfig` - Base plugin configuration
|
|
137
|
+
- `ToolDefinition` - Tool metadata with Zod schema
|
|
138
|
+
- `Workflow` - Workflow definition
|
|
139
|
+
- `WorkflowStep` - Single workflow step
|
|
140
|
+
- `Credential` - Credential structure
|
|
141
|
+
- `AgentResult` - AI agent execution result
|
|
142
|
+
- Various log message types
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
MIT
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { SDKMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
export type LogLevel = "info" | "warn" | "error" | "debug";
|
|
3
|
+
export interface BaseLogMessage {
|
|
4
|
+
timestamp: string;
|
|
5
|
+
level: LogLevel;
|
|
6
|
+
type: string;
|
|
7
|
+
stepId?: string;
|
|
8
|
+
message?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface StepStartMessage extends BaseLogMessage {
|
|
11
|
+
type: "step_start";
|
|
12
|
+
stepId: string;
|
|
13
|
+
stepType: "ai_agent" | "tool";
|
|
14
|
+
toolName?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface StepCompleteMessage extends BaseLogMessage {
|
|
17
|
+
type: "step_complete";
|
|
18
|
+
stepId: string;
|
|
19
|
+
duration?: number;
|
|
20
|
+
outputPreview?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface StepErrorMessage extends BaseLogMessage {
|
|
23
|
+
type: "step_error";
|
|
24
|
+
stepId: string;
|
|
25
|
+
error: string;
|
|
26
|
+
}
|
|
27
|
+
export interface StepSkipMessage extends BaseLogMessage {
|
|
28
|
+
type: "step_skip";
|
|
29
|
+
stepId: string;
|
|
30
|
+
reason: string;
|
|
31
|
+
}
|
|
32
|
+
export interface AiAgentPromptMessage extends BaseLogMessage {
|
|
33
|
+
type: "ai_agent_prompt";
|
|
34
|
+
stepId: string;
|
|
35
|
+
promptLength: number;
|
|
36
|
+
truncated: boolean;
|
|
37
|
+
fullPromptPath?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface AiAgentResponseMessage extends BaseLogMessage {
|
|
40
|
+
type: "ai_agent_response";
|
|
41
|
+
stepId: string;
|
|
42
|
+
responseLength: number;
|
|
43
|
+
responsePreview: string;
|
|
44
|
+
}
|
|
45
|
+
export interface ToolCallMessage extends BaseLogMessage {
|
|
46
|
+
type: "tool_call";
|
|
47
|
+
toolCallId: string;
|
|
48
|
+
toolName: string;
|
|
49
|
+
args: Record<string, any>;
|
|
50
|
+
}
|
|
51
|
+
export interface ToolResultMessage extends BaseLogMessage {
|
|
52
|
+
type: "tool_result";
|
|
53
|
+
toolName: string;
|
|
54
|
+
toolCallId: string;
|
|
55
|
+
resultLength: number;
|
|
56
|
+
resultPreview: string;
|
|
57
|
+
hasJsonResult?: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface ClaudeCodeSdkMessage extends BaseLogMessage {
|
|
60
|
+
type: "claude_code_sdk";
|
|
61
|
+
sdkMessage: SDKMessage;
|
|
62
|
+
}
|
|
63
|
+
export interface ClaudeCodeStderrMessage extends BaseLogMessage {
|
|
64
|
+
type: "claude_code_stderr";
|
|
65
|
+
stderr: string;
|
|
66
|
+
}
|
|
67
|
+
export interface GenericLogMessage extends BaseLogMessage {
|
|
68
|
+
type: "log";
|
|
69
|
+
}
|
|
70
|
+
export type LogMessage = StepStartMessage | StepCompleteMessage | StepErrorMessage | StepSkipMessage | AiAgentPromptMessage | AiAgentResponseMessage | ToolCallMessage | ToolResultMessage | ClaudeCodeSdkMessage | ClaudeCodeStderrMessage | GenericLogMessage;
|
|
71
|
+
export type LogMessageType = LogMessage["type"];
|
|
72
|
+
export declare function createLogMessage<T extends LogMessage>(message: Omit<T, "timestamp">): T;
|
|
73
|
+
//# sourceMappingURL=log-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-types.d.ts","sourceRoot":"","sources":["../../src/core/log-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { McpPlugin } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Framework runner for autonomous agents.
|
|
4
|
+
* Handles workflow execution and plugin management.
|
|
5
|
+
*/
|
|
6
|
+
export declare class Runner {
|
|
7
|
+
private plugins;
|
|
8
|
+
private pluginRegistry;
|
|
9
|
+
/**
|
|
10
|
+
* Register a plugin with its configuration.
|
|
11
|
+
* TypeScript will enforce the config type matches the plugin's requirements.
|
|
12
|
+
*/
|
|
13
|
+
registerPlugin<TConfig extends Record<string, any>>(plugin: McpPlugin<TConfig>, config: TConfig): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Validate that all required tools are available and there are no conflicts.
|
|
16
|
+
*/
|
|
17
|
+
private validateTools;
|
|
18
|
+
private createMcpServer;
|
|
19
|
+
/**
|
|
20
|
+
* Run a workflow from a file.
|
|
21
|
+
* @param workflowPath - Path to the workflow JSON file
|
|
22
|
+
* @param initialContext - Optional additional context to merge
|
|
23
|
+
*/
|
|
24
|
+
runWorkflow(workflowPath: string, initialContext?: Record<string, any>): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAoBvC;;;GAGG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,cAAc,CAAqC;IAE3D;;;OAGG;IACG,cAAc,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACtD,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,EAC1B,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,IAAI,CAAC;IAchB;;OAEG;IACH,OAAO,CAAC,aAAa;IA8CrB,OAAO,CAAC,eAAe;IA8BvB;;;;OAIG;IACG,WAAW,CACf,YAAY,EAAE,MAAM,EACpB,cAAc,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GACvC,OAAO,CAAC,IAAI,CAAC;CAwJjB"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import { loadWorkflow } from "../utils/workflow.js";
|
|
5
|
+
import { runAgent } from "../utils/agent.js";
|
|
6
|
+
import { logger } from "../utils/logger.js";
|
|
7
|
+
import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
8
|
+
function interpolate(text, context) {
|
|
9
|
+
if (typeof text !== "string")
|
|
10
|
+
return text;
|
|
11
|
+
return text.replace(/\{\{\s*([\w\.]+)\s*\}\}/g, (_, path) => {
|
|
12
|
+
const keys = path.split(".");
|
|
13
|
+
let value = context;
|
|
14
|
+
for (const key of keys) {
|
|
15
|
+
value = value?.[key];
|
|
16
|
+
}
|
|
17
|
+
// If value is an object, stringify it. If string, return as is.
|
|
18
|
+
return value !== undefined
|
|
19
|
+
? typeof value === "object"
|
|
20
|
+
? JSON.stringify(value, null, 2)
|
|
21
|
+
: String(value)
|
|
22
|
+
: "";
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Framework runner for autonomous agents.
|
|
27
|
+
* Handles workflow execution and plugin management.
|
|
28
|
+
*/
|
|
29
|
+
export class Runner {
|
|
30
|
+
plugins = [];
|
|
31
|
+
pluginRegistry = new Map();
|
|
32
|
+
/**
|
|
33
|
+
* Register a plugin with its configuration.
|
|
34
|
+
* TypeScript will enforce the config type matches the plugin's requirements.
|
|
35
|
+
*/
|
|
36
|
+
async registerPlugin(plugin, config) {
|
|
37
|
+
await plugin.init(config);
|
|
38
|
+
this.plugins.push(plugin);
|
|
39
|
+
this.pluginRegistry.set(plugin.name, plugin);
|
|
40
|
+
logger.info(`Plugin ${plugin.name} registered with ${plugin.registerTools().length} tools: ${plugin
|
|
41
|
+
.registerTools()
|
|
42
|
+
.map((t) => t.name)
|
|
43
|
+
.join(", ")}`);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Validate that all required tools are available and there are no conflicts.
|
|
47
|
+
*/
|
|
48
|
+
validateTools(workflow) {
|
|
49
|
+
// Build a map of all available tools from registered plugins
|
|
50
|
+
const availableTools = new Map(); // toolName -> pluginName
|
|
51
|
+
for (const plugin of this.plugins) {
|
|
52
|
+
const tools = plugin.registerTools();
|
|
53
|
+
for (const tool of tools) {
|
|
54
|
+
if (availableTools.has(tool.name)) {
|
|
55
|
+
const existingPlugin = availableTools.get(tool.name);
|
|
56
|
+
throw new Error(`Duplicate tool name detected: "${tool.name}" is provided by both "${existingPlugin}" and "${plugin.name}" plugins`);
|
|
57
|
+
}
|
|
58
|
+
availableTools.set(tool.name, plugin.name);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Check that all tools referenced in workflow steps are available
|
|
62
|
+
const missingTools = [];
|
|
63
|
+
for (const step of workflow.steps) {
|
|
64
|
+
if (step.type === "tool" && step.tool) {
|
|
65
|
+
if (!availableTools.has(step.tool)) {
|
|
66
|
+
missingTools.push(step.tool);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (missingTools.length > 0) {
|
|
71
|
+
throw new Error(`Workflow references tools that are not available: ${missingTools.join(", ")}\n` +
|
|
72
|
+
`Available tools: ${Array.from(availableTools.keys()).join(", ")}\n` +
|
|
73
|
+
`Make sure the required plugins are registered in index.ts`);
|
|
74
|
+
}
|
|
75
|
+
logger.info(`✅ All required workflow tools are available:\n${Array.from(availableTools.keys())
|
|
76
|
+
.map((t) => `- ${t}`)
|
|
77
|
+
.join("\n")}`);
|
|
78
|
+
}
|
|
79
|
+
createMcpServer() {
|
|
80
|
+
const tools = [];
|
|
81
|
+
for (const plugin of this.plugins) {
|
|
82
|
+
const pluginTools = plugin.registerTools();
|
|
83
|
+
for (const pluginTool of pluginTools) {
|
|
84
|
+
tools.push(tool(pluginTool.name, pluginTool.description, pluginTool.zodSchema.shape, async (args) => {
|
|
85
|
+
return await plugin.handleToolCall(pluginTool.name, args);
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const server = createSdkMcpServer({
|
|
90
|
+
name: "agent-tools",
|
|
91
|
+
version: "1.0.0",
|
|
92
|
+
tools,
|
|
93
|
+
});
|
|
94
|
+
logger.info(`✅ MCP server created with ${tools.length} tools:\n${tools
|
|
95
|
+
.map((t) => `- ${t.name}`)
|
|
96
|
+
.join("\n")}`);
|
|
97
|
+
return server;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Run a workflow from a file.
|
|
101
|
+
* @param workflowPath - Path to the workflow JSON file
|
|
102
|
+
* @param initialContext - Optional additional context to merge
|
|
103
|
+
*/
|
|
104
|
+
async runWorkflow(workflowPath, initialContext = {}) {
|
|
105
|
+
const workflow = await loadWorkflow(workflowPath);
|
|
106
|
+
logger.info(`Workflow loaded (${workflow.steps.length} steps)`);
|
|
107
|
+
// Validate tools before running
|
|
108
|
+
this.validateTools(workflow);
|
|
109
|
+
const mcpServer = this.createMcpServer();
|
|
110
|
+
// Build initial context with trigger payload from environment
|
|
111
|
+
const context = { ...initialContext };
|
|
112
|
+
// Automatically inject BUILD0_TRIGGER_PAYLOAD as 'input' if present
|
|
113
|
+
if (process.env.BUILD0_TRIGGER_PAYLOAD) {
|
|
114
|
+
try {
|
|
115
|
+
const triggerPayload = JSON.parse(process.env.BUILD0_TRIGGER_PAYLOAD);
|
|
116
|
+
context.input = triggerPayload;
|
|
117
|
+
logger.info(`Trigger payload loaded from BUILD0_TRIGGER_PAYLOAD`);
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
logger.error(`Failed to parse BUILD0_TRIGGER_PAYLOAD: ${e}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
let isFirstAiAgentStep = true;
|
|
124
|
+
for (const step of workflow.steps) {
|
|
125
|
+
logger.stepStart(step.id, step.type, step.tool);
|
|
126
|
+
// Check 'if' condition
|
|
127
|
+
if (step.if) {
|
|
128
|
+
const condition = interpolate(step.if, context);
|
|
129
|
+
if (!condition || condition === "false") {
|
|
130
|
+
logger.stepSkip(step.id, "condition false");
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
switch (step.type) {
|
|
136
|
+
case "ai_agent":
|
|
137
|
+
// Extract and interpolate args for ai_agent
|
|
138
|
+
const agentArgs = {};
|
|
139
|
+
if (step.args) {
|
|
140
|
+
for (const [k, v] of Object.entries(step.args)) {
|
|
141
|
+
agentArgs[k] =
|
|
142
|
+
typeof v === "string" ? interpolate(v, context) : v;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const fullPrompt = agentArgs.prompt || "";
|
|
146
|
+
const workingDir = path.resolve(process.cwd(), agentArgs.working_dir || ".");
|
|
147
|
+
// Write full prompt to a temp file OUTSIDE the working directory
|
|
148
|
+
// to avoid Claude Code scanning it automatically
|
|
149
|
+
const tempPromptFile = path.join(os.tmpdir(), `prompt_${step.id}_${Date.now()}.txt`);
|
|
150
|
+
// Truncate prompt to ~2000 tokens (1 token ≈ 4 chars -> 8000 chars)
|
|
151
|
+
const MAX_CHARS = 2000 * 4;
|
|
152
|
+
await fs.writeFile(tempPromptFile, fullPrompt, "utf-8");
|
|
153
|
+
logger.aiAgentPrompt(step.id, fullPrompt.length, fullPrompt.length > MAX_CHARS, tempPromptFile);
|
|
154
|
+
let truncatedPrompt = fullPrompt.substring(0, MAX_CHARS);
|
|
155
|
+
if (fullPrompt.length > MAX_CHARS) {
|
|
156
|
+
truncatedPrompt += `\n\n[Note: The full prompt was truncated. I have saved the complete details to the file '${tempPromptFile}' (outside the workspace). You can read it if needed, but it is large.]`;
|
|
157
|
+
}
|
|
158
|
+
const result = await runAgent({
|
|
159
|
+
prompt: truncatedPrompt,
|
|
160
|
+
workingDirectory: workingDir,
|
|
161
|
+
mcpServers: {
|
|
162
|
+
"agent-tools": mcpServer,
|
|
163
|
+
},
|
|
164
|
+
shouldContinuePreviousSession: !isFirstAiAgentStep,
|
|
165
|
+
});
|
|
166
|
+
isFirstAiAgentStep = false;
|
|
167
|
+
logger.aiAgentResponse(step.id, result.output.length, result.output.substring(0, 100));
|
|
168
|
+
context[step.id] = { output: result.output };
|
|
169
|
+
break;
|
|
170
|
+
case "tool":
|
|
171
|
+
const toolName = step.tool;
|
|
172
|
+
if (!toolName)
|
|
173
|
+
throw new Error("Tool name missing");
|
|
174
|
+
// Find plugin that provides this tool
|
|
175
|
+
const plugin = this.plugins.find((p) => p.registerTools().some((t) => t.name === toolName));
|
|
176
|
+
if (!plugin)
|
|
177
|
+
throw new Error(`Plugin for tool ${toolName} not found`);
|
|
178
|
+
// Interpolate args
|
|
179
|
+
const args = {};
|
|
180
|
+
if (step.args) {
|
|
181
|
+
for (const [k, v] of Object.entries(step.args)) {
|
|
182
|
+
args[k] =
|
|
183
|
+
typeof v === "string" ? interpolate(v, context) : v;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const toolCallId = `${step.id}_${toolName}_${Date.now()}`;
|
|
187
|
+
logger.toolCall(toolName, toolCallId, args);
|
|
188
|
+
const toolResult = await plugin.handleToolCall(toolName, args);
|
|
189
|
+
// Extract text content
|
|
190
|
+
const outputText = toolResult.content
|
|
191
|
+
.map((c) => c.text)
|
|
192
|
+
.join("\n");
|
|
193
|
+
logger.toolResult(toolName, toolCallId, outputText.length, outputText.substring(0, 100), true);
|
|
194
|
+
// Try to parse JSON if possible for easier access
|
|
195
|
+
let outputData = outputText;
|
|
196
|
+
try {
|
|
197
|
+
outputData = JSON.parse(outputText);
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
// ignore
|
|
201
|
+
}
|
|
202
|
+
context[step.id] = { output: outputData };
|
|
203
|
+
// Tool result already logged above
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
logger.stepComplete(step.id);
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
logger.stepError(step.id, error instanceof Error ? error.message : String(error));
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { z, ZodRawShape } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* Base configuration provided to all plugins.
|
|
5
|
+
* Contains credentials fetched from remote API.
|
|
6
|
+
*/
|
|
7
|
+
export interface BasePluginConfig {
|
|
8
|
+
[key: string]: string | undefined;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Tool definition with Zod schema for MCP SDK compatibility
|
|
12
|
+
*/
|
|
13
|
+
export interface ToolDefinition {
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
zodSchema: z.ZodObject<ZodRawShape>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generic plugin interface.
|
|
20
|
+
* Each plugin should define its own TConfig interface with required keys.
|
|
21
|
+
*/
|
|
22
|
+
export interface McpPlugin<TConfig extends BasePluginConfig = BasePluginConfig> {
|
|
23
|
+
name: string;
|
|
24
|
+
config?: TConfig;
|
|
25
|
+
init(config: TConfig): Promise<void>;
|
|
26
|
+
registerTools(): ToolDefinition[];
|
|
27
|
+
handleToolCall(name: string, args: Record<string, unknown>): Promise<CallToolResult>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAErC;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;CACrC;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS,CACxB,OAAO,SAAS,gBAAgB,GAAG,gBAAgB;IAEnD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,aAAa,IAAI,cAAc,EAAE,CAAC;IAClC,cAAc,CACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,cAAc,CAAC,CAAC;CAC5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { Runner } from "./core/runner.js";
|
|
2
|
+
export type { McpPlugin, BasePluginConfig, ToolDefinition, } from "./core/types.js";
|
|
3
|
+
export type { Workflow, WorkflowStep } from "./utils/workflow.js";
|
|
4
|
+
export { logger } from "./utils/logger.js";
|
|
5
|
+
export type { LogMessage, LogLevel, LogMessageType, BaseLogMessage, StepStartMessage, StepCompleteMessage, StepErrorMessage, StepSkipMessage, AiAgentPromptMessage, AiAgentResponseMessage, ToolCallMessage, ToolResultMessage, ClaudeCodeSdkMessage, ClaudeCodeStderrMessage, GenericLogMessage, } from "./core/log-types.js";
|
|
6
|
+
export { credentialManager } from "./services/credential-manager.js";
|
|
7
|
+
export type { Credential } from "./services/credential-manager.js";
|
|
8
|
+
export type { AgentResult } from "./utils/agent.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,YAAY,EACV,SAAS,EACT,gBAAgB,EAChB,cAAc,GACf,MAAM,iBAAiB,CAAC;AAGzB,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGlE,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,YAAY,EACV,UAAU,EACV,QAAQ,EACR,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,YAAY,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAGnE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface Credential {
|
|
2
|
+
type: string;
|
|
3
|
+
provider: string;
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
access_token?: string;
|
|
6
|
+
raw?: any;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Centralized credential management service.
|
|
10
|
+
* Fetches, decrypts, and caches credentials from remote API.
|
|
11
|
+
* Provides a singleton instance that all parts of the framework can use.
|
|
12
|
+
*/
|
|
13
|
+
declare class CredentialManager {
|
|
14
|
+
private credentials;
|
|
15
|
+
private fetchPromise;
|
|
16
|
+
/**
|
|
17
|
+
* Fetch credentials from remote API and cache them.
|
|
18
|
+
* Ensures credentials are only fetched once even if called multiple times.
|
|
19
|
+
* Returns raw credentials - transformation to env vars happens in index.ts
|
|
20
|
+
*/
|
|
21
|
+
fetchCredentials(): Promise<Record<string, Credential>>;
|
|
22
|
+
private _performFetch;
|
|
23
|
+
/**
|
|
24
|
+
* Get cached raw credentials. Returns empty object if not yet fetched.
|
|
25
|
+
*/
|
|
26
|
+
getCredentials(): Record<string, Credential>;
|
|
27
|
+
}
|
|
28
|
+
export declare const credentialManager: CredentialManager;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=credential-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-manager.d.ts","sourceRoot":"","sources":["../../src/services/credential-manager.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,GAAG,CAAC;CACX;AAyBD;;;;GAIG;AACH,cAAM,iBAAiB;IACrB,OAAO,CAAC,WAAW,CAA2C;IAC9D,OAAO,CAAC,YAAY,CAAoD;IAExE;;;;OAIG;IACG,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;YAsB/C,aAAa;IA8C3B;;OAEG;IACH,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC;CAG7C;AAGD,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
import { logger } from "../utils/logger.js";
|
|
3
|
+
const ALGORITHM = "aes-256-gcm";
|
|
4
|
+
const CREDENTIALS_AUTH_KEY = "agent-credentials";
|
|
5
|
+
function decryptCredentials(encryptedData) {
|
|
6
|
+
const encryptionKey = process.env.BUILD0_AGENT_ENCRYPTION_KEY;
|
|
7
|
+
if (!encryptionKey) {
|
|
8
|
+
throw new Error("BUILD0_ENCRYPTION_KEY environment variable is not set. Cannot decrypt credentials.");
|
|
9
|
+
}
|
|
10
|
+
const parts = encryptedData.split(":");
|
|
11
|
+
if (parts.length !== 3) {
|
|
12
|
+
throw new Error("Invalid encrypted data format");
|
|
13
|
+
}
|
|
14
|
+
const [ivHex, authTagHex, encrypted] = parts;
|
|
15
|
+
const key = Buffer.from(encryptionKey, "hex");
|
|
16
|
+
const iv = Buffer.from(ivHex, "hex");
|
|
17
|
+
const authTag = Buffer.from(authTagHex, "hex");
|
|
18
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
|
|
19
|
+
decipher.setAuthTag(authTag);
|
|
20
|
+
decipher.setAAD(Buffer.from(CREDENTIALS_AUTH_KEY));
|
|
21
|
+
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
22
|
+
decrypted += decipher.final("utf8");
|
|
23
|
+
return JSON.parse(decrypted);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Centralized credential management service.
|
|
27
|
+
* Fetches, decrypts, and caches credentials from remote API.
|
|
28
|
+
* Provides a singleton instance that all parts of the framework can use.
|
|
29
|
+
*/
|
|
30
|
+
class CredentialManager {
|
|
31
|
+
credentials = null;
|
|
32
|
+
fetchPromise = null;
|
|
33
|
+
/**
|
|
34
|
+
* Fetch credentials from remote API and cache them.
|
|
35
|
+
* Ensures credentials are only fetched once even if called multiple times.
|
|
36
|
+
* Returns raw credentials - transformation to env vars happens in index.ts
|
|
37
|
+
*/
|
|
38
|
+
async fetchCredentials() {
|
|
39
|
+
// If already fetched, return cached credentials
|
|
40
|
+
if (this.credentials !== null) {
|
|
41
|
+
return this.credentials;
|
|
42
|
+
}
|
|
43
|
+
// If fetch is in progress, wait for it
|
|
44
|
+
if (this.fetchPromise) {
|
|
45
|
+
return this.fetchPromise;
|
|
46
|
+
}
|
|
47
|
+
// Start new fetch
|
|
48
|
+
this.fetchPromise = this._performFetch();
|
|
49
|
+
this.credentials = await this.fetchPromise;
|
|
50
|
+
logger.info(`✅ Credentials fetched successfully:\n${Object.keys(this.credentials)
|
|
51
|
+
.map((key) => `- ${key}`)
|
|
52
|
+
.join("\n")}`);
|
|
53
|
+
return this.credentials;
|
|
54
|
+
}
|
|
55
|
+
async _performFetch() {
|
|
56
|
+
const credentialsUrl = process.env.BUILD0_AGENT_CREDENTIALS_URL;
|
|
57
|
+
const authToken = process.env.BUILD0_AGENT_AUTH_TOKEN;
|
|
58
|
+
if (!credentialsUrl) {
|
|
59
|
+
throw new Error("BUILD0_AGENT_CREDENTIALS_URL environment variable is not set.");
|
|
60
|
+
}
|
|
61
|
+
if (!authToken) {
|
|
62
|
+
throw new Error("BUILD0_AGENT_AUTH_TOKEN environment variable is not set.");
|
|
63
|
+
}
|
|
64
|
+
console.log(`[Credentials] Fetching remote credentials from ${credentialsUrl}...`);
|
|
65
|
+
try {
|
|
66
|
+
const response = await fetch(credentialsUrl, {
|
|
67
|
+
headers: { "x-agent-auth-token": authToken },
|
|
68
|
+
});
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
throw new Error(`Failed to fetch credentials: ${response.statusText}`);
|
|
71
|
+
}
|
|
72
|
+
const data = await response.json();
|
|
73
|
+
if (!data.encrypted) {
|
|
74
|
+
throw new Error("Invalid response format: missing 'encrypted' field");
|
|
75
|
+
}
|
|
76
|
+
const rawCredentials = decryptCredentials(data.encrypted);
|
|
77
|
+
console.log("[Credentials] Successfully decrypted credentials");
|
|
78
|
+
// Return raw credentials - transformation happens in index.ts
|
|
79
|
+
return rawCredentials;
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.error("[Credentials] Error fetching/decrypting credentials:", error instanceof Error ? error.message : JSON.stringify(error));
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get cached raw credentials. Returns empty object if not yet fetched.
|
|
88
|
+
*/
|
|
89
|
+
getCredentials() {
|
|
90
|
+
return this.credentials || {};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Singleton instance
|
|
94
|
+
export const credentialManager = new CredentialManager();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { McpSdkServerConfigWithInstance } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
export interface AgentResult {
|
|
3
|
+
output: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function runAgent(params: {
|
|
6
|
+
prompt: string;
|
|
7
|
+
workingDirectory: string;
|
|
8
|
+
mcpServers: Record<string, McpSdkServerConfigWithInstance>;
|
|
9
|
+
shouldContinuePreviousSession?: boolean;
|
|
10
|
+
}): Promise<AgentResult>;
|
|
11
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/utils/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAG/B,MAAM,gCAAgC,CAAC;AAGxC,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,QAAQ,CAAC,MAAM,EAAE;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,8BAA8B,CAAC,CAAC;IAC3D,6BAA6B,CAAC,EAAE,OAAO,CAAC;CACzC,GAAG,OAAO,CAAC,WAAW,CAAC,CAsDvB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { query, } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
import { logger } from "./logger.js";
|
|
3
|
+
export async function runAgent(params) {
|
|
4
|
+
// Capture stderr to see actual errors from Claude Code process
|
|
5
|
+
const stderrMessages = [];
|
|
6
|
+
const stderrCallback = (message) => {
|
|
7
|
+
stderrMessages.push(message);
|
|
8
|
+
logger.claudeCodeStderr(message);
|
|
9
|
+
};
|
|
10
|
+
const stream = query({
|
|
11
|
+
prompt: params.prompt,
|
|
12
|
+
options: {
|
|
13
|
+
continue: params.shouldContinuePreviousSession ?? false,
|
|
14
|
+
cwd: params.workingDirectory,
|
|
15
|
+
// We can use the default executable (node)
|
|
16
|
+
mcpServers: params.mcpServers,
|
|
17
|
+
// Enable all tools by default
|
|
18
|
+
permissionMode: "bypassPermissions",
|
|
19
|
+
stderr: stderrCallback,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
let finalResult = null;
|
|
23
|
+
try {
|
|
24
|
+
for await (const message of stream) {
|
|
25
|
+
logger.claudeCodeSdkMessage(message);
|
|
26
|
+
if (message.type === "result") {
|
|
27
|
+
finalResult = message;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
logger.claudeCodeStderr(error instanceof Error ? error.message : String(error));
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
if (finalResult) {
|
|
36
|
+
if (finalResult.subtype === "success") {
|
|
37
|
+
return {
|
|
38
|
+
output: finalResult.result,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
throw new Error(`Error: ${finalResult.subtype}. Message: ${JSON.stringify(finalResult.errors, null, 2)}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
throw new Error("No final result received");
|
|
46
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { SDKMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
declare class Logger {
|
|
3
|
+
private currentStepId?;
|
|
4
|
+
private originalConsoleLog;
|
|
5
|
+
private originalConsoleWarn;
|
|
6
|
+
private originalConsoleError;
|
|
7
|
+
private originalConsoleDebug;
|
|
8
|
+
constructor();
|
|
9
|
+
private overrideConsole;
|
|
10
|
+
private log;
|
|
11
|
+
private createMessage;
|
|
12
|
+
stepStart(stepId: string, stepType: "ai_agent" | "tool", toolName?: string): void;
|
|
13
|
+
stepComplete(stepId: string, duration?: number, outputPreview?: string): void;
|
|
14
|
+
stepError(stepId: string, error: string): void;
|
|
15
|
+
stepSkip(stepId: string, reason: string): void;
|
|
16
|
+
aiAgentPrompt(stepId: string, promptLength: number, truncated: boolean, fullPromptPath?: string): void;
|
|
17
|
+
aiAgentResponse(stepId: string, responseLength: number, responsePreview: string): void;
|
|
18
|
+
toolCall(toolName: string, toolCallId: string, args: Record<string, any>): void;
|
|
19
|
+
toolResult(toolName: string, toolCallId: string, resultLength: number, resultPreview: string, hasJsonResult?: boolean): void;
|
|
20
|
+
claudeCodeSdkMessage(message: SDKMessage): void;
|
|
21
|
+
claudeCodeStderr(stderr: string): void;
|
|
22
|
+
info(message: string, data?: Record<string, any>): void;
|
|
23
|
+
warn(message: string, data?: Record<string, any>): void;
|
|
24
|
+
error(message: string, data?: Record<string, any>): void;
|
|
25
|
+
debug(message: string, data?: Record<string, any>): void;
|
|
26
|
+
}
|
|
27
|
+
export declare const logger: Logger;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAiB5D,cAAM,MAAM;IACV,OAAO,CAAC,aAAa,CAAC,CAAS;IAC/B,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,oBAAoB,CAAuB;;IAanD,OAAO,CAAC,eAAe;IAgCvB,OAAO,CAAC,GAAG;IAKX,OAAO,CAAC,aAAa;IAcrB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IAW1E,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM;IAWtE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAUvC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAUvC,aAAa,CACX,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,OAAO,EAClB,cAAc,CAAC,EAAE,MAAM;IAYzB,eAAe,CACb,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,MAAM;IAWzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAUxE,UAAU,CACR,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,aAAa,CAAC,EAAE,OAAO;IAazB,oBAAoB,CAAC,OAAO,EAAE,UAAU;IAQxC,gBAAgB,CAAC,MAAM,EAAE,MAAM;IAW/B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAWhD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAWhD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAWjD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;CAUlD;AAED,eAAO,MAAM,MAAM,QAAe,CAAC"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// Logger utility for structured JSON logging
|
|
2
|
+
class Logger {
|
|
3
|
+
currentStepId;
|
|
4
|
+
originalConsoleLog;
|
|
5
|
+
originalConsoleWarn;
|
|
6
|
+
originalConsoleError;
|
|
7
|
+
originalConsoleDebug;
|
|
8
|
+
constructor() {
|
|
9
|
+
// Store original console methods
|
|
10
|
+
this.originalConsoleLog = console.log.bind(console);
|
|
11
|
+
this.originalConsoleWarn = console.warn.bind(console);
|
|
12
|
+
this.originalConsoleError = console.error.bind(console);
|
|
13
|
+
this.originalConsoleDebug = console.debug.bind(console);
|
|
14
|
+
// Override console methods to automatically include step ID
|
|
15
|
+
this.overrideConsole();
|
|
16
|
+
}
|
|
17
|
+
overrideConsole() {
|
|
18
|
+
const self = this;
|
|
19
|
+
console.log = (...args) => {
|
|
20
|
+
const message = args
|
|
21
|
+
.map((arg) => (typeof arg === "string" ? arg : JSON.stringify(arg)))
|
|
22
|
+
.join(" ");
|
|
23
|
+
self.info(message);
|
|
24
|
+
};
|
|
25
|
+
console.warn = (...args) => {
|
|
26
|
+
const message = args
|
|
27
|
+
.map((arg) => (typeof arg === "string" ? arg : JSON.stringify(arg)))
|
|
28
|
+
.join(" ");
|
|
29
|
+
self.warn(message);
|
|
30
|
+
};
|
|
31
|
+
console.error = (...args) => {
|
|
32
|
+
const message = args
|
|
33
|
+
.map((arg) => (typeof arg === "string" ? arg : JSON.stringify(arg)))
|
|
34
|
+
.join(" ");
|
|
35
|
+
self.error(message);
|
|
36
|
+
};
|
|
37
|
+
console.debug = (...args) => {
|
|
38
|
+
const message = args
|
|
39
|
+
.map((arg) => (typeof arg === "string" ? arg : JSON.stringify(arg)))
|
|
40
|
+
.join(" ");
|
|
41
|
+
self.debug(message);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
log(message) {
|
|
45
|
+
// Use original console.log to avoid recursion
|
|
46
|
+
this.originalConsoleLog(JSON.stringify(message));
|
|
47
|
+
}
|
|
48
|
+
createMessage(type, level, data) {
|
|
49
|
+
return {
|
|
50
|
+
timestamp: new Date().toISOString(),
|
|
51
|
+
level,
|
|
52
|
+
type,
|
|
53
|
+
stepId: this.currentStepId,
|
|
54
|
+
...data,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
stepStart(stepId, stepType, toolName) {
|
|
58
|
+
this.currentStepId = stepId;
|
|
59
|
+
this.log(this.createMessage("step_start", "info", {
|
|
60
|
+
stepId,
|
|
61
|
+
stepType,
|
|
62
|
+
toolName,
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
stepComplete(stepId, duration, outputPreview) {
|
|
66
|
+
this.log(this.createMessage("step_complete", "info", {
|
|
67
|
+
stepId,
|
|
68
|
+
duration,
|
|
69
|
+
outputPreview,
|
|
70
|
+
}));
|
|
71
|
+
this.currentStepId = undefined;
|
|
72
|
+
}
|
|
73
|
+
stepError(stepId, error) {
|
|
74
|
+
this.log(this.createMessage("step_error", "error", {
|
|
75
|
+
stepId,
|
|
76
|
+
error,
|
|
77
|
+
}));
|
|
78
|
+
this.currentStepId = undefined;
|
|
79
|
+
}
|
|
80
|
+
stepSkip(stepId, reason) {
|
|
81
|
+
this.log(this.createMessage("step_skip", "warn", {
|
|
82
|
+
stepId,
|
|
83
|
+
reason,
|
|
84
|
+
}));
|
|
85
|
+
this.currentStepId = undefined;
|
|
86
|
+
}
|
|
87
|
+
aiAgentPrompt(stepId, promptLength, truncated, fullPromptPath) {
|
|
88
|
+
this.log(this.createMessage("ai_agent_prompt", "info", {
|
|
89
|
+
stepId,
|
|
90
|
+
promptLength,
|
|
91
|
+
truncated,
|
|
92
|
+
fullPromptPath,
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
aiAgentResponse(stepId, responseLength, responsePreview) {
|
|
96
|
+
this.log(this.createMessage("ai_agent_response", "info", {
|
|
97
|
+
stepId,
|
|
98
|
+
responseLength,
|
|
99
|
+
responsePreview,
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
toolCall(toolName, toolCallId, args) {
|
|
103
|
+
this.log(this.createMessage("tool_call", "info", {
|
|
104
|
+
toolName,
|
|
105
|
+
toolCallId,
|
|
106
|
+
args,
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
toolResult(toolName, toolCallId, resultLength, resultPreview, hasJsonResult) {
|
|
110
|
+
this.log(this.createMessage("tool_result", "info", {
|
|
111
|
+
toolName,
|
|
112
|
+
toolCallId,
|
|
113
|
+
resultLength,
|
|
114
|
+
resultPreview,
|
|
115
|
+
hasJsonResult,
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
claudeCodeSdkMessage(message) {
|
|
119
|
+
this.log(this.createMessage("claude_code_sdk", "info", {
|
|
120
|
+
sdkMessage: message,
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
claudeCodeStderr(stderr) {
|
|
124
|
+
this.log(this.createMessage("claude_code_stderr", "error", { stderr }));
|
|
125
|
+
}
|
|
126
|
+
// Generic log methods that automatically include current step ID
|
|
127
|
+
info(message, data) {
|
|
128
|
+
this.log({
|
|
129
|
+
timestamp: new Date().toISOString(),
|
|
130
|
+
level: "info",
|
|
131
|
+
type: "log",
|
|
132
|
+
message,
|
|
133
|
+
stepId: this.currentStepId ?? undefined,
|
|
134
|
+
...data,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
warn(message, data) {
|
|
138
|
+
this.log({
|
|
139
|
+
timestamp: new Date().toISOString(),
|
|
140
|
+
level: "warn",
|
|
141
|
+
type: "log",
|
|
142
|
+
message,
|
|
143
|
+
stepId: this.currentStepId ?? undefined,
|
|
144
|
+
...data,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
error(message, data) {
|
|
148
|
+
this.log({
|
|
149
|
+
timestamp: new Date().toISOString(),
|
|
150
|
+
level: "error",
|
|
151
|
+
type: "log",
|
|
152
|
+
message,
|
|
153
|
+
stepId: this.currentStepId ?? undefined,
|
|
154
|
+
...data,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
debug(message, data) {
|
|
158
|
+
this.log({
|
|
159
|
+
timestamp: new Date().toISOString(),
|
|
160
|
+
level: "debug",
|
|
161
|
+
type: "log",
|
|
162
|
+
message,
|
|
163
|
+
stepId: this.currentStepId ?? undefined,
|
|
164
|
+
...data,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export const logger = new Logger();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface WorkflowStep {
|
|
2
|
+
id: string;
|
|
3
|
+
type: "ai_agent" | "tool";
|
|
4
|
+
tool?: string;
|
|
5
|
+
args?: Record<string, any>;
|
|
6
|
+
if?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface Workflow {
|
|
9
|
+
steps: WorkflowStep[];
|
|
10
|
+
}
|
|
11
|
+
export declare function loadWorkflow(filePath: string): Promise<Workflow>;
|
|
12
|
+
//# sourceMappingURL=workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/utils/workflow.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAGtE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@build0.ai/agent-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Core framework for Build0 autonomous coding agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@anthropic-ai/claude-agent-sdk": "^0.1.54",
|
|
24
|
+
"@modelcontextprotocol/sdk": "^1.0.1",
|
|
25
|
+
"zod": "^3.22.4"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@anthropic-ai/sdk": "^0.71.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^20.11.0",
|
|
32
|
+
"typescript": "^5.3.3"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"ai",
|
|
36
|
+
"agent",
|
|
37
|
+
"claude",
|
|
38
|
+
"anthropic",
|
|
39
|
+
"autonomous",
|
|
40
|
+
"mcp"
|
|
41
|
+
],
|
|
42
|
+
"license": "MIT"
|
|
43
|
+
}
|