@letta-ai/letta-code 0.11.2-next.1 → 0.11.2-next.3
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/letta.js +18 -2
- package/package.json +1 -1
- package/skills/creating-skills/SKILL.md +3 -3
- package/skills/finding-agents/SKILL.md +9 -9
- package/skills/finding-agents/scripts/find-agents.ts +54 -11
- package/skills/migrating-memory/SKILL.md +7 -7
- package/skills/migrating-memory/scripts/attach-block.ts +77 -16
- package/skills/migrating-memory/scripts/copy-block.ts +87 -22
- package/skills/migrating-memory/scripts/get-agent-blocks.ts +52 -11
- package/skills/searching-messages/scripts/get-messages.ts +10 -3
- package/skills/searching-messages/scripts/search-messages.ts +10 -3
package/letta.js
CHANGED
|
@@ -3237,7 +3237,7 @@ var package_default;
|
|
|
3237
3237
|
var init_package = __esm(() => {
|
|
3238
3238
|
package_default = {
|
|
3239
3239
|
name: "@letta-ai/letta-code",
|
|
3240
|
-
version: "0.11.2-next.
|
|
3240
|
+
version: "0.11.2-next.3",
|
|
3241
3241
|
description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
|
|
3242
3242
|
type: "module",
|
|
3243
3243
|
bin: {
|
|
@@ -33383,6 +33383,17 @@ function getRipgrepBinDir() {
|
|
|
33383
33383
|
return;
|
|
33384
33384
|
}
|
|
33385
33385
|
}
|
|
33386
|
+
function getPackageNodeModulesDir() {
|
|
33387
|
+
try {
|
|
33388
|
+
const __filename2 = fileURLToPath(import.meta.url);
|
|
33389
|
+
const require2 = createRequire2(__filename2);
|
|
33390
|
+
const clientPath = require2.resolve("@letta-ai/letta-client");
|
|
33391
|
+
const match = clientPath.match(/^(.+[/\\]node_modules)[/\\]/);
|
|
33392
|
+
return match ? match[1] : undefined;
|
|
33393
|
+
} catch {
|
|
33394
|
+
return;
|
|
33395
|
+
}
|
|
33396
|
+
}
|
|
33386
33397
|
function getShellEnv() {
|
|
33387
33398
|
const env3 = { ...process.env };
|
|
33388
33399
|
const rgBinDir = getRipgrepBinDir();
|
|
@@ -33401,6 +33412,11 @@ function getShellEnv() {
|
|
|
33401
33412
|
}
|
|
33402
33413
|
} catch {}
|
|
33403
33414
|
}
|
|
33415
|
+
const nodeModulesDir = getPackageNodeModulesDir();
|
|
33416
|
+
if (nodeModulesDir) {
|
|
33417
|
+
const currentNodePath = env3.NODE_PATH || "";
|
|
33418
|
+
env3.NODE_PATH = currentNodePath ? `${nodeModulesDir}${path3.delimiter}${currentNodePath}` : nodeModulesDir;
|
|
33419
|
+
}
|
|
33404
33420
|
return env3;
|
|
33405
33421
|
}
|
|
33406
33422
|
var init_shellEnv = __esm(async () => {
|
|
@@ -77085,4 +77101,4 @@ Error during initialization: ${message}`);
|
|
|
77085
77101
|
}
|
|
77086
77102
|
main();
|
|
77087
77103
|
|
|
77088
|
-
//# debugId=
|
|
77104
|
+
//# debugId=97EC206550CD703E64756E2164756E21
|
package/package.json
CHANGED
|
@@ -263,7 +263,7 @@ When creating a new skill from scratch, always run the `init-skill.ts` script. T
|
|
|
263
263
|
Usage:
|
|
264
264
|
|
|
265
265
|
```bash
|
|
266
|
-
npx
|
|
266
|
+
npx tsx <SKILL_DIR>/scripts/init-skill.ts <skill-name> --path <output-directory>
|
|
267
267
|
```
|
|
268
268
|
|
|
269
269
|
The script:
|
|
@@ -335,13 +335,13 @@ Write instructions for using the skill and its bundled resources.
|
|
|
335
335
|
Once development of the skill is complete, it must be packaged into a distributable .skill file that gets shared with the user. The packaging process automatically validates the skill first to ensure it meets all requirements:
|
|
336
336
|
|
|
337
337
|
```bash
|
|
338
|
-
npx
|
|
338
|
+
npx tsx <SKILL_DIR>/scripts/package-skill.ts <path/to/skill-folder>
|
|
339
339
|
```
|
|
340
340
|
|
|
341
341
|
Optional output directory specification:
|
|
342
342
|
|
|
343
343
|
```bash
|
|
344
|
-
npx
|
|
344
|
+
npx tsx <SKILL_DIR>/scripts/package-skill.ts <path/to/skill-folder> ./dist
|
|
345
345
|
```
|
|
346
346
|
|
|
347
347
|
The packaging script will:
|
|
@@ -18,7 +18,7 @@ This skill helps you find other agents on the same Letta server.
|
|
|
18
18
|
## Script Usage
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
npx
|
|
21
|
+
npx tsx <SKILL_DIR>/scripts/find-agents.ts [options]
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
### Options
|
|
@@ -39,7 +39,7 @@ npx ts-node scripts/find-agents.ts [options]
|
|
|
39
39
|
Agents created by Letta Code are tagged with `origin:letta-code`. To find only Letta Code agents:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
npx
|
|
42
|
+
npx tsx <SKILL_DIR>/scripts/find-agents.ts --tags "origin:letta-code"
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
This is useful when the user is looking for agents they've worked with in Letta Code CLI sessions.
|
|
@@ -49,39 +49,39 @@ This is useful when the user is looking for agents they've worked with in Letta
|
|
|
49
49
|
If the user has agents created outside Letta Code (via ADE, SDK, etc.), search without the tag filter:
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
|
-
npx
|
|
52
|
+
npx tsx <SKILL_DIR>/scripts/find-agents.ts
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
## Examples
|
|
56
56
|
|
|
57
57
|
**List all agents (up to 20):**
|
|
58
58
|
```bash
|
|
59
|
-
npx
|
|
59
|
+
npx tsx <SKILL_DIR>/scripts/find-agents.ts
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
**Find agent by exact name:**
|
|
63
63
|
```bash
|
|
64
|
-
npx
|
|
64
|
+
npx tsx <SKILL_DIR>/scripts/find-agents.ts --name "ProjectX-v1"
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
**Search agents by name (fuzzy):**
|
|
68
68
|
```bash
|
|
69
|
-
npx
|
|
69
|
+
npx tsx <SKILL_DIR>/scripts/find-agents.ts --query "project"
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
**Find only Letta Code agents:**
|
|
73
73
|
```bash
|
|
74
|
-
npx
|
|
74
|
+
npx tsx <SKILL_DIR>/scripts/find-agents.ts --tags "origin:letta-code"
|
|
75
75
|
```
|
|
76
76
|
|
|
77
77
|
**Find agents with multiple tags:**
|
|
78
78
|
```bash
|
|
79
|
-
npx
|
|
79
|
+
npx tsx <SKILL_DIR>/scripts/find-agents.ts --tags "frontend,production" --match-all-tags
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
**Include memory blocks in results:**
|
|
83
83
|
```bash
|
|
84
|
-
npx
|
|
84
|
+
npx tsx <SKILL_DIR>/scripts/find-agents.ts --query "project" --include-blocks
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
## Output
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
#!/usr/bin/env npx
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
2
|
/**
|
|
3
3
|
* Find Agents - Search for agents with various filters
|
|
4
4
|
*
|
|
5
|
+
* This script is standalone and can be run outside the CLI process.
|
|
6
|
+
* It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
|
|
7
|
+
*
|
|
5
8
|
* Usage:
|
|
6
|
-
* npx
|
|
9
|
+
* npx tsx find-agents.ts [options]
|
|
7
10
|
*
|
|
8
11
|
* Options:
|
|
9
12
|
* --name <name> Exact name match
|
|
@@ -17,9 +20,17 @@
|
|
|
17
20
|
* Raw API response from GET /v1/agents
|
|
18
21
|
*/
|
|
19
22
|
|
|
20
|
-
import
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
+
import { readFileSync } from "node:fs";
|
|
24
|
+
import { createRequire } from "node:module";
|
|
25
|
+
import { homedir } from "node:os";
|
|
26
|
+
import { join } from "node:path";
|
|
27
|
+
|
|
28
|
+
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
|
29
|
+
// (ES module imports don't respect NODE_PATH, but require does)
|
|
30
|
+
const require = createRequire(import.meta.url);
|
|
31
|
+
const Letta = require("@letta-ai/letta-client")
|
|
32
|
+
.default as typeof import("@letta-ai/letta-client").default;
|
|
33
|
+
type LettaClient = InstanceType<typeof Letta>;
|
|
23
34
|
|
|
24
35
|
interface FindAgentsOptions {
|
|
25
36
|
name?: string;
|
|
@@ -30,6 +41,38 @@ interface FindAgentsOptions {
|
|
|
30
41
|
limit?: number;
|
|
31
42
|
}
|
|
32
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Get API key from env var or settings file
|
|
46
|
+
*/
|
|
47
|
+
function getApiKey(): string {
|
|
48
|
+
// First check env var (set by CLI's getShellEnv)
|
|
49
|
+
if (process.env.LETTA_API_KEY) {
|
|
50
|
+
return process.env.LETTA_API_KEY;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Fall back to settings file
|
|
54
|
+
const settingsPath = join(homedir(), ".letta", "settings.json");
|
|
55
|
+
try {
|
|
56
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
57
|
+
if (settings.env?.LETTA_API_KEY) {
|
|
58
|
+
return settings.env.LETTA_API_KEY;
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
// Settings file doesn't exist or is invalid
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
throw new Error(
|
|
65
|
+
"No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create a Letta client with auth from env/settings
|
|
71
|
+
*/
|
|
72
|
+
function createClient(): LettaClient {
|
|
73
|
+
return new Letta({ apiKey: getApiKey() });
|
|
74
|
+
}
|
|
75
|
+
|
|
33
76
|
/**
|
|
34
77
|
* Find agents matching the given criteria
|
|
35
78
|
* @param client - Letta client instance
|
|
@@ -37,7 +80,7 @@ interface FindAgentsOptions {
|
|
|
37
80
|
* @returns Array of agent objects from the API
|
|
38
81
|
*/
|
|
39
82
|
export async function findAgents(
|
|
40
|
-
client:
|
|
83
|
+
client: LettaClient,
|
|
41
84
|
options: FindAgentsOptions = {},
|
|
42
85
|
): Promise<Awaited<ReturnType<typeof client.agents.list>>> {
|
|
43
86
|
const params: Parameters<typeof client.agents.list>[0] = {
|
|
@@ -103,13 +146,13 @@ function parseArgs(args: string[]): FindAgentsOptions {
|
|
|
103
146
|
return options;
|
|
104
147
|
}
|
|
105
148
|
|
|
106
|
-
// CLI entry point
|
|
107
|
-
|
|
149
|
+
// CLI entry point - check if this file is being run directly
|
|
150
|
+
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
|
151
|
+
if (isMainModule) {
|
|
108
152
|
(async () => {
|
|
109
153
|
try {
|
|
110
154
|
const options = parseArgs(process.argv.slice(2));
|
|
111
|
-
|
|
112
|
-
const client = await getClient();
|
|
155
|
+
const client = createClient();
|
|
113
156
|
const result = await findAgents(client, options);
|
|
114
157
|
console.log(JSON.stringify(result, null, 2));
|
|
115
158
|
} catch (error) {
|
|
@@ -118,7 +161,7 @@ if (require.main === module) {
|
|
|
118
161
|
error instanceof Error ? error.message : String(error),
|
|
119
162
|
);
|
|
120
163
|
console.error(`
|
|
121
|
-
Usage: npx
|
|
164
|
+
Usage: npx tsx find-agents.ts [options]
|
|
122
165
|
|
|
123
166
|
Options:
|
|
124
167
|
--name <name> Exact name match
|
|
@@ -60,7 +60,7 @@ Example: "What's the ID of the agent you want to migrate memory from?"
|
|
|
60
60
|
Inspect what memory blocks the source agent has:
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
|
-
npx
|
|
63
|
+
npx tsx <SKILL_DIR>/scripts/get-agent-blocks.ts --agent-id <source-agent-id>
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
This shows each block's ID, label, description, and value.
|
|
@@ -71,14 +71,14 @@ For each block you want to migrate, choose copy or share:
|
|
|
71
71
|
|
|
72
72
|
**To Copy (create independent block):**
|
|
73
73
|
```bash
|
|
74
|
-
npx
|
|
74
|
+
npx tsx <SKILL_DIR>/scripts/copy-block.ts --block-id <block-id> [--label <new-label>]
|
|
75
75
|
```
|
|
76
76
|
|
|
77
77
|
Use `--label` if you already have a block with that label (e.g., `--label project-imported`).
|
|
78
78
|
|
|
79
79
|
**To Share (attach existing block):**
|
|
80
80
|
```bash
|
|
81
|
-
npx
|
|
81
|
+
npx tsx <SKILL_DIR>/scripts/attach-block.ts --block-id <block-id>
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
Add `--read-only` flag to share to make this agent unable to modify the block.
|
|
@@ -113,20 +113,20 @@ Scenario: You're a new agent and want to inherit memory from an existing agent "
|
|
|
113
113
|
|
|
114
114
|
2. **List its blocks:**
|
|
115
115
|
```bash
|
|
116
|
-
npx
|
|
116
|
+
npx tsx <SKILL_DIR>/scripts/get-agent-blocks.ts --agent-id agent-abc123
|
|
117
117
|
# Shows: project (block-def456), human (block-ghi789), persona (block-jkl012)
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
3. **Copy project knowledge to yourself:**
|
|
121
121
|
```bash
|
|
122
122
|
# If you don't have a 'project' block yet:
|
|
123
|
-
npx
|
|
123
|
+
npx tsx <SKILL_DIR>/scripts/copy-block.ts --block-id block-def456
|
|
124
124
|
|
|
125
125
|
# If you already have 'project', use --label to rename:
|
|
126
|
-
npx
|
|
126
|
+
npx tsx <SKILL_DIR>/scripts/copy-block.ts --block-id block-def456 --label project-v1
|
|
127
127
|
```
|
|
128
128
|
|
|
129
129
|
4. **Optionally share human preferences (read-only):**
|
|
130
130
|
```bash
|
|
131
|
-
npx
|
|
131
|
+
npx tsx <SKILL_DIR>/scripts/attach-block.ts --block-id block-ghi789 --read-only
|
|
132
132
|
```
|
|
@@ -1,24 +1,79 @@
|
|
|
1
|
-
#!/usr/bin/env npx
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
2
|
/**
|
|
3
3
|
* Attach Block - Attaches an existing memory block to an agent (sharing)
|
|
4
4
|
*
|
|
5
|
+
* This script is standalone and can be run outside the CLI process.
|
|
6
|
+
* It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
|
|
7
|
+
* It reads agent ID from LETTA_AGENT_ID env var or --agent-id arg.
|
|
8
|
+
*
|
|
5
9
|
* Usage:
|
|
6
|
-
* npx
|
|
10
|
+
* npx tsx attach-block.ts --block-id <block-id> [--agent-id <agent-id>] [--read-only]
|
|
7
11
|
*
|
|
8
12
|
* This attaches an existing block to another agent, making it shared.
|
|
9
13
|
* Changes to the block will be visible to all agents that have it attached.
|
|
10
14
|
*
|
|
11
15
|
* Options:
|
|
12
|
-
* --
|
|
16
|
+
* --agent-id Target agent ID (overrides LETTA_AGENT_ID env var)
|
|
17
|
+
* --read-only Target agent can read but not modify the block
|
|
13
18
|
*
|
|
14
19
|
* Output:
|
|
15
20
|
* Raw API response from the attach operation
|
|
16
21
|
*/
|
|
17
22
|
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
23
|
+
import { readFileSync } from "node:fs";
|
|
24
|
+
import { createRequire } from "node:module";
|
|
25
|
+
import { homedir } from "node:os";
|
|
26
|
+
import { join } from "node:path";
|
|
27
|
+
|
|
28
|
+
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
|
29
|
+
// (ES module imports don't respect NODE_PATH, but require does)
|
|
30
|
+
const require = createRequire(import.meta.url);
|
|
31
|
+
const Letta = require("@letta-ai/letta-client")
|
|
32
|
+
.default as typeof import("@letta-ai/letta-client").default;
|
|
33
|
+
type LettaClient = InstanceType<typeof Letta>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get API key from env var or settings file
|
|
37
|
+
*/
|
|
38
|
+
function getApiKey(): string {
|
|
39
|
+
if (process.env.LETTA_API_KEY) {
|
|
40
|
+
return process.env.LETTA_API_KEY;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const settingsPath = join(homedir(), ".letta", "settings.json");
|
|
44
|
+
try {
|
|
45
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
46
|
+
if (settings.env?.LETTA_API_KEY) {
|
|
47
|
+
return settings.env.LETTA_API_KEY;
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
// Settings file doesn't exist or is invalid
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
throw new Error(
|
|
54
|
+
"No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get agent ID from CLI arg, env var, or throw
|
|
60
|
+
*/
|
|
61
|
+
function getAgentId(cliArg?: string): string {
|
|
62
|
+
if (cliArg) return cliArg;
|
|
63
|
+
if (process.env.LETTA_AGENT_ID) {
|
|
64
|
+
return process.env.LETTA_AGENT_ID;
|
|
65
|
+
}
|
|
66
|
+
throw new Error(
|
|
67
|
+
"No agent ID provided. Use --agent-id or ensure LETTA_AGENT_ID env var is set.",
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Create a Letta client with auth from env/settings
|
|
73
|
+
*/
|
|
74
|
+
function createClient(): LettaClient {
|
|
75
|
+
return new Letta({ apiKey: getApiKey() });
|
|
76
|
+
}
|
|
22
77
|
|
|
23
78
|
/**
|
|
24
79
|
* Attach an existing block to the current agent (sharing it)
|
|
@@ -29,13 +84,13 @@ import { settingsManager } from "../../../../settings-manager";
|
|
|
29
84
|
* @returns API response from the attach operation
|
|
30
85
|
*/
|
|
31
86
|
export async function attachBlock(
|
|
32
|
-
client:
|
|
87
|
+
client: LettaClient,
|
|
33
88
|
blockId: string,
|
|
34
89
|
readOnly = false,
|
|
35
90
|
targetAgentId?: string,
|
|
36
91
|
): Promise<Awaited<ReturnType<typeof client.agents.blocks.attach>>> {
|
|
37
92
|
// Get current agent ID (the agent calling this script) or use provided ID
|
|
38
|
-
const currentAgentId = targetAgentId
|
|
93
|
+
const currentAgentId = getAgentId(targetAgentId);
|
|
39
94
|
|
|
40
95
|
const result = await client.agents.blocks.attach(blockId, {
|
|
41
96
|
agent_id: currentAgentId,
|
|
@@ -58,8 +113,10 @@ export async function attachBlock(
|
|
|
58
113
|
function parseArgs(args: string[]): {
|
|
59
114
|
blockId: string;
|
|
60
115
|
readOnly: boolean;
|
|
116
|
+
agentId?: string;
|
|
61
117
|
} {
|
|
62
118
|
const blockIdIndex = args.indexOf("--block-id");
|
|
119
|
+
const agentIdIndex = args.indexOf("--agent-id");
|
|
63
120
|
const readOnly = args.includes("--read-only");
|
|
64
121
|
|
|
65
122
|
if (blockIdIndex === -1 || blockIdIndex + 1 >= args.length) {
|
|
@@ -69,17 +126,21 @@ function parseArgs(args: string[]): {
|
|
|
69
126
|
return {
|
|
70
127
|
blockId: args[blockIdIndex + 1] as string,
|
|
71
128
|
readOnly,
|
|
129
|
+
agentId:
|
|
130
|
+
agentIdIndex !== -1 && agentIdIndex + 1 < args.length
|
|
131
|
+
? (args[agentIdIndex + 1] as string)
|
|
132
|
+
: undefined,
|
|
72
133
|
};
|
|
73
134
|
}
|
|
74
135
|
|
|
75
|
-
// CLI entry point
|
|
76
|
-
|
|
136
|
+
// CLI entry point - check if this file is being run directly
|
|
137
|
+
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
|
138
|
+
if (isMainModule) {
|
|
77
139
|
(async () => {
|
|
78
140
|
try {
|
|
79
|
-
const { blockId, readOnly } = parseArgs(process.argv.slice(2));
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
const result = await attachBlock(client, blockId, readOnly);
|
|
141
|
+
const { blockId, readOnly, agentId } = parseArgs(process.argv.slice(2));
|
|
142
|
+
const client = createClient();
|
|
143
|
+
const result = await attachBlock(client, blockId, readOnly, agentId);
|
|
83
144
|
console.log(JSON.stringify(result, null, 2));
|
|
84
145
|
} catch (error) {
|
|
85
146
|
console.error(
|
|
@@ -91,7 +152,7 @@ if (require.main === module) {
|
|
|
91
152
|
error.message.includes("Missing required argument")
|
|
92
153
|
) {
|
|
93
154
|
console.error(
|
|
94
|
-
"\nUsage: npx
|
|
155
|
+
"\nUsage: npx tsx attach-block.ts --block-id <block-id> [--agent-id <agent-id>] [--read-only]",
|
|
95
156
|
);
|
|
96
157
|
}
|
|
97
158
|
process.exit(1);
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
#!/usr/bin/env npx
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
2
|
/**
|
|
3
3
|
* Copy Block - Copies a memory block to create a new independent block for the current agent
|
|
4
4
|
*
|
|
5
|
+
* This script is standalone and can be run outside the CLI process.
|
|
6
|
+
* It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
|
|
7
|
+
* It reads agent ID from LETTA_AGENT_ID env var or --agent-id arg.
|
|
8
|
+
*
|
|
5
9
|
* Usage:
|
|
6
|
-
* npx
|
|
10
|
+
* npx tsx copy-block.ts --block-id <block-id> [--label <new-label>] [--agent-id <agent-id>]
|
|
7
11
|
*
|
|
8
12
|
* Options:
|
|
9
|
-
* --label
|
|
13
|
+
* --label Override the block label (required if you already have a block with that label)
|
|
14
|
+
* --agent-id Target agent ID (overrides LETTA_AGENT_ID env var)
|
|
10
15
|
*
|
|
11
16
|
* This creates a new block with the same content as the source block,
|
|
12
17
|
* then attaches it to the current agent. Changes to the new block
|
|
@@ -16,17 +21,65 @@
|
|
|
16
21
|
* Raw API response from each step (retrieve, create, attach)
|
|
17
22
|
*/
|
|
18
23
|
|
|
19
|
-
import
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
24
|
+
import { readFileSync } from "node:fs";
|
|
25
|
+
import { createRequire } from "node:module";
|
|
26
|
+
import { homedir } from "node:os";
|
|
27
|
+
import { join } from "node:path";
|
|
28
|
+
|
|
29
|
+
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
|
30
|
+
// (ES module imports don't respect NODE_PATH, but require does)
|
|
31
|
+
const require = createRequire(import.meta.url);
|
|
32
|
+
const Letta = require("@letta-ai/letta-client")
|
|
33
|
+
.default as typeof import("@letta-ai/letta-client").default;
|
|
34
|
+
type LettaClient = InstanceType<typeof Letta>;
|
|
23
35
|
|
|
24
36
|
interface CopyBlockResult {
|
|
25
|
-
sourceBlock: Awaited<ReturnType<
|
|
26
|
-
newBlock: Awaited<ReturnType<
|
|
27
|
-
attachResult: Awaited<
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
sourceBlock: Awaited<ReturnType<LettaClient["blocks"]["retrieve"]>>;
|
|
38
|
+
newBlock: Awaited<ReturnType<LettaClient["blocks"]["create"]>>;
|
|
39
|
+
attachResult: Awaited<ReturnType<LettaClient["agents"]["blocks"]["attach"]>>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get API key from env var or settings file
|
|
44
|
+
*/
|
|
45
|
+
function getApiKey(): string {
|
|
46
|
+
if (process.env.LETTA_API_KEY) {
|
|
47
|
+
return process.env.LETTA_API_KEY;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const settingsPath = join(homedir(), ".letta", "settings.json");
|
|
51
|
+
try {
|
|
52
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
53
|
+
if (settings.env?.LETTA_API_KEY) {
|
|
54
|
+
return settings.env.LETTA_API_KEY;
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
// Settings file doesn't exist or is invalid
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
throw new Error(
|
|
61
|
+
"No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get agent ID from CLI arg, env var, or throw
|
|
67
|
+
*/
|
|
68
|
+
function getAgentId(cliArg?: string): string {
|
|
69
|
+
if (cliArg) return cliArg;
|
|
70
|
+
if (process.env.LETTA_AGENT_ID) {
|
|
71
|
+
return process.env.LETTA_AGENT_ID;
|
|
72
|
+
}
|
|
73
|
+
throw new Error(
|
|
74
|
+
"No agent ID provided. Use --agent-id or ensure LETTA_AGENT_ID env var is set.",
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Create a Letta client with auth from env/settings
|
|
80
|
+
*/
|
|
81
|
+
function createClient(): LettaClient {
|
|
82
|
+
return new Letta({ apiKey: getApiKey() });
|
|
30
83
|
}
|
|
31
84
|
|
|
32
85
|
/**
|
|
@@ -37,12 +90,12 @@ interface CopyBlockResult {
|
|
|
37
90
|
* @returns Object containing source block, new block, and attach result
|
|
38
91
|
*/
|
|
39
92
|
export async function copyBlock(
|
|
40
|
-
client:
|
|
93
|
+
client: LettaClient,
|
|
41
94
|
blockId: string,
|
|
42
95
|
options?: { labelOverride?: string; targetAgentId?: string },
|
|
43
96
|
): Promise<CopyBlockResult> {
|
|
44
97
|
// Get current agent ID (the agent calling this script) or use provided ID
|
|
45
|
-
const currentAgentId = options?.targetAgentId
|
|
98
|
+
const currentAgentId = getAgentId(options?.targetAgentId);
|
|
46
99
|
|
|
47
100
|
// 1. Get source block details
|
|
48
101
|
const sourceBlock = await client.blocks.retrieve(blockId);
|
|
@@ -63,9 +116,14 @@ export async function copyBlock(
|
|
|
63
116
|
return { sourceBlock, newBlock, attachResult };
|
|
64
117
|
}
|
|
65
118
|
|
|
66
|
-
function parseArgs(args: string[]): {
|
|
119
|
+
function parseArgs(args: string[]): {
|
|
120
|
+
blockId: string;
|
|
121
|
+
label?: string;
|
|
122
|
+
agentId?: string;
|
|
123
|
+
} {
|
|
67
124
|
const blockIdIndex = args.indexOf("--block-id");
|
|
68
125
|
const labelIndex = args.indexOf("--label");
|
|
126
|
+
const agentIdIndex = args.indexOf("--agent-id");
|
|
69
127
|
|
|
70
128
|
if (blockIdIndex === -1 || blockIdIndex + 1 >= args.length) {
|
|
71
129
|
throw new Error("Missing required argument: --block-id <block-id>");
|
|
@@ -77,17 +135,24 @@ function parseArgs(args: string[]): { blockId: string; label?: string } {
|
|
|
77
135
|
labelIndex !== -1 && labelIndex + 1 < args.length
|
|
78
136
|
? (args[labelIndex + 1] as string)
|
|
79
137
|
: undefined,
|
|
138
|
+
agentId:
|
|
139
|
+
agentIdIndex !== -1 && agentIdIndex + 1 < args.length
|
|
140
|
+
? (args[agentIdIndex + 1] as string)
|
|
141
|
+
: undefined,
|
|
80
142
|
};
|
|
81
143
|
}
|
|
82
144
|
|
|
83
|
-
// CLI entry point
|
|
84
|
-
|
|
145
|
+
// CLI entry point - check if this file is being run directly
|
|
146
|
+
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
|
147
|
+
if (isMainModule) {
|
|
85
148
|
(async () => {
|
|
86
149
|
try {
|
|
87
|
-
const { blockId, label } = parseArgs(process.argv.slice(2));
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
150
|
+
const { blockId, label, agentId } = parseArgs(process.argv.slice(2));
|
|
151
|
+
const client = createClient();
|
|
152
|
+
const result = await copyBlock(client, blockId, {
|
|
153
|
+
labelOverride: label,
|
|
154
|
+
targetAgentId: agentId,
|
|
155
|
+
});
|
|
91
156
|
console.log(JSON.stringify(result, null, 2));
|
|
92
157
|
} catch (error) {
|
|
93
158
|
console.error(
|
|
@@ -99,7 +164,7 @@ if (require.main === module) {
|
|
|
99
164
|
error.message.includes("Missing required argument")
|
|
100
165
|
) {
|
|
101
166
|
console.error(
|
|
102
|
-
"\nUsage: npx
|
|
167
|
+
"\nUsage: npx tsx copy-block.ts --block-id <block-id> [--label <new-label>] [--agent-id <agent-id>]",
|
|
103
168
|
);
|
|
104
169
|
}
|
|
105
170
|
process.exit(1);
|
|
@@ -1,17 +1,58 @@
|
|
|
1
|
-
#!/usr/bin/env npx
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
2
|
/**
|
|
3
3
|
* Get Agent Blocks - Retrieves memory blocks from a specific agent
|
|
4
4
|
*
|
|
5
|
+
* This script is standalone and can be run outside the CLI process.
|
|
6
|
+
* It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
|
|
7
|
+
*
|
|
5
8
|
* Usage:
|
|
6
|
-
* npx
|
|
9
|
+
* npx tsx get-agent-blocks.ts --agent-id <agent-id>
|
|
7
10
|
*
|
|
8
11
|
* Output:
|
|
9
12
|
* Raw API response from GET /v1/agents/{id}/core-memory/blocks
|
|
10
13
|
*/
|
|
11
14
|
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
+
import { readFileSync } from "node:fs";
|
|
16
|
+
import { createRequire } from "node:module";
|
|
17
|
+
import { homedir } from "node:os";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
|
|
20
|
+
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
|
21
|
+
// (ES module imports don't respect NODE_PATH, but require does)
|
|
22
|
+
const require = createRequire(import.meta.url);
|
|
23
|
+
const Letta = require("@letta-ai/letta-client")
|
|
24
|
+
.default as typeof import("@letta-ai/letta-client").default;
|
|
25
|
+
type LettaClient = InstanceType<typeof Letta>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get API key from env var or settings file
|
|
29
|
+
*/
|
|
30
|
+
function getApiKey(): string {
|
|
31
|
+
if (process.env.LETTA_API_KEY) {
|
|
32
|
+
return process.env.LETTA_API_KEY;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const settingsPath = join(homedir(), ".letta", "settings.json");
|
|
36
|
+
try {
|
|
37
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
38
|
+
if (settings.env?.LETTA_API_KEY) {
|
|
39
|
+
return settings.env.LETTA_API_KEY;
|
|
40
|
+
}
|
|
41
|
+
} catch {
|
|
42
|
+
// Settings file doesn't exist or is invalid
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
throw new Error(
|
|
46
|
+
"No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create a Letta client with auth from env/settings
|
|
52
|
+
*/
|
|
53
|
+
function createClient(): LettaClient {
|
|
54
|
+
return new Letta({ apiKey: getApiKey() });
|
|
55
|
+
}
|
|
15
56
|
|
|
16
57
|
/**
|
|
17
58
|
* Get memory blocks for a specific agent
|
|
@@ -20,7 +61,7 @@ import { settingsManager } from "../../../../settings-manager";
|
|
|
20
61
|
* @returns Array of block objects from the API
|
|
21
62
|
*/
|
|
22
63
|
export async function getAgentBlocks(
|
|
23
|
-
client:
|
|
64
|
+
client: LettaClient,
|
|
24
65
|
agentId: string,
|
|
25
66
|
): Promise<Awaited<ReturnType<typeof client.agents.blocks.list>>> {
|
|
26
67
|
return await client.agents.blocks.list(agentId);
|
|
@@ -34,13 +75,13 @@ function parseArgs(args: string[]): { agentId: string } {
|
|
|
34
75
|
return { agentId: args[agentIdIndex + 1] as string };
|
|
35
76
|
}
|
|
36
77
|
|
|
37
|
-
// CLI entry point
|
|
38
|
-
|
|
78
|
+
// CLI entry point - check if this file is being run directly
|
|
79
|
+
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
|
80
|
+
if (isMainModule) {
|
|
39
81
|
(async () => {
|
|
40
82
|
try {
|
|
41
83
|
const { agentId } = parseArgs(process.argv.slice(2));
|
|
42
|
-
|
|
43
|
-
const client = await getClient();
|
|
84
|
+
const client = createClient();
|
|
44
85
|
const result = await getAgentBlocks(client, agentId);
|
|
45
86
|
console.log(JSON.stringify(result, null, 2));
|
|
46
87
|
} catch (error) {
|
|
@@ -53,7 +94,7 @@ if (require.main === module) {
|
|
|
53
94
|
error.message.includes("Missing required argument")
|
|
54
95
|
) {
|
|
55
96
|
console.error(
|
|
56
|
-
"\nUsage: npx
|
|
97
|
+
"\nUsage: npx tsx get-agent-blocks.ts --agent-id <agent-id>",
|
|
57
98
|
);
|
|
58
99
|
}
|
|
59
100
|
process.exit(1);
|
|
@@ -26,9 +26,16 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { readFileSync } from "node:fs";
|
|
29
|
+
import { createRequire } from "node:module";
|
|
29
30
|
import { homedir } from "node:os";
|
|
30
31
|
import { join } from "node:path";
|
|
31
|
-
|
|
32
|
+
|
|
33
|
+
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
|
34
|
+
// (ES module imports don't respect NODE_PATH, but require does)
|
|
35
|
+
const require = createRequire(import.meta.url);
|
|
36
|
+
const Letta = require("@letta-ai/letta-client")
|
|
37
|
+
.default as typeof import("@letta-ai/letta-client").default;
|
|
38
|
+
type LettaClient = InstanceType<typeof Letta>;
|
|
32
39
|
|
|
33
40
|
interface GetMessagesOptions {
|
|
34
41
|
startDate?: string;
|
|
@@ -85,7 +92,7 @@ function getAgentId(cliArg?: string): string {
|
|
|
85
92
|
/**
|
|
86
93
|
* Create a Letta client with auth from env/settings
|
|
87
94
|
*/
|
|
88
|
-
function createClient():
|
|
95
|
+
function createClient(): LettaClient {
|
|
89
96
|
return new Letta({ apiKey: getApiKey() });
|
|
90
97
|
}
|
|
91
98
|
|
|
@@ -96,7 +103,7 @@ function createClient(): Letta {
|
|
|
96
103
|
* @returns Array of messages in chronological order
|
|
97
104
|
*/
|
|
98
105
|
export async function getMessages(
|
|
99
|
-
client:
|
|
106
|
+
client: LettaClient,
|
|
100
107
|
options: GetMessagesOptions = {},
|
|
101
108
|
): Promise<unknown[]> {
|
|
102
109
|
const agentId = getAgentId(options.agentId);
|
|
@@ -24,9 +24,16 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import { readFileSync } from "node:fs";
|
|
27
|
+
import { createRequire } from "node:module";
|
|
27
28
|
import { homedir } from "node:os";
|
|
28
29
|
import { join } from "node:path";
|
|
29
|
-
|
|
30
|
+
|
|
31
|
+
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
|
32
|
+
// (ES module imports don't respect NODE_PATH, but require does)
|
|
33
|
+
const require = createRequire(import.meta.url);
|
|
34
|
+
const Letta = require("@letta-ai/letta-client")
|
|
35
|
+
.default as typeof import("@letta-ai/letta-client").default;
|
|
36
|
+
type LettaClient = InstanceType<typeof Letta>;
|
|
30
37
|
|
|
31
38
|
interface SearchMessagesOptions {
|
|
32
39
|
query: string;
|
|
@@ -83,7 +90,7 @@ function getAgentId(cliArg?: string): string {
|
|
|
83
90
|
/**
|
|
84
91
|
* Create a Letta client with auth from env/settings
|
|
85
92
|
*/
|
|
86
|
-
function createClient():
|
|
93
|
+
function createClient(): LettaClient {
|
|
87
94
|
return new Letta({ apiKey: getApiKey() });
|
|
88
95
|
}
|
|
89
96
|
|
|
@@ -94,7 +101,7 @@ function createClient(): Letta {
|
|
|
94
101
|
* @returns Array of search results with scores
|
|
95
102
|
*/
|
|
96
103
|
export async function searchMessages(
|
|
97
|
-
client:
|
|
104
|
+
client: LettaClient,
|
|
98
105
|
options: SearchMessagesOptions,
|
|
99
106
|
): Promise<Awaited<ReturnType<typeof client.messages.search>>> {
|
|
100
107
|
// Default to current agent unless --all-agents is specified
|