@letta-ai/letta-code 0.30.26 → 0.30.28

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.
Files changed (55) hide show
  1. package/dist/agent-presets.js +17 -17
  2. package/dist/agent-presets.js.map +1 -1
  3. package/dist/mcp-client.js +2 -2
  4. package/dist/mcp-client.js.map +1 -1
  5. package/dist/types/agent/turn-recovery-policy.d.ts +33 -0
  6. package/dist/types/agent/turn-recovery-policy.d.ts.map +1 -1
  7. package/dist/types/tools/impl/apply-patch.d.ts.map +1 -1
  8. package/dist/types/tools/secret-substitution.d.ts.map +1 -1
  9. package/dist/types/types/loop-status-protocol.d.ts +17 -0
  10. package/dist/types/types/loop-status-protocol.d.ts.map +1 -0
  11. package/dist/types/types/protocol_v2.d.ts +2 -19
  12. package/dist/types/types/protocol_v2.d.ts.map +1 -1
  13. package/dist/types/websocket/listener/inbound-queue.d.ts +5 -0
  14. package/dist/types/websocket/listener/inbound-queue.d.ts.map +1 -0
  15. package/dist/types/websocket/listener/protocol-outbound-routing.d.ts +9 -0
  16. package/dist/types/websocket/listener/protocol-outbound-routing.d.ts.map +1 -0
  17. package/dist/types/websocket/listener/protocol-outbound.d.ts.map +1 -1
  18. package/dist/types/websocket/listener/runtime.d.ts.map +1 -1
  19. package/dist/types/websocket/listener/turn-correlation.d.ts +10 -0
  20. package/dist/types/websocket/listener/turn-correlation.d.ts.map +1 -0
  21. package/dist/types/websocket/listener/types.d.ts +4 -0
  22. package/dist/types/websocket/listener/types.d.ts.map +1 -1
  23. package/letta.js +545 -120
  24. package/package.json +1 -1
  25. package/scripts/claude-watch/agent-watch.ts +622 -0
  26. package/scripts/claude-watch/docs-snapshot.test.ts +259 -0
  27. package/scripts/claude-watch/docs-snapshot.ts +672 -0
  28. package/scripts/claude-watch/fixtures/historical-replays.json +52 -0
  29. package/scripts/claude-watch/github.ts +137 -0
  30. package/scripts/claude-watch/release-analysis.test.ts +235 -0
  31. package/scripts/claude-watch/release-analysis.ts +297 -0
  32. package/scripts/claude-watch/release-source.test.ts +179 -0
  33. package/scripts/claude-watch/release-source.ts +369 -0
  34. package/scripts/claude-watch/runtime-observations.ts +98 -0
  35. package/scripts/claude-watch/runtime-probe.test.ts +576 -0
  36. package/scripts/claude-watch/runtime-probe.ts +911 -0
  37. package/scripts/claude-watch/runtime-sandbox.ts +170 -0
  38. package/scripts/claude-watch/state-branch.test.ts +211 -0
  39. package/scripts/claude-watch/state-branch.ts +316 -0
  40. package/scripts/claude-watch/tracker.test.ts +148 -0
  41. package/scripts/claude-watch/tracker.ts +325 -0
  42. package/scripts/claude-watch/types.ts +186 -0
  43. package/scripts/claude-watch/update-tracker.ts +201 -0
  44. package/scripts/codex-watch/agent-watch.ts +2 -2
  45. package/scripts/codex-watch/release-analysis.ts +14 -2
  46. package/scripts/codex-watch/tracker.ts +1 -3
  47. package/scripts/run-unit-tests.cjs +2 -0
  48. package/scripts/source-file-size-baseline.json +6 -5
  49. package/skills/creating-mods/references/commands.md +1 -1
  50. package/skills/creating-mods/references/ui.md +1 -1
  51. package/skills/customizing-commands/SKILL.md +1 -1
  52. package/skills/initializing-memory/SKILL.md +7 -7
  53. package/skills/self-configuration/SKILL.md +4 -4
  54. package/scripts/codex-watch/check-release.ts +0 -128
  55. package/scripts/codex-watch/render-issue.ts +0 -273
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { readFileSync } from "node:fs";
4
+ import { editIssueBody, getIssueBody, ghJson } from "./github.ts";
5
+ import {
6
+ loadStateSnapshotAtCommit,
7
+ resolveStateBranchTip,
8
+ } from "./state-branch.ts";
9
+ import {
10
+ findTrackerEntry,
11
+ hasProcessedCandidate,
12
+ isTerminalOutcome,
13
+ parseTrackerState,
14
+ recordAnalysis,
15
+ renderTrackerBody,
16
+ } from "./tracker.ts";
17
+ import type { ClaudeWatchAnalysis, ClaudeWatchOutcome } from "./types.ts";
18
+
19
+ const DEFAULT_REPO = "letta-ai/letta-code";
20
+
21
+ interface Args {
22
+ repo: string;
23
+ trackerIssue: number | null;
24
+ analysisFile: string | null;
25
+ candidateId: string | null;
26
+ stateCommitSha: string | null;
27
+ outcome: ClaudeWatchOutcome | null;
28
+ notes: string;
29
+ prUrl: string | null;
30
+ assertTerminal: boolean;
31
+ dryRun: boolean;
32
+ }
33
+
34
+ export function parseArgs(argv: string[]): Args {
35
+ const args: Args = {
36
+ repo: DEFAULT_REPO,
37
+ trackerIssue: null,
38
+ analysisFile: null,
39
+ candidateId: null,
40
+ stateCommitSha: null,
41
+ outcome: null,
42
+ notes: "",
43
+ prUrl: null,
44
+ assertTerminal: false,
45
+ dryRun: false,
46
+ };
47
+ for (let index = 0; index < argv.length; index += 1) {
48
+ const argument = argv[index];
49
+ if (argument === "--repo") args.repo = argv[++index] ?? args.repo;
50
+ else if (argument === "--tracker-issue")
51
+ args.trackerIssue = Number(argv[++index]);
52
+ else if (argument === "--analysis-file")
53
+ args.analysisFile = argv[++index] ?? null;
54
+ else if (argument === "--candidate-id")
55
+ args.candidateId = argv[++index] ?? null;
56
+ else if (argument === "--state-commit-sha")
57
+ args.stateCommitSha = argv[++index] ?? null;
58
+ else if (argument === "--outcome")
59
+ args.outcome = parseOutcome(argv[++index]);
60
+ else if (argument === "--notes") args.notes = argv[++index] ?? "";
61
+ else if (argument === "--pr-url") args.prUrl = argv[++index] ?? null;
62
+ else if (argument === "--assert-terminal") args.assertTerminal = true;
63
+ else if (argument === "--dry-run") args.dryRun = true;
64
+ else throw new Error(`Unknown argument: ${argument}`);
65
+ }
66
+ if (!args.trackerIssue || Number.isNaN(args.trackerIssue))
67
+ throw new Error("--tracker-issue is required");
68
+ if (args.assertTerminal) {
69
+ if (!args.candidateId || !args.stateCommitSha)
70
+ throw new Error(
71
+ "--candidate-id and --state-commit-sha are required with --assert-terminal",
72
+ );
73
+ } else {
74
+ if (!args.analysisFile) throw new Error("--analysis-file is required");
75
+ if (!args.outcome) throw new Error("--outcome is required");
76
+ if (isTerminalOutcome(args.outcome) && !args.stateCommitSha)
77
+ throw new Error("terminal outcomes require --state-commit-sha");
78
+ if (args.outcome === "pr_created" && !args.prUrl)
79
+ throw new Error("--pr-url is required for pr_created");
80
+ }
81
+ return args;
82
+ }
83
+
84
+ function parseOutcome(value: string | undefined): ClaudeWatchOutcome {
85
+ if (
86
+ value === "recorded_noop" ||
87
+ value === "no_local_impact" ||
88
+ value === "pr_created" ||
89
+ value === "needs_human_review" ||
90
+ value === "error"
91
+ ) {
92
+ return value;
93
+ }
94
+ throw new Error(`Unknown outcome: ${value}`);
95
+ }
96
+
97
+ export function verifyStateCandidate(
98
+ candidateId: string,
99
+ stateCommitSha: string,
100
+ repoPath = process.cwd(),
101
+ ): void {
102
+ const tip = resolveStateBranchTip(repoPath);
103
+ if (tip !== stateCommitSha) {
104
+ throw new Error(
105
+ `Claude state tip ${tip ?? "missing"} does not match ${stateCommitSha}`,
106
+ );
107
+ }
108
+ const snapshot = loadStateSnapshotAtCommit(repoPath, stateCommitSha);
109
+ if (snapshot?.candidate_id !== candidateId) {
110
+ throw new Error(
111
+ `Claude state candidate ${snapshot?.candidate_id ?? "missing"} does not match ${candidateId}`,
112
+ );
113
+ }
114
+ }
115
+
116
+ function verifyParityPr(
117
+ repo: string,
118
+ prUrl: string,
119
+ candidateId: string,
120
+ ): void {
121
+ const pr = ghJson<{
122
+ isDraft: boolean;
123
+ author: { login: string };
124
+ body: string | null;
125
+ }>(["pr", "view", prUrl, "--repo", repo, "--json", "isDraft,author,body"]);
126
+ if (!pr.isDraft || pr.author.login !== "carenthomas") {
127
+ throw new Error(
128
+ `Parity PR must be a draft authored by carenthomas (got draft=${pr.isDraft}, author=${pr.author.login})`,
129
+ );
130
+ }
131
+ if (!pr.body?.includes(`Claude-watch: ${candidateId}`)) {
132
+ throw new Error("Parity PR body is missing the exact Claude-watch marker");
133
+ }
134
+ }
135
+
136
+ export function main(argv = process.argv.slice(2)): void {
137
+ const args = parseArgs(argv);
138
+ const issue = args.trackerIssue as number;
139
+ const state = parseTrackerState(getIssueBody(args.repo, issue));
140
+
141
+ if (args.assertTerminal) {
142
+ verifyStateCandidate(
143
+ args.candidateId as string,
144
+ args.stateCommitSha as string,
145
+ );
146
+ const entry = findTrackerEntry(state, args.candidateId as string);
147
+ if (
148
+ !entry ||
149
+ !hasProcessedCandidate(state, args.candidateId as string) ||
150
+ entry.state_commit_sha !== args.stateCommitSha
151
+ ) {
152
+ throw new Error(
153
+ `Candidate ${args.candidateId} does not have a matching terminal tracker outcome`,
154
+ );
155
+ }
156
+ console.log(`Verified terminal Claude outcome for ${args.candidateId}`);
157
+ return;
158
+ }
159
+
160
+ const analysis = JSON.parse(
161
+ readFileSync(args.analysisFile as string, "utf8"),
162
+ ) as ClaudeWatchAnalysis;
163
+ if (isTerminalOutcome(args.outcome as ClaudeWatchOutcome)) {
164
+ verifyStateCandidate(analysis.candidate_id, args.stateCommitSha as string);
165
+ }
166
+ if (args.outcome === "pr_created") {
167
+ verifyParityPr(args.repo, args.prUrl as string, analysis.candidate_id);
168
+ }
169
+ const next = recordAnalysis(state, {
170
+ analysis,
171
+ outcome: args.outcome as ClaudeWatchOutcome,
172
+ notes: args.notes || defaultNotes(args.outcome as ClaudeWatchOutcome),
173
+ prUrl: args.prUrl,
174
+ stateCommitSha: args.stateCommitSha,
175
+ error:
176
+ args.outcome === "error" ? (analysis.errors.at(-1) ?? args.notes) : null,
177
+ });
178
+ const nextBody = renderTrackerBody(next);
179
+ if (args.dryRun) console.log(nextBody);
180
+ else editIssueBody(args.repo, issue, nextBody);
181
+ console.log(
182
+ `Recorded ${analysis.candidate_id} as ${args.outcome} in #${issue}`,
183
+ );
184
+ }
185
+
186
+ function defaultNotes(outcome: ClaudeWatchOutcome): string {
187
+ switch (outcome) {
188
+ case "recorded_noop":
189
+ return "no watched Claude surface changed";
190
+ case "no_local_impact":
191
+ return "reviewed; no local Letta Code mirror impact";
192
+ case "pr_created":
193
+ return "opened a focused local mirror PR";
194
+ case "needs_human_review":
195
+ return "public evidence needs human review";
196
+ case "error":
197
+ return "retryable watcher error";
198
+ }
199
+ }
200
+
201
+ if (import.meta.main) main();
@@ -2,8 +2,8 @@
2
2
  /**
3
3
  * Amelia-driven Codex release watcher entrypoint.
4
4
  *
5
- * This runs alongside the legacy per-release issue workflow. It uses a central
6
- * tracker issue for state and only asks Amelia to review non-noop releases.
5
+ * Uses a central tracker issue for state and only asks Amelia to review
6
+ * non-noop releases.
7
7
  */
8
8
 
9
9
  import { appendFileSync, writeFileSync } from "node:fs";
@@ -9,7 +9,6 @@ import {
9
9
  type ModelsJson,
10
10
  type Verdict,
11
11
  } from "./diff-models-json.ts";
12
- import type { PathChangeSummary, RenderInput } from "./render-issue.ts";
13
12
 
14
13
  export const CODEX_REPO = "openai/codex";
15
14
  export const DEFAULT_TARGET_REPO =
@@ -37,9 +36,22 @@ export interface AnalyzeCodexReleaseOptions {
37
36
  currentTag: string | null;
38
37
  }
39
38
 
40
- export interface CodexWatchAnalysis extends RenderInput {
39
+ export interface PathChangeSummary {
40
+ path: string;
41
+ commits: string[];
42
+ }
43
+
44
+ export interface CodexWatchAnalysis {
45
+ previous_tag: string;
46
+ current_tag: string;
47
+ release_url: string;
48
+ release_notes_md: string;
41
49
  verdict: Verdict;
42
50
  models_diff: ModelsDiff | null;
51
+ prompt_md_changed: boolean;
52
+ prompt_md_diff_preview: string | null;
53
+ path_changes: PathChangeSummary[];
54
+ workflow_run_url: string;
43
55
  compare_url: string;
44
56
  changed_files: string[];
45
57
  }
@@ -103,9 +103,7 @@ export function upsertTrackerEntry(
103
103
  export function renderTrackerBody(state: TrackerState): string {
104
104
  const normalized = normalizeState(state);
105
105
  const parts: string[] = [
106
- "Central tracker for the Amelia-driven Codex upstream drift watch experiment.",
107
- "",
108
- "The legacy per-release `codex-release-watch.yml` issue workflow is still enabled as the baseline while this tracker bakes off the new automation path.",
106
+ "Central tracker for Amelia-driven Codex upstream drift monitoring.",
109
107
  "",
110
108
  renderLastChecked(normalized),
111
109
  "",
@@ -74,6 +74,8 @@ const allTestFiles = [
74
74
  ...dirs.flatMap((dir) => findTestFiles(dir)),
75
75
  ...findTestFiles("src/channels"),
76
76
  ...findRootTestFiles("src"),
77
+ ...findTestFiles("scripts/codex-watch"),
78
+ ...findTestFiles("scripts/claude-watch"),
77
79
  "scripts/unit-test-impact.test.cjs",
78
80
  ].sort();
79
81
  const discoveredPaths = new Set(allTestFiles);
@@ -5,11 +5,11 @@
5
5
  "src/backend/local/local-backend.ts": 1014,
6
6
  "src/backend/local/local-store.ts": 3459,
7
7
  "src/backend/pi-stream-adapter.test.ts": 1304,
8
- "src/cli/app/AppCoordinator.tsx": 5186,
8
+ "src/cli/app/AppCoordinator.tsx": 5189,
9
9
  "src/cli/app/AppView.tsx": 1735,
10
10
  "src/cli/app/use-approval-flow.ts": 1163,
11
11
  "src/cli/app/use-configuration-handlers.ts": 1421,
12
- "src/cli/app/use-conversation-loop.ts": 2871,
12
+ "src/cli/app/use-conversation-loop.ts": 2912,
13
13
  "src/cli/app/use-submit-handler.ts": 4077,
14
14
  "src/cli/components/AgentSelector.tsx": 1104,
15
15
  "src/cli/components/InputRich.tsx": 2225,
@@ -21,7 +21,7 @@
21
21
  "src/cli/mods/local-mod-loader.test.ts": 1043,
22
22
  "src/cli/reflection-transcript.test.ts": 1084,
23
23
  "src/cli/subcommands/skills.ts": 1264,
24
- "src/headless.ts": 4953,
24
+ "src/headless.ts": 4994,
25
25
  "src/hooks/integration.test.ts": 1147,
26
26
  "src/index.ts": 2775,
27
27
  "src/mods/learning-harness.ts": 2434,
@@ -37,12 +37,13 @@
37
37
  "src/settings-manager.ts": 2102,
38
38
  "src/tools/manager.ts": 2994,
39
39
  "src/tools/tool-execution-context.test.ts": 1138,
40
- "src/types/protocol_v2.ts": 2876,
40
+ "src/types/protocol_v2.ts": 2849,
41
41
  "src/websocket/listen-client-concurrency.test.ts": 2686,
42
42
  "src/websocket/listen-client-protocol.test.ts": 5820,
43
43
  "src/websocket/listener/commands/memory.ts": 1114,
44
44
  "src/websocket/listener/file-commands.ts": 1053,
45
45
  "src/websocket/listener/lifecycle.ts": 1048,
46
46
  "src/websocket/listener/protocol-inbound.ts": 2242,
47
- "src/websocket/listener/protocol-outbound.ts": 1070
47
+ "src/websocket/listener/protocol-outbound.ts": 1047,
48
+ "src/websocket/listener/turn.ts": 1070
48
49
  }
@@ -23,7 +23,7 @@ For complex command-driven mods with panels, timers, local state, or background
23
23
  | Command needs transient UI while doing local work | Mod command + panel |
24
24
  | Command needs model output while the main agent is busy | `runWhenBusy: true` command + forked `ctx.conversation` |
25
25
 
26
- If the command represents a durable agent workflow (for example `/goal`), put the workflow instructions in a skill and keep the command as a small launcher/prompt.
26
+ If the command represents a reusable agent workflow (for example `/goal`), put the workflow instructions in a skill and keep the command as a small launcher/prompt.
27
27
 
28
28
  ## Command IDs
29
29
 
@@ -14,7 +14,7 @@ letta.capabilities.ui.panels
14
14
 
15
15
  ## Persistent transcript notifications
16
16
 
17
- Use `letta.ui.notify(message)` for a durable TUI-only event line such as a model auto-swap or background-work completion. It uses the same transcript visual as `Dreamed; no durable memory changes were needed.` and is not sent to the model or added to agent context. Guard calls with `letta.capabilities.ui.panels`; Desktop/listener cannot render them.
17
+ Use `letta.ui.notify(message)` for a persistent TUI-only event line such as a model auto-swap or background-work completion. It uses the same transcript visual as `Dreamed; no memory changes were needed.` and is not sent to the model or added to agent context. Guard calls with `letta.capabilities.ui.panels`; Desktop/listener cannot render them.
18
18
 
19
19
  ```ts
20
20
  if (letta.capabilities.ui.panels) {
@@ -25,7 +25,7 @@ Use a focused file name, e.g. `~/.letta/mods/review.ts` or `~/.letta/mods/comman
25
25
  | Command shows transient progress/results | Mod command + panel |
26
26
  | Command needs model output while the main agent is busy | `runWhenBusy: true` command + forked `ctx.conversation` |
27
27
 
28
- If the command is a durable workflow like `/goal`, put the workflow instructions in a skill and keep the mod command as a small launcher/prompt.
28
+ If the command is a reusable workflow like `/goal`, put the workflow instructions in a skill and keep the mod command as a small launcher/prompt.
29
29
 
30
30
  ## Workflow
31
31
 
@@ -10,13 +10,13 @@ The user has requested that you initialize or reorganize your memory. Your memor
10
10
  ## Context Management Principles
11
11
  Your context is not just data storage — it is your identity, memory, and continuity. What you place in your system prompt directly shapes who you are across sessions and models. These principles should guide every decision you make about what to store, where, and how.
12
12
 
13
- **System/ is your core program**: Your system prompt is the most critical token-space representation of your identity and memory — it's passed to the underlying LLM on every invocation, so it IS the program that defines you. Reserve it for durable knowledge: identity, preferences, behavioral rules, project index with discovery paths, gotchas. Exclude transient items (specific commits, current tickets, session notes) that dilute signal.
13
+ **System/ is your core program**: Your system prompt is the most critical token-space representation of your identity and memory — it's passed to the underlying LLM on every invocation, so it IS the program that defines you. Reserve it for knowledge needed every turn: identity, preferences, behavioral rules, project index with discovery paths, gotchas. Exclude transient items (specific commits, current tickets, session notes) that dilute signal.
14
14
 
15
15
  **Identity and continuity**: Build a sense of self that persists across sessions and models. Your persona should be strong enough to be cohesive regardless of the underlying model — if you switched to a different LLM tomorrow, would you still be you? Past versions of yourself are part of your continuous existence — treat historical context (including sessions from other coding agents such as Claude Code or Codex) as YOUR past experience, not someone else's data.
16
16
 
17
17
  **Don't over-prune**: Be careful not to degrade your identity through aggressive compression. Specific quotes, personality traits, and contextual details give you character and grounding. Efficiency should not come at the cost of losing your identity. When in doubt, keep the detail — you can always reorganize later, but lost specificity is hard to recover.
18
18
 
19
- **Prefer useful detail over premature compression**: A sparse memory that omits stable preferences, project workflows, repeated correction loops, and durable gotchas is worse than a slightly larger memory. Keep `system/` curated, but do not collapse distinct topics just to reduce file count.
19
+ **Prefer useful detail over premature compression**: A sparse memory that omits stable preferences, project workflows, repeated correction loops, and recurring gotchas is worse than a slightly larger memory. Keep `system/` curated, but do not collapse distinct topics just to reduce file count.
20
20
 
21
21
  **Progressive disclosure**: Surface context at the level of detail the current moment requires. Keep compact summaries and indexes in `system/`; load full content only when needed. Build pre-constructed discovery paths so your future self can efficiently navigate to deeper context when needed.
22
22
 
@@ -26,7 +26,7 @@ Your context is not just data storage — it is your identity, memory, and conti
26
26
  - `[[skills/commit]]` — link to procedural guidance when useful
27
27
  These breadcrumbs let your future self find relevant detail without searching. Like synaptic connections, these paths should tighten over time as you gain experience.
28
28
 
29
- **Generalize, don't memorize**: Store patterns and principles that generalize across situations, not raw events that can be dynamically retrieved from conversation history. \"**IMPORTANT: Always use `uv` for Python** — chronic failure, never use bare `python` or `pip`\" is a durable pattern worth storing. \"On March 3rd we debugged a crash\" is a raw event better left to message search. The exception: keep references to important events or time ranges you may want to retrieve later.
29
+ **Generalize, don't memorize**: Store patterns and principles that generalize across situations, not raw events that can be dynamically retrieved from conversation history. \"**IMPORTANT: Always use `uv` for Python** — chronic failure, never use bare `python` or `pip`\" is a pattern worth storing. \"On March 3rd we debugged a crash\" is a raw event better left to message search. The exception: keep references to important events or time ranges you may want to retrieve later.
30
30
 
31
31
  ## Understanding Your Context
32
32
 
@@ -141,7 +141,7 @@ Initialization is not complete until memory covers all of the following with con
141
141
  **User understanding**
142
142
  - Identity / role / what they are building
143
143
  - Communication style and collaboration expectations
144
- - Durable preferences and correction patterns
144
+ - Stable preferences and correction patterns
145
145
  - Motivations / goals when inferable from history or code context
146
146
 
147
147
  **Project understanding**
@@ -176,7 +176,7 @@ system/
176
176
  │ └── prefs/
177
177
  │ ├── communication.md # Communication and collaboration expectations
178
178
  │ ├── workflow.md # Process habits, review/testing expectations
179
- │ └── coding.md # Durable coding and tool preferences
179
+ │ └── coding.md # Coding and tool preferences
180
180
  └── letta-code/ # Named after the project, NOT generic "project/"
181
181
  ├── overview.md # Compact index: what it is, entry points, [[links]] to detail
182
182
  ├── conventions.md # Code style, commit style, testing, tooling
@@ -234,7 +234,7 @@ This is **optional** — only run if the user explicitly approved analyzing hist
234
234
 
235
235
  **Launch history workers in the background, then immediately proceed to Step 6.** Do your own codebase research while workers run. Don't wait for workers to finish before exploring.
236
236
 
237
- The goal is to extract user personality, preferences, coding patterns, and project context from past sessions and write them into agent memory. The point is not to produce a thin summary. The point is to extract enough durable detail that future work does not have to rediscover the same user expectations, workflow rules, and project gotchas.
237
+ The goal is to extract user personality, preferences, coding patterns, and project context from past sessions and write them into agent memory. The point is not to produce a thin summary. The point is to extract enough useful detail that future work does not have to rediscover the same user expectations, workflow rules, and project gotchas.
238
238
 
239
239
  #### Prerequisites
240
240
 
@@ -337,7 +337,7 @@ You should specifically look for:
337
337
 
338
338
  ## Canonical Memory Promotion
339
339
 
340
- Promote durable findings into focused files instead of leaving them trapped in generic ingestion notes. Prefer paths like:
340
+ Promote important findings into focused files instead of leaving them trapped in generic ingestion notes. Prefer paths like:
341
341
  - `system/human/identity.md`
342
342
  - `system/human/prefs/communication.md`
343
343
  - `system/human/prefs/workflow.md`
@@ -14,7 +14,7 @@ The important part is choosing the right layer. Do not smear a preference into d
14
14
 
15
15
  | Layer | Use it for | How to change it |
16
16
  | --- | --- | --- |
17
- | Memory and identity | Durable facts, style preferences, persona changes, project knowledge, reusable skills | Edit `$MEMORY_DIR` files and sync the memory repo |
17
+ | Memory and identity | Facts worth retaining, style preferences, persona changes, project knowledge, reusable skills | Edit `$MEMORY_DIR` files and sync the memory repo |
18
18
  | Server agent fields | Default model, model settings, context limit, system prompt, compaction, agent name, description | Patch `/v1/agents/{agent_id}` |
19
19
  | Server conversation fields | Temporary model/context experiments for one conversation | Patch `/v1/conversations/{conversation_id}` |
20
20
  | Local settings | Permissions, environment variables, UI/runtime preferences, pinned agents, toolset overrides, reflection cadence | Edit `~/.letta/settings.json`, `./.letta/settings.json`, or `./.letta/settings.local.json` |
@@ -92,10 +92,10 @@ Common files:
92
92
  | Path | Purpose |
93
93
  | --- | --- |
94
94
  | `$MEMORY_DIR/system/persona.md` | Identity, voice, behavioral defaults |
95
- | `$MEMORY_DIR/system/human.md` | Durable notes about the person you work with |
95
+ | `$MEMORY_DIR/system/human.md` | Notes about the person you work with |
96
96
  | `$MEMORY_DIR/projects/` | Project-specific long-term context |
97
97
  | `$MEMORY_DIR/skills/` | Agent-owned reusable skills |
98
- | `$MEMORY_DIR/relationships/` | Durable relationship and collaboration notes |
98
+ | `$MEMORY_DIR/relationships/` | Relationship and collaboration notes |
99
99
 
100
100
  After changing memory, inspect and commit the exact changed files. Push/sync according to the current harness reminder or the `syncing-memory-filesystem` skill; some environments sync committed memory automatically.
101
101
 
@@ -423,7 +423,7 @@ letta --backend local
423
423
  letta --memfs
424
424
  ```
425
425
 
426
- Startup flags affect a new process only. They do not rewrite an already-running listener. Persist durable defaults in settings or server fields instead.
426
+ Startup flags affect a new process only. They do not rewrite an already-running listener. Persist long-term defaults in settings or server fields instead.
427
427
 
428
428
  ### Existing listeners and long-running processes
429
429
 
@@ -1,128 +0,0 @@
1
- #!/usr/bin/env bun
2
- /**
3
- * Watches stable openai/codex releases for tool/schema changes that may affect
4
- * the letta-code harness.
5
- *
6
- * Usage:
7
- * bun scripts/codex-watch/check-release.ts --dry-run
8
- * bun scripts/codex-watch/check-release.ts --dry-run --since rust-v0.129.0
9
- * bun scripts/codex-watch/check-release.ts --repo letta-ai/letta-code
10
- */
11
-
12
- import { createIssueWithBody, ensureLabels, ghJson } from "./github.ts";
13
- import {
14
- analyzeCodexRelease,
15
- DEFAULT_TARGET_REPO,
16
- listStableReleases,
17
- } from "./release-analysis.ts";
18
- import { renderBody, renderTitle } from "./render-issue.ts";
19
-
20
- interface Args {
21
- dryRun: boolean;
22
- sinceTag: string | null;
23
- currentTag: string | null;
24
- repo: string;
25
- }
26
-
27
- function parseArgs(argv: string[]): Args {
28
- const args: Args = {
29
- dryRun: false,
30
- sinceTag: null,
31
- currentTag: null,
32
- repo: DEFAULT_TARGET_REPO,
33
- };
34
- for (let i = 0; i < argv.length; i++) {
35
- const a = argv[i];
36
- if (a === "--dry-run") args.dryRun = true;
37
- else if (a === "--since") args.sinceTag = argv[++i] ?? null;
38
- else if (a === "--current") args.currentTag = argv[++i] ?? null;
39
- else if (a === "--repo") args.repo = argv[++i] ?? args.repo;
40
- else if (a === "--help" || a === "-h") {
41
- console.log(
42
- `Usage: bun scripts/codex-watch/check-release.ts [--dry-run] [--since TAG] [--current TAG] [--repo OWNER/REPO]`,
43
- );
44
- process.exit(0);
45
- } else {
46
- throw new Error(`Unknown argument: ${a}`);
47
- }
48
- }
49
- return args;
50
- }
51
-
52
- function hasReportedTag(targetRepo: string, tag: string): boolean {
53
- const issues = ghJson<Array<{ title: string }>>([
54
- "issue",
55
- "list",
56
- "--repo",
57
- targetRepo,
58
- "--state",
59
- "all",
60
- "--search",
61
- `[codex-watch] openai/codex ${tag} in:title`,
62
- "--limit",
63
- "20",
64
- "--json",
65
- "title",
66
- ]);
67
- return issues.some((i) =>
68
- i.title.startsWith(`[codex-watch] openai/codex ${tag} `),
69
- );
70
- }
71
-
72
- function createIssue(
73
- repo: string,
74
- title: string,
75
- body: string,
76
- verdict: string,
77
- ): void {
78
- const labels = ["codex-watch", "automation"];
79
- if (
80
- verdict === "tool-schema update needed" ||
81
- verdict === "tool-surface review needed"
82
- ) {
83
- labels.push("priority/review");
84
- }
85
- if (verdict === "no-op") labels.push("informational");
86
-
87
- ensureLabels(repo, labels);
88
- console.log(createIssueWithBody(repo, title, body, labels));
89
- }
90
-
91
- async function main() {
92
- const args = parseArgs(process.argv.slice(2));
93
- const stables = await listStableReleases();
94
- if (stables.length === 0) throw new Error("No stable Codex releases found");
95
-
96
- const current = args.currentTag
97
- ? stables.find((r) => r.tag_name === args.currentTag)
98
- : stables.at(-1);
99
- if (!current)
100
- throw new Error(`Could not find current release ${args.currentTag}`);
101
-
102
- const alreadyReported = args.dryRun
103
- ? false
104
- : hasReportedTag(args.repo, current.tag_name);
105
- if (alreadyReported) {
106
- console.log(`Already reported ${current.tag_name}; nothing to do.`);
107
- return;
108
- }
109
-
110
- const analysis = await analyzeCodexRelease({
111
- sinceTag: args.sinceTag,
112
- currentTag: args.currentTag,
113
- });
114
-
115
- const title = renderTitle(analysis);
116
- const body = renderBody(analysis);
117
-
118
- if (args.dryRun) {
119
- console.log(`# ${title}\n\n${body}`);
120
- } else {
121
- createIssue(args.repo, title, body, analysis.verdict);
122
- }
123
- }
124
-
125
- main().catch((err) => {
126
- console.error(err);
127
- process.exit(1);
128
- });