@letta-ai/letta-agent-sdk 0.3.0 → 0.3.2
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 +29 -1
- package/dist/app-server-management.d.ts +61 -1
- package/dist/app-server-management.d.ts.map +1 -1
- package/dist/app-server-session.d.ts +7 -4
- package/dist/app-server-session.d.ts.map +1 -1
- package/dist/client-base.d.ts +9 -1
- package/dist/client-base.d.ts.map +1 -1
- package/dist/client-entry.js +677 -61
- package/dist/client-entry.js.map +12 -11
- package/dist/client.d.ts.map +1 -1
- package/dist/cloud-management.d.ts +3 -1
- package/dist/cloud-management.d.ts.map +1 -1
- package/dist/cloud-session.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +733 -71
- package/dist/index.js.map +16 -15
- package/dist/interactiveToolPolicy.d.ts +7 -0
- package/dist/interactiveToolPolicy.d.ts.map +1 -1
- package/dist/local-app-server-session.d.ts +1 -1
- package/dist/local-app-server-session.d.ts.map +1 -1
- package/dist/management-types.d.ts +11 -1
- package/dist/management-types.d.ts.map +1 -1
- package/dist/management.d.ts +12 -1
- package/dist/management.d.ts.map +1 -1
- package/dist/remote-client-session-core.d.ts +24 -1
- package/dist/remote-client-session-core.d.ts.map +1 -1
- package/dist/request-ids.d.ts +24 -0
- package/dist/request-ids.d.ts.map +1 -0
- package/dist/session.d.ts +13 -2
- package/dist/session.d.ts.map +1 -1
- package/dist/types.d.ts +133 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -701,18 +701,101 @@ function createAppServerClient(options) {
|
|
|
701
701
|
return new AppServerClient(options);
|
|
702
702
|
}
|
|
703
703
|
|
|
704
|
+
// src/request-ids.ts
|
|
705
|
+
var processRequestCounter = 0;
|
|
706
|
+
function createRequestIdGenerator() {
|
|
707
|
+
const nonce = Math.random().toString(36).slice(2, 10);
|
|
708
|
+
return (prefix = "req") => `${prefix}-${nonce}-${++processRequestCounter}`;
|
|
709
|
+
}
|
|
710
|
+
function applyUniqueRequestIds(client) {
|
|
711
|
+
client.nextRequestId = createRequestIdGenerator();
|
|
712
|
+
return client;
|
|
713
|
+
}
|
|
714
|
+
|
|
704
715
|
// src/app-server-management.ts
|
|
716
|
+
var DEFAULT_IDLE_LINGER_MS = 250;
|
|
717
|
+
var registeredTransportsByUrl = new Map;
|
|
718
|
+
function appServerUrlKey(value) {
|
|
719
|
+
try {
|
|
720
|
+
const url = new URL(value);
|
|
721
|
+
if (url.protocol === "http:")
|
|
722
|
+
url.protocol = "ws:";
|
|
723
|
+
if (url.protocol === "https:")
|
|
724
|
+
url.protocol = "wss:";
|
|
725
|
+
url.hash = "";
|
|
726
|
+
url.searchParams.delete("channel");
|
|
727
|
+
return url.toString();
|
|
728
|
+
} catch {
|
|
729
|
+
return value;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
function registerManagementTransport(url, transport) {
|
|
733
|
+
const key = appServerUrlKey(url);
|
|
734
|
+
const registration = { transport };
|
|
735
|
+
const transports = registeredTransportsByUrl.get(key) ?? new Set;
|
|
736
|
+
transports.add(registration);
|
|
737
|
+
registeredTransportsByUrl.set(key, transports);
|
|
738
|
+
return () => {
|
|
739
|
+
transports.delete(registration);
|
|
740
|
+
if (transports.size === 0) {
|
|
741
|
+
registeredTransportsByUrl.delete(key);
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
async function releaseAppServerManagementConnections(url) {
|
|
746
|
+
const key = appServerUrlKey(url);
|
|
747
|
+
while (true) {
|
|
748
|
+
const transports = [...registeredTransportsByUrl.get(key) ?? []];
|
|
749
|
+
if (transports.length === 0)
|
|
750
|
+
return;
|
|
751
|
+
await Promise.all(transports.map(({ transport }) => transport.releaseIdleConnection()));
|
|
752
|
+
}
|
|
753
|
+
}
|
|
705
754
|
function ensureResponse(response, value, fallback) {
|
|
706
755
|
if (!response.success || value == null) {
|
|
707
756
|
throw new Error(response.error ?? fallback);
|
|
708
757
|
}
|
|
709
758
|
return value;
|
|
710
759
|
}
|
|
760
|
+
function modelEntries(value) {
|
|
761
|
+
if (!Array.isArray(value))
|
|
762
|
+
return [];
|
|
763
|
+
return value.flatMap((entry) => {
|
|
764
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
765
|
+
return [];
|
|
766
|
+
const record = entry;
|
|
767
|
+
if (typeof record.id !== "string" || typeof record.handle !== "string" || typeof record.label !== "string" || typeof record.description !== "string") {
|
|
768
|
+
return [];
|
|
769
|
+
}
|
|
770
|
+
return [{
|
|
771
|
+
...record,
|
|
772
|
+
id: record.id,
|
|
773
|
+
handle: record.handle,
|
|
774
|
+
label: record.label,
|
|
775
|
+
description: record.description
|
|
776
|
+
}];
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
function stringRecord(value) {
|
|
780
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
const entries = Object.entries(value).filter((entry) => typeof entry[1] === "string");
|
|
784
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
785
|
+
}
|
|
711
786
|
|
|
712
787
|
class AppServerManagementTransport {
|
|
713
788
|
options;
|
|
789
|
+
connectionPromise = null;
|
|
790
|
+
releasePromise = null;
|
|
791
|
+
closingConnections = new Set;
|
|
792
|
+
inFlightRequests = 0;
|
|
793
|
+
idleWaiters = new Set;
|
|
794
|
+
idleTimer = null;
|
|
795
|
+
idleLingerMs;
|
|
714
796
|
constructor(options) {
|
|
715
797
|
this.options = options;
|
|
798
|
+
this.idleLingerMs = options.idleLingerMs ?? DEFAULT_IDLE_LINGER_MS;
|
|
716
799
|
}
|
|
717
800
|
async listAgents(query) {
|
|
718
801
|
const response = await this.request("agent_list", { query }, "agent_list_response");
|
|
@@ -729,6 +812,30 @@ class AppServerManagementTransport {
|
|
|
729
812
|
const response = await this.request("agent_update", { agent_id: agentId, body }, "agent_update_response");
|
|
730
813
|
return ensureResponse(response, response.agent, `Failed to update agent ${agentId}.`);
|
|
731
814
|
}
|
|
815
|
+
async deleteAgent(agentId) {
|
|
816
|
+
const response = await this.request("agent_delete", { agent_id: agentId }, "agent_delete_response");
|
|
817
|
+
if (!response.success) {
|
|
818
|
+
throw new Error(response.error ?? `Failed to delete agent ${agentId}.`);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
async listModels() {
|
|
822
|
+
const response = await this.request("list_models", {}, "list_models_response");
|
|
823
|
+
if (!response.success) {
|
|
824
|
+
throw new Error(response.error ?? "Failed to list models.");
|
|
825
|
+
}
|
|
826
|
+
const result = {
|
|
827
|
+
entries: modelEntries(response.entries)
|
|
828
|
+
};
|
|
829
|
+
if (response.available_handles === null) {
|
|
830
|
+
result.availableHandles = null;
|
|
831
|
+
} else if (Array.isArray(response.available_handles)) {
|
|
832
|
+
result.availableHandles = response.available_handles.filter((handle) => typeof handle === "string");
|
|
833
|
+
}
|
|
834
|
+
const aliases = stringRecord(response.byok_provider_aliases);
|
|
835
|
+
if (aliases)
|
|
836
|
+
result.byokProviderAliases = aliases;
|
|
837
|
+
return result;
|
|
838
|
+
}
|
|
732
839
|
async listConversations(query) {
|
|
733
840
|
const response = await this.request("conversation_list", { query }, "conversation_list_response");
|
|
734
841
|
if (!response.success) {
|
|
@@ -755,38 +862,206 @@ class AppServerManagementTransport {
|
|
|
755
862
|
}
|
|
756
863
|
return { messages: response.messages };
|
|
757
864
|
}
|
|
865
|
+
releaseIdleConnection() {
|
|
866
|
+
this.clearIdleTimer();
|
|
867
|
+
if (this.releasePromise)
|
|
868
|
+
return this.releasePromise;
|
|
869
|
+
const release = this.releaseConnectionWhenIdle();
|
|
870
|
+
this.releasePromise = release;
|
|
871
|
+
release.then(() => {
|
|
872
|
+
if (this.releasePromise === release) {
|
|
873
|
+
this.releasePromise = null;
|
|
874
|
+
}
|
|
875
|
+
}, () => {
|
|
876
|
+
if (this.releasePromise === release) {
|
|
877
|
+
this.releasePromise = null;
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
return release;
|
|
881
|
+
}
|
|
758
882
|
async request(type, body, responseType) {
|
|
759
|
-
|
|
883
|
+
if (this.releasePromise) {
|
|
884
|
+
await this.releasePromise;
|
|
885
|
+
}
|
|
886
|
+
if (this.closingConnections.size > 0) {
|
|
887
|
+
await Promise.all([...this.closingConnections]);
|
|
888
|
+
}
|
|
889
|
+
this.clearIdleTimer();
|
|
890
|
+
this.inFlightRequests += 1;
|
|
891
|
+
try {
|
|
892
|
+
const { client } = await this.acquireConnection();
|
|
893
|
+
const command = {
|
|
894
|
+
type,
|
|
895
|
+
request_id: client.nextRequestId(type),
|
|
896
|
+
...body
|
|
897
|
+
};
|
|
898
|
+
const response = await client.request(command, {
|
|
899
|
+
predicate: (message) => message.type === responseType
|
|
900
|
+
});
|
|
901
|
+
return response;
|
|
902
|
+
} finally {
|
|
903
|
+
this.inFlightRequests -= 1;
|
|
904
|
+
if (this.inFlightRequests === 0) {
|
|
905
|
+
const waiters = [...this.idleWaiters];
|
|
906
|
+
this.idleWaiters.clear();
|
|
907
|
+
for (const resolve of waiters)
|
|
908
|
+
resolve();
|
|
909
|
+
if (!this.releasePromise) {
|
|
910
|
+
this.scheduleIdleRelease();
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
async releaseConnectionWhenIdle() {
|
|
916
|
+
if (this.inFlightRequests > 0) {
|
|
917
|
+
await new Promise((resolve) => {
|
|
918
|
+
this.idleWaiters.add(resolve);
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
this.clearIdleTimer();
|
|
922
|
+
const promise = this.connectionPromise;
|
|
923
|
+
try {
|
|
924
|
+
if (promise) {
|
|
925
|
+
this.connectionPromise = null;
|
|
926
|
+
await this.trackClosingConnection(await promise);
|
|
927
|
+
}
|
|
928
|
+
if (this.closingConnections.size > 0) {
|
|
929
|
+
await Promise.all([...this.closingConnections]);
|
|
930
|
+
}
|
|
931
|
+
} catch {}
|
|
932
|
+
}
|
|
933
|
+
acquireConnection() {
|
|
934
|
+
if (this.connectionPromise)
|
|
935
|
+
return this.connectionPromise;
|
|
936
|
+
const promise = this.openConnection().then((connection) => {
|
|
937
|
+
connection.detachDisconnect = connection.client.onDisconnect(() => {
|
|
938
|
+
this.discardConnection(promise, connection);
|
|
939
|
+
});
|
|
940
|
+
return connection;
|
|
941
|
+
}, (error) => {
|
|
942
|
+
if (this.connectionPromise === promise) {
|
|
943
|
+
this.connectionPromise = null;
|
|
944
|
+
}
|
|
945
|
+
throw error;
|
|
946
|
+
});
|
|
947
|
+
this.connectionPromise = promise;
|
|
948
|
+
return promise;
|
|
949
|
+
}
|
|
950
|
+
async openConnection() {
|
|
951
|
+
const ownedConnection = this.options.url ? null : await this.options.connect?.() ?? null;
|
|
760
952
|
const url = this.options.url ?? ownedConnection?.url;
|
|
761
953
|
if (!url) {
|
|
762
954
|
throw new Error("App-server management requires a url or connect hook.");
|
|
763
955
|
}
|
|
956
|
+
const unregister = registerManagementTransport(url, this);
|
|
764
957
|
let client = null;
|
|
765
958
|
try {
|
|
766
|
-
client = createAppServerClient({
|
|
959
|
+
client = applyUniqueRequestIds(createAppServerClient({
|
|
767
960
|
url,
|
|
768
961
|
...this.options.authToken !== undefined ? { authToken: this.options.authToken } : {},
|
|
769
962
|
...this.options.WebSocket ? {
|
|
770
963
|
WebSocket: this.options.WebSocket
|
|
771
964
|
} : {},
|
|
772
965
|
...this.options.requestTimeoutMs !== undefined ? { requestTimeoutMs: this.options.requestTimeoutMs } : {}
|
|
773
|
-
});
|
|
966
|
+
}));
|
|
774
967
|
await client.connect();
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
968
|
+
} catch (error) {
|
|
969
|
+
try {
|
|
970
|
+
if (client) {
|
|
971
|
+
const controlClosed = waitForSocketClose(client.control);
|
|
972
|
+
client.close();
|
|
973
|
+
ownedConnection?.close();
|
|
974
|
+
await controlClosed;
|
|
975
|
+
} else {
|
|
976
|
+
ownedConnection?.close();
|
|
977
|
+
}
|
|
978
|
+
} catch {} finally {
|
|
979
|
+
unregister();
|
|
980
|
+
}
|
|
981
|
+
throw error;
|
|
982
|
+
}
|
|
983
|
+
return {
|
|
984
|
+
client,
|
|
985
|
+
ownedConnection,
|
|
986
|
+
detachDisconnect: () => {},
|
|
987
|
+
unregister
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
discardConnection(promise, connection) {
|
|
991
|
+
if (this.connectionPromise === promise) {
|
|
992
|
+
this.connectionPromise = null;
|
|
993
|
+
this.clearIdleTimer();
|
|
787
994
|
}
|
|
995
|
+
this.trackClosingConnection(connection);
|
|
996
|
+
}
|
|
997
|
+
trackClosingConnection(connection) {
|
|
998
|
+
const closing = closeConnection(connection);
|
|
999
|
+
this.closingConnections.add(closing);
|
|
1000
|
+
closing.then(() => this.closingConnections.delete(closing), () => this.closingConnections.delete(closing));
|
|
1001
|
+
return closing;
|
|
1002
|
+
}
|
|
1003
|
+
scheduleIdleRelease() {
|
|
1004
|
+
if (!this.connectionPromise)
|
|
1005
|
+
return;
|
|
1006
|
+
this.clearIdleTimer();
|
|
1007
|
+
const timer = setTimeout(() => {
|
|
1008
|
+
this.idleTimer = null;
|
|
1009
|
+
this.releaseIdleConnection();
|
|
1010
|
+
}, this.idleLingerMs);
|
|
1011
|
+
this.idleTimer = timer;
|
|
1012
|
+
timer.unref?.();
|
|
1013
|
+
}
|
|
1014
|
+
clearIdleTimer() {
|
|
1015
|
+
if (this.idleTimer === null)
|
|
1016
|
+
return;
|
|
1017
|
+
clearTimeout(this.idleTimer);
|
|
1018
|
+
this.idleTimer = null;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
async function closeConnection(connection) {
|
|
1022
|
+
connection.detachDisconnect();
|
|
1023
|
+
const controlClosed = waitForSocketClose(connection.client.control);
|
|
1024
|
+
try {
|
|
1025
|
+
connection.client.close();
|
|
1026
|
+
connection.ownedConnection?.close();
|
|
1027
|
+
await controlClosed;
|
|
1028
|
+
} finally {
|
|
1029
|
+
connection.unregister();
|
|
788
1030
|
}
|
|
789
1031
|
}
|
|
1032
|
+
function waitForSocketClose(socket) {
|
|
1033
|
+
if (socket.readyState === 3)
|
|
1034
|
+
return Promise.resolve();
|
|
1035
|
+
return new Promise((resolve) => {
|
|
1036
|
+
let settled = false;
|
|
1037
|
+
let detach = () => {};
|
|
1038
|
+
const finish = () => {
|
|
1039
|
+
if (settled)
|
|
1040
|
+
return;
|
|
1041
|
+
settled = true;
|
|
1042
|
+
clearTimeout(timeout);
|
|
1043
|
+
detach();
|
|
1044
|
+
resolve();
|
|
1045
|
+
};
|
|
1046
|
+
if (socket.addEventListener) {
|
|
1047
|
+
socket.addEventListener("close", finish);
|
|
1048
|
+
detach = () => socket.removeEventListener?.("close", finish);
|
|
1049
|
+
} else if (socket.once) {
|
|
1050
|
+
socket.once("close", finish);
|
|
1051
|
+
detach = () => socket.off?.("close", finish);
|
|
1052
|
+
} else if (socket.on) {
|
|
1053
|
+
socket.on("close", finish);
|
|
1054
|
+
detach = () => socket.off?.("close", finish);
|
|
1055
|
+
} else {
|
|
1056
|
+
resolve();
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
const timeout = setTimeout(finish, 1000);
|
|
1060
|
+
timeout.unref?.();
|
|
1061
|
+
if (socket.readyState === 3)
|
|
1062
|
+
finish();
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
790
1065
|
|
|
791
1066
|
// node_modules/@letta-ai/letta-code/dist/agent-presets.js
|
|
792
1067
|
var LETTA_CODE_ORIGIN_TAG = "origin:letta-code";
|
|
@@ -5424,6 +5699,36 @@ function requiresRuntimeUserInput(toolName) {
|
|
|
5424
5699
|
function isHeadlessAutoAllowTool(toolName) {
|
|
5425
5700
|
return HEADLESS_AUTO_ALLOW_TOOLS.has(toolName);
|
|
5426
5701
|
}
|
|
5702
|
+
function normalizePermissionSuggestions(value) {
|
|
5703
|
+
if (!Array.isArray(value))
|
|
5704
|
+
return;
|
|
5705
|
+
const suggestions = [];
|
|
5706
|
+
for (const entry of value) {
|
|
5707
|
+
if (!entry || typeof entry !== "object")
|
|
5708
|
+
continue;
|
|
5709
|
+
const record = entry;
|
|
5710
|
+
if (typeof record.id === "string" && typeof record.text === "string") {
|
|
5711
|
+
suggestions.push({ id: record.id, text: record.text });
|
|
5712
|
+
}
|
|
5713
|
+
}
|
|
5714
|
+
return suggestions;
|
|
5715
|
+
}
|
|
5716
|
+
function buildCanUseToolContext(request, requestId) {
|
|
5717
|
+
const context = {};
|
|
5718
|
+
if (typeof requestId === "string")
|
|
5719
|
+
context.requestId = requestId;
|
|
5720
|
+
if (typeof request.tool_call_id === "string")
|
|
5721
|
+
context.toolCallId = request.tool_call_id;
|
|
5722
|
+
const suggestions = normalizePermissionSuggestions(request.permission_suggestions);
|
|
5723
|
+
if (suggestions !== undefined)
|
|
5724
|
+
context.permissionSuggestions = suggestions;
|
|
5725
|
+
if (typeof request.blocked_path === "string" || request.blocked_path === null) {
|
|
5726
|
+
context.blockedPath = request.blocked_path;
|
|
5727
|
+
}
|
|
5728
|
+
if (Array.isArray(request.diffs))
|
|
5729
|
+
context.diffs = request.diffs;
|
|
5730
|
+
return context;
|
|
5731
|
+
}
|
|
5427
5732
|
|
|
5428
5733
|
// src/remote-client-session-core.ts
|
|
5429
5734
|
var FAILURE_STOP_REASONS = new Set([
|
|
@@ -5701,6 +6006,121 @@ function queueItems(message) {
|
|
|
5701
6006
|
];
|
|
5702
6007
|
});
|
|
5703
6008
|
}
|
|
6009
|
+
function deviceStatusRecord(message) {
|
|
6010
|
+
if (message.type !== "update_device_status")
|
|
6011
|
+
return null;
|
|
6012
|
+
const status = message.device_status;
|
|
6013
|
+
return status && typeof status === "object" && !Array.isArray(status) ? status : null;
|
|
6014
|
+
}
|
|
6015
|
+
function pendingControlRequests(status) {
|
|
6016
|
+
const pending = status.pending_control_requests;
|
|
6017
|
+
if (!Array.isArray(pending))
|
|
6018
|
+
return [];
|
|
6019
|
+
return pending.flatMap((item) => {
|
|
6020
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
6021
|
+
return [];
|
|
6022
|
+
const record = item;
|
|
6023
|
+
if (typeof record.request_id !== "string")
|
|
6024
|
+
return [];
|
|
6025
|
+
const request = record.request && typeof record.request === "object" && !Array.isArray(record.request) ? record.request : null;
|
|
6026
|
+
if (!request || typeof request.tool_name !== "string")
|
|
6027
|
+
return [];
|
|
6028
|
+
const entry = {
|
|
6029
|
+
requestId: record.request_id,
|
|
6030
|
+
toolName: request.tool_name,
|
|
6031
|
+
permissionSuggestions: permissionSuggestions(request.permission_suggestions),
|
|
6032
|
+
blockedPath: typeof request.blocked_path === "string" || request.blocked_path === null ? request.blocked_path : null
|
|
6033
|
+
};
|
|
6034
|
+
if (typeof request.tool_call_id === "string")
|
|
6035
|
+
entry.toolCallId = request.tool_call_id;
|
|
6036
|
+
if (request.input && typeof request.input === "object" && !Array.isArray(request.input)) {
|
|
6037
|
+
entry.toolInput = request.input;
|
|
6038
|
+
}
|
|
6039
|
+
const previews = diffPreviews(request.diffs);
|
|
6040
|
+
if (previews !== undefined)
|
|
6041
|
+
entry.diffs = previews;
|
|
6042
|
+
return [entry];
|
|
6043
|
+
});
|
|
6044
|
+
}
|
|
6045
|
+
function permissionSuggestions(value) {
|
|
6046
|
+
if (!Array.isArray(value))
|
|
6047
|
+
return [];
|
|
6048
|
+
return value.flatMap((item) => {
|
|
6049
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
6050
|
+
return [];
|
|
6051
|
+
const record = item;
|
|
6052
|
+
return typeof record.id === "string" && typeof record.text === "string" ? [{ id: record.id, text: record.text }] : [];
|
|
6053
|
+
});
|
|
6054
|
+
}
|
|
6055
|
+
function diffPreviews(value) {
|
|
6056
|
+
if (!Array.isArray(value))
|
|
6057
|
+
return;
|
|
6058
|
+
return value.flatMap((item) => {
|
|
6059
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
6060
|
+
return [];
|
|
6061
|
+
const record = item;
|
|
6062
|
+
if (record.mode === "advanced" && typeof record.fileName === "string" && Array.isArray(record.hunks)) {
|
|
6063
|
+
return [{
|
|
6064
|
+
mode: "advanced",
|
|
6065
|
+
fileName: record.fileName,
|
|
6066
|
+
hunks: diffHunks(record.hunks)
|
|
6067
|
+
}];
|
|
6068
|
+
}
|
|
6069
|
+
if ((record.mode === "fallback" || record.mode === "unpreviewable") && typeof record.fileName === "string" && typeof record.reason === "string") {
|
|
6070
|
+
return [{
|
|
6071
|
+
mode: record.mode,
|
|
6072
|
+
fileName: record.fileName,
|
|
6073
|
+
reason: record.reason
|
|
6074
|
+
}];
|
|
6075
|
+
}
|
|
6076
|
+
return [];
|
|
6077
|
+
});
|
|
6078
|
+
}
|
|
6079
|
+
function diffHunks(value) {
|
|
6080
|
+
return value.flatMap((item) => {
|
|
6081
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
6082
|
+
return [];
|
|
6083
|
+
const record = item;
|
|
6084
|
+
if (typeof record.oldStart !== "number" || typeof record.oldLines !== "number" || typeof record.newStart !== "number" || typeof record.newLines !== "number" || !Array.isArray(record.lines)) {
|
|
6085
|
+
return [];
|
|
6086
|
+
}
|
|
6087
|
+
const lines = [];
|
|
6088
|
+
for (const line of record.lines) {
|
|
6089
|
+
if (!line || typeof line !== "object" || Array.isArray(line))
|
|
6090
|
+
continue;
|
|
6091
|
+
const lineRecord = line;
|
|
6092
|
+
const type = lineRecord.type;
|
|
6093
|
+
if (type !== "context" && type !== "add" && type !== "remove" || typeof lineRecord.content !== "string") {
|
|
6094
|
+
continue;
|
|
6095
|
+
}
|
|
6096
|
+
lines.push({
|
|
6097
|
+
type,
|
|
6098
|
+
content: lineRecord.content
|
|
6099
|
+
});
|
|
6100
|
+
}
|
|
6101
|
+
return [{
|
|
6102
|
+
oldStart: record.oldStart,
|
|
6103
|
+
oldLines: record.oldLines,
|
|
6104
|
+
newStart: record.newStart,
|
|
6105
|
+
newLines: record.newLines,
|
|
6106
|
+
lines
|
|
6107
|
+
}];
|
|
6108
|
+
});
|
|
6109
|
+
}
|
|
6110
|
+
function toSessionDeviceStatus(status) {
|
|
6111
|
+
const permissionMode = normalizePermissionMode(status.current_permission_mode);
|
|
6112
|
+
if (typeof status.is_online !== "boolean" || typeof status.is_processing !== "boolean" || permissionMode === undefined || !(typeof status.current_working_directory === "string" || status.current_working_directory === null)) {
|
|
6113
|
+
return null;
|
|
6114
|
+
}
|
|
6115
|
+
return {
|
|
6116
|
+
isOnline: status.is_online,
|
|
6117
|
+
isProcessing: status.is_processing,
|
|
6118
|
+
permissionMode,
|
|
6119
|
+
workingDirectory: status.current_working_directory,
|
|
6120
|
+
pendingControlRequests: pendingControlRequests(status),
|
|
6121
|
+
raw: { ...status }
|
|
6122
|
+
};
|
|
6123
|
+
}
|
|
5704
6124
|
function normalizeSendMessage(message) {
|
|
5705
6125
|
return message;
|
|
5706
6126
|
}
|
|
@@ -5719,6 +6139,7 @@ class RemoteClientSessionCore {
|
|
|
5719
6139
|
_modelSettings = null;
|
|
5720
6140
|
label;
|
|
5721
6141
|
requestTimeoutMs;
|
|
6142
|
+
initializePromise = null;
|
|
5722
6143
|
streamQueue = [];
|
|
5723
6144
|
streamResolvers = [];
|
|
5724
6145
|
removeMessageHandler = null;
|
|
@@ -5728,49 +6149,80 @@ class RemoteClientSessionCore {
|
|
|
5728
6149
|
messageCounter = 0;
|
|
5729
6150
|
clientMessageCounter = 0;
|
|
5730
6151
|
toolNames;
|
|
6152
|
+
deviceStatusListeners = new Set;
|
|
6153
|
+
deviceStatusRefreshCancels = new Set;
|
|
5731
6154
|
constructor(mode, config) {
|
|
5732
6155
|
this.mode = mode;
|
|
5733
6156
|
this.label = config.label;
|
|
5734
6157
|
this.requestTimeoutMs = config.requestTimeoutMs;
|
|
5735
6158
|
}
|
|
5736
6159
|
async initialize() {
|
|
6160
|
+
if (this.closed) {
|
|
6161
|
+
throw new Error("Session is closed");
|
|
6162
|
+
}
|
|
6163
|
+
if (this.initializePromise) {
|
|
6164
|
+
return this.initializePromise;
|
|
6165
|
+
}
|
|
5737
6166
|
if (this.initialized) {
|
|
5738
6167
|
throw new Error("Session already initialized");
|
|
5739
6168
|
}
|
|
6169
|
+
const attempt = this.performInitialize();
|
|
6170
|
+
const memo = attempt.catch((error) => {
|
|
6171
|
+
this.cleanupFailedInitialize();
|
|
6172
|
+
throw error;
|
|
6173
|
+
}).finally(() => {
|
|
6174
|
+
if (this.initializePromise === memo) {
|
|
6175
|
+
this.initializePromise = null;
|
|
6176
|
+
}
|
|
6177
|
+
});
|
|
6178
|
+
this.initializePromise = memo;
|
|
6179
|
+
return memo;
|
|
6180
|
+
}
|
|
6181
|
+
async performInitialize() {
|
|
6182
|
+
const init = await this.initializeRuntimeController();
|
|
6183
|
+
this.controller = init.controller;
|
|
6184
|
+
this.runtime = init.runtime;
|
|
6185
|
+
this._agentId = init.runtime.agent_id;
|
|
6186
|
+
this._conversationId = init.runtime.conversation_id;
|
|
6187
|
+
this._sessionId = `${init.runtime.agent_id}:${init.runtime.conversation_id}`;
|
|
6188
|
+
this._modelSettings = init.modelSettings ?? null;
|
|
6189
|
+
this._model = typeof init.model === "string" ? init.model : typeof this._modelSettings?.model === "string" ? this._modelSettings.model : "";
|
|
6190
|
+
this.toolNames = init.tools;
|
|
6191
|
+
this.removeMessageHandler = this.controller.onMessage(this.handleProtocolMessage);
|
|
6192
|
+
await this.afterRuntimeInitialized();
|
|
6193
|
+
await this.applyPostInitializeOptions();
|
|
5740
6194
|
if (this.closed) {
|
|
5741
6195
|
throw new Error("Session is closed");
|
|
5742
6196
|
}
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
this.
|
|
5748
|
-
|
|
5749
|
-
this.
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
await this.afterRuntimeInitialized();
|
|
5756
|
-
await this.applyPostInitializeOptions();
|
|
5757
|
-
const initMessage = {
|
|
5758
|
-
type: "init",
|
|
5759
|
-
agentId: init.runtime.agent_id,
|
|
5760
|
-
sessionId: this._sessionId,
|
|
5761
|
-
conversationId: init.runtime.conversation_id,
|
|
5762
|
-
model: this._model
|
|
5763
|
-
};
|
|
5764
|
-
if (this.toolNames !== undefined)
|
|
5765
|
-
initMessage.tools = this.toolNames;
|
|
5766
|
-
if (init.skillSources !== undefined) {
|
|
5767
|
-
initMessage.skillSources = init.skillSources;
|
|
5768
|
-
}
|
|
5769
|
-
return initMessage;
|
|
5770
|
-
} catch (error) {
|
|
5771
|
-
this.close();
|
|
5772
|
-
throw error;
|
|
6197
|
+
this.initialized = true;
|
|
6198
|
+
const initMessage = {
|
|
6199
|
+
type: "init",
|
|
6200
|
+
agentId: init.runtime.agent_id,
|
|
6201
|
+
sessionId: this._sessionId,
|
|
6202
|
+
conversationId: init.runtime.conversation_id,
|
|
6203
|
+
model: this._model
|
|
6204
|
+
};
|
|
6205
|
+
if (this.toolNames !== undefined)
|
|
6206
|
+
initMessage.tools = this.toolNames;
|
|
6207
|
+
if (init.skillSources !== undefined) {
|
|
6208
|
+
initMessage.skillSources = init.skillSources;
|
|
5773
6209
|
}
|
|
6210
|
+
return initMessage;
|
|
6211
|
+
}
|
|
6212
|
+
cleanupFailedInitialize() {
|
|
6213
|
+
this.removeMessageHandler?.();
|
|
6214
|
+
this.removeMessageHandler = null;
|
|
6215
|
+
this.controller?.close();
|
|
6216
|
+
this.controller = null;
|
|
6217
|
+
this.onCoreClose();
|
|
6218
|
+
this.runtime = null;
|
|
6219
|
+
this._agentId = null;
|
|
6220
|
+
this._conversationId = null;
|
|
6221
|
+
this._sessionId = null;
|
|
6222
|
+
this._modelSettings = null;
|
|
6223
|
+
this._model = "";
|
|
6224
|
+
this.toolNames = undefined;
|
|
6225
|
+
this.initialized = false;
|
|
5774
6226
|
}
|
|
5775
6227
|
async send(message) {
|
|
5776
6228
|
if (!this.initialized) {
|
|
@@ -5867,6 +6319,12 @@ class RemoteClientSessionCore {
|
|
|
5867
6319
|
if (!this.initialized) {
|
|
5868
6320
|
await this.initialize();
|
|
5869
6321
|
}
|
|
6322
|
+
if (!this.controller || !this.runtime) {
|
|
6323
|
+
throw new Error("Session is not initialized");
|
|
6324
|
+
}
|
|
6325
|
+
return this.applyModelUpdate(update);
|
|
6326
|
+
}
|
|
6327
|
+
async applyModelUpdate(update) {
|
|
5870
6328
|
if (!this.controller || !this.runtime) {
|
|
5871
6329
|
throw new Error("Session is not initialized");
|
|
5872
6330
|
}
|
|
@@ -5918,6 +6376,86 @@ class RemoteClientSessionCore {
|
|
|
5918
6376
|
removed: response.success
|
|
5919
6377
|
};
|
|
5920
6378
|
}
|
|
6379
|
+
async getDeviceStatus(options = {}) {
|
|
6380
|
+
if (!this.initialized) {
|
|
6381
|
+
await this.initialize();
|
|
6382
|
+
}
|
|
6383
|
+
if (!this.controller || !this.runtime) {
|
|
6384
|
+
throw new Error("Session is not initialized");
|
|
6385
|
+
}
|
|
6386
|
+
const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs ?? 30000;
|
|
6387
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) {
|
|
6388
|
+
throw new Error("Invalid device status timeout. Expected a positive integer.");
|
|
6389
|
+
}
|
|
6390
|
+
return this.refreshDeviceStatus(timeoutMs);
|
|
6391
|
+
}
|
|
6392
|
+
onDeviceStatus(listener) {
|
|
6393
|
+
if (typeof listener !== "function") {
|
|
6394
|
+
throw new Error("Invalid device status listener. Expected a function.");
|
|
6395
|
+
}
|
|
6396
|
+
this.deviceStatusListeners.add(listener);
|
|
6397
|
+
return () => {
|
|
6398
|
+
this.deviceStatusListeners.delete(listener);
|
|
6399
|
+
};
|
|
6400
|
+
}
|
|
6401
|
+
refreshDeviceStatus(timeoutMs) {
|
|
6402
|
+
const controller = this.controller;
|
|
6403
|
+
const runtime = this.runtime;
|
|
6404
|
+
if (!controller || !runtime) {
|
|
6405
|
+
return Promise.reject(new Error("Session is not initialized"));
|
|
6406
|
+
}
|
|
6407
|
+
return new Promise((resolve, reject) => {
|
|
6408
|
+
let status = null;
|
|
6409
|
+
let syncAcknowledged = false;
|
|
6410
|
+
let settled = false;
|
|
6411
|
+
const cleanup = () => {
|
|
6412
|
+
clearTimeout(timer);
|
|
6413
|
+
unsubscribe();
|
|
6414
|
+
this.deviceStatusRefreshCancels.delete(cancel);
|
|
6415
|
+
};
|
|
6416
|
+
const rejectOnce = (error) => {
|
|
6417
|
+
if (settled)
|
|
6418
|
+
return;
|
|
6419
|
+
settled = true;
|
|
6420
|
+
cleanup();
|
|
6421
|
+
reject(error);
|
|
6422
|
+
};
|
|
6423
|
+
const resolveIfComplete = () => {
|
|
6424
|
+
if (settled || !syncAcknowledged || !status)
|
|
6425
|
+
return;
|
|
6426
|
+
settled = true;
|
|
6427
|
+
cleanup();
|
|
6428
|
+
resolve(status);
|
|
6429
|
+
};
|
|
6430
|
+
const cancel = (error) => rejectOnce(error);
|
|
6431
|
+
const timer = setTimeout(() => {
|
|
6432
|
+
rejectOnce(new Error(`Timed out waiting for ${this.label} device status`));
|
|
6433
|
+
}, timeoutMs);
|
|
6434
|
+
timer.unref?.();
|
|
6435
|
+
const unsubscribe = this.onDeviceStatus((nextStatus) => {
|
|
6436
|
+
status = nextStatus;
|
|
6437
|
+
resolveIfComplete();
|
|
6438
|
+
});
|
|
6439
|
+
this.deviceStatusRefreshCancels.add(cancel);
|
|
6440
|
+
controller.request("sync", {
|
|
6441
|
+
runtime,
|
|
6442
|
+
recover_approvals: false,
|
|
6443
|
+
force_device_status: true
|
|
6444
|
+
}, {
|
|
6445
|
+
timeoutMs,
|
|
6446
|
+
predicate: (message) => message.type === "sync_response"
|
|
6447
|
+
}).then((response) => {
|
|
6448
|
+
if (response.success === false) {
|
|
6449
|
+
rejectOnce(new Error(typeof response.error === "string" ? response.error : `Failed to refresh ${this.label} device status`));
|
|
6450
|
+
return;
|
|
6451
|
+
}
|
|
6452
|
+
syncAcknowledged = true;
|
|
6453
|
+
resolveIfComplete();
|
|
6454
|
+
}, (error) => {
|
|
6455
|
+
rejectOnce(error instanceof Error ? error : new Error(String(error)));
|
|
6456
|
+
});
|
|
6457
|
+
});
|
|
6458
|
+
}
|
|
5921
6459
|
async listMessages(options = {}) {
|
|
5922
6460
|
if (!this.initialized) {
|
|
5923
6461
|
await this.initialize();
|
|
@@ -5970,6 +6508,11 @@ class RemoteClientSessionCore {
|
|
|
5970
6508
|
}
|
|
5971
6509
|
this.activeTurn = null;
|
|
5972
6510
|
this.pendingTurns.length = 0;
|
|
6511
|
+
for (const cancel of [...this.deviceStatusRefreshCancels]) {
|
|
6512
|
+
cancel(new Error(`Session closed while waiting for ${this.label} device status`));
|
|
6513
|
+
}
|
|
6514
|
+
this.deviceStatusRefreshCancels.clear();
|
|
6515
|
+
this.deviceStatusListeners.clear();
|
|
5973
6516
|
this.controller?.close();
|
|
5974
6517
|
this.controller = null;
|
|
5975
6518
|
this.onCoreClose();
|
|
@@ -6198,7 +6741,7 @@ class RemoteClientSessionCore {
|
|
|
6198
6741
|
if (this.mode.kind !== "session")
|
|
6199
6742
|
return;
|
|
6200
6743
|
if (this.mode.options.model !== undefined || this.mode.options.reasoningEffort !== undefined) {
|
|
6201
|
-
await this.
|
|
6744
|
+
await this.applyModelUpdate({
|
|
6202
6745
|
...this.mode.options.model !== undefined ? { model: this.mode.options.model } : {},
|
|
6203
6746
|
...this.mode.options.reasoningEffort !== undefined ? { reasoningEffort: this.mode.options.reasoningEffort } : {}
|
|
6204
6747
|
});
|
|
@@ -6207,6 +6750,18 @@ class RemoteClientSessionCore {
|
|
|
6207
6750
|
handleProtocolMessage = (message) => {
|
|
6208
6751
|
if (!this.runtime || !sameRuntime2(message, this.runtime))
|
|
6209
6752
|
return;
|
|
6753
|
+
const deviceStatus = deviceStatusRecord(message);
|
|
6754
|
+
if (deviceStatus) {
|
|
6755
|
+
const status = toSessionDeviceStatus(deviceStatus);
|
|
6756
|
+
if (!status)
|
|
6757
|
+
return;
|
|
6758
|
+
for (const listener of [...this.deviceStatusListeners]) {
|
|
6759
|
+
try {
|
|
6760
|
+
listener(status);
|
|
6761
|
+
} catch {}
|
|
6762
|
+
}
|
|
6763
|
+
return;
|
|
6764
|
+
}
|
|
6210
6765
|
if (message.type === "update_queue") {
|
|
6211
6766
|
const sdkMessage2 = {
|
|
6212
6767
|
type: "queue_update",
|
|
@@ -6636,7 +7191,7 @@ function createExternalToolCallHandler(externalTools) {
|
|
|
6636
7191
|
};
|
|
6637
7192
|
};
|
|
6638
7193
|
}
|
|
6639
|
-
async function resolveAppServerToolApproval(options, toolName, toolInput) {
|
|
7194
|
+
async function resolveAppServerToolApproval(options, toolName, toolInput, context) {
|
|
6640
7195
|
const hasCallback = typeof options.canUseTool === "function";
|
|
6641
7196
|
const toolNeedsRuntimeUserInput = requiresRuntimeUserInput(toolName);
|
|
6642
7197
|
if (toolNeedsRuntimeUserInput && !hasCallback) {
|
|
@@ -6651,7 +7206,7 @@ async function resolveAppServerToolApproval(options, toolName, toolInput) {
|
|
|
6651
7206
|
}
|
|
6652
7207
|
if (hasCallback) {
|
|
6653
7208
|
try {
|
|
6654
|
-
const result = await options.canUseTool(toolName, toolInput);
|
|
7209
|
+
const result = await options.canUseTool(toolName, toolInput, context);
|
|
6655
7210
|
if (result.behavior === "allow") {
|
|
6656
7211
|
return {
|
|
6657
7212
|
behavior: "allow",
|
|
@@ -6711,7 +7266,7 @@ function toAppServerApprovalDecision(decision) {
|
|
|
6711
7266
|
selected_permission_suggestion_ids: selectedPermissionSuggestionIds
|
|
6712
7267
|
};
|
|
6713
7268
|
}
|
|
6714
|
-
function
|
|
7269
|
+
function stringRecord2(value) {
|
|
6715
7270
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
6716
7271
|
return;
|
|
6717
7272
|
const record = {};
|
|
@@ -6757,7 +7312,7 @@ function normalizeListModelsResponse(response) {
|
|
|
6757
7312
|
} else if (Array.isArray(response.available_handles)) {
|
|
6758
7313
|
result.availableHandles = response.available_handles.filter((handle) => typeof handle === "string");
|
|
6759
7314
|
}
|
|
6760
|
-
const aliases =
|
|
7315
|
+
const aliases = stringRecord2(response.byok_provider_aliases);
|
|
6761
7316
|
if (aliases)
|
|
6762
7317
|
result.byokProviderAliases = aliases;
|
|
6763
7318
|
return result;
|
|
@@ -6812,7 +7367,7 @@ async function respondToAppServerControlRequest(config, message) {
|
|
|
6812
7367
|
return;
|
|
6813
7368
|
const toolName = typeof requestRecord.tool_name === "string" ? requestRecord.tool_name : "unknown";
|
|
6814
7369
|
const toolInput = requestRecord.input && typeof requestRecord.input === "object" && !Array.isArray(requestRecord.input) ? requestRecord.input : {};
|
|
6815
|
-
const decision = await resolveAppServerToolApproval(config.getOptions(), toolName, toolInput);
|
|
7370
|
+
const decision = await resolveAppServerToolApproval(config.getOptions(), toolName, toolInput, buildCanUseToolContext(requestRecord, requestId));
|
|
6816
7371
|
config.client.input({
|
|
6817
7372
|
runtime,
|
|
6818
7373
|
payload: {
|
|
@@ -6949,13 +7504,15 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
6949
7504
|
return options.memfs !== false;
|
|
6950
7505
|
}
|
|
6951
7506
|
async initializeRuntimeController() {
|
|
7507
|
+
await this.remoteOptions.beforeConnect?.();
|
|
6952
7508
|
const url = await this.resolveAppServerUrl();
|
|
6953
|
-
|
|
7509
|
+
await releaseAppServerManagementConnections(url);
|
|
7510
|
+
const client = applyUniqueRequestIds(createAppServerClient({
|
|
6954
7511
|
url,
|
|
6955
7512
|
...this.remoteOptions.authToken !== undefined ? { authToken: this.remoteOptions.authToken } : {},
|
|
6956
7513
|
...this.remoteOptions.WebSocket ? { WebSocket: this.remoteOptions.WebSocket } : {},
|
|
6957
7514
|
...this.remoteOptions.requestTimeoutMs !== undefined ? { requestTimeoutMs: this.remoteOptions.requestTimeoutMs } : {}
|
|
6958
|
-
});
|
|
7515
|
+
}));
|
|
6959
7516
|
this.removeControlRequestHandler = registerAppServerControlRequestHandler({
|
|
6960
7517
|
client,
|
|
6961
7518
|
getRuntime: () => this.runtime,
|
|
@@ -7155,6 +7712,19 @@ function asArray(body, action) {
|
|
|
7155
7712
|
}
|
|
7156
7713
|
return body;
|
|
7157
7714
|
}
|
|
7715
|
+
function cloudModelEntry(raw) {
|
|
7716
|
+
if (typeof raw.handle !== "string" || raw.handle.length === 0) {
|
|
7717
|
+
return null;
|
|
7718
|
+
}
|
|
7719
|
+
const label = typeof raw.display_name === "string" ? raw.display_name : typeof raw.name === "string" ? raw.name : raw.handle;
|
|
7720
|
+
return {
|
|
7721
|
+
...raw,
|
|
7722
|
+
id: typeof raw.id === "string" && raw.id.length > 0 ? raw.id : raw.handle,
|
|
7723
|
+
handle: raw.handle,
|
|
7724
|
+
label,
|
|
7725
|
+
description: typeof raw.description === "string" ? raw.description : ""
|
|
7726
|
+
};
|
|
7727
|
+
}
|
|
7158
7728
|
|
|
7159
7729
|
class CloudManagementTransport {
|
|
7160
7730
|
options;
|
|
@@ -7170,6 +7740,20 @@ class CloudManagementTransport {
|
|
|
7170
7740
|
updateAgent(agentId, body) {
|
|
7171
7741
|
return this.requestObject(`/v1/agents/${encodeURIComponent(agentId)}`, "PATCH", body, "Cloud update agent");
|
|
7172
7742
|
}
|
|
7743
|
+
async deleteAgent(agentId) {
|
|
7744
|
+
await this.requestUrl(this.url(`/v1/agents/${encodeURIComponent(agentId)}`), "DELETE", undefined, "Cloud delete agent");
|
|
7745
|
+
}
|
|
7746
|
+
async listModels() {
|
|
7747
|
+
const entries = await this.getArray("/v1/models", {}, "Cloud list models");
|
|
7748
|
+
const normalized = entries.flatMap((entry) => {
|
|
7749
|
+
const model = cloudModelEntry(entry);
|
|
7750
|
+
return model ? [model] : [];
|
|
7751
|
+
});
|
|
7752
|
+
return {
|
|
7753
|
+
entries: normalized,
|
|
7754
|
+
availableHandles: normalized.map((entry) => entry.handle)
|
|
7755
|
+
};
|
|
7756
|
+
}
|
|
7173
7757
|
listConversations(query) {
|
|
7174
7758
|
return this.getArray("/v1/conversations/", query, "Cloud list conversations");
|
|
7175
7759
|
}
|
|
@@ -7799,14 +8383,14 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
7799
8383
|
apiKey,
|
|
7800
8384
|
authMode: this.cloudOptions.webSocketAuth ?? "header"
|
|
7801
8385
|
});
|
|
7802
|
-
const client = createAppServerClient({
|
|
8386
|
+
const client = applyUniqueRequestIds(createAppServerClient({
|
|
7803
8387
|
url,
|
|
7804
8388
|
WebSocket: createCloudStatusWebSocketConstructor({
|
|
7805
8389
|
cloudOptions: this.cloudOptions,
|
|
7806
8390
|
runtime: resolved.runtime
|
|
7807
8391
|
}),
|
|
7808
8392
|
requestTimeoutMs: this.cloudOptions.requestTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS
|
|
7809
|
-
});
|
|
8393
|
+
}));
|
|
7810
8394
|
this.removeControlRequestHandler = registerAppServerControlRequestHandler({
|
|
7811
8395
|
client,
|
|
7812
8396
|
getRuntime: () => this.runtime,
|
|
@@ -8254,6 +8838,11 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
8254
8838
|
function definedEntries(values) {
|
|
8255
8839
|
return Object.fromEntries(Object.entries(values).filter(([, value]) => value !== undefined));
|
|
8256
8840
|
}
|
|
8841
|
+
function assertNonEmptyId(value, name) {
|
|
8842
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
8843
|
+
throw new Error(`Invalid ${name}. Expected a non-empty string.`);
|
|
8844
|
+
}
|
|
8845
|
+
}
|
|
8257
8846
|
function agentListQuery(options) {
|
|
8258
8847
|
const orderBy = options.orderBy?.replace(/[A-Z]/g, (character) => `_${character.toLowerCase()}`);
|
|
8259
8848
|
return {
|
|
@@ -8326,7 +8915,16 @@ function createAgentsClient(transport) {
|
|
|
8326
8915
|
return {
|
|
8327
8916
|
list: (options = {}) => transport().listAgents(agentListQuery(options)),
|
|
8328
8917
|
retrieve: (agentId) => transport().retrieveAgent(agentId),
|
|
8329
|
-
update: (agentId, options) => transport().updateAgent(agentId, agentUpdateBody(options))
|
|
8918
|
+
update: (agentId, options) => transport().updateAgent(agentId, agentUpdateBody(options)),
|
|
8919
|
+
delete: async (agentId) => {
|
|
8920
|
+
assertNonEmptyId(agentId, "agent id");
|
|
8921
|
+
await transport().deleteAgent(agentId);
|
|
8922
|
+
}
|
|
8923
|
+
};
|
|
8924
|
+
}
|
|
8925
|
+
function createModelsClient(transport) {
|
|
8926
|
+
return {
|
|
8927
|
+
list: () => transport().listModels()
|
|
8330
8928
|
};
|
|
8331
8929
|
}
|
|
8332
8930
|
function createConversationsClient(transport) {
|
|
@@ -8497,6 +9095,7 @@ class LettaAgentClientBase {
|
|
|
8497
9095
|
environment;
|
|
8498
9096
|
agents;
|
|
8499
9097
|
conversations;
|
|
9098
|
+
models;
|
|
8500
9099
|
options;
|
|
8501
9100
|
repositoriesClient = null;
|
|
8502
9101
|
managementTransport = null;
|
|
@@ -8510,6 +9109,7 @@ class LettaAgentClientBase {
|
|
|
8510
9109
|
this.options = options;
|
|
8511
9110
|
this.agents = createAgentsClient(() => this.getManagementTransport());
|
|
8512
9111
|
this.conversations = createConversationsClient(() => this.getManagementTransport());
|
|
9112
|
+
this.models = createModelsClient(() => this.getManagementTransport());
|
|
8513
9113
|
if (this.backend === "local" && this.environment !== undefined) {
|
|
8514
9114
|
throw new Error('LettaAgentClient environment is only valid with backend: "cloud".');
|
|
8515
9115
|
}
|
|
@@ -8528,6 +9128,10 @@ class LettaAgentClientBase {
|
|
|
8528
9128
|
if (requestTimeoutMs !== undefined && (!Number.isInteger(requestTimeoutMs) || requestTimeoutMs <= 0)) {
|
|
8529
9129
|
throw new Error("Invalid appServer.requestTimeoutMs. Expected a positive integer.");
|
|
8530
9130
|
}
|
|
9131
|
+
const idleLingerMs = localOptions.appServer?.idleLingerMs;
|
|
9132
|
+
if (idleLingerMs !== undefined && (!Number.isInteger(idleLingerMs) || idleLingerMs < 0)) {
|
|
9133
|
+
throw new Error("Invalid appServer.idleLingerMs. Expected a non-negative integer.");
|
|
9134
|
+
}
|
|
8531
9135
|
const startupTimeoutMs = localOptions.appServer?.startupTimeoutMs;
|
|
8532
9136
|
if (startupTimeoutMs !== undefined && (!Number.isInteger(startupTimeoutMs) || startupTimeoutMs <= 0)) {
|
|
8533
9137
|
throw new Error("Invalid appServer.startupTimeoutMs. Expected a positive integer.");
|
|
@@ -8540,6 +9144,9 @@ class LettaAgentClientBase {
|
|
|
8540
9144
|
if (options.requestTimeoutMs !== undefined && (!Number.isInteger(options.requestTimeoutMs) || options.requestTimeoutMs <= 0)) {
|
|
8541
9145
|
throw new Error("Invalid requestTimeoutMs. Expected a positive integer.");
|
|
8542
9146
|
}
|
|
9147
|
+
if (options.idleLingerMs !== undefined && (!Number.isInteger(options.idleLingerMs) || options.idleLingerMs < 0)) {
|
|
9148
|
+
throw new Error("Invalid idleLingerMs. Expected a non-negative integer.");
|
|
9149
|
+
}
|
|
8543
9150
|
}
|
|
8544
9151
|
if (this.backend === "cloud") {
|
|
8545
9152
|
validateCloudClientOptions(options);
|
|
@@ -8554,7 +9161,7 @@ class LettaAgentClientBase {
|
|
|
8554
9161
|
}
|
|
8555
9162
|
validateCreateAgentOptions(options);
|
|
8556
9163
|
if (this.backend === "remote") {
|
|
8557
|
-
const session = new AppServerSession(this.
|
|
9164
|
+
const session = new AppServerSession(this.appServerSessionOptions(), {
|
|
8558
9165
|
kind: "create-agent",
|
|
8559
9166
|
options
|
|
8560
9167
|
});
|
|
@@ -8577,7 +9184,7 @@ class LettaAgentClientBase {
|
|
|
8577
9184
|
if (!agentId) {
|
|
8578
9185
|
throw new Error("App-server createSession() requires an agent id. Call createAgent() first or pass an agent id.");
|
|
8579
9186
|
}
|
|
8580
|
-
return new AppServerSession(this.
|
|
9187
|
+
return new AppServerSession(this.appServerSessionOptions(), {
|
|
8581
9188
|
kind: "session",
|
|
8582
9189
|
agentId,
|
|
8583
9190
|
newConversation: true,
|
|
@@ -8603,13 +9210,13 @@ class LettaAgentClientBase {
|
|
|
8603
9210
|
validateCreateSessionOptions(sessionOptions);
|
|
8604
9211
|
if (this.backend === "remote") {
|
|
8605
9212
|
if (looksLikeConversationId(id)) {
|
|
8606
|
-
return new AppServerSession(this.
|
|
9213
|
+
return new AppServerSession(this.appServerSessionOptions(), {
|
|
8607
9214
|
kind: "session",
|
|
8608
9215
|
conversationId: id,
|
|
8609
9216
|
options
|
|
8610
9217
|
});
|
|
8611
9218
|
}
|
|
8612
|
-
return new AppServerSession(this.
|
|
9219
|
+
return new AppServerSession(this.appServerSessionOptions(), {
|
|
8613
9220
|
kind: "session",
|
|
8614
9221
|
agentId: id,
|
|
8615
9222
|
defaultConversation: true,
|
|
@@ -8708,6 +9315,12 @@ class LettaAgentClientBase {
|
|
|
8708
9315
|
}
|
|
8709
9316
|
return this.options;
|
|
8710
9317
|
}
|
|
9318
|
+
appServerSessionOptions() {
|
|
9319
|
+
return {
|
|
9320
|
+
...this.remoteOptions(),
|
|
9321
|
+
beforeConnect: () => this.releaseIdleManagementConnection()
|
|
9322
|
+
};
|
|
9323
|
+
}
|
|
8711
9324
|
getRepositoriesClient() {
|
|
8712
9325
|
if (this.backend !== "cloud") {
|
|
8713
9326
|
throw new Error('client.repositories is only available with backend: "cloud".');
|
|
@@ -8715,6 +9328,9 @@ class LettaAgentClientBase {
|
|
|
8715
9328
|
this.repositoriesClient ??= new RepositoriesClient(this.cloudOptions());
|
|
8716
9329
|
return this.repositoriesClient;
|
|
8717
9330
|
}
|
|
9331
|
+
async releaseIdleManagementConnection() {
|
|
9332
|
+
await this.managementTransport?.releaseIdleConnection?.();
|
|
9333
|
+
}
|
|
8718
9334
|
getManagementTransport() {
|
|
8719
9335
|
if (this.managementTransport)
|
|
8720
9336
|
return this.managementTransport;
|
|
@@ -8864,7 +9480,7 @@ ${output.trim()}` : ""}`));
|
|
|
8864
9480
|
}
|
|
8865
9481
|
|
|
8866
9482
|
// src/local-app-server-session.ts
|
|
8867
|
-
function createLocalAppServerSession(options, mode) {
|
|
9483
|
+
function createLocalAppServerSession(options, mode, beforeConnect) {
|
|
8868
9484
|
const appServer = options ?? {};
|
|
8869
9485
|
const sessionOptions = {
|
|
8870
9486
|
...appServer.url !== undefined ? { url: appServer.url } : {
|
|
@@ -8876,7 +9492,9 @@ function createLocalAppServerSession(options, mode) {
|
|
|
8876
9492
|
})
|
|
8877
9493
|
},
|
|
8878
9494
|
...appServer.WebSocket !== undefined ? { WebSocket: appServer.WebSocket } : {},
|
|
8879
|
-
...appServer.requestTimeoutMs !== undefined ? { requestTimeoutMs: appServer.requestTimeoutMs } : {}
|
|
9495
|
+
...appServer.requestTimeoutMs !== undefined ? { requestTimeoutMs: appServer.requestTimeoutMs } : {},
|
|
9496
|
+
...appServer.pinGlobalAgent !== undefined ? { pinGlobalAgent: appServer.pinGlobalAgent } : {},
|
|
9497
|
+
...beforeConnect ? { beforeConnect } : {}
|
|
8880
9498
|
};
|
|
8881
9499
|
return new AppServerSession(sessionOptions, mode);
|
|
8882
9500
|
}
|
|
@@ -9276,6 +9894,8 @@ class Session {
|
|
|
9276
9894
|
_sessionId = null;
|
|
9277
9895
|
_conversationId = null;
|
|
9278
9896
|
initialized = false;
|
|
9897
|
+
closed = false;
|
|
9898
|
+
initializePromise = null;
|
|
9279
9899
|
externalTools = new Map;
|
|
9280
9900
|
streamQueue = [];
|
|
9281
9901
|
streamResolvers = [];
|
|
@@ -9298,9 +9918,28 @@ class Session {
|
|
|
9298
9918
|
}
|
|
9299
9919
|
}
|
|
9300
9920
|
async initialize() {
|
|
9921
|
+
if (this.closed) {
|
|
9922
|
+
throw new Error("Session is closed");
|
|
9923
|
+
}
|
|
9924
|
+
if (this.initializePromise) {
|
|
9925
|
+
return this.initializePromise;
|
|
9926
|
+
}
|
|
9301
9927
|
if (this.initialized) {
|
|
9302
9928
|
throw new Error("Session already initialized");
|
|
9303
9929
|
}
|
|
9930
|
+
const attempt = this.performInitialize();
|
|
9931
|
+
const memo = attempt.catch((error) => {
|
|
9932
|
+
this.cleanupFailedInitialize();
|
|
9933
|
+
throw error;
|
|
9934
|
+
}).finally(() => {
|
|
9935
|
+
if (this.initializePromise === memo) {
|
|
9936
|
+
this.initializePromise = null;
|
|
9937
|
+
}
|
|
9938
|
+
});
|
|
9939
|
+
this.initializePromise = memo;
|
|
9940
|
+
return memo;
|
|
9941
|
+
}
|
|
9942
|
+
async performInitialize() {
|
|
9304
9943
|
sessionLog("init", "connecting transport...");
|
|
9305
9944
|
await this.transport.connect();
|
|
9306
9945
|
sessionLog("init", "transport connected, sending initialize request");
|
|
@@ -9325,11 +9964,14 @@ class Session {
|
|
|
9325
9964
|
this._agentId = initMsg.agent_id;
|
|
9326
9965
|
this._sessionId = initMsg.session_id;
|
|
9327
9966
|
this._conversationId = initMsg.conversation_id;
|
|
9328
|
-
this.initialized = true;
|
|
9329
|
-
this.startBackgroundPump();
|
|
9330
9967
|
if (this.externalTools.size > 0) {
|
|
9331
9968
|
await this.registerExternalTools();
|
|
9332
9969
|
}
|
|
9970
|
+
if (this.closed) {
|
|
9971
|
+
throw new Error("Session is closed");
|
|
9972
|
+
}
|
|
9973
|
+
this.initialized = true;
|
|
9974
|
+
this.startBackgroundPump();
|
|
9333
9975
|
const allTools = [
|
|
9334
9976
|
...initMsg.tools,
|
|
9335
9977
|
...Array.from(this.externalTools.keys())
|
|
@@ -9360,6 +10002,16 @@ ${stderr}` : "";
|
|
|
9360
10002
|
sessionLog("init", `ERROR: transport closed before init message received${detail}`);
|
|
9361
10003
|
throw new Error(`Failed to initialize session - no init message received${detail}`);
|
|
9362
10004
|
}
|
|
10005
|
+
cleanupFailedInitialize() {
|
|
10006
|
+
this.transport.close();
|
|
10007
|
+
this.closed = true;
|
|
10008
|
+
this.initialized = false;
|
|
10009
|
+
this._agentId = null;
|
|
10010
|
+
this._sessionId = null;
|
|
10011
|
+
this._conversationId = null;
|
|
10012
|
+
this.pumpClosed = true;
|
|
10013
|
+
this.resolveAllStreamWaiters(null);
|
|
10014
|
+
}
|
|
9363
10015
|
async send(message) {
|
|
9364
10016
|
if (!this.initialized) {
|
|
9365
10017
|
sessionLog("send", "auto-initializing (not yet initialized)");
|
|
@@ -9861,7 +10513,7 @@ ${stderr}` : "";
|
|
|
9861
10513
|
};
|
|
9862
10514
|
} else if (hasCallback) {
|
|
9863
10515
|
try {
|
|
9864
|
-
const result = await this.options.canUseTool(toolName, req.input);
|
|
10516
|
+
const result = await this.options.canUseTool(toolName, req.input, buildCanUseToolContext(req, requestId));
|
|
9865
10517
|
if (result.behavior === "allow") {
|
|
9866
10518
|
response = {
|
|
9867
10519
|
behavior: "allow",
|
|
@@ -9993,6 +10645,12 @@ ${stderr}` : "";
|
|
|
9993
10645
|
async removeQueuedMessage(_itemId) {
|
|
9994
10646
|
throw new Error("removeQueuedMessage() is not supported by the legacy stdio transport. Use a Cloud, Remote, or local app-server session.");
|
|
9995
10647
|
}
|
|
10648
|
+
async getDeviceStatus(_options) {
|
|
10649
|
+
throw new Error("getDeviceStatus() is not supported by the legacy stdio transport. Use a Cloud, Remote, or local app-server session.");
|
|
10650
|
+
}
|
|
10651
|
+
onDeviceStatus(_listener) {
|
|
10652
|
+
throw new Error("onDeviceStatus() is not supported by the legacy stdio transport. Use a Cloud, Remote, or local app-server session.");
|
|
10653
|
+
}
|
|
9996
10654
|
async bootstrapState(options = {}) {
|
|
9997
10655
|
if (!this.initialized) {
|
|
9998
10656
|
throw new Error("Session must be initialized before calling bootstrapState()");
|
|
@@ -10030,6 +10688,9 @@ ${stderr}` : "";
|
|
|
10030
10688
|
throw new Error("updateToolset() is not supported by this session. Use a Cloud, Remote, or local agent session.");
|
|
10031
10689
|
}
|
|
10032
10690
|
close() {
|
|
10691
|
+
if (this.closed)
|
|
10692
|
+
return;
|
|
10693
|
+
this.closed = true;
|
|
10033
10694
|
sessionLog("close", `closing session (agent=${this._agentId}, conversation=${this._conversationId})`);
|
|
10034
10695
|
this.transport.close();
|
|
10035
10696
|
this.pumpClosed = true;
|
|
@@ -10227,7 +10888,8 @@ class LettaAgentClient extends LettaAgentClientBase {
|
|
|
10227
10888
|
})
|
|
10228
10889
|
},
|
|
10229
10890
|
...localOptions?.WebSocket !== undefined ? { WebSocket: localOptions.WebSocket } : {},
|
|
10230
|
-
...localOptions?.requestTimeoutMs !== undefined ? { requestTimeoutMs: localOptions.requestTimeoutMs } : {}
|
|
10891
|
+
...localOptions?.requestTimeoutMs !== undefined ? { requestTimeoutMs: localOptions.requestTimeoutMs } : {},
|
|
10892
|
+
...localOptions?.idleLingerMs !== undefined ? { idleLingerMs: localOptions.idleLingerMs } : {}
|
|
10231
10893
|
});
|
|
10232
10894
|
}
|
|
10233
10895
|
async createLocalAgent(options) {
|
|
@@ -10236,7 +10898,7 @@ class LettaAgentClient extends LettaAgentClientBase {
|
|
|
10236
10898
|
const session2 = createLocalAppServerSession(localOptions.appServer, {
|
|
10237
10899
|
kind: "create-agent",
|
|
10238
10900
|
options
|
|
10239
|
-
});
|
|
10901
|
+
}, () => this.releaseIdleManagementConnection());
|
|
10240
10902
|
const initMsg2 = await session2.initialize();
|
|
10241
10903
|
session2.close();
|
|
10242
10904
|
return initMsg2.agentId;
|
|
@@ -10254,7 +10916,7 @@ class LettaAgentClient extends LettaAgentClientBase {
|
|
|
10254
10916
|
agentId,
|
|
10255
10917
|
newConversation: true,
|
|
10256
10918
|
options
|
|
10257
|
-
});
|
|
10919
|
+
}, () => this.releaseIdleManagementConnection());
|
|
10258
10920
|
}
|
|
10259
10921
|
if (agentId) {
|
|
10260
10922
|
return new Session({ ...sessionOptions, agentId, newConversation: true });
|
|
@@ -10269,14 +10931,14 @@ class LettaAgentClient extends LettaAgentClientBase {
|
|
|
10269
10931
|
kind: "session",
|
|
10270
10932
|
conversationId: id,
|
|
10271
10933
|
options
|
|
10272
|
-
});
|
|
10934
|
+
}, () => this.releaseIdleManagementConnection());
|
|
10273
10935
|
}
|
|
10274
10936
|
return createLocalAppServerSession(localOptions.appServer, {
|
|
10275
10937
|
kind: "session",
|
|
10276
10938
|
agentId: id,
|
|
10277
10939
|
defaultConversation: true,
|
|
10278
10940
|
options
|
|
10279
|
-
});
|
|
10941
|
+
}, () => this.releaseIdleManagementConnection());
|
|
10280
10942
|
}
|
|
10281
10943
|
if (looksLikeConversationId2(id)) {
|
|
10282
10944
|
return new Session({ ...sessionOptions, conversationId: id });
|
|
@@ -10544,4 +11206,4 @@ export {
|
|
|
10544
11206
|
CloudManagedSandboxExpiredError
|
|
10545
11207
|
};
|
|
10546
11208
|
|
|
10547
|
-
//# debugId=
|
|
11209
|
+
//# debugId=98DBA625B202A81364756E2164756E21
|