@gr33n-ai/jade-sdk-rn-client 0.1.7 → 0.2.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/README.md +12 -5
- package/dist/index.js +204 -68
- package/dist/index.mjs +204 -68
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,14 +72,21 @@ function Chat() {
|
|
|
72
72
|
}
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
##
|
|
75
|
+
## Examples
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
See the [jade-sdk-examples][examples] repo for a complete React Native app
|
|
78
|
+
with:
|
|
79
|
+
|
|
80
|
+
[examples]: https://github.com/gr33n-ai/jade-sdk-examples/tree/main/react-native
|
|
78
81
|
|
|
79
|
-
|
|
82
|
+
- Chat UI with streaming
|
|
83
|
+
- Media gallery
|
|
84
|
+
- Custom tool UI components
|
|
85
|
+
- Session management
|
|
80
86
|
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
## Authentication
|
|
88
|
+
|
|
89
|
+
Create an API key in your [organization dashboard](https://jade.gr33n.ai/dashboard/org/).
|
|
83
90
|
|
|
84
91
|
## Features
|
|
85
92
|
|
package/dist/index.js
CHANGED
|
@@ -577,6 +577,13 @@ var SkillNotFoundError = class extends AgentClientError {
|
|
|
577
577
|
this.name = "SkillNotFoundError";
|
|
578
578
|
}
|
|
579
579
|
};
|
|
580
|
+
var WorkflowNotFoundError = class extends AgentClientError {
|
|
581
|
+
constructor(workflowName) {
|
|
582
|
+
super(`Workflow not found: ${workflowName}`);
|
|
583
|
+
this.workflowName = workflowName;
|
|
584
|
+
this.name = "WorkflowNotFoundError";
|
|
585
|
+
}
|
|
586
|
+
};
|
|
580
587
|
var RequestError = class extends AgentClientError {
|
|
581
588
|
constructor(message, statusCode) {
|
|
582
589
|
super(message);
|
|
@@ -660,7 +667,7 @@ async function createSSEStream(options) {
|
|
|
660
667
|
emitter.emit("message_delta", data);
|
|
661
668
|
break;
|
|
662
669
|
case "message_stop":
|
|
663
|
-
emitter.emit("message_stop", {});
|
|
670
|
+
emitter.emit("message_stop", { type: "message_stop" });
|
|
664
671
|
break;
|
|
665
672
|
// Anthropic content block events (native, not wrapped)
|
|
666
673
|
case "content_block_start":
|
|
@@ -763,6 +770,8 @@ var AgentClient = class {
|
|
|
763
770
|
}
|
|
764
771
|
async request(path, options = {}) {
|
|
765
772
|
const url = `${this.baseUrl}${path}`;
|
|
773
|
+
const method = options.method || "GET";
|
|
774
|
+
console.log(`[AgentClient] ${method} ${url}`, options.body ? JSON.parse(options.body) : "");
|
|
766
775
|
const headers = await this.getHeaders();
|
|
767
776
|
const controller = new AbortController();
|
|
768
777
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
@@ -773,9 +782,12 @@ var AgentClient = class {
|
|
|
773
782
|
signal: controller.signal
|
|
774
783
|
});
|
|
775
784
|
if (!response.ok) {
|
|
785
|
+
console.log(`[AgentClient] ${method} ${url} -> ${response.status}`);
|
|
776
786
|
await this.handleErrorResponse(response, path);
|
|
777
787
|
}
|
|
778
|
-
|
|
788
|
+
const json = await response.json();
|
|
789
|
+
console.log(`[AgentClient] ${method} ${url} -> ${response.status}`, JSON.stringify(json).slice(0, 200));
|
|
790
|
+
return json;
|
|
779
791
|
} finally {
|
|
780
792
|
clearTimeout(timeoutId);
|
|
781
793
|
}
|
|
@@ -794,6 +806,10 @@ var AgentClient = class {
|
|
|
794
806
|
const skillName = path.split("/").pop() || "";
|
|
795
807
|
throw new SkillNotFoundError(skillName);
|
|
796
808
|
}
|
|
809
|
+
if (text.toLowerCase().includes("workflow")) {
|
|
810
|
+
const workflowName = path.split("/").pop() || "";
|
|
811
|
+
throw new WorkflowNotFoundError(workflowName);
|
|
812
|
+
}
|
|
797
813
|
}
|
|
798
814
|
throw new RequestError(`Request failed: ${response.status}`, response.status);
|
|
799
815
|
}
|
|
@@ -1001,6 +1017,88 @@ var AgentClient = class {
|
|
|
1001
1017
|
return this.request(`/plugins/bundle${query}`);
|
|
1002
1018
|
}
|
|
1003
1019
|
// ===========================================================================
|
|
1020
|
+
// Workflows API - Personal
|
|
1021
|
+
// ===========================================================================
|
|
1022
|
+
/**
|
|
1023
|
+
* List personal workflows.
|
|
1024
|
+
*/
|
|
1025
|
+
async listWorkflows() {
|
|
1026
|
+
return this.request("/workflows");
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Get personal workflow by name.
|
|
1030
|
+
*/
|
|
1031
|
+
async getWorkflow(name) {
|
|
1032
|
+
return this.request(`/workflows/${name}`);
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Create or update a personal workflow.
|
|
1036
|
+
*/
|
|
1037
|
+
async saveWorkflow(workflow) {
|
|
1038
|
+
return this.request("/workflows", {
|
|
1039
|
+
method: "POST",
|
|
1040
|
+
body: JSON.stringify(workflow)
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Delete a personal workflow.
|
|
1045
|
+
*/
|
|
1046
|
+
async deleteWorkflow(name) {
|
|
1047
|
+
return this.request(`/workflows/${name}`, {
|
|
1048
|
+
method: "DELETE"
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Run a personal workflow.
|
|
1053
|
+
*/
|
|
1054
|
+
async runWorkflow(name, inputs) {
|
|
1055
|
+
return this.request(`/workflows/${name}/run`, {
|
|
1056
|
+
method: "POST",
|
|
1057
|
+
body: JSON.stringify({ inputs })
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
// ===========================================================================
|
|
1061
|
+
// Workflows API - Organization
|
|
1062
|
+
// ===========================================================================
|
|
1063
|
+
/**
|
|
1064
|
+
* List organization workflows.
|
|
1065
|
+
*/
|
|
1066
|
+
async listOrgWorkflows() {
|
|
1067
|
+
return this.request("/workflows/org");
|
|
1068
|
+
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Get organization workflow by name.
|
|
1071
|
+
*/
|
|
1072
|
+
async getOrgWorkflow(name) {
|
|
1073
|
+
return this.request(`/workflows/org/${name}`);
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Create or update an organization workflow.
|
|
1077
|
+
*/
|
|
1078
|
+
async saveOrgWorkflow(workflow) {
|
|
1079
|
+
return this.request("/workflows/org", {
|
|
1080
|
+
method: "POST",
|
|
1081
|
+
body: JSON.stringify(workflow)
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Delete an organization workflow.
|
|
1086
|
+
*/
|
|
1087
|
+
async deleteOrgWorkflow(name) {
|
|
1088
|
+
return this.request(`/workflows/org/${name}`, {
|
|
1089
|
+
method: "DELETE"
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Run an organization workflow.
|
|
1094
|
+
*/
|
|
1095
|
+
async runOrgWorkflow(name, inputs) {
|
|
1096
|
+
return this.request(`/workflows/org/${name}/run`, {
|
|
1097
|
+
method: "POST",
|
|
1098
|
+
body: JSON.stringify({ inputs })
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
// ===========================================================================
|
|
1004
1102
|
// Utility methods
|
|
1005
1103
|
// ===========================================================================
|
|
1006
1104
|
encodeBase64(data) {
|
|
@@ -2063,7 +2161,7 @@ async function createRNSSEStream(options) {
|
|
|
2063
2161
|
reject(new Error(authError));
|
|
2064
2162
|
return;
|
|
2065
2163
|
}
|
|
2066
|
-
if (errorMessage.includes("404")) {
|
|
2164
|
+
if (status === 404 || errorMessage.includes("404")) {
|
|
2067
2165
|
emitter.emit("error", { error: "Session not found or not active" });
|
|
2068
2166
|
cleanup();
|
|
2069
2167
|
reject(new Error("Session not found or not active"));
|
|
@@ -2443,26 +2541,38 @@ function useJadeSessionCore(options = {}) {
|
|
|
2443
2541
|
}
|
|
2444
2542
|
}, [appState, pauseOnBackground, session.isStreaming]);
|
|
2445
2543
|
const setupEventHandlers = react.useCallback(
|
|
2446
|
-
(emitter) => {
|
|
2544
|
+
(emitter, streamSessionId) => {
|
|
2545
|
+
const resolvedId = { current: streamSessionId };
|
|
2546
|
+
const guard = (prev) => {
|
|
2547
|
+
if (resolvedId.current && prev.sessionId !== resolvedId.current) return true;
|
|
2548
|
+
return false;
|
|
2549
|
+
};
|
|
2447
2550
|
emitter.on("session", (data) => {
|
|
2551
|
+
resolvedId.current = data.session_id;
|
|
2448
2552
|
setSession((prev) => ({ ...prev, sessionId: data.session_id }));
|
|
2449
2553
|
});
|
|
2450
2554
|
emitter.on("content_block_start", (data) => {
|
|
2451
2555
|
const block = data.content_block;
|
|
2452
2556
|
if (block?.type === "text") {
|
|
2453
2557
|
accumulatedTextRef.current[data.index] = "";
|
|
2454
|
-
setSession((prev) =>
|
|
2558
|
+
setSession((prev) => {
|
|
2559
|
+
if (guard(prev)) return prev;
|
|
2560
|
+
return { ...prev, showTinkering: false };
|
|
2561
|
+
});
|
|
2455
2562
|
} else if (block?.type === "tool_use") {
|
|
2456
|
-
setSession((prev) =>
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2563
|
+
setSession((prev) => {
|
|
2564
|
+
if (guard(prev)) return prev;
|
|
2565
|
+
return {
|
|
2566
|
+
...prev,
|
|
2567
|
+
streamingToolCall: {
|
|
2568
|
+
tool_name: block.name || "",
|
|
2569
|
+
tool_use_id: block.id || "",
|
|
2570
|
+
block_index: data.index,
|
|
2571
|
+
accumulated_input: ""
|
|
2572
|
+
},
|
|
2573
|
+
showTinkering: false
|
|
2574
|
+
};
|
|
2575
|
+
});
|
|
2466
2576
|
}
|
|
2467
2577
|
});
|
|
2468
2578
|
emitter.on("content_block_delta", (data) => {
|
|
@@ -2470,14 +2580,18 @@ function useJadeSessionCore(options = {}) {
|
|
|
2470
2580
|
if (delta?.type === "text_delta" && delta.text) {
|
|
2471
2581
|
const idx = data.index ?? 0;
|
|
2472
2582
|
accumulatedTextRef.current[idx] = (accumulatedTextRef.current[idx] || "") + delta.text;
|
|
2473
|
-
setSession((prev) =>
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2583
|
+
setSession((prev) => {
|
|
2584
|
+
if (guard(prev)) return prev;
|
|
2585
|
+
return {
|
|
2586
|
+
...prev,
|
|
2587
|
+
streamingText: accumulatedTextRef.current[idx],
|
|
2588
|
+
streamingBlockIndex: idx,
|
|
2589
|
+
showTinkering: false
|
|
2590
|
+
};
|
|
2591
|
+
});
|
|
2479
2592
|
} else if (delta?.type === "input_json_delta" && delta.partial_json) {
|
|
2480
2593
|
setSession((prev) => {
|
|
2594
|
+
if (guard(prev)) return prev;
|
|
2481
2595
|
if (!prev.streamingToolCall) return prev;
|
|
2482
2596
|
return {
|
|
2483
2597
|
...prev,
|
|
@@ -2493,11 +2607,14 @@ function useJadeSessionCore(options = {}) {
|
|
|
2493
2607
|
const idx = data.index ?? 0;
|
|
2494
2608
|
if (accumulatedTextRef.current[idx] !== void 0) {
|
|
2495
2609
|
delete accumulatedTextRef.current[idx];
|
|
2496
|
-
setSession((prev) =>
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2610
|
+
setSession((prev) => {
|
|
2611
|
+
if (guard(prev)) return prev;
|
|
2612
|
+
return {
|
|
2613
|
+
...prev,
|
|
2614
|
+
streamingText: void 0,
|
|
2615
|
+
streamingBlockIndex: void 0
|
|
2616
|
+
};
|
|
2617
|
+
});
|
|
2501
2618
|
}
|
|
2502
2619
|
});
|
|
2503
2620
|
emitter.on("entry", (data) => {
|
|
@@ -2509,54 +2626,69 @@ function useJadeSessionCore(options = {}) {
|
|
|
2509
2626
|
}
|
|
2510
2627
|
}
|
|
2511
2628
|
if (entry.type === "tool_call") {
|
|
2512
|
-
setSession((prev) =>
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2629
|
+
setSession((prev) => {
|
|
2630
|
+
if (guard(prev)) return prev;
|
|
2631
|
+
return {
|
|
2632
|
+
...prev,
|
|
2633
|
+
conversation: [...prev.conversation, entry],
|
|
2634
|
+
streamingToolCall: void 0,
|
|
2635
|
+
isExecutingTool: true,
|
|
2636
|
+
executingToolName: entry.tool_name
|
|
2637
|
+
};
|
|
2638
|
+
});
|
|
2519
2639
|
} else if (entry.type === "tool_result") {
|
|
2520
|
-
setSession((prev) =>
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2640
|
+
setSession((prev) => {
|
|
2641
|
+
if (guard(prev)) return prev;
|
|
2642
|
+
return {
|
|
2643
|
+
...prev,
|
|
2644
|
+
conversation: [...prev.conversation, entry],
|
|
2645
|
+
isExecutingTool: false,
|
|
2646
|
+
executingToolName: void 0
|
|
2647
|
+
};
|
|
2648
|
+
});
|
|
2526
2649
|
} else {
|
|
2527
|
-
setSession((prev) =>
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2650
|
+
setSession((prev) => {
|
|
2651
|
+
if (guard(prev)) return prev;
|
|
2652
|
+
return {
|
|
2653
|
+
...prev,
|
|
2654
|
+
conversation: [...prev.conversation, entry]
|
|
2655
|
+
};
|
|
2656
|
+
});
|
|
2531
2657
|
}
|
|
2532
2658
|
});
|
|
2533
2659
|
emitter.on("complete", (data) => {
|
|
2534
|
-
setSession((prev) =>
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2660
|
+
setSession((prev) => {
|
|
2661
|
+
if (guard(prev)) return prev;
|
|
2662
|
+
return {
|
|
2663
|
+
...prev,
|
|
2664
|
+
sessionId: data.session_id || prev.sessionId,
|
|
2665
|
+
isStreaming: false,
|
|
2666
|
+
showTinkering: false,
|
|
2667
|
+
isExecutingTool: false,
|
|
2668
|
+
executingToolName: void 0
|
|
2669
|
+
};
|
|
2670
|
+
});
|
|
2542
2671
|
abortRef.current = null;
|
|
2543
2672
|
});
|
|
2544
2673
|
emitter.on("error", (data) => {
|
|
2545
|
-
setSession((prev) =>
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
...prev
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2674
|
+
setSession((prev) => {
|
|
2675
|
+
if (guard(prev)) return prev;
|
|
2676
|
+
return {
|
|
2677
|
+
...prev,
|
|
2678
|
+
conversation: [
|
|
2679
|
+
...prev.conversation,
|
|
2680
|
+
{
|
|
2681
|
+
type: "error",
|
|
2682
|
+
text: data.error,
|
|
2683
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2684
|
+
}
|
|
2685
|
+
],
|
|
2686
|
+
isStreaming: false,
|
|
2687
|
+
showTinkering: false,
|
|
2688
|
+
isExecutingTool: false,
|
|
2689
|
+
executingToolName: void 0
|
|
2690
|
+
};
|
|
2691
|
+
});
|
|
2560
2692
|
abortRef.current = null;
|
|
2561
2693
|
});
|
|
2562
2694
|
},
|
|
@@ -2583,7 +2715,7 @@ function useJadeSessionCore(options = {}) {
|
|
|
2583
2715
|
skills
|
|
2584
2716
|
});
|
|
2585
2717
|
abortRef.current = abort;
|
|
2586
|
-
setupEventHandlers(emitter);
|
|
2718
|
+
setupEventHandlers(emitter, session.sessionId);
|
|
2587
2719
|
},
|
|
2588
2720
|
[client, session.sessionId, session.isStreaming, setupEventHandlers]
|
|
2589
2721
|
);
|
|
@@ -2620,6 +2752,10 @@ function useJadeSessionCore(options = {}) {
|
|
|
2620
2752
|
}
|
|
2621
2753
|
}, [client, session.sessionId]);
|
|
2622
2754
|
const clear = react.useCallback(() => {
|
|
2755
|
+
abortRef.current?.();
|
|
2756
|
+
abortRef.current = null;
|
|
2757
|
+
accumulatedTextRef.current = {};
|
|
2758
|
+
cancelInProgressRef.current = false;
|
|
2623
2759
|
setSession({
|
|
2624
2760
|
conversation: [],
|
|
2625
2761
|
isStreaming: false
|
|
@@ -2665,7 +2801,7 @@ function useJadeSessionCore(options = {}) {
|
|
|
2665
2801
|
accumulatedTextRef.current = {};
|
|
2666
2802
|
const { emitter, abort } = client.reconnect(targetSessionId, -1);
|
|
2667
2803
|
abortRef.current = abort;
|
|
2668
|
-
setupEventHandlers(emitter);
|
|
2804
|
+
setupEventHandlers(emitter, targetSessionId);
|
|
2669
2805
|
},
|
|
2670
2806
|
[client, setupEventHandlers]
|
|
2671
2807
|
);
|
package/dist/index.mjs
CHANGED
|
@@ -575,6 +575,13 @@ var SkillNotFoundError = class extends AgentClientError {
|
|
|
575
575
|
this.name = "SkillNotFoundError";
|
|
576
576
|
}
|
|
577
577
|
};
|
|
578
|
+
var WorkflowNotFoundError = class extends AgentClientError {
|
|
579
|
+
constructor(workflowName) {
|
|
580
|
+
super(`Workflow not found: ${workflowName}`);
|
|
581
|
+
this.workflowName = workflowName;
|
|
582
|
+
this.name = "WorkflowNotFoundError";
|
|
583
|
+
}
|
|
584
|
+
};
|
|
578
585
|
var RequestError = class extends AgentClientError {
|
|
579
586
|
constructor(message, statusCode) {
|
|
580
587
|
super(message);
|
|
@@ -658,7 +665,7 @@ async function createSSEStream(options) {
|
|
|
658
665
|
emitter.emit("message_delta", data);
|
|
659
666
|
break;
|
|
660
667
|
case "message_stop":
|
|
661
|
-
emitter.emit("message_stop", {});
|
|
668
|
+
emitter.emit("message_stop", { type: "message_stop" });
|
|
662
669
|
break;
|
|
663
670
|
// Anthropic content block events (native, not wrapped)
|
|
664
671
|
case "content_block_start":
|
|
@@ -761,6 +768,8 @@ var AgentClient = class {
|
|
|
761
768
|
}
|
|
762
769
|
async request(path, options = {}) {
|
|
763
770
|
const url = `${this.baseUrl}${path}`;
|
|
771
|
+
const method = options.method || "GET";
|
|
772
|
+
console.log(`[AgentClient] ${method} ${url}`, options.body ? JSON.parse(options.body) : "");
|
|
764
773
|
const headers = await this.getHeaders();
|
|
765
774
|
const controller = new AbortController();
|
|
766
775
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
@@ -771,9 +780,12 @@ var AgentClient = class {
|
|
|
771
780
|
signal: controller.signal
|
|
772
781
|
});
|
|
773
782
|
if (!response.ok) {
|
|
783
|
+
console.log(`[AgentClient] ${method} ${url} -> ${response.status}`);
|
|
774
784
|
await this.handleErrorResponse(response, path);
|
|
775
785
|
}
|
|
776
|
-
|
|
786
|
+
const json = await response.json();
|
|
787
|
+
console.log(`[AgentClient] ${method} ${url} -> ${response.status}`, JSON.stringify(json).slice(0, 200));
|
|
788
|
+
return json;
|
|
777
789
|
} finally {
|
|
778
790
|
clearTimeout(timeoutId);
|
|
779
791
|
}
|
|
@@ -792,6 +804,10 @@ var AgentClient = class {
|
|
|
792
804
|
const skillName = path.split("/").pop() || "";
|
|
793
805
|
throw new SkillNotFoundError(skillName);
|
|
794
806
|
}
|
|
807
|
+
if (text.toLowerCase().includes("workflow")) {
|
|
808
|
+
const workflowName = path.split("/").pop() || "";
|
|
809
|
+
throw new WorkflowNotFoundError(workflowName);
|
|
810
|
+
}
|
|
795
811
|
}
|
|
796
812
|
throw new RequestError(`Request failed: ${response.status}`, response.status);
|
|
797
813
|
}
|
|
@@ -999,6 +1015,88 @@ var AgentClient = class {
|
|
|
999
1015
|
return this.request(`/plugins/bundle${query}`);
|
|
1000
1016
|
}
|
|
1001
1017
|
// ===========================================================================
|
|
1018
|
+
// Workflows API - Personal
|
|
1019
|
+
// ===========================================================================
|
|
1020
|
+
/**
|
|
1021
|
+
* List personal workflows.
|
|
1022
|
+
*/
|
|
1023
|
+
async listWorkflows() {
|
|
1024
|
+
return this.request("/workflows");
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Get personal workflow by name.
|
|
1028
|
+
*/
|
|
1029
|
+
async getWorkflow(name) {
|
|
1030
|
+
return this.request(`/workflows/${name}`);
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Create or update a personal workflow.
|
|
1034
|
+
*/
|
|
1035
|
+
async saveWorkflow(workflow) {
|
|
1036
|
+
return this.request("/workflows", {
|
|
1037
|
+
method: "POST",
|
|
1038
|
+
body: JSON.stringify(workflow)
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Delete a personal workflow.
|
|
1043
|
+
*/
|
|
1044
|
+
async deleteWorkflow(name) {
|
|
1045
|
+
return this.request(`/workflows/${name}`, {
|
|
1046
|
+
method: "DELETE"
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Run a personal workflow.
|
|
1051
|
+
*/
|
|
1052
|
+
async runWorkflow(name, inputs) {
|
|
1053
|
+
return this.request(`/workflows/${name}/run`, {
|
|
1054
|
+
method: "POST",
|
|
1055
|
+
body: JSON.stringify({ inputs })
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
// ===========================================================================
|
|
1059
|
+
// Workflows API - Organization
|
|
1060
|
+
// ===========================================================================
|
|
1061
|
+
/**
|
|
1062
|
+
* List organization workflows.
|
|
1063
|
+
*/
|
|
1064
|
+
async listOrgWorkflows() {
|
|
1065
|
+
return this.request("/workflows/org");
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Get organization workflow by name.
|
|
1069
|
+
*/
|
|
1070
|
+
async getOrgWorkflow(name) {
|
|
1071
|
+
return this.request(`/workflows/org/${name}`);
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* Create or update an organization workflow.
|
|
1075
|
+
*/
|
|
1076
|
+
async saveOrgWorkflow(workflow) {
|
|
1077
|
+
return this.request("/workflows/org", {
|
|
1078
|
+
method: "POST",
|
|
1079
|
+
body: JSON.stringify(workflow)
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Delete an organization workflow.
|
|
1084
|
+
*/
|
|
1085
|
+
async deleteOrgWorkflow(name) {
|
|
1086
|
+
return this.request(`/workflows/org/${name}`, {
|
|
1087
|
+
method: "DELETE"
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Run an organization workflow.
|
|
1092
|
+
*/
|
|
1093
|
+
async runOrgWorkflow(name, inputs) {
|
|
1094
|
+
return this.request(`/workflows/org/${name}/run`, {
|
|
1095
|
+
method: "POST",
|
|
1096
|
+
body: JSON.stringify({ inputs })
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
// ===========================================================================
|
|
1002
1100
|
// Utility methods
|
|
1003
1101
|
// ===========================================================================
|
|
1004
1102
|
encodeBase64(data) {
|
|
@@ -2061,7 +2159,7 @@ async function createRNSSEStream(options) {
|
|
|
2061
2159
|
reject(new Error(authError));
|
|
2062
2160
|
return;
|
|
2063
2161
|
}
|
|
2064
|
-
if (errorMessage.includes("404")) {
|
|
2162
|
+
if (status === 404 || errorMessage.includes("404")) {
|
|
2065
2163
|
emitter.emit("error", { error: "Session not found or not active" });
|
|
2066
2164
|
cleanup();
|
|
2067
2165
|
reject(new Error("Session not found or not active"));
|
|
@@ -2441,26 +2539,38 @@ function useJadeSessionCore(options = {}) {
|
|
|
2441
2539
|
}
|
|
2442
2540
|
}, [appState, pauseOnBackground, session.isStreaming]);
|
|
2443
2541
|
const setupEventHandlers = useCallback(
|
|
2444
|
-
(emitter) => {
|
|
2542
|
+
(emitter, streamSessionId) => {
|
|
2543
|
+
const resolvedId = { current: streamSessionId };
|
|
2544
|
+
const guard = (prev) => {
|
|
2545
|
+
if (resolvedId.current && prev.sessionId !== resolvedId.current) return true;
|
|
2546
|
+
return false;
|
|
2547
|
+
};
|
|
2445
2548
|
emitter.on("session", (data) => {
|
|
2549
|
+
resolvedId.current = data.session_id;
|
|
2446
2550
|
setSession((prev) => ({ ...prev, sessionId: data.session_id }));
|
|
2447
2551
|
});
|
|
2448
2552
|
emitter.on("content_block_start", (data) => {
|
|
2449
2553
|
const block = data.content_block;
|
|
2450
2554
|
if (block?.type === "text") {
|
|
2451
2555
|
accumulatedTextRef.current[data.index] = "";
|
|
2452
|
-
setSession((prev) =>
|
|
2556
|
+
setSession((prev) => {
|
|
2557
|
+
if (guard(prev)) return prev;
|
|
2558
|
+
return { ...prev, showTinkering: false };
|
|
2559
|
+
});
|
|
2453
2560
|
} else if (block?.type === "tool_use") {
|
|
2454
|
-
setSession((prev) =>
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2561
|
+
setSession((prev) => {
|
|
2562
|
+
if (guard(prev)) return prev;
|
|
2563
|
+
return {
|
|
2564
|
+
...prev,
|
|
2565
|
+
streamingToolCall: {
|
|
2566
|
+
tool_name: block.name || "",
|
|
2567
|
+
tool_use_id: block.id || "",
|
|
2568
|
+
block_index: data.index,
|
|
2569
|
+
accumulated_input: ""
|
|
2570
|
+
},
|
|
2571
|
+
showTinkering: false
|
|
2572
|
+
};
|
|
2573
|
+
});
|
|
2464
2574
|
}
|
|
2465
2575
|
});
|
|
2466
2576
|
emitter.on("content_block_delta", (data) => {
|
|
@@ -2468,14 +2578,18 @@ function useJadeSessionCore(options = {}) {
|
|
|
2468
2578
|
if (delta?.type === "text_delta" && delta.text) {
|
|
2469
2579
|
const idx = data.index ?? 0;
|
|
2470
2580
|
accumulatedTextRef.current[idx] = (accumulatedTextRef.current[idx] || "") + delta.text;
|
|
2471
|
-
setSession((prev) =>
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2581
|
+
setSession((prev) => {
|
|
2582
|
+
if (guard(prev)) return prev;
|
|
2583
|
+
return {
|
|
2584
|
+
...prev,
|
|
2585
|
+
streamingText: accumulatedTextRef.current[idx],
|
|
2586
|
+
streamingBlockIndex: idx,
|
|
2587
|
+
showTinkering: false
|
|
2588
|
+
};
|
|
2589
|
+
});
|
|
2477
2590
|
} else if (delta?.type === "input_json_delta" && delta.partial_json) {
|
|
2478
2591
|
setSession((prev) => {
|
|
2592
|
+
if (guard(prev)) return prev;
|
|
2479
2593
|
if (!prev.streamingToolCall) return prev;
|
|
2480
2594
|
return {
|
|
2481
2595
|
...prev,
|
|
@@ -2491,11 +2605,14 @@ function useJadeSessionCore(options = {}) {
|
|
|
2491
2605
|
const idx = data.index ?? 0;
|
|
2492
2606
|
if (accumulatedTextRef.current[idx] !== void 0) {
|
|
2493
2607
|
delete accumulatedTextRef.current[idx];
|
|
2494
|
-
setSession((prev) =>
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2608
|
+
setSession((prev) => {
|
|
2609
|
+
if (guard(prev)) return prev;
|
|
2610
|
+
return {
|
|
2611
|
+
...prev,
|
|
2612
|
+
streamingText: void 0,
|
|
2613
|
+
streamingBlockIndex: void 0
|
|
2614
|
+
};
|
|
2615
|
+
});
|
|
2499
2616
|
}
|
|
2500
2617
|
});
|
|
2501
2618
|
emitter.on("entry", (data) => {
|
|
@@ -2507,54 +2624,69 @@ function useJadeSessionCore(options = {}) {
|
|
|
2507
2624
|
}
|
|
2508
2625
|
}
|
|
2509
2626
|
if (entry.type === "tool_call") {
|
|
2510
|
-
setSession((prev) =>
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2627
|
+
setSession((prev) => {
|
|
2628
|
+
if (guard(prev)) return prev;
|
|
2629
|
+
return {
|
|
2630
|
+
...prev,
|
|
2631
|
+
conversation: [...prev.conversation, entry],
|
|
2632
|
+
streamingToolCall: void 0,
|
|
2633
|
+
isExecutingTool: true,
|
|
2634
|
+
executingToolName: entry.tool_name
|
|
2635
|
+
};
|
|
2636
|
+
});
|
|
2517
2637
|
} else if (entry.type === "tool_result") {
|
|
2518
|
-
setSession((prev) =>
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2638
|
+
setSession((prev) => {
|
|
2639
|
+
if (guard(prev)) return prev;
|
|
2640
|
+
return {
|
|
2641
|
+
...prev,
|
|
2642
|
+
conversation: [...prev.conversation, entry],
|
|
2643
|
+
isExecutingTool: false,
|
|
2644
|
+
executingToolName: void 0
|
|
2645
|
+
};
|
|
2646
|
+
});
|
|
2524
2647
|
} else {
|
|
2525
|
-
setSession((prev) =>
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2648
|
+
setSession((prev) => {
|
|
2649
|
+
if (guard(prev)) return prev;
|
|
2650
|
+
return {
|
|
2651
|
+
...prev,
|
|
2652
|
+
conversation: [...prev.conversation, entry]
|
|
2653
|
+
};
|
|
2654
|
+
});
|
|
2529
2655
|
}
|
|
2530
2656
|
});
|
|
2531
2657
|
emitter.on("complete", (data) => {
|
|
2532
|
-
setSession((prev) =>
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2658
|
+
setSession((prev) => {
|
|
2659
|
+
if (guard(prev)) return prev;
|
|
2660
|
+
return {
|
|
2661
|
+
...prev,
|
|
2662
|
+
sessionId: data.session_id || prev.sessionId,
|
|
2663
|
+
isStreaming: false,
|
|
2664
|
+
showTinkering: false,
|
|
2665
|
+
isExecutingTool: false,
|
|
2666
|
+
executingToolName: void 0
|
|
2667
|
+
};
|
|
2668
|
+
});
|
|
2540
2669
|
abortRef.current = null;
|
|
2541
2670
|
});
|
|
2542
2671
|
emitter.on("error", (data) => {
|
|
2543
|
-
setSession((prev) =>
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
...prev
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2672
|
+
setSession((prev) => {
|
|
2673
|
+
if (guard(prev)) return prev;
|
|
2674
|
+
return {
|
|
2675
|
+
...prev,
|
|
2676
|
+
conversation: [
|
|
2677
|
+
...prev.conversation,
|
|
2678
|
+
{
|
|
2679
|
+
type: "error",
|
|
2680
|
+
text: data.error,
|
|
2681
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2682
|
+
}
|
|
2683
|
+
],
|
|
2684
|
+
isStreaming: false,
|
|
2685
|
+
showTinkering: false,
|
|
2686
|
+
isExecutingTool: false,
|
|
2687
|
+
executingToolName: void 0
|
|
2688
|
+
};
|
|
2689
|
+
});
|
|
2558
2690
|
abortRef.current = null;
|
|
2559
2691
|
});
|
|
2560
2692
|
},
|
|
@@ -2581,7 +2713,7 @@ function useJadeSessionCore(options = {}) {
|
|
|
2581
2713
|
skills
|
|
2582
2714
|
});
|
|
2583
2715
|
abortRef.current = abort;
|
|
2584
|
-
setupEventHandlers(emitter);
|
|
2716
|
+
setupEventHandlers(emitter, session.sessionId);
|
|
2585
2717
|
},
|
|
2586
2718
|
[client, session.sessionId, session.isStreaming, setupEventHandlers]
|
|
2587
2719
|
);
|
|
@@ -2618,6 +2750,10 @@ function useJadeSessionCore(options = {}) {
|
|
|
2618
2750
|
}
|
|
2619
2751
|
}, [client, session.sessionId]);
|
|
2620
2752
|
const clear = useCallback(() => {
|
|
2753
|
+
abortRef.current?.();
|
|
2754
|
+
abortRef.current = null;
|
|
2755
|
+
accumulatedTextRef.current = {};
|
|
2756
|
+
cancelInProgressRef.current = false;
|
|
2621
2757
|
setSession({
|
|
2622
2758
|
conversation: [],
|
|
2623
2759
|
isStreaming: false
|
|
@@ -2663,7 +2799,7 @@ function useJadeSessionCore(options = {}) {
|
|
|
2663
2799
|
accumulatedTextRef.current = {};
|
|
2664
2800
|
const { emitter, abort } = client.reconnect(targetSessionId, -1);
|
|
2665
2801
|
abortRef.current = abort;
|
|
2666
|
-
setupEventHandlers(emitter);
|
|
2802
|
+
setupEventHandlers(emitter, targetSessionId);
|
|
2667
2803
|
},
|
|
2668
2804
|
[client, setupEventHandlers]
|
|
2669
2805
|
);
|