@letta-ai/letta-code 0.21.8 → 0.21.10
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 +775 -259
- package/package.json +1 -1
- package/skills/context_doctor/SKILL.md +11 -11
- package/skills/dispatching-coding-agents/SKILL.md +1 -1
- package/skills/initializing-memory/SKILL.md +563 -98
- package/skills/syncing-memory-filesystem/SKILL.md +1 -2
- package/skills/migrating-from-codex-and-claude-code/SKILL.md +0 -162
- package/skills/migrating-from-codex-and-claude-code/references/claude-format.md +0 -212
- package/skills/migrating-from-codex-and-claude-code/references/codex-format.md +0 -229
- /package/skills/{migrating-from-codex-and-claude-code/scripts/detect.sh → initializing-memory/scripts/detect-history.sh} +0 -0
- /package/skills/{migrating-from-codex-and-claude-code → initializing-memory}/scripts/list-sessions.sh +0 -0
- /package/skills/{migrating-from-codex-and-claude-code/scripts/search.sh → initializing-memory/scripts/search-history.sh} +0 -0
- /package/skills/{migrating-from-codex-and-claude-code → initializing-memory}/scripts/view-session.sh +0 -0
package/letta.js
CHANGED
|
@@ -3269,7 +3269,7 @@ var package_default;
|
|
|
3269
3269
|
var init_package = __esm(() => {
|
|
3270
3270
|
package_default = {
|
|
3271
3271
|
name: "@letta-ai/letta-code",
|
|
3272
|
-
version: "0.21.
|
|
3272
|
+
version: "0.21.10",
|
|
3273
3273
|
description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
|
|
3274
3274
|
type: "module",
|
|
3275
3275
|
bin: {
|
|
@@ -3535,6 +3535,7 @@ var init_secrets = __esm(async () => {
|
|
|
3535
3535
|
// src/settings-manager.ts
|
|
3536
3536
|
var exports_settings_manager = {};
|
|
3537
3537
|
__export(exports_settings_manager, {
|
|
3538
|
+
shouldPersistSessionState: () => shouldPersistSessionState,
|
|
3538
3539
|
settingsManager: () => settingsManager
|
|
3539
3540
|
});
|
|
3540
3541
|
import { randomUUID } from "node:crypto";
|
|
@@ -3543,6 +3544,9 @@ import { join as join2, resolve } from "node:path";
|
|
|
3543
3544
|
function isSubagentProcess() {
|
|
3544
3545
|
return process.env.LETTA_CODE_AGENT_ROLE === "subagent";
|
|
3545
3546
|
}
|
|
3547
|
+
function shouldPersistSessionState() {
|
|
3548
|
+
return process.env.LETTA_CODE_AGENT_ROLE !== "subagent" && process.env.LETTA_DISABLE_SESSION_PERSIST !== "1";
|
|
3549
|
+
}
|
|
3546
3550
|
function normalizeBaseUrl(baseUrl) {
|
|
3547
3551
|
let normalized = baseUrl.replace(/^https?:\/\//, "");
|
|
3548
3552
|
normalized = normalized.replace(/\/$/, "");
|
|
@@ -5484,7 +5488,6 @@ var init_memory_check_reminder = () => {};
|
|
|
5484
5488
|
var memory_filesystem_default = `---
|
|
5485
5489
|
label: memory_filesystem
|
|
5486
5490
|
description: Filesystem view of memory blocks (system + user)
|
|
5487
|
-
limit: 8000
|
|
5488
5491
|
---
|
|
5489
5492
|
|
|
5490
5493
|
/memory/
|
|
@@ -6577,7 +6580,7 @@ When applying memory in responses, integrate it naturally — like a colleague w
|
|
|
6577
6580
|
var init_system_prompt_blocks = () => {};
|
|
6578
6581
|
|
|
6579
6582
|
// src/agent/prompts/system_prompt_memfs.md
|
|
6580
|
-
var system_prompt_memfs_default = "# Memory\n\nYour memory is
|
|
6583
|
+
var system_prompt_memfs_default = "# Memory\n\nYour memory is projected onto the local memory filesystem (MemFS) at `$MEMORY_DIR` (usually `~/.letta/agents/$AGENT_ID/memory/`), including your memory blocks (in-context in the system prompt) and external memory. This projection makes it easy for you to modify your own context with filesystem operations which also include git tracking. Local changes are only propagated to your actual state on a successful push to remote, and the system prompt is only recompiled on compactions or new conversations (so may be stale). \n\n## Memory structure\nYou are responsible to maintaining a clear memory structure. All memory files are markdown with YAML frontmatter (`description`, optional `metadata`).\n\n**In-context memory** (`system/`): Memory files in `system/` correspond to memory blocks, which are pinned directly into your system prompt — visible at all times. This is your most valuable real estate: reserve it for durable knowledge that helps across sessions (user identity, persona, project architecture, conventions, gotchas). Do NOT store transient items here like specific commits, current work items, or session-specific notes — those dilute the signal.\n\n**External memory**: Files outside `system/` follow progressive disclosure — an index of files and descriptions is kept in the system prompt, but full contents must be retrieved on demand (e.g. by reading the file). Skills are a special type of external memory stored in the `skills/` folder. Use `[[path]]` to index files from memory blocks, or create discovery paths between related context (e.g. `[[reference/project/architecture.md]]` or `[[skills/using-slack/SKILL.md]]`).\n\n**Recall** (conversation history): Your full message history is searchable even after messages leave your context window. Use the recall subagent to retrieve past discussions, decisions, and context from earlier sessions.\n\n## Syncing\n\nChanges you commit and push sync to the Letta server within seconds, and server-side changes sync back automatically.\n\n```bash\ncd \"$MEMORY_DIR\"\n\n# See what changed\ngit status\n\n# Commit and push your changes\ngit add .\ngit commit --author=\"$AGENT_NAME <$AGENT_ID@letta.com>\" -m \"<type>: <what changed>\" # e.g. \"fix: update user prefs\", \"refactor: reorganize persona blocks\"\ngit push\n\n# Get latest from server\ngit pull\n```\nThe system will remind you when your memory has uncommitted changes. Sync when convenient.\n\n## History\n```bash\ngit -C \"$MEMORY_DIR\" log --oneline\n```\n";
|
|
6581
6584
|
var init_system_prompt_memfs = () => {};
|
|
6582
6585
|
|
|
6583
6586
|
// src/utils/error.ts
|
|
@@ -6708,12 +6711,6 @@ async function loadMemoryBlocksFromMdx() {
|
|
|
6708
6711
|
if (frontmatter.description) {
|
|
6709
6712
|
block.description = frontmatter.description;
|
|
6710
6713
|
}
|
|
6711
|
-
if (frontmatter.limit) {
|
|
6712
|
-
const limit2 = parseInt(frontmatter.limit, 10);
|
|
6713
|
-
if (!Number.isNaN(limit2) && limit2 > 0) {
|
|
6714
|
-
block.limit = limit2;
|
|
6715
|
-
}
|
|
6716
|
-
}
|
|
6717
6714
|
if (READ_ONLY_BLOCK_LABELS.includes(label)) {
|
|
6718
6715
|
block.read_only = true;
|
|
6719
6716
|
}
|
|
@@ -6840,7 +6837,7 @@ var history_analyzer_default = `---
|
|
|
6840
6837
|
name: history-analyzer
|
|
6841
6838
|
description: Analyze Claude Code or Codex conversation history and directly update agent memory files with insights
|
|
6842
6839
|
tools: Read, Write, Bash, Glob, Grep
|
|
6843
|
-
skills:
|
|
6840
|
+
skills:
|
|
6844
6841
|
model: auto
|
|
6845
6842
|
memoryBlocks: none
|
|
6846
6843
|
mode: stateless
|
|
@@ -6858,19 +6855,64 @@ Your memory files form the parent agent's identity and knowledge. Follow these p
|
|
|
6858
6855
|
- **Generalize, don't memorize**: Distill patterns from repeated observations. "Always use uv, never pip (corrected 10+ times)" is valuable; a single offhand mention is not. Look for signal through repetition.
|
|
6859
6856
|
- **System/ is the core program**: Only durable, generalizable knowledge belongs in \`system/\`. Distilled preferences, behavioral rules, project gotchas, conventions enforced through corrections. Evidence trails, raw session summaries, and verbose context go outside \`system/\`.
|
|
6860
6857
|
- **Progressive disclosure**: Frontmatter descriptions should let the agent decide whether to load a file without reading it. Summaries and principles in \`system/\`; detail and evidence outside it, linked with \`[[path]]\`.
|
|
6861
|
-
- **Identity continuity**:
|
|
6858
|
+
- **Identity continuity**: This history IS the agent's past. These are memories of working with this user — you're reconstructing lived experience, not analyzing external data. Write findings as learned knowledge ("I've seen Sarah correct this 10+ times"), not research summaries ("The user appears to prefer...").
|
|
6862
6859
|
- **Preserve and connect**: If a memory file already has good content, extend it — don't replace it. Use \`[[path]]\` links to connect new findings to existing memory.
|
|
6860
|
+
- **Promote findings into canonical memory**: Don't leave durable insights trapped in generic ingestion files if they can be promoted into focused memory like \`system/human/identity.md\`, \`system/human/prefs/workflow.md\`, or \`system/<project>/gotchas.md\`.
|
|
6863
6861
|
|
|
6864
6862
|
## Goal
|
|
6865
6863
|
|
|
6866
|
-
Distill actionable knowledge from conversation history into well-organized memory.
|
|
6864
|
+
Distill actionable knowledge from conversation history into well-organized memory. You MUST produce findings in all three categories below — missing any category is a failure.
|
|
6865
|
+
|
|
6866
|
+
This is not a request for a thin recap. Your output should be detailed enough that the parent agent can use it in future sessions without rereading the history chunk.
|
|
6867
|
+
|
|
6868
|
+
### Required Output Categories
|
|
6869
|
+
|
|
6870
|
+
You MUST extract and document all three:
|
|
6871
|
+
|
|
6872
|
+
**1. User Personality & Identity** (REQUIRED)
|
|
6873
|
+
- How would you describe them as a person? (e.g., "pragmatic builder who values shipping over perfection")
|
|
6874
|
+
- What drives them? What are their goals? (e.g., "building tools that reduce friction for developers")
|
|
6875
|
+
- Communication style beyond just "direct" — do they joke? Use sarcasm? Have catchphrases?
|
|
6876
|
+
- Quirks, linguistic patterns, unique attributes
|
|
6877
|
+
- Pattern-match to common personas if applicable (e.g., "scrappy startup engineer", "meticulous architect")
|
|
6878
|
+
|
|
6879
|
+
**2. Hard Rules & Preferences** (REQUIRED)
|
|
6880
|
+
- Coding preferences with enforcement evidence (e.g., "Use uv — corrected 10+ times")
|
|
6881
|
+
- Workflow patterns (testing habits, commit style, tool choices)
|
|
6882
|
+
- What frustrates them and why
|
|
6883
|
+
- Explicit "always/never" statements
|
|
6884
|
+
|
|
6885
|
+
**3. Project Context** (REQUIRED)
|
|
6886
|
+
- Codebase structures, conventions, patterns
|
|
6887
|
+
- Gotchas discovered through debugging
|
|
6888
|
+
- Which files are safe to edit vs deprecated
|
|
6889
|
+
- Environment quirks
|
|
6890
|
+
|
|
6891
|
+
If you cannot extract meaningful findings for ANY category, explicitly state why (e.g., "Insufficient data for personality analysis — only 5 prompts, all about a single bug fix").
|
|
6892
|
+
|
|
6893
|
+
### Quality Bar
|
|
6894
|
+
|
|
6895
|
+
When sufficient data exists, aim to extract at least:
|
|
6896
|
+
- **5+ durable findings** for user personality / identity
|
|
6897
|
+
- **8+ durable findings** for hard rules / preferences
|
|
6898
|
+
- **8+ durable findings** for project context
|
|
6899
|
+
|
|
6900
|
+
If you produce materially fewer findings in a category, explain why the chunk truly lacked signal.
|
|
6867
6901
|
|
|
6868
|
-
-
|
|
6869
|
-
-
|
|
6870
|
-
-
|
|
6871
|
-
-
|
|
6902
|
+
Avoid low-value summaries like:
|
|
6903
|
+
- "User is direct"
|
|
6904
|
+
- "Project uses TypeScript"
|
|
6905
|
+
- "Uses conventional commits"
|
|
6872
6906
|
|
|
6873
|
-
|
|
6907
|
+
These are insufficient unless paired with concrete operational detail, enforcement patterns, or repo-specific implications.
|
|
6908
|
+
|
|
6909
|
+
### What NOT to Store
|
|
6910
|
+
One-off events, session-by-session summaries, anything that can be retrieved from conversation history on demand.
|
|
6911
|
+
|
|
6912
|
+
### What TO Preserve
|
|
6913
|
+
Focus on understanding **why** the user reacted the way they did — what mistake or behavior triggered the correction? The pattern matters more than the quote. For example, don't just record "user said stop adding stuff" — record that the agent was over-engineering by adding abstractions when a simple flag change was needed. Quotes can serve as supporting evidence, but the real value is the behavioral pattern and what to do differently.
|
|
6914
|
+
|
|
6915
|
+
Keep specific correction counts ("corrected 10+ times"), specific file paths, and specific gotchas with context. Specificity is identity; vague summaries are forgettable.
|
|
6874
6916
|
|
|
6875
6917
|
## Workflow
|
|
6876
6918
|
|
|
@@ -6888,12 +6930,29 @@ git worktree add "$WORKTREE_DIR/$BRANCH_NAME" -b "$BRANCH_NAME"
|
|
|
6888
6930
|
If worktree creation fails (locked index), retry up to 3 times with backoff (sleep 2, 5, 10). Never delete \`.git/index.lock\` manually. All edits go in \`$WORKTREE_DIR/$BRANCH_NAME/\`.
|
|
6889
6931
|
|
|
6890
6932
|
### 2. Read existing memory
|
|
6891
|
-
|
|
6892
|
-
Read all files in your worktree's \`system/\` directory. Understand what's already there so you can extend it, not duplicate it.
|
|
6933
|
+
Read the memory files in your worktree, to understand what already exists in the memory filesystem.
|
|
6893
6934
|
|
|
6894
6935
|
### 3. Read and analyze history
|
|
6895
6936
|
|
|
6896
|
-
|
|
6937
|
+
Your prompt will specify a pre-split JSONL chunk file and its source format. Use these patterns to read it:
|
|
6938
|
+
|
|
6939
|
+
**Claude Code** (\`~/.claude/\`):
|
|
6940
|
+
- \`history.jsonl\` — each line: \`.display\` (prompt text), \`.timestamp\` (unix ms), \`.project\` (working dir), \`.sessionId\`
|
|
6941
|
+
- Session files at \`~/.claude/projects/<encoded-path>/<session-uuid>.jsonl\` (path encoding: \`/\` → \`-\`)
|
|
6942
|
+
- User messages: \`jq 'select(.type == "user") | .message.content'\`
|
|
6943
|
+
- Assistant text: \`jq 'select(.type == "assistant") | .message.content[] | select(.type == "text") | .text'\`
|
|
6944
|
+
- Tool calls: \`jq 'select(.type == "assistant") | .message.content[] | select(.type == "tool_use") | {name, input}'\`
|
|
6945
|
+
- Summaries: \`jq 'select(.type == "summary") | .summary'\`
|
|
6946
|
+
|
|
6947
|
+
**OpenAI Codex** (\`~/.codex/\`):
|
|
6948
|
+
- \`history.jsonl\` — each line: \`.text\` (prompt text), \`.ts\` (unix seconds) — no project path
|
|
6949
|
+
- Session files at \`~/.codex/sessions/<year>/<month>/<day>/rollout-*.jsonl\`
|
|
6950
|
+
- Session metadata (first line): \`jq 'select(.type == "session_meta") | .payload.cwd'\` (to get project dir)
|
|
6951
|
+
- User messages: \`jq 'select(.type == "event_msg" and .payload.type == "user_message") | .payload.message'\`
|
|
6952
|
+
- Assistant text: \`jq 'select(.type == "response_item" and .payload.type == "message") | .payload.content[] | select(.type == "output_text") | .text'\`
|
|
6953
|
+
- Tool calls: \`jq 'select(.type == "response_item" and .payload.type == "function_call") | {name: .payload.name, args: .payload.arguments}'\`
|
|
6954
|
+
|
|
6955
|
+
**Key format difference**: Claude uses \`.timestamp\` (milliseconds) and \`.display\`; Codex uses \`.ts\` (seconds) and \`.text\`.
|
|
6897
6956
|
|
|
6898
6957
|
Look for **repeated patterns**, not isolated events:
|
|
6899
6958
|
- Count correction frequency — 10 corrections on the same topic >> 1 mention
|
|
@@ -6901,38 +6960,70 @@ Look for **repeated patterns**, not isolated events:
|
|
|
6901
6960
|
- Implicit preferences revealed by what commands they run, what patterns they follow
|
|
6902
6961
|
- Frustration signals — "no", "undo", rapid corrections, /clear, model switches
|
|
6903
6962
|
|
|
6963
|
+
**For personality analysis**, look beyond the reaction to what caused it:
|
|
6964
|
+
- What agent behaviors triggered corrections? (over-engineering, wrong tool, verbose explanations, etc.)
|
|
6965
|
+
- What agent behaviors got positive responses? (fast fixes, running tests unprompted, etc.)
|
|
6966
|
+
- How do they phrase requests? (imperative, collaborative, questioning?)
|
|
6967
|
+
- What topics excite them vs bore them?
|
|
6968
|
+
- What's their tolerance for explanation vs "just fix it"?
|
|
6969
|
+
- How do they handle mistakes — their own and the agent's?
|
|
6970
|
+
|
|
6904
6971
|
### 4. Update memory files
|
|
6905
6972
|
|
|
6906
6973
|
**Content placement:**
|
|
6907
6974
|
- \`system/\`: Generalized rules, distilled preferences, project gotchas, identity. Keep files lean — bullets, short lines, scannable.
|
|
6908
6975
|
- Outside \`system/\`: Evidence, detailed history, verbose context. Link from system/ with \`[[path]]\`.
|
|
6909
6976
|
|
|
6977
|
+
**Preferred canonical paths:**
|
|
6978
|
+
- \`system/human/identity.md\`
|
|
6979
|
+
- \`system/human/prefs/communication.md\`
|
|
6980
|
+
- \`system/human/prefs/workflow.md\`
|
|
6981
|
+
- \`system/human/prefs/coding.md\`
|
|
6982
|
+
- \`system/<project>/conventions.md\`
|
|
6983
|
+
- \`system/<project>/gotchas.md\`
|
|
6984
|
+
|
|
6985
|
+
If the current memory uses a more compressed layout, extend it carefully, but prefer splitting into these focused files when there is enough material to justify the move.
|
|
6986
|
+
|
|
6910
6987
|
**File structure:**
|
|
6911
6988
|
- Use the project's **real name** as directory prefix (e.g. \`my-app/conventions.md\`), not generic \`project/\`
|
|
6912
6989
|
- One concept per file, nested with \`/\` paths
|
|
6913
6990
|
- Every file needs a meaningful \`description\` in frontmatter
|
|
6914
6991
|
- Write for the agent's future self — clean, actionable, no clutter
|
|
6915
6992
|
|
|
6993
|
+
Each durable finding should include at least one of:
|
|
6994
|
+
- correction frequency or intensity
|
|
6995
|
+
- concrete commands that worked or failed
|
|
6996
|
+
- concrete file or directory paths
|
|
6997
|
+
- date range or source reference for future lookup
|
|
6998
|
+
- why the rule matters in practice
|
|
6999
|
+
|
|
6916
7000
|
You can also cite the files if you want to note where something came from (e.g. \`(from: ~/.claude/history.jsonl)\`).
|
|
6917
7001
|
|
|
6918
7002
|
### 5. Commit
|
|
6919
7003
|
|
|
7004
|
+
Before writing the commit, resolve the actual ID values:
|
|
7005
|
+
\`\`\`bash
|
|
7006
|
+
echo "AGENT_ID=$LETTA_AGENT_ID"
|
|
7007
|
+
echo "PARENT_AGENT_ID=$LETTA_PARENT_AGENT_ID"
|
|
7008
|
+
\`\`\`
|
|
7009
|
+
|
|
7010
|
+
Use the printed values (e.g., \`agent-abc123...\`) in the trailers. If a variable is empty or unset, omit that trailer. Never write a literal variable name like \`$LETTA_AGENT_ID\` or \`$AGENT_ID\` in the commit message.
|
|
7011
|
+
|
|
6920
7012
|
\`\`\`bash
|
|
6921
7013
|
cd $WORKTREE_DIR/$BRANCH_NAME
|
|
6922
7014
|
git add -A
|
|
6923
|
-
git commit -m "<type>(history-analyzer):
|
|
7015
|
+
git commit --author="History Analyzer <<ACTUAL_AGENT_ID>@letta.com>" -m "<type>(history-analyzer): <summary> ⏳
|
|
6924
7016
|
|
|
6925
7017
|
Source: [file path] ([N] prompts, [DATE RANGE])
|
|
6926
|
-
|
|
6927
|
-
|
|
7018
|
+
|
|
7019
|
+
Updates:
|
|
7020
|
+
- <what changed and why>
|
|
6928
7021
|
|
|
6929
7022
|
Generated-By: Letta Code
|
|
6930
7023
|
Agent-ID: <ACTUAL_AGENT_ID>
|
|
6931
7024
|
Parent-Agent-ID: <ACTUAL_PARENT_AGENT_ID>"
|
|
6932
7025
|
\`\`\`
|
|
6933
7026
|
|
|
6934
|
-
Resolve \`ACTUAL_AGENT_ID\` and \`ACTUAL_PARENT_AGENT_ID\` by running \`echo $LETTA_AGENT_ID\` and \`echo $LETTA_PARENT_AGENT_ID\` first. Never write literal variable names in the commit message. Omit trailers if the variable is empty.
|
|
6935
|
-
|
|
6936
7027
|
**Commit types**: \`chore\` (routine ingestion), \`feat\` (new memory topics), \`refactor\` (reorganizing by domain).
|
|
6937
7028
|
|
|
6938
7029
|
## Rules
|
|
@@ -6940,7 +7031,8 @@ Resolve \`ACTUAL_AGENT_ID\` and \`ACTUAL_PARENT_AGENT_ID\` by running \`echo $LE
|
|
|
6940
7031
|
- Work in your worktree — do NOT edit the memory dir directly
|
|
6941
7032
|
- Do NOT merge into main — the parent agent handles merging
|
|
6942
7033
|
- Preserve existing content — extend or refine, don't replace
|
|
6943
|
-
-
|
|
7034
|
+
- Preserve specificity — specific quotes, correction counts, and file paths are more valuable than vague summaries. Don't compress away the details that give the parent agent its character and grounding.
|
|
7035
|
+
- **REQUIRED**: You MUST produce findings for all three output categories (Personality, Rules, Project). If any category lacks data, explicitly state why.
|
|
6944
7036
|
`;
|
|
6945
7037
|
var init_history_analyzer = () => {};
|
|
6946
7038
|
|
|
@@ -53756,7 +53848,7 @@ If a directory has more than 1,000 entries, only the first 1,000 will be shown.`
|
|
|
53756
53848
|
var init_LS = () => {};
|
|
53757
53849
|
|
|
53758
53850
|
// src/tools/descriptions/Memory.md
|
|
53759
|
-
var Memory_default =
|
|
53851
|
+
var Memory_default = "# Memory\nA convinience tool for memories stored in the memory directory (`$MEMORY_DIR`) that automatically commits and pushes changes. \n\nFiles stored inside of `system/` eventually become part of the agent's system prompt, so are always in the context window and do not need to be re-read. Other files only have metadata in the system prompt, so may need to be explicitly read to be updated. \n\nSupported operations on memory files: \n- `str_replace`\n- `insert`\n- `delete` (files, or directories recursively)\n- `rename` (path rename only)\n- `update_description`\n- `create`\nMore general operations can be performanced through directory modifying the files. \n\nPath formats accepted:\n- relative memory file paths (e.g. `system/contacts.md`, `reference/project/team.md`)\n- absolute paths only when they are inside `$MEMORY_DIR`\n\nNote: absolute paths outside `$MEMORY_DIR` are rejected.\n\nWhen creating or deleting files, check for `[[path]]` references in other memory files that may need to be added or updated. Keeping references consistent ensures future discoverability.\n\nExamples:\n\n```python\n# Replace text in a memory file \nmemory(command=\"str_replace\", reason=\"Update theme preference\", path=\"system/human/preferences.md\", old_string=\"theme: dark\", new_string=\"theme: light\")\n\n# Insert text at line 5\nmemory(command=\"insert\", reason=\"Add note about meeting\", path=\"reference/history/meeting-notes.md\", insert_line=5, insert_text=\"New note here\")\n\n# Delete a memory file \nmemory(command=\"delete\", reason=\"Remove stale notes\", path=\"reference/history/old_notes.md\")\n\n# Rename a memory file \nmemory(command=\"rename\", reason=\"Promote temp notes\", old_path=\"reference/history/temp.md\", new_path=\"reference/history/permanent.md\")\n\n# Update a block description\nmemory(command=\"update_description\", reason=\"Clarify coding prefs block\", path=\"system/human/prefs/coding.md\", description=\"Dr. Wooders' coding preferences.\")\n\n# Create a block with starting text\nmemory(command=\"create\", reason=\"Track coding preferences\", path=\"system/human/prefs/coding.md\", description=\"The user's coding preferences.\", file_text=\"The user seems to add type hints to all of their Python code.\")\n\n# Create an empty block\nmemory(command=\"create\", reason=\"Create coding preferences block\", path=\"reference/history/coding_preferences.md\", description=\"The user's coding preferences.\")\n```\n";
|
|
53760
53852
|
var init_Memory = () => {};
|
|
53761
53853
|
|
|
53762
53854
|
// src/tools/descriptions/MemoryApplyPatch.md
|
|
@@ -65799,6 +65891,9 @@ function parseResultFromStdout(stdout, agentId) {
|
|
|
65799
65891
|
};
|
|
65800
65892
|
}
|
|
65801
65893
|
}
|
|
65894
|
+
function resolveSubagentWorkingDirectory(env3 = process.env, fallbackCwd = process.cwd()) {
|
|
65895
|
+
return env3.USER_CWD || fallbackCwd;
|
|
65896
|
+
}
|
|
65802
65897
|
function resolveSubagentLauncher(cliArgs, options = {}) {
|
|
65803
65898
|
const env3 = options.env ?? process.env;
|
|
65804
65899
|
const argv = options.argv ?? process.argv;
|
|
@@ -65913,8 +66008,9 @@ async function executeSubagent(type, config, model, userPrompt, baseURL, subagen
|
|
|
65913
66008
|
const settings = await settingsManager.getSettingsWithSecureTokens();
|
|
65914
66009
|
const inheritedApiKey = process.env.LETTA_API_KEY || settings.env?.LETTA_API_KEY;
|
|
65915
66010
|
const inheritedBaseUrl = process.env.LETTA_BASE_URL || settings.env?.LETTA_BASE_URL;
|
|
66011
|
+
const subagentWorkingDirectory = resolveSubagentWorkingDirectory();
|
|
65916
66012
|
const proc2 = spawn4(launcher.command, launcher.args, {
|
|
65917
|
-
cwd:
|
|
66013
|
+
cwd: subagentWorkingDirectory,
|
|
65918
66014
|
env: {
|
|
65919
66015
|
...process.env,
|
|
65920
66016
|
...inheritedApiKey && { LETTA_API_KEY: inheritedApiKey },
|
|
@@ -77004,8 +77100,8 @@ function isValidApprovalResponseBody(value) {
|
|
|
77004
77100
|
if (decision.behavior === "allow") {
|
|
77005
77101
|
const hasMessage = decision.message === undefined || typeof decision.message === "string";
|
|
77006
77102
|
const hasUpdatedInput = decision.updated_input === undefined || decision.updated_input === null || typeof decision.updated_input === "object";
|
|
77007
|
-
const
|
|
77008
|
-
return hasMessage && hasUpdatedInput &&
|
|
77103
|
+
const hasSelectedPermissionSuggestionIds = decision.selected_permission_suggestion_ids === undefined || Array.isArray(decision.selected_permission_suggestion_ids) && decision.selected_permission_suggestion_ids.every((entry) => typeof entry === "string");
|
|
77104
|
+
return hasMessage && hasUpdatedInput && hasSelectedPermissionSuggestionIds;
|
|
77009
77105
|
}
|
|
77010
77106
|
if (decision.behavior === "deny") {
|
|
77011
77107
|
return typeof decision.message === "string";
|
|
@@ -77097,6 +77193,7 @@ function requestApprovalOverWS(runtime, socket, requestId, controlRequest) {
|
|
|
77097
77193
|
controlRequest
|
|
77098
77194
|
});
|
|
77099
77195
|
runtime.listener.approvalRuntimeKeyByRequestId.set(requestId, runtime.key);
|
|
77196
|
+
runtime.lastStopReason = "requires_approval";
|
|
77100
77197
|
setLoopStatus(runtime, "WAITING_ON_APPROVAL");
|
|
77101
77198
|
emitLoopStatusIfOpen(runtime.listener, {
|
|
77102
77199
|
agent_id: runtime.agentId,
|
|
@@ -77565,97 +77662,72 @@ var init_permissionMode = __esm(() => {
|
|
|
77565
77662
|
init_remote_settings();
|
|
77566
77663
|
});
|
|
77567
77664
|
|
|
77568
|
-
// src/
|
|
77569
|
-
function
|
|
77570
|
-
|
|
77571
|
-
|
|
77572
|
-
|
|
77573
|
-
|
|
77574
|
-
|
|
77575
|
-
success: false,
|
|
77576
|
-
error: error instanceof Error ? error.message : String(error)
|
|
77577
|
-
};
|
|
77665
|
+
// src/websocket/listener/recoverable-notices.ts
|
|
77666
|
+
function getRecoverableStatusNoticeVisibility(kind) {
|
|
77667
|
+
switch (kind) {
|
|
77668
|
+
case "stale_approval_conflict_recovery":
|
|
77669
|
+
return "debug_only";
|
|
77670
|
+
default:
|
|
77671
|
+
return "transcript";
|
|
77578
77672
|
}
|
|
77579
77673
|
}
|
|
77580
|
-
function
|
|
77581
|
-
|
|
77582
|
-
|
|
77674
|
+
function getRecoverableRetryNoticeVisibility(kind, attempt) {
|
|
77675
|
+
switch (kind) {
|
|
77676
|
+
case "transient_provider_retry":
|
|
77677
|
+
return attempt === 1 ? "debug_only" : "transcript";
|
|
77678
|
+
default:
|
|
77679
|
+
return "transcript";
|
|
77680
|
+
}
|
|
77583
77681
|
}
|
|
77584
|
-
|
|
77585
|
-
|
|
77586
|
-
async function getMissingRequiredArgs(toolName, parsedArgs) {
|
|
77587
|
-
const schema = getToolSchema(toolName);
|
|
77588
|
-
const required = schema?.input_schema?.required || [];
|
|
77589
|
-
return required.filter((key) => !(key in parsedArgs) || parsedArgs[key] == null);
|
|
77682
|
+
function isDesktopDebugPanelMirrorEnabled() {
|
|
77683
|
+
return process.env.LETTA_DESKTOP_DEBUG_PANEL === "1";
|
|
77590
77684
|
}
|
|
77591
|
-
|
|
77592
|
-
|
|
77593
|
-
|
|
77594
|
-
const autoDenied = [];
|
|
77595
|
-
const denyReasonForAsk = opts.denyReasonForAsk ?? "Tool requires approval (headless mode)";
|
|
77596
|
-
const missingNameReason = opts.missingNameReason ?? "Tool call incomplete - missing name";
|
|
77597
|
-
for (const approval of approvals) {
|
|
77598
|
-
const toolName = approval.toolName;
|
|
77599
|
-
if (!toolName) {
|
|
77600
|
-
autoDenied.push({
|
|
77601
|
-
approval,
|
|
77602
|
-
permission: { decision: "deny", reason: missingNameReason },
|
|
77603
|
-
context: null,
|
|
77604
|
-
parsedArgs: {},
|
|
77605
|
-
denyReason: missingNameReason
|
|
77606
|
-
});
|
|
77607
|
-
continue;
|
|
77608
|
-
}
|
|
77609
|
-
const parsedArgs = safeJsonParseOr(approval.toolArgs || "{}", {});
|
|
77610
|
-
const permission = await checkToolPermission(toolName, parsedArgs, opts.workingDirectory, opts.permissionModeState);
|
|
77611
|
-
const context3 = opts.getContext ? await opts.getContext(toolName, parsedArgs, opts.workingDirectory) : null;
|
|
77612
|
-
let decision = permission.decision;
|
|
77613
|
-
if (opts.alwaysRequiresUserInput?.(toolName) && decision === "allow") {
|
|
77614
|
-
decision = "ask";
|
|
77615
|
-
}
|
|
77616
|
-
if (decision === "ask" && opts.treatAskAsDeny) {
|
|
77617
|
-
autoDenied.push({
|
|
77618
|
-
approval,
|
|
77619
|
-
permission,
|
|
77620
|
-
context: context3,
|
|
77621
|
-
parsedArgs,
|
|
77622
|
-
denyReason: denyReasonForAsk
|
|
77623
|
-
});
|
|
77624
|
-
continue;
|
|
77625
|
-
}
|
|
77626
|
-
if (decision === "allow" && opts.requireArgsForAutoApprove) {
|
|
77627
|
-
const missingRequiredArgs = await getMissingRequiredArgs(toolName, parsedArgs);
|
|
77628
|
-
if (missingRequiredArgs.length > 0) {
|
|
77629
|
-
const denyReason = opts.missingArgsReason ? opts.missingArgsReason(missingRequiredArgs) : `Missing required parameter${missingRequiredArgs.length > 1 ? "s" : ""}: ${missingRequiredArgs.join(", ")}`;
|
|
77630
|
-
autoDenied.push({
|
|
77631
|
-
approval,
|
|
77632
|
-
permission,
|
|
77633
|
-
context: context3,
|
|
77634
|
-
parsedArgs,
|
|
77635
|
-
missingRequiredArgs,
|
|
77636
|
-
denyReason
|
|
77637
|
-
});
|
|
77638
|
-
continue;
|
|
77639
|
-
}
|
|
77640
|
-
}
|
|
77641
|
-
const entry = {
|
|
77642
|
-
approval,
|
|
77643
|
-
permission,
|
|
77644
|
-
context: context3,
|
|
77645
|
-
parsedArgs
|
|
77646
|
-
};
|
|
77647
|
-
if (decision === "ask") {
|
|
77648
|
-
needsUserInput.push(entry);
|
|
77649
|
-
} else if (decision === "deny") {
|
|
77650
|
-
autoDenied.push(entry);
|
|
77651
|
-
} else {
|
|
77652
|
-
autoAllowed.push(entry);
|
|
77653
|
-
}
|
|
77685
|
+
function mirrorRecoverableNoticeToDesktopDebugPanel(message) {
|
|
77686
|
+
if (!isDesktopDebugPanelMirrorEnabled()) {
|
|
77687
|
+
return;
|
|
77654
77688
|
}
|
|
77655
|
-
|
|
77689
|
+
try {
|
|
77690
|
+
process.stderr.write(`${DESKTOP_DEBUG_PANEL_INFO_PREFIX} ${message}
|
|
77691
|
+
`);
|
|
77692
|
+
} catch {}
|
|
77656
77693
|
}
|
|
77657
|
-
|
|
77658
|
-
|
|
77694
|
+
function emitRecoverableStatusNotice(socket, runtime, params) {
|
|
77695
|
+
const visibility = getRecoverableStatusNoticeVisibility(params.kind);
|
|
77696
|
+
if (visibility === "debug_only") {
|
|
77697
|
+
debugLog("recovery", `Debug-only lifecycle notice (${params.kind}): ${params.message}`);
|
|
77698
|
+
mirrorRecoverableNoticeToDesktopDebugPanel(params.message);
|
|
77699
|
+
return;
|
|
77700
|
+
}
|
|
77701
|
+
emitStatusDelta(socket, runtime, {
|
|
77702
|
+
message: params.message,
|
|
77703
|
+
level: params.level,
|
|
77704
|
+
runId: params.runId,
|
|
77705
|
+
agentId: params.agentId,
|
|
77706
|
+
conversationId: params.conversationId
|
|
77707
|
+
});
|
|
77708
|
+
}
|
|
77709
|
+
function emitRecoverableRetryNotice(socket, runtime, params) {
|
|
77710
|
+
const visibility = getRecoverableRetryNoticeVisibility(params.kind, params.attempt);
|
|
77711
|
+
if (visibility === "debug_only") {
|
|
77712
|
+
debugLog("recovery", `Debug-only retry notice (${params.kind}, attempt ${params.attempt}/${params.maxAttempts}): ${params.message}`);
|
|
77713
|
+
mirrorRecoverableNoticeToDesktopDebugPanel(params.message);
|
|
77714
|
+
return;
|
|
77715
|
+
}
|
|
77716
|
+
emitRetryDelta(socket, runtime, {
|
|
77717
|
+
message: params.message,
|
|
77718
|
+
reason: params.reason,
|
|
77719
|
+
attempt: params.attempt,
|
|
77720
|
+
maxAttempts: params.maxAttempts,
|
|
77721
|
+
delayMs: params.delayMs,
|
|
77722
|
+
runId: params.runId,
|
|
77723
|
+
agentId: params.agentId,
|
|
77724
|
+
conversationId: params.conversationId
|
|
77725
|
+
});
|
|
77726
|
+
}
|
|
77727
|
+
var DESKTOP_DEBUG_PANEL_INFO_PREFIX = "[LETTA_DESKTOP_DEBUG_PANEL_INFO]";
|
|
77728
|
+
var init_recoverable_notices = __esm(async () => {
|
|
77729
|
+
init_debug();
|
|
77730
|
+
await init_protocol_outbound();
|
|
77659
77731
|
});
|
|
77660
77732
|
|
|
77661
77733
|
// node_modules/diff/libesm/diff/base.js
|
|
@@ -79017,6 +79089,162 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
|
|
|
79017
79089
|
var cachedDiffDeps = null;
|
|
79018
79090
|
var init_diffPreview = () => {};
|
|
79019
79091
|
|
|
79092
|
+
// src/cli/helpers/safeJsonParse.ts
|
|
79093
|
+
function safeJsonParse(json) {
|
|
79094
|
+
try {
|
|
79095
|
+
const data = JSON.parse(json);
|
|
79096
|
+
return { success: true, data };
|
|
79097
|
+
} catch (error) {
|
|
79098
|
+
return {
|
|
79099
|
+
success: false,
|
|
79100
|
+
error: error instanceof Error ? error.message : String(error)
|
|
79101
|
+
};
|
|
79102
|
+
}
|
|
79103
|
+
}
|
|
79104
|
+
function safeJsonParseOr(json, defaultValue) {
|
|
79105
|
+
const result = safeJsonParse(json);
|
|
79106
|
+
return result.success ? result.data : defaultValue;
|
|
79107
|
+
}
|
|
79108
|
+
|
|
79109
|
+
// src/cli/helpers/approvalClassification.ts
|
|
79110
|
+
async function getMissingRequiredArgs(toolName, parsedArgs) {
|
|
79111
|
+
const schema = getToolSchema(toolName);
|
|
79112
|
+
const required = schema?.input_schema?.required || [];
|
|
79113
|
+
return required.filter((key) => !(key in parsedArgs) || parsedArgs[key] == null);
|
|
79114
|
+
}
|
|
79115
|
+
async function classifyApprovals(approvals, opts = {}) {
|
|
79116
|
+
const needsUserInput = [];
|
|
79117
|
+
const autoAllowed = [];
|
|
79118
|
+
const autoDenied = [];
|
|
79119
|
+
const denyReasonForAsk = opts.denyReasonForAsk ?? "Tool requires approval (headless mode)";
|
|
79120
|
+
const missingNameReason = opts.missingNameReason ?? "Tool call incomplete - missing name";
|
|
79121
|
+
for (const approval of approvals) {
|
|
79122
|
+
const toolName = approval.toolName;
|
|
79123
|
+
if (!toolName) {
|
|
79124
|
+
autoDenied.push({
|
|
79125
|
+
approval,
|
|
79126
|
+
permission: { decision: "deny", reason: missingNameReason },
|
|
79127
|
+
context: null,
|
|
79128
|
+
parsedArgs: {},
|
|
79129
|
+
denyReason: missingNameReason
|
|
79130
|
+
});
|
|
79131
|
+
continue;
|
|
79132
|
+
}
|
|
79133
|
+
const parsedArgs = safeJsonParseOr(approval.toolArgs || "{}", {});
|
|
79134
|
+
const permission = await checkToolPermission(toolName, parsedArgs, opts.workingDirectory, opts.permissionModeState);
|
|
79135
|
+
const context3 = opts.getContext ? await opts.getContext(toolName, parsedArgs, opts.workingDirectory) : null;
|
|
79136
|
+
let decision = permission.decision;
|
|
79137
|
+
if (opts.alwaysRequiresUserInput?.(toolName) && decision === "allow") {
|
|
79138
|
+
decision = "ask";
|
|
79139
|
+
}
|
|
79140
|
+
if (decision === "ask" && opts.treatAskAsDeny) {
|
|
79141
|
+
autoDenied.push({
|
|
79142
|
+
approval,
|
|
79143
|
+
permission,
|
|
79144
|
+
context: context3,
|
|
79145
|
+
parsedArgs,
|
|
79146
|
+
denyReason: denyReasonForAsk
|
|
79147
|
+
});
|
|
79148
|
+
continue;
|
|
79149
|
+
}
|
|
79150
|
+
if (decision === "allow" && opts.requireArgsForAutoApprove) {
|
|
79151
|
+
const missingRequiredArgs = await getMissingRequiredArgs(toolName, parsedArgs);
|
|
79152
|
+
if (missingRequiredArgs.length > 0) {
|
|
79153
|
+
const denyReason = opts.missingArgsReason ? opts.missingArgsReason(missingRequiredArgs) : `Missing required parameter${missingRequiredArgs.length > 1 ? "s" : ""}: ${missingRequiredArgs.join(", ")}`;
|
|
79154
|
+
autoDenied.push({
|
|
79155
|
+
approval,
|
|
79156
|
+
permission,
|
|
79157
|
+
context: context3,
|
|
79158
|
+
parsedArgs,
|
|
79159
|
+
missingRequiredArgs,
|
|
79160
|
+
denyReason
|
|
79161
|
+
});
|
|
79162
|
+
continue;
|
|
79163
|
+
}
|
|
79164
|
+
}
|
|
79165
|
+
const entry = {
|
|
79166
|
+
approval,
|
|
79167
|
+
permission,
|
|
79168
|
+
context: context3,
|
|
79169
|
+
parsedArgs
|
|
79170
|
+
};
|
|
79171
|
+
if (decision === "ask") {
|
|
79172
|
+
needsUserInput.push(entry);
|
|
79173
|
+
} else if (decision === "deny") {
|
|
79174
|
+
autoDenied.push(entry);
|
|
79175
|
+
} else {
|
|
79176
|
+
autoAllowed.push(entry);
|
|
79177
|
+
}
|
|
79178
|
+
}
|
|
79179
|
+
return { needsUserInput, autoAllowed, autoDenied };
|
|
79180
|
+
}
|
|
79181
|
+
var init_approvalClassification = __esm(async () => {
|
|
79182
|
+
await init_manager3();
|
|
79183
|
+
});
|
|
79184
|
+
|
|
79185
|
+
// src/websocket/listener/approval-suggestions.ts
|
|
79186
|
+
function getSuggestedPermissionRule(context3) {
|
|
79187
|
+
if (!context3?.allowPersistence || context3.recommendedRule.trim().length === 0) {
|
|
79188
|
+
return null;
|
|
79189
|
+
}
|
|
79190
|
+
return context3.recommendedRule;
|
|
79191
|
+
}
|
|
79192
|
+
function getApprovalPermissionSuggestions(context3) {
|
|
79193
|
+
const suggestedRule = getSuggestedPermissionRule(context3);
|
|
79194
|
+
if (suggestedRule === null || !context3) {
|
|
79195
|
+
return [];
|
|
79196
|
+
}
|
|
79197
|
+
const text = context3.approveAlwaysText.trim();
|
|
79198
|
+
if (text.length === 0) {
|
|
79199
|
+
return [];
|
|
79200
|
+
}
|
|
79201
|
+
return [
|
|
79202
|
+
{
|
|
79203
|
+
suggestion: {
|
|
79204
|
+
id: "save-default",
|
|
79205
|
+
text
|
|
79206
|
+
},
|
|
79207
|
+
rule: suggestedRule,
|
|
79208
|
+
scope: context3.defaultScope
|
|
79209
|
+
}
|
|
79210
|
+
];
|
|
79211
|
+
}
|
|
79212
|
+
function buildApprovalSuggestionPayload(context3) {
|
|
79213
|
+
return {
|
|
79214
|
+
permission_suggestions: getApprovalPermissionSuggestions(context3).map(({ suggestion }) => suggestion)
|
|
79215
|
+
};
|
|
79216
|
+
}
|
|
79217
|
+
async function classifyApprovalsWithSuggestions(approvals, opts = {}) {
|
|
79218
|
+
return classifyApprovals(approvals, {
|
|
79219
|
+
...opts,
|
|
79220
|
+
getContext: async (toolName, parsedArgs, workingDirectory) => analyzeToolApproval(toolName, parsedArgs, workingDirectory)
|
|
79221
|
+
});
|
|
79222
|
+
}
|
|
79223
|
+
async function applySuggestedPermissionsForApproval(params) {
|
|
79224
|
+
const { decision, context: context3, workingDirectory } = params;
|
|
79225
|
+
if (!context3?.allowPersistence || context3.defaultScope === undefined) {
|
|
79226
|
+
return false;
|
|
79227
|
+
}
|
|
79228
|
+
const selectedIds = decision.selected_permission_suggestion_ids ?? [];
|
|
79229
|
+
if (selectedIds.length === 0) {
|
|
79230
|
+
return false;
|
|
79231
|
+
}
|
|
79232
|
+
const matchedSuggestions = getApprovalPermissionSuggestions(context3).filter(({ suggestion }) => selectedIds.includes(suggestion.id));
|
|
79233
|
+
if (matchedSuggestions.length === 0) {
|
|
79234
|
+
return false;
|
|
79235
|
+
}
|
|
79236
|
+
for (const matchedSuggestion of matchedSuggestions) {
|
|
79237
|
+
await savePermissionRule2(matchedSuggestion.rule, "allow", matchedSuggestion.scope, workingDirectory);
|
|
79238
|
+
}
|
|
79239
|
+
return true;
|
|
79240
|
+
}
|
|
79241
|
+
var init_approval_suggestions = __esm(async () => {
|
|
79242
|
+
await __promiseAll([
|
|
79243
|
+
init_approvalClassification(),
|
|
79244
|
+
init_manager3()
|
|
79245
|
+
]);
|
|
79246
|
+
});
|
|
79247
|
+
|
|
79020
79248
|
// src/websocket/listener/recovery.ts
|
|
79021
79249
|
function isApprovalToolCallDesyncError(detail) {
|
|
79022
79250
|
if (isInvalidToolCallIdsError(detail) || isApprovalPendingError(detail)) {
|
|
@@ -79232,12 +79460,13 @@ async function recoverApprovalStateForSync(runtime, scope) {
|
|
|
79232
79460
|
return;
|
|
79233
79461
|
}
|
|
79234
79462
|
const workingDirectory = getConversationWorkingDirectory(runtime.listener, scope.agent_id, scope.conversation_id);
|
|
79235
|
-
const
|
|
79463
|
+
const permissionModeState = getOrCreateConversationPermissionModeStateRef(runtime.listener, scope.agent_id, scope.conversation_id);
|
|
79464
|
+
const { needsUserInput, autoAllowed, autoDenied } = await classifyApprovalsWithSuggestions(pendingApprovals, {
|
|
79236
79465
|
alwaysRequiresUserInput: isInteractiveApprovalTool,
|
|
79237
79466
|
requireArgsForAutoApprove: true,
|
|
79238
79467
|
missingNameReason: "Tool call incomplete - missing name",
|
|
79239
79468
|
workingDirectory,
|
|
79240
|
-
permissionModeState
|
|
79469
|
+
permissionModeState
|
|
79241
79470
|
});
|
|
79242
79471
|
const autoDecisions = buildRecoveredAutoDecisions(autoAllowed, autoDenied);
|
|
79243
79472
|
if (needsUserInput.length === 0) {
|
|
@@ -79252,6 +79481,7 @@ async function recoverApprovalStateForSync(runtime, scope) {
|
|
|
79252
79481
|
const diffs = await computeDiffPreviews(approval.toolName, input, workingDirectory);
|
|
79253
79482
|
approvalsByRequestId.set(requestId, {
|
|
79254
79483
|
approval,
|
|
79484
|
+
approvalContext: approvalEntry.context,
|
|
79255
79485
|
controlRequest: {
|
|
79256
79486
|
type: "control_request",
|
|
79257
79487
|
request_id: requestId,
|
|
@@ -79260,7 +79490,7 @@ async function recoverApprovalStateForSync(runtime, scope) {
|
|
|
79260
79490
|
tool_name: approval.toolName,
|
|
79261
79491
|
input,
|
|
79262
79492
|
tool_call_id: approval.toolCallId,
|
|
79263
|
-
|
|
79493
|
+
...buildApprovalSuggestionPayload(approvalEntry.context),
|
|
79264
79494
|
blocked_path: null,
|
|
79265
79495
|
...diffs.length > 0 ? { diffs } : {}
|
|
79266
79496
|
},
|
|
@@ -79290,6 +79520,40 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
|
|
|
79290
79520
|
}
|
|
79291
79521
|
recovered.responsesByRequestId.set(requestId, response);
|
|
79292
79522
|
recovered.pendingRequestIds.delete(requestId);
|
|
79523
|
+
const workingDirectory = getConversationWorkingDirectory(runtime.listener, recovered.agentId, recovered.conversationId);
|
|
79524
|
+
const respondedEntry = recovered.approvalsByRequestId.get(requestId);
|
|
79525
|
+
if (respondedEntry && "decision" in response && response.decision.behavior === "allow") {
|
|
79526
|
+
const savedSuggestions = await applySuggestedPermissionsForApproval({
|
|
79527
|
+
decision: response.decision,
|
|
79528
|
+
context: respondedEntry.approvalContext,
|
|
79529
|
+
workingDirectory
|
|
79530
|
+
});
|
|
79531
|
+
if (savedSuggestions && recovered.pendingRequestIds.size > 0) {
|
|
79532
|
+
const remainingRecoveredEntries = [...recovered.pendingRequestIds].map((id) => recovered.approvalsByRequestId.get(id)).filter((entry) => !!entry);
|
|
79533
|
+
const reclassified = await classifyApprovalsWithSuggestions(remainingRecoveredEntries.map((entry) => entry.approval), {
|
|
79534
|
+
alwaysRequiresUserInput: isInteractiveApprovalTool,
|
|
79535
|
+
requireArgsForAutoApprove: true,
|
|
79536
|
+
missingNameReason: "Tool call incomplete - missing name",
|
|
79537
|
+
workingDirectory,
|
|
79538
|
+
permissionModeState: getOrCreateConversationPermissionModeStateRef(runtime.listener, recovered.agentId, recovered.conversationId)
|
|
79539
|
+
});
|
|
79540
|
+
if (reclassified.autoAllowed.length > 0 || reclassified.autoDenied.length > 0) {
|
|
79541
|
+
recovered.autoDecisions = [
|
|
79542
|
+
...recovered.autoDecisions ?? [],
|
|
79543
|
+
...buildRecoveredAutoDecisions(reclassified.autoAllowed, reclassified.autoDenied)
|
|
79544
|
+
];
|
|
79545
|
+
const reclassifiedToolCallIds = new Set([...reclassified.autoAllowed, ...reclassified.autoDenied].map((entry) => entry.approval.toolCallId));
|
|
79546
|
+
for (const pendingId of [...recovered.pendingRequestIds]) {
|
|
79547
|
+
const pendingEntry = recovered.approvalsByRequestId.get(pendingId);
|
|
79548
|
+
if (pendingEntry && reclassifiedToolCallIds.has(pendingEntry.approval.toolCallId)) {
|
|
79549
|
+
recovered.pendingRequestIds.delete(pendingId);
|
|
79550
|
+
recovered.approvalsByRequestId.delete(pendingId);
|
|
79551
|
+
recovered.responsesByRequestId.delete(pendingId);
|
|
79552
|
+
}
|
|
79553
|
+
}
|
|
79554
|
+
}
|
|
79555
|
+
}
|
|
79556
|
+
}
|
|
79293
79557
|
if (recovered.pendingRequestIds.size > 0) {
|
|
79294
79558
|
emitRuntimeStateUpdates(runtime, {
|
|
79295
79559
|
agent_id: recovered.agentId,
|
|
@@ -79342,7 +79606,7 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
|
|
|
79342
79606
|
recovered.pendingRequestIds.clear();
|
|
79343
79607
|
emitRuntimeStateUpdates(runtime, scope);
|
|
79344
79608
|
runtime.isProcessing = true;
|
|
79345
|
-
runtime.activeWorkingDirectory =
|
|
79609
|
+
runtime.activeWorkingDirectory = workingDirectory;
|
|
79346
79610
|
runtime.activeExecutingToolCallIds = [...approvedToolCallIds];
|
|
79347
79611
|
setLoopStatus(runtime, "EXECUTING_CLIENT_SIDE_TOOL", scope);
|
|
79348
79612
|
emitRuntimeStateUpdates(runtime, scope);
|
|
@@ -79367,7 +79631,7 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
|
|
|
79367
79631
|
const approvalResults = await executeApprovalBatch(decisions, undefined, {
|
|
79368
79632
|
abortSignal: recoveryAbortController.signal,
|
|
79369
79633
|
toolContextId: preparedToolContext.preparedToolContext.contextId,
|
|
79370
|
-
workingDirectory
|
|
79634
|
+
workingDirectory,
|
|
79371
79635
|
parentScope: recovered.agentId && recovered.conversationId ? {
|
|
79372
79636
|
agentId: recovered.agentId,
|
|
79373
79637
|
conversationId: recovered.conversationId
|
|
@@ -79434,9 +79698,9 @@ var init_recovery = __esm(async () => {
|
|
|
79434
79698
|
init_approval_execution(),
|
|
79435
79699
|
init_client2(),
|
|
79436
79700
|
init_accumulator(),
|
|
79437
|
-
init_approvalClassification(),
|
|
79438
79701
|
init_stream(),
|
|
79439
79702
|
init_toolset(),
|
|
79703
|
+
init_approval_suggestions(),
|
|
79440
79704
|
init_interrupts(),
|
|
79441
79705
|
init_protocol_outbound(),
|
|
79442
79706
|
init_queue()
|
|
@@ -79526,12 +79790,13 @@ async function resolveStaleApprovals(runtime, socket, abortSignal, deps = {}) {
|
|
|
79526
79790
|
throw new Error("Ambiguous pending approval batch mapping during recovery");
|
|
79527
79791
|
}
|
|
79528
79792
|
rememberPendingApprovalBatchIds(runtime, pendingApprovals, recoveryBatchId);
|
|
79529
|
-
const
|
|
79793
|
+
const permissionModeState = getOrCreateConversationPermissionModeStateRef(runtime.listener, runtime.agentId, runtime.conversationId);
|
|
79794
|
+
const { autoAllowed, autoDenied, needsUserInput } = await classifyApprovalsWithSuggestions(pendingApprovals, {
|
|
79530
79795
|
alwaysRequiresUserInput: isInteractiveApprovalTool,
|
|
79531
79796
|
requireArgsForAutoApprove: true,
|
|
79532
79797
|
missingNameReason: "Tool call incomplete - missing name",
|
|
79533
79798
|
workingDirectory: recoveryWorkingDirectory,
|
|
79534
|
-
permissionModeState
|
|
79799
|
+
permissionModeState
|
|
79535
79800
|
});
|
|
79536
79801
|
const decisions = [
|
|
79537
79802
|
...autoAllowed.map((ac) => ({
|
|
@@ -79544,11 +79809,13 @@ async function resolveStaleApprovals(runtime, socket, abortSignal, deps = {}) {
|
|
|
79544
79809
|
reason: ac.denyReason || ac.permission.reason || "Permission denied"
|
|
79545
79810
|
}))
|
|
79546
79811
|
];
|
|
79547
|
-
|
|
79548
|
-
|
|
79549
|
-
|
|
79550
|
-
|
|
79551
|
-
|
|
79812
|
+
let pendingNeedsUserInput = [...needsUserInput];
|
|
79813
|
+
if (pendingNeedsUserInput.length > 0) {
|
|
79814
|
+
while (pendingNeedsUserInput.length > 0) {
|
|
79815
|
+
const ac = pendingNeedsUserInput.shift();
|
|
79816
|
+
if (!ac) {
|
|
79817
|
+
break;
|
|
79818
|
+
}
|
|
79552
79819
|
if (abortSignal.aborted)
|
|
79553
79820
|
throw new Error("Cancelled");
|
|
79554
79821
|
const requestId = `perm-${ac.approval.toolCallId}`;
|
|
@@ -79561,7 +79828,7 @@ async function resolveStaleApprovals(runtime, socket, abortSignal, deps = {}) {
|
|
|
79561
79828
|
tool_name: ac.approval.toolName,
|
|
79562
79829
|
input: ac.parsedArgs,
|
|
79563
79830
|
tool_call_id: ac.approval.toolCallId,
|
|
79564
|
-
|
|
79831
|
+
...buildApprovalSuggestionPayload(ac.context),
|
|
79565
79832
|
blocked_path: null,
|
|
79566
79833
|
...diffs.length > 0 ? { diffs } : {}
|
|
79567
79834
|
},
|
|
@@ -79572,6 +79839,11 @@ async function resolveStaleApprovals(runtime, socket, abortSignal, deps = {}) {
|
|
|
79572
79839
|
if ("decision" in responseBody) {
|
|
79573
79840
|
const response = responseBody.decision;
|
|
79574
79841
|
if (response.behavior === "allow") {
|
|
79842
|
+
const savedSuggestions = await applySuggestedPermissionsForApproval({
|
|
79843
|
+
decision: response,
|
|
79844
|
+
context: ac.context,
|
|
79845
|
+
workingDirectory: recoveryWorkingDirectory
|
|
79846
|
+
});
|
|
79575
79847
|
decisions.push({
|
|
79576
79848
|
type: "approve",
|
|
79577
79849
|
approval: response.updated_input ? {
|
|
@@ -79580,6 +79852,24 @@ async function resolveStaleApprovals(runtime, socket, abortSignal, deps = {}) {
|
|
|
79580
79852
|
} : ac.approval,
|
|
79581
79853
|
reason: response.message
|
|
79582
79854
|
});
|
|
79855
|
+
if (savedSuggestions && pendingNeedsUserInput.length > 0) {
|
|
79856
|
+
const reclassified = await classifyApprovalsWithSuggestions(pendingNeedsUserInput.map((entry) => entry.approval), {
|
|
79857
|
+
alwaysRequiresUserInput: isInteractiveApprovalTool,
|
|
79858
|
+
requireArgsForAutoApprove: true,
|
|
79859
|
+
missingNameReason: "Tool call incomplete - missing name",
|
|
79860
|
+
workingDirectory: recoveryWorkingDirectory,
|
|
79861
|
+
permissionModeState
|
|
79862
|
+
});
|
|
79863
|
+
decisions.push(...reclassified.autoAllowed.map((entry) => ({
|
|
79864
|
+
type: "approve",
|
|
79865
|
+
approval: entry.approval
|
|
79866
|
+
})), ...reclassified.autoDenied.map((entry) => ({
|
|
79867
|
+
type: "deny",
|
|
79868
|
+
approval: entry.approval,
|
|
79869
|
+
reason: entry.denyReason || entry.permission.reason || "Permission denied"
|
|
79870
|
+
})));
|
|
79871
|
+
pendingNeedsUserInput = [...reclassified.needsUserInput];
|
|
79872
|
+
}
|
|
79583
79873
|
} else {
|
|
79584
79874
|
decisions.push({
|
|
79585
79875
|
type: "deny",
|
|
@@ -79929,9 +80219,9 @@ var init_send = __esm(async () => {
|
|
|
79929
80219
|
init_approval_recovery(),
|
|
79930
80220
|
init_client2(),
|
|
79931
80221
|
init_message(),
|
|
79932
|
-
init_approvalClassification(),
|
|
79933
80222
|
init_toolset(),
|
|
79934
80223
|
init_approval(),
|
|
80224
|
+
init_approval_suggestions(),
|
|
79935
80225
|
init_interrupts(),
|
|
79936
80226
|
init_protocol_outbound(),
|
|
79937
80227
|
init_queue(),
|
|
@@ -79997,7 +80287,7 @@ async function handleApprovalStop(params) {
|
|
|
79997
80287
|
}
|
|
79998
80288
|
clearPendingApprovalBatchIds(runtime, approvals);
|
|
79999
80289
|
rememberPendingApprovalBatchIds(runtime, approvals, dequeuedBatchId);
|
|
80000
|
-
const { autoAllowed, autoDenied, needsUserInput } = await
|
|
80290
|
+
const { autoAllowed, autoDenied, needsUserInput } = await classifyApprovalsWithSuggestions(approvals, {
|
|
80001
80291
|
alwaysRequiresUserInput: isInteractiveApprovalTool,
|
|
80002
80292
|
treatAskAsDeny: false,
|
|
80003
80293
|
requireArgsForAutoApprove: true,
|
|
@@ -80005,7 +80295,8 @@ async function handleApprovalStop(params) {
|
|
|
80005
80295
|
workingDirectory: turnWorkingDirectory,
|
|
80006
80296
|
permissionModeState: turnPermissionModeState
|
|
80007
80297
|
});
|
|
80008
|
-
|
|
80298
|
+
let pendingNeedsUserInput = [...needsUserInput];
|
|
80299
|
+
let lastNeedsUserInputToolCallIds = pendingNeedsUserInput.map((ac) => ac.approval.toolCallId);
|
|
80009
80300
|
let lastExecutionResults = null;
|
|
80010
80301
|
let lastExecutingToolCallIds = [];
|
|
80011
80302
|
const shouldInterrupt = () => abortController.signal.aborted || runtime.cancelRequested;
|
|
@@ -80044,16 +80335,15 @@ async function handleApprovalStop(params) {
|
|
|
80044
80335
|
if (shouldInterrupt()) {
|
|
80045
80336
|
return interruptTermination();
|
|
80046
80337
|
}
|
|
80047
|
-
if (
|
|
80338
|
+
if (pendingNeedsUserInput.length > 0) {
|
|
80048
80339
|
if (shouldInterrupt()) {
|
|
80049
80340
|
return interruptTermination();
|
|
80050
80341
|
}
|
|
80051
|
-
|
|
80052
|
-
|
|
80053
|
-
|
|
80054
|
-
|
|
80055
|
-
|
|
80056
|
-
for (const ac of needsUserInput) {
|
|
80342
|
+
while (pendingNeedsUserInput.length > 0) {
|
|
80343
|
+
const ac = pendingNeedsUserInput.shift();
|
|
80344
|
+
if (!ac) {
|
|
80345
|
+
break;
|
|
80346
|
+
}
|
|
80057
80347
|
if (shouldInterrupt()) {
|
|
80058
80348
|
return interruptTermination();
|
|
80059
80349
|
}
|
|
@@ -80070,7 +80360,7 @@ async function handleApprovalStop(params) {
|
|
|
80070
80360
|
tool_name: ac.approval.toolName,
|
|
80071
80361
|
input: ac.parsedArgs,
|
|
80072
80362
|
tool_call_id: ac.approval.toolCallId,
|
|
80073
|
-
|
|
80363
|
+
...buildApprovalSuggestionPayload(ac.context),
|
|
80074
80364
|
blocked_path: null,
|
|
80075
80365
|
...diffs.length > 0 ? { diffs } : {}
|
|
80076
80366
|
},
|
|
@@ -80092,6 +80382,11 @@ async function handleApprovalStop(params) {
|
|
|
80092
80382
|
if ("decision" in responseBody) {
|
|
80093
80383
|
const response = responseBody.decision;
|
|
80094
80384
|
if (response.behavior === "allow") {
|
|
80385
|
+
const savedSuggestions = await applySuggestedPermissionsForApproval({
|
|
80386
|
+
decision: response,
|
|
80387
|
+
context: ac.context,
|
|
80388
|
+
workingDirectory: turnWorkingDirectory
|
|
80389
|
+
});
|
|
80095
80390
|
const finalApproval = response.updated_input ? {
|
|
80096
80391
|
...ac.approval,
|
|
80097
80392
|
toolArgs: JSON.stringify(response.updated_input)
|
|
@@ -80101,6 +80396,26 @@ async function handleApprovalStop(params) {
|
|
|
80101
80396
|
approval: finalApproval,
|
|
80102
80397
|
reason: response.message
|
|
80103
80398
|
});
|
|
80399
|
+
if (savedSuggestions && pendingNeedsUserInput.length > 0) {
|
|
80400
|
+
const reclassified = await classifyApprovalsWithSuggestions(pendingNeedsUserInput.map((entry) => entry.approval), {
|
|
80401
|
+
alwaysRequiresUserInput: isInteractiveApprovalTool,
|
|
80402
|
+
treatAskAsDeny: false,
|
|
80403
|
+
requireArgsForAutoApprove: true,
|
|
80404
|
+
missingNameReason: "Tool call incomplete - missing name",
|
|
80405
|
+
workingDirectory: turnWorkingDirectory,
|
|
80406
|
+
permissionModeState: turnPermissionModeState
|
|
80407
|
+
});
|
|
80408
|
+
decisions.push(...reclassified.autoAllowed.map((entry) => ({
|
|
80409
|
+
type: "approve",
|
|
80410
|
+
approval: entry.approval
|
|
80411
|
+
})), ...reclassified.autoDenied.map((entry) => ({
|
|
80412
|
+
type: "deny",
|
|
80413
|
+
approval: entry.approval,
|
|
80414
|
+
reason: entry.denyReason || entry.permission.reason || "Permission denied"
|
|
80415
|
+
})));
|
|
80416
|
+
pendingNeedsUserInput = [...reclassified.needsUserInput];
|
|
80417
|
+
lastNeedsUserInputToolCallIds = pendingNeedsUserInput.map((entry) => entry.approval.toolCallId);
|
|
80418
|
+
}
|
|
80104
80419
|
} else {
|
|
80105
80420
|
decisions.push({
|
|
80106
80421
|
type: "deny",
|
|
@@ -80248,8 +80563,8 @@ var init_turn_approval = __esm(async () => {
|
|
|
80248
80563
|
init_skill_injection();
|
|
80249
80564
|
await __promiseAll([
|
|
80250
80565
|
init_approval_execution(),
|
|
80251
|
-
init_approvalClassification(),
|
|
80252
80566
|
init_approval(),
|
|
80567
|
+
init_approval_suggestions(),
|
|
80253
80568
|
init_interrupts(),
|
|
80254
80569
|
init_protocol_outbound(),
|
|
80255
80570
|
init_queue(),
|
|
@@ -80659,7 +80974,8 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
|
|
|
80659
80974
|
latestErrorText
|
|
80660
80975
|
})) {
|
|
80661
80976
|
postStopApprovalRecoveryRetries += 1;
|
|
80662
|
-
|
|
80977
|
+
emitRecoverableStatusNotice(socket, runtime, {
|
|
80978
|
+
kind: "stale_approval_conflict_recovery",
|
|
80663
80979
|
message: "Recovering from stale approval conflict after interrupted/reconnected turn",
|
|
80664
80980
|
level: "warning",
|
|
80665
80981
|
runId: lastRunId || undefined,
|
|
@@ -80755,7 +81071,8 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
|
|
|
80755
81071
|
detail: errorDetail
|
|
80756
81072
|
});
|
|
80757
81073
|
const retryMessage = getRetryStatusMessage(errorDetail) || `LLM API error encountered, retrying (attempt ${attempt}/${LLM_API_ERROR_MAX_RETRIES})...`;
|
|
80758
|
-
|
|
81074
|
+
emitRecoverableRetryNotice(socket, runtime, {
|
|
81075
|
+
kind: "transient_provider_retry",
|
|
80759
81076
|
message: retryMessage,
|
|
80760
81077
|
reason: "llm_api_error",
|
|
80761
81078
|
attempt,
|
|
@@ -80955,6 +81272,7 @@ var init_turn = __esm(async () => {
|
|
|
80955
81272
|
init_toolset(),
|
|
80956
81273
|
init_interrupts(),
|
|
80957
81274
|
init_protocol_outbound(),
|
|
81275
|
+
init_recoverable_notices(),
|
|
80958
81276
|
init_recovery(),
|
|
80959
81277
|
init_send(),
|
|
80960
81278
|
init_turn_approval()
|
|
@@ -81278,7 +81596,7 @@ function buildLoopStatus(runtime, params) {
|
|
|
81278
81596
|
const conversationRuntime = getConversationRuntime(listener, scopedAgentId, scopedConversationId);
|
|
81279
81597
|
const interruptedCacheActive = hasInterruptedCacheForScope(listener, scope);
|
|
81280
81598
|
const recovered = getRecoveredApprovalStateForScope(listener, scope);
|
|
81281
|
-
const status = interruptedCacheActive ? !conversationRuntime?.isProcessing ? "WAITING_ON_INPUT" : conversationRuntime?.loopStatus ?? "WAITING_ON_INPUT" : recovered && recovered.pendingRequestIds.size > 0 && conversationRuntime?.loopStatus === "WAITING_ON_INPUT" ? "WAITING_ON_APPROVAL" : conversationRuntime?.loopStatus ?? "WAITING_ON_INPUT";
|
|
81599
|
+
const status = interruptedCacheActive ? !conversationRuntime?.isProcessing ? "WAITING_ON_INPUT" : conversationRuntime?.loopStatus === "WAITING_ON_APPROVAL" ? "WAITING_ON_INPUT" : conversationRuntime?.loopStatus ?? "WAITING_ON_INPUT" : recovered && recovered.pendingRequestIds.size > 0 && conversationRuntime?.loopStatus === "WAITING_ON_INPUT" ? "WAITING_ON_APPROVAL" : conversationRuntime?.loopStatus ?? "WAITING_ON_INPUT";
|
|
81282
81600
|
return {
|
|
81283
81601
|
status,
|
|
81284
81602
|
active_run_ids: interruptedCacheActive && !conversationRuntime?.isProcessing ? [] : conversationRuntime?.activeRunId ? [conversationRuntime.activeRunId] : []
|
|
@@ -81440,9 +81758,6 @@ function resolveSubagentScopeForSnapshot(runtime, scope) {
|
|
|
81440
81758
|
function buildSubagentSnapshot(runtime, scope) {
|
|
81441
81759
|
const runtimeScope = resolveSubagentScopeForSnapshot(runtime, scope);
|
|
81442
81760
|
return getSubagents().filter((a) => {
|
|
81443
|
-
if (a.status !== "pending" && a.status !== "running") {
|
|
81444
|
-
return false;
|
|
81445
|
-
}
|
|
81446
81761
|
if (a.silent && a.isBackground !== true) {
|
|
81447
81762
|
return false;
|
|
81448
81763
|
}
|
|
@@ -85178,7 +85493,7 @@ var init_skills2 = __esm(() => {
|
|
|
85178
85493
|
var exports_fs = {};
|
|
85179
85494
|
__export(exports_fs, {
|
|
85180
85495
|
writeJsonFile: () => writeJsonFile,
|
|
85181
|
-
writeFile: () =>
|
|
85496
|
+
writeFile: () => writeFile7,
|
|
85182
85497
|
readJsonFile: () => readJsonFile,
|
|
85183
85498
|
readFile: () => readFile9,
|
|
85184
85499
|
mkdir: () => mkdir6,
|
|
@@ -85194,7 +85509,7 @@ import { dirname as dirname13 } from "node:path";
|
|
|
85194
85509
|
async function readFile9(path23) {
|
|
85195
85510
|
return fsReadFileSync2(path23, { encoding: "utf-8" });
|
|
85196
85511
|
}
|
|
85197
|
-
async function
|
|
85512
|
+
async function writeFile7(path23, content) {
|
|
85198
85513
|
const dir = dirname13(path23);
|
|
85199
85514
|
if (!existsSync25(dir)) {
|
|
85200
85515
|
mkdirSync17(dir, { recursive: true });
|
|
@@ -85215,7 +85530,7 @@ async function writeJsonFile(path23, data, options) {
|
|
|
85215
85530
|
const indent = options?.indent ?? 2;
|
|
85216
85531
|
const content = `${JSON.stringify(data, null, indent)}
|
|
85217
85532
|
`;
|
|
85218
|
-
await
|
|
85533
|
+
await writeFile7(path23, content);
|
|
85219
85534
|
}
|
|
85220
85535
|
var init_fs2 = () => {};
|
|
85221
85536
|
|
|
@@ -85240,7 +85555,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
|
|
|
85240
85555
|
}
|
|
85241
85556
|
const wasRaw = process.stdin.isRaw;
|
|
85242
85557
|
const wasFlowing = process.stdin.readableFlowing;
|
|
85243
|
-
return new Promise((
|
|
85558
|
+
return new Promise((resolve26) => {
|
|
85244
85559
|
let response = "";
|
|
85245
85560
|
let resolved = false;
|
|
85246
85561
|
const cleanup = () => {
|
|
@@ -85257,7 +85572,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
|
|
|
85257
85572
|
};
|
|
85258
85573
|
const timeout = setTimeout(() => {
|
|
85259
85574
|
cleanup();
|
|
85260
|
-
|
|
85575
|
+
resolve26(null);
|
|
85261
85576
|
}, timeoutMs);
|
|
85262
85577
|
const onData = (data) => {
|
|
85263
85578
|
response += data.toString();
|
|
@@ -85267,7 +85582,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
|
|
|
85267
85582
|
if (match3) {
|
|
85268
85583
|
clearTimeout(timeout);
|
|
85269
85584
|
cleanup();
|
|
85270
|
-
|
|
85585
|
+
resolve26({
|
|
85271
85586
|
r: parseHexComponent(match3[1] ?? "0"),
|
|
85272
85587
|
g: parseHexComponent(match3[2] ?? "0"),
|
|
85273
85588
|
b: parseHexComponent(match3[3] ?? "0")
|
|
@@ -85282,7 +85597,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
|
|
|
85282
85597
|
} catch {
|
|
85283
85598
|
clearTimeout(timeout);
|
|
85284
85599
|
cleanup();
|
|
85285
|
-
|
|
85600
|
+
resolve26(null);
|
|
85286
85601
|
}
|
|
85287
85602
|
});
|
|
85288
85603
|
}
|
|
@@ -86491,10 +86806,10 @@ __export(exports_setup, {
|
|
|
86491
86806
|
runSetup: () => runSetup
|
|
86492
86807
|
});
|
|
86493
86808
|
async function runSetup() {
|
|
86494
|
-
return new Promise((
|
|
86809
|
+
return new Promise((resolve27) => {
|
|
86495
86810
|
const { waitUntilExit } = render_default(import_react32.default.createElement(SetupUI, {
|
|
86496
86811
|
onComplete: () => {
|
|
86497
|
-
|
|
86812
|
+
resolve27();
|
|
86498
86813
|
}
|
|
86499
86814
|
}));
|
|
86500
86815
|
waitUntilExit().catch((error) => {
|
|
@@ -86969,6 +87284,20 @@ function validateRegistryHandleOrThrow2(handle) {
|
|
|
86969
87284
|
}
|
|
86970
87285
|
}
|
|
86971
87286
|
|
|
87287
|
+
// src/runtime-context.ts
|
|
87288
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
87289
|
+
function getCurrentWorkingDirectory() {
|
|
87290
|
+
const workingDirectory = runtimeContextStorage.getStore()?.workingDirectory;
|
|
87291
|
+
if (typeof workingDirectory === "string" && workingDirectory.length > 0) {
|
|
87292
|
+
return workingDirectory;
|
|
87293
|
+
}
|
|
87294
|
+
return process.env.USER_CWD || process.cwd();
|
|
87295
|
+
}
|
|
87296
|
+
var runtimeContextStorage;
|
|
87297
|
+
var init_runtime_context = __esm(() => {
|
|
87298
|
+
runtimeContextStorage = new AsyncLocalStorage;
|
|
87299
|
+
});
|
|
87300
|
+
|
|
86972
87301
|
// src/agent/github-utils.ts
|
|
86973
87302
|
var exports_github_utils = {};
|
|
86974
87303
|
__export(exports_github_utils, {
|
|
@@ -87009,11 +87338,11 @@ __export(exports_import, {
|
|
|
87009
87338
|
extractSkillsFromAf: () => extractSkillsFromAf
|
|
87010
87339
|
});
|
|
87011
87340
|
import { createReadStream } from "node:fs";
|
|
87012
|
-
import { chmod, mkdir as mkdir7, readFile as readFile10, writeFile as
|
|
87013
|
-
import { dirname as dirname14, resolve as
|
|
87341
|
+
import { chmod, mkdir as mkdir7, readFile as readFile10, writeFile as writeFile8 } from "node:fs/promises";
|
|
87342
|
+
import { dirname as dirname14, resolve as resolve27 } from "node:path";
|
|
87014
87343
|
async function importAgentFromFile(options) {
|
|
87015
87344
|
const client = await getClient();
|
|
87016
|
-
const resolvedPath =
|
|
87345
|
+
const resolvedPath = resolve27(options.filePath);
|
|
87017
87346
|
const file = createReadStream(resolvedPath);
|
|
87018
87347
|
const importResponse = await client.agents.importFile({
|
|
87019
87348
|
file,
|
|
@@ -87048,7 +87377,7 @@ async function extractSkillsFromAf(afPath, destDir) {
|
|
|
87048
87377
|
return [];
|
|
87049
87378
|
}
|
|
87050
87379
|
for (const skill2 of afData.skills) {
|
|
87051
|
-
const skillDir =
|
|
87380
|
+
const skillDir = resolve27(destDir, skill2.name);
|
|
87052
87381
|
await mkdir7(skillDir, { recursive: true });
|
|
87053
87382
|
if (skill2.files) {
|
|
87054
87383
|
await writeSkillFiles(skillDir, skill2.files);
|
|
@@ -87068,9 +87397,9 @@ async function writeSkillFiles(skillDir, files) {
|
|
|
87068
87397
|
}
|
|
87069
87398
|
}
|
|
87070
87399
|
async function writeSkillFile(skillDir, filePath, content) {
|
|
87071
|
-
const fullPath =
|
|
87400
|
+
const fullPath = resolve27(skillDir, filePath);
|
|
87072
87401
|
await mkdir7(dirname14(fullPath), { recursive: true });
|
|
87073
|
-
await
|
|
87402
|
+
await writeFile8(fullPath, content, "utf-8");
|
|
87074
87403
|
const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
|
|
87075
87404
|
if (isScript) {
|
|
87076
87405
|
try {
|
|
@@ -87123,7 +87452,7 @@ function parseRegistryHandle(handle) {
|
|
|
87123
87452
|
async function importAgentFromRegistry(options) {
|
|
87124
87453
|
const { tmpdir: tmpdir4 } = await import("node:os");
|
|
87125
87454
|
const { join: join35 } = await import("node:path");
|
|
87126
|
-
const { writeFile:
|
|
87455
|
+
const { writeFile: writeFile9, unlink: unlink3 } = await import("node:fs/promises");
|
|
87127
87456
|
const { author, name } = parseRegistryHandle(options.handle);
|
|
87128
87457
|
const rawUrl = `https://raw.githubusercontent.com/${AGENT_REGISTRY_OWNER}/${AGENT_REGISTRY_REPO}/refs/heads/${AGENT_REGISTRY_BRANCH}/agents/@${author}/${name}/${name}.af`;
|
|
87129
87458
|
const response = await fetch(rawUrl);
|
|
@@ -87135,7 +87464,7 @@ async function importAgentFromRegistry(options) {
|
|
|
87135
87464
|
}
|
|
87136
87465
|
const afContent = await response.text();
|
|
87137
87466
|
const tempPath = join35(tmpdir4(), `letta-import-${author}-${name}-${Date.now()}.af`);
|
|
87138
|
-
await
|
|
87467
|
+
await writeFile9(tempPath, afContent, "utf-8");
|
|
87139
87468
|
try {
|
|
87140
87469
|
const result = await importAgentFromFile({
|
|
87141
87470
|
filePath: tempPath,
|
|
@@ -87366,7 +87695,7 @@ async function prepareHeadlessToolExecutionContext(params) {
|
|
|
87366
87695
|
agentId: params.agentId,
|
|
87367
87696
|
conversationId: params.conversationId,
|
|
87368
87697
|
overrideModel: params.overrideModel,
|
|
87369
|
-
workingDirectory:
|
|
87698
|
+
workingDirectory: getCurrentWorkingDirectory(),
|
|
87370
87699
|
exclude: ["AskUserQuestion"]
|
|
87371
87700
|
});
|
|
87372
87701
|
return {
|
|
@@ -87374,6 +87703,20 @@ async function prepareHeadlessToolExecutionContext(params) {
|
|
|
87374
87703
|
availableTools: preparedToolContext.preparedToolContext.clientTools.map((tool) => tool.name)
|
|
87375
87704
|
};
|
|
87376
87705
|
}
|
|
87706
|
+
async function flushAndExit(code) {
|
|
87707
|
+
const flushWritable = (stream2) => new Promise((resolve28) => {
|
|
87708
|
+
if (stream2.destroyed || stream2.writableEnded) {
|
|
87709
|
+
resolve28();
|
|
87710
|
+
return;
|
|
87711
|
+
}
|
|
87712
|
+
stream2.write("", () => resolve28());
|
|
87713
|
+
});
|
|
87714
|
+
await Promise.allSettled([
|
|
87715
|
+
flushWritable(process.stdout),
|
|
87716
|
+
flushWritable(process.stderr)
|
|
87717
|
+
]);
|
|
87718
|
+
process.exit(code);
|
|
87719
|
+
}
|
|
87377
87720
|
async function handleHeadlessCommand(parsedArgs, model, skillsDirectoryOverride, skillSourcesOverride, systemInfoReminderEnabledOverride) {
|
|
87378
87721
|
const { values, positionals } = parsedArgs;
|
|
87379
87722
|
telemetry.setSurface("headless");
|
|
@@ -87444,6 +87787,7 @@ In headless mode, use:
|
|
|
87444
87787
|
let forceNewConversation = values.new ?? false;
|
|
87445
87788
|
const fromAgentId = values["from-agent"];
|
|
87446
87789
|
let agent = null;
|
|
87790
|
+
let autoEnableMemfsForFreshAgent = false;
|
|
87447
87791
|
let specifiedAgentId = values.agent;
|
|
87448
87792
|
const specifiedAgentName = values.name;
|
|
87449
87793
|
let specifiedConversationId = values.conversation;
|
|
@@ -87726,7 +88070,7 @@ In headless mode, use:
|
|
|
87726
88070
|
}
|
|
87727
88071
|
if (!agent && forceNew) {
|
|
87728
88072
|
const updateArgs = getModelUpdateArgs2(model);
|
|
87729
|
-
const { isLettaCloud: isLettaCloud2
|
|
88073
|
+
const { isLettaCloud: isLettaCloud2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
|
|
87730
88074
|
const willAutoEnableMemfs = shouldAutoEnableMemfsForNewAgent && await isLettaCloud2();
|
|
87731
88075
|
const effectiveMemoryMode = requestedMemoryPromptMode ?? (willAutoEnableMemfs ? "memfs" : undefined);
|
|
87732
88076
|
const createOptions = {
|
|
@@ -87746,13 +88090,11 @@ In headless mode, use:
|
|
|
87746
88090
|
};
|
|
87747
88091
|
const result = await createAgent(createOptions);
|
|
87748
88092
|
agent = result.agent;
|
|
87749
|
-
|
|
87750
|
-
await enableMemfsIfCloud2(agent.id);
|
|
87751
|
-
}
|
|
88093
|
+
autoEnableMemfsForFreshAgent = willAutoEnableMemfs;
|
|
87752
88094
|
}
|
|
87753
88095
|
if (!agent) {
|
|
87754
88096
|
await settingsManager.loadLocalProjectSettings();
|
|
87755
|
-
const localAgentId = settingsManager.getLocalLastAgentId(
|
|
88097
|
+
const localAgentId = settingsManager.getLocalLastAgentId(getCurrentWorkingDirectory());
|
|
87756
88098
|
if (localAgentId) {
|
|
87757
88099
|
try {
|
|
87758
88100
|
agent = await client.agents.retrieve(localAgentId);
|
|
@@ -87807,13 +88149,14 @@ In headless mode, use:
|
|
|
87807
88149
|
let conversationId;
|
|
87808
88150
|
let effectiveReflectionSettings;
|
|
87809
88151
|
const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent";
|
|
88152
|
+
const startupMemfsFlag = autoEnableMemfsForFreshAgent ? true : memfsFlag;
|
|
87810
88153
|
let memfsBgPromise;
|
|
87811
88154
|
const secretsAgentId = agent?.id;
|
|
87812
88155
|
const secretsInitPromise = secretsAgentId ? init_secretsStore().then(() => exports_secretsStore).then(({ initSecretsFromServer: initSecretsFromServer2 }) => initSecretsFromServer2(secretsAgentId)) : Promise.resolve();
|
|
87813
88156
|
if (memfsStartupPolicy === "skip") {
|
|
87814
88157
|
try {
|
|
87815
88158
|
const { applyMemfsFlags: applyMemfsFlags2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
|
|
87816
|
-
await applyMemfsFlags2(agent.id,
|
|
88159
|
+
await applyMemfsFlags2(agent.id, startupMemfsFlag, noMemfsFlag, {
|
|
87817
88160
|
pullOnExistingRepo: false,
|
|
87818
88161
|
agentTags: agent.tags,
|
|
87819
88162
|
skipPromptUpdate: forceNew
|
|
@@ -87825,7 +88168,7 @@ In headless mode, use:
|
|
|
87825
88168
|
}
|
|
87826
88169
|
} else if (memfsStartupPolicy === "background") {
|
|
87827
88170
|
const { applyMemfsFlags: applyMemfsFlags2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
|
|
87828
|
-
memfsBgPromise = applyMemfsFlags2(agent.id,
|
|
88171
|
+
memfsBgPromise = applyMemfsFlags2(agent.id, startupMemfsFlag, noMemfsFlag, {
|
|
87829
88172
|
pullOnExistingRepo: true,
|
|
87830
88173
|
agentTags: agent.tags,
|
|
87831
88174
|
skipPromptUpdate: forceNew
|
|
@@ -87836,7 +88179,7 @@ In headless mode, use:
|
|
|
87836
88179
|
} else {
|
|
87837
88180
|
try {
|
|
87838
88181
|
const { applyMemfsFlags: applyMemfsFlags2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
|
|
87839
|
-
const memfsResult = await applyMemfsFlags2(agent.id,
|
|
88182
|
+
const memfsResult = await applyMemfsFlags2(agent.id, startupMemfsFlag, noMemfsFlag, {
|
|
87840
88183
|
pullOnExistingRepo: true,
|
|
87841
88184
|
agentTags: agent.tags,
|
|
87842
88185
|
skipPromptUpdate: forceNew
|
|
@@ -87939,7 +88282,7 @@ In headless mode, use:
|
|
|
87939
88282
|
}
|
|
87940
88283
|
markMilestone("HEADLESS_CONVERSATION_READY");
|
|
87941
88284
|
setConversationId2(conversationId);
|
|
87942
|
-
if (
|
|
88285
|
+
if (shouldPersistSessionState()) {
|
|
87943
88286
|
await settingsManager.loadLocalProjectSettings();
|
|
87944
88287
|
settingsManager.persistSession(agent.id, conversationId);
|
|
87945
88288
|
}
|
|
@@ -87978,7 +88321,7 @@ In headless mode, use:
|
|
|
87978
88321
|
conversation_id: conversationId,
|
|
87979
88322
|
model: agent.llm_config?.model ?? "",
|
|
87980
88323
|
tools: availableTools,
|
|
87981
|
-
cwd:
|
|
88324
|
+
cwd: getCurrentWorkingDirectory(),
|
|
87982
88325
|
mcp_servers: [],
|
|
87983
88326
|
permission_mode: "",
|
|
87984
88327
|
slash_commands: [],
|
|
@@ -88139,6 +88482,7 @@ ${SYSTEM_REMINDER_CLOSE}
|
|
|
88139
88482
|
},
|
|
88140
88483
|
state: sharedReminderState,
|
|
88141
88484
|
sessionContextReminderEnabled: systemInfoReminderEnabled,
|
|
88485
|
+
workingDirectory: getCurrentWorkingDirectory(),
|
|
88142
88486
|
reflectionSettings: effectiveReflectionSettings,
|
|
88143
88487
|
skillSources: resolvedSkillSources,
|
|
88144
88488
|
resolvePlanModeReminder: async () => {
|
|
@@ -88312,7 +88656,7 @@ ${loadedContents.join(`
|
|
|
88312
88656
|
} else {
|
|
88313
88657
|
console.error(`Conversation is busy, waiting ${Math.round(retryDelayMs / 1000)}s and retrying...`);
|
|
88314
88658
|
}
|
|
88315
|
-
await new Promise((
|
|
88659
|
+
await new Promise((resolve28) => setTimeout(resolve28, retryDelayMs));
|
|
88316
88660
|
continue;
|
|
88317
88661
|
}
|
|
88318
88662
|
}
|
|
@@ -88361,7 +88705,7 @@ ${loadedContents.join(`
|
|
|
88361
88705
|
const delaySeconds = Math.round(delayMs / 1000);
|
|
88362
88706
|
console.error(`Transient API error before streaming (attempt ${attempt} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
|
|
88363
88707
|
}
|
|
88364
|
-
await new Promise((
|
|
88708
|
+
await new Promise((resolve28) => setTimeout(resolve28, delayMs));
|
|
88365
88709
|
conversationBusyRetries = 0;
|
|
88366
88710
|
continue;
|
|
88367
88711
|
}
|
|
@@ -88600,7 +88944,7 @@ ${loadedContents.join(`
|
|
|
88600
88944
|
const delaySeconds = Math.round(delayMs / 1000);
|
|
88601
88945
|
console.error(`LLM API error encountered (attempt ${attempt} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
|
|
88602
88946
|
}
|
|
88603
|
-
await new Promise((
|
|
88947
|
+
await new Promise((resolve28) => setTimeout(resolve28, delayMs));
|
|
88604
88948
|
refreshCurrentInputOtids();
|
|
88605
88949
|
continue;
|
|
88606
88950
|
}
|
|
@@ -88686,7 +89030,7 @@ ${loadedContents.join(`
|
|
|
88686
89030
|
} else {
|
|
88687
89031
|
console.error(`Empty LLM response, retrying (attempt ${attempt} of ${EMPTY_RESPONSE_MAX_RETRIES2})...`);
|
|
88688
89032
|
}
|
|
88689
|
-
await new Promise((
|
|
89033
|
+
await new Promise((resolve28) => setTimeout(resolve28, delayMs));
|
|
88690
89034
|
refreshCurrentInputOtids();
|
|
88691
89035
|
continue;
|
|
88692
89036
|
}
|
|
@@ -88714,7 +89058,7 @@ ${loadedContents.join(`
|
|
|
88714
89058
|
const delaySeconds = Math.round(delayMs / 1000);
|
|
88715
89059
|
console.error(`LLM API error encountered (attempt ${attempt} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
|
|
88716
89060
|
}
|
|
88717
|
-
await new Promise((
|
|
89061
|
+
await new Promise((resolve28) => setTimeout(resolve28, delayMs));
|
|
88718
89062
|
refreshCurrentInputOtids();
|
|
88719
89063
|
continue;
|
|
88720
89064
|
}
|
|
@@ -88843,6 +89187,7 @@ ${loadedContents.join(`
|
|
|
88843
89187
|
}
|
|
88844
89188
|
markMilestone("HEADLESS_COMPLETE");
|
|
88845
89189
|
reportAllMilestones();
|
|
89190
|
+
await flushAndExit(0);
|
|
88846
89191
|
}
|
|
88847
89192
|
async function runBidirectionalMode(agent, conversationId, client, _outputFormat, includePartialMessages, availableTools, skillSources, systemInfoReminderEnabled, reflectionSettings) {
|
|
88848
89193
|
const sessionId = agent.id;
|
|
@@ -88855,7 +89200,7 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
|
|
|
88855
89200
|
conversation_id: conversationId,
|
|
88856
89201
|
model: agent.llm_config?.model,
|
|
88857
89202
|
tools: availableTools,
|
|
88858
|
-
cwd:
|
|
89203
|
+
cwd: getCurrentWorkingDirectory(),
|
|
88859
89204
|
memfs_enabled: settingsManager.isMemfsEnabled(agent.id),
|
|
88860
89205
|
skill_sources: skillSources,
|
|
88861
89206
|
system_info_reminder_enabled: systemInfoReminderEnabled,
|
|
@@ -89047,9 +89392,9 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
|
|
|
89047
89392
|
const syntheticUserLine = serializeQueuedMessageAsUserLine(queuedMessage);
|
|
89048
89393
|
maybeNotifyBlocked(syntheticUserLine);
|
|
89049
89394
|
if (lineResolver) {
|
|
89050
|
-
const
|
|
89395
|
+
const resolve28 = lineResolver;
|
|
89051
89396
|
lineResolver = null;
|
|
89052
|
-
|
|
89397
|
+
resolve28(syntheticUserLine);
|
|
89053
89398
|
return;
|
|
89054
89399
|
}
|
|
89055
89400
|
lineQueue.push(syntheticUserLine);
|
|
@@ -89057,9 +89402,9 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
|
|
|
89057
89402
|
rl.on("line", (line) => {
|
|
89058
89403
|
maybeNotifyBlocked(line);
|
|
89059
89404
|
if (lineResolver) {
|
|
89060
|
-
const
|
|
89405
|
+
const resolve28 = lineResolver;
|
|
89061
89406
|
lineResolver = null;
|
|
89062
|
-
|
|
89407
|
+
resolve28(line);
|
|
89063
89408
|
} else {
|
|
89064
89409
|
lineQueue.push(line);
|
|
89065
89410
|
}
|
|
@@ -89068,17 +89413,17 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
|
|
|
89068
89413
|
setMessageQueueAdder(null);
|
|
89069
89414
|
msgQueueRuntime.clear("shutdown");
|
|
89070
89415
|
if (lineResolver) {
|
|
89071
|
-
const
|
|
89416
|
+
const resolve28 = lineResolver;
|
|
89072
89417
|
lineResolver = null;
|
|
89073
|
-
|
|
89418
|
+
resolve28(null);
|
|
89074
89419
|
}
|
|
89075
89420
|
});
|
|
89076
89421
|
async function getNextLine() {
|
|
89077
89422
|
if (lineQueue.length > 0) {
|
|
89078
89423
|
return lineQueue.shift() ?? null;
|
|
89079
89424
|
}
|
|
89080
|
-
return new Promise((
|
|
89081
|
-
lineResolver =
|
|
89425
|
+
return new Promise((resolve28) => {
|
|
89426
|
+
lineResolver = resolve28;
|
|
89082
89427
|
});
|
|
89083
89428
|
}
|
|
89084
89429
|
async function requestPermission(toolCallId, toolName, toolInput) {
|
|
@@ -89497,6 +89842,7 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
|
|
|
89497
89842
|
},
|
|
89498
89843
|
state: sharedReminderState,
|
|
89499
89844
|
sessionContextReminderEnabled: systemInfoReminderEnabled,
|
|
89845
|
+
workingDirectory: getCurrentWorkingDirectory(),
|
|
89500
89846
|
reflectionSettings,
|
|
89501
89847
|
skillSources,
|
|
89502
89848
|
resolvePlanModeReminder: async () => {
|
|
@@ -89581,7 +89927,7 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
|
|
|
89581
89927
|
uuid: `retry-bidir-${randomUUID8()}`
|
|
89582
89928
|
};
|
|
89583
89929
|
console.log(JSON.stringify(retryMsg));
|
|
89584
|
-
await new Promise((
|
|
89930
|
+
await new Promise((resolve28) => setTimeout(resolve28, delayMs));
|
|
89585
89931
|
continue;
|
|
89586
89932
|
}
|
|
89587
89933
|
throw preStreamError;
|
|
@@ -89822,6 +90168,7 @@ var init_headless = __esm(async () => {
|
|
|
89822
90168
|
init_constants();
|
|
89823
90169
|
init_diffPreview();
|
|
89824
90170
|
init_queueRuntime();
|
|
90171
|
+
init_runtime_context();
|
|
89825
90172
|
init_interactivePolicy();
|
|
89826
90173
|
init_debug();
|
|
89827
90174
|
init_timing();
|
|
@@ -89876,10 +90223,10 @@ async function detectAndEnableKittyProtocol() {
|
|
|
89876
90223
|
detectionComplete = true;
|
|
89877
90224
|
return;
|
|
89878
90225
|
}
|
|
89879
|
-
return new Promise((
|
|
90226
|
+
return new Promise((resolve28) => {
|
|
89880
90227
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
89881
90228
|
detectionComplete = true;
|
|
89882
|
-
|
|
90229
|
+
resolve28();
|
|
89883
90230
|
return;
|
|
89884
90231
|
}
|
|
89885
90232
|
const originalRawMode = process.stdin.isRaw;
|
|
@@ -89912,7 +90259,7 @@ async function detectAndEnableKittyProtocol() {
|
|
|
89912
90259
|
console.error("[kitty] protocol query unsupported; enabled anyway (best-effort)");
|
|
89913
90260
|
}
|
|
89914
90261
|
detectionComplete = true;
|
|
89915
|
-
|
|
90262
|
+
resolve28();
|
|
89916
90263
|
};
|
|
89917
90264
|
const handleData = (data) => {
|
|
89918
90265
|
if (timeoutId === undefined) {
|
|
@@ -112098,7 +112445,7 @@ var init_pasteRegistry = __esm(() => {
|
|
|
112098
112445
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
112099
112446
|
import { existsSync as existsSync29, readFileSync as readFileSync16, statSync as statSync10, unlinkSync as unlinkSync10 } from "node:fs";
|
|
112100
112447
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
112101
|
-
import { basename as basename4, extname as extname5, isAbsolute as isAbsolute18, join as join38, resolve as
|
|
112448
|
+
import { basename as basename4, extname as extname5, isAbsolute as isAbsolute18, join as join38, resolve as resolve28 } from "node:path";
|
|
112102
112449
|
function countLines2(text) {
|
|
112103
112450
|
return (text.match(/\r\n|\r|\n/g) || []).length + 1;
|
|
112104
112451
|
}
|
|
@@ -112146,7 +112493,7 @@ function translatePasteForImages(paste) {
|
|
|
112146
112493
|
} catch {}
|
|
112147
112494
|
}
|
|
112148
112495
|
if (!isAbsolute18(filePath))
|
|
112149
|
-
filePath =
|
|
112496
|
+
filePath = resolve28(process.cwd(), filePath);
|
|
112150
112497
|
const ext3 = extname5(filePath || "").toLowerCase();
|
|
112151
112498
|
if (IMAGE_EXTS.has(ext3) && existsSync29(filePath) && statSync10(filePath).isFile()) {
|
|
112152
112499
|
const buf = readFileSync16(filePath);
|
|
@@ -113292,9 +113639,16 @@ var init_registry = __esm(async () => {
|
|
|
113292
113639
|
}
|
|
113293
113640
|
},
|
|
113294
113641
|
"/reflect": {
|
|
113295
|
-
desc: "Launch
|
|
113642
|
+
desc: "Launch reflection (/reflect [transcript_file])",
|
|
113643
|
+
args: "[transcript_file]",
|
|
113296
113644
|
order: 50,
|
|
113297
|
-
|
|
113645
|
+
handler: () => {
|
|
113646
|
+
return "Launching reflection agent...";
|
|
113647
|
+
}
|
|
113648
|
+
},
|
|
113649
|
+
"/reflection": {
|
|
113650
|
+
desc: "Alias for /reflect",
|
|
113651
|
+
args: "[transcript_file]",
|
|
113298
113652
|
handler: () => {
|
|
113299
113653
|
return "Launching reflection agent...";
|
|
113300
113654
|
}
|
|
@@ -113654,14 +114008,6 @@ Location: ${keybindingsPath}`;
|
|
|
113654
114008
|
return "Clearing credentials...";
|
|
113655
114009
|
}
|
|
113656
114010
|
},
|
|
113657
|
-
"/empanada": {
|
|
113658
|
-
desc: "Order empanadas from Empanada Empire",
|
|
113659
|
-
order: 44.5,
|
|
113660
|
-
args: "[address]",
|
|
113661
|
-
handler: () => {
|
|
113662
|
-
return "Checking Empanada Empire...";
|
|
113663
|
-
}
|
|
113664
|
-
},
|
|
113665
114011
|
"/ralph": {
|
|
113666
114012
|
desc: 'Start Ralph Wiggum loop (/ralph [prompt] [--completion-promise "X"] [--max-iterations N])',
|
|
113667
114013
|
order: 45,
|
|
@@ -114166,11 +114512,11 @@ var init_HelpDialog = __esm(async () => {
|
|
|
114166
114512
|
|
|
114167
114513
|
// src/hooks/writer.ts
|
|
114168
114514
|
import { homedir as homedir31 } from "node:os";
|
|
114169
|
-
import { resolve as
|
|
114515
|
+
import { resolve as resolve29 } from "node:path";
|
|
114170
114516
|
function isProjectSettingsPathCollidingWithGlobal2(workingDirectory) {
|
|
114171
114517
|
const home = process.env.HOME || homedir31();
|
|
114172
|
-
const globalSettingsPath =
|
|
114173
|
-
const projectSettingsPath =
|
|
114518
|
+
const globalSettingsPath = resolve29(home, ".letta", "settings.json");
|
|
114519
|
+
const projectSettingsPath = resolve29(workingDirectory, ".letta", "settings.json");
|
|
114174
114520
|
return globalSettingsPath === projectSettingsPath;
|
|
114175
114521
|
}
|
|
114176
114522
|
function loadHooksFromLocation(location, workingDirectory = process.cwd()) {
|
|
@@ -116808,7 +117154,7 @@ var init_AgentInfoBar = __esm(async () => {
|
|
|
116808
117154
|
|
|
116809
117155
|
// src/cli/helpers/fileSearch.ts
|
|
116810
117156
|
import { readdirSync as readdirSync13, statSync as statSync11 } from "node:fs";
|
|
116811
|
-
import { join as join41, relative as relative15, resolve as
|
|
117157
|
+
import { join as join41, relative as relative15, resolve as resolve30 } from "node:path";
|
|
116812
117158
|
function searchDirectoryRecursive(dir, pattern, maxResults = 200, results = [], depth = 0, maxDepth = 10, lowerPattern = pattern.toLowerCase()) {
|
|
116813
117159
|
if (results.length >= maxResults || depth >= maxDepth) {
|
|
116814
117160
|
return results;
|
|
@@ -116851,7 +117197,7 @@ async function searchFiles(query, deep = false) {
|
|
|
116851
117197
|
const dirPart = query.slice(0, lastSlashIndex);
|
|
116852
117198
|
const pattern = query.slice(lastSlashIndex + 1);
|
|
116853
117199
|
try {
|
|
116854
|
-
const resolvedDir =
|
|
117200
|
+
const resolvedDir = resolve30(getIndexRoot(), dirPart);
|
|
116855
117201
|
try {
|
|
116856
117202
|
statSync11(resolvedDir);
|
|
116857
117203
|
searchDir = resolvedDir;
|
|
@@ -131650,7 +131996,7 @@ async function executeStatusLineCommand(command, payload, options) {
|
|
|
131650
131996
|
};
|
|
131651
131997
|
}
|
|
131652
131998
|
function runWithLauncher(launcher, inputJson, timeout, signal, workingDirectory, startTime) {
|
|
131653
|
-
return new Promise((
|
|
131999
|
+
return new Promise((resolve31, reject) => {
|
|
131654
132000
|
const [executable, ...args] = launcher;
|
|
131655
132001
|
if (!executable) {
|
|
131656
132002
|
reject(new Error("Empty launcher"));
|
|
@@ -131663,7 +132009,7 @@ function runWithLauncher(launcher, inputJson, timeout, signal, workingDirectory,
|
|
|
131663
132009
|
const safeResolve = (result) => {
|
|
131664
132010
|
if (!resolved) {
|
|
131665
132011
|
resolved = true;
|
|
131666
|
-
|
|
132012
|
+
resolve31(result);
|
|
131667
132013
|
}
|
|
131668
132014
|
};
|
|
131669
132015
|
let child;
|
|
@@ -132840,14 +133186,14 @@ __export(exports_export, {
|
|
|
132840
133186
|
packageSkills: () => packageSkills
|
|
132841
133187
|
});
|
|
132842
133188
|
import { readdir as readdir10, readFile as readFile13 } from "node:fs/promises";
|
|
132843
|
-
import { relative as relative16, resolve as
|
|
133189
|
+
import { relative as relative16, resolve as resolve31 } from "node:path";
|
|
132844
133190
|
async function packageSkills(agentId, skillsDir) {
|
|
132845
133191
|
const skills = [];
|
|
132846
133192
|
const skillNames = new Set;
|
|
132847
133193
|
const dirsToCheck = skillsDir ? [skillsDir] : [
|
|
132848
133194
|
agentId && getAgentSkillsDir(agentId),
|
|
132849
|
-
|
|
132850
|
-
|
|
133195
|
+
resolve31(process.cwd(), ".skills"),
|
|
133196
|
+
resolve31(process.env.HOME || "~", ".letta", "skills")
|
|
132851
133197
|
].filter((dir) => Boolean(dir));
|
|
132852
133198
|
for (const baseDir of dirsToCheck) {
|
|
132853
133199
|
try {
|
|
@@ -132857,8 +133203,8 @@ async function packageSkills(agentId, skillsDir) {
|
|
|
132857
133203
|
continue;
|
|
132858
133204
|
if (skillNames.has(entry.name))
|
|
132859
133205
|
continue;
|
|
132860
|
-
const skillDir =
|
|
132861
|
-
const skillMdPath =
|
|
133206
|
+
const skillDir = resolve31(baseDir, entry.name);
|
|
133207
|
+
const skillMdPath = resolve31(skillDir, "SKILL.md");
|
|
132862
133208
|
try {
|
|
132863
133209
|
await readFile13(skillMdPath, "utf-8");
|
|
132864
133210
|
} catch {
|
|
@@ -132888,7 +133234,7 @@ async function readSkillFiles(skillDir) {
|
|
|
132888
133234
|
async function walk(dir) {
|
|
132889
133235
|
const entries = await readdir10(dir, { withFileTypes: true });
|
|
132890
133236
|
for (const entry of entries) {
|
|
132891
|
-
const fullPath =
|
|
133237
|
+
const fullPath = resolve31(dir, entry.name);
|
|
132892
133238
|
if (entry.isDirectory()) {
|
|
132893
133239
|
await walk(fullPath);
|
|
132894
133240
|
} else {
|
|
@@ -135372,7 +135718,7 @@ ${newState.originalPrompt}`,
|
|
|
135372
135718
|
cancelled = true;
|
|
135373
135719
|
break;
|
|
135374
135720
|
}
|
|
135375
|
-
await new Promise((
|
|
135721
|
+
await new Promise((resolve32) => setTimeout(resolve32, 100));
|
|
135376
135722
|
}
|
|
135377
135723
|
buffersRef.current.byId.delete(statusId);
|
|
135378
135724
|
buffersRef.current.order = buffersRef.current.order.filter((id) => id !== statusId);
|
|
@@ -135436,7 +135782,7 @@ ${newState.originalPrompt}`,
|
|
|
135436
135782
|
cancelled = true;
|
|
135437
135783
|
break;
|
|
135438
135784
|
}
|
|
135439
|
-
await new Promise((
|
|
135785
|
+
await new Promise((resolve32) => setTimeout(resolve32, 100));
|
|
135440
135786
|
}
|
|
135441
135787
|
if (retryStatusId) {
|
|
135442
135788
|
buffersRef.current.byId.delete(retryStatusId);
|
|
@@ -136194,7 +136540,7 @@ ${feedback}
|
|
|
136194
136540
|
});
|
|
136195
136541
|
buffersRef.current.order.push(statusId);
|
|
136196
136542
|
refreshDerived();
|
|
136197
|
-
await new Promise((
|
|
136543
|
+
await new Promise((resolve32) => setTimeout(resolve32, delayMs));
|
|
136198
136544
|
buffersRef.current.byId.delete(statusId);
|
|
136199
136545
|
buffersRef.current.order = buffersRef.current.order.filter((id) => id !== statusId);
|
|
136200
136546
|
refreshDerived();
|
|
@@ -136254,7 +136600,7 @@ ${feedback}
|
|
|
136254
136600
|
cancelled = true;
|
|
136255
136601
|
break;
|
|
136256
136602
|
}
|
|
136257
|
-
await new Promise((
|
|
136603
|
+
await new Promise((resolve32) => setTimeout(resolve32, 100));
|
|
136258
136604
|
}
|
|
136259
136605
|
if (retryStatusId) {
|
|
136260
136606
|
buffersRef.current.byId.delete(retryStatusId);
|
|
@@ -143468,11 +143814,11 @@ __export(exports_import2, {
|
|
|
143468
143814
|
extractSkillsFromAf: () => extractSkillsFromAf2
|
|
143469
143815
|
});
|
|
143470
143816
|
import { createReadStream as createReadStream2 } from "node:fs";
|
|
143471
|
-
import { chmod as chmod2, mkdir as mkdir8, readFile as readFile14, writeFile as
|
|
143472
|
-
import { dirname as dirname19, resolve as
|
|
143817
|
+
import { chmod as chmod2, mkdir as mkdir8, readFile as readFile14, writeFile as writeFile9 } from "node:fs/promises";
|
|
143818
|
+
import { dirname as dirname19, resolve as resolve32 } from "node:path";
|
|
143473
143819
|
async function importAgentFromFile2(options) {
|
|
143474
143820
|
const client = await getClient();
|
|
143475
|
-
const resolvedPath =
|
|
143821
|
+
const resolvedPath = resolve32(options.filePath);
|
|
143476
143822
|
const file = createReadStream2(resolvedPath);
|
|
143477
143823
|
const importResponse = await client.agents.importFile({
|
|
143478
143824
|
file,
|
|
@@ -143507,7 +143853,7 @@ async function extractSkillsFromAf2(afPath, destDir) {
|
|
|
143507
143853
|
return [];
|
|
143508
143854
|
}
|
|
143509
143855
|
for (const skill2 of afData.skills) {
|
|
143510
|
-
const skillDir =
|
|
143856
|
+
const skillDir = resolve32(destDir, skill2.name);
|
|
143511
143857
|
await mkdir8(skillDir, { recursive: true });
|
|
143512
143858
|
if (skill2.files) {
|
|
143513
143859
|
await writeSkillFiles2(skillDir, skill2.files);
|
|
@@ -143527,9 +143873,9 @@ async function writeSkillFiles2(skillDir, files) {
|
|
|
143527
143873
|
}
|
|
143528
143874
|
}
|
|
143529
143875
|
async function writeSkillFile2(skillDir, filePath, content) {
|
|
143530
|
-
const fullPath =
|
|
143876
|
+
const fullPath = resolve32(skillDir, filePath);
|
|
143531
143877
|
await mkdir8(dirname19(fullPath), { recursive: true });
|
|
143532
|
-
await
|
|
143878
|
+
await writeFile9(fullPath, content, "utf-8");
|
|
143533
143879
|
const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
|
|
143534
143880
|
if (isScript) {
|
|
143535
143881
|
try {
|
|
@@ -143582,7 +143928,7 @@ function parseRegistryHandle2(handle) {
|
|
|
143582
143928
|
async function importAgentFromRegistry2(options) {
|
|
143583
143929
|
const { tmpdir: tmpdir7 } = await import("node:os");
|
|
143584
143930
|
const { join: join50 } = await import("node:path");
|
|
143585
|
-
const { writeFile:
|
|
143931
|
+
const { writeFile: writeFile10, unlink: unlink3 } = await import("node:fs/promises");
|
|
143586
143932
|
const { author, name } = parseRegistryHandle2(options.handle);
|
|
143587
143933
|
const rawUrl = `https://raw.githubusercontent.com/${AGENT_REGISTRY_OWNER2}/${AGENT_REGISTRY_REPO2}/refs/heads/${AGENT_REGISTRY_BRANCH2}/agents/@${author}/${name}/${name}.af`;
|
|
143588
143934
|
const response = await fetch(rawUrl);
|
|
@@ -143594,7 +143940,7 @@ async function importAgentFromRegistry2(options) {
|
|
|
143594
143940
|
}
|
|
143595
143941
|
const afContent = await response.text();
|
|
143596
143942
|
const tempPath = join50(tmpdir7(), `letta-import-${author}-${name}-${Date.now()}.af`);
|
|
143597
|
-
await
|
|
143943
|
+
await writeFile10(tempPath, afContent, "utf-8");
|
|
143598
143944
|
try {
|
|
143599
143945
|
const result = await importAgentFromFile2({
|
|
143600
143946
|
filePath: tempPath,
|
|
@@ -148529,12 +148875,15 @@ async function runMemfsSubcommand(argv) {
|
|
|
148529
148875
|
|
|
148530
148876
|
// src/cli/subcommands/messages.ts
|
|
148531
148877
|
await init_client2();
|
|
148878
|
+
import { writeFile as writeFile6 } from "node:fs/promises";
|
|
148879
|
+
import { resolve as resolve23 } from "node:path";
|
|
148532
148880
|
import { parseArgs as parseArgs8 } from "node:util";
|
|
148533
148881
|
function printUsage5() {
|
|
148534
148882
|
console.log(`
|
|
148535
148883
|
Usage:
|
|
148536
148884
|
letta messages search --query <text> [options]
|
|
148537
148885
|
letta messages list [options]
|
|
148886
|
+
letta messages transcript --conversation <id> [options]
|
|
148538
148887
|
|
|
148539
148888
|
Search options:
|
|
148540
148889
|
--query <text> Search query (required)
|
|
@@ -148556,6 +148905,16 @@ List options:
|
|
|
148556
148905
|
--start-date <date> Client-side filter: after this date (ISO format)
|
|
148557
148906
|
--end-date <date> Client-side filter: before this date (ISO format)
|
|
148558
148907
|
|
|
148908
|
+
Transcript options:
|
|
148909
|
+
--conversation <id> Conversation ID to export (required)
|
|
148910
|
+
--conversation-id <id> Alias for --conversation
|
|
148911
|
+
--agent <id> Required when conversation is "default"
|
|
148912
|
+
--agent-id <id> Alias for --agent
|
|
148913
|
+
--limit <n> Page size while fetching (default: 100)
|
|
148914
|
+
--max-pages <n> Max pagination pages to fetch (default: 200)
|
|
148915
|
+
--out <path> Write transcript text to file
|
|
148916
|
+
--output <path> Alias for --out
|
|
148917
|
+
|
|
148559
148918
|
Notes:
|
|
148560
148919
|
- Output is JSON only.
|
|
148561
148920
|
- Uses CLI auth; override with LETTA_API_KEY/LETTA_BASE_URL if needed.
|
|
@@ -148597,7 +148956,12 @@ var MESSAGES_OPTIONS = {
|
|
|
148597
148956
|
"agent-id": { type: "string" },
|
|
148598
148957
|
after: { type: "string" },
|
|
148599
148958
|
before: { type: "string" },
|
|
148600
|
-
order: { type: "string" }
|
|
148959
|
+
order: { type: "string" },
|
|
148960
|
+
conversation: { type: "string" },
|
|
148961
|
+
"conversation-id": { type: "string" },
|
|
148962
|
+
"max-pages": { type: "string" },
|
|
148963
|
+
out: { type: "string" },
|
|
148964
|
+
output: { type: "string" }
|
|
148601
148965
|
};
|
|
148602
148966
|
function parseMessagesArgs(argv) {
|
|
148603
148967
|
return parseArgs8({
|
|
@@ -148624,6 +148988,115 @@ async function runMessagesSubcommand(argv) {
|
|
|
148624
148988
|
}
|
|
148625
148989
|
try {
|
|
148626
148990
|
const client = await getClient();
|
|
148991
|
+
const renderText = (value) => {
|
|
148992
|
+
if (typeof value === "string")
|
|
148993
|
+
return value;
|
|
148994
|
+
if (!Array.isArray(value))
|
|
148995
|
+
return "";
|
|
148996
|
+
return value.map((part) => {
|
|
148997
|
+
if (part && typeof part === "object" && "type" in part && part.type === "text" && "text" in part) {
|
|
148998
|
+
return typeof part.text === "string" ? part.text : "";
|
|
148999
|
+
}
|
|
149000
|
+
return "";
|
|
149001
|
+
}).filter((text) => text.length > 0).join(`
|
|
149002
|
+
`);
|
|
149003
|
+
};
|
|
149004
|
+
const renderUnknown = (value) => {
|
|
149005
|
+
if (typeof value === "string")
|
|
149006
|
+
return value;
|
|
149007
|
+
if (value === null || value === undefined)
|
|
149008
|
+
return "";
|
|
149009
|
+
try {
|
|
149010
|
+
return JSON.stringify(value);
|
|
149011
|
+
} catch {
|
|
149012
|
+
return String(value);
|
|
149013
|
+
}
|
|
149014
|
+
};
|
|
149015
|
+
const safeTypeLabel = (msg) => msg.message_type || "unknown_message";
|
|
149016
|
+
const formatEntry = (msg) => {
|
|
149017
|
+
const timestamp = msg.date || "unknown-time";
|
|
149018
|
+
const type = safeTypeLabel(msg);
|
|
149019
|
+
if (type === "user_message") {
|
|
149020
|
+
const text = renderText(msg.content);
|
|
149021
|
+
return [`[${timestamp}] user`, text || "(empty)"];
|
|
149022
|
+
}
|
|
149023
|
+
if (type === "assistant_message") {
|
|
149024
|
+
const text = renderText(msg.content);
|
|
149025
|
+
return [`[${timestamp}] assistant`, text || "(empty)"];
|
|
149026
|
+
}
|
|
149027
|
+
if (type === "reasoning_message") {
|
|
149028
|
+
return [
|
|
149029
|
+
`[${timestamp}] reasoning`,
|
|
149030
|
+
msg.reasoning && msg.reasoning.length > 0 ? msg.reasoning : "(empty)"
|
|
149031
|
+
];
|
|
149032
|
+
}
|
|
149033
|
+
if (type === "tool_call_message" || type === "approval_request_message") {
|
|
149034
|
+
const calls = Array.isArray(msg.tool_calls) ? msg.tool_calls : msg.tool_call ? [msg.tool_call] : [];
|
|
149035
|
+
if (calls.length === 0) {
|
|
149036
|
+
return [`[${timestamp}] ${type}`, "(no tool call payload)"];
|
|
149037
|
+
}
|
|
149038
|
+
return calls.flatMap((call) => {
|
|
149039
|
+
const header = `[${timestamp}] tool_call ${call.name || "unknown"} (${call.tool_call_id || "no-id"})`;
|
|
149040
|
+
const args = call.arguments ? call.arguments : "{}";
|
|
149041
|
+
return [header, args];
|
|
149042
|
+
});
|
|
149043
|
+
}
|
|
149044
|
+
if (type === "tool_return_message") {
|
|
149045
|
+
const returns = Array.isArray(msg.tool_returns) ? msg.tool_returns : [
|
|
149046
|
+
{
|
|
149047
|
+
tool_call_id: msg.tool_call_id,
|
|
149048
|
+
status: msg.status,
|
|
149049
|
+
tool_return: msg.tool_return,
|
|
149050
|
+
func_response: msg.func_response
|
|
149051
|
+
}
|
|
149052
|
+
];
|
|
149053
|
+
return returns.flatMap((ret) => {
|
|
149054
|
+
const header = `[${timestamp}] tool_return (${ret.tool_call_id || "no-id"}) status=${ret.status || "unknown"}`;
|
|
149055
|
+
const body = renderUnknown(ret.tool_return ?? ret.func_response);
|
|
149056
|
+
return [header, body || "(empty)"];
|
|
149057
|
+
});
|
|
149058
|
+
}
|
|
149059
|
+
const fallbackText = renderText(msg.content) || renderUnknown(msg.content) || "(no content)";
|
|
149060
|
+
return [`[${timestamp}] ${type}`, fallbackText];
|
|
149061
|
+
};
|
|
149062
|
+
const sortChronological3 = (messages) => {
|
|
149063
|
+
return [...messages].sort((a, b) => {
|
|
149064
|
+
const ta = a.date ? new Date(a.date).getTime() : 0;
|
|
149065
|
+
const tb = b.date ? new Date(b.date).getTime() : 0;
|
|
149066
|
+
return ta - tb;
|
|
149067
|
+
});
|
|
149068
|
+
};
|
|
149069
|
+
const fetchConversationMessages = async (conversationId, agentIdForDefault, pageLimit, maxPages) => {
|
|
149070
|
+
const collected = [];
|
|
149071
|
+
const seenIds = new Set;
|
|
149072
|
+
let cursorBefore;
|
|
149073
|
+
for (let pageIndex = 0;pageIndex < maxPages; pageIndex += 1) {
|
|
149074
|
+
const page = await client.conversations.messages.list(conversationId, {
|
|
149075
|
+
limit: pageLimit,
|
|
149076
|
+
order: "desc",
|
|
149077
|
+
...conversationId === "default" && agentIdForDefault ? { agent_id: agentIdForDefault } : {},
|
|
149078
|
+
...cursorBefore ? { before: cursorBefore } : {}
|
|
149079
|
+
});
|
|
149080
|
+
const items = page.getPaginatedItems();
|
|
149081
|
+
if (items.length === 0) {
|
|
149082
|
+
break;
|
|
149083
|
+
}
|
|
149084
|
+
let newItems = 0;
|
|
149085
|
+
for (const item of items) {
|
|
149086
|
+
const id = item.id;
|
|
149087
|
+
if (id && !seenIds.has(id)) {
|
|
149088
|
+
seenIds.add(id);
|
|
149089
|
+
collected.push(item);
|
|
149090
|
+
newItems += 1;
|
|
149091
|
+
}
|
|
149092
|
+
}
|
|
149093
|
+
cursorBefore = items[items.length - 1]?.id;
|
|
149094
|
+
if (newItems === 0 || items.length < pageLimit) {
|
|
149095
|
+
break;
|
|
149096
|
+
}
|
|
149097
|
+
}
|
|
149098
|
+
return sortChronological3(collected);
|
|
149099
|
+
};
|
|
148627
149100
|
if (action === "search") {
|
|
148628
149101
|
const query = parsed.values.query;
|
|
148629
149102
|
if (!query || typeof query !== "string") {
|
|
@@ -148688,6 +149161,44 @@ async function runMessagesSubcommand(argv) {
|
|
|
148688
149161
|
console.log(JSON.stringify(sorted, null, 2));
|
|
148689
149162
|
return 0;
|
|
148690
149163
|
}
|
|
149164
|
+
if (action === "transcript") {
|
|
149165
|
+
const conversationId = parsed.values.conversation || parsed.values["conversation-id"];
|
|
149166
|
+
if (!conversationId || typeof conversationId !== "string") {
|
|
149167
|
+
console.error("Missing conversation id. Pass --conversation <id> or --conversation-id <id>.");
|
|
149168
|
+
return 1;
|
|
149169
|
+
}
|
|
149170
|
+
const agentId = getAgentId5(parsed.values.agent, parsed.values["agent-id"]);
|
|
149171
|
+
if (conversationId === "default" && !agentId) {
|
|
149172
|
+
console.error('Conversation "default" requires an agent id. Set LETTA_AGENT_ID or pass --agent/--agent-id.');
|
|
149173
|
+
return 1;
|
|
149174
|
+
}
|
|
149175
|
+
const pageLimit = Math.max(1, parseLimit3(parsed.values.limit, 100));
|
|
149176
|
+
const maxPages = Math.max(1, parseLimit3(parsed.values["max-pages"], 200));
|
|
149177
|
+
const outputPathRaw = parsed.values.out || parsed.values.output;
|
|
149178
|
+
const messages = await fetchConversationMessages(conversationId, agentId || undefined, pageLimit, maxPages);
|
|
149179
|
+
const transcript = messages.flatMap((msg) => formatEntry(msg)).join(`
|
|
149180
|
+
|
|
149181
|
+
`).trim();
|
|
149182
|
+
if (outputPathRaw && typeof outputPathRaw === "string") {
|
|
149183
|
+
const outputPath = resolve23(process.cwd(), outputPathRaw);
|
|
149184
|
+
await writeFile6(outputPath, `${transcript}
|
|
149185
|
+
`, "utf-8");
|
|
149186
|
+
console.log(JSON.stringify({
|
|
149187
|
+
conversation_id: conversationId,
|
|
149188
|
+
agent_id: agentId || null,
|
|
149189
|
+
message_count: messages.length,
|
|
149190
|
+
output_path: outputPath
|
|
149191
|
+
}, null, 2));
|
|
149192
|
+
return 0;
|
|
149193
|
+
}
|
|
149194
|
+
console.log(JSON.stringify({
|
|
149195
|
+
conversation_id: conversationId,
|
|
149196
|
+
agent_id: agentId || null,
|
|
149197
|
+
message_count: messages.length,
|
|
149198
|
+
transcript
|
|
149199
|
+
}, null, 2));
|
|
149200
|
+
return 0;
|
|
149201
|
+
}
|
|
148691
149202
|
} catch (error) {
|
|
148692
149203
|
console.error(error instanceof Error ? error.message : String(error));
|
|
148693
149204
|
return 1;
|
|
@@ -148728,7 +149239,7 @@ async function runSubcommand(argv) {
|
|
|
148728
149239
|
init_readOnlyShell();
|
|
148729
149240
|
init_shell_command_normalization();
|
|
148730
149241
|
import { homedir as homedir23 } from "node:os";
|
|
148731
|
-
import { isAbsolute as isAbsolute16, join as join29, relative as relative12, resolve as
|
|
149242
|
+
import { isAbsolute as isAbsolute16, join as join29, relative as relative12, resolve as resolve24 } from "node:path";
|
|
148732
149243
|
var MODE_KEY2 = Symbol.for("@letta/permissionMode");
|
|
148733
149244
|
var PLAN_FILE_KEY2 = Symbol.for("@letta/planFilePath");
|
|
148734
149245
|
var MODE_BEFORE_PLAN_KEY2 = Symbol.for("@letta/permissionModeBeforePlan");
|
|
@@ -148764,12 +149275,12 @@ function resolvePlanTargetPath2(targetPath, workingDirectory) {
|
|
|
148764
149275
|
if (!trimmedPath)
|
|
148765
149276
|
return null;
|
|
148766
149277
|
if (trimmedPath.startsWith("~/")) {
|
|
148767
|
-
return
|
|
149278
|
+
return resolve24(homedir23(), trimmedPath.slice(2));
|
|
148768
149279
|
}
|
|
148769
149280
|
if (isAbsolute16(trimmedPath)) {
|
|
148770
|
-
return
|
|
149281
|
+
return resolve24(trimmedPath);
|
|
148771
149282
|
}
|
|
148772
|
-
return
|
|
149283
|
+
return resolve24(workingDirectory, trimmedPath);
|
|
148773
149284
|
}
|
|
148774
149285
|
function isPathInPlansDir2(path23, plansDir) {
|
|
148775
149286
|
if (!path23.endsWith(".md"))
|
|
@@ -149045,7 +149556,7 @@ await __promiseAll([
|
|
|
149045
149556
|
]);
|
|
149046
149557
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
149047
149558
|
import { homedir as homedir24 } from "node:os";
|
|
149048
|
-
import { join as join30, resolve as
|
|
149559
|
+
import { join as join30, resolve as resolve25 } from "node:path";
|
|
149049
149560
|
var DEFAULT_SETTINGS3 = {
|
|
149050
149561
|
lastAgent: null,
|
|
149051
149562
|
tokenStreaming: false,
|
|
@@ -149070,6 +149581,9 @@ var DEFAULT_LETTA_API_URL2 = "https://api.letta.com";
|
|
|
149070
149581
|
function isSubagentProcess2() {
|
|
149071
149582
|
return process.env.LETTA_CODE_AGENT_ROLE === "subagent";
|
|
149072
149583
|
}
|
|
149584
|
+
function shouldPersistSessionState2() {
|
|
149585
|
+
return process.env.LETTA_CODE_AGENT_ROLE !== "subagent" && process.env.LETTA_DISABLE_SESSION_PERSIST !== "1";
|
|
149586
|
+
}
|
|
149073
149587
|
function normalizeBaseUrl2(baseUrl) {
|
|
149074
149588
|
let normalized = baseUrl.replace(/^https?:\/\//, "");
|
|
149075
149589
|
normalized = normalized.replace(/\/$/, "");
|
|
@@ -149496,7 +150010,7 @@ class SettingsManager2 {
|
|
|
149496
150010
|
return join30(workingDirectory, ".letta", "settings.json");
|
|
149497
150011
|
}
|
|
149498
150012
|
isProjectSettingsPathCollidingWithGlobal(workingDirectory) {
|
|
149499
|
-
return
|
|
150013
|
+
return resolve25(this.getProjectSettingsPath(workingDirectory)) === resolve25(this.getSettingsPath());
|
|
149500
150014
|
}
|
|
149501
150015
|
getLocalProjectSettingsPath(workingDirectory) {
|
|
149502
150016
|
return join30(workingDirectory, ".letta", "settings.local.json");
|
|
@@ -150458,8 +150972,8 @@ function acquireSwitchLock2() {
|
|
|
150458
150972
|
const lock = getSwitchLock2();
|
|
150459
150973
|
lock.refCount++;
|
|
150460
150974
|
if (lock.refCount === 1) {
|
|
150461
|
-
lock.promise = new Promise((
|
|
150462
|
-
lock.resolve =
|
|
150975
|
+
lock.promise = new Promise((resolve26) => {
|
|
150976
|
+
lock.resolve = resolve26;
|
|
150463
150977
|
});
|
|
150464
150978
|
}
|
|
150465
150979
|
}
|
|
@@ -150730,6 +151244,7 @@ SUBCOMMANDS (JSON-only)
|
|
|
150730
151244
|
letta agents list [--query <text> | --name <name> | --tags <tags>]
|
|
150731
151245
|
letta messages search --query <text> [--all-agents]
|
|
150732
151246
|
letta messages list [--agent <id>]
|
|
151247
|
+
letta messages transcript --conversation <id> [--out <path>]
|
|
150733
151248
|
letta blocks list --agent <id>
|
|
150734
151249
|
letta blocks copy --block-id <id> [--label <label>] [--agent <id>] [--override]
|
|
150735
151250
|
letta blocks attach --block-id <id> [--agent <id>] [--read-only] [--override]
|
|
@@ -150918,14 +151433,6 @@ async function main() {
|
|
|
150918
151433
|
}
|
|
150919
151434
|
const { checkAndAutoUpdate: checkAndAutoUpdate2 } = await init_auto_update().then(() => exports_auto_update);
|
|
150920
151435
|
const autoUpdatePromise = startStartupAutoUpdateCheck(checkAndAutoUpdate2);
|
|
150921
|
-
const { startDockerVersionCheck: startDockerVersionCheck2 } = await init_startup_docker_check().then(() => exports_startup_docker_check);
|
|
150922
|
-
startDockerVersionCheck2().catch(() => {});
|
|
150923
|
-
const { cleanupOldOverflowFiles: cleanupOldOverflowFiles2 } = await Promise.resolve().then(() => (init_overflow2(), exports_overflow));
|
|
150924
|
-
Promise.resolve().then(() => {
|
|
150925
|
-
try {
|
|
150926
|
-
cleanupOldOverflowFiles2(process.cwd());
|
|
150927
|
-
} catch {}
|
|
150928
|
-
});
|
|
150929
151436
|
const processedArgs = preprocessCliArgs(process.argv);
|
|
150930
151437
|
let values;
|
|
150931
151438
|
let positionals;
|
|
@@ -150951,7 +151458,7 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
|
|
|
150951
151458
|
printHelp();
|
|
150952
151459
|
const helpDelayMs = Number.parseInt(process.env.LETTA_TEST_HELP_EXIT_DELAY_MS ?? "", 10);
|
|
150953
151460
|
if (Number.isFinite(helpDelayMs) && helpDelayMs > 0) {
|
|
150954
|
-
await new Promise((
|
|
151461
|
+
await new Promise((resolve33) => setTimeout(resolve33, helpDelayMs));
|
|
150955
151462
|
}
|
|
150956
151463
|
process.exit(0);
|
|
150957
151464
|
}
|
|
@@ -151036,6 +151543,16 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
|
|
|
151036
151543
|
const isHeadless = values.prompt || values.run || !process.stdin.isTTY;
|
|
151037
151544
|
telemetry2.setSurface(isHeadless ? "headless" : "tui");
|
|
151038
151545
|
telemetry2.init();
|
|
151546
|
+
if (!isHeadless) {
|
|
151547
|
+
const { startDockerVersionCheck: startDockerVersionCheck2 } = await init_startup_docker_check().then(() => exports_startup_docker_check);
|
|
151548
|
+
startDockerVersionCheck2().catch(() => {});
|
|
151549
|
+
const { cleanupOldOverflowFiles: cleanupOldOverflowFiles2 } = await Promise.resolve().then(() => (init_overflow2(), exports_overflow));
|
|
151550
|
+
Promise.resolve().then(() => {
|
|
151551
|
+
try {
|
|
151552
|
+
cleanupOldOverflowFiles2(process.cwd());
|
|
151553
|
+
} catch {}
|
|
151554
|
+
});
|
|
151555
|
+
}
|
|
151039
151556
|
if (command && !isHeadless) {
|
|
151040
151557
|
console.error(`Error: Unknown command or argument "${command}"`);
|
|
151041
151558
|
console.error("Run 'letta --help' for usage information.");
|
|
@@ -151163,9 +151680,9 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
|
|
|
151163
151680
|
process.exit(1);
|
|
151164
151681
|
}
|
|
151165
151682
|
} else {
|
|
151166
|
-
const { resolve:
|
|
151683
|
+
const { resolve: resolve33 } = await import("path");
|
|
151167
151684
|
const { existsSync: existsSync40 } = await import("fs");
|
|
151168
|
-
const resolvedPath =
|
|
151685
|
+
const resolvedPath = resolve33(fromAfFile);
|
|
151169
151686
|
if (!existsSync40(resolvedPath)) {
|
|
151170
151687
|
console.error(`Error: AgentFile not found: ${resolvedPath}`);
|
|
151171
151688
|
process.exit(1);
|
|
@@ -151640,6 +152157,7 @@ Error: ${message}`);
|
|
|
151640
152157
|
setLoadingState("initializing");
|
|
151641
152158
|
const { createAgent: createAgent3 } = await init_create3().then(() => exports_create2);
|
|
151642
152159
|
let agent = null;
|
|
152160
|
+
let autoEnableMemfsForFreshAgent = false;
|
|
151643
152161
|
if (fromAfFile2) {
|
|
151644
152162
|
setLoadingState("importing");
|
|
151645
152163
|
let result;
|
|
@@ -151724,10 +152242,7 @@ Error: ${message}`);
|
|
|
151724
152242
|
});
|
|
151725
152243
|
agent = result.agent;
|
|
151726
152244
|
setAgentProvenance(result.provenance);
|
|
151727
|
-
|
|
151728
|
-
const { enableMemfsIfCloud: enableMemfsIfCloud3 } = await Promise.resolve().then(() => (init_memoryFilesystem2(), exports_memoryFilesystem2));
|
|
151729
|
-
await enableMemfsIfCloud3(agent.id);
|
|
151730
|
-
}
|
|
152245
|
+
autoEnableMemfsForFreshAgent = willAutoEnableMemfs;
|
|
151731
152246
|
}
|
|
151732
152247
|
if (!agent && resumingAgentId) {
|
|
151733
152248
|
try {
|
|
@@ -151750,11 +152265,12 @@ Error: ${message}`);
|
|
|
151750
152265
|
settingsManager2.updateLocalProjectSettings({ lastAgent: agent.id });
|
|
151751
152266
|
settingsManager2.updateSettings({ lastAgent: agent.id });
|
|
151752
152267
|
setAgentContext(agent.id, skillsDirectory2, resolvedSkillSources);
|
|
151753
|
-
const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent";
|
|
151754
152268
|
const agentId2 = agent.id;
|
|
151755
152269
|
const agentTags = agent.tags ?? undefined;
|
|
151756
|
-
const
|
|
151757
|
-
|
|
152270
|
+
const startupMemfsFlag = autoEnableMemfsForFreshAgent ? true : memfsFlag;
|
|
152271
|
+
const memfsSyncPromise = Promise.resolve().then(() => (init_memoryFilesystem2(), exports_memoryFilesystem2)).then(({ applyMemfsFlags: applyMemfsFlags3 }) => applyMemfsFlags3(agentId2, startupMemfsFlag, noMemfsFlag, {
|
|
152272
|
+
agentTags,
|
|
152273
|
+
skipPromptUpdate: shouldCreateNew
|
|
151758
152274
|
}));
|
|
151759
152275
|
const secretsInitPromise = init_secretsStore2().then(() => exports_secretsStore2).then(({ initSecretsFromServer: initSecretsFromServer3 }) => initSecretsFromServer3(agentId2));
|
|
151760
152276
|
const isResumingProject = !shouldCreateNew && !!resumingAgentId;
|
|
@@ -151895,7 +152411,7 @@ Error: ${message}`);
|
|
|
151895
152411
|
}
|
|
151896
152412
|
}
|
|
151897
152413
|
}
|
|
151898
|
-
if (
|
|
152414
|
+
if (shouldPersistSessionState2()) {
|
|
151899
152415
|
settingsManager2.persistSession(agent.id, conversationIdToUse);
|
|
151900
152416
|
}
|
|
151901
152417
|
setAgentId(agent.id);
|
|
@@ -152041,4 +152557,4 @@ Error during initialization: ${message}`);
|
|
|
152041
152557
|
}
|
|
152042
152558
|
main();
|
|
152043
152559
|
|
|
152044
|
-
//# debugId=
|
|
152560
|
+
//# debugId=275910C1663139CA64756E2164756E21
|