macroclaw 0.31.0 → 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/app.test.ts +159 -103
- package/src/app.ts +13 -0
- package/src/claude.integration-test.ts +65 -27
- package/src/claude.test.ts +369 -189
- package/src/claude.ts +171 -71
- package/src/orchestrator.test.ts +301 -249
- package/src/orchestrator.ts +198 -166
- package/src/prompts.test.ts +102 -162
- package/src/prompts.ts +62 -53
package/src/prompts.test.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { describe, expect, it } from "bun:test";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
backgroundAgentProgressEvent,
|
|
4
|
+
backgroundAgentResultEvent,
|
|
5
|
+
backgroundAgentStartEvent,
|
|
6
|
+
buttonClickEvent,
|
|
7
|
+
healthCheckEvent,
|
|
8
|
+
peekEvent,
|
|
9
|
+
SYSTEM_PROMPT,
|
|
10
|
+
scheduleTriggerEvent,
|
|
11
|
+
userMessageEvent,
|
|
12
|
+
} from "./prompts";
|
|
3
13
|
|
|
4
14
|
describe("SYSTEM_PROMPT", () => {
|
|
5
15
|
it("contains key sections", () => {
|
|
@@ -50,35 +60,16 @@ describe("SYSTEM_PROMPT", () => {
|
|
|
50
60
|
});
|
|
51
61
|
});
|
|
52
62
|
|
|
53
|
-
describe("
|
|
54
|
-
it("escapes &, <, >, \"", () => {
|
|
55
|
-
expect(escapeXml('a & b < c > d "e"')).toBe("a & b < c > d "e"");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("returns plain text unchanged", () => {
|
|
59
|
-
expect(escapeXml("hello world")).toBe("hello world");
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
describe("buildEvent", () => {
|
|
63
|
+
describe("userMessageEvent", () => {
|
|
64
64
|
it("builds user message event", () => {
|
|
65
|
-
const result =
|
|
66
|
-
name: "check-logs",
|
|
67
|
-
type: "user-message",
|
|
68
|
-
session: "main",
|
|
69
|
-
text: "hello",
|
|
70
|
-
});
|
|
65
|
+
const result = userMessageEvent("check-logs", "hello");
|
|
71
66
|
expect(result).toStartWith('<event name="check-logs" type="user-message" session="main">');
|
|
72
67
|
expect(result).toContain("<text>hello</text>");
|
|
73
68
|
expect(result).toEndWith("</event>");
|
|
74
69
|
});
|
|
75
70
|
|
|
76
71
|
it("builds user message with files", () => {
|
|
77
|
-
const result =
|
|
78
|
-
name: "analyze-photo",
|
|
79
|
-
type: "user-message",
|
|
80
|
-
session: "main",
|
|
81
|
-
text: "what's in this image?",
|
|
72
|
+
const result = userMessageEvent("analyze-photo", "what's in this image?", {
|
|
82
73
|
files: ["/tmp/photo.jpg", "/tmp/doc.pdf"],
|
|
83
74
|
});
|
|
84
75
|
expect(result).toContain("<text>what's in this image?</text>");
|
|
@@ -88,74 +79,66 @@ describe("buildEvent", () => {
|
|
|
88
79
|
expect(result).toContain("</files>");
|
|
89
80
|
});
|
|
90
81
|
|
|
91
|
-
it("builds user message with files only (no text)", () => {
|
|
92
|
-
const result = buildEvent({
|
|
93
|
-
name: "task",
|
|
94
|
-
type: "user-message",
|
|
95
|
-
session: "main",
|
|
96
|
-
files: ["/tmp/photo.jpg"],
|
|
97
|
-
});
|
|
98
|
-
expect(result).not.toContain("<text>");
|
|
99
|
-
expect(result).toContain('<file path="/tmp/photo.jpg" />');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
82
|
it("builds user message with backgrounded event", () => {
|
|
103
|
-
const result =
|
|
104
|
-
name: "check-logs",
|
|
105
|
-
type: "user-message",
|
|
106
|
-
session: "main",
|
|
83
|
+
const result = userMessageEvent("check-logs", "check the logs", {
|
|
107
84
|
backgroundedEvent: "deploy-cluster",
|
|
108
|
-
text: "check the logs",
|
|
109
85
|
});
|
|
110
86
|
expect(result).toContain('<backgrounded-event name="deploy-cluster" />');
|
|
111
87
|
expect(result).toContain("<text>check the logs</text>");
|
|
112
88
|
});
|
|
113
89
|
|
|
114
90
|
it("places backgrounded-event before text", () => {
|
|
115
|
-
const result =
|
|
116
|
-
name: "check-logs",
|
|
117
|
-
type: "user-message",
|
|
118
|
-
session: "main",
|
|
91
|
+
const result = userMessageEvent("check-logs", "hello", {
|
|
119
92
|
backgroundedEvent: "deploy",
|
|
120
|
-
text: "hello",
|
|
121
93
|
});
|
|
122
94
|
const bgIdx = result.indexOf("backgrounded-event");
|
|
123
95
|
const textIdx = result.indexOf("<text>");
|
|
124
96
|
expect(bgIdx).toBeLessThan(textIdx);
|
|
125
97
|
});
|
|
126
98
|
|
|
127
|
-
it("
|
|
128
|
-
const result =
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
99
|
+
it("escapes XML in text content", () => {
|
|
100
|
+
const result = userMessageEvent("test", "a < b & c > d");
|
|
101
|
+
expect(result).toContain("<text>a < b & c > d</text>");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("escapes XML in name attribute", () => {
|
|
105
|
+
const result = userMessageEvent('a & "b"', "test");
|
|
106
|
+
expect(result).toContain('name="a & "b""');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("escapes XML in backgrounded event name", () => {
|
|
110
|
+
const result = userMessageEvent("test", "hello", {
|
|
111
|
+
backgroundedEvent: 'task & "stuff"',
|
|
133
112
|
});
|
|
113
|
+
expect(result).toContain('backgrounded-event name="task & "stuff""');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("buttonClickEvent", () => {
|
|
118
|
+
it("builds button click event", () => {
|
|
119
|
+
const result = buttonClickEvent("btn-yes", "Yes");
|
|
134
120
|
expect(result).toContain('type="button-click"');
|
|
135
121
|
expect(result).toContain("<button>Yes</button>");
|
|
136
122
|
expect(result).not.toContain("<text>");
|
|
137
123
|
});
|
|
138
124
|
|
|
139
125
|
it("builds button click with backgrounded event", () => {
|
|
140
|
-
const result =
|
|
141
|
-
name: "btn-yes",
|
|
142
|
-
type: "button-click",
|
|
143
|
-
session: "main",
|
|
144
|
-
button: "Yes",
|
|
126
|
+
const result = buttonClickEvent("btn-yes", "Yes", {
|
|
145
127
|
backgroundedEvent: "deploy-cluster",
|
|
146
128
|
});
|
|
147
129
|
expect(result).toContain('<backgrounded-event name="deploy-cluster" />');
|
|
148
130
|
expect(result).toContain("<button>Yes</button>");
|
|
149
131
|
});
|
|
150
132
|
|
|
133
|
+
it("escapes XML in button label", () => {
|
|
134
|
+
const result = buttonClickEvent("btn", 'a & "b"');
|
|
135
|
+
expect(result).toContain("<button>a & "b"</button>");
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("scheduleTriggerEvent", () => {
|
|
151
140
|
it("builds schedule trigger event", () => {
|
|
152
|
-
const result =
|
|
153
|
-
name: "cron-daily",
|
|
154
|
-
type: "schedule-trigger",
|
|
155
|
-
session: "background",
|
|
156
|
-
schedule: { name: "daily" },
|
|
157
|
-
text: "check updates",
|
|
158
|
-
});
|
|
141
|
+
const result = scheduleTriggerEvent("cron-daily", { name: "daily" }, "check updates");
|
|
159
142
|
expect(result).toContain('type="schedule-trigger"');
|
|
160
143
|
expect(result).toContain('session="background"');
|
|
161
144
|
expect(result).toContain('<schedule name="daily" />');
|
|
@@ -163,38 +146,34 @@ describe("buildEvent", () => {
|
|
|
163
146
|
});
|
|
164
147
|
|
|
165
148
|
it("builds missed schedule trigger with attributes", () => {
|
|
166
|
-
const result =
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
text: "buy milk",
|
|
172
|
-
});
|
|
149
|
+
const result = scheduleTriggerEvent(
|
|
150
|
+
"cron-reminder",
|
|
151
|
+
{ name: "reminder", missedBy: "15m", scheduledAt: "2026-03-20T06:00:00Z" },
|
|
152
|
+
"buy milk",
|
|
153
|
+
);
|
|
173
154
|
expect(result).toContain('missed-by="15m"');
|
|
174
155
|
expect(result).toContain('scheduled-at="2026-03-20T06:00:00Z"');
|
|
175
156
|
expect(result).toContain("<text>buy milk</text>");
|
|
176
157
|
});
|
|
158
|
+
});
|
|
177
159
|
|
|
160
|
+
describe("backgroundAgentStartEvent", () => {
|
|
178
161
|
it("builds background agent start event", () => {
|
|
179
|
-
const result =
|
|
180
|
-
name: "research",
|
|
181
|
-
type: "background-agent-start",
|
|
182
|
-
session: "background",
|
|
183
|
-
text: "find papers about transformers",
|
|
184
|
-
});
|
|
162
|
+
const result = backgroundAgentStartEvent("research", "find papers about transformers");
|
|
185
163
|
expect(result).toContain('type="background-agent-start"');
|
|
186
164
|
expect(result).toContain('session="background"');
|
|
187
165
|
expect(result).toContain("<text>find papers about transformers</text>");
|
|
188
166
|
});
|
|
167
|
+
});
|
|
189
168
|
|
|
169
|
+
describe("backgroundAgentResultEvent", () => {
|
|
190
170
|
it("builds background agent result (text only)", () => {
|
|
191
|
-
const result =
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
});
|
|
171
|
+
const result = backgroundAgentResultEvent(
|
|
172
|
+
"bg-research",
|
|
173
|
+
"research",
|
|
174
|
+
{ text: "found 3 papers" },
|
|
175
|
+
"Forward to user.",
|
|
176
|
+
);
|
|
198
177
|
expect(result).toContain('type="background-agent-result"');
|
|
199
178
|
expect(result).toContain('<original-event name="research" />');
|
|
200
179
|
expect(result).toContain("<result>");
|
|
@@ -204,102 +183,63 @@ describe("buildEvent", () => {
|
|
|
204
183
|
});
|
|
205
184
|
|
|
206
185
|
it("builds background agent result with files", () => {
|
|
207
|
-
const result =
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
});
|
|
186
|
+
const result = backgroundAgentResultEvent(
|
|
187
|
+
"bg-research",
|
|
188
|
+
"research",
|
|
189
|
+
{ text: "here are the screenshots", files: ["/tmp/screenshot.png"] },
|
|
190
|
+
"Forward to user.",
|
|
191
|
+
);
|
|
214
192
|
expect(result).toContain("<result>");
|
|
215
193
|
expect(result).toContain("<text>here are the screenshots</text>");
|
|
216
194
|
expect(result).toContain('<file path="/tmp/screenshot.png" />');
|
|
217
195
|
expect(result).toContain("</result>");
|
|
218
196
|
});
|
|
219
197
|
|
|
220
|
-
it("
|
|
221
|
-
const result =
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
});
|
|
228
|
-
expect(result).toContain('type="peek"');
|
|
229
|
-
expect(result).toContain('<target-event name="deploy" />');
|
|
230
|
-
expect(result).toContain("<instructions>Brief status update.</instructions>");
|
|
231
|
-
expect(result).not.toContain("<text>");
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it("builds progress event with progress tag", () => {
|
|
235
|
-
const result = buildEvent({
|
|
236
|
-
name: "progress-research",
|
|
237
|
-
type: "background-agent-progress",
|
|
238
|
-
session: "main",
|
|
239
|
-
originalEvent: "research",
|
|
240
|
-
progress: "indexing 500 documents",
|
|
241
|
-
});
|
|
242
|
-
expect(result).toContain('type="background-agent-progress"');
|
|
243
|
-
expect(result).toContain('<original-event name="research" />');
|
|
244
|
-
expect(result).toContain("<progress>indexing 500 documents</progress>");
|
|
245
|
-
expect(result).not.toContain("<result>");
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it("includes instructions in event", () => {
|
|
249
|
-
const result = buildEvent({
|
|
250
|
-
name: "bg-research",
|
|
251
|
-
type: "background-agent-result",
|
|
252
|
-
session: "main",
|
|
253
|
-
originalEvent: "research",
|
|
254
|
-
result: { text: "done" },
|
|
255
|
-
instructions: "Forward to user.",
|
|
256
|
-
});
|
|
198
|
+
it("includes instructions after result", () => {
|
|
199
|
+
const result = backgroundAgentResultEvent(
|
|
200
|
+
"bg-research",
|
|
201
|
+
"research",
|
|
202
|
+
{ text: "done" },
|
|
203
|
+
"Forward to user.",
|
|
204
|
+
);
|
|
257
205
|
expect(result).toContain("<instructions>Forward to user.</instructions>");
|
|
258
|
-
// instructions come last, before </event>
|
|
259
206
|
const instrIdx = result.indexOf("<instructions>");
|
|
260
207
|
const closeIdx = result.indexOf("</event>");
|
|
261
208
|
expect(instrIdx).toBeLessThan(closeIdx);
|
|
262
209
|
expect(instrIdx).toBeGreaterThan(result.indexOf("</result>"));
|
|
263
210
|
});
|
|
211
|
+
});
|
|
264
212
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
name: 'a & "b"',
|
|
278
|
-
type: "user-message",
|
|
279
|
-
session: "main",
|
|
280
|
-
text: "test",
|
|
281
|
-
});
|
|
282
|
-
expect(result).toContain('name="a & "b""');
|
|
213
|
+
describe("backgroundAgentProgressEvent", () => {
|
|
214
|
+
it("builds progress event with progress tag", () => {
|
|
215
|
+
const result = backgroundAgentProgressEvent(
|
|
216
|
+
"progress-research",
|
|
217
|
+
"research",
|
|
218
|
+
"indexing 500 documents",
|
|
219
|
+
"Do not report unless important.",
|
|
220
|
+
);
|
|
221
|
+
expect(result).toContain('type="background-agent-progress"');
|
|
222
|
+
expect(result).toContain('<original-event name="research" />');
|
|
223
|
+
expect(result).toContain("<progress>indexing 500 documents</progress>");
|
|
224
|
+
expect(result).not.toContain("<result>");
|
|
283
225
|
});
|
|
226
|
+
});
|
|
284
227
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
expect(result).toContain("<button>a & "b"</button>");
|
|
228
|
+
describe("peekEvent", () => {
|
|
229
|
+
it("builds peek event with instructions", () => {
|
|
230
|
+
const result = peekEvent("peek-deploy", "deploy", "Brief status update.");
|
|
231
|
+
expect(result).toContain('type="peek"');
|
|
232
|
+
expect(result).toContain('<target-event name="deploy" />');
|
|
233
|
+
expect(result).toContain("<instructions>Brief status update.</instructions>");
|
|
234
|
+
expect(result).not.toContain("<text>");
|
|
293
235
|
});
|
|
236
|
+
});
|
|
294
237
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
text: "hello",
|
|
302
|
-
});
|
|
303
|
-
expect(result).toContain('backgrounded-event name="task & "stuff""');
|
|
238
|
+
describe("healthCheckEvent", () => {
|
|
239
|
+
it("builds health check event with instructions", () => {
|
|
240
|
+
const result = healthCheckEvent("health-check-deploy", "deploy", "Report status.");
|
|
241
|
+
expect(result).toContain('type="health-check"');
|
|
242
|
+
expect(result).toContain('<target-event name="deploy" />');
|
|
243
|
+
expect(result).toContain("<instructions>Report status.</instructions>");
|
|
304
244
|
});
|
|
305
245
|
});
|
package/src/prompts.ts
CHANGED
|
@@ -67,26 +67,11 @@ Each button gets its own row. Max 27 characters per label — if options need mo
|
|
|
67
67
|
|
|
68
68
|
// --- Event builder ---
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
function escapeXml(text: string): string {
|
|
71
71
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
export type EventType =
|
|
77
|
-
| "user-message"
|
|
78
|
-
| "button-click"
|
|
79
|
-
| "schedule-trigger"
|
|
80
|
-
| "background-agent-start"
|
|
81
|
-
| "background-agent-result"
|
|
82
|
-
| "background-agent-progress"
|
|
83
|
-
| "peek"
|
|
84
|
-
| "health-check";
|
|
85
|
-
|
|
86
|
-
export interface EventInput {
|
|
87
|
-
name: string;
|
|
88
|
-
type: EventType;
|
|
89
|
-
session: SessionType;
|
|
74
|
+
interface BuildXmlFields {
|
|
90
75
|
text?: string;
|
|
91
76
|
files?: string[];
|
|
92
77
|
button?: string;
|
|
@@ -99,46 +84,40 @@ export interface EventInput {
|
|
|
99
84
|
result?: { text: string; files?: string[] };
|
|
100
85
|
}
|
|
101
86
|
|
|
102
|
-
|
|
87
|
+
function buildXml(name: string, type: string, session: string, fields: BuildXmlFields): string {
|
|
103
88
|
const lines: string[] = [
|
|
104
|
-
`<event name="${escapeXml(
|
|
89
|
+
`<event name="${escapeXml(name)}" type="${type}" session="${session}">`,
|
|
105
90
|
];
|
|
106
91
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
lines.push(`<backgrounded-event name="${escapeXml(input.backgroundedEvent)}" />`);
|
|
92
|
+
if (fields.backgroundedEvent) {
|
|
93
|
+
lines.push(`<backgrounded-event name="${escapeXml(fields.backgroundedEvent)}" />`);
|
|
110
94
|
}
|
|
111
95
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (
|
|
116
|
-
if (input.schedule.scheduledAt) attrs.push(`scheduled-at="${escapeXml(input.schedule.scheduledAt)}"`);
|
|
96
|
+
if (fields.schedule) {
|
|
97
|
+
const attrs = [`name="${escapeXml(fields.schedule.name)}"`];
|
|
98
|
+
if (fields.schedule.missedBy) attrs.push(`missed-by="${escapeXml(fields.schedule.missedBy)}"`);
|
|
99
|
+
if (fields.schedule.scheduledAt) attrs.push(`scheduled-at="${escapeXml(fields.schedule.scheduledAt)}"`);
|
|
117
100
|
lines.push(`<schedule ${attrs.join(" ")} />`);
|
|
118
101
|
}
|
|
119
102
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
lines.push(`<original-event name="${escapeXml(input.originalEvent)}" />`);
|
|
103
|
+
if (fields.originalEvent) {
|
|
104
|
+
lines.push(`<original-event name="${escapeXml(fields.originalEvent)}" />`);
|
|
123
105
|
}
|
|
124
106
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
lines.push(`<target-event name="${escapeXml(input.targetEvent)}" />`);
|
|
107
|
+
if (fields.targetEvent) {
|
|
108
|
+
lines.push(`<target-event name="${escapeXml(fields.targetEvent)}" />`);
|
|
128
109
|
}
|
|
129
110
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
lines.push(`<progress>${escapeXml(input.progress)}</progress>`);
|
|
111
|
+
if (fields.progress) {
|
|
112
|
+
lines.push(`<progress>${escapeXml(fields.progress)}</progress>`);
|
|
133
113
|
}
|
|
134
114
|
|
|
135
|
-
|
|
136
|
-
if (input.result) {
|
|
115
|
+
if (fields.result) {
|
|
137
116
|
lines.push("<result>");
|
|
138
|
-
lines.push(`<text>${escapeXml(
|
|
139
|
-
if (
|
|
117
|
+
lines.push(`<text>${escapeXml(fields.result.text)}</text>`);
|
|
118
|
+
if (fields.result.files?.length) {
|
|
140
119
|
lines.push("<files>");
|
|
141
|
-
for (const f of
|
|
120
|
+
for (const f of fields.result.files) {
|
|
142
121
|
lines.push(` <file path="${escapeXml(f)}" />`);
|
|
143
122
|
}
|
|
144
123
|
lines.push("</files>");
|
|
@@ -146,30 +125,60 @@ export function buildEvent(input: EventInput): string {
|
|
|
146
125
|
lines.push("</result>");
|
|
147
126
|
}
|
|
148
127
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
lines.push(`<button>${escapeXml(input.button)}</button>`);
|
|
128
|
+
if (fields.button) {
|
|
129
|
+
lines.push(`<button>${escapeXml(fields.button)}</button>`);
|
|
152
130
|
}
|
|
153
131
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
lines.push(`<text>${escapeXml(input.text)}</text>`);
|
|
132
|
+
if (fields.text) {
|
|
133
|
+
lines.push(`<text>${escapeXml(fields.text)}</text>`);
|
|
157
134
|
}
|
|
158
135
|
|
|
159
|
-
|
|
160
|
-
if (input.files?.length) {
|
|
136
|
+
if (fields.files?.length) {
|
|
161
137
|
lines.push("<files>");
|
|
162
|
-
for (const f of
|
|
138
|
+
for (const f of fields.files) {
|
|
163
139
|
lines.push(` <file path="${escapeXml(f)}" />`);
|
|
164
140
|
}
|
|
165
141
|
lines.push("</files>");
|
|
166
142
|
}
|
|
167
143
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
lines.push(`<instructions>${escapeXml(input.instructions)}</instructions>`);
|
|
144
|
+
if (fields.instructions) {
|
|
145
|
+
lines.push(`<instructions>${escapeXml(fields.instructions)}</instructions>`);
|
|
171
146
|
}
|
|
172
147
|
|
|
173
148
|
lines.push("</event>");
|
|
174
149
|
return lines.join("\n");
|
|
175
150
|
}
|
|
151
|
+
|
|
152
|
+
// --- Per-type event builders ---
|
|
153
|
+
|
|
154
|
+
export function userMessageEvent(name: string, text: string, opts?: { files?: string[]; backgroundedEvent?: string }): string {
|
|
155
|
+
return buildXml(name, "user-message", "main", { text, files: opts?.files, backgroundedEvent: opts?.backgroundedEvent });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function buttonClickEvent(name: string, button: string, opts?: { backgroundedEvent?: string }): string {
|
|
159
|
+
return buildXml(name, "button-click", "main", { button, backgroundedEvent: opts?.backgroundedEvent });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function scheduleTriggerEvent(name: string, schedule: { name: string; missedBy?: string; scheduledAt?: string }, text: string): string {
|
|
163
|
+
return buildXml(name, "schedule-trigger", "background", { schedule, text });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function backgroundAgentStartEvent(name: string, text: string): string {
|
|
167
|
+
return buildXml(name, "background-agent-start", "background", { text });
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function backgroundAgentResultEvent(name: string, originalEvent: string, result: { text: string; files?: string[] }, instructions: string, opts?: { backgroundedEvent?: string }): string {
|
|
171
|
+
return buildXml(name, "background-agent-result", "main", { originalEvent, result, instructions, backgroundedEvent: opts?.backgroundedEvent });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function backgroundAgentProgressEvent(name: string, originalEvent: string, progress: string, instructions: string, opts?: { backgroundedEvent?: string }): string {
|
|
175
|
+
return buildXml(name, "background-agent-progress", "main", { originalEvent, progress, instructions, backgroundedEvent: opts?.backgroundedEvent });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function peekEvent(name: string, targetEvent: string, instructions: string): string {
|
|
179
|
+
return buildXml(name, "peek", "background", { targetEvent, instructions });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function healthCheckEvent(name: string, targetEvent: string, instructions: string): string {
|
|
183
|
+
return buildXml(name, "health-check", "background", { targetEvent, instructions });
|
|
184
|
+
}
|