@positronic/spec 0.0.68 → 0.0.70
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/brains.d.ts.map +1 -1
- package/dist/api/brains.js +108 -299
- package/dist/api/brains.js.map +1 -1
- package/dist/api/helpers.d.ts +12 -0
- package/dist/api/helpers.d.ts.map +1 -0
- package/dist/api/helpers.js +68 -0
- package/dist/api/helpers.js.map +1 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +1 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/schedules.d.ts.map +1 -1
- package/dist/api/schedules.js.map +1 -1
- package/dist/api/scoping.d.ts +39 -0
- package/dist/api/scoping.d.ts.map +1 -0
- package/dist/api/scoping.js +265 -0
- package/dist/api/scoping.js.map +1 -0
- package/dist/api/types.d.ts +4 -0
- package/dist/api/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- 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/brains.js +192 -581
- package/dist/src/api/helpers.js +280 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/api/scoping.js +756 -0
- package/dist/src/index.js +1 -1
- package/package.json +1 -1
package/dist/api/brains.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brains.d.ts","sourceRoot":"","sources":["../../src/api/brains.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"brains.d.ts","sourceRoot":"","sources":["../../src/api/brains.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC,eAAO,MAAM,MAAM;IACjB;;OAEG;eAEM,KAAK,cACA,MAAM,YACR,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAwCzB;;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;kBAEM,KAAK,SACL,MAAM,GACZ,OAAO,CAAC;QACT,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;0BAEM,KAAK,oBACM,MAAM,GACvB,OAAO,CAAC,OAAO,CAAC;IA4BnB;;;OAGG;iCAEM,KAAK,uBACS,MAAM,GAC1B,OAAO,CAAC,OAAO,CAAC;IAqDnB;;OAEG;wBAEM,KAAK,uBACS,MAAM,GAC1B,OAAO,CAAC,OAAO,CAAC;IAkEnB;;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;IA0JnB;;;;;;;;;;OAUG;4BAEM,KAAK,wBACU,MAAM,GAC3B,OAAO,CAAC,OAAO,CAAC;IAuGnB;;;;;;;;;;OAUG;8BAEM,KAAK,wBACU,MAAM,eACf,MAAM,kBACH,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAClC,OAAO,CAAC,OAAO,CAAC;IAuJnB;;;;;;;;;;;;;OAaG;sDAEM,KAAK,wBACU,MAAM,eACf,MAAM,kBACH,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAClC,OAAO,CAAC,OAAO,CAAC;CAqKpB,CAAC"}
|
package/dist/api/brains.js
CHANGED
|
@@ -1,37 +1,38 @@
|
|
|
1
1
|
import { STATUS, BRAIN_EVENTS } from '@positronic/core';
|
|
2
|
+
import { startBrainRun, readSseUntil } from './helpers.js';
|
|
2
3
|
export const brains = {
|
|
3
4
|
/**
|
|
4
5
|
* Test POST /brains/runs - Create a new brain run
|
|
5
6
|
*/
|
|
6
7
|
async run(fetch, identifier, options) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
8
|
+
if (options && Object.keys(options).length > 0) {
|
|
9
|
+
// When options are provided, we need the full request (can't use startBrainRun)
|
|
10
|
+
try {
|
|
11
|
+
const request = new Request('http://example.com/brains/runs', {
|
|
12
|
+
method: 'POST',
|
|
13
|
+
headers: {
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
},
|
|
16
|
+
body: JSON.stringify({ identifier, options }),
|
|
17
|
+
});
|
|
18
|
+
const response = await fetch(request);
|
|
19
|
+
if (response.status !== 201) {
|
|
20
|
+
console.error(`POST /brains/runs returned ${response.status}, expected 201`);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const data = (await response.json());
|
|
24
|
+
if (!data.brainRunId || typeof data.brainRunId !== 'string') {
|
|
25
|
+
console.error(`Expected brainRunId to be string, got ${typeof data.brainRunId}`);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return data.brainRunId;
|
|
23
29
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
console.error(`Expected brainRunId to be string, got ${typeof data.brainRunId}`);
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error(`Failed to test POST /brains/runs:`, error);
|
|
27
32
|
return null;
|
|
28
33
|
}
|
|
29
|
-
return data.brainRunId;
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
console.error(`Failed to test POST /brains/runs:`, error);
|
|
33
|
-
return null;
|
|
34
34
|
}
|
|
35
|
+
return startBrainRun(fetch, identifier);
|
|
35
36
|
},
|
|
36
37
|
/**
|
|
37
38
|
* Test POST /brains/runs with options - Create a brain run with runtime options
|
|
@@ -636,17 +637,9 @@ export const brains = {
|
|
|
636
637
|
async killSuspended(fetch, loopBrainIdentifier, webhookSlug, webhookPayload) {
|
|
637
638
|
try {
|
|
638
639
|
// Step 1: Start the loop brain
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
headers: { 'Content-Type': 'application/json' },
|
|
642
|
-
body: JSON.stringify({ identifier: loopBrainIdentifier }),
|
|
643
|
-
});
|
|
644
|
-
const runResponse = await fetch(runRequest);
|
|
645
|
-
if (runResponse.status !== 201) {
|
|
646
|
-
console.error(`POST /brains/runs returned ${runResponse.status}, expected 201`);
|
|
640
|
+
const brainRunId = await startBrainRun(fetch, loopBrainIdentifier);
|
|
641
|
+
if (!brainRunId)
|
|
647
642
|
return false;
|
|
648
|
-
}
|
|
649
|
-
const { brainRunId } = (await runResponse.json());
|
|
650
643
|
// Step 2: Watch until WEBHOOK event (brain pauses)
|
|
651
644
|
const watchRequest = new Request(`http://example.com/brains/runs/${brainRunId}/watch`, { method: 'GET' });
|
|
652
645
|
const watchResponse = await fetch(watchRequest);
|
|
@@ -654,45 +647,25 @@ export const brains = {
|
|
|
654
647
|
console.error(`GET /brains/runs/${brainRunId}/watch returned ${watchResponse.status}`);
|
|
655
648
|
return false;
|
|
656
649
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const decoder = new TextDecoder();
|
|
661
|
-
let buffer = '';
|
|
662
|
-
try {
|
|
663
|
-
while (!foundWebhookEvent) {
|
|
664
|
-
const { value, done } = await reader.read();
|
|
665
|
-
if (done)
|
|
666
|
-
break;
|
|
667
|
-
buffer += decoder.decode(value, { stream: true });
|
|
668
|
-
let eventEndIndex;
|
|
669
|
-
while ((eventEndIndex = buffer.indexOf('\n\n')) !== -1) {
|
|
670
|
-
const message = buffer.substring(0, eventEndIndex);
|
|
671
|
-
buffer = buffer.substring(eventEndIndex + 2);
|
|
672
|
-
if (message.startsWith('data: ')) {
|
|
673
|
-
try {
|
|
674
|
-
const event = JSON.parse(message.substring(6));
|
|
675
|
-
if (event.type === BRAIN_EVENTS.WEBHOOK) {
|
|
676
|
-
foundWebhookEvent = true;
|
|
677
|
-
break;
|
|
678
|
-
}
|
|
679
|
-
if (event.type === BRAIN_EVENTS.COMPLETE ||
|
|
680
|
-
event.type === BRAIN_EVENTS.ERROR) {
|
|
681
|
-
console.error(`Brain completed/errored before WEBHOOK event: ${event.type}`);
|
|
682
|
-
return false;
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
catch (e) {
|
|
686
|
-
// Ignore parse errors
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
finally {
|
|
693
|
-
await reader.cancel();
|
|
694
|
-
}
|
|
650
|
+
if (!watchResponse.body) {
|
|
651
|
+
console.error('Watch response has no body');
|
|
652
|
+
return false;
|
|
695
653
|
}
|
|
654
|
+
let earlyTermination = false;
|
|
655
|
+
const events = await readSseUntil(watchResponse.body, (event) => {
|
|
656
|
+
if (event.type === BRAIN_EVENTS.WEBHOOK)
|
|
657
|
+
return true;
|
|
658
|
+
if (event.type === BRAIN_EVENTS.COMPLETE ||
|
|
659
|
+
event.type === BRAIN_EVENTS.ERROR) {
|
|
660
|
+
console.error(`Brain completed/errored before WEBHOOK event: ${event.type}`);
|
|
661
|
+
earlyTermination = true;
|
|
662
|
+
return true;
|
|
663
|
+
}
|
|
664
|
+
return false;
|
|
665
|
+
});
|
|
666
|
+
if (earlyTermination)
|
|
667
|
+
return false;
|
|
668
|
+
const foundWebhookEvent = events.some((e) => e.type === BRAIN_EVENTS.WEBHOOK);
|
|
696
669
|
if (!foundWebhookEvent) {
|
|
697
670
|
console.error('Brain did not emit WEBHOOK event');
|
|
698
671
|
return false;
|
|
@@ -768,17 +741,9 @@ export const brains = {
|
|
|
768
741
|
async watchAgentEvents(fetch, agentBrainIdentifier) {
|
|
769
742
|
try {
|
|
770
743
|
// Start the agent brain
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
headers: { 'Content-Type': 'application/json' },
|
|
774
|
-
body: JSON.stringify({ identifier: agentBrainIdentifier }),
|
|
775
|
-
});
|
|
776
|
-
const runResponse = await fetch(runRequest);
|
|
777
|
-
if (runResponse.status !== 201) {
|
|
778
|
-
console.error(`POST /brains/runs returned ${runResponse.status}, expected 201`);
|
|
744
|
+
const brainRunId = await startBrainRun(fetch, agentBrainIdentifier);
|
|
745
|
+
if (!brainRunId)
|
|
779
746
|
return false;
|
|
780
|
-
}
|
|
781
|
-
const { brainRunId } = (await runResponse.json());
|
|
782
747
|
// Watch the brain run
|
|
783
748
|
const watchRequest = new Request(`http://example.com/brains/runs/${brainRunId}/watch`, { method: 'GET' });
|
|
784
749
|
const watchResponse = await fetch(watchRequest);
|
|
@@ -787,46 +752,11 @@ export const brains = {
|
|
|
787
752
|
return false;
|
|
788
753
|
}
|
|
789
754
|
// Read SSE events until we get WEBHOOK or COMPLETE/ERROR
|
|
790
|
-
const events =
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
let done = false;
|
|
796
|
-
try {
|
|
797
|
-
while (!done) {
|
|
798
|
-
const { value, done: streamDone } = await reader.read();
|
|
799
|
-
if (streamDone)
|
|
800
|
-
break;
|
|
801
|
-
buffer += decoder.decode(value, { stream: true });
|
|
802
|
-
// Process complete SSE messages
|
|
803
|
-
let eventEndIndex;
|
|
804
|
-
while ((eventEndIndex = buffer.indexOf('\n\n')) !== -1) {
|
|
805
|
-
const message = buffer.substring(0, eventEndIndex);
|
|
806
|
-
buffer = buffer.substring(eventEndIndex + 2);
|
|
807
|
-
if (message.startsWith('data: ')) {
|
|
808
|
-
try {
|
|
809
|
-
const event = JSON.parse(message.substring(6));
|
|
810
|
-
events.push(event);
|
|
811
|
-
// Stop on terminal events
|
|
812
|
-
if (event.type === BRAIN_EVENTS.WEBHOOK ||
|
|
813
|
-
event.type === BRAIN_EVENTS.COMPLETE ||
|
|
814
|
-
event.type === BRAIN_EVENTS.ERROR) {
|
|
815
|
-
done = true;
|
|
816
|
-
break;
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
catch (e) {
|
|
820
|
-
// Ignore parse errors
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
finally {
|
|
827
|
-
await reader.cancel();
|
|
828
|
-
}
|
|
829
|
-
}
|
|
755
|
+
const events = watchResponse.body
|
|
756
|
+
? await readSseUntil(watchResponse.body, (event) => event.type === BRAIN_EVENTS.WEBHOOK ||
|
|
757
|
+
event.type === BRAIN_EVENTS.COMPLETE ||
|
|
758
|
+
event.type === BRAIN_EVENTS.ERROR)
|
|
759
|
+
: [];
|
|
830
760
|
// Verify required agent events are present
|
|
831
761
|
const hasAgentStart = events.some((e) => e.type === BRAIN_EVENTS.AGENT_START);
|
|
832
762
|
if (!hasAgentStart) {
|
|
@@ -890,17 +820,9 @@ export const brains = {
|
|
|
890
820
|
async agentWebhookResume(fetch, agentBrainIdentifier, webhookSlug, webhookPayload) {
|
|
891
821
|
try {
|
|
892
822
|
// Step 1: Start the agent brain
|
|
893
|
-
const
|
|
894
|
-
|
|
895
|
-
headers: { 'Content-Type': 'application/json' },
|
|
896
|
-
body: JSON.stringify({ identifier: agentBrainIdentifier }),
|
|
897
|
-
});
|
|
898
|
-
const runResponse = await fetch(runRequest);
|
|
899
|
-
if (runResponse.status !== 201) {
|
|
900
|
-
console.error(`POST /brains/runs returned ${runResponse.status}, expected 201`);
|
|
823
|
+
const brainRunId = await startBrainRun(fetch, agentBrainIdentifier);
|
|
824
|
+
if (!brainRunId)
|
|
901
825
|
return false;
|
|
902
|
-
}
|
|
903
|
-
const { brainRunId } = (await runResponse.json());
|
|
904
826
|
// Step 2: Watch until WEBHOOK event (brain pauses)
|
|
905
827
|
const watchRequest = new Request(`http://example.com/brains/runs/${brainRunId}/watch`, { method: 'GET' });
|
|
906
828
|
const watchResponse = await fetch(watchRequest);
|
|
@@ -908,45 +830,25 @@ export const brains = {
|
|
|
908
830
|
console.error(`GET /brains/runs/${brainRunId}/watch returned ${watchResponse.status}`);
|
|
909
831
|
return false;
|
|
910
832
|
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
const decoder = new TextDecoder();
|
|
915
|
-
let buffer = '';
|
|
916
|
-
try {
|
|
917
|
-
while (!foundWebhookEvent) {
|
|
918
|
-
const { value, done } = await reader.read();
|
|
919
|
-
if (done)
|
|
920
|
-
break;
|
|
921
|
-
buffer += decoder.decode(value, { stream: true });
|
|
922
|
-
let eventEndIndex;
|
|
923
|
-
while ((eventEndIndex = buffer.indexOf('\n\n')) !== -1) {
|
|
924
|
-
const message = buffer.substring(0, eventEndIndex);
|
|
925
|
-
buffer = buffer.substring(eventEndIndex + 2);
|
|
926
|
-
if (message.startsWith('data: ')) {
|
|
927
|
-
try {
|
|
928
|
-
const event = JSON.parse(message.substring(6));
|
|
929
|
-
if (event.type === BRAIN_EVENTS.WEBHOOK) {
|
|
930
|
-
foundWebhookEvent = true;
|
|
931
|
-
break;
|
|
932
|
-
}
|
|
933
|
-
if (event.type === BRAIN_EVENTS.COMPLETE ||
|
|
934
|
-
event.type === BRAIN_EVENTS.ERROR) {
|
|
935
|
-
console.error(`Brain completed/errored before WEBHOOK event: ${event.type}`);
|
|
936
|
-
return false;
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
catch (e) {
|
|
940
|
-
// Ignore parse errors
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
finally {
|
|
947
|
-
await reader.cancel();
|
|
948
|
-
}
|
|
833
|
+
if (!watchResponse.body) {
|
|
834
|
+
console.error('Watch response has no body');
|
|
835
|
+
return false;
|
|
949
836
|
}
|
|
837
|
+
let earlyTermination = false;
|
|
838
|
+
const watchEvents = await readSseUntil(watchResponse.body, (event) => {
|
|
839
|
+
if (event.type === BRAIN_EVENTS.WEBHOOK)
|
|
840
|
+
return true;
|
|
841
|
+
if (event.type === BRAIN_EVENTS.COMPLETE ||
|
|
842
|
+
event.type === BRAIN_EVENTS.ERROR) {
|
|
843
|
+
console.error(`Brain completed/errored before WEBHOOK event: ${event.type}`);
|
|
844
|
+
earlyTermination = true;
|
|
845
|
+
return true;
|
|
846
|
+
}
|
|
847
|
+
return false;
|
|
848
|
+
});
|
|
849
|
+
if (earlyTermination)
|
|
850
|
+
return false;
|
|
851
|
+
const foundWebhookEvent = watchEvents.some((e) => e.type === BRAIN_EVENTS.WEBHOOK);
|
|
950
852
|
if (!foundWebhookEvent) {
|
|
951
853
|
console.error('Brain did not emit WEBHOOK event');
|
|
952
854
|
return false;
|
|
@@ -978,43 +880,10 @@ export const brains = {
|
|
|
978
880
|
console.error(`GET /brains/runs/${brainRunId}/watch (resume) returned ${resumeWatchResponse.status}`);
|
|
979
881
|
return false;
|
|
980
882
|
}
|
|
981
|
-
const resumeEvents =
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
let buffer = '';
|
|
986
|
-
let done = false;
|
|
987
|
-
try {
|
|
988
|
-
while (!done) {
|
|
989
|
-
const { value, done: streamDone } = await reader.read();
|
|
990
|
-
if (streamDone)
|
|
991
|
-
break;
|
|
992
|
-
buffer += decoder.decode(value, { stream: true });
|
|
993
|
-
let eventEndIndex;
|
|
994
|
-
while ((eventEndIndex = buffer.indexOf('\n\n')) !== -1) {
|
|
995
|
-
const message = buffer.substring(0, eventEndIndex);
|
|
996
|
-
buffer = buffer.substring(eventEndIndex + 2);
|
|
997
|
-
if (message.startsWith('data: ')) {
|
|
998
|
-
try {
|
|
999
|
-
const event = JSON.parse(message.substring(6));
|
|
1000
|
-
resumeEvents.push(event);
|
|
1001
|
-
if (event.type === BRAIN_EVENTS.COMPLETE ||
|
|
1002
|
-
event.type === BRAIN_EVENTS.ERROR) {
|
|
1003
|
-
done = true;
|
|
1004
|
-
break;
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
catch (e) {
|
|
1008
|
-
// Ignore parse errors
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
finally {
|
|
1015
|
-
await reader.cancel();
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
883
|
+
const resumeEvents = resumeWatchResponse.body
|
|
884
|
+
? await readSseUntil(resumeWatchResponse.body, (event) => event.type === BRAIN_EVENTS.COMPLETE ||
|
|
885
|
+
event.type === BRAIN_EVENTS.ERROR)
|
|
886
|
+
: [];
|
|
1018
887
|
// Verify WEBHOOK_RESPONSE event is present
|
|
1019
888
|
const hasWebhookResponse = resumeEvents.some((e) => e.type === BRAIN_EVENTS.WEBHOOK_RESPONSE);
|
|
1020
889
|
if (!hasWebhookResponse) {
|
|
@@ -1061,17 +930,9 @@ export const brains = {
|
|
|
1061
930
|
async innerBrainCompleteDoesNotAffectOuterStatus(fetch, outerBrainIdentifier, webhookSlug, webhookPayload) {
|
|
1062
931
|
try {
|
|
1063
932
|
// Step 1: Start the outer brain
|
|
1064
|
-
const
|
|
1065
|
-
|
|
1066
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1067
|
-
body: JSON.stringify({ identifier: outerBrainIdentifier }),
|
|
1068
|
-
});
|
|
1069
|
-
const runResponse = await fetch(runRequest);
|
|
1070
|
-
if (runResponse.status !== 201) {
|
|
1071
|
-
console.error(`POST /brains/runs returned ${runResponse.status}, expected 201`);
|
|
933
|
+
const brainRunId = await startBrainRun(fetch, outerBrainIdentifier);
|
|
934
|
+
if (!brainRunId)
|
|
1072
935
|
return false;
|
|
1073
|
-
}
|
|
1074
|
-
const { brainRunId } = (await runResponse.json());
|
|
1075
936
|
// Step 2: Watch SSE until WEBHOOK event from outer brain (after inner completes)
|
|
1076
937
|
const watchRequest = new Request(`http://example.com/brains/runs/${brainRunId}/watch`, { method: 'GET' });
|
|
1077
938
|
const watchResponse = await fetch(watchRequest);
|
|
@@ -1079,51 +940,28 @@ export const brains = {
|
|
|
1079
940
|
console.error(`GET /brains/runs/${brainRunId}/watch returned ${watchResponse.status}`);
|
|
1080
941
|
return false;
|
|
1081
942
|
}
|
|
1082
|
-
|
|
943
|
+
if (!watchResponse.body) {
|
|
944
|
+
console.error('Watch response has no body');
|
|
945
|
+
return false;
|
|
946
|
+
}
|
|
1083
947
|
let innerCompleteCount = 0;
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
try {
|
|
1089
|
-
while (!foundOuterWebhook) {
|
|
1090
|
-
const { value, done } = await reader.read();
|
|
1091
|
-
if (done)
|
|
1092
|
-
break;
|
|
1093
|
-
buffer += decoder.decode(value, { stream: true });
|
|
1094
|
-
let eventEndIndex;
|
|
1095
|
-
while ((eventEndIndex = buffer.indexOf('\n\n')) !== -1) {
|
|
1096
|
-
const message = buffer.substring(0, eventEndIndex);
|
|
1097
|
-
buffer = buffer.substring(eventEndIndex + 2);
|
|
1098
|
-
if (message.startsWith('data: ')) {
|
|
1099
|
-
try {
|
|
1100
|
-
const event = JSON.parse(message.substring(6));
|
|
1101
|
-
// Track inner brain completes
|
|
1102
|
-
if (event.type === BRAIN_EVENTS.COMPLETE) {
|
|
1103
|
-
innerCompleteCount++;
|
|
1104
|
-
// First complete is inner brain, second would be outer
|
|
1105
|
-
}
|
|
1106
|
-
// Outer brain webhook (happens after inner brain completes)
|
|
1107
|
-
if (event.type === BRAIN_EVENTS.WEBHOOK) {
|
|
1108
|
-
foundOuterWebhook = true;
|
|
1109
|
-
break;
|
|
1110
|
-
}
|
|
1111
|
-
if (event.type === BRAIN_EVENTS.ERROR) {
|
|
1112
|
-
console.error(`Brain errored: ${JSON.stringify(event.error)}`);
|
|
1113
|
-
return false;
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
catch {
|
|
1117
|
-
// Ignore parse errors
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
948
|
+
let hitError = false;
|
|
949
|
+
const watchEvents = await readSseUntil(watchResponse.body, (event) => {
|
|
950
|
+
if (event.type === BRAIN_EVENTS.COMPLETE) {
|
|
951
|
+
innerCompleteCount++;
|
|
1122
952
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
953
|
+
if (event.type === BRAIN_EVENTS.WEBHOOK)
|
|
954
|
+
return true;
|
|
955
|
+
if (event.type === BRAIN_EVENTS.ERROR) {
|
|
956
|
+
console.error(`Brain errored: ${JSON.stringify(event.error)}`);
|
|
957
|
+
hitError = true;
|
|
958
|
+
return true;
|
|
1125
959
|
}
|
|
1126
|
-
|
|
960
|
+
return false;
|
|
961
|
+
});
|
|
962
|
+
if (hitError)
|
|
963
|
+
return false;
|
|
964
|
+
const foundOuterWebhook = watchEvents.some((e) => e.type === BRAIN_EVENTS.WEBHOOK);
|
|
1127
965
|
if (!foundOuterWebhook) {
|
|
1128
966
|
console.error('Did not receive outer brain WEBHOOK event');
|
|
1129
967
|
return false;
|
|
@@ -1173,48 +1011,19 @@ export const brains = {
|
|
|
1173
1011
|
// Wait for the OUTER brain's COMPLETE event specifically (by tracking depth)
|
|
1174
1012
|
// When resuming, the SSE stream includes historical events, so we need to
|
|
1175
1013
|
// track START/COMPLETE events to know when the outer brain truly finishes
|
|
1176
|
-
let foundFinalComplete = false;
|
|
1177
|
-
let depth = 0;
|
|
1178
1014
|
if (resumeWatchResponse.body) {
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
while (!foundFinalComplete) {
|
|
1184
|
-
const { value, done } = await reader.read();
|
|
1185
|
-
if (done)
|
|
1186
|
-
break;
|
|
1187
|
-
buffer += decoder.decode(value, { stream: true });
|
|
1188
|
-
let eventEndIndex;
|
|
1189
|
-
while ((eventEndIndex = buffer.indexOf('\n\n')) !== -1) {
|
|
1190
|
-
const message = buffer.substring(0, eventEndIndex);
|
|
1191
|
-
buffer = buffer.substring(eventEndIndex + 2);
|
|
1192
|
-
if (message.startsWith('data: ')) {
|
|
1193
|
-
try {
|
|
1194
|
-
const event = JSON.parse(message.substring(6));
|
|
1195
|
-
// Track depth to find the outer brain's COMPLETE
|
|
1196
|
-
if (event.type === BRAIN_EVENTS.START) {
|
|
1197
|
-
depth++;
|
|
1198
|
-
}
|
|
1199
|
-
else if (event.type === BRAIN_EVENTS.COMPLETE) {
|
|
1200
|
-
depth--;
|
|
1201
|
-
// When depth reaches 0, the outer brain has completed
|
|
1202
|
-
if (depth <= 0) {
|
|
1203
|
-
foundFinalComplete = true;
|
|
1204
|
-
break;
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
catch {
|
|
1209
|
-
// Ignore parse errors
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1015
|
+
let depth = 0;
|
|
1016
|
+
await readSseUntil(resumeWatchResponse.body, (event) => {
|
|
1017
|
+
if (event.type === BRAIN_EVENTS.START) {
|
|
1018
|
+
depth++;
|
|
1213
1019
|
}
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1020
|
+
else if (event.type === BRAIN_EVENTS.COMPLETE) {
|
|
1021
|
+
depth--;
|
|
1022
|
+
if (depth <= 0)
|
|
1023
|
+
return true;
|
|
1024
|
+
}
|
|
1025
|
+
return false;
|
|
1026
|
+
});
|
|
1218
1027
|
}
|
|
1219
1028
|
// Step 6: Verify final status is COMPLETE
|
|
1220
1029
|
const finalHistoryResponse = await fetch(historyRequest);
|