pi-blackhole 0.3.4 → 0.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/README.md +13 -16
- package/example-config.json +19 -27
- package/package.json +1 -1
- package/src/commands/memory.ts +1 -1
- package/src/commands/pi-vcc.ts +3 -9
- package/src/commands/vcc-recall.ts +16 -3
- package/src/core/brief.ts +18 -13
- package/src/core/build-sections.ts +1 -2
- package/src/core/content.ts +8 -5
- package/src/core/drill-down.ts +2 -9
- package/src/core/filter-noise.ts +7 -10
- package/src/core/format.ts +25 -25
- package/src/core/load-messages.ts +71 -2
- package/src/core/normalize.ts +10 -4
- package/src/core/recall-scope.ts +3 -3
- package/src/core/render-entries.ts +10 -2
- package/src/core/search-entries.ts +12 -7
- package/src/core/settings.ts +3 -34
- package/src/core/summarize.ts +33 -13
- package/src/core/tool-args.ts +4 -1
- package/src/core/unified-config.ts +27 -25
- package/src/extract/commits.ts +3 -2
- package/src/extract/files.ts +6 -4
- package/src/hooks/before-compact.ts +7 -3
- package/src/om/agents/dropper/agent.ts +2 -7
- package/src/om/agents/observer/agent.ts +11 -13
- package/src/om/agents/reflector/agent.ts +2 -7
- package/src/om/compaction-trigger.ts +54 -18
- package/src/om/configure-overlay.ts +5 -1
- package/src/om/consolidation.ts +35 -13
- package/src/om/cooldown.ts +15 -12
- package/src/om/debug-log.ts +79 -11
- package/src/om/key-matcher.ts +6 -32
- package/src/om/ledger/fold.ts +8 -0
- package/src/om/ledger/render-summary.ts +16 -15
- package/src/om/ledger/types.ts +1 -1
- package/src/om/pending.ts +38 -5
- package/src/om/provider-stream.ts +17 -0
- package/src/om/retryable-error.ts +26 -0
- package/src/om/reverse-recall.ts +9 -1
- package/src/om/runtime.ts +39 -18
- package/src/sections.ts +0 -3
- package/src/tools/recall.ts +11 -9
package/src/om/runtime.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - markConsolidationError sets 30s retry gate for failed runs.
|
|
11
11
|
*/
|
|
12
12
|
import { type Config, type ConfiguredModel, DEFAULTS, loadConfig } from "./config.js";
|
|
13
|
-
import { isCooldownActive, recordCooldown, expireCooldowns, modelKey } from "./cooldown.js";
|
|
13
|
+
import { isCooldownActive, getCooldownEntry, recordCooldown, expireCooldowns, modelKey } from "./cooldown.js";
|
|
14
14
|
|
|
15
15
|
export type ResolveResult =
|
|
16
16
|
| { ok: true; model: any; apiKey: string; headers?: Record<string, string>; cooldownApplied?: boolean }
|
|
@@ -94,13 +94,17 @@ export class Runtime {
|
|
|
94
94
|
* Tries the candidate list in order:
|
|
95
95
|
* 1. Primary stage model → 2. Stage fallbacks → 3. Base config.model → 4. Session model.
|
|
96
96
|
*
|
|
97
|
+
* Session model fallback can be disabled via config.sessionFallback: false.
|
|
98
|
+
* When disabled, returns { ok: false } instead of using the session model,
|
|
99
|
+
* allowing the stage to be skipped entirely when all configured OM models fail.
|
|
100
|
+
*
|
|
97
101
|
* Skips models that are currently in a cooldown window.
|
|
98
102
|
* On retryable error (after the agent runs), the model that failed is cooled down
|
|
99
103
|
* and the next candidate is tried. The caller must call `recordRetryableError`
|
|
100
104
|
* after the API attempt to mark the failed model.
|
|
101
105
|
*
|
|
102
106
|
* Returns `ok: true` with the resolved model, or `ok: false` with a reason
|
|
103
|
-
* if all candidates (including session model) are exhausted or unavailable.
|
|
107
|
+
* if all candidates (including session model, if enabled) are exhausted or unavailable.
|
|
104
108
|
*/
|
|
105
109
|
async resolveModel(ctx: ResolveCtx): Promise<ResolveResult> {
|
|
106
110
|
const candidates = this.buildCandidateList(ctx.stageModel, ctx.stageFallbacks);
|
|
@@ -123,8 +127,10 @@ export class Runtime {
|
|
|
123
127
|
|
|
124
128
|
if (isCooldownActive(candidate)) {
|
|
125
129
|
if (ctx.hasUI && ctx.ui) {
|
|
130
|
+
const entry = getCooldownEntry(candidate);
|
|
131
|
+
const reason = entry ? `: ${entry.reason}` : "";
|
|
126
132
|
ctx.ui.notify(
|
|
127
|
-
`Observational memory: ${stageName} skipping ${key} (cooldown
|
|
133
|
+
`Observational memory: ${stageName} skipping ${key} (cooldown${reason})`,
|
|
128
134
|
"info",
|
|
129
135
|
);
|
|
130
136
|
}
|
|
@@ -162,25 +168,40 @@ export class Runtime {
|
|
|
162
168
|
};
|
|
163
169
|
}
|
|
164
170
|
|
|
165
|
-
// Fall back to session model
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
171
|
+
// Fall back to session model (if enabled)
|
|
172
|
+
if (this.config.sessionFallback !== false) {
|
|
173
|
+
const sessionModel = ctx.model;
|
|
174
|
+
if (!sessionModel) {
|
|
175
|
+
return { ok: false, reason: `no model available for ${stageName} (all candidates exhausted, no session model)` };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(sessionModel);
|
|
179
|
+
if (!auth.ok || !auth.apiKey) {
|
|
180
|
+
const provider = (sessionModel as { provider?: string }).provider ?? "unknown";
|
|
181
|
+
return { ok: false, reason: `no API key for session model provider "${provider}"` };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
ok: true,
|
|
186
|
+
model: sessionModel,
|
|
187
|
+
apiKey: auth.apiKey as string,
|
|
188
|
+
headers: auth.headers as Record<string, string> | undefined,
|
|
189
|
+
cooldownApplied: false,
|
|
190
|
+
};
|
|
169
191
|
}
|
|
170
192
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
193
|
+
// All configured candidates exhausted and session fallback disabled —
|
|
194
|
+
// skip the stage entirely. Info-level to match cooldown-disabled pattern.
|
|
195
|
+
// Set resolveFailureNotified so the consolidation layer doesn't duplicate.
|
|
196
|
+
if (ctx.hasUI && ctx.ui) {
|
|
197
|
+
ctx.ui.notify(
|
|
198
|
+
`Observational memory: ${stageName} skipped — all candidates failed (sessionFallback disabled, won't use main model)`,
|
|
199
|
+
"info",
|
|
200
|
+
);
|
|
175
201
|
}
|
|
202
|
+
this.resolveFailureNotified = true;
|
|
176
203
|
|
|
177
|
-
return {
|
|
178
|
-
ok: true,
|
|
179
|
-
model: sessionModel,
|
|
180
|
-
apiKey: auth.apiKey as string,
|
|
181
|
-
headers: auth.headers as Record<string, string> | undefined,
|
|
182
|
-
cooldownApplied: false,
|
|
183
|
-
};
|
|
204
|
+
return { ok: false, reason: `no model available for ${stageName} (all candidates exhausted, sessionFallback disabled)` };
|
|
184
205
|
}
|
|
185
206
|
|
|
186
207
|
/**
|
package/src/sections.ts
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* Upstream: https://github.com/sting8k/pi-vcc (src/sections.ts)
|
|
5
5
|
* Unmodified.
|
|
6
6
|
*/
|
|
7
|
-
import type { TranscriptEntry } from "./core/brief";
|
|
8
7
|
|
|
9
8
|
export interface SectionData {
|
|
10
9
|
sessionGoal: string[];
|
|
@@ -13,6 +12,4 @@ export interface SectionData {
|
|
|
13
12
|
commits: string[];
|
|
14
13
|
userPreferences: string[];
|
|
15
14
|
briefTranscript: string;
|
|
16
|
-
/** Structured transcript entries (verbose object format) */
|
|
17
|
-
transcriptEntries: TranscriptEntry[];
|
|
18
15
|
}
|
package/src/tools/recall.ts
CHANGED
|
@@ -138,7 +138,11 @@ async function vccRecall(params: { query?: string; expand?: number[]; page?: num
|
|
|
138
138
|
|
|
139
139
|
// Merge expanded entries into full result set BEFORE pagination
|
|
140
140
|
// so pagination counts and positioning stay consistent
|
|
141
|
+
let appendedExpandCount = 0;
|
|
141
142
|
if (expandedFullEntries) {
|
|
143
|
+
// Track expand-only entries that don't match the query so the header can distinguish them
|
|
144
|
+
const existingIndices = new Set(allResults.map((r) => r.index));
|
|
145
|
+
appendedExpandCount = expandedFullEntries.filter((fe) => !existingIndices.has(fe.index)).length;
|
|
142
146
|
allResults = mergeExpandedIntoSearchResults(allResults, expandedFullEntries);
|
|
143
147
|
}
|
|
144
148
|
|
|
@@ -148,9 +152,10 @@ async function vccRecall(params: { query?: string; expand?: number[]; page?: num
|
|
|
148
152
|
const pageResults: SearchHit[] = allResults.slice(start, start + PAGE_SIZE);
|
|
149
153
|
const totalPages = Math.ceil(allResults.length / PAGE_SIZE);
|
|
150
154
|
const scopeSuffix = scope === "all" ? " (scope: all)" : "";
|
|
155
|
+
const matchCount = allResults.length - appendedExpandCount;
|
|
151
156
|
const header = totalPages > 1
|
|
152
|
-
? `Page ${page}/${totalPages} (${
|
|
153
|
-
: `${
|
|
157
|
+
? `Page ${page}/${totalPages} (${matchCount} matches${appendedExpandCount > 0 ? ` + ${appendedExpandCount} expanded` : ""}${scopeSuffix})`
|
|
158
|
+
: `${matchCount} matches${appendedExpandCount > 0 ? ` (+ ${appendedExpandCount} expanded)` : ""}${scopeSuffix}`;
|
|
154
159
|
const footer = page < totalPages
|
|
155
160
|
? `\n--- Use page:${page + 1}${scope === "all" ? " with scope:'all'" : ""} for more results ---`
|
|
156
161
|
: "";
|
|
@@ -233,13 +238,10 @@ export function registerRecallTool(pi: ExtensionAPI): void {
|
|
|
233
238
|
"Search session history and earlier lines omitted, file write/edit content by text/regex. " +
|
|
234
239
|
"Expand entries (#N), drill-down file content (#N:path) with paging, or aggregate touched files (mode:touched).",
|
|
235
240
|
promptSnippet:
|
|
236
|
-
"recall: Search session history + file write/edit content by text/regex. #N expand, #N:path drill-down with offset
|
|
241
|
+
"recall: Search session history + file write/edit content by text/regex. #N expand, #N:path drill-down with optional :offset:limit or :full, mode:file/touched.",
|
|
237
242
|
promptGuidelines: [
|
|
238
|
-
"Use recall —
|
|
239
|
-
"Use recall —
|
|
240
|
-
"Use recall — drill-down supports paging: #42:auth.ts shows first 30 lines, #42:auth.ts:30 shows next 30, #42:auth.ts:full shows everything. Note: edit diffs are not indexed for text search — drill-down reads them from raw JSONL.",
|
|
241
|
-
"Use recall — when a drill-down path matches multiple files, options are listed. Narrow with a more specific path substring.",
|
|
242
|
-
"Use recall — hex observation/reflection ids (12-char hex) link memory evidence to session entries with cross-references for navigation.",
|
|
243
|
+
"Use recall — literal text/regex search across session history and file write/edit content. #N expands an entry; #N:path with optional :offset:limit or :full drills down into file content; 12-char hex ids recover observation/reflection sources. mode:file for file-content-only, mode:touched for aggregated files-by-path. scope:'all' to search the full session. If no results, try fewer terms or a regex pattern.",
|
|
244
|
+
"Use recall — when a drill-down path matches multiple files, options are listed. Narrow with a more specific path substring. Only full-file writes are indexed for text search (edit diffs are not).",
|
|
243
245
|
],
|
|
244
246
|
parameters: Type.Object({
|
|
245
247
|
query: Type.Optional(
|
|
@@ -255,7 +257,7 @@ export function registerRecallTool(pi: ExtensionAPI): void {
|
|
|
255
257
|
StringEnum(["lineage", "all"] as const, { description: "Search scope. lineage = active lineage (default), all = entire session." }),
|
|
256
258
|
),
|
|
257
259
|
mode: Type.Optional(
|
|
258
|
-
StringEnum(["hybrid", "file", "
|
|
260
|
+
StringEnum(["hybrid", "file", "touched"] as const, { description: "What content to search. hybrid (default) = all session content. file = file content only. touched = files-by-path summary with entry indices." }),
|
|
259
261
|
),
|
|
260
262
|
}),
|
|
261
263
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|