@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.
@@ -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":"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"}
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";
@@ -1 +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"}
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
@@ -1,5 +1,4 @@
1
- // Core
2
- export { Runner } from "./core/runner.js";
1
+ export { loadWorkflow, parseDuration } from "./utils/workflow.js";
3
2
  // Logger
4
3
  export { logger } from "./utils/logger.js";
5
4
  // Credentials
@@ -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"}
@@ -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
+ });
@@ -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;IAY7F,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"}
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"}
@@ -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;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"}
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"}
@@ -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.2.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",