@positronic/spec 0.0.37 → 0.0.39

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/src/api.js CHANGED
@@ -122,7 +122,7 @@ function _ts_generator(thisArg, body) {
122
122
  };
123
123
  }
124
124
  }
125
- import { STATUS } from '@positronic/core';
125
+ import { STATUS, BRAIN_EVENTS } from '@positronic/core';
126
126
  export function testStatus(fetch) {
127
127
  return _async_to_generator(function() {
128
128
  var request, response, data, error;
@@ -1392,6 +1392,116 @@ export var brains = {
1392
1392
  });
1393
1393
  })();
1394
1394
  },
1395
+ search: /**
1396
+ * Test GET /brains?q=<query> - Search brains by query string
1397
+ * Returns brains matching the query (by title, filename, or description).
1398
+ * The matching algorithm is implementation-defined; the spec only verifies
1399
+ * the response structure and that results are relevant to the query.
1400
+ */ function search(fetch, query) {
1401
+ return _async_to_generator(function() {
1402
+ var url, request, response, data, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, brain, error;
1403
+ return _ts_generator(this, function(_state) {
1404
+ switch(_state.label){
1405
+ case 0:
1406
+ _state.trys.push([
1407
+ 0,
1408
+ 3,
1409
+ ,
1410
+ 4
1411
+ ]);
1412
+ url = new URL('http://example.com/brains');
1413
+ url.searchParams.set('q', query);
1414
+ request = new Request(url.toString(), {
1415
+ method: 'GET'
1416
+ });
1417
+ return [
1418
+ 4,
1419
+ fetch(request)
1420
+ ];
1421
+ case 1:
1422
+ response = _state.sent();
1423
+ if (!response.ok) {
1424
+ console.error("GET /brains?q=".concat(query, " returned ").concat(response.status));
1425
+ return [
1426
+ 2,
1427
+ null
1428
+ ];
1429
+ }
1430
+ return [
1431
+ 4,
1432
+ response.json()
1433
+ ];
1434
+ case 2:
1435
+ data = _state.sent();
1436
+ // Validate response structure
1437
+ if (!Array.isArray(data.brains)) {
1438
+ console.error("Expected brains to be an array, got ".concat(_type_of(data.brains)));
1439
+ return [
1440
+ 2,
1441
+ null
1442
+ ];
1443
+ }
1444
+ if (typeof data.count !== 'number') {
1445
+ console.error("Expected count to be number, got ".concat(_type_of(data.count)));
1446
+ return [
1447
+ 2,
1448
+ null
1449
+ ];
1450
+ }
1451
+ _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1452
+ try {
1453
+ // Validate each brain has required fields
1454
+ for(_iterator = data.brains[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1455
+ brain = _step.value;
1456
+ if (!brain.title || typeof brain.title !== 'string' || !brain.description || typeof brain.description !== 'string') {
1457
+ console.error("Brain missing required fields or has invalid types: ".concat(JSON.stringify(brain)));
1458
+ return [
1459
+ 2,
1460
+ null
1461
+ ];
1462
+ }
1463
+ }
1464
+ } catch (err) {
1465
+ _didIteratorError = true;
1466
+ _iteratorError = err;
1467
+ } finally{
1468
+ try {
1469
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1470
+ _iterator.return();
1471
+ }
1472
+ } finally{
1473
+ if (_didIteratorError) {
1474
+ throw _iteratorError;
1475
+ }
1476
+ }
1477
+ }
1478
+ // Count should match array length
1479
+ if (data.count !== data.brains.length) {
1480
+ console.error("Count (".concat(data.count, ") does not match brains array length (").concat(data.brains.length, ")"));
1481
+ return [
1482
+ 2,
1483
+ null
1484
+ ];
1485
+ }
1486
+ return [
1487
+ 2,
1488
+ data
1489
+ ];
1490
+ case 3:
1491
+ error = _state.sent();
1492
+ console.error("Failed to test GET /brains?q=".concat(query, ":"), error);
1493
+ return [
1494
+ 2,
1495
+ null
1496
+ ];
1497
+ case 4:
1498
+ return [
1499
+ 2
1500
+ ];
1501
+ }
1502
+ });
1503
+ })();
1504
+ },
1395
1505
  getBrainInfo: /**
1396
1506
  * Test GET /brains/:identifier - Get brain structure/definition
1397
1507
  * (For future brain exploration/info command)
@@ -2066,6 +2176,870 @@ export var brains = {
2066
2176
  }
2067
2177
  });
2068
2178
  })();
2179
+ },
2180
+ killSuspended: /**
2181
+ * Test DELETE /brains/runs/:runId for a brain suspended on a webhook.
2182
+ * This tests that killing a webhook-suspended brain:
2183
+ * 1. Returns 204 (not 409)
2184
+ * 2. Updates status to CANCELLED
2185
+ * 3. Clears webhook registrations (webhook no longer resumes the brain)
2186
+ *
2187
+ * Requires a brain with a loop step that will pause on a webhook.
2188
+ */ function killSuspended(fetch, loopBrainIdentifier, webhookSlug, webhookPayload) {
2189
+ return _async_to_generator(function() {
2190
+ var runRequest, runResponse, brainRunId, watchRequest, watchResponse, foundWebhookEvent, reader, decoder, buffer, _ref, value, done, eventEndIndex, message, event, killRequest, killResponse, getRunRequest, getRunResponse, runData, webhookRequest, webhookResponse, webhookResult, finalCheckRequest, finalCheckResponse, finalRunData, error;
2191
+ return _ts_generator(this, function(_state) {
2192
+ switch(_state.label){
2193
+ case 0:
2194
+ _state.trys.push([
2195
+ 0,
2196
+ 18,
2197
+ ,
2198
+ 19
2199
+ ]);
2200
+ // Step 1: Start the loop brain
2201
+ runRequest = new Request('http://example.com/brains/runs', {
2202
+ method: 'POST',
2203
+ headers: {
2204
+ 'Content-Type': 'application/json'
2205
+ },
2206
+ body: JSON.stringify({
2207
+ identifier: loopBrainIdentifier
2208
+ })
2209
+ });
2210
+ return [
2211
+ 4,
2212
+ fetch(runRequest)
2213
+ ];
2214
+ case 1:
2215
+ runResponse = _state.sent();
2216
+ if (runResponse.status !== 201) {
2217
+ console.error("POST /brains/runs returned ".concat(runResponse.status, ", expected 201"));
2218
+ return [
2219
+ 2,
2220
+ false
2221
+ ];
2222
+ }
2223
+ return [
2224
+ 4,
2225
+ runResponse.json()
2226
+ ];
2227
+ case 2:
2228
+ brainRunId = _state.sent().brainRunId;
2229
+ // Step 2: Watch until WEBHOOK event (brain pauses)
2230
+ watchRequest = new Request("http://example.com/brains/runs/".concat(brainRunId, "/watch"), {
2231
+ method: 'GET'
2232
+ });
2233
+ return [
2234
+ 4,
2235
+ fetch(watchRequest)
2236
+ ];
2237
+ case 3:
2238
+ watchResponse = _state.sent();
2239
+ if (!watchResponse.ok) {
2240
+ console.error("GET /brains/runs/".concat(brainRunId, "/watch returned ").concat(watchResponse.status));
2241
+ return [
2242
+ 2,
2243
+ false
2244
+ ];
2245
+ }
2246
+ foundWebhookEvent = false;
2247
+ if (!watchResponse.body) return [
2248
+ 3,
2249
+ 10
2250
+ ];
2251
+ reader = watchResponse.body.getReader();
2252
+ decoder = new TextDecoder();
2253
+ buffer = '';
2254
+ _state.label = 4;
2255
+ case 4:
2256
+ _state.trys.push([
2257
+ 4,
2258
+ ,
2259
+ 8,
2260
+ 10
2261
+ ]);
2262
+ _state.label = 5;
2263
+ case 5:
2264
+ if (!!foundWebhookEvent) return [
2265
+ 3,
2266
+ 7
2267
+ ];
2268
+ return [
2269
+ 4,
2270
+ reader.read()
2271
+ ];
2272
+ case 6:
2273
+ _ref = _state.sent(), value = _ref.value, done = _ref.done;
2274
+ if (done) return [
2275
+ 3,
2276
+ 7
2277
+ ];
2278
+ buffer += decoder.decode(value, {
2279
+ stream: true
2280
+ });
2281
+ eventEndIndex = void 0;
2282
+ while((eventEndIndex = buffer.indexOf('\n\n')) !== -1){
2283
+ message = buffer.substring(0, eventEndIndex);
2284
+ buffer = buffer.substring(eventEndIndex + 2);
2285
+ if (message.startsWith('data: ')) {
2286
+ try {
2287
+ event = JSON.parse(message.substring(6));
2288
+ if (event.type === BRAIN_EVENTS.WEBHOOK) {
2289
+ foundWebhookEvent = true;
2290
+ break;
2291
+ }
2292
+ if (event.type === BRAIN_EVENTS.COMPLETE || event.type === BRAIN_EVENTS.ERROR) {
2293
+ console.error("Brain completed/errored before WEBHOOK event: ".concat(event.type));
2294
+ return [
2295
+ 2,
2296
+ false
2297
+ ];
2298
+ }
2299
+ } catch (e) {
2300
+ // Ignore parse errors
2301
+ }
2302
+ }
2303
+ }
2304
+ return [
2305
+ 3,
2306
+ 5
2307
+ ];
2308
+ case 7:
2309
+ return [
2310
+ 3,
2311
+ 10
2312
+ ];
2313
+ case 8:
2314
+ return [
2315
+ 4,
2316
+ reader.cancel()
2317
+ ];
2318
+ case 9:
2319
+ _state.sent();
2320
+ return [
2321
+ 7
2322
+ ];
2323
+ case 10:
2324
+ if (!foundWebhookEvent) {
2325
+ console.error('Brain did not emit WEBHOOK event');
2326
+ return [
2327
+ 2,
2328
+ false
2329
+ ];
2330
+ }
2331
+ // Step 3: Kill the suspended brain
2332
+ killRequest = new Request("http://example.com/brains/runs/".concat(brainRunId), {
2333
+ method: 'DELETE'
2334
+ });
2335
+ return [
2336
+ 4,
2337
+ fetch(killRequest)
2338
+ ];
2339
+ case 11:
2340
+ killResponse = _state.sent();
2341
+ if (killResponse.status !== 204) {
2342
+ console.error("DELETE /brains/runs/".concat(brainRunId, " returned ").concat(killResponse.status, ", expected 204"));
2343
+ return [
2344
+ 2,
2345
+ false
2346
+ ];
2347
+ }
2348
+ // Step 4: Verify status is CANCELLED via getRun
2349
+ getRunRequest = new Request("http://example.com/brains/runs/".concat(brainRunId), {
2350
+ method: 'GET'
2351
+ });
2352
+ return [
2353
+ 4,
2354
+ fetch(getRunRequest)
2355
+ ];
2356
+ case 12:
2357
+ getRunResponse = _state.sent();
2358
+ if (!getRunResponse.ok) {
2359
+ console.error("GET /brains/runs/".concat(brainRunId, " returned ").concat(getRunResponse.status));
2360
+ return [
2361
+ 2,
2362
+ false
2363
+ ];
2364
+ }
2365
+ return [
2366
+ 4,
2367
+ getRunResponse.json()
2368
+ ];
2369
+ case 13:
2370
+ runData = _state.sent();
2371
+ if (runData.status !== STATUS.CANCELLED) {
2372
+ console.error("Expected status to be '".concat(STATUS.CANCELLED, "', got '").concat(runData.status, "'"));
2373
+ return [
2374
+ 2,
2375
+ false
2376
+ ];
2377
+ }
2378
+ // Step 5: Verify webhook no longer resumes the brain
2379
+ // Send a webhook - it should return 'no-match' since registrations were cleared
2380
+ webhookRequest = new Request("http://example.com/webhooks/".concat(encodeURIComponent(webhookSlug)), {
2381
+ method: 'POST',
2382
+ headers: {
2383
+ 'Content-Type': 'application/json'
2384
+ },
2385
+ body: JSON.stringify(webhookPayload)
2386
+ });
2387
+ return [
2388
+ 4,
2389
+ fetch(webhookRequest)
2390
+ ];
2391
+ case 14:
2392
+ webhookResponse = _state.sent();
2393
+ // Accept 200/202 - the important thing is it doesn't resume the brain
2394
+ if (!webhookResponse.ok) {
2395
+ console.error("POST /webhooks/".concat(webhookSlug, " returned ").concat(webhookResponse.status));
2396
+ return [
2397
+ 2,
2398
+ false
2399
+ ];
2400
+ }
2401
+ return [
2402
+ 4,
2403
+ webhookResponse.json()
2404
+ ];
2405
+ case 15:
2406
+ webhookResult = _state.sent();
2407
+ // The action should be 'no-match' since webhook registrations were cleared
2408
+ if (webhookResult.action === 'resumed') {
2409
+ console.error('Webhook resumed the brain after it was killed - webhook registrations were not cleared');
2410
+ return [
2411
+ 2,
2412
+ false
2413
+ ];
2414
+ }
2415
+ // Verify the brain is still CANCELLED (didn't restart)
2416
+ finalCheckRequest = new Request("http://example.com/brains/runs/".concat(brainRunId), {
2417
+ method: 'GET'
2418
+ });
2419
+ return [
2420
+ 4,
2421
+ fetch(finalCheckRequest)
2422
+ ];
2423
+ case 16:
2424
+ finalCheckResponse = _state.sent();
2425
+ if (!finalCheckResponse.ok) {
2426
+ console.error("Final GET /brains/runs/".concat(brainRunId, " returned ").concat(finalCheckResponse.status));
2427
+ return [
2428
+ 2,
2429
+ false
2430
+ ];
2431
+ }
2432
+ return [
2433
+ 4,
2434
+ finalCheckResponse.json()
2435
+ ];
2436
+ case 17:
2437
+ finalRunData = _state.sent();
2438
+ if (finalRunData.status !== STATUS.CANCELLED) {
2439
+ console.error("Final status check: expected '".concat(STATUS.CANCELLED, "', got '").concat(finalRunData.status, "'"));
2440
+ return [
2441
+ 2,
2442
+ false
2443
+ ];
2444
+ }
2445
+ return [
2446
+ 2,
2447
+ true
2448
+ ];
2449
+ case 18:
2450
+ error = _state.sent();
2451
+ console.error("Failed to test kill suspended brain for ".concat(loopBrainIdentifier, ":"), error);
2452
+ return [
2453
+ 2,
2454
+ false
2455
+ ];
2456
+ case 19:
2457
+ return [
2458
+ 2
2459
+ ];
2460
+ }
2461
+ });
2462
+ })();
2463
+ },
2464
+ watchLoopEvents: /**
2465
+ * Test that loop steps emit proper LOOP_* events in the SSE stream.
2466
+ * Requires a brain with a loop step that will pause on a webhook.
2467
+ *
2468
+ * Expected events before webhook pause:
2469
+ * - LOOP_START (with prompt and optional system)
2470
+ * - LOOP_ITERATION
2471
+ * - LOOP_TOOL_CALL
2472
+ * - LOOP_WEBHOOK (before WEBHOOK event)
2473
+ * - WEBHOOK
2474
+ */ function watchLoopEvents(fetch, loopBrainIdentifier) {
2475
+ return _async_to_generator(function() {
2476
+ var runRequest, runResponse, brainRunId, watchRequest, watchResponse, events, reader, decoder, buffer, done, _ref, value, streamDone, eventEndIndex, message, event, hasLoopStart, loopStartEvent, hasLoopIteration, hasLoopToolCall, webhookIndex, loopWebhookIndex, loopWebhookEvent, error;
2477
+ return _ts_generator(this, function(_state) {
2478
+ switch(_state.label){
2479
+ case 0:
2480
+ _state.trys.push([
2481
+ 0,
2482
+ 11,
2483
+ ,
2484
+ 12
2485
+ ]);
2486
+ // Start the loop brain
2487
+ runRequest = new Request('http://example.com/brains/runs', {
2488
+ method: 'POST',
2489
+ headers: {
2490
+ 'Content-Type': 'application/json'
2491
+ },
2492
+ body: JSON.stringify({
2493
+ identifier: loopBrainIdentifier
2494
+ })
2495
+ });
2496
+ return [
2497
+ 4,
2498
+ fetch(runRequest)
2499
+ ];
2500
+ case 1:
2501
+ runResponse = _state.sent();
2502
+ if (runResponse.status !== 201) {
2503
+ console.error("POST /brains/runs returned ".concat(runResponse.status, ", expected 201"));
2504
+ return [
2505
+ 2,
2506
+ false
2507
+ ];
2508
+ }
2509
+ return [
2510
+ 4,
2511
+ runResponse.json()
2512
+ ];
2513
+ case 2:
2514
+ brainRunId = _state.sent().brainRunId;
2515
+ // Watch the brain run
2516
+ watchRequest = new Request("http://example.com/brains/runs/".concat(brainRunId, "/watch"), {
2517
+ method: 'GET'
2518
+ });
2519
+ return [
2520
+ 4,
2521
+ fetch(watchRequest)
2522
+ ];
2523
+ case 3:
2524
+ watchResponse = _state.sent();
2525
+ if (!watchResponse.ok) {
2526
+ console.error("GET /brains/runs/".concat(brainRunId, "/watch returned ").concat(watchResponse.status));
2527
+ return [
2528
+ 2,
2529
+ false
2530
+ ];
2531
+ }
2532
+ // Read SSE events until we get WEBHOOK or COMPLETE/ERROR
2533
+ events = [];
2534
+ if (!watchResponse.body) return [
2535
+ 3,
2536
+ 10
2537
+ ];
2538
+ reader = watchResponse.body.getReader();
2539
+ decoder = new TextDecoder();
2540
+ buffer = '';
2541
+ done = false;
2542
+ _state.label = 4;
2543
+ case 4:
2544
+ _state.trys.push([
2545
+ 4,
2546
+ ,
2547
+ 8,
2548
+ 10
2549
+ ]);
2550
+ _state.label = 5;
2551
+ case 5:
2552
+ if (!!done) return [
2553
+ 3,
2554
+ 7
2555
+ ];
2556
+ return [
2557
+ 4,
2558
+ reader.read()
2559
+ ];
2560
+ case 6:
2561
+ _ref = _state.sent(), value = _ref.value, streamDone = _ref.done;
2562
+ if (streamDone) return [
2563
+ 3,
2564
+ 7
2565
+ ];
2566
+ buffer += decoder.decode(value, {
2567
+ stream: true
2568
+ });
2569
+ // Process complete SSE messages
2570
+ eventEndIndex = void 0;
2571
+ while((eventEndIndex = buffer.indexOf('\n\n')) !== -1){
2572
+ message = buffer.substring(0, eventEndIndex);
2573
+ buffer = buffer.substring(eventEndIndex + 2);
2574
+ if (message.startsWith('data: ')) {
2575
+ try {
2576
+ event = JSON.parse(message.substring(6));
2577
+ events.push(event);
2578
+ // Stop on terminal events
2579
+ if (event.type === BRAIN_EVENTS.WEBHOOK || event.type === BRAIN_EVENTS.COMPLETE || event.type === BRAIN_EVENTS.ERROR) {
2580
+ done = true;
2581
+ break;
2582
+ }
2583
+ } catch (e) {
2584
+ // Ignore parse errors
2585
+ }
2586
+ }
2587
+ }
2588
+ return [
2589
+ 3,
2590
+ 5
2591
+ ];
2592
+ case 7:
2593
+ return [
2594
+ 3,
2595
+ 10
2596
+ ];
2597
+ case 8:
2598
+ return [
2599
+ 4,
2600
+ reader.cancel()
2601
+ ];
2602
+ case 9:
2603
+ _state.sent();
2604
+ return [
2605
+ 7
2606
+ ];
2607
+ case 10:
2608
+ // Verify required loop events are present
2609
+ hasLoopStart = events.some(function(e) {
2610
+ return e.type === BRAIN_EVENTS.LOOP_START;
2611
+ });
2612
+ if (!hasLoopStart) {
2613
+ console.error('Missing LOOP_START event in SSE stream');
2614
+ return [
2615
+ 2,
2616
+ false
2617
+ ];
2618
+ }
2619
+ // Verify LOOP_START has prompt field
2620
+ loopStartEvent = events.find(function(e) {
2621
+ return e.type === BRAIN_EVENTS.LOOP_START;
2622
+ });
2623
+ if (!loopStartEvent.prompt || typeof loopStartEvent.prompt !== 'string') {
2624
+ console.error('LOOP_START event missing prompt field');
2625
+ return [
2626
+ 2,
2627
+ false
2628
+ ];
2629
+ }
2630
+ hasLoopIteration = events.some(function(e) {
2631
+ return e.type === BRAIN_EVENTS.LOOP_ITERATION;
2632
+ });
2633
+ if (!hasLoopIteration) {
2634
+ console.error('Missing LOOP_ITERATION event in SSE stream');
2635
+ return [
2636
+ 2,
2637
+ false
2638
+ ];
2639
+ }
2640
+ hasLoopToolCall = events.some(function(e) {
2641
+ return e.type === BRAIN_EVENTS.LOOP_TOOL_CALL;
2642
+ });
2643
+ if (!hasLoopToolCall) {
2644
+ console.error('Missing LOOP_TOOL_CALL event in SSE stream');
2645
+ return [
2646
+ 2,
2647
+ false
2648
+ ];
2649
+ }
2650
+ // If we got a WEBHOOK event, verify LOOP_WEBHOOK came before it
2651
+ webhookIndex = events.findIndex(function(e) {
2652
+ return e.type === BRAIN_EVENTS.WEBHOOK;
2653
+ });
2654
+ if (webhookIndex !== -1) {
2655
+ loopWebhookIndex = events.findIndex(function(e) {
2656
+ return e.type === BRAIN_EVENTS.LOOP_WEBHOOK;
2657
+ });
2658
+ if (loopWebhookIndex === -1) {
2659
+ console.error('Missing LOOP_WEBHOOK event before WEBHOOK event');
2660
+ return [
2661
+ 2,
2662
+ false
2663
+ ];
2664
+ }
2665
+ if (loopWebhookIndex >= webhookIndex) {
2666
+ console.error('LOOP_WEBHOOK event must come before WEBHOOK event');
2667
+ return [
2668
+ 2,
2669
+ false
2670
+ ];
2671
+ }
2672
+ // Verify LOOP_WEBHOOK has required fields
2673
+ loopWebhookEvent = events[loopWebhookIndex];
2674
+ if (!loopWebhookEvent.toolCallId || !loopWebhookEvent.toolName) {
2675
+ console.error('LOOP_WEBHOOK event missing toolCallId or toolName fields');
2676
+ return [
2677
+ 2,
2678
+ false
2679
+ ];
2680
+ }
2681
+ }
2682
+ return [
2683
+ 2,
2684
+ true
2685
+ ];
2686
+ case 11:
2687
+ error = _state.sent();
2688
+ console.error("Failed to test loop events for ".concat(loopBrainIdentifier, ":"), error);
2689
+ return [
2690
+ 2,
2691
+ false
2692
+ ];
2693
+ case 12:
2694
+ return [
2695
+ 2
2696
+ ];
2697
+ }
2698
+ });
2699
+ })();
2700
+ },
2701
+ loopWebhookResume: /**
2702
+ * Test full loop webhook resumption flow:
2703
+ * 1. Start a loop brain that will pause on a webhook
2704
+ * 2. Verify it pauses with WEBHOOK event
2705
+ * 3. Trigger the webhook with a response
2706
+ * 4. Verify the brain resumes and emits WEBHOOK_RESPONSE and LOOP_TOOL_RESULT
2707
+ *
2708
+ * Requires:
2709
+ * - A brain with a loop step that calls a tool returning { waitFor: webhook(...) }
2710
+ * - The webhook slug and identifier to trigger
2711
+ */ function loopWebhookResume(fetch, loopBrainIdentifier, webhookSlug, webhookPayload) {
2712
+ return _async_to_generator(function() {
2713
+ var runRequest, runResponse, brainRunId, watchRequest, watchResponse, foundWebhookEvent, reader, decoder, buffer, _ref, value, done, eventEndIndex, message, event, webhookRequest, webhookResponse, webhookResult, resumeWatchRequest, resumeWatchResponse, resumeEvents, reader1, decoder1, buffer1, done1, _ref1, value1, streamDone, eventEndIndex1, message1, event1, hasWebhookResponse, hasLoopToolResult, completeEvent, error;
2714
+ return _ts_generator(this, function(_state) {
2715
+ switch(_state.label){
2716
+ case 0:
2717
+ _state.trys.push([
2718
+ 0,
2719
+ 21,
2720
+ ,
2721
+ 22
2722
+ ]);
2723
+ // Step 1: Start the loop brain
2724
+ runRequest = new Request('http://example.com/brains/runs', {
2725
+ method: 'POST',
2726
+ headers: {
2727
+ 'Content-Type': 'application/json'
2728
+ },
2729
+ body: JSON.stringify({
2730
+ identifier: loopBrainIdentifier
2731
+ })
2732
+ });
2733
+ return [
2734
+ 4,
2735
+ fetch(runRequest)
2736
+ ];
2737
+ case 1:
2738
+ runResponse = _state.sent();
2739
+ if (runResponse.status !== 201) {
2740
+ console.error("POST /brains/runs returned ".concat(runResponse.status, ", expected 201"));
2741
+ return [
2742
+ 2,
2743
+ false
2744
+ ];
2745
+ }
2746
+ return [
2747
+ 4,
2748
+ runResponse.json()
2749
+ ];
2750
+ case 2:
2751
+ brainRunId = _state.sent().brainRunId;
2752
+ // Step 2: Watch until WEBHOOK event (brain pauses)
2753
+ watchRequest = new Request("http://example.com/brains/runs/".concat(brainRunId, "/watch"), {
2754
+ method: 'GET'
2755
+ });
2756
+ return [
2757
+ 4,
2758
+ fetch(watchRequest)
2759
+ ];
2760
+ case 3:
2761
+ watchResponse = _state.sent();
2762
+ if (!watchResponse.ok) {
2763
+ console.error("GET /brains/runs/".concat(brainRunId, "/watch returned ").concat(watchResponse.status));
2764
+ return [
2765
+ 2,
2766
+ false
2767
+ ];
2768
+ }
2769
+ foundWebhookEvent = false;
2770
+ if (!watchResponse.body) return [
2771
+ 3,
2772
+ 10
2773
+ ];
2774
+ reader = watchResponse.body.getReader();
2775
+ decoder = new TextDecoder();
2776
+ buffer = '';
2777
+ _state.label = 4;
2778
+ case 4:
2779
+ _state.trys.push([
2780
+ 4,
2781
+ ,
2782
+ 8,
2783
+ 10
2784
+ ]);
2785
+ _state.label = 5;
2786
+ case 5:
2787
+ if (!!foundWebhookEvent) return [
2788
+ 3,
2789
+ 7
2790
+ ];
2791
+ return [
2792
+ 4,
2793
+ reader.read()
2794
+ ];
2795
+ case 6:
2796
+ _ref = _state.sent(), value = _ref.value, done = _ref.done;
2797
+ if (done) return [
2798
+ 3,
2799
+ 7
2800
+ ];
2801
+ buffer += decoder.decode(value, {
2802
+ stream: true
2803
+ });
2804
+ eventEndIndex = void 0;
2805
+ while((eventEndIndex = buffer.indexOf('\n\n')) !== -1){
2806
+ message = buffer.substring(0, eventEndIndex);
2807
+ buffer = buffer.substring(eventEndIndex + 2);
2808
+ if (message.startsWith('data: ')) {
2809
+ try {
2810
+ event = JSON.parse(message.substring(6));
2811
+ if (event.type === BRAIN_EVENTS.WEBHOOK) {
2812
+ foundWebhookEvent = true;
2813
+ break;
2814
+ }
2815
+ if (event.type === BRAIN_EVENTS.COMPLETE || event.type === BRAIN_EVENTS.ERROR) {
2816
+ console.error("Brain completed/errored before WEBHOOK event: ".concat(event.type));
2817
+ return [
2818
+ 2,
2819
+ false
2820
+ ];
2821
+ }
2822
+ } catch (e) {
2823
+ // Ignore parse errors
2824
+ }
2825
+ }
2826
+ }
2827
+ return [
2828
+ 3,
2829
+ 5
2830
+ ];
2831
+ case 7:
2832
+ return [
2833
+ 3,
2834
+ 10
2835
+ ];
2836
+ case 8:
2837
+ return [
2838
+ 4,
2839
+ reader.cancel()
2840
+ ];
2841
+ case 9:
2842
+ _state.sent();
2843
+ return [
2844
+ 7
2845
+ ];
2846
+ case 10:
2847
+ if (!foundWebhookEvent) {
2848
+ console.error('Brain did not emit WEBHOOK event');
2849
+ return [
2850
+ 2,
2851
+ false
2852
+ ];
2853
+ }
2854
+ // Step 3: Trigger the webhook
2855
+ webhookRequest = new Request("http://example.com/webhooks/".concat(encodeURIComponent(webhookSlug)), {
2856
+ method: 'POST',
2857
+ headers: {
2858
+ 'Content-Type': 'application/json'
2859
+ },
2860
+ body: JSON.stringify(webhookPayload)
2861
+ });
2862
+ return [
2863
+ 4,
2864
+ fetch(webhookRequest)
2865
+ ];
2866
+ case 11:
2867
+ webhookResponse = _state.sent();
2868
+ if (!webhookResponse.ok) {
2869
+ console.error("POST /webhooks/".concat(webhookSlug, " returned ").concat(webhookResponse.status));
2870
+ return [
2871
+ 2,
2872
+ false
2873
+ ];
2874
+ }
2875
+ return [
2876
+ 4,
2877
+ webhookResponse.json()
2878
+ ];
2879
+ case 12:
2880
+ webhookResult = _state.sent();
2881
+ if (!webhookResult.received) {
2882
+ console.error('Webhook was not received');
2883
+ return [
2884
+ 2,
2885
+ false
2886
+ ];
2887
+ }
2888
+ if (webhookResult.action !== 'resumed') {
2889
+ console.error("Expected webhook action 'resumed', got '".concat(webhookResult.action, "'"));
2890
+ return [
2891
+ 2,
2892
+ false
2893
+ ];
2894
+ }
2895
+ // Step 4: Watch again for resumed events
2896
+ resumeWatchRequest = new Request("http://example.com/brains/runs/".concat(brainRunId, "/watch"), {
2897
+ method: 'GET'
2898
+ });
2899
+ return [
2900
+ 4,
2901
+ fetch(resumeWatchRequest)
2902
+ ];
2903
+ case 13:
2904
+ resumeWatchResponse = _state.sent();
2905
+ if (!resumeWatchResponse.ok) {
2906
+ console.error("GET /brains/runs/".concat(brainRunId, "/watch (resume) returned ").concat(resumeWatchResponse.status));
2907
+ return [
2908
+ 2,
2909
+ false
2910
+ ];
2911
+ }
2912
+ resumeEvents = [];
2913
+ if (!resumeWatchResponse.body) return [
2914
+ 3,
2915
+ 20
2916
+ ];
2917
+ reader1 = resumeWatchResponse.body.getReader();
2918
+ decoder1 = new TextDecoder();
2919
+ buffer1 = '';
2920
+ done1 = false;
2921
+ _state.label = 14;
2922
+ case 14:
2923
+ _state.trys.push([
2924
+ 14,
2925
+ ,
2926
+ 18,
2927
+ 20
2928
+ ]);
2929
+ _state.label = 15;
2930
+ case 15:
2931
+ if (!!done1) return [
2932
+ 3,
2933
+ 17
2934
+ ];
2935
+ return [
2936
+ 4,
2937
+ reader1.read()
2938
+ ];
2939
+ case 16:
2940
+ _ref1 = _state.sent(), value1 = _ref1.value, streamDone = _ref1.done;
2941
+ if (streamDone) return [
2942
+ 3,
2943
+ 17
2944
+ ];
2945
+ buffer1 += decoder1.decode(value1, {
2946
+ stream: true
2947
+ });
2948
+ eventEndIndex1 = void 0;
2949
+ while((eventEndIndex1 = buffer1.indexOf('\n\n')) !== -1){
2950
+ message1 = buffer1.substring(0, eventEndIndex1);
2951
+ buffer1 = buffer1.substring(eventEndIndex1 + 2);
2952
+ if (message1.startsWith('data: ')) {
2953
+ try {
2954
+ event1 = JSON.parse(message1.substring(6));
2955
+ resumeEvents.push(event1);
2956
+ if (event1.type === BRAIN_EVENTS.COMPLETE || event1.type === BRAIN_EVENTS.ERROR) {
2957
+ done1 = true;
2958
+ break;
2959
+ }
2960
+ } catch (e) {
2961
+ // Ignore parse errors
2962
+ }
2963
+ }
2964
+ }
2965
+ return [
2966
+ 3,
2967
+ 15
2968
+ ];
2969
+ case 17:
2970
+ return [
2971
+ 3,
2972
+ 20
2973
+ ];
2974
+ case 18:
2975
+ return [
2976
+ 4,
2977
+ reader1.cancel()
2978
+ ];
2979
+ case 19:
2980
+ _state.sent();
2981
+ return [
2982
+ 7
2983
+ ];
2984
+ case 20:
2985
+ // Verify WEBHOOK_RESPONSE event is present
2986
+ hasWebhookResponse = resumeEvents.some(function(e) {
2987
+ return e.type === BRAIN_EVENTS.WEBHOOK_RESPONSE;
2988
+ });
2989
+ if (!hasWebhookResponse) {
2990
+ console.error('Missing WEBHOOK_RESPONSE event after resume');
2991
+ return [
2992
+ 2,
2993
+ false
2994
+ ];
2995
+ }
2996
+ // Verify LOOP_TOOL_RESULT event is present (with the webhook response as result)
2997
+ hasLoopToolResult = resumeEvents.some(function(e) {
2998
+ return e.type === BRAIN_EVENTS.LOOP_TOOL_RESULT;
2999
+ });
3000
+ if (!hasLoopToolResult) {
3001
+ console.error('Missing LOOP_TOOL_RESULT event after resume');
3002
+ return [
3003
+ 2,
3004
+ false
3005
+ ];
3006
+ }
3007
+ // Verify brain completed successfully
3008
+ completeEvent = resumeEvents.find(function(e) {
3009
+ return e.type === BRAIN_EVENTS.COMPLETE;
3010
+ });
3011
+ if (!completeEvent) {
3012
+ console.error('Brain did not complete after resume');
3013
+ return [
3014
+ 2,
3015
+ false
3016
+ ];
3017
+ }
3018
+ if (completeEvent.status !== STATUS.COMPLETE) {
3019
+ console.error("Expected COMPLETE status, got ".concat(completeEvent.status));
3020
+ return [
3021
+ 2,
3022
+ false
3023
+ ];
3024
+ }
3025
+ return [
3026
+ 2,
3027
+ true
3028
+ ];
3029
+ case 21:
3030
+ error = _state.sent();
3031
+ console.error("Failed to test loop webhook resume for ".concat(loopBrainIdentifier, ":"), error);
3032
+ return [
3033
+ 2,
3034
+ false
3035
+ ];
3036
+ case 22:
3037
+ return [
3038
+ 2
3039
+ ];
3040
+ }
3041
+ });
3042
+ })();
2069
3043
  }
2070
3044
  };
2071
3045
  export var schedules = {