@positronic/spec 0.0.51 → 0.0.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.d.ts +56 -12
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +382 -46
- package/dist/api.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/src/api.js +915 -175
- package/dist/src/index.js +1 -1
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -113,29 +113,44 @@ export declare const brains: {
|
|
|
113
113
|
*/
|
|
114
114
|
killSuspended(fetch: Fetch, loopBrainIdentifier: string, webhookSlug: string, webhookPayload: Record<string, any>): Promise<boolean>;
|
|
115
115
|
/**
|
|
116
|
-
* Test that
|
|
117
|
-
* Requires a brain with
|
|
116
|
+
* Test that agent steps emit proper AGENT_* events in the SSE stream.
|
|
117
|
+
* Requires a brain with an agent step that will pause on a webhook.
|
|
118
118
|
*
|
|
119
119
|
* Expected events before webhook pause:
|
|
120
|
-
* -
|
|
121
|
-
* -
|
|
122
|
-
* -
|
|
123
|
-
* -
|
|
120
|
+
* - AGENT_START (with prompt and optional system)
|
|
121
|
+
* - AGENT_ITERATION
|
|
122
|
+
* - AGENT_TOOL_CALL
|
|
123
|
+
* - AGENT_WEBHOOK (before WEBHOOK event)
|
|
124
124
|
* - WEBHOOK
|
|
125
125
|
*/
|
|
126
|
-
|
|
126
|
+
watchAgentEvents(fetch: Fetch, agentBrainIdentifier: string): Promise<boolean>;
|
|
127
127
|
/**
|
|
128
|
-
* Test full
|
|
129
|
-
* 1. Start
|
|
128
|
+
* Test full agent webhook resumption flow:
|
|
129
|
+
* 1. Start an agent brain that will pause on a webhook
|
|
130
130
|
* 2. Verify it pauses with WEBHOOK event
|
|
131
131
|
* 3. Trigger the webhook with a response
|
|
132
|
-
* 4. Verify the brain resumes and emits WEBHOOK_RESPONSE and
|
|
132
|
+
* 4. Verify the brain resumes and emits WEBHOOK_RESPONSE and AGENT_TOOL_RESULT
|
|
133
133
|
*
|
|
134
134
|
* Requires:
|
|
135
|
-
* - A brain with
|
|
135
|
+
* - A brain with an agent step that calls a tool returning { waitFor: webhook(...) }
|
|
136
136
|
* - The webhook slug and identifier to trigger
|
|
137
137
|
*/
|
|
138
|
-
|
|
138
|
+
agentWebhookResume(fetch: Fetch, agentBrainIdentifier: string, webhookSlug: string, webhookPayload: Record<string, any>): Promise<boolean>;
|
|
139
|
+
/**
|
|
140
|
+
* Test that inner brain COMPLETE events don't overwrite outer brain status.
|
|
141
|
+
*
|
|
142
|
+
* This test verifies that when a nested inner brain completes, the outer brain's
|
|
143
|
+
* status in history remains RUNNING (not prematurely set to COMPLETE).
|
|
144
|
+
*
|
|
145
|
+
* Test scenario:
|
|
146
|
+
* 1. Run outer brain that contains inner brain + webhook step after inner brain
|
|
147
|
+
* 2. Watch SSE until outer brain's WEBHOOK event (after inner completes)
|
|
148
|
+
* 3. Query history for the brain
|
|
149
|
+
* 4. Assert status is RUNNING (not COMPLETE from inner brain)
|
|
150
|
+
* 5. Trigger webhook to complete outer brain
|
|
151
|
+
* 6. Assert final status is COMPLETE
|
|
152
|
+
*/
|
|
153
|
+
innerBrainCompleteDoesNotAffectOuterStatus(fetch: Fetch, outerBrainIdentifier: string, webhookSlug: string, webhookPayload: Record<string, any>): Promise<boolean>;
|
|
139
154
|
};
|
|
140
155
|
export declare const schedules: {
|
|
141
156
|
/**
|
|
@@ -193,6 +208,20 @@ export declare const webhooks: {
|
|
|
193
208
|
* Test POST /webhooks/:slug with non-existent webhook - Should return 404
|
|
194
209
|
*/
|
|
195
210
|
notFound(fetch: Fetch, slug: string): Promise<boolean>;
|
|
211
|
+
/**
|
|
212
|
+
* Test POST /webhooks/system/ui-form - Built-in webhook for UI form submissions.
|
|
213
|
+
* This is used by pages generated via .ui() steps to submit form data.
|
|
214
|
+
*
|
|
215
|
+
* The endpoint:
|
|
216
|
+
* - Accepts form data (application/x-www-form-urlencoded or multipart/form-data)
|
|
217
|
+
* - Requires an `identifier` query parameter to match the waiting brain
|
|
218
|
+
* - Returns { received: true, action: 'resumed' | 'not_found', ... }
|
|
219
|
+
*/
|
|
220
|
+
uiForm(fetch: Fetch, identifier: string, formData: Record<string, string | string[]>): Promise<boolean>;
|
|
221
|
+
/**
|
|
222
|
+
* Test POST /webhooks/system/ui-form with missing identifier - Should return 400
|
|
223
|
+
*/
|
|
224
|
+
uiFormMissingIdentifier(fetch: Fetch): Promise<boolean>;
|
|
196
225
|
};
|
|
197
226
|
export declare const pages: {
|
|
198
227
|
/**
|
|
@@ -238,5 +267,20 @@ export declare const pages: {
|
|
|
238
267
|
*/
|
|
239
268
|
deletePreservesResources(fetch: Fetch): Promise<boolean>;
|
|
240
269
|
};
|
|
270
|
+
/**
|
|
271
|
+
* Bundle API Tests
|
|
272
|
+
*
|
|
273
|
+
* Tests for the /bundle/components.js endpoint which serves the component bundle.
|
|
274
|
+
*
|
|
275
|
+
* NOTE: These tests only verify the API endpoint behavior. The bundle build and
|
|
276
|
+
* upload process is backend-specific and must be tested separately by each
|
|
277
|
+
* backend implementation.
|
|
278
|
+
*/
|
|
279
|
+
export declare const bundle: {
|
|
280
|
+
/**
|
|
281
|
+
* Test GET /bundle/components.js - Serve the component bundle
|
|
282
|
+
*/
|
|
283
|
+
get(fetch: Fetch): Promise<boolean>;
|
|
284
|
+
};
|
|
241
285
|
export {};
|
|
242
286
|
//# sourceMappingURL=api.d.ts.map
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAEA,KAAK,KAAK,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAErD,wBAAsB,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAyB/D;AAED,eAAO,MAAM,SAAS;IACpB;;OAEG;gBACe,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC;IAyE1C;;OAEG;kBACiB,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC;IAwE5C;;OAEG;kBACiB,KAAK,OAAO,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAyBzD;;OAEG;qBACoB,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC;IA0C/C;;OAEG;mCACkC,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC;IA6F7D;;OAEG;iCACgC,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC;CA2E5D,CAAC;AAEF,eAAO,MAAM,MAAM;IACjB;;OAEG;eACc,KAAK,cAAc,MAAM,YAAY,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAwCrG;;OAEG;0BAEM,KAAK,cACA,MAAM,WACT,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAmCzB;;OAEG;uBAEM,KAAK,yBACW,MAAM,GAC5B,OAAO,CAAC,OAAO,CAAC;IAuDnB;;OAEG;iBACgB,KAAK,SAAS,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA2D1D;;OAEG;mBAEM,KAAK,cACA,MAAM,UACV,MAAM,GACb,OAAO,CAAC,OAAO,CAAC;IA2DnB;;OAEG;oBACmB,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC;IAsD9C;;OAEG;gBACe,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC;IA6D1C;;;;;OAKG;kBACiB,KAAK,SAAS,MAAM,GAAG,OAAO,CAAC;QACjD,MAAM,EAAE,KAAK,CAAC;YACZ,KAAK,EAAE,MAAM,CAAC;YACd,WAAW,EAAE,MAAM,CAAC;SACrB,CAAC,CAAC;QACH,KAAK,EAAE,MAAM,CAAC;KACf,GAAG,IAAI,CAAC;IAqET;;;OAGG;wBACuB,KAAK,cAAc,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAqFtE;;OAEG;sBACqB,KAAK,cAAc,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAsEpE;;OAEG;kBACiB,KAAK,SAAS,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA0F3D;;OAEG;0BACyB,KAAK,oBAAoB,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA4B9E;;;OAGG;iCAEM,KAAK,uBACS,MAAM,GAC1B,OAAO,CAAC,OAAO,CAAC;IAkDnB;;OAEG;wBAEM,KAAK,uBACS,MAAM,GAC1B,OAAO,CAAC,OAAO,CAAC;IA+DnB;;OAEG;iBAEM,KAAK,cACA,MAAM,UACV,MAAM,aACH,MAAM,eACJ,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAuCzB;;OAEG;gBACe,KAAK,SAAS,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAsBzD;;;;;;;;OAQG;yBAEM,KAAK,uBACS,MAAM,eACd,MAAM,kBACH,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAClC,OAAO,CAAC,OAAO,CAAC;IAwLnB;;;;;;;;;;OAUG;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
CHANGED
|
@@ -1048,23 +1048,23 @@ export const brains = {
|
|
|
1048
1048
|
}
|
|
1049
1049
|
},
|
|
1050
1050
|
/**
|
|
1051
|
-
* Test that
|
|
1052
|
-
* Requires a brain with
|
|
1051
|
+
* Test that agent steps emit proper AGENT_* events in the SSE stream.
|
|
1052
|
+
* Requires a brain with an agent step that will pause on a webhook.
|
|
1053
1053
|
*
|
|
1054
1054
|
* Expected events before webhook pause:
|
|
1055
|
-
* -
|
|
1056
|
-
* -
|
|
1057
|
-
* -
|
|
1058
|
-
* -
|
|
1055
|
+
* - AGENT_START (with prompt and optional system)
|
|
1056
|
+
* - AGENT_ITERATION
|
|
1057
|
+
* - AGENT_TOOL_CALL
|
|
1058
|
+
* - AGENT_WEBHOOK (before WEBHOOK event)
|
|
1059
1059
|
* - WEBHOOK
|
|
1060
1060
|
*/
|
|
1061
|
-
async
|
|
1061
|
+
async watchAgentEvents(fetch, agentBrainIdentifier) {
|
|
1062
1062
|
try {
|
|
1063
|
-
// Start the
|
|
1063
|
+
// Start the agent brain
|
|
1064
1064
|
const runRequest = new Request('http://example.com/brains/runs', {
|
|
1065
1065
|
method: 'POST',
|
|
1066
1066
|
headers: { 'Content-Type': 'application/json' },
|
|
1067
|
-
body: JSON.stringify({ identifier:
|
|
1067
|
+
body: JSON.stringify({ identifier: agentBrainIdentifier }),
|
|
1068
1068
|
});
|
|
1069
1069
|
const runResponse = await fetch(runRequest);
|
|
1070
1070
|
if (runResponse.status !== 201) {
|
|
@@ -1120,72 +1120,72 @@ export const brains = {
|
|
|
1120
1120
|
await reader.cancel();
|
|
1121
1121
|
}
|
|
1122
1122
|
}
|
|
1123
|
-
// Verify required
|
|
1124
|
-
const
|
|
1125
|
-
if (!
|
|
1126
|
-
console.error('Missing
|
|
1123
|
+
// Verify required agent events are present
|
|
1124
|
+
const hasAgentStart = events.some((e) => e.type === BRAIN_EVENTS.AGENT_START);
|
|
1125
|
+
if (!hasAgentStart) {
|
|
1126
|
+
console.error('Missing AGENT_START event in SSE stream');
|
|
1127
1127
|
return false;
|
|
1128
1128
|
}
|
|
1129
|
-
// Verify
|
|
1130
|
-
const
|
|
1131
|
-
if (!
|
|
1132
|
-
console.error('
|
|
1129
|
+
// Verify AGENT_START has prompt field
|
|
1130
|
+
const agentStartEvent = events.find((e) => e.type === BRAIN_EVENTS.AGENT_START);
|
|
1131
|
+
if (!agentStartEvent.prompt || typeof agentStartEvent.prompt !== 'string') {
|
|
1132
|
+
console.error('AGENT_START event missing prompt field');
|
|
1133
1133
|
return false;
|
|
1134
1134
|
}
|
|
1135
|
-
const
|
|
1136
|
-
if (!
|
|
1137
|
-
console.error('Missing
|
|
1135
|
+
const hasAgentIteration = events.some((e) => e.type === BRAIN_EVENTS.AGENT_ITERATION);
|
|
1136
|
+
if (!hasAgentIteration) {
|
|
1137
|
+
console.error('Missing AGENT_ITERATION event in SSE stream');
|
|
1138
1138
|
return false;
|
|
1139
1139
|
}
|
|
1140
|
-
const
|
|
1141
|
-
if (!
|
|
1142
|
-
console.error('Missing
|
|
1140
|
+
const hasAgentToolCall = events.some((e) => e.type === BRAIN_EVENTS.AGENT_TOOL_CALL);
|
|
1141
|
+
if (!hasAgentToolCall) {
|
|
1142
|
+
console.error('Missing AGENT_TOOL_CALL event in SSE stream');
|
|
1143
1143
|
return false;
|
|
1144
1144
|
}
|
|
1145
|
-
// If we got a WEBHOOK event, verify
|
|
1145
|
+
// If we got a WEBHOOK event, verify AGENT_WEBHOOK came before it
|
|
1146
1146
|
const webhookIndex = events.findIndex((e) => e.type === BRAIN_EVENTS.WEBHOOK);
|
|
1147
1147
|
if (webhookIndex !== -1) {
|
|
1148
|
-
const
|
|
1149
|
-
if (
|
|
1150
|
-
console.error('Missing
|
|
1148
|
+
const agentWebhookIndex = events.findIndex((e) => e.type === BRAIN_EVENTS.AGENT_WEBHOOK);
|
|
1149
|
+
if (agentWebhookIndex === -1) {
|
|
1150
|
+
console.error('Missing AGENT_WEBHOOK event before WEBHOOK event');
|
|
1151
1151
|
return false;
|
|
1152
1152
|
}
|
|
1153
|
-
if (
|
|
1154
|
-
console.error('
|
|
1153
|
+
if (agentWebhookIndex >= webhookIndex) {
|
|
1154
|
+
console.error('AGENT_WEBHOOK event must come before WEBHOOK event');
|
|
1155
1155
|
return false;
|
|
1156
1156
|
}
|
|
1157
|
-
// Verify
|
|
1158
|
-
const
|
|
1159
|
-
if (!
|
|
1160
|
-
console.error('
|
|
1157
|
+
// Verify AGENT_WEBHOOK has required fields
|
|
1158
|
+
const agentWebhookEvent = events[agentWebhookIndex];
|
|
1159
|
+
if (!agentWebhookEvent.toolCallId || !agentWebhookEvent.toolName) {
|
|
1160
|
+
console.error('AGENT_WEBHOOK event missing toolCallId or toolName fields');
|
|
1161
1161
|
return false;
|
|
1162
1162
|
}
|
|
1163
1163
|
}
|
|
1164
1164
|
return true;
|
|
1165
1165
|
}
|
|
1166
1166
|
catch (error) {
|
|
1167
|
-
console.error(`Failed to test
|
|
1167
|
+
console.error(`Failed to test agent events for ${agentBrainIdentifier}:`, error);
|
|
1168
1168
|
return false;
|
|
1169
1169
|
}
|
|
1170
1170
|
},
|
|
1171
1171
|
/**
|
|
1172
|
-
* Test full
|
|
1173
|
-
* 1. Start
|
|
1172
|
+
* Test full agent webhook resumption flow:
|
|
1173
|
+
* 1. Start an agent brain that will pause on a webhook
|
|
1174
1174
|
* 2. Verify it pauses with WEBHOOK event
|
|
1175
1175
|
* 3. Trigger the webhook with a response
|
|
1176
|
-
* 4. Verify the brain resumes and emits WEBHOOK_RESPONSE and
|
|
1176
|
+
* 4. Verify the brain resumes and emits WEBHOOK_RESPONSE and AGENT_TOOL_RESULT
|
|
1177
1177
|
*
|
|
1178
1178
|
* Requires:
|
|
1179
|
-
* - A brain with
|
|
1179
|
+
* - A brain with an agent step that calls a tool returning { waitFor: webhook(...) }
|
|
1180
1180
|
* - The webhook slug and identifier to trigger
|
|
1181
1181
|
*/
|
|
1182
|
-
async
|
|
1182
|
+
async agentWebhookResume(fetch, agentBrainIdentifier, webhookSlug, webhookPayload) {
|
|
1183
1183
|
try {
|
|
1184
|
-
// Step 1: Start the
|
|
1184
|
+
// Step 1: Start the agent brain
|
|
1185
1185
|
const runRequest = new Request('http://example.com/brains/runs', {
|
|
1186
1186
|
method: 'POST',
|
|
1187
1187
|
headers: { 'Content-Type': 'application/json' },
|
|
1188
|
-
body: JSON.stringify({ identifier:
|
|
1188
|
+
body: JSON.stringify({ identifier: agentBrainIdentifier }),
|
|
1189
1189
|
});
|
|
1190
1190
|
const runResponse = await fetch(runRequest);
|
|
1191
1191
|
if (runResponse.status !== 201) {
|
|
@@ -1313,10 +1313,10 @@ export const brains = {
|
|
|
1313
1313
|
console.error('Missing WEBHOOK_RESPONSE event after resume');
|
|
1314
1314
|
return false;
|
|
1315
1315
|
}
|
|
1316
|
-
// Verify
|
|
1317
|
-
const
|
|
1318
|
-
if (!
|
|
1319
|
-
console.error('Missing
|
|
1316
|
+
// Verify AGENT_TOOL_RESULT event is present (with the webhook response as result)
|
|
1317
|
+
const hasAgentToolResult = resumeEvents.some((e) => e.type === BRAIN_EVENTS.AGENT_TOOL_RESULT);
|
|
1318
|
+
if (!hasAgentToolResult) {
|
|
1319
|
+
console.error('Missing AGENT_TOOL_RESULT event after resume');
|
|
1320
1320
|
return false;
|
|
1321
1321
|
}
|
|
1322
1322
|
// Verify brain completed successfully
|
|
@@ -1332,7 +1332,197 @@ export const brains = {
|
|
|
1332
1332
|
return true;
|
|
1333
1333
|
}
|
|
1334
1334
|
catch (error) {
|
|
1335
|
-
console.error(`Failed to test
|
|
1335
|
+
console.error(`Failed to test agent webhook resume for ${agentBrainIdentifier}:`, error);
|
|
1336
|
+
return false;
|
|
1337
|
+
}
|
|
1338
|
+
},
|
|
1339
|
+
/**
|
|
1340
|
+
* Test that inner brain COMPLETE events don't overwrite outer brain status.
|
|
1341
|
+
*
|
|
1342
|
+
* This test verifies that when a nested inner brain completes, the outer brain's
|
|
1343
|
+
* status in history remains RUNNING (not prematurely set to COMPLETE).
|
|
1344
|
+
*
|
|
1345
|
+
* Test scenario:
|
|
1346
|
+
* 1. Run outer brain that contains inner brain + webhook step after inner brain
|
|
1347
|
+
* 2. Watch SSE until outer brain's WEBHOOK event (after inner completes)
|
|
1348
|
+
* 3. Query history for the brain
|
|
1349
|
+
* 4. Assert status is RUNNING (not COMPLETE from inner brain)
|
|
1350
|
+
* 5. Trigger webhook to complete outer brain
|
|
1351
|
+
* 6. Assert final status is COMPLETE
|
|
1352
|
+
*/
|
|
1353
|
+
async innerBrainCompleteDoesNotAffectOuterStatus(fetch, outerBrainIdentifier, webhookSlug, webhookPayload) {
|
|
1354
|
+
try {
|
|
1355
|
+
// Step 1: Start the outer brain
|
|
1356
|
+
const runRequest = new Request('http://example.com/brains/runs', {
|
|
1357
|
+
method: 'POST',
|
|
1358
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1359
|
+
body: JSON.stringify({ identifier: outerBrainIdentifier }),
|
|
1360
|
+
});
|
|
1361
|
+
const runResponse = await fetch(runRequest);
|
|
1362
|
+
if (runResponse.status !== 201) {
|
|
1363
|
+
console.error(`POST /brains/runs returned ${runResponse.status}, expected 201`);
|
|
1364
|
+
return false;
|
|
1365
|
+
}
|
|
1366
|
+
const { brainRunId } = (await runResponse.json());
|
|
1367
|
+
// Step 2: Watch SSE until WEBHOOK event from outer brain (after inner completes)
|
|
1368
|
+
const watchRequest = new Request(`http://example.com/brains/runs/${brainRunId}/watch`, { method: 'GET' });
|
|
1369
|
+
const watchResponse = await fetch(watchRequest);
|
|
1370
|
+
if (!watchResponse.ok) {
|
|
1371
|
+
console.error(`GET /brains/runs/${brainRunId}/watch returned ${watchResponse.status}`);
|
|
1372
|
+
return false;
|
|
1373
|
+
}
|
|
1374
|
+
let foundOuterWebhook = false;
|
|
1375
|
+
let innerCompleteCount = 0;
|
|
1376
|
+
if (watchResponse.body) {
|
|
1377
|
+
const reader = watchResponse.body.getReader();
|
|
1378
|
+
const decoder = new TextDecoder();
|
|
1379
|
+
let buffer = '';
|
|
1380
|
+
try {
|
|
1381
|
+
while (!foundOuterWebhook) {
|
|
1382
|
+
const { value, done } = await reader.read();
|
|
1383
|
+
if (done)
|
|
1384
|
+
break;
|
|
1385
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1386
|
+
let eventEndIndex;
|
|
1387
|
+
while ((eventEndIndex = buffer.indexOf('\n\n')) !== -1) {
|
|
1388
|
+
const message = buffer.substring(0, eventEndIndex);
|
|
1389
|
+
buffer = buffer.substring(eventEndIndex + 2);
|
|
1390
|
+
if (message.startsWith('data: ')) {
|
|
1391
|
+
try {
|
|
1392
|
+
const event = JSON.parse(message.substring(6));
|
|
1393
|
+
// Track inner brain completes
|
|
1394
|
+
if (event.type === BRAIN_EVENTS.COMPLETE) {
|
|
1395
|
+
innerCompleteCount++;
|
|
1396
|
+
// First complete is inner brain, second would be outer
|
|
1397
|
+
}
|
|
1398
|
+
// Outer brain webhook (happens after inner brain completes)
|
|
1399
|
+
if (event.type === BRAIN_EVENTS.WEBHOOK) {
|
|
1400
|
+
foundOuterWebhook = true;
|
|
1401
|
+
break;
|
|
1402
|
+
}
|
|
1403
|
+
if (event.type === BRAIN_EVENTS.ERROR) {
|
|
1404
|
+
console.error(`Brain errored: ${JSON.stringify(event.error)}`);
|
|
1405
|
+
return false;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
catch {
|
|
1409
|
+
// Ignore parse errors
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
finally {
|
|
1416
|
+
await reader.cancel();
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
if (!foundOuterWebhook) {
|
|
1420
|
+
console.error('Did not receive outer brain WEBHOOK event');
|
|
1421
|
+
return false;
|
|
1422
|
+
}
|
|
1423
|
+
if (innerCompleteCount === 0) {
|
|
1424
|
+
console.error('Inner brain did not emit COMPLETE event');
|
|
1425
|
+
return false;
|
|
1426
|
+
}
|
|
1427
|
+
// Step 3: Query history - status should be RUNNING, not COMPLETE
|
|
1428
|
+
const historyRequest = new Request(`http://example.com/brains/${encodeURIComponent(outerBrainIdentifier)}/history?limit=1`, { method: 'GET' });
|
|
1429
|
+
const historyResponse = await fetch(historyRequest);
|
|
1430
|
+
if (!historyResponse.ok) {
|
|
1431
|
+
console.error(`GET /brains/${outerBrainIdentifier}/history returned ${historyResponse.status}`);
|
|
1432
|
+
return false;
|
|
1433
|
+
}
|
|
1434
|
+
const historyData = (await historyResponse.json());
|
|
1435
|
+
const ourRun = historyData.runs.find((r) => r.brainRunId === brainRunId);
|
|
1436
|
+
if (!ourRun) {
|
|
1437
|
+
console.error(`Run ${brainRunId} not found in history`);
|
|
1438
|
+
return false;
|
|
1439
|
+
}
|
|
1440
|
+
// KEY ASSERTION: Status should be RUNNING despite inner brain COMPLETE event
|
|
1441
|
+
if (ourRun.status !== STATUS.RUNNING) {
|
|
1442
|
+
console.error(`Expected status '${STATUS.RUNNING}' but got '${ourRun.status}'. ` +
|
|
1443
|
+
`Bug: Inner brain COMPLETE event overwrote outer brain status!`);
|
|
1444
|
+
return false;
|
|
1445
|
+
}
|
|
1446
|
+
// Step 4: Trigger webhook to complete outer brain
|
|
1447
|
+
const webhookRequest = new Request(`http://example.com/webhooks/${encodeURIComponent(webhookSlug)}`, {
|
|
1448
|
+
method: 'POST',
|
|
1449
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1450
|
+
body: JSON.stringify(webhookPayload),
|
|
1451
|
+
});
|
|
1452
|
+
const webhookResponse = await fetch(webhookRequest);
|
|
1453
|
+
if (!webhookResponse.ok) {
|
|
1454
|
+
console.error(`POST /webhooks/${webhookSlug} returned ${webhookResponse.status}`);
|
|
1455
|
+
return false;
|
|
1456
|
+
}
|
|
1457
|
+
// Step 5: Watch for final completion
|
|
1458
|
+
const resumeWatchRequest = new Request(`http://example.com/brains/runs/${brainRunId}/watch`, { method: 'GET' });
|
|
1459
|
+
const resumeWatchResponse = await fetch(resumeWatchRequest);
|
|
1460
|
+
if (!resumeWatchResponse.ok) {
|
|
1461
|
+
console.error(`Resume watch returned ${resumeWatchResponse.status}`);
|
|
1462
|
+
return false;
|
|
1463
|
+
}
|
|
1464
|
+
// Wait for the OUTER brain's COMPLETE event specifically (by tracking depth)
|
|
1465
|
+
// When resuming, the SSE stream includes historical events, so we need to
|
|
1466
|
+
// track START/COMPLETE events to know when the outer brain truly finishes
|
|
1467
|
+
let foundFinalComplete = false;
|
|
1468
|
+
let depth = 0;
|
|
1469
|
+
if (resumeWatchResponse.body) {
|
|
1470
|
+
const reader = resumeWatchResponse.body.getReader();
|
|
1471
|
+
const decoder = new TextDecoder();
|
|
1472
|
+
let buffer = '';
|
|
1473
|
+
try {
|
|
1474
|
+
while (!foundFinalComplete) {
|
|
1475
|
+
const { value, done } = await reader.read();
|
|
1476
|
+
if (done)
|
|
1477
|
+
break;
|
|
1478
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1479
|
+
let eventEndIndex;
|
|
1480
|
+
while ((eventEndIndex = buffer.indexOf('\n\n')) !== -1) {
|
|
1481
|
+
const message = buffer.substring(0, eventEndIndex);
|
|
1482
|
+
buffer = buffer.substring(eventEndIndex + 2);
|
|
1483
|
+
if (message.startsWith('data: ')) {
|
|
1484
|
+
try {
|
|
1485
|
+
const event = JSON.parse(message.substring(6));
|
|
1486
|
+
// Track depth to find the outer brain's COMPLETE
|
|
1487
|
+
if (event.type === BRAIN_EVENTS.START) {
|
|
1488
|
+
depth++;
|
|
1489
|
+
}
|
|
1490
|
+
else if (event.type === BRAIN_EVENTS.COMPLETE) {
|
|
1491
|
+
depth--;
|
|
1492
|
+
// When depth reaches 0, the outer brain has completed
|
|
1493
|
+
if (depth <= 0) {
|
|
1494
|
+
foundFinalComplete = true;
|
|
1495
|
+
break;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
catch {
|
|
1500
|
+
// Ignore parse errors
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
finally {
|
|
1507
|
+
await reader.cancel();
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
// Step 6: Verify final status is COMPLETE
|
|
1511
|
+
const finalHistoryResponse = await fetch(historyRequest);
|
|
1512
|
+
if (!finalHistoryResponse.ok) {
|
|
1513
|
+
console.error('Final history query failed');
|
|
1514
|
+
return false;
|
|
1515
|
+
}
|
|
1516
|
+
const finalHistoryData = (await finalHistoryResponse.json());
|
|
1517
|
+
const finalRun = finalHistoryData.runs.find((r) => r.brainRunId === brainRunId);
|
|
1518
|
+
if (!finalRun || finalRun.status !== STATUS.COMPLETE) {
|
|
1519
|
+
console.error(`Expected final status '${STATUS.COMPLETE}' but got '${finalRun?.status}'`);
|
|
1520
|
+
return false;
|
|
1521
|
+
}
|
|
1522
|
+
return true;
|
|
1523
|
+
}
|
|
1524
|
+
catch (error) {
|
|
1525
|
+
console.error(`Failed to test inner brain complete status for ${outerBrainIdentifier}:`, error);
|
|
1336
1526
|
return false;
|
|
1337
1527
|
}
|
|
1338
1528
|
},
|
|
@@ -1784,6 +1974,97 @@ export const webhooks = {
|
|
|
1784
1974
|
return false;
|
|
1785
1975
|
}
|
|
1786
1976
|
},
|
|
1977
|
+
/**
|
|
1978
|
+
* Test POST /webhooks/system/ui-form - Built-in webhook for UI form submissions.
|
|
1979
|
+
* This is used by pages generated via .ui() steps to submit form data.
|
|
1980
|
+
*
|
|
1981
|
+
* The endpoint:
|
|
1982
|
+
* - Accepts form data (application/x-www-form-urlencoded or multipart/form-data)
|
|
1983
|
+
* - Requires an `identifier` query parameter to match the waiting brain
|
|
1984
|
+
* - Returns { received: true, action: 'resumed' | 'not_found', ... }
|
|
1985
|
+
*/
|
|
1986
|
+
async uiForm(fetch, identifier, formData) {
|
|
1987
|
+
try {
|
|
1988
|
+
// Build URLSearchParams from form data
|
|
1989
|
+
const params = new URLSearchParams();
|
|
1990
|
+
for (const [key, value] of Object.entries(formData)) {
|
|
1991
|
+
if (Array.isArray(value)) {
|
|
1992
|
+
for (const v of value) {
|
|
1993
|
+
params.append(`${key}[]`, v);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
else {
|
|
1997
|
+
params.append(key, value);
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
const request = new Request(`http://example.com/webhooks/system/ui-form?identifier=${encodeURIComponent(identifier)}`, {
|
|
2001
|
+
method: 'POST',
|
|
2002
|
+
headers: {
|
|
2003
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
2004
|
+
},
|
|
2005
|
+
body: params.toString(),
|
|
2006
|
+
});
|
|
2007
|
+
const response = await fetch(request);
|
|
2008
|
+
// Accept 200 (found and processed) or 404 (no brain waiting)
|
|
2009
|
+
if (response.status !== 200 && response.status !== 404) {
|
|
2010
|
+
console.error(`POST /webhooks/system/ui-form returned ${response.status}, expected 200 or 404`);
|
|
2011
|
+
return false;
|
|
2012
|
+
}
|
|
2013
|
+
const data = (await response.json());
|
|
2014
|
+
// Validate response structure
|
|
2015
|
+
if (typeof data.received !== 'boolean') {
|
|
2016
|
+
console.error(`Expected received to be boolean, got ${typeof data.received}`);
|
|
2017
|
+
return false;
|
|
2018
|
+
}
|
|
2019
|
+
if (!data.action || typeof data.action !== 'string') {
|
|
2020
|
+
console.error(`Expected action to be string, got ${typeof data.action}`);
|
|
2021
|
+
return false;
|
|
2022
|
+
}
|
|
2023
|
+
// Action should be 'resumed' or 'not_found'
|
|
2024
|
+
if (data.action !== 'resumed' && data.action !== 'not_found') {
|
|
2025
|
+
console.error(`Expected action to be 'resumed' or 'not_found', got '${data.action}'`);
|
|
2026
|
+
return false;
|
|
2027
|
+
}
|
|
2028
|
+
return true;
|
|
2029
|
+
}
|
|
2030
|
+
catch (error) {
|
|
2031
|
+
console.error('Failed to test POST /webhooks/system/ui-form:', error);
|
|
2032
|
+
return false;
|
|
2033
|
+
}
|
|
2034
|
+
},
|
|
2035
|
+
/**
|
|
2036
|
+
* Test POST /webhooks/system/ui-form with missing identifier - Should return 400
|
|
2037
|
+
*/
|
|
2038
|
+
async uiFormMissingIdentifier(fetch) {
|
|
2039
|
+
try {
|
|
2040
|
+
const request = new Request('http://example.com/webhooks/system/ui-form', {
|
|
2041
|
+
method: 'POST',
|
|
2042
|
+
headers: {
|
|
2043
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
2044
|
+
},
|
|
2045
|
+
body: 'test=data',
|
|
2046
|
+
});
|
|
2047
|
+
const response = await fetch(request);
|
|
2048
|
+
if (response.status !== 400) {
|
|
2049
|
+
console.error(`POST /webhooks/system/ui-form without identifier returned ${response.status}, expected 400`);
|
|
2050
|
+
return false;
|
|
2051
|
+
}
|
|
2052
|
+
const data = (await response.json());
|
|
2053
|
+
if (!data.error || typeof data.error !== 'string') {
|
|
2054
|
+
console.error(`Expected error to be string, got ${typeof data.error}`);
|
|
2055
|
+
return false;
|
|
2056
|
+
}
|
|
2057
|
+
if (!data.error.toLowerCase().includes('identifier')) {
|
|
2058
|
+
console.error(`Expected error message to mention identifier, got: ${data.error}`);
|
|
2059
|
+
return false;
|
|
2060
|
+
}
|
|
2061
|
+
return true;
|
|
2062
|
+
}
|
|
2063
|
+
catch (error) {
|
|
2064
|
+
console.error('Failed to test POST /webhooks/system/ui-form without identifier:', error);
|
|
2065
|
+
return false;
|
|
2066
|
+
}
|
|
2067
|
+
},
|
|
1787
2068
|
};
|
|
1788
2069
|
export const pages = {
|
|
1789
2070
|
/**
|
|
@@ -2116,4 +2397,59 @@ export const pages = {
|
|
|
2116
2397
|
}
|
|
2117
2398
|
},
|
|
2118
2399
|
};
|
|
2400
|
+
/**
|
|
2401
|
+
* Bundle API Tests
|
|
2402
|
+
*
|
|
2403
|
+
* Tests for the /bundle/components.js endpoint which serves the component bundle.
|
|
2404
|
+
*
|
|
2405
|
+
* NOTE: These tests only verify the API endpoint behavior. The bundle build and
|
|
2406
|
+
* upload process is backend-specific and must be tested separately by each
|
|
2407
|
+
* backend implementation.
|
|
2408
|
+
*/
|
|
2409
|
+
export const bundle = {
|
|
2410
|
+
/**
|
|
2411
|
+
* Test GET /bundle/components.js - Serve the component bundle
|
|
2412
|
+
*/
|
|
2413
|
+
async get(fetch) {
|
|
2414
|
+
try {
|
|
2415
|
+
const request = new Request('http://example.com/bundle/components.js', {
|
|
2416
|
+
method: 'GET',
|
|
2417
|
+
});
|
|
2418
|
+
const response = await fetch(request);
|
|
2419
|
+
// Bundle may or may not exist depending on project setup
|
|
2420
|
+
// 200 = bundle exists and served correctly
|
|
2421
|
+
// 404 = bundle not found (expected if no components/ directory)
|
|
2422
|
+
if (response.status !== 200 && response.status !== 404) {
|
|
2423
|
+
console.error(`GET /bundle/components.js returned unexpected status ${response.status}`);
|
|
2424
|
+
return false;
|
|
2425
|
+
}
|
|
2426
|
+
const contentType = response.headers.get('Content-Type');
|
|
2427
|
+
if (!contentType || !contentType.includes('application/javascript')) {
|
|
2428
|
+
console.error(`Expected Content-Type application/javascript, got ${contentType}`);
|
|
2429
|
+
return false;
|
|
2430
|
+
}
|
|
2431
|
+
// If 200, verify we got some content
|
|
2432
|
+
if (response.status === 200) {
|
|
2433
|
+
const content = await response.text();
|
|
2434
|
+
if (!content || content.length === 0) {
|
|
2435
|
+
console.error('Bundle endpoint returned 200 but empty content');
|
|
2436
|
+
return false;
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
// If 404, verify we got the helpful error message
|
|
2440
|
+
if (response.status === 404) {
|
|
2441
|
+
const content = await response.text();
|
|
2442
|
+
if (!content.includes('Bundle not found')) {
|
|
2443
|
+
console.error('Bundle 404 response missing helpful error message');
|
|
2444
|
+
return false;
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
return true;
|
|
2448
|
+
}
|
|
2449
|
+
catch (error) {
|
|
2450
|
+
console.error(`Failed to test GET /bundle/components.js:`, error);
|
|
2451
|
+
return false;
|
|
2452
|
+
}
|
|
2453
|
+
},
|
|
2454
|
+
};
|
|
2119
2455
|
//# sourceMappingURL=api.js.map
|