@oh-my-pi/pi-coding-agent 17.3.4 → 17.3.7
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/CHANGELOG.md +88 -0
- package/dist/{CHANGELOG-trcc215s.md → CHANGELOG-1hmwwt45.md} +88 -0
- package/dist/cli.js +3156 -3163
- package/dist/docs-index.generated.txt +1 -1
- package/dist/types/cli/stats-cli.d.ts +1 -6
- package/dist/types/commands/stats.d.ts +4 -0
- package/dist/types/config/model-discovery.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +6 -1
- package/dist/types/config/settings-schema.d.ts +10 -0
- package/dist/types/debug/report-bundle.d.ts +7 -2
- package/dist/types/extensibility/extensions/loader.d.ts +2 -0
- package/dist/types/extensibility/extensions/runner.d.ts +24 -6
- package/dist/types/extensibility/extensions/types.d.ts +60 -0
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +4 -0
- package/dist/types/launch/presence.d.ts +11 -0
- package/dist/types/lsp/client.d.ts +22 -0
- package/dist/types/lsp/workspace-diagnostics.d.ts +2 -0
- package/dist/types/mcp/config.d.ts +3 -1
- package/dist/types/modes/components/tool-execution.d.ts +3 -1
- package/dist/types/registry/agent-lifecycle.d.ts +11 -0
- package/dist/types/registry/agent-registry.d.ts +4 -0
- package/dist/types/session/agent-session-types.d.ts +0 -2
- package/dist/types/session/agent-session.d.ts +20 -0
- package/dist/types/session/date-cwd-reminder.d.ts +22 -0
- package/dist/types/session/irc-bridge.d.ts +2 -0
- package/dist/types/session/messages.d.ts +4 -0
- package/dist/types/session/session-loader.d.ts +18 -11
- package/dist/types/session/session-maintenance.d.ts +6 -2
- package/dist/types/session/session-manager.d.ts +19 -3
- package/dist/types/session/session-tools.d.ts +0 -1
- package/dist/types/session/turn-recovery.d.ts +7 -7
- package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +1 -0
- package/dist/types/tools/browser/attach.d.ts +16 -0
- package/dist/types/tools/browser/launch.d.ts +2 -2
- package/dist/types/tools/browser.d.ts +1 -1
- package/dist/types/tools/builtin-names.d.ts +1 -1
- package/dist/types/tools/file-write-fallback.d.ts +124 -0
- package/dist/types/tools/hub/jobs.d.ts +6 -0
- package/dist/types/tools/hub/types.d.ts +6 -0
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/path-utils.d.ts +23 -0
- package/dist/types/vibe/runtime.d.ts +3 -3
- package/package.json +13 -13
- package/src/auto-thinking/classifier.ts +37 -23
- package/src/cli/models-cli.ts +1 -1
- package/src/cli/stats-cli.ts +6 -72
- package/src/commands/stats.ts +7 -4
- package/src/commit/analysis/conventional.ts +15 -9
- package/src/commit/analysis/summary.ts +15 -9
- package/src/commit/changelog/generate.ts +15 -9
- package/src/commit/map-reduce/map-phase.ts +3 -19
- package/src/commit/map-reduce/reduce-phase.ts +15 -9
- package/src/config/model-discovery.ts +3 -3
- package/src/config/model-registry.ts +29 -16
- package/src/config/settings-schema.ts +13 -0
- package/src/debug/report-bundle.ts +25 -59
- package/src/discovery/claude-plugins.ts +2 -1
- package/src/discovery/claude.ts +4 -2
- package/src/discovery/helpers.ts +5 -0
- package/src/edit/hashline/filesystem.ts +9 -3
- package/src/edit/modes/patch.ts +31 -5
- package/src/edit/renderer.ts +48 -19
- package/src/extensibility/extensions/loader.ts +20 -5
- package/src/extensibility/extensions/runner.ts +239 -24
- package/src/extensibility/extensions/types.ts +62 -0
- package/src/extensibility/extensions/wrapper.ts +26 -11
- package/src/extensibility/legacy-pi-ai-shim.ts +4 -0
- package/src/extensibility/plugins/marketplace/manager.ts +15 -18
- package/src/irc/bus.ts +1 -1
- package/src/launch/broker.ts +8 -1
- package/src/launch/presence.ts +77 -1
- package/src/lsp/client.ts +35 -3
- package/src/lsp/clients/biome-client.ts +47 -93
- package/src/lsp/servers.ts +17 -11
- package/src/lsp/tool.ts +2 -4
- package/src/lsp/workspace-diagnostics.ts +24 -2
- package/src/lsp/writethrough.ts +20 -10
- package/src/mcp/config.ts +50 -6
- package/src/memories/index.ts +29 -25
- package/src/mnemopi/backend.ts +13 -11
- package/src/modes/components/ask-dialog.ts +25 -5
- package/src/modes/components/tool-execution.ts +10 -6
- package/src/modes/components/welcome.ts +4 -1
- package/src/modes/controllers/extension-ui-controller.ts +52 -32
- package/src/modes/controllers/selector-controller.ts +6 -0
- package/src/modes/controllers/tan-command-controller.ts +1 -0
- package/src/modes/interactive-mode.ts +49 -10
- package/src/modes/theme/defaults/birch.json +2 -2
- package/src/prompts/system/checkpoint-active-notice.md +5 -0
- package/src/prompts/system/date-cwd-reminder.md +3 -0
- package/src/prompts/system/project-prompt.md +0 -1
- package/src/prompts/system/rewind-report.md +1 -3
- package/src/prompts/tools/browser.md +1 -0
- package/src/prompts/tools/rewind.md +1 -13
- package/src/registry/agent-lifecycle.ts +34 -0
- package/src/registry/agent-registry.ts +27 -2
- package/src/registry/persisted-agents.ts +40 -20
- package/src/sdk.ts +32 -2
- package/src/session/agent-session-types.ts +0 -2
- package/src/session/agent-session.ts +178 -46
- package/src/session/date-cwd-reminder.ts +77 -0
- package/src/session/irc-bridge.ts +5 -0
- package/src/session/messages.ts +5 -1
- package/src/session/session-loader.ts +106 -64
- package/src/session/session-maintenance.ts +189 -115
- package/src/session/session-manager.ts +142 -35
- package/src/session/session-persistence.ts +4 -4
- package/src/session/session-storage.ts +18 -5
- package/src/session/session-tools.ts +5 -10
- package/src/session/turn-recovery.ts +102 -20
- package/src/session/unexpected-stop-classifier.ts +33 -21
- package/src/slash-commands/builtin-session.ts +1 -1
- package/src/slash-commands/helpers/stats-dashboard.ts +23 -9
- package/src/system-prompt.ts +0 -5
- package/src/task/executor.ts +4 -15
- package/src/task/persisted-revive.ts +3 -6
- package/src/tools/ask.ts +10 -3
- package/src/tools/browser/attach.ts +80 -25
- package/src/tools/browser/launch.ts +44 -15
- package/src/tools/browser/relay/daemon.ts +3 -7
- package/src/tools/browser/render.ts +1 -1
- package/src/tools/browser/shared-daemon.ts +3 -7
- package/src/tools/browser.ts +5 -5
- package/src/tools/builtin-names.ts +7 -3
- package/src/tools/checkpoint.ts +1 -7
- package/src/tools/file-write-fallback.ts +467 -0
- package/src/tools/hub/index.ts +1 -1
- package/src/tools/hub/jobs.ts +20 -3
- package/src/tools/hub/types.ts +6 -0
- package/src/tools/index.ts +1 -0
- package/src/tools/path-utils.ts +79 -0
- package/src/tools/render-utils.ts +53 -21
- package/src/tools/think.ts +15 -3
- package/src/tts/speech-enhancer.ts +19 -14
- package/src/utils/commit-message-generator.ts +14 -12
- package/src/utils/title-generator.ts +23 -19
- package/src/vibe/runtime.ts +32 -17
|
@@ -2,14 +2,7 @@ import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
|
2
2
|
import { getBlobsDir, isEnoent, parseJsonlLenient } from "@oh-my-pi/pi-utils";
|
|
3
3
|
import { BlobStore, isBlobRef, resolveImageData, resolveImageDataUrl } from "./blob-store";
|
|
4
4
|
import { buildSessionContext } from "./session-context";
|
|
5
|
-
import {
|
|
6
|
-
type FileEntry,
|
|
7
|
-
type RawFileEntry,
|
|
8
|
-
SESSION_TITLE_SLOT_BYTES,
|
|
9
|
-
type SessionEntry,
|
|
10
|
-
type SessionHeader,
|
|
11
|
-
type SessionTitleSlotEntry,
|
|
12
|
-
} from "./session-entries";
|
|
5
|
+
import type { FileEntry, RawFileEntry, SessionEntry, SessionHeader } from "./session-entries";
|
|
13
6
|
import { migrateToCurrentVersion } from "./session-migrations";
|
|
14
7
|
import { isImageBlock, isImageDataPayload } from "./session-persistence";
|
|
15
8
|
import { FileSessionStorage, type SessionStorage } from "./session-storage";
|
|
@@ -33,6 +26,15 @@ export interface VisitEntriesFromFileStreamOptions {
|
|
|
33
26
|
yieldEveryBytes?: number;
|
|
34
27
|
/** Yield to the macrotask queue after this many entries have been visited. */
|
|
35
28
|
yieldEveryEntries?: number;
|
|
29
|
+
/** Called once for every malformed JSONL record skipped by the stream. */
|
|
30
|
+
onMalformedRecord?: () => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Parsed session entries plus corruption metadata needed by writable loaders. */
|
|
34
|
+
export interface SessionLoadResult {
|
|
35
|
+
entries: FileEntry[];
|
|
36
|
+
titleSlot: SessionTitleUpdate | undefined;
|
|
37
|
+
malformedRecords: number;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
function splitTitleSlot(content: string): { body: string; slot: SessionTitleUpdate | undefined } {
|
|
@@ -42,31 +44,35 @@ function splitTitleSlot(content: string): { body: string; slot: SessionTitleUpda
|
|
|
42
44
|
return { body: content.slice(newlineIndex + 1), slot };
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
function
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
function isValidSessionHeader(entry: FileEntry | undefined): entry is SessionHeader {
|
|
48
|
+
return entry?.type === "session" && typeof entry.id === "string";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function applyTitleSlot(entry: FileEntry | undefined, slot: SessionTitleUpdate | undefined): void {
|
|
52
|
+
if (!slot || !isValidSessionHeader(entry)) return;
|
|
49
53
|
if (slot.title && slot.title.length > 0) {
|
|
50
|
-
|
|
54
|
+
entry.title = slot.title;
|
|
51
55
|
} else {
|
|
52
|
-
delete
|
|
56
|
+
delete entry.title;
|
|
53
57
|
}
|
|
54
58
|
if (slot.source) {
|
|
55
|
-
|
|
59
|
+
entry.titleSource = slot.source;
|
|
56
60
|
} else {
|
|
57
|
-
delete
|
|
61
|
+
delete entry.titleSource;
|
|
58
62
|
}
|
|
59
|
-
return entries;
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
/** Parse session JSONL while stripping and folding the optional fixed title slot. */
|
|
63
|
-
export function parseSessionContent(content: string): {
|
|
64
|
-
entries: FileEntry[];
|
|
65
|
-
titleSlot: SessionTitleUpdate | undefined;
|
|
66
|
-
} {
|
|
66
|
+
export function parseSessionContent(content: string): SessionLoadResult {
|
|
67
67
|
const { body, slot } = splitTitleSlot(content);
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
let malformedRecords = 0;
|
|
69
|
+
const entries = parseJsonlLenient<RawFileEntry>(body, {
|
|
70
|
+
onMalformedRecord: () => {
|
|
71
|
+
malformedRecords++;
|
|
72
|
+
},
|
|
73
|
+
}) as FileEntry[];
|
|
74
|
+
applyTitleSlot(entries[0], slot);
|
|
75
|
+
return { entries, titleSlot: slot, malformedRecords };
|
|
70
76
|
}
|
|
71
77
|
|
|
72
78
|
/** Parse session JSONL and visit each entry without retaining prior entries. */
|
|
@@ -77,6 +83,7 @@ export async function visitEntriesFromFileStream(
|
|
|
77
83
|
): Promise<SessionTitleUpdate | undefined> {
|
|
78
84
|
let titleSlot: SessionTitleUpdate | undefined;
|
|
79
85
|
let sawFirstLine = false;
|
|
86
|
+
let sawFirstEntry = false;
|
|
80
87
|
let bytesSinceYield = 0;
|
|
81
88
|
let entriesSinceYield = 0;
|
|
82
89
|
let recordsSeen = 0;
|
|
@@ -121,8 +128,13 @@ export async function visitEntriesFromFileStream(
|
|
|
121
128
|
stopped = true;
|
|
122
129
|
break;
|
|
123
130
|
}
|
|
131
|
+
const entry = value as FileEntry;
|
|
132
|
+
if (!sawFirstEntry) {
|
|
133
|
+
sawFirstEntry = true;
|
|
134
|
+
applyTitleSlot(entry, titleSlot);
|
|
135
|
+
}
|
|
124
136
|
try {
|
|
125
|
-
if (visit(
|
|
137
|
+
if (visit(entry) === false) {
|
|
126
138
|
stopped = true;
|
|
127
139
|
break;
|
|
128
140
|
}
|
|
@@ -143,6 +155,15 @@ export async function visitEntriesFromFileStream(
|
|
|
143
155
|
// Malformed record: skip past the next newline and continue.
|
|
144
156
|
const nextNewline = buffer.indexOf(0x0a, read);
|
|
145
157
|
if (nextNewline === -1) break; // rest of the bad line not yet received
|
|
158
|
+
let nonWhitespace = false;
|
|
159
|
+
for (let index = read; index < nextNewline; index++) {
|
|
160
|
+
const byte = buffer[index];
|
|
161
|
+
if (byte !== 0x09 && byte !== 0x0d && byte !== 0x20) {
|
|
162
|
+
nonWhitespace = true;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (nonWhitespace) options.onMalformedRecord?.();
|
|
146
167
|
recordsSeen++;
|
|
147
168
|
buffer = buffer.subarray(nextNewline + 1);
|
|
148
169
|
if (recordsSeen >= maxRecords) {
|
|
@@ -203,64 +224,85 @@ export async function visitEntriesFromFileStream(
|
|
|
203
224
|
}
|
|
204
225
|
|
|
205
226
|
/** Exported for testing — the ≥8MiB streaming path (works on any file size). */
|
|
206
|
-
export async function loadEntriesFromFileStream(filePath: string): Promise<{
|
|
207
|
-
entries: FileEntry[];
|
|
208
|
-
titleSlot: SessionTitleUpdate | undefined;
|
|
209
|
-
}> {
|
|
227
|
+
export async function loadEntriesFromFileStream(filePath: string): Promise<SessionLoadResult> {
|
|
210
228
|
const entries: FileEntry[] = [];
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
229
|
+
let malformedRecords = 0;
|
|
230
|
+
const titleSlot = await visitEntriesFromFileStream(
|
|
231
|
+
filePath,
|
|
232
|
+
entry => {
|
|
233
|
+
entries.push(entry);
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
onMalformedRecord: () => {
|
|
237
|
+
malformedRecords++;
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
);
|
|
241
|
+
return { entries, titleSlot, malformedRecords };
|
|
215
242
|
}
|
|
216
243
|
|
|
217
|
-
/**
|
|
218
|
-
export
|
|
244
|
+
/** Exported for compaction.test.ts */
|
|
245
|
+
export function parseSessionEntries(content: string): FileEntry[] {
|
|
246
|
+
return parseSessionContent(content).entries;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function shouldStreamEntries(storage: SessionStorage, size: number): boolean {
|
|
250
|
+
return storage instanceof FileSessionStorage && size >= STREAM_LOAD_THRESHOLD_BYTES;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function loadWithKnownSize(filePath: string, storage: SessionStorage, size: number): Promise<SessionLoadResult> {
|
|
254
|
+
const loaded = shouldStreamEntries(storage, size)
|
|
255
|
+
? await loadEntriesFromFileStream(filePath)
|
|
256
|
+
: parseSessionContent(await storage.readText(filePath));
|
|
257
|
+
return isValidSessionHeader(loaded.entries[0]) ? loaded : { ...loaded, entries: [] };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Load and validate a session while retaining malformed-record diagnostics. */
|
|
261
|
+
export async function loadSessionFile(
|
|
219
262
|
filePath: string,
|
|
220
263
|
storage: SessionStorage = new FileSessionStorage(),
|
|
221
|
-
): Promise<
|
|
222
|
-
let head: string;
|
|
264
|
+
): Promise<SessionLoadResult> {
|
|
223
265
|
try {
|
|
224
|
-
|
|
266
|
+
return await loadWithKnownSize(filePath, storage, storage.statSync(filePath).size);
|
|
225
267
|
} catch (err) {
|
|
226
|
-
if (isEnoent(err)) return undefined;
|
|
268
|
+
if (isEnoent(err)) return { entries: [], titleSlot: undefined, malformedRecords: 0 };
|
|
227
269
|
throw err;
|
|
228
270
|
}
|
|
229
|
-
const newlineIndex = head.indexOf("\n");
|
|
230
|
-
if (newlineIndex < 0) return undefined;
|
|
231
|
-
return parseTitleSlotLine(head.slice(0, newlineIndex));
|
|
232
|
-
}
|
|
233
|
-
/** Exported for compaction.test.ts */
|
|
234
|
-
export function parseSessionEntries(content: string): FileEntry[] {
|
|
235
|
-
return parseSessionContent(content).entries;
|
|
236
271
|
}
|
|
237
272
|
|
|
238
|
-
/**
|
|
273
|
+
/** Load the valid entries from a session file, skipping malformed records. */
|
|
239
274
|
export async function loadEntriesFromFile(
|
|
240
275
|
filePath: string,
|
|
241
276
|
storage: SessionStorage = new FileSessionStorage(),
|
|
242
277
|
): Promise<FileEntry[]> {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const stat = storage.statSync(filePath);
|
|
246
|
-
loaded =
|
|
247
|
-
storage instanceof FileSessionStorage && stat.size >= STREAM_LOAD_THRESHOLD_BYTES
|
|
248
|
-
? await loadEntriesFromFileStream(filePath)
|
|
249
|
-
: parseSessionContent(await storage.readText(filePath));
|
|
250
|
-
} catch (err) {
|
|
251
|
-
if (isEnoent(err)) return [];
|
|
252
|
-
throw err;
|
|
253
|
-
}
|
|
254
|
-
const { entries } = loaded;
|
|
278
|
+
return (await loadSessionFile(filePath, storage)).entries;
|
|
279
|
+
}
|
|
255
280
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
281
|
+
/**
|
|
282
|
+
* Visit session entries, using bounded streaming for large file-backed journals.
|
|
283
|
+
* Small files and non-file backends keep the existing full-load path.
|
|
284
|
+
*/
|
|
285
|
+
export async function visitEntriesFromFile(
|
|
286
|
+
filePath: string,
|
|
287
|
+
visit: (entry: FileEntry) => void | boolean,
|
|
288
|
+
storage: SessionStorage = new FileSessionStorage(),
|
|
289
|
+
): Promise<void> {
|
|
290
|
+
const size = storage.statSync(filePath).size;
|
|
291
|
+
if (shouldStreamEntries(storage, size)) {
|
|
292
|
+
let sawFirstEntry = false;
|
|
293
|
+
await visitEntriesFromFileStream(filePath, entry => {
|
|
294
|
+
if (!sawFirstEntry) {
|
|
295
|
+
sawFirstEntry = true;
|
|
296
|
+
if (!isValidSessionHeader(entry)) return false;
|
|
297
|
+
}
|
|
298
|
+
return visit(entry);
|
|
299
|
+
});
|
|
300
|
+
return;
|
|
261
301
|
}
|
|
262
302
|
|
|
263
|
-
|
|
303
|
+
for (const entry of (await loadWithKnownSize(filePath, storage, size)).entries) {
|
|
304
|
+
if (visit(entry) === false) return;
|
|
305
|
+
}
|
|
264
306
|
}
|
|
265
307
|
|
|
266
308
|
/**
|