@builder.io/ai-utils 0.32.1 → 0.33.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/package.json +1 -1
- package/src/claw.d.ts +33 -2
- package/src/claw.js +97 -7
- package/src/claw.spec.d.ts +1 -0
- package/src/claw.spec.js +200 -0
- package/src/codegen.d.ts +21 -1
- package/src/events.d.ts +52 -2
- package/src/events.js +8 -0
- package/src/organization.d.ts +5 -0
- package/src/projects.d.ts +3 -0
- package/src/projects.js +1 -0
package/package.json
CHANGED
package/src/claw.d.ts
CHANGED
|
@@ -44,7 +44,39 @@ export interface ParsedChannelId {
|
|
|
44
44
|
* "inbox/user/builder-user-id" → { platform: "inbox", type: "user", ids: ["builder-user-id"] }
|
|
45
45
|
*/
|
|
46
46
|
export declare function parseChannelId(channelId: string): ParsedChannelId;
|
|
47
|
+
/**
|
|
48
|
+
* Converts a Builder channel_id URI to a clickable URL for the
|
|
49
|
+
* corresponding platform (Slack, Jira, etc.).
|
|
50
|
+
*
|
|
51
|
+
* Supported formats:
|
|
52
|
+
*
|
|
53
|
+
* Slack (uses app_redirect URLs - works for all workspaces):
|
|
54
|
+
* slack/thread/TEAM_ID/CHANNEL_ID/THREAD_TS
|
|
55
|
+
* → https://slack.com/app_redirect?team=TEAM_ID&channel=CHANNEL_ID&message_ts=THREAD_TS
|
|
56
|
+
* slack/channel/TEAM_ID/CHANNEL_ID
|
|
57
|
+
* → https://slack.com/app_redirect?team=TEAM_ID&channel=CHANNEL_ID
|
|
58
|
+
* slack/dm/TEAM_ID/USER_ID
|
|
59
|
+
* → https://slack.com/app_redirect?team=TEAM_ID&channel=USER_ID
|
|
60
|
+
*
|
|
61
|
+
* Jira:
|
|
62
|
+
* Returns null - needs integration.baseUrl from database
|
|
63
|
+
* (Future: jira/comment/CLOUD_ID/ISSUE_KEY → {baseUrl}/browse/ISSUE_KEY)
|
|
64
|
+
*
|
|
65
|
+
* Returns null for unsupported platforms or malformed channel IDs.
|
|
66
|
+
*
|
|
67
|
+
* TODO: Accept optional integration metadata parameter to construct proper
|
|
68
|
+
* workspace-specific URLs (Slack teamDomain, Jira baseUrl).
|
|
69
|
+
*/
|
|
70
|
+
export declare function convertChannelIdToUrl(channelId: string): string | null;
|
|
47
71
|
export interface WorkerReportOptions {
|
|
72
|
+
/** The original user's channel that triggered this work. */
|
|
73
|
+
originChannelId?: string;
|
|
74
|
+
/** The report content. */
|
|
75
|
+
content: string;
|
|
76
|
+
/** Agent/tool ID (for sub-agent reports). */
|
|
77
|
+
agentId: string;
|
|
78
|
+
}
|
|
79
|
+
export interface WorkerMessageOptions {
|
|
48
80
|
/** The original user's channel that triggered this work. */
|
|
49
81
|
originChannelId?: string;
|
|
50
82
|
/** The report content. */
|
|
@@ -53,8 +85,6 @@ export interface WorkerReportOptions {
|
|
|
53
85
|
projectId?: string;
|
|
54
86
|
/** Branch name (for branch reports). */
|
|
55
87
|
branchName?: string;
|
|
56
|
-
/** Agent/tool ID (for sub-agent reports). */
|
|
57
|
-
agentId?: string;
|
|
58
88
|
}
|
|
59
89
|
/**
|
|
60
90
|
* Formats a `<worker_report>` message for the org-agent.
|
|
@@ -64,6 +94,7 @@ export interface WorkerReportOptions {
|
|
|
64
94
|
* correct user channel.
|
|
65
95
|
*/
|
|
66
96
|
export declare function formatWorkerReport(opts: WorkerReportOptions): string;
|
|
97
|
+
export declare function formatWorkerMessage(opts: WorkerMessageOptions): string;
|
|
67
98
|
export interface IncomingMessageOptions {
|
|
68
99
|
/** The source channel (e.g. slack/thread/TEAM/CHANNEL/TS). */
|
|
69
100
|
channelId: string;
|
package/src/claw.js
CHANGED
|
@@ -16,6 +16,78 @@ export function parseChannelId(channelId) {
|
|
|
16
16
|
const [platform, type, ...ids] = parts;
|
|
17
17
|
return { platform, type, ids };
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Converts a Builder channel_id URI to a clickable URL for the
|
|
21
|
+
* corresponding platform (Slack, Jira, etc.).
|
|
22
|
+
*
|
|
23
|
+
* Supported formats:
|
|
24
|
+
*
|
|
25
|
+
* Slack (uses app_redirect URLs - works for all workspaces):
|
|
26
|
+
* slack/thread/TEAM_ID/CHANNEL_ID/THREAD_TS
|
|
27
|
+
* → https://slack.com/app_redirect?team=TEAM_ID&channel=CHANNEL_ID&message_ts=THREAD_TS
|
|
28
|
+
* slack/channel/TEAM_ID/CHANNEL_ID
|
|
29
|
+
* → https://slack.com/app_redirect?team=TEAM_ID&channel=CHANNEL_ID
|
|
30
|
+
* slack/dm/TEAM_ID/USER_ID
|
|
31
|
+
* → https://slack.com/app_redirect?team=TEAM_ID&channel=USER_ID
|
|
32
|
+
*
|
|
33
|
+
* Jira:
|
|
34
|
+
* Returns null - needs integration.baseUrl from database
|
|
35
|
+
* (Future: jira/comment/CLOUD_ID/ISSUE_KEY → {baseUrl}/browse/ISSUE_KEY)
|
|
36
|
+
*
|
|
37
|
+
* Returns null for unsupported platforms or malformed channel IDs.
|
|
38
|
+
*
|
|
39
|
+
* TODO: Accept optional integration metadata parameter to construct proper
|
|
40
|
+
* workspace-specific URLs (Slack teamDomain, Jira baseUrl).
|
|
41
|
+
*/
|
|
42
|
+
export function convertChannelIdToUrl(channelId) {
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = parseChannelId(channelId);
|
|
46
|
+
}
|
|
47
|
+
catch (_a) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const { platform, type, ids } = parsed;
|
|
51
|
+
if (platform === "slack") {
|
|
52
|
+
return slackChannelIdToUrl(type, ids);
|
|
53
|
+
}
|
|
54
|
+
// Jira URLs need integration.baseUrl from database - not available here
|
|
55
|
+
// TODO: Add integration metadata parameter to support Jira URLs
|
|
56
|
+
if (platform === "jira") {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function slackChannelIdToUrl(type, ids) {
|
|
62
|
+
// Use app_redirect URLs for all Slack types - they work universally across
|
|
63
|
+
// all workspaces without needing to know the workspace domain.
|
|
64
|
+
// These URLs open in the Slack app or web client automatically.
|
|
65
|
+
if (type === "thread") {
|
|
66
|
+
// slack/thread/TEAM_ID/CHANNEL_ID/THREAD_TS
|
|
67
|
+
const [teamId, slackChannelId, threadTs] = ids;
|
|
68
|
+
if (!teamId || !slackChannelId || !threadTs) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
return `https://slack.com/app_redirect?team=${teamId}&channel=${slackChannelId}&message_ts=${threadTs}`;
|
|
72
|
+
}
|
|
73
|
+
if (type === "channel") {
|
|
74
|
+
// slack/channel/TEAM_ID/CHANNEL_ID
|
|
75
|
+
const [teamId, slackChannelId] = ids;
|
|
76
|
+
if (!teamId || !slackChannelId) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
return `https://slack.com/app_redirect?team=${teamId}&channel=${slackChannelId}`;
|
|
80
|
+
}
|
|
81
|
+
if (type === "dm") {
|
|
82
|
+
// slack/dm/TEAM_ID/USER_ID
|
|
83
|
+
const [teamId, userId] = ids;
|
|
84
|
+
if (!teamId || !userId) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
return `https://slack.com/app_redirect?team=${teamId}&channel=${userId}`;
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
19
91
|
// ── Org-agent message formatting ──
|
|
20
92
|
//
|
|
21
93
|
// The org-agent receives two kinds of messages in its conversation:
|
|
@@ -36,19 +108,33 @@ export function formatWorkerReport(opts) {
|
|
|
36
108
|
let xml = `<worker_report>\n`;
|
|
37
109
|
if (opts.originChannelId) {
|
|
38
110
|
xml += `<origin_channel_id>${opts.originChannelId}</origin_channel_id>\n`;
|
|
111
|
+
const url = convertChannelIdToUrl(opts.originChannelId);
|
|
112
|
+
if (url) {
|
|
113
|
+
xml += `<origin_channel_url>${url}</origin_channel_url>\n`;
|
|
114
|
+
}
|
|
39
115
|
}
|
|
40
|
-
|
|
41
|
-
|
|
116
|
+
xml += `<agent_id>${opts.agentId}</agent_id>\n`;
|
|
117
|
+
xml += `<content>${opts.content}</content>\n`;
|
|
118
|
+
xml += `</worker_report>\n`;
|
|
119
|
+
xml += WORKER_REPORT_TRAILER;
|
|
120
|
+
return xml;
|
|
121
|
+
}
|
|
122
|
+
export function formatWorkerMessage(opts) {
|
|
123
|
+
let xml = `<worker_message>\n`;
|
|
124
|
+
if (opts.originChannelId) {
|
|
125
|
+
xml += `<origin_channel_id>${opts.originChannelId}</origin_channel_id>\n`;
|
|
126
|
+
const url = convertChannelIdToUrl(opts.originChannelId);
|
|
127
|
+
if (url) {
|
|
128
|
+
xml += `<origin_channel_url>${url}</origin_channel_url>\n`;
|
|
129
|
+
}
|
|
42
130
|
}
|
|
43
|
-
if (opts.
|
|
131
|
+
if (opts.projectId && opts.branchName) {
|
|
132
|
+
xml += `<project_id>${opts.projectId}</project_id>\n`;
|
|
44
133
|
xml += `<branch_name>${opts.branchName}</branch_name>\n`;
|
|
45
134
|
xml += `<channel_id>builder/branch/${opts.projectId}/${opts.branchName}</channel_id>\n`;
|
|
46
135
|
}
|
|
47
|
-
if (opts.agentId) {
|
|
48
|
-
xml += `<agent_id>${opts.agentId}</agent_id>\n`;
|
|
49
|
-
}
|
|
50
136
|
xml += `<content>${opts.content}</content>\n`;
|
|
51
|
-
xml += `</
|
|
137
|
+
xml += `</worker_message>\n`;
|
|
52
138
|
xml += WORKER_REPORT_TRAILER;
|
|
53
139
|
return xml;
|
|
54
140
|
}
|
|
@@ -65,6 +151,10 @@ export function formatIncomingMessage(opts) {
|
|
|
65
151
|
}
|
|
66
152
|
result += `<incoming_message>\n`;
|
|
67
153
|
result += `<channel_id>${opts.channelId}</channel_id>\n`;
|
|
154
|
+
const channelUrl = convertChannelIdToUrl(opts.channelId);
|
|
155
|
+
if (channelUrl) {
|
|
156
|
+
result += `<channel_url>${channelUrl}</channel_url>\n`;
|
|
157
|
+
}
|
|
68
158
|
if (opts.dmId) {
|
|
69
159
|
result += `<dm_id>${opts.dmId}</dm_id>\n`;
|
|
70
160
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/src/claw.spec.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { convertChannelIdToUrl, formatIncomingMessage, formatWorkerMessage, formatWorkerReport, } from "./claw";
|
|
3
|
+
describe("convertChannelIdToUrl", () => {
|
|
4
|
+
describe("slack/thread format", () => {
|
|
5
|
+
it("converts a thread channel ID to a Slack app_redirect URL", () => {
|
|
6
|
+
const url = convertChannelIdToUrl("slack/thread/T01ABC123/C01DEF456/1700000000.123456");
|
|
7
|
+
expect(url).toBe("https://slack.com/app_redirect?team=T01ABC123&channel=C01DEF456&message_ts=1700000000.123456");
|
|
8
|
+
});
|
|
9
|
+
it("handles timestamps with dots correctly", () => {
|
|
10
|
+
const url = convertChannelIdToUrl("slack/thread/TTEAM/CCHAN/1234567890.000100");
|
|
11
|
+
expect(url).toBe("https://slack.com/app_redirect?team=TTEAM&channel=CCHAN&message_ts=1234567890.000100");
|
|
12
|
+
});
|
|
13
|
+
it("returns null when teamId is missing", () => {
|
|
14
|
+
expect(convertChannelIdToUrl("slack/thread/CCHAN/1234567890.000100")).toBeNull();
|
|
15
|
+
});
|
|
16
|
+
it("returns null when channelId is missing", () => {
|
|
17
|
+
expect(convertChannelIdToUrl("slack/thread/TTEAM/1234567890.000100")).toBeNull();
|
|
18
|
+
});
|
|
19
|
+
it("returns null when threadTs is missing", () => {
|
|
20
|
+
expect(convertChannelIdToUrl("slack/thread/TTEAM/CCHAN")).toBeNull();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe("slack/channel format", () => {
|
|
24
|
+
it("converts a channel ID to a Slack app_redirect URL", () => {
|
|
25
|
+
const url = convertChannelIdToUrl("slack/channel/T01ABC123/C01DEF456");
|
|
26
|
+
expect(url).toBe("https://slack.com/app_redirect?team=T01ABC123&channel=C01DEF456");
|
|
27
|
+
});
|
|
28
|
+
it("returns null when teamId is missing", () => {
|
|
29
|
+
expect(convertChannelIdToUrl("slack/channel/C01DEF456")).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
it("returns null when channelId is missing", () => {
|
|
32
|
+
expect(convertChannelIdToUrl("slack/channel/TTEAM")).toBeNull();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe("slack/dm format", () => {
|
|
36
|
+
it("converts a DM channel ID to a Slack app_redirect URL", () => {
|
|
37
|
+
const url = convertChannelIdToUrl("slack/dm/T01ABC123/U01XYZ789");
|
|
38
|
+
expect(url).toBe("https://slack.com/app_redirect?team=T01ABC123&channel=U01XYZ789");
|
|
39
|
+
});
|
|
40
|
+
it("returns null when teamId is missing", () => {
|
|
41
|
+
expect(convertChannelIdToUrl("slack/dm/U01XYZ789")).toBeNull();
|
|
42
|
+
});
|
|
43
|
+
it("returns null when userId is missing", () => {
|
|
44
|
+
expect(convertChannelIdToUrl("slack/dm/TTEAM")).toBeNull();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe("jira/comment format", () => {
|
|
48
|
+
it("returns null (needs integration.baseUrl from database)", () => {
|
|
49
|
+
const url = convertChannelIdToUrl("jira/comment/cloud-id/PROJ-123");
|
|
50
|
+
expect(url).toBeNull();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe("unsupported platforms", () => {
|
|
54
|
+
it("returns null for telegram channel IDs", () => {
|
|
55
|
+
expect(convertChannelIdToUrl("telegram/chat/123456")).toBeNull();
|
|
56
|
+
});
|
|
57
|
+
it("returns null for builder branch channel IDs", () => {
|
|
58
|
+
expect(convertChannelIdToUrl("builder/branch/proj-id/my-branch")).toBeNull();
|
|
59
|
+
});
|
|
60
|
+
it("returns null for inbox channel IDs", () => {
|
|
61
|
+
expect(convertChannelIdToUrl("inbox/user/user-id")).toBeNull();
|
|
62
|
+
});
|
|
63
|
+
it("returns null for internal channel IDs", () => {
|
|
64
|
+
expect(convertChannelIdToUrl("internal/support")).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
it("returns null for completely invalid input", () => {
|
|
67
|
+
expect(convertChannelIdToUrl("not-a-channel-id")).toBeNull();
|
|
68
|
+
});
|
|
69
|
+
it("returns null for empty string", () => {
|
|
70
|
+
expect(convertChannelIdToUrl("")).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe("formatIncomingMessage", () => {
|
|
75
|
+
it("includes channel_url for Slack thread channel IDs", () => {
|
|
76
|
+
const result = formatIncomingMessage({
|
|
77
|
+
channelId: "slack/thread/TTEAM/CCHAN/1234567890.000100",
|
|
78
|
+
sender: "Alice",
|
|
79
|
+
timestamp: "Monday, January 1, 2024 at 10:00 AM PST",
|
|
80
|
+
content: "Hello!",
|
|
81
|
+
});
|
|
82
|
+
expect(result).toContain("<channel_url>https://slack.com/app_redirect?team=TTEAM&channel=CCHAN&message_ts=1234567890.000100</channel_url>");
|
|
83
|
+
});
|
|
84
|
+
it("includes channel_url for Slack channel IDs", () => {
|
|
85
|
+
const result = formatIncomingMessage({
|
|
86
|
+
channelId: "slack/channel/TTEAM/CCHAN",
|
|
87
|
+
sender: "Bob",
|
|
88
|
+
timestamp: "Tuesday, January 2, 2024 at 11:00 AM PST",
|
|
89
|
+
content: "Hey there",
|
|
90
|
+
});
|
|
91
|
+
expect(result).toContain("<channel_url>https://slack.com/app_redirect?team=TTEAM&channel=CCHAN</channel_url>");
|
|
92
|
+
});
|
|
93
|
+
it("does not include channel_url for Jira (needs integration.baseUrl)", () => {
|
|
94
|
+
const result = formatIncomingMessage({
|
|
95
|
+
channelId: "jira/comment/cloud-id/PROJ-123",
|
|
96
|
+
sender: "Charlie",
|
|
97
|
+
timestamp: "Wednesday, January 3, 2024 at 12:00 PM PST",
|
|
98
|
+
content: "Jira comment",
|
|
99
|
+
});
|
|
100
|
+
expect(result).not.toContain("<channel_url>");
|
|
101
|
+
});
|
|
102
|
+
it("does not include channel_url for unsupported platforms", () => {
|
|
103
|
+
const result = formatIncomingMessage({
|
|
104
|
+
channelId: "telegram/chat/123456",
|
|
105
|
+
sender: "Charlie",
|
|
106
|
+
timestamp: "Wednesday, January 3, 2024 at 12:00 PM PST",
|
|
107
|
+
content: "Telegram message",
|
|
108
|
+
});
|
|
109
|
+
expect(result).not.toContain("<channel_url>");
|
|
110
|
+
});
|
|
111
|
+
it("includes dm_id without generating dm_url", () => {
|
|
112
|
+
const result = formatIncomingMessage({
|
|
113
|
+
channelId: "slack/thread/TTEAM/CCHAN/1234567890.000100",
|
|
114
|
+
dmId: "slack/dm/TTEAM/UUSER",
|
|
115
|
+
sender: "Dave",
|
|
116
|
+
timestamp: "Thursday, January 4, 2024 at 1:00 PM PST",
|
|
117
|
+
content: "DM context",
|
|
118
|
+
});
|
|
119
|
+
expect(result).toContain("<dm_id>slack/dm/TTEAM/UUSER</dm_id>");
|
|
120
|
+
expect(result).not.toContain("<dm_url>");
|
|
121
|
+
});
|
|
122
|
+
it("preserves all other fields unchanged", () => {
|
|
123
|
+
const result = formatIncomingMessage({
|
|
124
|
+
channelId: "slack/thread/TTEAM/CCHAN/1234567890.000100",
|
|
125
|
+
sender: "Frank",
|
|
126
|
+
timestamp: "Saturday, January 6, 2024 at 3:00 PM PST",
|
|
127
|
+
content: "Test message",
|
|
128
|
+
messageContext: "Prior context",
|
|
129
|
+
});
|
|
130
|
+
expect(result).toContain("<channel_id>slack/thread/TTEAM/CCHAN/1234567890.000100</channel_id>");
|
|
131
|
+
expect(result).toContain("<sender>Frank</sender>");
|
|
132
|
+
expect(result).toContain("Prior context");
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe("formatWorkerMessage", () => {
|
|
136
|
+
it("includes origin_channel_url for Slack originChannelId", () => {
|
|
137
|
+
const result = formatWorkerMessage({
|
|
138
|
+
originChannelId: "slack/thread/TTEAM/CCHAN/1234567890.000100",
|
|
139
|
+
content: "Worker result",
|
|
140
|
+
projectId: "proj-123",
|
|
141
|
+
branchName: "feat/my-feature",
|
|
142
|
+
});
|
|
143
|
+
expect(result).toContain("<origin_channel_url>https://slack.com/app_redirect?team=TTEAM&channel=CCHAN&message_ts=1234567890.000100</origin_channel_url>");
|
|
144
|
+
});
|
|
145
|
+
it("does not include origin_channel_url for Jira (needs integration.baseUrl)", () => {
|
|
146
|
+
const result = formatWorkerMessage({
|
|
147
|
+
originChannelId: "jira/comment/cloud-id/PROJ-456",
|
|
148
|
+
content: "Worker result",
|
|
149
|
+
});
|
|
150
|
+
expect(result).not.toContain("<origin_channel_url>");
|
|
151
|
+
});
|
|
152
|
+
it("does not include origin_channel_url for unsupported platforms", () => {
|
|
153
|
+
const result = formatWorkerMessage({
|
|
154
|
+
originChannelId: "builder/branch/proj-id/my-branch",
|
|
155
|
+
content: "Worker result",
|
|
156
|
+
});
|
|
157
|
+
expect(result).not.toContain("<origin_channel_url>");
|
|
158
|
+
});
|
|
159
|
+
it("omits origin_channel_url when originChannelId is absent", () => {
|
|
160
|
+
const result = formatWorkerMessage({
|
|
161
|
+
content: "Worker result without origin",
|
|
162
|
+
});
|
|
163
|
+
expect(result).not.toContain("<origin_channel_url>");
|
|
164
|
+
expect(result).not.toContain("<origin_channel_id>");
|
|
165
|
+
});
|
|
166
|
+
it("uses matching XML tags (worker_message, not worker_report)", () => {
|
|
167
|
+
const result = formatWorkerMessage({
|
|
168
|
+
content: "Test content",
|
|
169
|
+
});
|
|
170
|
+
expect(result).toContain("<worker_message>");
|
|
171
|
+
expect(result).toContain("</worker_message>");
|
|
172
|
+
expect(result).not.toContain("</worker_report>");
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
describe("formatWorkerReport", () => {
|
|
176
|
+
it("includes origin_channel_url for Slack originChannelId", () => {
|
|
177
|
+
const result = formatWorkerReport({
|
|
178
|
+
originChannelId: "slack/channel/TTEAM/CCHAN",
|
|
179
|
+
content: "Report content",
|
|
180
|
+
agentId: "agent-abc",
|
|
181
|
+
});
|
|
182
|
+
expect(result).toContain("<origin_channel_url>https://slack.com/app_redirect?team=TTEAM&channel=CCHAN</origin_channel_url>");
|
|
183
|
+
});
|
|
184
|
+
it("does not include origin_channel_url for Jira (needs integration.baseUrl)", () => {
|
|
185
|
+
const result = formatWorkerReport({
|
|
186
|
+
originChannelId: "jira/comment/cloud-id/PROJ-789",
|
|
187
|
+
content: "Report content",
|
|
188
|
+
agentId: "agent-xyz",
|
|
189
|
+
});
|
|
190
|
+
expect(result).not.toContain("<origin_channel_url>");
|
|
191
|
+
});
|
|
192
|
+
it("does not include origin_channel_url for unsupported platforms", () => {
|
|
193
|
+
const result = formatWorkerReport({
|
|
194
|
+
originChannelId: "telegram/chat/123456",
|
|
195
|
+
content: "Report content",
|
|
196
|
+
agentId: "agent-xyz",
|
|
197
|
+
});
|
|
198
|
+
expect(result).not.toContain("<origin_channel_url>");
|
|
199
|
+
});
|
|
200
|
+
});
|
package/src/codegen.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ export interface CustomAgentDefinition {
|
|
|
45
45
|
systemPrompt?: string;
|
|
46
46
|
tools?: string[];
|
|
47
47
|
model?: string;
|
|
48
|
+
roundRobinModels?: string[];
|
|
48
49
|
mode?: CodeGenMode;
|
|
49
50
|
position?: string;
|
|
50
51
|
needDevServer?: boolean;
|
|
@@ -554,6 +555,11 @@ export interface SendMessageToolInput {
|
|
|
554
555
|
markdown: string;
|
|
555
556
|
status: "starting" | "question" | "will-follow-up" | "done:success" | "done:error";
|
|
556
557
|
loadingMessage?: string;
|
|
558
|
+
/**
|
|
559
|
+
* When true, send the response as a voice message using text-to-speech.
|
|
560
|
+
* Only supported for Telegram channels.
|
|
561
|
+
*/
|
|
562
|
+
voiceResponse?: boolean;
|
|
557
563
|
}
|
|
558
564
|
export interface SpawnBranchToolInput {
|
|
559
565
|
projectId: string;
|
|
@@ -782,6 +788,17 @@ export interface CodeGenInputOptions {
|
|
|
782
788
|
projectId?: string;
|
|
783
789
|
branchName?: string;
|
|
784
790
|
repoHash?: string;
|
|
791
|
+
/**
|
|
792
|
+
* Server-side branch.agentType cached from middleware Firestore lookup.
|
|
793
|
+
* Used to avoid redundant getBranch calls for billing exemption checks.
|
|
794
|
+
* @internal - Set by middleware, not by clients
|
|
795
|
+
*/
|
|
796
|
+
branchAgentType?: string | null;
|
|
797
|
+
/**
|
|
798
|
+
* True when middleware performed a Firestore branch lookup; do not use source-based exemption.
|
|
799
|
+
* @internal - Set by middleware, not by clients
|
|
800
|
+
*/
|
|
801
|
+
branchAgentTypeChecked?: boolean;
|
|
785
802
|
/** @deprecated */
|
|
786
803
|
prevId?: string;
|
|
787
804
|
/** @deprecated */
|
|
@@ -1177,6 +1194,8 @@ export interface GenerateCompletionStepToolCallRequest {
|
|
|
1177
1194
|
id: string;
|
|
1178
1195
|
name: string;
|
|
1179
1196
|
input: any;
|
|
1197
|
+
/** When true, this tool call can be resolved by a user message if no client handler exists */
|
|
1198
|
+
messageValid?: boolean;
|
|
1180
1199
|
}
|
|
1181
1200
|
export interface GenerateCompletionStepTerminals {
|
|
1182
1201
|
type: "terminals";
|
|
@@ -1223,10 +1242,11 @@ export interface UserSourceBase {
|
|
|
1223
1242
|
principals?: string[];
|
|
1224
1243
|
jobs?: string[];
|
|
1225
1244
|
permissions?: UserSourcePermission[];
|
|
1245
|
+
channelId?: string;
|
|
1226
1246
|
}
|
|
1227
1247
|
/** All integration sources: Slack, Teams, Jira, Linear, and git providers (GitHub, GitLab, Azure, Bitbucket). */
|
|
1228
1248
|
export interface UserSourceOther extends UserSourceBase {
|
|
1229
|
-
source: "slack" | "telegram" | "teams" | "jira" | "linear" | "github" | "gitlab" | "azure" | "bitbucket";
|
|
1249
|
+
source: "slack" | "telegram" | "whatsapp" | "teams" | "jira" | "linear" | "github" | "gitlab" | "azure" | "bitbucket";
|
|
1230
1250
|
/** User ID from the external platform (Slack user ID, GitHub user id, etc.) */
|
|
1231
1251
|
userId?: string;
|
|
1232
1252
|
userName?: string;
|
package/src/events.d.ts
CHANGED
|
@@ -530,7 +530,6 @@ export declare const ForceSetupAgentV1: {
|
|
|
530
530
|
};
|
|
531
531
|
export type ClawMessageSentV1 = FusionEventVariant<"claw.message.sent", {
|
|
532
532
|
branchName: string;
|
|
533
|
-
messageType: "text" | "task-result" | "cron-trigger";
|
|
534
533
|
content: string;
|
|
535
534
|
senderType: "user" | "sub-agent" | "system";
|
|
536
535
|
senderId?: string;
|
|
@@ -538,6 +537,8 @@ export type ClawMessageSentV1 = FusionEventVariant<"claw.message.sent", {
|
|
|
538
537
|
channelId?: string;
|
|
539
538
|
dmId?: string;
|
|
540
539
|
senderDisplayName?: string;
|
|
540
|
+
agentBranchName?: string;
|
|
541
|
+
agentProjectId?: string;
|
|
541
542
|
userSource?: UserSource;
|
|
542
543
|
/** Optional context to include alongside the message (e.g. Slack thread history). */
|
|
543
544
|
messageContext?: string;
|
|
@@ -548,13 +549,62 @@ export declare const ClawMessageSentV1: {
|
|
|
548
549
|
eventName: "claw.message.sent";
|
|
549
550
|
version: "1";
|
|
550
551
|
};
|
|
552
|
+
export type CodegenCompletionV1 = FusionEventVariant<"codegen.completion", {
|
|
553
|
+
completionId: string;
|
|
554
|
+
role?: string;
|
|
555
|
+
projectId?: string;
|
|
556
|
+
branchName?: string;
|
|
557
|
+
sessionId: string;
|
|
558
|
+
userSourceUserId?: string;
|
|
559
|
+
model?: string;
|
|
560
|
+
compact?: boolean;
|
|
561
|
+
usedTokens?: number;
|
|
562
|
+
category?: string;
|
|
563
|
+
userPrompt?: string;
|
|
564
|
+
}, {
|
|
565
|
+
projectId?: string;
|
|
566
|
+
branchName?: string;
|
|
567
|
+
sessionId: string;
|
|
568
|
+
}, 1>;
|
|
569
|
+
export declare const CodegenCompletionV1: {
|
|
570
|
+
eventName: "codegen.completion";
|
|
571
|
+
version: "1";
|
|
572
|
+
};
|
|
573
|
+
export type CodegenUserPromptV1 = FusionEventVariant<"codegen.user.prompt", {
|
|
574
|
+
completionId: string;
|
|
575
|
+
role?: string;
|
|
576
|
+
projectId?: string;
|
|
577
|
+
branchName?: string;
|
|
578
|
+
sessionId: string;
|
|
579
|
+
userSourceUserId?: string;
|
|
580
|
+
model?: string;
|
|
581
|
+
compact?: boolean;
|
|
582
|
+
usedTokens?: number;
|
|
583
|
+
category?: string;
|
|
584
|
+
userPrompt: string;
|
|
585
|
+
}, {
|
|
586
|
+
projectId?: string;
|
|
587
|
+
branchName?: string;
|
|
588
|
+
sessionId: string;
|
|
589
|
+
}, 1>;
|
|
590
|
+
export declare const CodegenUserPromptV1: {
|
|
591
|
+
eventName: "codegen.user.prompt";
|
|
592
|
+
version: "1";
|
|
593
|
+
};
|
|
551
594
|
export interface SendMessageToOrgAgentInput {
|
|
595
|
+
agentBranchName?: string;
|
|
596
|
+
agentProjectId?: string;
|
|
552
597
|
content: string;
|
|
598
|
+
senderType?: "user" | "sub-agent" | "system";
|
|
553
599
|
channelId?: string;
|
|
554
600
|
senderDisplayName?: string;
|
|
555
601
|
messageContext?: string;
|
|
602
|
+
senderId?: string;
|
|
603
|
+
dmId?: string;
|
|
604
|
+
userSource?: UserSource;
|
|
605
|
+
attachments?: FileUpload[];
|
|
556
606
|
}
|
|
557
|
-
export type FusionEvent = AiTaskCompletedEvent | AiTaskFailedEvent | GitPrCreatedEvent | ClientDevtoolsSessionStartedEvent | ClientDevtoolsSessionIdleEventV1 | FusionProjectCreatedV1 | SetupAgentCompletedV1 | ForceSetupAgentV1 | ClawMessageSentV1;
|
|
607
|
+
export type FusionEvent = AiTaskCompletedEvent | AiTaskFailedEvent | GitPrCreatedEvent | ClientDevtoolsSessionStartedEvent | ClientDevtoolsSessionIdleEventV1 | FusionProjectCreatedV1 | SetupAgentCompletedV1 | ForceSetupAgentV1 | ClawMessageSentV1 | CodegenCompletionV1 | CodegenUserPromptV1;
|
|
558
608
|
export interface ModelPermissionRequiredEvent {
|
|
559
609
|
type: "assistant.model.permission.required";
|
|
560
610
|
data: {
|
package/src/events.js
CHANGED
|
@@ -34,3 +34,11 @@ export const ClawMessageSentV1 = {
|
|
|
34
34
|
eventName: "claw.message.sent",
|
|
35
35
|
version: "1",
|
|
36
36
|
};
|
|
37
|
+
export const CodegenCompletionV1 = {
|
|
38
|
+
eventName: "codegen.completion",
|
|
39
|
+
version: "1",
|
|
40
|
+
};
|
|
41
|
+
export const CodegenUserPromptV1 = {
|
|
42
|
+
eventName: "codegen.user.prompt",
|
|
43
|
+
version: "1",
|
|
44
|
+
};
|
package/src/organization.d.ts
CHANGED
|
@@ -84,6 +84,10 @@ interface OrganizationSettings {
|
|
|
84
84
|
enabled?: boolean;
|
|
85
85
|
};
|
|
86
86
|
disableFigmaImageUpload?: boolean;
|
|
87
|
+
prReviewer?: {
|
|
88
|
+
instructions?: string;
|
|
89
|
+
model?: string;
|
|
90
|
+
};
|
|
87
91
|
enableTicketAssessment?: boolean;
|
|
88
92
|
ticketAssessmentPrompt?: string;
|
|
89
93
|
ticketAssessmentModel?: string;
|
|
@@ -292,6 +296,7 @@ export interface FeatureMap {
|
|
|
292
296
|
enterpriseGitProviders?: boolean;
|
|
293
297
|
agentCreditsRollover?: boolean;
|
|
294
298
|
selfHostedGitProviders?: boolean;
|
|
299
|
+
reviewAgent?: boolean;
|
|
295
300
|
}
|
|
296
301
|
export interface SubscriptionInfo {
|
|
297
302
|
price?: number;
|
package/src/projects.d.ts
CHANGED
|
@@ -569,6 +569,7 @@ export interface Project {
|
|
|
569
569
|
/** MIGRATION: accepts both string and number during migration period */
|
|
570
570
|
updatedAt: InMigrationDate;
|
|
571
571
|
pinned?: boolean;
|
|
572
|
+
pinOrder?: number;
|
|
572
573
|
archived?: boolean;
|
|
573
574
|
createdBy: string;
|
|
574
575
|
lastUpdateBy?: string;
|
|
@@ -644,6 +645,7 @@ export interface Project {
|
|
|
644
645
|
prReviewer?: {
|
|
645
646
|
enabled: boolean;
|
|
646
647
|
instructions?: string;
|
|
648
|
+
model?: string;
|
|
647
649
|
};
|
|
648
650
|
enableSnapshots?: boolean;
|
|
649
651
|
postMergeMemories?: boolean;
|
|
@@ -867,6 +869,7 @@ export interface SendMessageOptions {
|
|
|
867
869
|
featureFlags?: Record<string, boolean>;
|
|
868
870
|
webhook?: WebhookConfig;
|
|
869
871
|
fireAndForget?: boolean;
|
|
872
|
+
skipPromptAnalysis?: boolean;
|
|
870
873
|
canHandleTools?: (keyof CodeGenToolMap)[];
|
|
871
874
|
}
|
|
872
875
|
export interface MemoryData {
|
package/src/projects.js
CHANGED
|
@@ -30,6 +30,7 @@ export const EXAMPLE_REPOS = [
|
|
|
30
30
|
"BuilderIO/fusion-angular-tailwind-starter",
|
|
31
31
|
"BuilderIO/fusion-svelte-tailwind-starter",
|
|
32
32
|
"BuilderIO/fusion-vue-tailwind-starter",
|
|
33
|
+
"BuilderIO/org-agent-starter",
|
|
33
34
|
];
|
|
34
35
|
export const STARTER_REPO = "BuilderIO/fusion-starter";
|
|
35
36
|
export const EXAMPLE_OR_STARTER_REPOS = [...EXAMPLE_REPOS, STARTER_REPO];
|