@letta-ai/letta-agent-sdk 0.8.0 → 0.8.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 +23 -0
- package/dist/agent-creation.d.ts +2 -1
- package/dist/agent-creation.d.ts.map +1 -1
- package/dist/app-server-session.d.ts.map +1 -1
- package/dist/client-base.d.ts +12 -0
- package/dist/client-base.d.ts.map +1 -1
- package/dist/client-entry.d.ts +1 -0
- package/dist/client-entry.d.ts.map +1 -1
- package/dist/client-entry.js +509 -2527
- package/dist/client-entry.js.map +14 -12
- package/dist/client.d.ts +4 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/cloud-session.d.ts +4 -1
- package/dist/cloud-session.d.ts.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +795 -2645
- package/dist/index.js.map +18 -15
- package/dist/query-types.d.ts +25 -0
- package/dist/query-types.d.ts.map +1 -0
- package/dist/query.d.ts +7 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/remote-session-protocol.d.ts +11 -1
- package/dist/remote-session-protocol.d.ts.map +1 -1
- package/dist/remote-turn-coordinator.d.ts.map +1 -1
- package/dist/repository-types.d.ts +89 -0
- package/dist/repository-types.d.ts.map +1 -0
- package/dist/skill-loading.d.ts +84 -0
- package/dist/skill-loading.d.ts.map +1 -0
- package/dist/skill-node.d.ts +28 -0
- package/dist/skill-node.d.ts.map +1 -0
- package/dist/types.d.ts +17 -84
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/agent-creation.ts +37 -1
- package/src/app-server-session.ts +35 -2
- package/src/client-base.ts +138 -1
- package/src/client-entry.ts +1 -0
- package/src/client.ts +39 -0
- package/src/cloud-session.ts +84 -16
- package/src/index.ts +16 -0
- package/src/local-app-server.ts +1 -1
- package/src/query-types.ts +40 -0
- package/src/query.ts +43 -0
- package/src/remote-client-session-core.ts +1 -1
- package/src/remote-session-protocol.ts +12 -1
- package/src/remote-turn-coordinator.ts +5 -1
- package/src/repository-types.ts +105 -0
- package/src/skill-loading.ts +194 -0
- package/src/skill-node.ts +139 -0
- package/src/types.ts +42 -100
|
@@ -657,8 +657,16 @@ export class AppServerSession extends RemoteClientSessionCore {
|
|
|
657
657
|
clientToolset,
|
|
658
658
|
),
|
|
659
659
|
runtime: response.runtime,
|
|
660
|
-
model:
|
|
661
|
-
|
|
660
|
+
model:
|
|
661
|
+
typeof response.agent?.model === "string"
|
|
662
|
+
? response.agent.model
|
|
663
|
+
: typeof response.conversation?.model === "string"
|
|
664
|
+
? response.conversation.model
|
|
665
|
+
: "",
|
|
666
|
+
modelSettings:
|
|
667
|
+
objectRecord(response.agent?.model_settings) ??
|
|
668
|
+
objectRecord(response.conversation?.model_settings) ??
|
|
669
|
+
null,
|
|
662
670
|
...(availableTools !== undefined ? { tools: availableTools } : {}),
|
|
663
671
|
...(skillSources !== undefined ? { skillSources: [...skillSources] } : {}),
|
|
664
672
|
};
|
|
@@ -763,6 +771,31 @@ export class AppServerSession extends RemoteClientSessionCore {
|
|
|
763
771
|
return command as RuntimeStartCommand;
|
|
764
772
|
}
|
|
765
773
|
|
|
774
|
+
if (this.mode.kind === "agent-free") {
|
|
775
|
+
if (this.mode.createConversation) {
|
|
776
|
+
const create = this.mode.createConversation;
|
|
777
|
+
command.create_conversation = {
|
|
778
|
+
body: {
|
|
779
|
+
model: create.model,
|
|
780
|
+
system: create.system,
|
|
781
|
+
...(create.modelSettings !== undefined
|
|
782
|
+
? { model_settings: create.modelSettings }
|
|
783
|
+
: {}),
|
|
784
|
+
...(create.contextWindowLimit !== undefined
|
|
785
|
+
? { context_window_limit: create.contextWindowLimit }
|
|
786
|
+
: {}),
|
|
787
|
+
},
|
|
788
|
+
};
|
|
789
|
+
} else if (this.mode.conversationId) {
|
|
790
|
+
command.conversation_id = this.mode.conversationId;
|
|
791
|
+
} else {
|
|
792
|
+
throw new Error(
|
|
793
|
+
"Agent-free sessions require a conversation to create or resume.",
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
return command as RuntimeStartCommand;
|
|
797
|
+
}
|
|
798
|
+
|
|
766
799
|
if (this.mode.agentId) {
|
|
767
800
|
command.agent_id = this.mode.agentId;
|
|
768
801
|
if (this.mode.newConversation) {
|
package/src/client-base.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type Letta from "@letta-ai/letta-client";
|
|
2
2
|
import { RepositoriesClient } from "./repositories.js";
|
|
3
|
+
import { resolveSkillItems, type SkillNodeSupport } from "./skill-loading.js";
|
|
3
4
|
import { createAgentRepositoriesClient } from "./agent-repositories.js";
|
|
4
5
|
import { AppServerManagementTransport } from "./app-server-management.js";
|
|
5
6
|
import {
|
|
@@ -31,6 +32,12 @@ import type {
|
|
|
31
32
|
ConversationsClient,
|
|
32
33
|
ModelsClient,
|
|
33
34
|
} from "./management-types.js";
|
|
35
|
+
import { createQuery } from "./query.js";
|
|
36
|
+
import type {
|
|
37
|
+
AgentFreeQueryOptions,
|
|
38
|
+
Query,
|
|
39
|
+
QueryParams,
|
|
40
|
+
} from "./query-types.js";
|
|
34
41
|
import type {
|
|
35
42
|
CreateAgentOptions,
|
|
36
43
|
CreateSessionOptions,
|
|
@@ -93,6 +100,28 @@ function looksLikeConversationId(id: string): boolean {
|
|
|
93
100
|
return id.startsWith("conv-") || id.startsWith("local-conv-");
|
|
94
101
|
}
|
|
95
102
|
|
|
103
|
+
function agentFreeSessionOptions(
|
|
104
|
+
options: AgentFreeQueryOptions,
|
|
105
|
+
): LettaCodeClientSessionOptions {
|
|
106
|
+
const {
|
|
107
|
+
system: _system,
|
|
108
|
+
modelSettings: _modelSettings,
|
|
109
|
+
contextWindowLimit: _contextWindowLimit,
|
|
110
|
+
...sessionOptions
|
|
111
|
+
} = options;
|
|
112
|
+
return sessionOptions;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function validateAgentFreeQueryOptions(options: AgentFreeQueryOptions): void {
|
|
116
|
+
if (typeof options.model !== "string" || options.model.length === 0) {
|
|
117
|
+
throw new Error("query() requires a non-empty model.");
|
|
118
|
+
}
|
|
119
|
+
if (typeof options.system !== "string") {
|
|
120
|
+
throw new Error("query() requires a system prompt.");
|
|
121
|
+
}
|
|
122
|
+
validateCreateSessionOptions(agentFreeSessionOptions(options));
|
|
123
|
+
}
|
|
124
|
+
|
|
96
125
|
type OneShotSession = LettaCodeSession & {
|
|
97
126
|
sendAndWaitForResult(message: SendMessage): Promise<SDKResultMessage>;
|
|
98
127
|
};
|
|
@@ -227,6 +256,18 @@ export class LettaAgentClientBase {
|
|
|
227
256
|
|
|
228
257
|
validateCreateAgentOptions(options);
|
|
229
258
|
|
|
259
|
+
// Resolve skill directory paths to inline skills up front so every
|
|
260
|
+
// backend receives the same shape. Directory loading is Node-only; the
|
|
261
|
+
// portable client resolves inline skills and rejects paths.
|
|
262
|
+
if (options.skills !== undefined && options.skills.length > 0) {
|
|
263
|
+
const nodeSupport = this.skillNodeSupport();
|
|
264
|
+
const skills = await resolveSkillItems(
|
|
265
|
+
options.skills,
|
|
266
|
+
nodeSupport?.loadSkillDirectory,
|
|
267
|
+
);
|
|
268
|
+
options = { ...options, skills };
|
|
269
|
+
}
|
|
270
|
+
|
|
230
271
|
if (this.backend === "remote") {
|
|
231
272
|
const session = new AppServerSession(this.appServerSessionOptions(), {
|
|
232
273
|
kind: "create-agent",
|
|
@@ -234,10 +275,17 @@ export class LettaAgentClientBase {
|
|
|
234
275
|
});
|
|
235
276
|
const initMsg = await session.initialize();
|
|
236
277
|
session.close();
|
|
278
|
+
if (!initMsg.agentId) {
|
|
279
|
+
throw new Error("App Server agent creation did not return an agent id.");
|
|
280
|
+
}
|
|
237
281
|
return initMsg.agentId;
|
|
238
282
|
}
|
|
239
283
|
if (this.backend === "cloud") {
|
|
240
|
-
return createCloudAgent(
|
|
284
|
+
return createCloudAgent(
|
|
285
|
+
this.getCloudClient(),
|
|
286
|
+
options,
|
|
287
|
+
this.skillNodeSupport()?.pushSkillSupportFiles,
|
|
288
|
+
);
|
|
241
289
|
}
|
|
242
290
|
return this.createLocalAgent(options);
|
|
243
291
|
}
|
|
@@ -354,6 +402,79 @@ export class LettaAgentClientBase {
|
|
|
354
402
|
}
|
|
355
403
|
}
|
|
356
404
|
|
|
405
|
+
/** Run one prompt in a new agent-free ephemeral conversation. */
|
|
406
|
+
query(params: QueryParams): Query {
|
|
407
|
+
validateAgentFreeQueryOptions(params.options);
|
|
408
|
+
return createQuery((options) => this.createAgentFreeSession(options), params);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
private async createAgentFreeSession(
|
|
412
|
+
options: AgentFreeQueryOptions,
|
|
413
|
+
): Promise<LettaCodeSession> {
|
|
414
|
+
validateAgentFreeQueryOptions(options);
|
|
415
|
+
const sessionOptions = agentFreeSessionOptions(options);
|
|
416
|
+
this.assertSessionBackend("query", sessionOptions);
|
|
417
|
+
|
|
418
|
+
if (this.backend === "remote") {
|
|
419
|
+
return new AppServerSession(this.appServerSessionOptions(), {
|
|
420
|
+
kind: "agent-free",
|
|
421
|
+
createConversation: {
|
|
422
|
+
model: options.model,
|
|
423
|
+
system: options.system,
|
|
424
|
+
...(options.modelSettings !== undefined
|
|
425
|
+
? { modelSettings: options.modelSettings }
|
|
426
|
+
: {}),
|
|
427
|
+
...(options.contextWindowLimit !== undefined
|
|
428
|
+
? { contextWindowLimit: options.contextWindowLimit }
|
|
429
|
+
: {}),
|
|
430
|
+
},
|
|
431
|
+
options: sessionOptions,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
if (this.backend === "cloud") {
|
|
435
|
+
const computer = options.computer ?? options.environment ?? this.computer;
|
|
436
|
+
if (computer === undefined) {
|
|
437
|
+
throw new Error(
|
|
438
|
+
"Cloud query() requires an explicit computer; managed sandboxes are agent-scoped.",
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
const conversation = await this.getCloudClient().post<unknown>(
|
|
442
|
+
"/v1/conversations/ephemeral",
|
|
443
|
+
{
|
|
444
|
+
body: {
|
|
445
|
+
model: options.model,
|
|
446
|
+
system: options.system,
|
|
447
|
+
...(options.modelSettings !== undefined
|
|
448
|
+
? { model_settings: options.modelSettings }
|
|
449
|
+
: {}),
|
|
450
|
+
...(options.contextWindowLimit !== undefined
|
|
451
|
+
? { context_window_limit: options.contextWindowLimit }
|
|
452
|
+
: {}),
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
);
|
|
456
|
+
if (
|
|
457
|
+
!conversation ||
|
|
458
|
+
typeof conversation !== "object" ||
|
|
459
|
+
typeof (conversation as { id?: unknown }).id !== "string"
|
|
460
|
+
) {
|
|
461
|
+
throw new Error(
|
|
462
|
+
"Cloud ephemeral conversation response did not include a conversation id.",
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
return new CloudEnvironmentSession(
|
|
466
|
+
this.cloudOptions(),
|
|
467
|
+
{
|
|
468
|
+
kind: "agent-free",
|
|
469
|
+
conversationId: (conversation as { id: string }).id,
|
|
470
|
+
options: sessionOptions,
|
|
471
|
+
},
|
|
472
|
+
this.getCloudClient(),
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
return this.createLocalAgentFreeSession(options, sessionOptions);
|
|
476
|
+
}
|
|
477
|
+
|
|
357
478
|
private assertSessionBackend(
|
|
358
479
|
action: string,
|
|
359
480
|
options: LettaCodeClientSessionOptions,
|
|
@@ -451,6 +572,15 @@ export class LettaAgentClientBase {
|
|
|
451
572
|
throw this.localBackendUnavailableError();
|
|
452
573
|
}
|
|
453
574
|
|
|
575
|
+
/**
|
|
576
|
+
* Node-only skill seeding support (directory loading, memory-repo git
|
|
577
|
+
* push). The Node client overrides this; the portable client returns
|
|
578
|
+
* undefined, which limits skills to inline SKILL.md-only objects.
|
|
579
|
+
*/
|
|
580
|
+
protected skillNodeSupport(): SkillNodeSupport | undefined {
|
|
581
|
+
return undefined;
|
|
582
|
+
}
|
|
583
|
+
|
|
454
584
|
protected createLocalSession(
|
|
455
585
|
_agentId: string,
|
|
456
586
|
_options: LettaCodeClientSessionOptions,
|
|
@@ -458,6 +588,13 @@ export class LettaAgentClientBase {
|
|
|
458
588
|
throw this.localBackendUnavailableError();
|
|
459
589
|
}
|
|
460
590
|
|
|
591
|
+
protected createLocalAgentFreeSession(
|
|
592
|
+
_queryOptions: AgentFreeQueryOptions,
|
|
593
|
+
_sessionOptions: LettaCodeClientSessionOptions,
|
|
594
|
+
): LettaCodeSession {
|
|
595
|
+
throw this.localBackendUnavailableError();
|
|
596
|
+
}
|
|
597
|
+
|
|
461
598
|
protected resumeLocalSession(
|
|
462
599
|
_id: string,
|
|
463
600
|
_options: LettaCodeClientSessionOptions,
|
package/src/client-entry.ts
CHANGED
package/src/client.ts
CHANGED
|
@@ -3,6 +3,9 @@ import { AppServerManagementTransport } from "./app-server-management.js";
|
|
|
3
3
|
import type { ManagementTransport } from "./management.js";
|
|
4
4
|
import { createLocalAppServerSession } from "./local-app-server-session.js";
|
|
5
5
|
import { startLocalAppServer } from "./local-app-server.js";
|
|
6
|
+
import type { AgentFreeQueryOptions } from "./query-types.js";
|
|
7
|
+
import type { SkillNodeSupport } from "./skill-loading.js";
|
|
8
|
+
import { loadSkillDirectory, pushSkillSupportFiles } from "./skill-node.js";
|
|
6
9
|
import type {
|
|
7
10
|
CreateAgentOptions,
|
|
8
11
|
LettaCodeClientSessionOptions,
|
|
@@ -11,6 +14,10 @@ import type {
|
|
|
11
14
|
} from "./types.js";
|
|
12
15
|
|
|
13
16
|
export class LettaAgentClient extends LettaAgentClientBase {
|
|
17
|
+
protected override skillNodeSupport(): SkillNodeSupport {
|
|
18
|
+
return { loadSkillDirectory, pushSkillSupportFiles };
|
|
19
|
+
}
|
|
20
|
+
|
|
14
21
|
protected override createLocalManagementTransport(): ManagementTransport {
|
|
15
22
|
const localOptions = (
|
|
16
23
|
this.options as LettaCodeLocalClientOptions
|
|
@@ -48,6 +55,9 @@ export class LettaAgentClient extends LettaAgentClientBase {
|
|
|
48
55
|
);
|
|
49
56
|
const initMsg = await session.initialize();
|
|
50
57
|
session.close();
|
|
58
|
+
if (!initMsg.agentId) {
|
|
59
|
+
throw new Error("Local App Server agent creation did not return an agent id.");
|
|
60
|
+
}
|
|
51
61
|
return initMsg.agentId;
|
|
52
62
|
}
|
|
53
63
|
|
|
@@ -67,6 +77,35 @@ export class LettaAgentClient extends LettaAgentClientBase {
|
|
|
67
77
|
);
|
|
68
78
|
}
|
|
69
79
|
|
|
80
|
+
protected override createLocalAgentFreeSession(
|
|
81
|
+
queryOptions: AgentFreeQueryOptions,
|
|
82
|
+
sessionOptions: LettaCodeClientSessionOptions,
|
|
83
|
+
): LettaCodeSession {
|
|
84
|
+
const localOptions = this.options as LettaCodeLocalClientOptions;
|
|
85
|
+
if (
|
|
86
|
+
localOptions.appServer?.url === undefined &&
|
|
87
|
+
(localOptions.appServer?.harnessBackend ?? "local") === "local"
|
|
88
|
+
) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
'query() requires the API-backed App Server. Set appServer.harnessBackend to "api" or connect to an API-backed remote App Server.',
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return createLocalAppServerSession(localOptions.appServer, {
|
|
94
|
+
kind: "agent-free",
|
|
95
|
+
createConversation: {
|
|
96
|
+
model: queryOptions.model,
|
|
97
|
+
system: queryOptions.system,
|
|
98
|
+
...(queryOptions.modelSettings !== undefined
|
|
99
|
+
? { modelSettings: queryOptions.modelSettings }
|
|
100
|
+
: {}),
|
|
101
|
+
...(queryOptions.contextWindowLimit !== undefined
|
|
102
|
+
? { contextWindowLimit: queryOptions.contextWindowLimit }
|
|
103
|
+
: {}),
|
|
104
|
+
},
|
|
105
|
+
options: sessionOptions,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
70
109
|
protected override resumeLocalSession(
|
|
71
110
|
id: string,
|
|
72
111
|
options: LettaCodeClientSessionOptions,
|
package/src/cloud-session.ts
CHANGED
|
@@ -6,6 +6,11 @@ import {
|
|
|
6
6
|
type AppServerClient,
|
|
7
7
|
} from "@letta-ai/letta-code/app-server-client";
|
|
8
8
|
import { createAgentBody } from "./agent-creation.js";
|
|
9
|
+
import {
|
|
10
|
+
resolveSkillItems,
|
|
11
|
+
skillsHaveSupportFiles,
|
|
12
|
+
type SkillFilesPusher,
|
|
13
|
+
} from "./skill-loading.js";
|
|
9
14
|
import {
|
|
10
15
|
AppServerRuntimeController,
|
|
11
16
|
agentToolNames,
|
|
@@ -78,7 +83,12 @@ type CloudRuntimeStartResponse = ProtocolMessage & {
|
|
|
78
83
|
model?: string | null;
|
|
79
84
|
model_settings?: Record<string, unknown> | null;
|
|
80
85
|
}) | null;
|
|
81
|
-
conversation: (Record<string, unknown> & {
|
|
86
|
+
conversation: (Record<string, unknown> & {
|
|
87
|
+
id?: string;
|
|
88
|
+
agent_id?: string | null;
|
|
89
|
+
model?: string | null;
|
|
90
|
+
model_settings?: Record<string, unknown> | null;
|
|
91
|
+
}) | null;
|
|
82
92
|
error?: string;
|
|
83
93
|
};
|
|
84
94
|
|
|
@@ -143,7 +153,10 @@ export class CloudManagedSandboxExpiredError extends Error {
|
|
|
143
153
|
}
|
|
144
154
|
}
|
|
145
155
|
|
|
146
|
-
type CloudSessionMode = Extract<
|
|
156
|
+
type CloudSessionMode = Extract<
|
|
157
|
+
RuntimeSessionMode,
|
|
158
|
+
{ kind: "session" } | { kind: "agent-free" }
|
|
159
|
+
>;
|
|
147
160
|
|
|
148
161
|
function getWebSocketConstructor(
|
|
149
162
|
websocketOverride?: LettaCodeSocketConstructor,
|
|
@@ -226,7 +239,7 @@ function environmentToRemoteTarget(
|
|
|
226
239
|
function buildCloudStatusWebSocketUrl(params: {
|
|
227
240
|
apiBaseUrl?: string;
|
|
228
241
|
connectionId: string;
|
|
229
|
-
agentId: string;
|
|
242
|
+
agentId: string | null;
|
|
230
243
|
conversationId: string;
|
|
231
244
|
apiKey?: string;
|
|
232
245
|
authMode: "header" | "query";
|
|
@@ -240,7 +253,7 @@ function buildCloudStatusWebSocketUrl(params: {
|
|
|
240
253
|
throw new Error(`Unsupported cloud apiBaseUrl protocol: ${base.protocol}`);
|
|
241
254
|
}
|
|
242
255
|
base.pathname = `/v1/environments/${encodeURIComponent(params.connectionId)}/status/ws`;
|
|
243
|
-
base.searchParams.set("agentId", params.agentId);
|
|
256
|
+
if (params.agentId) base.searchParams.set("agentId", params.agentId);
|
|
244
257
|
base.searchParams.set("conversationId", params.conversationId);
|
|
245
258
|
base.searchParams.set("channel", "stream");
|
|
246
259
|
if (params.authMode === "query" && params.apiKey) {
|
|
@@ -301,12 +314,38 @@ function externalToolsByName(tools: AnyAgentTool[] | undefined): Map<string, Any
|
|
|
301
314
|
export async function createCloudAgent(
|
|
302
315
|
client: Letta,
|
|
303
316
|
agentOptions: CreateAgentOptions,
|
|
317
|
+
pushSkillSupportFiles?: SkillFilesPusher,
|
|
304
318
|
): Promise<string> {
|
|
305
|
-
|
|
319
|
+
// Directory paths were already resolved to inline skills by the client
|
|
320
|
+
// facade; this validates inline skills passed directly.
|
|
321
|
+
const skills = await resolveSkillItems(agentOptions.skills);
|
|
322
|
+
const hasSupportFiles = skillsHaveSupportFiles(skills);
|
|
323
|
+
if (hasSupportFiles && pushSkillSupportFiles === undefined) {
|
|
324
|
+
throw new Error(
|
|
325
|
+
"Skill support files (scripts/, references/) require the Node.js " +
|
|
326
|
+
"package root; the portable client seeds SKILL.md-only skills.",
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
const body = await createAgentBody(agentOptions, skills);
|
|
306
330
|
const agent = await client.agents.create(body as AgentCreateParams);
|
|
307
331
|
if (typeof agent.id !== "string" || agent.id.length === 0) {
|
|
308
332
|
throw new Error("Cloud create agent response did not include an agent id.");
|
|
309
333
|
}
|
|
334
|
+
// SKILL.md contents rode the create request as memory blocks; support
|
|
335
|
+
// files (scripts, references) have no block representation and go to the
|
|
336
|
+
// agent's memory git repo in one follow-up commit.
|
|
337
|
+
if (hasSupportFiles && pushSkillSupportFiles) {
|
|
338
|
+
if (typeof client.apiKey !== "string" || client.apiKey.length === 0) {
|
|
339
|
+
throw new Error(
|
|
340
|
+
`Agent ${agent.id} was created, but skill support files need an API ` +
|
|
341
|
+
"key to push to the agent's memory repo and none is configured.",
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
await pushSkillSupportFiles(
|
|
345
|
+
{ apiBaseUrl: client.baseURL, apiKey: client.apiKey, agentId: agent.id },
|
|
346
|
+
skills,
|
|
347
|
+
);
|
|
348
|
+
}
|
|
310
349
|
return agent.id;
|
|
311
350
|
}
|
|
312
351
|
|
|
@@ -364,10 +403,12 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
364
403
|
const resolved = await this.resolveRuntime();
|
|
365
404
|
const connection = await this.resolveConnectionForRuntime(resolved.runtime).catch(
|
|
366
405
|
async (error: unknown) => {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
406
|
+
if (resolved.runtime.agent_id) {
|
|
407
|
+
await this.cleanupSessionRepositories(
|
|
408
|
+
resolved.runtime.agent_id,
|
|
409
|
+
resolved.runtime.conversation_id,
|
|
410
|
+
);
|
|
411
|
+
}
|
|
371
412
|
throw error;
|
|
372
413
|
},
|
|
373
414
|
);
|
|
@@ -393,10 +434,12 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
393
434
|
} catch (error) {
|
|
394
435
|
await this.closeMcpBridge();
|
|
395
436
|
await this.cleanupManagedSandbox();
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
437
|
+
if (resolved.runtime.agent_id) {
|
|
438
|
+
await this.cleanupSessionRepositories(
|
|
439
|
+
resolved.runtime.agent_id,
|
|
440
|
+
resolved.runtime.conversation_id,
|
|
441
|
+
);
|
|
442
|
+
}
|
|
400
443
|
throw error;
|
|
401
444
|
}
|
|
402
445
|
}
|
|
@@ -438,8 +481,16 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
438
481
|
clientToolset,
|
|
439
482
|
),
|
|
440
483
|
runtime: response.runtime,
|
|
441
|
-
model:
|
|
442
|
-
|
|
484
|
+
model:
|
|
485
|
+
typeof response.agent?.model === "string"
|
|
486
|
+
? response.agent.model
|
|
487
|
+
: typeof response.conversation?.model === "string"
|
|
488
|
+
? response.conversation.model
|
|
489
|
+
: "",
|
|
490
|
+
modelSettings:
|
|
491
|
+
response.agent?.model_settings ??
|
|
492
|
+
response.conversation?.model_settings ??
|
|
493
|
+
null,
|
|
443
494
|
...(availableTools !== undefined ? { tools: availableTools } : {}),
|
|
444
495
|
...(skillSources !== undefined ? { skillSources: [...skillSources] } : {}),
|
|
445
496
|
};
|
|
@@ -590,11 +641,11 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
590
641
|
name: SDK_AGENT_ORIGIN,
|
|
591
642
|
title: "Letta Agent SDK",
|
|
592
643
|
},
|
|
593
|
-
agent_id: runtime.agent_id,
|
|
594
644
|
conversation_id: runtime.conversation_id,
|
|
595
645
|
recover_approvals: false,
|
|
596
646
|
force_device_status: true,
|
|
597
647
|
};
|
|
648
|
+
if (runtime.agent_id) command.agent_id = runtime.agent_id;
|
|
598
649
|
|
|
599
650
|
const mode = mapPermissionMode(options.permissionMode);
|
|
600
651
|
if (mode) command.mode = mode;
|
|
@@ -676,6 +727,18 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
676
727
|
}
|
|
677
728
|
|
|
678
729
|
private async resolveRuntime(): Promise<{ runtime: RuntimeScope }> {
|
|
730
|
+
if (this.cloudMode.kind === "agent-free") {
|
|
731
|
+
if (!this.cloudMode.conversationId) {
|
|
732
|
+
throw new Error("Cloud agent-free sessions require a conversation id.");
|
|
733
|
+
}
|
|
734
|
+
return {
|
|
735
|
+
runtime: {
|
|
736
|
+
agent_id: null,
|
|
737
|
+
conversation_id: this.cloudMode.conversationId,
|
|
738
|
+
},
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
|
|
679
742
|
let agentId = this.cloudMode.agentId;
|
|
680
743
|
let conversationId = this.cloudMode.conversationId;
|
|
681
744
|
|
|
@@ -890,6 +953,11 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
890
953
|
private async createManagedSandboxConnection(
|
|
891
954
|
runtime: RuntimeScope,
|
|
892
955
|
): Promise<ResolvedCloudConnection> {
|
|
956
|
+
if (!runtime.agent_id) {
|
|
957
|
+
throw new Error(
|
|
958
|
+
"Agent-free queries require an explicit Cloud computer; managed sandboxes are agent-scoped.",
|
|
959
|
+
);
|
|
960
|
+
}
|
|
893
961
|
// Scope the managed sandbox to the conversation when one exists: the
|
|
894
962
|
// server treats create-with-conversationId as create-or-resume, so
|
|
895
963
|
// sessions of the same conversation share one sandbox and sessions of
|
package/src/index.ts
CHANGED
|
@@ -39,6 +39,7 @@ import type {
|
|
|
39
39
|
SDKResultMessage,
|
|
40
40
|
SendMessage,
|
|
41
41
|
} from "./types.js";
|
|
42
|
+
import type { Query, QueryParams } from "./query-types.js";
|
|
42
43
|
import { validateCreateSessionOptions, validateCreateAgentOptions } from "./validation.js";
|
|
43
44
|
|
|
44
45
|
// Re-export types
|
|
@@ -94,6 +95,8 @@ export type {
|
|
|
94
95
|
SessionDiffHunk,
|
|
95
96
|
SessionDiffPreview,
|
|
96
97
|
SkillSource,
|
|
98
|
+
SkillItem,
|
|
99
|
+
AgentSkill,
|
|
97
100
|
DreamingOptions,
|
|
98
101
|
SessionDreamingOptions,
|
|
99
102
|
DreamingTrigger,
|
|
@@ -152,6 +155,11 @@ export type {
|
|
|
152
155
|
McpSseServerConfig,
|
|
153
156
|
McpServers,
|
|
154
157
|
} from "./types.js";
|
|
158
|
+
export type {
|
|
159
|
+
AgentFreeQueryOptions,
|
|
160
|
+
Query,
|
|
161
|
+
QueryParams,
|
|
162
|
+
} from "./query-types.js";
|
|
155
163
|
export type * from "./sandbox-files.js";
|
|
156
164
|
export type {
|
|
157
165
|
AgentRepositoriesClient,
|
|
@@ -325,6 +333,14 @@ export async function prompt(
|
|
|
325
333
|
}
|
|
326
334
|
}
|
|
327
335
|
|
|
336
|
+
/** Run an agent-free query in a new ephemeral conversation. */
|
|
337
|
+
export function query(params: QueryParams): Query {
|
|
338
|
+
return new LettaAgentClient({
|
|
339
|
+
backend: "local",
|
|
340
|
+
appServer: { harnessBackend: "api" },
|
|
341
|
+
}).query(params);
|
|
342
|
+
}
|
|
343
|
+
|
|
328
344
|
// ═══════════════════════════════════════════════════════════════
|
|
329
345
|
// SESSIONLESS APIs
|
|
330
346
|
// ═══════════════════════════════════════════════════════════════
|
package/src/local-app-server.ts
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LettaCodeClientSessionOptions,
|
|
3
|
+
SDKMessage,
|
|
4
|
+
SendMessage,
|
|
5
|
+
} from "./types.js";
|
|
6
|
+
|
|
7
|
+
/** Options for an agent-free query backed by an ephemeral conversation. */
|
|
8
|
+
export interface AgentFreeQueryOptions
|
|
9
|
+
extends Omit<
|
|
10
|
+
LettaCodeClientSessionOptions,
|
|
11
|
+
| "model"
|
|
12
|
+
| "reasoningEffort"
|
|
13
|
+
| "stateless"
|
|
14
|
+
| "dreaming"
|
|
15
|
+
| "resources"
|
|
16
|
+
| "filesystemConfinement"
|
|
17
|
+
> {
|
|
18
|
+
/** Model handle persisted on the ephemeral conversation. */
|
|
19
|
+
model: string;
|
|
20
|
+
/** Complete system prompt for the ephemeral conversation. */
|
|
21
|
+
system: string;
|
|
22
|
+
/** Provider model settings persisted on the ephemeral conversation. */
|
|
23
|
+
modelSettings?: Record<string, unknown>;
|
|
24
|
+
/** Optional context-window limit persisted on the ephemeral conversation. */
|
|
25
|
+
contextWindowLimit?: number | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Input accepted by query(). */
|
|
29
|
+
export interface QueryParams {
|
|
30
|
+
prompt: SendMessage;
|
|
31
|
+
options: AgentFreeQueryOptions;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Stream returned by query(), with controls for long-running execution. */
|
|
35
|
+
export interface Query extends AsyncGenerator<SDKMessage, void, unknown> {
|
|
36
|
+
/** Interrupt the active turn without closing the query session. */
|
|
37
|
+
interrupt(): Promise<void>;
|
|
38
|
+
/** Close the query and release its underlying session. */
|
|
39
|
+
close(): void;
|
|
40
|
+
}
|
package/src/query.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AgentFreeQueryOptions, Query, QueryParams } from "./query-types.js";
|
|
2
|
+
import type { LettaCodeSession, SDKMessage } from "./types.js";
|
|
3
|
+
|
|
4
|
+
type CreateAgentFreeSession = (
|
|
5
|
+
options: AgentFreeQueryOptions,
|
|
6
|
+
) => Promise<LettaCodeSession>;
|
|
7
|
+
|
|
8
|
+
/** Create one agent-free ephemeral conversation and stream its query result. */
|
|
9
|
+
export function createQuery(
|
|
10
|
+
createSession: CreateAgentFreeSession,
|
|
11
|
+
params: QueryParams,
|
|
12
|
+
): Query {
|
|
13
|
+
let session: LettaCodeSession | null = null;
|
|
14
|
+
let closed = false;
|
|
15
|
+
|
|
16
|
+
const iterator = (async function* runQuery(): AsyncGenerator<
|
|
17
|
+
SDKMessage,
|
|
18
|
+
void,
|
|
19
|
+
unknown
|
|
20
|
+
> {
|
|
21
|
+
try {
|
|
22
|
+
session = await createSession(params.options);
|
|
23
|
+
if (closed) return;
|
|
24
|
+
await session.send(params.prompt);
|
|
25
|
+
yield* session.stream();
|
|
26
|
+
} finally {
|
|
27
|
+
closed = true;
|
|
28
|
+
session?.close();
|
|
29
|
+
}
|
|
30
|
+
})();
|
|
31
|
+
|
|
32
|
+
return Object.assign(iterator, {
|
|
33
|
+
async interrupt(): Promise<void> {
|
|
34
|
+
await session?.abort();
|
|
35
|
+
},
|
|
36
|
+
close(): void {
|
|
37
|
+
if (closed) return;
|
|
38
|
+
closed = true;
|
|
39
|
+
session?.close();
|
|
40
|
+
void iterator.return();
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -166,7 +166,7 @@ export abstract class RemoteClientSessionCore implements LettaCodeSession {
|
|
|
166
166
|
this.runtime = init.runtime;
|
|
167
167
|
this._agentId = init.runtime.agent_id;
|
|
168
168
|
this._conversationId = init.runtime.conversation_id;
|
|
169
|
-
this._sessionId = `${init.runtime.agent_id}:${init.runtime.conversation_id}
|
|
169
|
+
this._sessionId = init.runtime.agent_id ? `${init.runtime.agent_id}:${init.runtime.conversation_id}` : init.runtime.conversation_id;
|
|
170
170
|
this._modelSettings = init.modelSettings ?? null;
|
|
171
171
|
this._model =
|
|
172
172
|
typeof init.model === "string"
|
|
@@ -23,7 +23,7 @@ import type {
|
|
|
23
23
|
} from "./types.js";
|
|
24
24
|
|
|
25
25
|
export type RuntimeScope = {
|
|
26
|
-
agent_id: string;
|
|
26
|
+
agent_id: string | null;
|
|
27
27
|
conversation_id: string;
|
|
28
28
|
};
|
|
29
29
|
|
|
@@ -35,6 +35,17 @@ export type ProtocolMessage = Record<string, unknown> & {
|
|
|
35
35
|
|
|
36
36
|
export type RuntimeSessionMode =
|
|
37
37
|
| { kind: "create-agent"; options: CreateAgentOptions }
|
|
38
|
+
| {
|
|
39
|
+
kind: "agent-free";
|
|
40
|
+
conversationId?: string;
|
|
41
|
+
createConversation?: {
|
|
42
|
+
model: string;
|
|
43
|
+
system: string;
|
|
44
|
+
modelSettings?: Record<string, unknown>;
|
|
45
|
+
contextWindowLimit?: number | null;
|
|
46
|
+
};
|
|
47
|
+
options: LettaCodeClientSessionOptions;
|
|
48
|
+
}
|
|
38
49
|
| {
|
|
39
50
|
kind: "session";
|
|
40
51
|
agentId?: string;
|