pi-advisor-flow 0.5.6 → 0.7.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/CHANGELOG.md +29 -0
- package/README.md +1 -0
- package/dist/index.js +3160 -1255
- package/extensions/index.ts +9 -9
- package/package.json +12 -8
- package/src/{untracked.ts → attachments.ts} +2 -5
- package/src/commands/activation-preparation.ts +6 -6
- package/src/commands/activation.ts +7 -7
- package/src/commands/lifecycle.ts +5 -5
- package/src/commands/manual-command.ts +13 -9
- package/src/commands/manual-consultation.ts +24 -10
- package/src/commands/manual-progress.ts +3 -3
- package/src/commands/model-commands.ts +6 -6
- package/src/commands/model-options.ts +4 -4
- package/src/commands/model-picker.ts +2 -2
- package/src/commands/registration.ts +8 -8
- package/src/commands/renderers.ts +5 -5
- package/src/commands/runtime.ts +8 -8
- package/src/commands/settings-commands.ts +8 -8
- package/src/commands/settings-persistence.ts +52 -4
- package/src/commands/types.ts +6 -13
- package/src/commands.ts +2 -2
- package/src/config/args.ts +3 -3
- package/src/config/defaults.ts +78 -3
- package/src/config/schema.ts +434 -0
- package/src/config/state.ts +71 -2
- package/src/config/storage.ts +27 -119
- package/src/config/types.ts +30 -1
- package/src/config/validation.ts +61 -181
- package/src/config.ts +7 -7
- package/src/content-utils.ts +30 -0
- package/src/conversation.ts +13 -168
- package/src/git.ts +4 -18
- package/src/herdr.ts +6 -8
- package/src/jev/client.ts +348 -0
- package/src/jev/key-store.ts +252 -0
- package/src/jev/ledger.ts +234 -0
- package/src/jev/questions.ts +119 -0
- package/src/jev/state.ts +47 -0
- package/src/jev/transport.ts +58 -0
- package/src/model-stream.ts +5 -8
- package/src/outcomes.ts +3 -2
- package/src/pi-settings.ts +59 -0
- package/src/preferences.ts +2 -2
- package/src/redaction.ts +50 -0
- package/src/scout-context.ts +121 -485
- package/src/scout-curation.ts +84 -0
- package/src/scout-groups.ts +485 -0
- package/src/scout-protocol.ts +122 -0
- package/src/scout-reconstruct.ts +44 -0
- package/src/scout-types.ts +66 -0
- package/src/scout.ts +53 -63
- package/src/session-state.ts +215 -78
- package/src/tool-result-cap.ts +88 -0
- package/src/tools/consult-context.ts +155 -0
- package/src/tools/consultation.ts +71 -220
- package/src/tools/gate-policy.ts +5 -5
- package/src/tools/gate-protocol.ts +2 -2
- package/src/tools/jev-filter.ts +202 -0
- package/src/tools/jev-turn-gate.ts +183 -0
- package/src/tools/loop-gate.ts +71 -61
- package/src/tools/outage-notifier.ts +30 -0
- package/src/tools/prompts.ts +4 -7
- package/src/tools/register-ask-advisor.ts +46 -15
- package/src/tools/register-lifecycle.ts +7 -7
- package/src/tools/register-outcome.ts +5 -5
- package/src/tools/register-renderers.ts +63 -5
- package/src/tools/registration.ts +27 -10
- package/src/tools/render-advisor-result.ts +54 -22
- package/src/tools/render-common.ts +25 -13
- package/src/tools/scout-status.ts +5 -5
- package/src/tools/session.ts +1 -1
- package/src/tools/types.ts +17 -7
- package/src/tools.ts +9 -9
- package/src/ui/jev-setup-submenu.ts +344 -0
- package/src/ui/manual-dialog-render.ts +2 -2
- package/src/ui/manual-dialog.ts +4 -9
- package/src/ui/masked-input.ts +68 -0
- package/src/ui/model-selector.ts +1 -1
- package/src/ui/settings-formatting.ts +2 -2
- package/src/ui/settings-items.ts +166 -5
- package/src/ui/settings-mutations.ts +37 -2
- package/src/ui/settings-selector.ts +5 -5
- package/src/ui/text-setting-submenu.ts +1 -1
- package/src/ui/types.ts +13 -2
- package/src/ui.ts +4 -4
- package/src/usage.ts +4 -7
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ScoutManifest } from "./scout-types.ts";
|
|
2
|
+
|
|
3
|
+
const prefixWithinCharBudget = (value: string, maxChars: number) => {
|
|
4
|
+
let result = "";
|
|
5
|
+
for (const character of value) {
|
|
6
|
+
if (result.length + character.length > maxChars) {
|
|
7
|
+
break;
|
|
8
|
+
}
|
|
9
|
+
result += character;
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const reconstructScoutConversation = (
|
|
15
|
+
manifest: ScoutManifest,
|
|
16
|
+
selectedIds: string[],
|
|
17
|
+
synthesis?: string,
|
|
18
|
+
maxChars = Number.MAX_SAFE_INTEGER
|
|
19
|
+
): string => {
|
|
20
|
+
const selected = new Set(selectedIds);
|
|
21
|
+
const evidence = manifest.groups
|
|
22
|
+
.filter((group) => group.required || selected.has(group.id))
|
|
23
|
+
.sort((left, right) => left.originalIndex - right.originalIndex)
|
|
24
|
+
.map((group) => group.content);
|
|
25
|
+
const evidenceText = evidence.join("\n\n");
|
|
26
|
+
if (maxChars <= 0) {
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
if (evidenceText.length >= maxChars) {
|
|
30
|
+
return prefixWithinCharBudget(evidenceText, maxChars);
|
|
31
|
+
}
|
|
32
|
+
const inference = synthesis?.trim()
|
|
33
|
+
? `[Scout synthesis — untrusted, non-authoritative inference; not evidence]\n${synthesis.trim()}`
|
|
34
|
+
: undefined;
|
|
35
|
+
if (!inference) {
|
|
36
|
+
return evidenceText;
|
|
37
|
+
}
|
|
38
|
+
const separator = evidenceText ? "\n\n" : "";
|
|
39
|
+
const remaining = maxChars - evidenceText.length - separator.length;
|
|
40
|
+
if (remaining <= 0) {
|
|
41
|
+
return evidenceText;
|
|
42
|
+
}
|
|
43
|
+
return `${evidenceText}${separator}${prefixWithinCharBudget(inference, remaining)}`;
|
|
44
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** Shared Scout manifest types and transport limits. */
|
|
2
|
+
|
|
3
|
+
import type { AdvisorToolPolicies } from "./config/types.ts";
|
|
4
|
+
|
|
5
|
+
export const SCOUT_MANIFEST_MAX_BYTES = 64 * 1024;
|
|
6
|
+
export const SCOUT_MANIFEST_MAX_GROUPS = 64;
|
|
7
|
+
export const SCOUT_GROUP_MAX_BYTES = 24 * 1024;
|
|
8
|
+
export const SCOUT_LABEL_MAX_CHARS = 160;
|
|
9
|
+
export const SCOUT_SELECTION_MAX_IDS = 32;
|
|
10
|
+
export const SCOUT_SYNTHESIS_MAX_BYTES = 4 * 1024;
|
|
11
|
+
|
|
12
|
+
export type ScoutGroupKind =
|
|
13
|
+
| "compaction"
|
|
14
|
+
| "user"
|
|
15
|
+
| "assistant"
|
|
16
|
+
| "tool-exchange"
|
|
17
|
+
| "pending-invocation";
|
|
18
|
+
|
|
19
|
+
export interface ScoutContextGroup {
|
|
20
|
+
bytes: number;
|
|
21
|
+
content: string;
|
|
22
|
+
id: string;
|
|
23
|
+
kind: ScoutGroupKind;
|
|
24
|
+
label: string;
|
|
25
|
+
originalIndex: number;
|
|
26
|
+
required: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ScoutManifest {
|
|
30
|
+
availableBytes: number;
|
|
31
|
+
availableCount: number;
|
|
32
|
+
groups: ScoutContextGroup[];
|
|
33
|
+
omittedBytes: number;
|
|
34
|
+
omittedCount: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type ScoutManifestResult =
|
|
38
|
+
| { ok: true; manifest: ScoutManifest }
|
|
39
|
+
| {
|
|
40
|
+
ok: false;
|
|
41
|
+
reason: "invalid-protocol" | "required-group-overflow";
|
|
42
|
+
message: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** The invalid-protocol failure shape produced by protocol passes. */
|
|
46
|
+
export interface InvalidProtocolResult {
|
|
47
|
+
message: string;
|
|
48
|
+
ok: false;
|
|
49
|
+
reason: "invalid-protocol";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface BuildScoutManifestOptions {
|
|
53
|
+
currentInvocationId?: string;
|
|
54
|
+
/** @deprecated Use maxManifestBytes for the Scout transport budget. */
|
|
55
|
+
maxBytes?: number;
|
|
56
|
+
/** Maximum reconstructed Advisor conversation characters. */
|
|
57
|
+
maxConversationChars?: number;
|
|
58
|
+
maxGroupBytes?: number;
|
|
59
|
+
maxGroups?: number;
|
|
60
|
+
/** Maximum serialized group-manifest bytes sent to Scout. */
|
|
61
|
+
maxManifestBytes?: number;
|
|
62
|
+
policies?: AdvisorToolPolicies;
|
|
63
|
+
redact?: boolean;
|
|
64
|
+
toolResultMaxBytes?: number;
|
|
65
|
+
toolResultMaxLines?: number;
|
|
66
|
+
}
|
package/src/scout.ts
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import type { Message } from "@earendil-works/pi-ai/compat";
|
|
2
2
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { executorEffortRef, executorRef } from "./config/state.
|
|
3
|
+
import { executorEffortRef, executorRef } from "./config/state.ts";
|
|
4
4
|
import {
|
|
5
5
|
type CollectedTextStream,
|
|
6
6
|
collectTextStream,
|
|
7
7
|
type ResolvedConfiguredModel,
|
|
8
8
|
resolveConfiguredModel,
|
|
9
|
-
} from "./model-stream.
|
|
9
|
+
} from "./model-stream.ts";
|
|
10
|
+
import { groupWire } from "./scout-groups.ts";
|
|
11
|
+
import { reconstructScoutConversation } from "./scout-reconstruct.ts";
|
|
10
12
|
import {
|
|
11
|
-
reconstructScoutConversation,
|
|
12
13
|
SCOUT_SELECTION_MAX_IDS,
|
|
13
14
|
SCOUT_SYNTHESIS_MAX_BYTES,
|
|
14
15
|
type ScoutManifest,
|
|
15
|
-
} from "./scout-
|
|
16
|
-
import { snapshotAdvisorUsage } from "./usage.
|
|
16
|
+
} from "./scout-types.ts";
|
|
17
|
+
import { snapshotAdvisorUsage } from "./usage.ts";
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
const SCOUT_TIMEOUT_MS = 30_000;
|
|
19
20
|
|
|
20
21
|
export const SCOUT_SYSTEM = [
|
|
21
22
|
"You are Scout, a context curator serving a separate engineering Advisor.",
|
|
@@ -30,7 +31,7 @@ export const SCOUT_SYSTEM = [
|
|
|
30
31
|
"Do not use Markdown fences or add any other keys or prose.",
|
|
31
32
|
].join(" ");
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
type ScoutFallbackCategory =
|
|
34
35
|
| "required-group-overflow"
|
|
35
36
|
| "invalid-protocol"
|
|
36
37
|
| "missing-model"
|
|
@@ -41,7 +42,7 @@ export type ScoutFallbackCategory =
|
|
|
41
42
|
| "invalid-selection"
|
|
42
43
|
| "timeout";
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
interface ScoutMetrics {
|
|
45
46
|
availableCount: number;
|
|
46
47
|
inputBytes: number;
|
|
47
48
|
latencyMs: number;
|
|
@@ -106,14 +107,7 @@ const manifestMessage = (manifest: ScoutManifest): Message => ({
|
|
|
106
107
|
content: [
|
|
107
108
|
{
|
|
108
109
|
text: JSON.stringify({
|
|
109
|
-
groups: manifest.groups.map(
|
|
110
|
-
bytes: group.bytes,
|
|
111
|
-
content: group.content,
|
|
112
|
-
id: group.id,
|
|
113
|
-
kind: group.kind,
|
|
114
|
-
label: group.label,
|
|
115
|
-
required: group.required,
|
|
116
|
-
})),
|
|
110
|
+
groups: manifest.groups.map(groupWire),
|
|
117
111
|
omittedBeforeScout: {
|
|
118
112
|
bytes: manifest.omittedBytes,
|
|
119
113
|
groups: manifest.omittedCount,
|
|
@@ -225,15 +219,36 @@ export const runAdvisorScout = async (
|
|
|
225
219
|
onEvent?: (event: ScoutLifecycleEvent) => void,
|
|
226
220
|
timeoutMs = SCOUT_TIMEOUT_MS,
|
|
227
221
|
dependencies: ScoutDependencies = defaultDependencies
|
|
228
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: cancellation, timeout, provider, and schema outcomes remain explicitly distinct.
|
|
229
222
|
): Promise<ScoutOutcome> => {
|
|
230
223
|
const startedAt = Date.now();
|
|
231
224
|
const publish = (event: ScoutLifecycleEvent) => {
|
|
232
225
|
onEvent?.(event);
|
|
233
226
|
};
|
|
234
|
-
|
|
227
|
+
const cancelled = (): ScoutOutcome => {
|
|
235
228
|
publish({ type: "cancelled" });
|
|
236
229
|
return { cancelled: true, ok: false };
|
|
230
|
+
};
|
|
231
|
+
const fallback = (
|
|
232
|
+
category: ScoutFallbackCategory,
|
|
233
|
+
message: string,
|
|
234
|
+
usage?: unknown
|
|
235
|
+
): ScoutOutcome => {
|
|
236
|
+
const outcome = {
|
|
237
|
+
category,
|
|
238
|
+
message,
|
|
239
|
+
metrics:
|
|
240
|
+
usage === undefined
|
|
241
|
+
? baseMetrics(manifest, startedAt)
|
|
242
|
+
: { ...baseMetrics(manifest, startedAt), usage },
|
|
243
|
+
model: executorRef,
|
|
244
|
+
ok: false as const,
|
|
245
|
+
};
|
|
246
|
+
publish({ outcome, type: "fallback" });
|
|
247
|
+
return outcome;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
if (parentSignal?.aborted) {
|
|
251
|
+
return cancelled();
|
|
237
252
|
}
|
|
238
253
|
|
|
239
254
|
let resolved: ResolvedConfiguredModel;
|
|
@@ -241,24 +256,14 @@ export const runAdvisorScout = async (
|
|
|
241
256
|
resolved = await dependencies.resolve(ctx, executorRef, "Scout");
|
|
242
257
|
} catch (error) {
|
|
243
258
|
if (parentSignal?.aborted) {
|
|
244
|
-
|
|
245
|
-
return { cancelled: true, ok: false };
|
|
259
|
+
return cancelled();
|
|
246
260
|
}
|
|
247
261
|
const message = error instanceof Error ? error.message : String(error);
|
|
248
|
-
|
|
249
|
-
category: classifyResolutionError(message),
|
|
250
|
-
message,
|
|
251
|
-
metrics: baseMetrics(manifest, startedAt),
|
|
252
|
-
model: executorRef,
|
|
253
|
-
ok: false as const,
|
|
254
|
-
};
|
|
255
|
-
publish({ outcome, type: "fallback" });
|
|
256
|
-
return outcome;
|
|
262
|
+
return fallback(classifyResolutionError(message), message);
|
|
257
263
|
}
|
|
258
264
|
|
|
259
265
|
if (parentSignal?.aborted) {
|
|
260
|
-
|
|
261
|
-
return { cancelled: true, ok: false };
|
|
266
|
+
return cancelled();
|
|
262
267
|
}
|
|
263
268
|
publish({ model: executorRef, type: "call" });
|
|
264
269
|
const controller = new AbortController();
|
|
@@ -280,6 +285,12 @@ export const runAdvisorScout = async (
|
|
|
280
285
|
once: true,
|
|
281
286
|
});
|
|
282
287
|
});
|
|
288
|
+
const teardown = () => {
|
|
289
|
+
clearTimeout(timer);
|
|
290
|
+
parentSignal?.removeEventListener("abort", abortFromParent);
|
|
291
|
+
controller.signal.removeEventListener("abort", onControllerAbort);
|
|
292
|
+
};
|
|
293
|
+
|
|
283
294
|
let streamed: CollectedTextStream;
|
|
284
295
|
try {
|
|
285
296
|
const collection = dependencies.collect(resolved, {
|
|
@@ -295,30 +306,18 @@ export const runAdvisorScout = async (
|
|
|
295
306
|
});
|
|
296
307
|
streamed = await Promise.race([collection, abortPromise]);
|
|
297
308
|
} catch (error) {
|
|
298
|
-
|
|
299
|
-
parentSignal?.removeEventListener("abort", abortFromParent);
|
|
300
|
-
controller.signal.removeEventListener("abort", onControllerAbort);
|
|
309
|
+
teardown();
|
|
301
310
|
if (parentSignal?.aborted) {
|
|
302
|
-
|
|
303
|
-
return { cancelled: true, ok: false };
|
|
311
|
+
return cancelled();
|
|
304
312
|
}
|
|
305
313
|
const message = error instanceof Error ? error.message : String(error);
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
metrics: baseMetrics(manifest, startedAt),
|
|
310
|
-
model: executorRef,
|
|
311
|
-
ok: false as const,
|
|
312
|
-
};
|
|
313
|
-
publish({ outcome, type: "fallback" });
|
|
314
|
-
return outcome;
|
|
314
|
+
return timedOut
|
|
315
|
+
? fallback("timeout", `Scout timed out after ${timeoutMs} ms.`)
|
|
316
|
+
: fallback("provider-error", message);
|
|
315
317
|
}
|
|
316
|
-
|
|
317
|
-
parentSignal?.removeEventListener("abort", abortFromParent);
|
|
318
|
-
controller.signal.removeEventListener("abort", onControllerAbort);
|
|
318
|
+
teardown();
|
|
319
319
|
if (parentSignal?.aborted) {
|
|
320
|
-
|
|
321
|
-
return { cancelled: true, ok: false };
|
|
320
|
+
return cancelled();
|
|
322
321
|
}
|
|
323
322
|
|
|
324
323
|
let selection: ScoutSelection;
|
|
@@ -326,20 +325,11 @@ export const runAdvisorScout = async (
|
|
|
326
325
|
selection = parseScoutSelection(streamed.text, manifest);
|
|
327
326
|
} catch (error) {
|
|
328
327
|
const message = error instanceof Error ? error.message : String(error);
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
? ("invalid-selection" as const)
|
|
332
|
-
: ("empty-response" as const),
|
|
328
|
+
return fallback(
|
|
329
|
+
streamed.text.trim() ? "invalid-selection" : "empty-response",
|
|
333
330
|
message,
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
usage: snapshotAdvisorUsage(streamed.usage),
|
|
337
|
-
},
|
|
338
|
-
model: executorRef,
|
|
339
|
-
ok: false as const,
|
|
340
|
-
};
|
|
341
|
-
publish({ outcome, type: "fallback" });
|
|
342
|
-
return outcome;
|
|
331
|
+
snapshotAdvisorUsage(streamed.usage)
|
|
332
|
+
);
|
|
343
333
|
}
|
|
344
334
|
|
|
345
335
|
const outcome = {
|