@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
|
@@ -614,12 +614,16 @@ export class RemoteTurnCoordinator {
|
|
|
614
614
|
const errorCode = approvalConflict
|
|
615
615
|
? "approval_conflict"
|
|
616
616
|
: (turn.errorCode ?? toSdkErrorCode(stopReason));
|
|
617
|
+
const publicError =
|
|
618
|
+
errorCode && errorCode !== "error"
|
|
619
|
+
? errorCode
|
|
620
|
+
: (turn.detail ?? stopReason ?? "error");
|
|
617
621
|
|
|
618
622
|
return {
|
|
619
623
|
type: "result",
|
|
620
624
|
success,
|
|
621
625
|
result: success ? tracker?.assistantText || undefined : undefined,
|
|
622
|
-
error: success ? undefined :
|
|
626
|
+
error: success ? undefined : publicError,
|
|
623
627
|
errorCode: success ? undefined : (errorCode ?? "error"),
|
|
624
628
|
approvalConflict: approvalConflict || undefined,
|
|
625
629
|
recoverable: approvalConflict
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Letta-hosted repository API types, used by RepositoriesClient and
|
|
3
|
+
* AgentRepositoriesClient. Kept beside their implementation
|
|
4
|
+
* (repositories.ts) and re-exported from types.ts.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface Repository {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
createdAt: string;
|
|
11
|
+
updatedAt: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface CreateRepositoryParams {
|
|
15
|
+
name: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ListRepositoriesParams {
|
|
19
|
+
limit?: number;
|
|
20
|
+
offset?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ListRepositoriesResult {
|
|
24
|
+
repositories: Repository[];
|
|
25
|
+
hasNextPage: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface RepositoryResource {
|
|
29
|
+
type: "repository";
|
|
30
|
+
repositoryId: string;
|
|
31
|
+
/**
|
|
32
|
+
* Whether to trigger a system-prompt recompile after attaching (and after
|
|
33
|
+
* detaching on cleanup) so the session's conversation does not retain
|
|
34
|
+
* stale repository projections. Defaults to `true`.
|
|
35
|
+
*/
|
|
36
|
+
recompile?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface RepositoryFileEntry {
|
|
40
|
+
path: string;
|
|
41
|
+
type: "file" | "directory";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ListRepositoryFilesParams {
|
|
45
|
+
pathPrefix?: string;
|
|
46
|
+
depth?: number;
|
|
47
|
+
ref?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface ListRepositoryFilesResult {
|
|
51
|
+
files: RepositoryFileEntry[];
|
|
52
|
+
ref: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface CreateRepositoryFileParams {
|
|
56
|
+
path: string;
|
|
57
|
+
content: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface RepositoryFile {
|
|
61
|
+
path: string;
|
|
62
|
+
content: string;
|
|
63
|
+
contentSha256: string;
|
|
64
|
+
ref?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface UpdateRepositoryFileParams {
|
|
68
|
+
path: string;
|
|
69
|
+
content?: string;
|
|
70
|
+
newPath?: string;
|
|
71
|
+
precondition?: {
|
|
72
|
+
contentSha256: string;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface RepositoryFileMutationResult {
|
|
77
|
+
path: string;
|
|
78
|
+
contentSha256: string;
|
|
79
|
+
commitSha: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface DeleteRepositoryFileParams {
|
|
83
|
+
path: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface DeleteRepositoryFileResult {
|
|
87
|
+
success: boolean;
|
|
88
|
+
commitSha: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface RepositoryVersion {
|
|
92
|
+
sha: string;
|
|
93
|
+
message: string;
|
|
94
|
+
timestamp: string;
|
|
95
|
+
author_name: string | null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface ListRepositoryVersionsParams {
|
|
99
|
+
path?: string;
|
|
100
|
+
limit?: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface GetRepositoryVersionParams {
|
|
104
|
+
path: string;
|
|
105
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill seeding for createAgent({ skills }) — portable core.
|
|
3
|
+
*
|
|
4
|
+
* A skill's canonical shape is a directory: `SKILL.md` plus optional support
|
|
5
|
+
* files (scripts/, references/, ...). Callers pass either a path to such a
|
|
6
|
+
* directory or an inline {@link AgentSkill} object.
|
|
7
|
+
*
|
|
8
|
+
* The platform stores `skills/{name}/SKILL.md` as a memory block labeled
|
|
9
|
+
* `skills/{name}` (see letta-cloud `blockMarkdownPath.ts` /
|
|
10
|
+
* `path_mapping.py`), so SKILL.md instructions ride the create-agent request
|
|
11
|
+
* as a memory block. Support files have no block representation; they are
|
|
12
|
+
* pushed to the agent's memory git repo after creation.
|
|
13
|
+
*
|
|
14
|
+
* This module is part of the portable client graph and must stay free of
|
|
15
|
+
* Node imports. Directory loading and the git push live in `skill-node.ts`,
|
|
16
|
+
* behind the package root, and are injected by the Node client.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A skill to seed into a new agent's memory filesystem at creation.
|
|
21
|
+
*
|
|
22
|
+
* The instructions become `skills/{name}/SKILL.md` in the agent's memory
|
|
23
|
+
* repo (stored as the memory block labeled `skills/{name}`), so the agent
|
|
24
|
+
* owns and can edit the skill like any other memory. Support `files` land
|
|
25
|
+
* beside it (`skills/{name}/{path}`); they require the Cloud backend from
|
|
26
|
+
* Node.
|
|
27
|
+
*/
|
|
28
|
+
export interface AgentSkill {
|
|
29
|
+
/** Directory name: lowercase letters, digits, ".", "_", "-". */
|
|
30
|
+
name: string;
|
|
31
|
+
/**
|
|
32
|
+
* Trigger text: when the agent should load this skill. Rendered as the
|
|
33
|
+
* SKILL.md frontmatter description and always visible to the agent.
|
|
34
|
+
*/
|
|
35
|
+
description: string;
|
|
36
|
+
/** SKILL.md body (markdown, without frontmatter). */
|
|
37
|
+
instructions: string;
|
|
38
|
+
/**
|
|
39
|
+
* Optional support files, keyed by path relative to the skill directory
|
|
40
|
+
* (e.g. "scripts/convert.sh"). Cloud backend from Node only.
|
|
41
|
+
*/
|
|
42
|
+
files?: Record<string, Uint8Array>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Skill item for createAgent(): a path to a skill directory containing
|
|
47
|
+
* SKILL.md (Node runtimes only), or an inline {@link AgentSkill}.
|
|
48
|
+
*/
|
|
49
|
+
export type SkillItem = string | AgentSkill;
|
|
50
|
+
|
|
51
|
+
/** Loads a skill directory from disk. Node-only; see skill-node.ts. */
|
|
52
|
+
export type SkillDirectoryLoader = (dirPath: string) => Promise<AgentSkill>;
|
|
53
|
+
|
|
54
|
+
/** Pushes skill support files to an agent's memory git repo. Node-only. */
|
|
55
|
+
export type SkillFilesPusher = (
|
|
56
|
+
target: { apiBaseUrl: string; apiKey: string; agentId: string },
|
|
57
|
+
skills: AgentSkill[],
|
|
58
|
+
) => Promise<void>;
|
|
59
|
+
|
|
60
|
+
/** Node-only skill capabilities injected by the package-root client. */
|
|
61
|
+
export interface SkillNodeSupport {
|
|
62
|
+
loadSkillDirectory: SkillDirectoryLoader;
|
|
63
|
+
pushSkillSupportFiles: SkillFilesPusher;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Skill directory names: lowercase, digits, hyphens (letta-code convention). */
|
|
67
|
+
const SKILL_NAME_RE = /^[a-z0-9][a-z0-9._-]*$/;
|
|
68
|
+
|
|
69
|
+
export function assertValidSkillName(name: string): void {
|
|
70
|
+
if (!SKILL_NAME_RE.test(name)) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`Invalid skill name "${name}". Skill names are directory names: ` +
|
|
73
|
+
`lowercase letters, digits, ".", "_", "-" (e.g. "generating-voice-memos").`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Minimal YAML frontmatter reader for SKILL.md: extracts `name` and
|
|
80
|
+
* `description` (plain, quoted, or `>`/`>-` folded scalars) and returns the
|
|
81
|
+
* body without the frontmatter fence. Anything unparsable is left in place.
|
|
82
|
+
*/
|
|
83
|
+
export function parseSkillMarkdown(content: string): {
|
|
84
|
+
name?: string;
|
|
85
|
+
description?: string;
|
|
86
|
+
body: string;
|
|
87
|
+
} {
|
|
88
|
+
if (!content.startsWith("---\n") && !content.startsWith("---\r\n")) {
|
|
89
|
+
return { body: content };
|
|
90
|
+
}
|
|
91
|
+
const fence = /\r?\n---[ \t]*(\r?\n|$)/.exec(content.slice(3));
|
|
92
|
+
if (!fence) return { body: content };
|
|
93
|
+
const yamlStart = content.startsWith("---\r\n") ? 5 : 4;
|
|
94
|
+
const yamlEnd = 3 + fence.index;
|
|
95
|
+
const yamlText = content.slice(yamlStart, yamlEnd);
|
|
96
|
+
const body = content.slice(yamlEnd + fence[0].length);
|
|
97
|
+
|
|
98
|
+
const fields: Record<string, string> = {};
|
|
99
|
+
const lines = yamlText.split(/\r?\n/);
|
|
100
|
+
for (let i = 0; i < lines.length; i++) {
|
|
101
|
+
const line = lines[i];
|
|
102
|
+
if (!line || /^\s/.test(line)) continue;
|
|
103
|
+
const colon = line.indexOf(":");
|
|
104
|
+
if (colon <= 0) continue;
|
|
105
|
+
const key = line.slice(0, colon).trim();
|
|
106
|
+
let value = line.slice(colon + 1).trim();
|
|
107
|
+
if (value === ">" || value === ">-" || value === "|" || value === "|-") {
|
|
108
|
+
const folded: string[] = [];
|
|
109
|
+
let next = lines[i + 1];
|
|
110
|
+
while (next !== undefined && (/^\s/.test(next) || next === "")) {
|
|
111
|
+
i++;
|
|
112
|
+
folded.push(next.trim());
|
|
113
|
+
next = lines[i + 1];
|
|
114
|
+
}
|
|
115
|
+
value = folded.filter((part) => part.length > 0).join(" ");
|
|
116
|
+
} else if (
|
|
117
|
+
(value.startsWith('"') && value.endsWith('"') && value.length >= 2) ||
|
|
118
|
+
(value.startsWith("'") && value.endsWith("'") && value.length >= 2)
|
|
119
|
+
) {
|
|
120
|
+
value = value.slice(1, -1);
|
|
121
|
+
}
|
|
122
|
+
fields[key] = value;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { name: fields["name"], description: fields["description"], body };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Resolve every skill item to an AgentSkill. Directory paths need the
|
|
130
|
+
* injected Node loader; without one (portable clients) they are rejected.
|
|
131
|
+
*/
|
|
132
|
+
export async function resolveSkillItems(
|
|
133
|
+
items: SkillItem[] | undefined,
|
|
134
|
+
loadDirectory?: SkillDirectoryLoader,
|
|
135
|
+
): Promise<AgentSkill[]> {
|
|
136
|
+
if (!items || items.length === 0) return [];
|
|
137
|
+
const resolved: AgentSkill[] = [];
|
|
138
|
+
for (const item of items) {
|
|
139
|
+
if (typeof item === "string") {
|
|
140
|
+
if (!loadDirectory) {
|
|
141
|
+
throw new Error(
|
|
142
|
+
`Skill directory paths ("${item}") require a Node.js runtime. ` +
|
|
143
|
+
"Pass an inline skill ({ name, description, instructions }) instead.",
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
resolved.push(await loadDirectory(item));
|
|
147
|
+
} else {
|
|
148
|
+
assertValidSkillName(item.name);
|
|
149
|
+
if (!item.instructions || item.instructions.trim().length === 0) {
|
|
150
|
+
throw new Error(`Skill "${item.name}" has empty instructions.`);
|
|
151
|
+
}
|
|
152
|
+
if (!item.description || item.description.trim().length === 0) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`Skill "${item.name}" has no description. ` +
|
|
155
|
+
`The description is the skill's trigger text; it is required.`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
resolved.push(item);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const seen = new Set<string>();
|
|
162
|
+
for (const skill of resolved) {
|
|
163
|
+
if (seen.has(skill.name)) {
|
|
164
|
+
throw new Error(`Duplicate skill name: "${skill.name}".`);
|
|
165
|
+
}
|
|
166
|
+
seen.add(skill.name);
|
|
167
|
+
}
|
|
168
|
+
return resolved;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** True when any resolved skill carries support files beyond SKILL.md. */
|
|
172
|
+
export function skillsHaveSupportFiles(skills: AgentSkill[]): boolean {
|
|
173
|
+
return skills.some(
|
|
174
|
+
(skill) => skill.files && Object.keys(skill.files).length > 0,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Collect `skills/{name}/{relPath}` entries for every skill support file. */
|
|
179
|
+
export function skillSupportFileEntries(
|
|
180
|
+
skills: AgentSkill[],
|
|
181
|
+
): Array<{ path: string; data: Uint8Array }> {
|
|
182
|
+
const entries: Array<{ path: string; data: Uint8Array }> = [];
|
|
183
|
+
for (const skill of skills) {
|
|
184
|
+
for (const [relPath, data] of Object.entries(skill.files ?? {})) {
|
|
185
|
+
if (relPath.split("/").some((part) => part === ".." || part === "")) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Skill "${skill.name}" file path escapes the skill directory: ${relPath}`,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
entries.push({ path: `skills/${skill.name}/${relPath}`, data });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return entries;
|
|
194
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node-only skill seeding: directory loading and the memory-repo git push.
|
|
3
|
+
*
|
|
4
|
+
* `skill-loading.ts` owns the portable core (types, validation, SKILL.md
|
|
5
|
+
* parsing, block mapping). This module owns everything that touches the
|
|
6
|
+
* filesystem or spawns git, and stays behind the package root so the
|
|
7
|
+
* portable `/client` entry never pulls in Node builtins.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFile, readdir, stat, mkdtemp, rm, mkdir, writeFile } from "node:fs/promises";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { basename, dirname, join, relative, sep } from "node:path";
|
|
13
|
+
import { execFile } from "node:child_process";
|
|
14
|
+
import { promisify } from "node:util";
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
assertValidSkillName,
|
|
18
|
+
parseSkillMarkdown,
|
|
19
|
+
skillSupportFileEntries,
|
|
20
|
+
type AgentSkill,
|
|
21
|
+
} from "./skill-loading.js";
|
|
22
|
+
|
|
23
|
+
const run = promisify(execFile);
|
|
24
|
+
|
|
25
|
+
/** Load one skill directory from disk. */
|
|
26
|
+
export async function loadSkillDirectory(dirPath: string): Promise<AgentSkill> {
|
|
27
|
+
const dirStat = await stat(dirPath).catch(() => null);
|
|
28
|
+
if (!dirStat?.isDirectory()) {
|
|
29
|
+
throw new Error(`Skill path is not a directory: ${dirPath}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const skillMd = await readFile(join(dirPath, "SKILL.md"), "utf-8").catch(
|
|
33
|
+
() => null,
|
|
34
|
+
);
|
|
35
|
+
if (skillMd === null) {
|
|
36
|
+
throw new Error(`Skill directory has no SKILL.md: ${dirPath}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const parsed = parseSkillMarkdown(skillMd);
|
|
40
|
+
const name = parsed.name ?? basename(dirPath);
|
|
41
|
+
assertValidSkillName(name);
|
|
42
|
+
if (!parsed.description) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`SKILL.md in ${dirPath} has no frontmatter description. ` +
|
|
45
|
+
`The description is the skill's trigger text; it is required.`,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Collect support files (everything except SKILL.md), preserving layout.
|
|
50
|
+
const files: Record<string, Uint8Array> = {};
|
|
51
|
+
const walk = async (current: string): Promise<void> => {
|
|
52
|
+
for (const entry of await readdir(current, { withFileTypes: true })) {
|
|
53
|
+
const full = join(current, entry.name);
|
|
54
|
+
if (entry.isDirectory()) {
|
|
55
|
+
await walk(full);
|
|
56
|
+
} else if (entry.isFile()) {
|
|
57
|
+
const rel = relative(dirPath, full).split(sep).join("/");
|
|
58
|
+
if (rel === "SKILL.md") continue;
|
|
59
|
+
files[rel] = new Uint8Array(await readFile(full));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
await walk(dirPath);
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
name,
|
|
67
|
+
description: parsed.description,
|
|
68
|
+
instructions: parsed.body.replace(/^\s+/, ""),
|
|
69
|
+
...(Object.keys(files).length > 0 ? { files } : {}),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface SkillFilesPushTarget {
|
|
74
|
+
apiBaseUrl: string;
|
|
75
|
+
apiKey: string;
|
|
76
|
+
agentId: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Push skill support files into a new agent's memory git repo.
|
|
81
|
+
*
|
|
82
|
+
* SKILL.md rides the create-agent request as a memory block; everything else
|
|
83
|
+
* in a skill directory (scripts/, references/, ...) has no block
|
|
84
|
+
* representation and exactly one write channel: the agent's memory git
|
|
85
|
+
* remote at `{apiBaseUrl}/v1/git/{agentId}/state.git`. This function owns
|
|
86
|
+
* that one commit: shallow clone, write files, commit, push (with one
|
|
87
|
+
* pull --rebase retry in case the server committed between clone and push).
|
|
88
|
+
*/
|
|
89
|
+
export async function pushSkillSupportFiles(
|
|
90
|
+
target: SkillFilesPushTarget,
|
|
91
|
+
skills: AgentSkill[],
|
|
92
|
+
): Promise<void> {
|
|
93
|
+
const entries = skillSupportFileEntries(skills);
|
|
94
|
+
if (entries.length === 0) return;
|
|
95
|
+
|
|
96
|
+
const base = target.apiBaseUrl.replace(/\/+$/, "");
|
|
97
|
+
const remote = `${base}/v1/git/${target.agentId}/state.git`;
|
|
98
|
+
// Credentials travel via a one-shot Authorization header, not the URL, so
|
|
99
|
+
// they cannot leak into error messages or the cloned repo's config.
|
|
100
|
+
const authHeader = `Authorization: Basic ${Buffer.from(`x:${target.apiKey}`).toString("base64")}`;
|
|
101
|
+
const gitEnv = {
|
|
102
|
+
...process.env,
|
|
103
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
104
|
+
GIT_AUTHOR_NAME: "letta-agent-sdk",
|
|
105
|
+
GIT_AUTHOR_EMAIL: "sdk@letta.com",
|
|
106
|
+
GIT_COMMITTER_NAME: "letta-agent-sdk",
|
|
107
|
+
GIT_COMMITTER_EMAIL: "sdk@letta.com",
|
|
108
|
+
};
|
|
109
|
+
const git = (args: string[], cwd?: string) =>
|
|
110
|
+
run("git", ["-c", `http.extraHeader=${authHeader}`, ...args], {
|
|
111
|
+
env: gitEnv,
|
|
112
|
+
...(cwd ? { cwd } : {}),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const work = await mkdtemp(join(tmpdir(), "letta-skill-seed-"));
|
|
116
|
+
try {
|
|
117
|
+
await git(["clone", "--quiet", "--depth", "1", remote, work]);
|
|
118
|
+
for (const entry of entries) {
|
|
119
|
+
const filePath = join(work, entry.path);
|
|
120
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
121
|
+
await writeFile(filePath, entry.data);
|
|
122
|
+
}
|
|
123
|
+
await git(["add", "-A"], work);
|
|
124
|
+
await git(["commit", "--quiet", "-m", "feat: seed skill support files"], work);
|
|
125
|
+
try {
|
|
126
|
+
await git(["push", "--quiet", "origin", "HEAD"], work);
|
|
127
|
+
} catch {
|
|
128
|
+
await git(["pull", "--quiet", "--rebase", "origin", "HEAD"], work);
|
|
129
|
+
await git(["push", "--quiet", "origin", "HEAD"], work);
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Agent ${target.agentId} was created, but pushing skill support files ` +
|
|
134
|
+
`to its memory repo failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
135
|
+
);
|
|
136
|
+
} finally {
|
|
137
|
+
await rm(work, { recursive: true, force: true });
|
|
138
|
+
}
|
|
139
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -209,6 +209,12 @@ export type MemoryItem =
|
|
|
209
209
|
| CreateBlock // Custom block: { label, value, description? }
|
|
210
210
|
| BlockReference; // Shared block reference: { blockId }
|
|
211
211
|
|
|
212
|
+
// Skill seeding types (AgentSkill, SkillItem) live beside their
|
|
213
|
+
// implementation in skill-loading.ts and are re-exported from the package
|
|
214
|
+
// root.
|
|
215
|
+
import type { SkillItem } from "./skill-loading.js";
|
|
216
|
+
export type { AgentSkill, SkillItem } from "./skill-loading.js";
|
|
217
|
+
|
|
212
218
|
/**
|
|
213
219
|
* Default memory block preset names.
|
|
214
220
|
*/
|
|
@@ -408,105 +414,28 @@ export type LettaCodeClientOptions =
|
|
|
408
414
|
| LettaCodeRemoteClientOptions
|
|
409
415
|
| LettaCodeCloudClientOptions;
|
|
410
416
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
type: "repository";
|
|
434
|
-
repositoryId: string;
|
|
435
|
-
/**
|
|
436
|
-
* Whether to trigger a system-prompt recompile after attaching (and after
|
|
437
|
-
* detaching on cleanup) so the session's conversation does not retain
|
|
438
|
-
* stale repository projections. Defaults to `true`.
|
|
439
|
-
*/
|
|
440
|
-
recompile?: boolean;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
export interface RepositoryFileEntry {
|
|
444
|
-
path: string;
|
|
445
|
-
type: "file" | "directory";
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
export interface ListRepositoryFilesParams {
|
|
449
|
-
pathPrefix?: string;
|
|
450
|
-
depth?: number;
|
|
451
|
-
ref?: string;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
export interface ListRepositoryFilesResult {
|
|
455
|
-
files: RepositoryFileEntry[];
|
|
456
|
-
ref: string;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
export interface CreateRepositoryFileParams {
|
|
460
|
-
path: string;
|
|
461
|
-
content: string;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
export interface RepositoryFile {
|
|
465
|
-
path: string;
|
|
466
|
-
content: string;
|
|
467
|
-
contentSha256: string;
|
|
468
|
-
ref?: string;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
export interface UpdateRepositoryFileParams {
|
|
472
|
-
path: string;
|
|
473
|
-
content?: string;
|
|
474
|
-
newPath?: string;
|
|
475
|
-
precondition?: {
|
|
476
|
-
contentSha256: string;
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
export interface RepositoryFileMutationResult {
|
|
481
|
-
path: string;
|
|
482
|
-
contentSha256: string;
|
|
483
|
-
commitSha: string;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
export interface DeleteRepositoryFileParams {
|
|
487
|
-
path: string;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
export interface DeleteRepositoryFileResult {
|
|
491
|
-
success: boolean;
|
|
492
|
-
commitSha: string;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
export interface RepositoryVersion {
|
|
496
|
-
sha: string;
|
|
497
|
-
message: string;
|
|
498
|
-
timestamp: string;
|
|
499
|
-
author_name: string | null;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
export interface ListRepositoryVersionsParams {
|
|
503
|
-
path?: string;
|
|
504
|
-
limit?: number;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
export interface GetRepositoryVersionParams {
|
|
508
|
-
path: string;
|
|
509
|
-
}
|
|
417
|
+
// Letta-hosted repository API types live beside their implementation in
|
|
418
|
+
// repository-types.ts and are re-exported here for compatibility.
|
|
419
|
+
import type { RepositoryResource } from "./repository-types.js";
|
|
420
|
+
export type {
|
|
421
|
+
Repository,
|
|
422
|
+
CreateRepositoryParams,
|
|
423
|
+
ListRepositoriesParams,
|
|
424
|
+
ListRepositoriesResult,
|
|
425
|
+
RepositoryResource,
|
|
426
|
+
RepositoryFileEntry,
|
|
427
|
+
ListRepositoryFilesParams,
|
|
428
|
+
ListRepositoryFilesResult,
|
|
429
|
+
CreateRepositoryFileParams,
|
|
430
|
+
RepositoryFile,
|
|
431
|
+
UpdateRepositoryFileParams,
|
|
432
|
+
RepositoryFileMutationResult,
|
|
433
|
+
DeleteRepositoryFileParams,
|
|
434
|
+
DeleteRepositoryFileResult,
|
|
435
|
+
RepositoryVersion,
|
|
436
|
+
ListRepositoryVersionsParams,
|
|
437
|
+
GetRepositoryVersionParams,
|
|
438
|
+
} from "./repository-types.js";
|
|
510
439
|
|
|
511
440
|
// ═══════════════════════════════════════════════════════════════
|
|
512
441
|
// SESSION OPTIONS
|
|
@@ -951,6 +880,19 @@ export interface CreateAgentOptions {
|
|
|
951
880
|
*/
|
|
952
881
|
memory?: MemoryItem[];
|
|
953
882
|
|
|
883
|
+
/**
|
|
884
|
+
* Skills to seed into the agent's memory filesystem at creation. Each item
|
|
885
|
+
* is a path to a skill directory containing SKILL.md (Node runtimes only)
|
|
886
|
+
* or an inline {@link AgentSkill}.
|
|
887
|
+
*
|
|
888
|
+
* SKILL.md instructions ride the create request itself (as the memory
|
|
889
|
+
* block labeled `skills/{name}`) and are present from the agent's first
|
|
890
|
+
* turn. Skill support files (scripts, references) are pushed to the
|
|
891
|
+
* agent's memory repo immediately after creation; that second step
|
|
892
|
+
* currently requires the Cloud backend. Requires memfs (the default).
|
|
893
|
+
*/
|
|
894
|
+
skills?: SkillItem[];
|
|
895
|
+
|
|
954
896
|
/** @deprecated Prefer a personality preset or a system memory file. */
|
|
955
897
|
persona?: string;
|
|
956
898
|
|
|
@@ -1038,7 +980,7 @@ export interface CreateAgentOptions {
|
|
|
1038
980
|
*/
|
|
1039
981
|
export interface SDKInitMessage {
|
|
1040
982
|
type: "init";
|
|
1041
|
-
agentId: string;
|
|
983
|
+
agentId: string | null;
|
|
1042
984
|
sessionId: string;
|
|
1043
985
|
conversationId: string;
|
|
1044
986
|
model: string;
|