@letta-ai/letta-code 0.30.31 → 0.31.0
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-agent-presets.js +11 -0
- package/dist/agent-presets-agent-presets.js.map +9 -0
- package/dist/agent-presets-personality-asset-content.js +42 -0
- package/dist/agent-presets-personality-asset-content.js.map +10 -0
- package/dist/agent-presets.js +17 -2
- package/dist/agent-presets.js.map +3 -3
- package/dist/channels-public.js +16 -1
- package/dist/channels-public.js.map +5 -4
- package/dist/gateway-core.js +15 -7
- package/dist/gateway-core.js.map +3 -3
- package/dist/mcp-client.js +2 -2
- package/dist/mcp-client.js.map +1 -1
- package/dist/types/agent/create-agent-request.d.ts +3 -0
- package/dist/types/agent/create-agent-request.d.ts.map +1 -1
- package/dist/types/agent/model.d.ts.map +1 -1
- package/dist/types/agent/personality-asset-content.d.ts +3 -0
- package/dist/types/agent/personality-asset-content.d.ts.map +1 -0
- package/dist/types/agent/skills.d.ts.map +1 -1
- package/dist/types/backend/api/ephemeral-conversations.d.ts +15 -0
- package/dist/types/backend/api/ephemeral-conversations.d.ts.map +1 -0
- package/dist/types/channels/gateway-core.d.ts.map +1 -1
- package/dist/types/channels/route-thread-key.d.ts +20 -0
- package/dist/types/channels/route-thread-key.d.ts.map +1 -0
- package/dist/types/channels-public.d.ts +1 -0
- package/dist/types/channels-public.d.ts.map +1 -1
- package/dist/types/providers/byok-providers.d.ts.map +1 -1
- package/dist/types/tools/impl/exec-command.d.ts.map +1 -1
- package/dist/types/tools/impl/shell-runner.d.ts +1 -0
- package/dist/types/tools/impl/shell-runner.d.ts.map +1 -1
- package/dist/types/tools/impl/skill.d.ts.map +1 -1
- package/dist/types/tools/toolset.d.ts +1 -1
- package/dist/types/tools/toolset.d.ts.map +1 -1
- package/dist/types/types/external-tool-protocol.d.ts +2 -2
- package/dist/types/types/external-tool-protocol.d.ts.map +1 -1
- package/dist/types/types/protocol_v2.d.ts +30 -190
- package/dist/types/types/protocol_v2.d.ts.map +1 -1
- package/dist/types/types/runtime-scope.d.ts +4 -2
- package/dist/types/types/runtime-scope.d.ts.map +1 -1
- package/dist/types/types/runtime-start-protocol.d.ts +21 -0
- package/dist/types/types/runtime-start-protocol.d.ts.map +1 -0
- package/dist/types/types/schedule-protocol.d.ts +151 -1
- package/dist/types/types/schedule-protocol.d.ts.map +1 -1
- package/dist/types/utils/frontmatter.d.ts.map +1 -1
- package/dist/types/websocket/listener/cwd-change.d.ts.map +1 -1
- package/dist/types/websocket/listener/protocol-outbound.d.ts +1 -1
- package/dist/types/websocket/listener/protocol-outbound.d.ts.map +1 -1
- package/dist/types/websocket/listener/scope.d.ts +1 -1
- package/dist/types/websocket/listener/scope.d.ts.map +1 -1
- package/letta.js +1230 -511
- package/package.json +5 -3
- package/scripts/builtin-skills-watch/agent-watch.test.ts +104 -0
- package/scripts/builtin-skills-watch/agent-watch.ts +337 -0
- package/scripts/builtin-skills-watch/aggregate-results.test.ts +79 -0
- package/scripts/builtin-skills-watch/aggregate-results.ts +326 -0
- package/scripts/builtin-skills-watch/analysis.test.ts +70 -0
- package/scripts/builtin-skills-watch/analysis.ts +228 -0
- package/scripts/builtin-skills-watch/evidence.test.ts +114 -0
- package/scripts/builtin-skills-watch/evidence.ts +145 -0
- package/scripts/builtin-skills-watch/github.ts +161 -0
- package/scripts/builtin-skills-watch/tracker.test.ts +249 -0
- package/scripts/builtin-skills-watch/tracker.ts +506 -0
- package/scripts/builtin-skills-watch/update-tracker.test.ts +234 -0
- package/scripts/builtin-skills-watch/update-tracker.ts +508 -0
- package/scripts/claude-watch/release-source.test.ts +16 -4
- package/scripts/claude-watch/release-source.ts +41 -2
- package/scripts/codex-watch/agent-watch.ts +8 -5
- package/scripts/codex-watch/release-analysis.test.ts +21 -27
- package/scripts/codex-watch/release-analysis.ts +30 -10
- package/scripts/codex-watch/tracker.test.ts +12 -33
- package/scripts/codex-watch/tracker.ts +30 -22
- package/scripts/pi-ai-watch/agent-watch.ts +9 -14
- package/scripts/pi-ai-watch/release-analysis.test.ts +24 -18
- package/scripts/pi-ai-watch/release-analysis.ts +85 -45
- package/scripts/pi-ai-watch/tracker.test.ts +25 -18
- package/scripts/pi-ai-watch/tracker.ts +7 -19
- package/scripts/pi-ai-watch/update-tracker.ts +2 -2
- package/scripts/source-file-size-baseline.json +3 -3
- package/skills/dispatching-coding-agents/SKILL.md +16 -7
- package/skills/self-configuration/SKILL.md +20 -1
- package/skills/syncing-memory-filesystem/SKILL.md +118 -226
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
findNextStableRelease,
|
|
2
|
+
findLatestStableReleaseAfter,
|
|
4
3
|
type Release,
|
|
4
|
+
releaseNotesForRange,
|
|
5
5
|
} from "./release-analysis.ts";
|
|
6
6
|
|
|
7
7
|
function release(tag: string, publishedAt: string): Release {
|
|
@@ -10,7 +10,7 @@ function release(tag: string, publishedAt: string): Release {
|
|
|
10
10
|
draft: false,
|
|
11
11
|
prerelease: false,
|
|
12
12
|
html_url: `https://github.com/openai/codex/releases/tag/${tag}`,
|
|
13
|
-
body:
|
|
13
|
+
body: `Notes for ${tag}`,
|
|
14
14
|
published_at: publishedAt,
|
|
15
15
|
};
|
|
16
16
|
}
|
|
@@ -21,37 +21,31 @@ const STABLES = [
|
|
|
21
21
|
release("rust-v0.3.0", "2026-08-03T00:00:00Z"),
|
|
22
22
|
];
|
|
23
23
|
|
|
24
|
-
describe("Codex stable release
|
|
25
|
-
test("selects the
|
|
26
|
-
expect(
|
|
27
|
-
|
|
28
|
-
)
|
|
24
|
+
describe("Codex stable release range", () => {
|
|
25
|
+
test("selects the latest release after the durable cursor", () => {
|
|
26
|
+
expect(findLatestStableReleaseAfter(STABLES, "rust-v0.1.0")?.tag_name).toBe(
|
|
27
|
+
"rust-v0.3.0",
|
|
28
|
+
);
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
test("
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
areAdjacentStableReleases(
|
|
41
|
-
STABLES,
|
|
42
|
-
"rust-v0.1.0",
|
|
43
|
-
"rust-v0.3.0",
|
|
44
|
-
),
|
|
45
|
-
).toBe(false);
|
|
31
|
+
test("includes release notes for the full cursor-to-latest range", () => {
|
|
32
|
+
const notes = releaseNotesForRange(STABLES, "rust-v0.1.0", "rust-v0.3.0");
|
|
33
|
+
expect(notes).toContain("## [rust-v0.2.0]");
|
|
34
|
+
expect(notes).toContain("Notes for rust-v0.2.0");
|
|
35
|
+
expect(notes).toContain("## [rust-v0.3.0]");
|
|
36
|
+
expect(notes).not.toContain("Notes for rust-v0.1.0");
|
|
37
|
+
expect(() =>
|
|
38
|
+
releaseNotesForRange(STABLES, "rust-v0.3.0", "rust-v0.2.0"),
|
|
39
|
+
).toThrow("Previous release rust-v0.3.0 must precede rust-v0.2.0");
|
|
46
40
|
});
|
|
47
41
|
|
|
48
42
|
test("returns null when the durable cursor is current", () => {
|
|
49
|
-
expect(
|
|
43
|
+
expect(findLatestStableReleaseAfter(STABLES, "rust-v0.3.0")).toBeNull();
|
|
50
44
|
});
|
|
51
45
|
|
|
52
46
|
test("fails instead of skipping an unavailable cursor", () => {
|
|
53
|
-
expect(() =>
|
|
54
|
-
|
|
55
|
-
)
|
|
47
|
+
expect(() => findLatestStableReleaseAfter(STABLES, "rust-v0.0.9")).toThrow(
|
|
48
|
+
"Could not find terminal release rust-v0.0.9",
|
|
49
|
+
);
|
|
56
50
|
});
|
|
57
51
|
});
|
|
@@ -45,7 +45,7 @@ export interface PathChangeSummary {
|
|
|
45
45
|
export interface CodexWatchAnalysis {
|
|
46
46
|
previous_tag: string;
|
|
47
47
|
current_tag: string;
|
|
48
|
-
|
|
48
|
+
is_latest_release: boolean;
|
|
49
49
|
release_url: string;
|
|
50
50
|
release_notes_md: string;
|
|
51
51
|
verdict: Verdict;
|
|
@@ -139,13 +139,13 @@ export async function analyzeCodexRelease(
|
|
|
139
139
|
return {
|
|
140
140
|
previous_tag: previous.tag_name,
|
|
141
141
|
current_tag: current.tag_name,
|
|
142
|
-
|
|
142
|
+
is_latest_release: current.tag_name === stables.at(-1)?.tag_name,
|
|
143
|
+
release_url: current.html_url,
|
|
144
|
+
release_notes_md: releaseNotesForRange(
|
|
143
145
|
stables,
|
|
144
146
|
previous.tag_name,
|
|
145
147
|
current.tag_name,
|
|
146
148
|
),
|
|
147
|
-
release_url: current.html_url,
|
|
148
|
-
release_notes_md: current.body ?? "",
|
|
149
149
|
verdict,
|
|
150
150
|
models_diff: modelsDiff,
|
|
151
151
|
prompt_md_changed: promptMdChanged,
|
|
@@ -207,17 +207,37 @@ function findPreviousStable(
|
|
|
207
207
|
return stables[idx - 1] ?? null;
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
export function
|
|
210
|
+
export function releaseNotesForRange(
|
|
211
211
|
stables: Release[],
|
|
212
212
|
previousTag: string,
|
|
213
213
|
currentTag: string,
|
|
214
|
-
):
|
|
215
|
-
|
|
216
|
-
|
|
214
|
+
): string {
|
|
215
|
+
const currentIndex = stables.findIndex(
|
|
216
|
+
(release) => release.tag_name === currentTag,
|
|
217
|
+
);
|
|
218
|
+
if (currentIndex < 0) {
|
|
219
|
+
throw new Error(`Could not find current release ${currentTag}`);
|
|
220
|
+
}
|
|
221
|
+
const previousIndex = stables.findIndex(
|
|
222
|
+
(release) => release.tag_name === previousTag,
|
|
217
223
|
);
|
|
224
|
+
if (previousIndex >= currentIndex) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
`Previous release ${previousTag} must precede ${currentTag}`,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
const firstIncludedIndex =
|
|
230
|
+
previousIndex < 0 ? currentIndex : previousIndex + 1;
|
|
231
|
+
return stables
|
|
232
|
+
.slice(firstIncludedIndex, currentIndex + 1)
|
|
233
|
+
.map(
|
|
234
|
+
(release) =>
|
|
235
|
+
`## [${release.tag_name}](${release.html_url})\n\n${release.body?.trim() || "_No release notes._"}`,
|
|
236
|
+
)
|
|
237
|
+
.join("\n\n");
|
|
218
238
|
}
|
|
219
239
|
|
|
220
|
-
export function
|
|
240
|
+
export function findLatestStableReleaseAfter(
|
|
221
241
|
stables: Release[],
|
|
222
242
|
terminalTag: string,
|
|
223
243
|
): Release | null {
|
|
@@ -227,7 +247,7 @@ export function findNextStableRelease(
|
|
|
227
247
|
if (terminalIndex < 0) {
|
|
228
248
|
throw new Error(`Could not find terminal release ${terminalTag}`);
|
|
229
249
|
}
|
|
230
|
-
return
|
|
250
|
+
return terminalIndex === stables.length - 1 ? null : (stables.at(-1) ?? null);
|
|
231
251
|
}
|
|
232
252
|
|
|
233
253
|
function cloneCodex(tmp: string): string {
|
|
@@ -21,7 +21,7 @@ function analysis(
|
|
|
21
21
|
return {
|
|
22
22
|
previous_tag: "rust-v0.1.0",
|
|
23
23
|
current_tag: tag,
|
|
24
|
-
|
|
24
|
+
is_latest_release: true,
|
|
25
25
|
release_url: `https://github.com/openai/codex/releases/tag/${tag}`,
|
|
26
26
|
release_notes_md: "",
|
|
27
27
|
verdict,
|
|
@@ -37,7 +37,7 @@ function analysis(
|
|
|
37
37
|
|
|
38
38
|
function withoutAuditCursor(body: string): string {
|
|
39
39
|
return body
|
|
40
|
-
.replace(/
|
|
40
|
+
.replace(/ {2}"audit_cursor_tag": (?:null|"[^"]+"),\n/, "")
|
|
41
41
|
.replace(' "audit_cursor_validated": true,\n', "");
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -166,7 +166,7 @@ describe("tracker state", () => {
|
|
|
166
166
|
test("fails closed on a cumulative legacy replay", () => {
|
|
167
167
|
const cumulative = {
|
|
168
168
|
...analysis("rust-v0.5.0", "tool-surface review needed"),
|
|
169
|
-
|
|
169
|
+
is_latest_release: false,
|
|
170
170
|
};
|
|
171
171
|
const state = recordAnalysis(emptyTrackerState(), {
|
|
172
172
|
analysis: cumulative,
|
|
@@ -198,11 +198,7 @@ describe("tracker state", () => {
|
|
|
198
198
|
|
|
199
199
|
const body = renderTrackerBody(state);
|
|
200
200
|
expect(
|
|
201
|
-
hasProcessedRange(
|
|
202
|
-
parseTrackerState(body),
|
|
203
|
-
"rust-v0.1.0",
|
|
204
|
-
"rust-v0.2.0",
|
|
205
|
-
),
|
|
201
|
+
hasProcessedRange(parseTrackerState(body), "rust-v0.1.0", "rust-v0.2.0"),
|
|
206
202
|
).toBe(true);
|
|
207
203
|
expect(body).toContain("_No actionable releases recorded yet._");
|
|
208
204
|
expect(body).toContain("no watched changes");
|
|
@@ -215,23 +211,16 @@ describe("tracker state", () => {
|
|
|
215
211
|
);
|
|
216
212
|
state = upsertTrackerEntry(state, entry(2, "error"));
|
|
217
213
|
|
|
218
|
-
expect(
|
|
219
|
-
hasProcessedRange(state, "rust-v0.1.0", "rust-v0.2.0"),
|
|
220
|
-
).toBe(false);
|
|
214
|
+
expect(hasProcessedRange(state, "rust-v0.1.0", "rust-v0.2.0")).toBe(false);
|
|
221
215
|
expect(getCodexAuditCursorTag(state)).toBe("rust-v0.1.0");
|
|
222
216
|
|
|
223
217
|
state = upsertTrackerEntry(state, entry(2, "no_local_impact"));
|
|
224
|
-
expect(
|
|
225
|
-
hasProcessedRange(state, "rust-v0.1.0", "rust-v0.2.0"),
|
|
226
|
-
).toBe(true);
|
|
218
|
+
expect(hasProcessedRange(state, "rust-v0.1.0", "rust-v0.2.0")).toBe(true);
|
|
227
219
|
expect(getCodexAuditCursorTag(state)).toBe("rust-v0.2.0");
|
|
228
220
|
});
|
|
229
221
|
|
|
230
222
|
test("uses an errored release baseline when no terminal entry exists", () => {
|
|
231
|
-
const state = upsertTrackerEntry(
|
|
232
|
-
emptyTrackerState(),
|
|
233
|
-
entry(2, "error"),
|
|
234
|
-
);
|
|
223
|
+
const state = upsertTrackerEntry(emptyTrackerState(), entry(2, "error"));
|
|
235
224
|
|
|
236
225
|
expect(getCodexAuditCursorTag(state)).toBe("rust-v0.1.0");
|
|
237
226
|
});
|
|
@@ -261,14 +250,14 @@ describe("tracker state", () => {
|
|
|
261
250
|
expect(getCodexAuditCursorTag(state)).toBe("rust-v0.2.0");
|
|
262
251
|
});
|
|
263
252
|
|
|
264
|
-
test("does not advance for an explicit non-
|
|
253
|
+
test("does not advance for an explicit non-latest range", () => {
|
|
265
254
|
const initial = upsertTrackerEntry(
|
|
266
255
|
emptyTrackerState(),
|
|
267
256
|
entry(1, "no_local_impact"),
|
|
268
257
|
);
|
|
269
258
|
const cumulative = {
|
|
270
259
|
...analysis("rust-v0.5.0", "tool-surface review needed"),
|
|
271
|
-
|
|
260
|
+
is_latest_release: false,
|
|
272
261
|
};
|
|
273
262
|
const state = recordAnalysis(initial, {
|
|
274
263
|
analysis: cumulative,
|
|
@@ -279,17 +268,12 @@ describe("tracker state", () => {
|
|
|
279
268
|
expect(getCodexAuditCursorTag(state)).toBe("rust-v0.1.0");
|
|
280
269
|
expect(
|
|
281
270
|
getCodexAuditCursorTag(
|
|
282
|
-
advanceCodexAuditCursor(
|
|
283
|
-
state,
|
|
284
|
-
"rust-v0.1.0",
|
|
285
|
-
"rust-v0.5.0",
|
|
286
|
-
false,
|
|
287
|
-
),
|
|
271
|
+
advanceCodexAuditCursor(state, "rust-v0.1.0", "rust-v0.5.0", false),
|
|
288
272
|
),
|
|
289
273
|
).toBe("rust-v0.1.0");
|
|
290
274
|
});
|
|
291
275
|
|
|
292
|
-
test("advances across an already audited
|
|
276
|
+
test("advances across an already audited latest range", () => {
|
|
293
277
|
let state = upsertTrackerEntry(
|
|
294
278
|
emptyTrackerState(),
|
|
295
279
|
entry(1, "no_local_impact"),
|
|
@@ -298,12 +282,7 @@ describe("tracker state", () => {
|
|
|
298
282
|
state = upsertTrackerEntry(state, entry(2, "no_local_impact"));
|
|
299
283
|
|
|
300
284
|
expect(getCodexAuditCursorTag(state)).toBe("rust-v0.2.0");
|
|
301
|
-
state = advanceCodexAuditCursor(
|
|
302
|
-
state,
|
|
303
|
-
"rust-v0.2.0",
|
|
304
|
-
"rust-v0.3.0",
|
|
305
|
-
true,
|
|
306
|
-
);
|
|
285
|
+
state = advanceCodexAuditCursor(state, "rust-v0.2.0", "rust-v0.3.0", true);
|
|
307
286
|
expect(getCodexAuditCursorTag(state)).toBe("rust-v0.3.0");
|
|
308
287
|
});
|
|
309
288
|
|
|
@@ -119,9 +119,9 @@ export function advanceCodexAuditCursor(
|
|
|
119
119
|
state: TrackerState,
|
|
120
120
|
previousTag: string,
|
|
121
121
|
currentTag: string,
|
|
122
|
-
|
|
122
|
+
isLatestRelease: boolean,
|
|
123
123
|
): TrackerState {
|
|
124
|
-
if (!
|
|
124
|
+
if (!isLatestRelease || state.audit_cursor_tag !== previousTag) return state;
|
|
125
125
|
if (!hasProcessedRange(state, previousTag, currentTag)) {
|
|
126
126
|
throw new Error(
|
|
127
127
|
`Cannot advance Codex audit cursor without terminal ${previousTag}...${currentTag}`,
|
|
@@ -135,35 +135,39 @@ export function recordAnalysis(
|
|
|
135
135
|
options: RecordAnalysisOptions,
|
|
136
136
|
): TrackerState {
|
|
137
137
|
const processedAt = options.processedAt ?? new Date().toISOString();
|
|
138
|
-
return upsertTrackerEntry(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
138
|
+
return upsertTrackerEntry(
|
|
139
|
+
state,
|
|
140
|
+
{
|
|
141
|
+
tag: options.analysis.current_tag,
|
|
142
|
+
previous_tag: options.analysis.previous_tag,
|
|
143
|
+
verdict: options.analysis.verdict,
|
|
144
|
+
outcome: options.outcome,
|
|
145
|
+
pr_url: options.prUrl ?? null,
|
|
146
|
+
notes: options.notes,
|
|
147
|
+
processed_at: processedAt,
|
|
148
|
+
compare_url: options.analysis.compare_url,
|
|
149
|
+
workflow_run_url: options.analysis.workflow_run_url,
|
|
150
|
+
},
|
|
151
|
+
options.analysis.is_latest_release,
|
|
152
|
+
);
|
|
149
153
|
}
|
|
150
154
|
|
|
151
155
|
export function upsertTrackerEntry(
|
|
152
156
|
state: TrackerState,
|
|
153
157
|
entry: TrackerEntry,
|
|
154
|
-
|
|
158
|
+
isLatestRelease = true,
|
|
155
159
|
): TrackerState {
|
|
156
160
|
let auditCursorTag = state.audit_cursor_tag;
|
|
157
|
-
if (auditCursorTag === null && !
|
|
161
|
+
if (auditCursorTag === null && !isLatestRelease) {
|
|
158
162
|
auditCursorTag = entry.previous_tag;
|
|
159
163
|
} else if (
|
|
160
|
-
|
|
164
|
+
isLatestRelease &&
|
|
161
165
|
entry.outcome === "error" &&
|
|
162
166
|
auditCursorTag === null
|
|
163
167
|
) {
|
|
164
168
|
auditCursorTag = entry.previous_tag;
|
|
165
169
|
} else if (
|
|
166
|
-
|
|
170
|
+
isLatestRelease &&
|
|
167
171
|
isTerminalOutcome(entry.outcome) &&
|
|
168
172
|
(auditCursorTag === null || entry.previous_tag === auditCursorTag)
|
|
169
173
|
) {
|
|
@@ -179,7 +183,10 @@ export function upsertTrackerEntry(
|
|
|
179
183
|
),
|
|
180
184
|
];
|
|
181
185
|
let processed = candidates.slice(0, HIDDEN_STATE_LIMIT);
|
|
182
|
-
if (
|
|
186
|
+
if (
|
|
187
|
+
auditCursorTag !== null &&
|
|
188
|
+
!isSupportedAuditCursor(processed, auditCursorTag)
|
|
189
|
+
) {
|
|
183
190
|
const support = candidates.find((candidate) =>
|
|
184
191
|
isAuditCursorSupportEntry(candidate, auditCursorTag),
|
|
185
192
|
);
|
|
@@ -276,7 +283,10 @@ function escapeTable(value: string): string {
|
|
|
276
283
|
|
|
277
284
|
function normalizeState(value: unknown): TrackerState {
|
|
278
285
|
if (!isRecord(value)) throw new TypeError("tracker state must be an object");
|
|
279
|
-
if (
|
|
286
|
+
if (
|
|
287
|
+
!Array.isArray(value.processed) ||
|
|
288
|
+
!value.processed.every(isTrackerEntry)
|
|
289
|
+
) {
|
|
280
290
|
throw new TypeError("tracker processed entries are invalid");
|
|
281
291
|
}
|
|
282
292
|
if (
|
|
@@ -346,9 +356,7 @@ function isSupportedAuditCursor(
|
|
|
346
356
|
processed: TrackerEntry[],
|
|
347
357
|
cursorTag: string,
|
|
348
358
|
): boolean {
|
|
349
|
-
return processed.some((entry) =>
|
|
350
|
-
isAuditCursorSupportEntry(entry, cursorTag),
|
|
351
|
-
);
|
|
359
|
+
return processed.some((entry) => isAuditCursorSupportEntry(entry, cursorTag));
|
|
352
360
|
}
|
|
353
361
|
|
|
354
362
|
function isAuditCursorSupportEntry(
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import { appendFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
import {
|
|
5
5
|
createIssueWithBody,
|
|
6
|
-
editIssueBody,
|
|
7
6
|
ensureLabels,
|
|
8
7
|
findIssueByExactTitle,
|
|
9
8
|
getIssueBody,
|
|
@@ -13,12 +12,11 @@ import {
|
|
|
13
12
|
analyzePiAiRelease,
|
|
14
13
|
compareStableVersions,
|
|
15
14
|
DEFAULT_TARGET_REPO,
|
|
16
|
-
|
|
15
|
+
findLatestStableReleaseAfter,
|
|
17
16
|
listStableReleases,
|
|
18
17
|
readInstalledVersion,
|
|
19
18
|
} from "./release-analysis.ts";
|
|
20
19
|
import {
|
|
21
|
-
advanceMergedPr,
|
|
22
20
|
getPendingPrForCursor,
|
|
23
21
|
hasCompletedRange,
|
|
24
22
|
initialTrackerState,
|
|
@@ -82,7 +80,7 @@ async function main(): Promise<void> {
|
|
|
82
80
|
const args = parseArgs(process.argv.slice(2));
|
|
83
81
|
const installedVersion = readInstalledVersion();
|
|
84
82
|
const tracker = ensureTrackerIssue(args, installedVersion);
|
|
85
|
-
|
|
83
|
+
const state = parseTrackerState(tracker.body);
|
|
86
84
|
const releases = await listStableReleases();
|
|
87
85
|
let previousVersion = args.previousVersion;
|
|
88
86
|
let currentVersion = args.currentVersion;
|
|
@@ -109,19 +107,16 @@ async function main(): Promise<void> {
|
|
|
109
107
|
`Merged pi-ai PR ${status.url} targets ${pending.version}, but main still declares ${installedVersion}`,
|
|
110
108
|
);
|
|
111
109
|
}
|
|
112
|
-
state = advanceMergedPr(state, pending.version);
|
|
113
|
-
if (!args.dryRun) {
|
|
114
|
-
editIssueBody(args.repo, tracker.number, renderTrackerBody(state));
|
|
115
|
-
}
|
|
116
110
|
} else {
|
|
117
|
-
console.log(
|
|
118
|
-
`Retrying ${pending.version}; prior PR was closed unmerged.`,
|
|
119
|
-
);
|
|
111
|
+
console.log(`Prior pi-ai PR ${status.url} was closed unmerged.`);
|
|
120
112
|
}
|
|
121
113
|
}
|
|
122
114
|
|
|
123
|
-
const
|
|
124
|
-
|
|
115
|
+
const latest = findLatestStableReleaseAfter(
|
|
116
|
+
releases,
|
|
117
|
+
state.audit_cursor_version,
|
|
118
|
+
);
|
|
119
|
+
if (!latest) {
|
|
125
120
|
console.log(
|
|
126
121
|
`No stable pi-ai release after ${state.audit_cursor_version}.`,
|
|
127
122
|
);
|
|
@@ -134,7 +129,7 @@ async function main(): Promise<void> {
|
|
|
134
129
|
return;
|
|
135
130
|
}
|
|
136
131
|
previousVersion = state.audit_cursor_version;
|
|
137
|
-
currentVersion =
|
|
132
|
+
currentVersion = latest.version;
|
|
138
133
|
}
|
|
139
134
|
|
|
140
135
|
const analysis = await analyzePiAiRelease({
|
|
@@ -3,10 +3,9 @@ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import {
|
|
6
|
-
areAdjacentStableReleases,
|
|
7
6
|
compareStableVersions,
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
extractChangelogRange,
|
|
8
|
+
findLatestStableReleaseAfter,
|
|
10
9
|
type PackageRelease,
|
|
11
10
|
parseRegistryMetadata,
|
|
12
11
|
readInstalledVersion,
|
|
@@ -59,24 +58,20 @@ describe("pi-ai npm releases", () => {
|
|
|
59
58
|
).toThrow("missing publication time for 0.82.1");
|
|
60
59
|
});
|
|
61
60
|
|
|
62
|
-
test("selects
|
|
61
|
+
test("selects the latest stable version after the cursor", () => {
|
|
63
62
|
const releases = releaseList("0.82.1", "0.83.0", "0.84.0");
|
|
64
|
-
expect(
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
expect(findLatestStableReleaseAfter(releases, "0.82.1")?.version).toBe(
|
|
64
|
+
"0.84.0",
|
|
65
|
+
);
|
|
66
|
+
expect(findLatestStableReleaseAfter(releases, "0.84.0")).toBeNull();
|
|
67
|
+
expect(() => findLatestStableReleaseAfter(releases, "0.80.0")).toThrow(
|
|
67
68
|
"Could not find pi-ai cursor release 0.80.0",
|
|
68
69
|
);
|
|
69
70
|
});
|
|
70
|
-
|
|
71
|
-
test("recognizes only adjacent release pairs", () => {
|
|
72
|
-
const releases = releaseList("0.82.1", "0.83.0", "0.84.0");
|
|
73
|
-
expect(areAdjacentStableReleases(releases, "0.82.1", "0.83.0")).toBe(true);
|
|
74
|
-
expect(areAdjacentStableReleases(releases, "0.82.1", "0.84.0")).toBe(false);
|
|
75
|
-
});
|
|
76
71
|
});
|
|
77
72
|
|
|
78
73
|
describe("pi-ai release evidence", () => {
|
|
79
|
-
test("extracts
|
|
74
|
+
test("extracts the cumulative changelog after the cursor", () => {
|
|
80
75
|
const changelog = `# Changelog
|
|
81
76
|
|
|
82
77
|
## [0.84.0] - 2026-08-06
|
|
@@ -88,16 +83,27 @@ describe("pi-ai release evidence", () => {
|
|
|
88
83
|
## [0.83.0] - 2026-07-29
|
|
89
84
|
|
|
90
85
|
- Previous feature.
|
|
86
|
+
|
|
87
|
+
## [0.82.1] - 2026-07-20
|
|
88
|
+
|
|
89
|
+
- Cursor release.
|
|
91
90
|
`;
|
|
92
91
|
expect(
|
|
93
|
-
|
|
92
|
+
extractChangelogRange(changelog, "0.82.1", "0.84.0"),
|
|
94
93
|
).toBe(`## [0.84.0] - 2026-08-06
|
|
95
94
|
|
|
96
95
|
### Added
|
|
97
96
|
|
|
98
|
-
- Current feature
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
- Current feature.
|
|
98
|
+
|
|
99
|
+
## [0.83.0] - 2026-07-29
|
|
100
|
+
|
|
101
|
+
- Previous feature.`);
|
|
102
|
+
expect(() => extractChangelogRange(changelog, "0.80.0", "0.84.0")).toThrow(
|
|
103
|
+
"Could not find pi-ai changelog section 0.80.0",
|
|
104
|
+
);
|
|
105
|
+
expect(() => extractChangelogRange(changelog, "0.82.1", "0.85.0")).toThrow(
|
|
106
|
+
"Could not find pi-ai changelog section 0.85.0",
|
|
101
107
|
);
|
|
102
108
|
});
|
|
103
109
|
|