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.
Files changed (87) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +1 -0
  3. package/dist/index.js +3160 -1255
  4. package/extensions/index.ts +9 -9
  5. package/package.json +12 -8
  6. package/src/{untracked.ts → attachments.ts} +2 -5
  7. package/src/commands/activation-preparation.ts +6 -6
  8. package/src/commands/activation.ts +7 -7
  9. package/src/commands/lifecycle.ts +5 -5
  10. package/src/commands/manual-command.ts +13 -9
  11. package/src/commands/manual-consultation.ts +24 -10
  12. package/src/commands/manual-progress.ts +3 -3
  13. package/src/commands/model-commands.ts +6 -6
  14. package/src/commands/model-options.ts +4 -4
  15. package/src/commands/model-picker.ts +2 -2
  16. package/src/commands/registration.ts +8 -8
  17. package/src/commands/renderers.ts +5 -5
  18. package/src/commands/runtime.ts +8 -8
  19. package/src/commands/settings-commands.ts +8 -8
  20. package/src/commands/settings-persistence.ts +52 -4
  21. package/src/commands/types.ts +6 -13
  22. package/src/commands.ts +2 -2
  23. package/src/config/args.ts +3 -3
  24. package/src/config/defaults.ts +78 -3
  25. package/src/config/schema.ts +434 -0
  26. package/src/config/state.ts +71 -2
  27. package/src/config/storage.ts +27 -119
  28. package/src/config/types.ts +30 -1
  29. package/src/config/validation.ts +61 -181
  30. package/src/config.ts +7 -7
  31. package/src/content-utils.ts +30 -0
  32. package/src/conversation.ts +13 -168
  33. package/src/git.ts +4 -18
  34. package/src/herdr.ts +6 -8
  35. package/src/jev/client.ts +348 -0
  36. package/src/jev/key-store.ts +252 -0
  37. package/src/jev/ledger.ts +234 -0
  38. package/src/jev/questions.ts +119 -0
  39. package/src/jev/state.ts +47 -0
  40. package/src/jev/transport.ts +58 -0
  41. package/src/model-stream.ts +5 -8
  42. package/src/outcomes.ts +3 -2
  43. package/src/pi-settings.ts +59 -0
  44. package/src/preferences.ts +2 -2
  45. package/src/redaction.ts +50 -0
  46. package/src/scout-context.ts +121 -485
  47. package/src/scout-curation.ts +84 -0
  48. package/src/scout-groups.ts +485 -0
  49. package/src/scout-protocol.ts +122 -0
  50. package/src/scout-reconstruct.ts +44 -0
  51. package/src/scout-types.ts +66 -0
  52. package/src/scout.ts +53 -63
  53. package/src/session-state.ts +215 -78
  54. package/src/tool-result-cap.ts +88 -0
  55. package/src/tools/consult-context.ts +155 -0
  56. package/src/tools/consultation.ts +71 -220
  57. package/src/tools/gate-policy.ts +5 -5
  58. package/src/tools/gate-protocol.ts +2 -2
  59. package/src/tools/jev-filter.ts +202 -0
  60. package/src/tools/jev-turn-gate.ts +183 -0
  61. package/src/tools/loop-gate.ts +71 -61
  62. package/src/tools/outage-notifier.ts +30 -0
  63. package/src/tools/prompts.ts +4 -7
  64. package/src/tools/register-ask-advisor.ts +46 -15
  65. package/src/tools/register-lifecycle.ts +7 -7
  66. package/src/tools/register-outcome.ts +5 -5
  67. package/src/tools/register-renderers.ts +63 -5
  68. package/src/tools/registration.ts +27 -10
  69. package/src/tools/render-advisor-result.ts +54 -22
  70. package/src/tools/render-common.ts +25 -13
  71. package/src/tools/scout-status.ts +5 -5
  72. package/src/tools/session.ts +1 -1
  73. package/src/tools/types.ts +17 -7
  74. package/src/tools.ts +9 -9
  75. package/src/ui/jev-setup-submenu.ts +344 -0
  76. package/src/ui/manual-dialog-render.ts +2 -2
  77. package/src/ui/manual-dialog.ts +4 -9
  78. package/src/ui/masked-input.ts +68 -0
  79. package/src/ui/model-selector.ts +1 -1
  80. package/src/ui/settings-formatting.ts +2 -2
  81. package/src/ui/settings-items.ts +166 -5
  82. package/src/ui/settings-mutations.ts +37 -2
  83. package/src/ui/settings-selector.ts +5 -5
  84. package/src/ui/text-setting-submenu.ts +1 -1
  85. package/src/ui/types.ts +13 -2
  86. package/src/ui.ts +4 -4
  87. 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.js";
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.js";
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-context.js";
16
- import { snapshotAdvisorUsage } from "./usage.js";
16
+ } from "./scout-types.ts";
17
+ import { snapshotAdvisorUsage } from "./usage.ts";
17
18
 
18
- export const SCOUT_TIMEOUT_MS = 30_000;
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
- export type ScoutFallbackCategory =
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
- export interface ScoutMetrics {
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((group) => ({
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
- if (parentSignal?.aborted) {
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
- publish({ type: "cancelled" });
245
- return { cancelled: true, ok: false };
259
+ return cancelled();
246
260
  }
247
261
  const message = error instanceof Error ? error.message : String(error);
248
- const outcome = {
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
- publish({ type: "cancelled" });
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
- clearTimeout(timer);
299
- parentSignal?.removeEventListener("abort", abortFromParent);
300
- controller.signal.removeEventListener("abort", onControllerAbort);
309
+ teardown();
301
310
  if (parentSignal?.aborted) {
302
- publish({ type: "cancelled" });
303
- return { cancelled: true, ok: false };
311
+ return cancelled();
304
312
  }
305
313
  const message = error instanceof Error ? error.message : String(error);
306
- const outcome = {
307
- category: timedOut ? ("timeout" as const) : ("provider-error" as const),
308
- message: timedOut ? `Scout timed out after ${timeoutMs} ms.` : message,
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
- clearTimeout(timer);
317
- parentSignal?.removeEventListener("abort", abortFromParent);
318
- controller.signal.removeEventListener("abort", onControllerAbort);
318
+ teardown();
319
319
  if (parentSignal?.aborted) {
320
- publish({ type: "cancelled" });
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
- const outcome = {
330
- category: streamed.text.trim()
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
- metrics: {
335
- ...baseMetrics(manifest, startedAt),
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 = {