@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.
- package/dist/agent-presets.js +17 -17
- package/dist/agent-presets.js.map +1 -1
- package/dist/mcp-client.js +2 -2
- package/dist/mcp-client.js.map +1 -1
- package/dist/types/agent/turn-recovery-policy.d.ts +33 -0
- package/dist/types/agent/turn-recovery-policy.d.ts.map +1 -1
- package/dist/types/tools/impl/apply-patch.d.ts.map +1 -1
- package/dist/types/tools/secret-substitution.d.ts.map +1 -1
- package/dist/types/types/loop-status-protocol.d.ts +17 -0
- package/dist/types/types/loop-status-protocol.d.ts.map +1 -0
- package/dist/types/types/protocol_v2.d.ts +2 -19
- package/dist/types/types/protocol_v2.d.ts.map +1 -1
- package/dist/types/websocket/listener/inbound-queue.d.ts +5 -0
- package/dist/types/websocket/listener/inbound-queue.d.ts.map +1 -0
- package/dist/types/websocket/listener/protocol-outbound-routing.d.ts +9 -0
- package/dist/types/websocket/listener/protocol-outbound-routing.d.ts.map +1 -0
- package/dist/types/websocket/listener/protocol-outbound.d.ts.map +1 -1
- package/dist/types/websocket/listener/runtime.d.ts.map +1 -1
- package/dist/types/websocket/listener/turn-correlation.d.ts +10 -0
- package/dist/types/websocket/listener/turn-correlation.d.ts.map +1 -0
- package/dist/types/websocket/listener/types.d.ts +4 -0
- package/dist/types/websocket/listener/types.d.ts.map +1 -1
- package/letta.js +545 -120
- package/package.json +1 -1
- package/scripts/claude-watch/agent-watch.ts +622 -0
- package/scripts/claude-watch/docs-snapshot.test.ts +259 -0
- package/scripts/claude-watch/docs-snapshot.ts +672 -0
- package/scripts/claude-watch/fixtures/historical-replays.json +52 -0
- package/scripts/claude-watch/github.ts +137 -0
- package/scripts/claude-watch/release-analysis.test.ts +235 -0
- package/scripts/claude-watch/release-analysis.ts +297 -0
- package/scripts/claude-watch/release-source.test.ts +179 -0
- package/scripts/claude-watch/release-source.ts +369 -0
- package/scripts/claude-watch/runtime-observations.ts +98 -0
- package/scripts/claude-watch/runtime-probe.test.ts +576 -0
- package/scripts/claude-watch/runtime-probe.ts +911 -0
- package/scripts/claude-watch/runtime-sandbox.ts +170 -0
- package/scripts/claude-watch/state-branch.test.ts +211 -0
- package/scripts/claude-watch/state-branch.ts +316 -0
- package/scripts/claude-watch/tracker.test.ts +148 -0
- package/scripts/claude-watch/tracker.ts +325 -0
- package/scripts/claude-watch/types.ts +186 -0
- package/scripts/claude-watch/update-tracker.ts +201 -0
- package/scripts/codex-watch/agent-watch.ts +2 -2
- package/scripts/codex-watch/release-analysis.ts +14 -2
- package/scripts/codex-watch/tracker.ts +1 -3
- package/scripts/run-unit-tests.cjs +2 -0
- package/scripts/source-file-size-baseline.json +6 -5
- package/skills/creating-mods/references/commands.md +1 -1
- package/skills/creating-mods/references/ui.md +1 -1
- package/skills/customizing-commands/SKILL.md +1 -1
- package/skills/initializing-memory/SKILL.md +7 -7
- package/skills/self-configuration/SKILL.md +4 -4
- package/scripts/codex-watch/check-release.ts +0 -128
- 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
|
-
*
|
|
6
|
-
*
|
|
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
|
|
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
|
|
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":
|
|
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":
|
|
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":
|
|
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":
|
|
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":
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
-
|
|
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 #
|
|
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
|
|
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
|
|
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 |
|
|
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` |
|
|
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/` |
|
|
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
|
|
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
|
-
});
|