@build0.ai/agent-core 0.2.0 → 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/dist/core/log-types.d.ts +1 -1
- package/dist/core/log-types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/plugins/github-polling.d.ts +31 -0
- package/dist/plugins/github-polling.d.ts.map +1 -0
- package/dist/plugins/github-polling.js +330 -0
- package/dist/plugins/index.d.ts +11 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +8 -0
- package/dist/plugins/slack-polling.d.ts +28 -0
- package/dist/plugins/slack-polling.d.ts.map +1 -0
- package/dist/plugins/slack-polling.js +207 -0
- package/dist/run-step.d.ts +31 -0
- package/dist/run-step.d.ts.map +1 -0
- package/dist/run-step.js +290 -0
- package/dist/utils/logger.d.ts +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/workflow.d.ts +17 -1
- package/dist/utils/workflow.d.ts.map +1 -1
- package/dist/utils/workflow.js +19 -0
- package/package.json +4 -1
package/dist/core/log-types.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface StepStartMessage extends BaseLogMessage {
|
|
|
11
11
|
type: "step_start";
|
|
12
12
|
stepId: string;
|
|
13
13
|
stepName?: string;
|
|
14
|
-
stepType: "ai_agent" | "tool";
|
|
14
|
+
stepType: "ai_agent" | "tool" | "sleep";
|
|
15
15
|
toolName?: string;
|
|
16
16
|
}
|
|
17
17
|
export interface StepCompleteMessage extends BaseLogMessage {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-types.d.ts","sourceRoot":"","sources":["../../src/core/log-types.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAE5D,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAGD,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,UAAU,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"log-types.d.ts","sourceRoot":"","sources":["../../src/core/log-types.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { Runner } from "./core/runner.js";
|
|
2
1
|
export type { McpPlugin, BasePluginConfig, ToolDefinition, } from "./core/types.js";
|
|
3
|
-
export type { Workflow, WorkflowStep } from "./utils/workflow.js";
|
|
2
|
+
export type { Workflow, WorkflowStep, WorkflowConfig } from "./utils/workflow.js";
|
|
3
|
+
export { loadWorkflow, parseDuration } from "./utils/workflow.js";
|
|
4
4
|
export { logger } from "./utils/logger.js";
|
|
5
5
|
export type { LogMessage, LogLevel, LogMessageType, BaseLogMessage, StepStartMessage, StepCompleteMessage, StepErrorMessage, StepSkipMessage, AiAgentPromptMessage, AiAgentResponseMessage, ToolCallMessage, ToolResultMessage, ClaudeCodeSdkMessage, ClaudeCodeStderrMessage, GenericLogMessage, } from "./core/log-types.js";
|
|
6
6
|
export { credentialManager } from "./services/credential-manager.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,SAAS,EACT,gBAAgB,EAChB,cAAc,GACf,MAAM,iBAAiB,CAAC;AAGzB,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,aAAa,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
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Polling Plugin
|
|
3
|
+
*
|
|
4
|
+
* Provides tools for polling GitHub for PR updates, comments, commits, etc.
|
|
5
|
+
* Used by long-running agents that need to monitor PR lifecycle.
|
|
6
|
+
*/
|
|
7
|
+
import { McpPlugin, ToolDefinition, BasePluginConfig } from "../core/types.js";
|
|
8
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
export interface GitHubPollingConfig extends BasePluginConfig {
|
|
10
|
+
/** GitHub personal access token or installation token */
|
|
11
|
+
token: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class GitHubPollingPlugin implements McpPlugin<GitHubPollingConfig> {
|
|
14
|
+
name: string;
|
|
15
|
+
private token;
|
|
16
|
+
private baseUrl;
|
|
17
|
+
init(config: GitHubPollingConfig): Promise<void>;
|
|
18
|
+
registerTools(): ToolDefinition[];
|
|
19
|
+
handleToolCall(name: string, args: Record<string, unknown>): Promise<CallToolResult>;
|
|
20
|
+
private fetch;
|
|
21
|
+
private getPr;
|
|
22
|
+
private getPrComments;
|
|
23
|
+
private getPrCommits;
|
|
24
|
+
private getPrFiles;
|
|
25
|
+
private getPrReviews;
|
|
26
|
+
private createPrComment;
|
|
27
|
+
private createReview;
|
|
28
|
+
private getFileContent;
|
|
29
|
+
}
|
|
30
|
+
export declare const githubPollingPlugin: GitHubPollingPlugin;
|
|
31
|
+
//# sourceMappingURL=github-polling.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-polling.d.ts","sourceRoot":"","sources":["../../src/plugins/github-polling.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEpE,MAAM,WAAW,mBAAoB,SAAQ,gBAAgB;IAC3D,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;CACf;AAgCD,qBAAa,mBAAoB,YAAW,SAAS,CAAC,mBAAmB,CAAC;IACxE,IAAI,SAAoB;IACxB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,OAAO,CAA4B;IAErC,IAAI,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAOtD,aAAa,IAAI,cAAc,EAAE;IAkF3B,cAAc,CAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,cAAc,CAAC;YA4DZ,KAAK;YAmBL,KAAK;YAoCL,aAAa;YAoCb,YAAY;YA4BZ,UAAU;YAsCV,YAAY;YAmCZ,eAAe;YAiCf,YAAY;YAkCZ,cAAc;CA6C7B;AAED,eAAO,MAAM,mBAAmB,qBAA4B,CAAC"}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Polling Plugin
|
|
3
|
+
*
|
|
4
|
+
* Provides tools for polling GitHub for PR updates, comments, commits, etc.
|
|
5
|
+
* Used by long-running agents that need to monitor PR lifecycle.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
export class GitHubPollingPlugin {
|
|
9
|
+
name = "github-polling";
|
|
10
|
+
token = "";
|
|
11
|
+
baseUrl = "https://api.github.com";
|
|
12
|
+
async init(config) {
|
|
13
|
+
if (!config.token) {
|
|
14
|
+
throw new Error("GitHub token is required for github-polling plugin");
|
|
15
|
+
}
|
|
16
|
+
this.token = config.token;
|
|
17
|
+
}
|
|
18
|
+
registerTools() {
|
|
19
|
+
return [
|
|
20
|
+
{
|
|
21
|
+
name: "github_get_pr",
|
|
22
|
+
description: "Get details of a pull request including title, state, body, author, and URLs",
|
|
23
|
+
zodSchema: z.object({
|
|
24
|
+
owner: z.string().describe("Repository owner"),
|
|
25
|
+
repo: z.string().describe("Repository name"),
|
|
26
|
+
pr_number: z.number().describe("Pull request number"),
|
|
27
|
+
}),
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "github_get_pr_comments",
|
|
31
|
+
description: "Get comments on a pull request, optionally filtered by time",
|
|
32
|
+
zodSchema: z.object({
|
|
33
|
+
owner: z.string().describe("Repository owner"),
|
|
34
|
+
repo: z.string().describe("Repository name"),
|
|
35
|
+
pr_number: z.number().describe("Pull request number"),
|
|
36
|
+
since: z.string().optional().describe("Only return comments updated after this ISO timestamp"),
|
|
37
|
+
}),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "github_get_pr_commits",
|
|
41
|
+
description: "Get commits on a pull request",
|
|
42
|
+
zodSchema: z.object({
|
|
43
|
+
owner: z.string().describe("Repository owner"),
|
|
44
|
+
repo: z.string().describe("Repository name"),
|
|
45
|
+
pr_number: z.number().describe("Pull request number"),
|
|
46
|
+
}),
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "github_get_pr_files",
|
|
50
|
+
description: "Get the list of files changed in a pull request",
|
|
51
|
+
zodSchema: z.object({
|
|
52
|
+
owner: z.string().describe("Repository owner"),
|
|
53
|
+
repo: z.string().describe("Repository name"),
|
|
54
|
+
pr_number: z.number().describe("Pull request number"),
|
|
55
|
+
}),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "github_get_pr_reviews",
|
|
59
|
+
description: "Get reviews on a pull request",
|
|
60
|
+
zodSchema: z.object({
|
|
61
|
+
owner: z.string().describe("Repository owner"),
|
|
62
|
+
repo: z.string().describe("Repository name"),
|
|
63
|
+
pr_number: z.number().describe("Pull request number"),
|
|
64
|
+
}),
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "github_create_pr_comment",
|
|
68
|
+
description: "Create a comment on a pull request",
|
|
69
|
+
zodSchema: z.object({
|
|
70
|
+
owner: z.string().describe("Repository owner"),
|
|
71
|
+
repo: z.string().describe("Repository name"),
|
|
72
|
+
pr_number: z.number().describe("Pull request number"),
|
|
73
|
+
body: z.string().describe("Comment body in markdown"),
|
|
74
|
+
}),
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "github_create_review",
|
|
78
|
+
description: "Create a review on a pull request",
|
|
79
|
+
zodSchema: z.object({
|
|
80
|
+
owner: z.string().describe("Repository owner"),
|
|
81
|
+
repo: z.string().describe("Repository name"),
|
|
82
|
+
pr_number: z.number().describe("Pull request number"),
|
|
83
|
+
body: z.string().describe("Review body in markdown"),
|
|
84
|
+
event: z.enum(["APPROVE", "REQUEST_CHANGES", "COMMENT"]).describe("Review action"),
|
|
85
|
+
}),
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "github_get_file_content",
|
|
89
|
+
description: "Get the content of a file from the repository",
|
|
90
|
+
zodSchema: z.object({
|
|
91
|
+
owner: z.string().describe("Repository owner"),
|
|
92
|
+
repo: z.string().describe("Repository name"),
|
|
93
|
+
path: z.string().describe("File path in the repository"),
|
|
94
|
+
ref: z.string().optional().describe("Branch, tag, or commit SHA (default: default branch)"),
|
|
95
|
+
}),
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
async handleToolCall(name, args) {
|
|
100
|
+
try {
|
|
101
|
+
switch (name) {
|
|
102
|
+
case "github_get_pr":
|
|
103
|
+
return await this.getPr(args);
|
|
104
|
+
case "github_get_pr_comments":
|
|
105
|
+
return await this.getPrComments(args);
|
|
106
|
+
case "github_get_pr_commits":
|
|
107
|
+
return await this.getPrCommits(args);
|
|
108
|
+
case "github_get_pr_files":
|
|
109
|
+
return await this.getPrFiles(args);
|
|
110
|
+
case "github_get_pr_reviews":
|
|
111
|
+
return await this.getPrReviews(args);
|
|
112
|
+
case "github_create_pr_comment":
|
|
113
|
+
return await this.createPrComment(args);
|
|
114
|
+
case "github_create_review":
|
|
115
|
+
return await this.createReview(args);
|
|
116
|
+
case "github_get_file_content":
|
|
117
|
+
return await this.getFileContent(args);
|
|
118
|
+
default:
|
|
119
|
+
return {
|
|
120
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
121
|
+
isError: true,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
return {
|
|
127
|
+
content: [
|
|
128
|
+
{
|
|
129
|
+
type: "text",
|
|
130
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
isError: true,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async fetch(endpoint, options) {
|
|
138
|
+
const response = await fetch(`${this.baseUrl}${endpoint}`, {
|
|
139
|
+
...options,
|
|
140
|
+
headers: {
|
|
141
|
+
Authorization: `Bearer ${this.token}`,
|
|
142
|
+
Accept: "application/vnd.github.v3+json",
|
|
143
|
+
"User-Agent": "Build0-Agent",
|
|
144
|
+
...options?.headers,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
if (!response.ok) {
|
|
148
|
+
const error = await response.text();
|
|
149
|
+
throw new Error(`GitHub API error: ${response.status} - ${error}`);
|
|
150
|
+
}
|
|
151
|
+
return response;
|
|
152
|
+
}
|
|
153
|
+
async getPr(args) {
|
|
154
|
+
const { owner, repo, pr_number } = args;
|
|
155
|
+
const response = await this.fetch(`/repos/${owner}/${repo}/pulls/${pr_number}`);
|
|
156
|
+
const pr = (await response.json());
|
|
157
|
+
return {
|
|
158
|
+
content: [
|
|
159
|
+
{
|
|
160
|
+
type: "text",
|
|
161
|
+
text: JSON.stringify({
|
|
162
|
+
number: pr.number,
|
|
163
|
+
title: pr.title,
|
|
164
|
+
state: pr.state,
|
|
165
|
+
merged: pr.merged,
|
|
166
|
+
author: pr.user.login,
|
|
167
|
+
head_sha: pr.head.sha,
|
|
168
|
+
head_ref: pr.head.ref,
|
|
169
|
+
base_ref: pr.base.ref,
|
|
170
|
+
body: pr.body,
|
|
171
|
+
created_at: pr.created_at,
|
|
172
|
+
updated_at: pr.updated_at,
|
|
173
|
+
url: pr.html_url,
|
|
174
|
+
}, null, 2),
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async getPrComments(args) {
|
|
180
|
+
const { owner, repo, pr_number, since } = args;
|
|
181
|
+
let endpoint = `/repos/${owner}/${repo}/issues/${pr_number}/comments`;
|
|
182
|
+
if (since) {
|
|
183
|
+
endpoint += `?since=${encodeURIComponent(since)}`;
|
|
184
|
+
}
|
|
185
|
+
const response = await this.fetch(endpoint);
|
|
186
|
+
const comments = (await response.json());
|
|
187
|
+
return {
|
|
188
|
+
content: [
|
|
189
|
+
{
|
|
190
|
+
type: "text",
|
|
191
|
+
text: JSON.stringify(comments.map((c) => ({
|
|
192
|
+
id: c.id,
|
|
193
|
+
author: c.user.login,
|
|
194
|
+
body: c.body,
|
|
195
|
+
created_at: c.created_at,
|
|
196
|
+
updated_at: c.updated_at,
|
|
197
|
+
url: c.html_url,
|
|
198
|
+
})), null, 2),
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
async getPrCommits(args) {
|
|
204
|
+
const { owner, repo, pr_number } = args;
|
|
205
|
+
const response = await this.fetch(`/repos/${owner}/${repo}/pulls/${pr_number}/commits`);
|
|
206
|
+
const commits = (await response.json());
|
|
207
|
+
return {
|
|
208
|
+
content: [
|
|
209
|
+
{
|
|
210
|
+
type: "text",
|
|
211
|
+
text: JSON.stringify(commits.map((c) => ({
|
|
212
|
+
sha: c.sha,
|
|
213
|
+
message: c.commit.message,
|
|
214
|
+
author: c.commit.author.name,
|
|
215
|
+
date: c.commit.author.date,
|
|
216
|
+
})), null, 2),
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
async getPrFiles(args) {
|
|
222
|
+
const { owner, repo, pr_number } = args;
|
|
223
|
+
const response = await this.fetch(`/repos/${owner}/${repo}/pulls/${pr_number}/files`);
|
|
224
|
+
const files = (await response.json());
|
|
225
|
+
return {
|
|
226
|
+
content: [
|
|
227
|
+
{
|
|
228
|
+
type: "text",
|
|
229
|
+
text: JSON.stringify(files.map((f) => ({
|
|
230
|
+
filename: f.filename,
|
|
231
|
+
status: f.status,
|
|
232
|
+
additions: f.additions,
|
|
233
|
+
deletions: f.deletions,
|
|
234
|
+
changes: f.changes,
|
|
235
|
+
// Truncate patch if too long
|
|
236
|
+
patch: f.patch?.substring(0, 2000),
|
|
237
|
+
})), null, 2),
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
async getPrReviews(args) {
|
|
243
|
+
const { owner, repo, pr_number } = args;
|
|
244
|
+
const response = await this.fetch(`/repos/${owner}/${repo}/pulls/${pr_number}/reviews`);
|
|
245
|
+
const reviews = (await response.json());
|
|
246
|
+
return {
|
|
247
|
+
content: [
|
|
248
|
+
{
|
|
249
|
+
type: "text",
|
|
250
|
+
text: JSON.stringify(reviews.map((r) => ({
|
|
251
|
+
id: r.id,
|
|
252
|
+
author: r.user.login,
|
|
253
|
+
state: r.state,
|
|
254
|
+
body: r.body,
|
|
255
|
+
submitted_at: r.submitted_at,
|
|
256
|
+
})), null, 2),
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
async createPrComment(args) {
|
|
262
|
+
const { owner, repo, pr_number, body } = args;
|
|
263
|
+
const response = await this.fetch(`/repos/${owner}/${repo}/issues/${pr_number}/comments`, {
|
|
264
|
+
method: "POST",
|
|
265
|
+
headers: { "Content-Type": "application/json" },
|
|
266
|
+
body: JSON.stringify({ body }),
|
|
267
|
+
});
|
|
268
|
+
const comment = (await response.json());
|
|
269
|
+
return {
|
|
270
|
+
content: [
|
|
271
|
+
{
|
|
272
|
+
type: "text",
|
|
273
|
+
text: JSON.stringify({
|
|
274
|
+
id: comment.id,
|
|
275
|
+
url: comment.html_url,
|
|
276
|
+
created_at: comment.created_at,
|
|
277
|
+
}, null, 2),
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
async createReview(args) {
|
|
283
|
+
const { owner, repo, pr_number, body, event } = args;
|
|
284
|
+
const response = await this.fetch(`/repos/${owner}/${repo}/pulls/${pr_number}/reviews`, {
|
|
285
|
+
method: "POST",
|
|
286
|
+
headers: { "Content-Type": "application/json" },
|
|
287
|
+
body: JSON.stringify({ body, event }),
|
|
288
|
+
});
|
|
289
|
+
const review = (await response.json());
|
|
290
|
+
return {
|
|
291
|
+
content: [
|
|
292
|
+
{
|
|
293
|
+
type: "text",
|
|
294
|
+
text: JSON.stringify({
|
|
295
|
+
id: review.id,
|
|
296
|
+
state: review.state,
|
|
297
|
+
url: review.html_url,
|
|
298
|
+
}, null, 2),
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
async getFileContent(args) {
|
|
304
|
+
const { owner, repo, path, ref } = args;
|
|
305
|
+
let endpoint = `/repos/${owner}/${repo}/contents/${path}`;
|
|
306
|
+
if (ref) {
|
|
307
|
+
endpoint += `?ref=${encodeURIComponent(ref)}`;
|
|
308
|
+
}
|
|
309
|
+
const response = await this.fetch(endpoint);
|
|
310
|
+
const file = (await response.json());
|
|
311
|
+
// Decode base64 content
|
|
312
|
+
const content = file.encoding === "base64"
|
|
313
|
+
? Buffer.from(file.content, "base64").toString("utf-8")
|
|
314
|
+
: file.content;
|
|
315
|
+
return {
|
|
316
|
+
content: [
|
|
317
|
+
{
|
|
318
|
+
type: "text",
|
|
319
|
+
text: JSON.stringify({
|
|
320
|
+
name: file.name,
|
|
321
|
+
path: file.path,
|
|
322
|
+
size: file.size,
|
|
323
|
+
content: content.substring(0, 50000), // Limit content size
|
|
324
|
+
}, null, 2),
|
|
325
|
+
},
|
|
326
|
+
],
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
export const githubPollingPlugin = new GitHubPollingPlugin();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common plugins for Build0 agents.
|
|
3
|
+
*
|
|
4
|
+
* These plugins provide reusable tools for interacting with external services.
|
|
5
|
+
* Agent templates can import and register these plugins, or create their own.
|
|
6
|
+
*/
|
|
7
|
+
export { GitHubPollingPlugin, githubPollingPlugin } from "./github-polling.js";
|
|
8
|
+
export type { GitHubPollingConfig } from "./github-polling.js";
|
|
9
|
+
export { SlackPollingPlugin, slackPollingPlugin } from "./slack-polling.js";
|
|
10
|
+
export type { SlackPollingConfig } from "./slack-polling.js";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common plugins for Build0 agents.
|
|
3
|
+
*
|
|
4
|
+
* These plugins provide reusable tools for interacting with external services.
|
|
5
|
+
* Agent templates can import and register these plugins, or create their own.
|
|
6
|
+
*/
|
|
7
|
+
export { GitHubPollingPlugin, githubPollingPlugin } from "./github-polling.js";
|
|
8
|
+
export { SlackPollingPlugin, slackPollingPlugin } from "./slack-polling.js";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack Polling Plugin
|
|
3
|
+
*
|
|
4
|
+
* Provides tools for interacting with Slack threads.
|
|
5
|
+
* Used by agents that need to maintain conversations with users via Slack.
|
|
6
|
+
*/
|
|
7
|
+
import { McpPlugin, ToolDefinition, BasePluginConfig } from "../core/types.js";
|
|
8
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
export interface SlackPollingConfig extends BasePluginConfig {
|
|
10
|
+
/** Slack bot token (xoxb-...) */
|
|
11
|
+
token: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class SlackPollingPlugin implements McpPlugin<SlackPollingConfig> {
|
|
14
|
+
name: string;
|
|
15
|
+
private token;
|
|
16
|
+
private baseUrl;
|
|
17
|
+
init(config: SlackPollingConfig): Promise<void>;
|
|
18
|
+
registerTools(): ToolDefinition[];
|
|
19
|
+
handleToolCall(name: string, args: Record<string, unknown>): Promise<CallToolResult>;
|
|
20
|
+
private fetch;
|
|
21
|
+
private postMessage;
|
|
22
|
+
private getThreadMessages;
|
|
23
|
+
private getUserInfo;
|
|
24
|
+
private addReaction;
|
|
25
|
+
private updateMessage;
|
|
26
|
+
}
|
|
27
|
+
export declare const slackPollingPlugin: SlackPollingPlugin;
|
|
28
|
+
//# sourceMappingURL=slack-polling.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-polling.d.ts","sourceRoot":"","sources":["../../src/plugins/slack-polling.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEpE,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC1D,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;CACf;AAgBD,qBAAa,kBAAmB,YAAW,SAAS,CAAC,kBAAkB,CAAC;IACtE,IAAI,SAAmB;IACvB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,OAAO,CAA2B;IAEpC,IAAI,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAOrD,aAAa,IAAI,cAAc,EAAE;IAgD3B,cAAc,CAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,cAAc,CAAC;YA6CZ,KAAK;YAyBL,WAAW;YAkCX,iBAAiB;YAsCjB,WAAW;YAwBX,WAAW;YAmBX,aAAa;CA2B5B;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAC"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack Polling Plugin
|
|
3
|
+
*
|
|
4
|
+
* Provides tools for interacting with Slack threads.
|
|
5
|
+
* Used by agents that need to maintain conversations with users via Slack.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
export class SlackPollingPlugin {
|
|
9
|
+
name = "slack-polling";
|
|
10
|
+
token = "";
|
|
11
|
+
baseUrl = "https://slack.com/api";
|
|
12
|
+
async init(config) {
|
|
13
|
+
if (!config.token) {
|
|
14
|
+
throw new Error("Slack token is required for slack-polling plugin");
|
|
15
|
+
}
|
|
16
|
+
this.token = config.token;
|
|
17
|
+
}
|
|
18
|
+
registerTools() {
|
|
19
|
+
return [
|
|
20
|
+
{
|
|
21
|
+
name: "slack_post_message",
|
|
22
|
+
description: "Post a message to a Slack channel. Returns the thread_ts for replies.",
|
|
23
|
+
zodSchema: z.object({
|
|
24
|
+
channel: z.string().describe("Channel ID (e.g., C1234567890)"),
|
|
25
|
+
text: z.string().describe("Message text (supports Slack markdown)"),
|
|
26
|
+
thread_ts: z.string().optional().describe("Thread timestamp to reply to"),
|
|
27
|
+
}),
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "slack_get_thread_messages",
|
|
31
|
+
description: "Get all messages in a Slack thread",
|
|
32
|
+
zodSchema: z.object({
|
|
33
|
+
channel: z.string().describe("Channel ID"),
|
|
34
|
+
thread_ts: z.string().describe("Thread timestamp (ts of the parent message)"),
|
|
35
|
+
oldest: z.string().optional().describe("Only return messages after this timestamp"),
|
|
36
|
+
}),
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "slack_get_user_info",
|
|
40
|
+
description: "Get information about a Slack user",
|
|
41
|
+
zodSchema: z.object({
|
|
42
|
+
user_id: z.string().describe("User ID (e.g., U1234567890)"),
|
|
43
|
+
}),
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "slack_add_reaction",
|
|
47
|
+
description: "Add a reaction emoji to a message",
|
|
48
|
+
zodSchema: z.object({
|
|
49
|
+
channel: z.string().describe("Channel ID"),
|
|
50
|
+
timestamp: z.string().describe("Message timestamp"),
|
|
51
|
+
name: z.string().describe("Emoji name without colons (e.g., 'thumbsup')"),
|
|
52
|
+
}),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "slack_update_message",
|
|
56
|
+
description: "Update an existing message",
|
|
57
|
+
zodSchema: z.object({
|
|
58
|
+
channel: z.string().describe("Channel ID"),
|
|
59
|
+
ts: z.string().describe("Timestamp of the message to update"),
|
|
60
|
+
text: z.string().describe("New message text"),
|
|
61
|
+
}),
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
async handleToolCall(name, args) {
|
|
66
|
+
try {
|
|
67
|
+
switch (name) {
|
|
68
|
+
case "slack_post_message":
|
|
69
|
+
return await this.postMessage(args);
|
|
70
|
+
case "slack_get_thread_messages":
|
|
71
|
+
return await this.getThreadMessages(args);
|
|
72
|
+
case "slack_get_user_info":
|
|
73
|
+
return await this.getUserInfo(args);
|
|
74
|
+
case "slack_add_reaction":
|
|
75
|
+
return await this.addReaction(args);
|
|
76
|
+
case "slack_update_message":
|
|
77
|
+
return await this.updateMessage(args);
|
|
78
|
+
default:
|
|
79
|
+
return {
|
|
80
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
81
|
+
isError: true,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return {
|
|
87
|
+
content: [
|
|
88
|
+
{
|
|
89
|
+
type: "text",
|
|
90
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
isError: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async fetch(method, params) {
|
|
98
|
+
const response = await fetch(`${this.baseUrl}/${method}`, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: {
|
|
101
|
+
Authorization: `Bearer ${this.token}`,
|
|
102
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
103
|
+
},
|
|
104
|
+
body: JSON.stringify(params),
|
|
105
|
+
});
|
|
106
|
+
const data = (await response.json());
|
|
107
|
+
if (!data.ok) {
|
|
108
|
+
throw new Error(`Slack API error: ${data.error || "Unknown error"}`);
|
|
109
|
+
}
|
|
110
|
+
return data;
|
|
111
|
+
}
|
|
112
|
+
async postMessage(args) {
|
|
113
|
+
const { channel, text, thread_ts } = args;
|
|
114
|
+
const params = { channel, text };
|
|
115
|
+
if (thread_ts) {
|
|
116
|
+
params.thread_ts = thread_ts;
|
|
117
|
+
}
|
|
118
|
+
const data = await this.fetch("chat.postMessage", params);
|
|
119
|
+
return {
|
|
120
|
+
content: [
|
|
121
|
+
{
|
|
122
|
+
type: "text",
|
|
123
|
+
text: JSON.stringify({
|
|
124
|
+
ok: true,
|
|
125
|
+
channel: data.channel,
|
|
126
|
+
ts: data.ts,
|
|
127
|
+
thread_ts: data.message?.thread_ts || data.ts,
|
|
128
|
+
message: "Message posted successfully",
|
|
129
|
+
}, null, 2),
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
async getThreadMessages(args) {
|
|
135
|
+
const { channel, thread_ts, oldest } = args;
|
|
136
|
+
const params = { channel, ts: thread_ts };
|
|
137
|
+
if (oldest) {
|
|
138
|
+
params.oldest = oldest;
|
|
139
|
+
}
|
|
140
|
+
const data = await this.fetch("conversations.replies", params);
|
|
141
|
+
const messages = data.messages;
|
|
142
|
+
return {
|
|
143
|
+
content: [
|
|
144
|
+
{
|
|
145
|
+
type: "text",
|
|
146
|
+
text: JSON.stringify({
|
|
147
|
+
thread_ts,
|
|
148
|
+
message_count: messages.length,
|
|
149
|
+
messages: messages.map((m) => ({
|
|
150
|
+
ts: m.ts,
|
|
151
|
+
user: m.user,
|
|
152
|
+
text: m.text,
|
|
153
|
+
is_parent: m.ts === thread_ts,
|
|
154
|
+
})),
|
|
155
|
+
}, null, 2),
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
async getUserInfo(args) {
|
|
161
|
+
const { user_id } = args;
|
|
162
|
+
const data = await this.fetch("users.info", { user: user_id });
|
|
163
|
+
const user = data.user;
|
|
164
|
+
return {
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
167
|
+
type: "text",
|
|
168
|
+
text: JSON.stringify({
|
|
169
|
+
id: user.id,
|
|
170
|
+
name: user.name,
|
|
171
|
+
real_name: user.real_name,
|
|
172
|
+
}, null, 2),
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
async addReaction(args) {
|
|
178
|
+
const { channel, timestamp, name } = args;
|
|
179
|
+
await this.fetch("reactions.add", { channel, timestamp, name });
|
|
180
|
+
return {
|
|
181
|
+
content: [
|
|
182
|
+
{
|
|
183
|
+
type: "text",
|
|
184
|
+
text: JSON.stringify({ ok: true, message: `Added :${name}: reaction` }, null, 2),
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
async updateMessage(args) {
|
|
190
|
+
const { channel, ts, text } = args;
|
|
191
|
+
const data = await this.fetch("chat.update", { channel, ts, text });
|
|
192
|
+
return {
|
|
193
|
+
content: [
|
|
194
|
+
{
|
|
195
|
+
type: "text",
|
|
196
|
+
text: JSON.stringify({
|
|
197
|
+
ok: true,
|
|
198
|
+
channel: data.channel,
|
|
199
|
+
ts: data.ts,
|
|
200
|
+
message: "Message updated successfully",
|
|
201
|
+
}, null, 2),
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
export const slackPollingPlugin = new SlackPollingPlugin();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point for executing a single workflow step.
|
|
4
|
+
* Used by the Vercel workflow (agent_run_v2) to run individual steps in the sandbox.
|
|
5
|
+
*
|
|
6
|
+
* This file contains all step execution logic directly (no separate executeStep method).
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node dist/run-step.js
|
|
10
|
+
*
|
|
11
|
+
* Environment variables:
|
|
12
|
+
* STEP_INPUT_FILE - Path to JSON file containing step input
|
|
13
|
+
* ANTHROPIC_API_KEY - API key for Claude
|
|
14
|
+
* BUILD0_AGENT_CREDENTIALS_URL - URL to fetch credentials
|
|
15
|
+
* BUILD0_AGENT_AUTH_TOKEN - Auth token for credentials
|
|
16
|
+
* BUILD0_AGENT_ENCRYPTION_KEY - Key to decrypt credentials
|
|
17
|
+
*
|
|
18
|
+
* Input file format:
|
|
19
|
+
* {
|
|
20
|
+
* "stepId": "review",
|
|
21
|
+
* "step": { ... step definition from workflow.json ... },
|
|
22
|
+
* "context": { ... accumulated context from previous steps ... },
|
|
23
|
+
* "shouldContinuePreviousSession": false
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* Output (JSON to stdout):
|
|
27
|
+
* ---STEP_RESULT_JSON---
|
|
28
|
+
* {"type":"step_result","stepId":"review","success":true,"output":{...}}
|
|
29
|
+
*/
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=run-step.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-step.d.ts","sourceRoot":"","sources":["../src/run-step.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG"}
|
package/dist/run-step.js
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point for executing a single workflow step.
|
|
4
|
+
* Used by the Vercel workflow (agent_run_v2) to run individual steps in the sandbox.
|
|
5
|
+
*
|
|
6
|
+
* This file contains all step execution logic directly (no separate executeStep method).
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node dist/run-step.js
|
|
10
|
+
*
|
|
11
|
+
* Environment variables:
|
|
12
|
+
* STEP_INPUT_FILE - Path to JSON file containing step input
|
|
13
|
+
* ANTHROPIC_API_KEY - API key for Claude
|
|
14
|
+
* BUILD0_AGENT_CREDENTIALS_URL - URL to fetch credentials
|
|
15
|
+
* BUILD0_AGENT_AUTH_TOKEN - Auth token for credentials
|
|
16
|
+
* BUILD0_AGENT_ENCRYPTION_KEY - Key to decrypt credentials
|
|
17
|
+
*
|
|
18
|
+
* Input file format:
|
|
19
|
+
* {
|
|
20
|
+
* "stepId": "review",
|
|
21
|
+
* "step": { ... step definition from workflow.json ... },
|
|
22
|
+
* "context": { ... accumulated context from previous steps ... },
|
|
23
|
+
* "shouldContinuePreviousSession": false
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* Output (JSON to stdout):
|
|
27
|
+
* ---STEP_RESULT_JSON---
|
|
28
|
+
* {"type":"step_result","stepId":"review","success":true,"output":{...}}
|
|
29
|
+
*/
|
|
30
|
+
import fs from "fs/promises";
|
|
31
|
+
import path from "path";
|
|
32
|
+
import os from "os";
|
|
33
|
+
import { runAgent } from "./utils/agent.js";
|
|
34
|
+
import { logger } from "./utils/logger.js";
|
|
35
|
+
import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Utility Functions
|
|
38
|
+
// ============================================================================
|
|
39
|
+
function interpolate(text, context) {
|
|
40
|
+
if (typeof text !== "string")
|
|
41
|
+
return text;
|
|
42
|
+
return text.replace(/\{\{\s*([\w.]+)\s*\}\}/g, (_, pathStr) => {
|
|
43
|
+
const keys = pathStr.split(".");
|
|
44
|
+
let value = context;
|
|
45
|
+
for (const key of keys) {
|
|
46
|
+
if (value && typeof value === "object" && key in value) {
|
|
47
|
+
value = value[key];
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return value !== undefined
|
|
54
|
+
? typeof value === "object"
|
|
55
|
+
? JSON.stringify(value, null, 2)
|
|
56
|
+
: String(value)
|
|
57
|
+
: "";
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function evaluateCondition(condition, context) {
|
|
61
|
+
const interpolated = condition.replace(/\{\{\s*([\w.]+)\s*\}\}/g, (_, pathStr) => {
|
|
62
|
+
const keys = pathStr.split(".");
|
|
63
|
+
let value = context;
|
|
64
|
+
for (const key of keys) {
|
|
65
|
+
if (value && typeof value === "object" && key in value) {
|
|
66
|
+
value = value[key];
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
return "undefined";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return JSON.stringify(value);
|
|
73
|
+
});
|
|
74
|
+
try {
|
|
75
|
+
// eslint-disable-next-line no-new-func
|
|
76
|
+
const result = new Function(`return ${interpolated}`)();
|
|
77
|
+
return Boolean(result);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// Plugin Loading
|
|
85
|
+
// ============================================================================
|
|
86
|
+
async function loadPlugins() {
|
|
87
|
+
const plugins = [];
|
|
88
|
+
// Try to load plugins from the agent's index.js
|
|
89
|
+
const agentIndexPath = path.resolve(process.cwd(), "dist/index.js");
|
|
90
|
+
try {
|
|
91
|
+
await fs.access(agentIndexPath);
|
|
92
|
+
const agentModule = await import(agentIndexPath);
|
|
93
|
+
if (typeof agentModule.getPlugins === "function") {
|
|
94
|
+
const loadedPlugins = await agentModule.getPlugins();
|
|
95
|
+
plugins.push(...loadedPlugins);
|
|
96
|
+
logger.info(`Loaded ${loadedPlugins.length} plugins from agent index.js`);
|
|
97
|
+
}
|
|
98
|
+
else if (typeof agentModule.registerPlugins === "function") {
|
|
99
|
+
// Legacy API - create a temporary collector
|
|
100
|
+
const collector = {
|
|
101
|
+
plugins: [],
|
|
102
|
+
async registerPlugin(plugin, config) {
|
|
103
|
+
await plugin.init(config);
|
|
104
|
+
this.plugins.push(plugin);
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
await agentModule.registerPlugins(collector);
|
|
108
|
+
plugins.push(...collector.plugins);
|
|
109
|
+
logger.info(`Loaded ${collector.plugins.length} plugins via registerPlugins`);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
logger.warn("Agent index.js does not export getPlugins or registerPlugins");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
logger.info("No agent index.js found, running without custom plugins");
|
|
117
|
+
}
|
|
118
|
+
return plugins;
|
|
119
|
+
}
|
|
120
|
+
function createMcpServer(plugins) {
|
|
121
|
+
const tools = [];
|
|
122
|
+
for (const plugin of plugins) {
|
|
123
|
+
const pluginTools = plugin.registerTools();
|
|
124
|
+
for (const pluginTool of pluginTools) {
|
|
125
|
+
tools.push(tool(pluginTool.name, pluginTool.description, pluginTool.zodSchema.shape, async (args) => {
|
|
126
|
+
return await plugin.handleToolCall(pluginTool.name, args);
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const server = createSdkMcpServer({
|
|
131
|
+
name: "agent-tools",
|
|
132
|
+
version: "1.0.0",
|
|
133
|
+
tools,
|
|
134
|
+
});
|
|
135
|
+
logger.info(`MCP server created with ${tools.length} tools`);
|
|
136
|
+
return server;
|
|
137
|
+
}
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// Step Execution
|
|
140
|
+
// ============================================================================
|
|
141
|
+
async function executeAiAgentStep(step, context, plugins, shouldContinuePreviousSession) {
|
|
142
|
+
const mcpServer = createMcpServer(plugins);
|
|
143
|
+
// Interpolate args
|
|
144
|
+
const agentArgs = {};
|
|
145
|
+
if (step.args) {
|
|
146
|
+
for (const [k, v] of Object.entries(step.args)) {
|
|
147
|
+
agentArgs[k] = typeof v === "string" ? interpolate(v, context) : v;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const fullPrompt = agentArgs.prompt || "";
|
|
151
|
+
const workingDir = path.resolve(process.cwd(), agentArgs.working_dir || ".");
|
|
152
|
+
// Write full prompt to temp file (outside workspace)
|
|
153
|
+
const tempPromptFile = path.join(os.tmpdir(), `prompt_${step.id}_${Date.now()}.txt`);
|
|
154
|
+
const MAX_CHARS = 2000 * 4;
|
|
155
|
+
await fs.writeFile(tempPromptFile, fullPrompt, "utf-8");
|
|
156
|
+
logger.aiAgentPrompt(step.id, fullPrompt.length, fullPrompt.length > MAX_CHARS, tempPromptFile);
|
|
157
|
+
let truncatedPrompt = fullPrompt.substring(0, MAX_CHARS);
|
|
158
|
+
if (fullPrompt.length > MAX_CHARS) {
|
|
159
|
+
truncatedPrompt += `\n\n[Note: The full prompt was truncated. See '${tempPromptFile}' for complete details.]`;
|
|
160
|
+
}
|
|
161
|
+
const result = await runAgent({
|
|
162
|
+
prompt: truncatedPrompt,
|
|
163
|
+
workingDirectory: workingDir,
|
|
164
|
+
mcpServers: {
|
|
165
|
+
"agent-tools": mcpServer,
|
|
166
|
+
},
|
|
167
|
+
shouldContinuePreviousSession,
|
|
168
|
+
});
|
|
169
|
+
logger.aiAgentResponse(step.id, result.output.length, result.output.substring(0, 100));
|
|
170
|
+
return { output: result.output };
|
|
171
|
+
}
|
|
172
|
+
async function executeToolStep(step, context, plugins) {
|
|
173
|
+
const toolName = step.tool;
|
|
174
|
+
if (!toolName)
|
|
175
|
+
throw new Error("Tool name missing");
|
|
176
|
+
// Find plugin that provides this tool
|
|
177
|
+
const plugin = plugins.find((p) => p.registerTools().some((t) => t.name === toolName));
|
|
178
|
+
if (!plugin)
|
|
179
|
+
throw new Error(`Plugin for tool ${toolName} not found`);
|
|
180
|
+
// Interpolate args
|
|
181
|
+
const args = {};
|
|
182
|
+
if (step.args) {
|
|
183
|
+
for (const [k, v] of Object.entries(step.args)) {
|
|
184
|
+
args[k] = typeof v === "string" ? interpolate(v, context) : v;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const toolCallId = `${step.id}_${toolName}_${Date.now()}`;
|
|
188
|
+
logger.toolCall(toolName, toolCallId, args);
|
|
189
|
+
const toolResult = await plugin.handleToolCall(toolName, args);
|
|
190
|
+
// Extract text content from tool result
|
|
191
|
+
const outputText = toolResult.content
|
|
192
|
+
.filter((c) => c.type === "text")
|
|
193
|
+
.map((c) => c.text)
|
|
194
|
+
.join("\n");
|
|
195
|
+
logger.toolResult(toolName, toolCallId, outputText.length, outputText.substring(0, 100), true);
|
|
196
|
+
// Try to parse JSON if possible
|
|
197
|
+
let outputData = outputText;
|
|
198
|
+
try {
|
|
199
|
+
outputData = JSON.parse(outputText);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// Keep as string
|
|
203
|
+
}
|
|
204
|
+
return { output: outputData };
|
|
205
|
+
}
|
|
206
|
+
// ============================================================================
|
|
207
|
+
// Main
|
|
208
|
+
// ============================================================================
|
|
209
|
+
async function main() {
|
|
210
|
+
const inputFile = process.env.STEP_INPUT_FILE;
|
|
211
|
+
if (!inputFile) {
|
|
212
|
+
console.error("Error: STEP_INPUT_FILE environment variable is required");
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
let input;
|
|
216
|
+
try {
|
|
217
|
+
const content = await fs.readFile(inputFile, "utf-8");
|
|
218
|
+
input = JSON.parse(content);
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
console.error(`Error reading input file: ${error}`);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
const { stepId, step, context, shouldContinuePreviousSession } = input;
|
|
225
|
+
logger.stepStart(step.id, step.type, step.tool, step.name);
|
|
226
|
+
// Check 'if' condition
|
|
227
|
+
if (step.if) {
|
|
228
|
+
if (!evaluateCondition(step.if, context)) {
|
|
229
|
+
logger.stepSkip(step.id, "condition false", step.name);
|
|
230
|
+
outputResult({
|
|
231
|
+
type: "step_result",
|
|
232
|
+
stepId,
|
|
233
|
+
success: true,
|
|
234
|
+
skipped: true,
|
|
235
|
+
});
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
// Load plugins
|
|
241
|
+
const plugins = await loadPlugins();
|
|
242
|
+
let result;
|
|
243
|
+
switch (step.type) {
|
|
244
|
+
case "sleep":
|
|
245
|
+
// Sleep should be handled by the workflow, not the sandbox
|
|
246
|
+
throw new Error("Sleep steps should be handled by the workflow orchestrator, not the sandbox");
|
|
247
|
+
case "ai_agent":
|
|
248
|
+
result = await executeAiAgentStep(step, context, plugins, shouldContinuePreviousSession ?? false);
|
|
249
|
+
break;
|
|
250
|
+
case "tool":
|
|
251
|
+
result = await executeToolStep(step, context, plugins);
|
|
252
|
+
break;
|
|
253
|
+
default:
|
|
254
|
+
throw new Error(`Unknown step type: ${step.type}`);
|
|
255
|
+
}
|
|
256
|
+
logger.stepComplete(step.id, step.name);
|
|
257
|
+
outputResult({
|
|
258
|
+
type: "step_result",
|
|
259
|
+
stepId,
|
|
260
|
+
success: true,
|
|
261
|
+
output: result.output,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
266
|
+
logger.stepError(step.id, errorMessage, step.name);
|
|
267
|
+
outputResult({
|
|
268
|
+
type: "step_result",
|
|
269
|
+
stepId,
|
|
270
|
+
success: false,
|
|
271
|
+
error: errorMessage,
|
|
272
|
+
});
|
|
273
|
+
process.exit(1);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function outputResult(output) {
|
|
277
|
+
// Print separator and result
|
|
278
|
+
console.log("---STEP_RESULT_JSON---");
|
|
279
|
+
console.log(JSON.stringify(output));
|
|
280
|
+
}
|
|
281
|
+
main().catch((error) => {
|
|
282
|
+
console.error("Fatal error:", error);
|
|
283
|
+
outputResult({
|
|
284
|
+
type: "step_result",
|
|
285
|
+
stepId: "unknown",
|
|
286
|
+
success: false,
|
|
287
|
+
error: error instanceof Error ? error.message : String(error),
|
|
288
|
+
});
|
|
289
|
+
process.exit(1);
|
|
290
|
+
});
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ declare class Logger {
|
|
|
9
9
|
private overrideConsole;
|
|
10
10
|
private log;
|
|
11
11
|
private createMessage;
|
|
12
|
-
stepStart(stepId: string, stepType: "ai_agent" | "tool", toolName?: string, stepName?: string): void;
|
|
12
|
+
stepStart(stepId: string, stepType: "ai_agent" | "tool" | "sleep", toolName?: string, stepName?: string): void;
|
|
13
13
|
stepComplete(stepId: string, stepName?: string, duration?: number, outputPreview?: string): void;
|
|
14
14
|
stepError(stepId: string, error: string, stepName?: string): void;
|
|
15
15
|
stepSkip(stepId: string, reason: string, stepName?: string): void;
|
|
@@ -1 +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,EAAE,QAAQ,CAAC,EAAE,MAAM;
|
|
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,GAAG,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IAYvG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM;IAYzF,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IAW1D,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IAW1D,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"}
|
package/dist/utils/workflow.d.ts
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
export interface WorkflowStep {
|
|
2
2
|
id: string;
|
|
3
3
|
name?: string;
|
|
4
|
-
type: "ai_agent" | "tool";
|
|
4
|
+
type: "ai_agent" | "tool" | "sleep";
|
|
5
5
|
tool?: string;
|
|
6
6
|
args?: Record<string, any>;
|
|
7
7
|
if?: string;
|
|
8
|
+
/** Jump to another step (for looping/branching) */
|
|
9
|
+
goto?: string;
|
|
10
|
+
/** Duration for sleep steps (e.g., "30s", "5m", "1h") */
|
|
11
|
+
duration?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface WorkflowConfig {
|
|
14
|
+
/** Maximum duration for the entire workflow (e.g., "7d") */
|
|
15
|
+
max_duration?: string;
|
|
8
16
|
}
|
|
9
17
|
export interface Workflow {
|
|
18
|
+
name?: string;
|
|
19
|
+
version?: string;
|
|
20
|
+
config?: WorkflowConfig;
|
|
10
21
|
steps: WorkflowStep[];
|
|
11
22
|
}
|
|
12
23
|
export declare function loadWorkflow(filePath: string): Promise<Workflow>;
|
|
24
|
+
/**
|
|
25
|
+
* Parse a duration string into milliseconds.
|
|
26
|
+
* Supports: "30s", "5m", "1h", "1d", "7d"
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseDuration(duration: string): number;
|
|
13
29
|
//# sourceMappingURL=workflow.d.ts.map
|
|
@@ -1 +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,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;
|
|
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,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAGtE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAetD"}
|
package/dist/utils/workflow.js
CHANGED
|
@@ -3,3 +3,22 @@ export async function loadWorkflow(filePath) {
|
|
|
3
3
|
const content = await fs.readFile(filePath, "utf-8");
|
|
4
4
|
return JSON.parse(content);
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Parse a duration string into milliseconds.
|
|
8
|
+
* Supports: "30s", "5m", "1h", "1d", "7d"
|
|
9
|
+
*/
|
|
10
|
+
export function parseDuration(duration) {
|
|
11
|
+
const match = duration.match(/^(\d+)(s|m|h|d)$/);
|
|
12
|
+
if (!match) {
|
|
13
|
+
throw new Error(`Invalid duration format: ${duration}. Use format like "30s", "5m", "1h", "7d"`);
|
|
14
|
+
}
|
|
15
|
+
const value = parseInt(match[1], 10);
|
|
16
|
+
const unit = match[2];
|
|
17
|
+
switch (unit) {
|
|
18
|
+
case "s": return value * 1000;
|
|
19
|
+
case "m": return value * 60 * 1000;
|
|
20
|
+
case "h": return value * 60 * 60 * 1000;
|
|
21
|
+
case "d": return value * 24 * 60 * 60 * 1000;
|
|
22
|
+
default: throw new Error(`Unknown duration unit: ${unit}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@build0.ai/agent-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Core framework for Build0 autonomous coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"run-step": "dist/run-step.js"
|
|
10
|
+
},
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
|
10
13
|
"import": "./dist/index.js",
|