@ferris1225/pi-subagents 1.0.0 → 2.0.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.
@@ -5,6 +5,8 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
5
5
  import { loadConfig, saveConfig } from "./config.ts";
6
6
  import { announceRecoveryRecords } from "./recovery.ts";
7
7
  import type { SubagentRuntime } from "./runtime.ts";
8
+ import { pruneResultArtifacts } from "./spawn.ts";
9
+ import { installActiveRunsWidget } from "./widget.ts";
8
10
 
9
11
  const ANNOUNCEMENTS: Array<{
10
12
  key: string;
@@ -17,6 +19,12 @@ const ANNOUNCEMENTS: Array<{
17
19
  message:
18
20
  "pi-subagents: new — a vision-capable model can now handle image tasks (screenshots, mockups, designs). Run /subagents-setup to configure it; until set, vision tasks use the main session's current model.",
19
21
  },
22
+ {
23
+ key: "cleanerAgent",
24
+ condition: (config) => !config.enabledAgents.includes("cleaner"),
25
+ message:
26
+ "pi-subagents: new built-in cleaner agent is available for evidence-first code cleanup. Run /subagents-setup to enable it; your existing enabledAgents selection was left unchanged.",
27
+ },
20
28
  ];
21
29
 
22
30
  async function announceNewFeatures(
@@ -53,7 +61,10 @@ async function announceNewFeatures(
53
61
 
54
62
  export function registerAnnouncements(pi: ExtensionAPI, runtime: SubagentRuntime): void {
55
63
  pi.on("session_start", async (_event, ctx) => {
64
+ pruneResultArtifacts();
56
65
  await announceRecoveryRecords(runtime.configPath, ctx);
57
- if (ctx.mode === "tui") await announceNewFeatures(ctx, runtime);
66
+ if (ctx.mode !== "tui") return;
67
+ installActiveRunsWidget(ctx);
68
+ await announceNewFeatures(ctx, runtime);
58
69
  });
59
70
  }
package/src/completion.ts CHANGED
@@ -2,8 +2,7 @@
2
2
  * Smart batching for successful background completions.
3
3
  *
4
4
  * A short debounce coalesces sibling runs while a max-wait timer, measured from
5
- * the first item in the open group, bounds delivery latency. Runs that finish
6
- * shortly after an emitted group use a smaller straggler window. Failures are
5
+ * the first item in the open group, bounds delivery latency. Failures are
7
6
  * intentionally handled by the caller: flush held successes, then emit the
8
7
  * failure directly so it is never delayed.
9
8
  */
@@ -13,30 +12,14 @@ import { getResultOutput, isFailedResult, reviewVerdict, type SingleResult } fro
13
12
  export interface CompletionBatchTimings {
14
13
  debounceMs: number;
15
14
  maxWaitMs: number;
16
- stragglerDebounceMs: number;
17
- stragglerMaxWaitMs: number;
18
- stragglerWindowMs: number;
19
15
  }
20
16
 
21
17
  export const DEFAULT_COMPLETION_BATCH_TIMINGS: CompletionBatchTimings = {
22
18
  debounceMs: 150,
23
19
  maxWaitMs: 1_000,
24
- stragglerDebounceMs: 75,
25
- stragglerMaxWaitMs: 400,
26
- stragglerWindowMs: 2_000,
27
20
  };
28
21
 
29
- type TimerHandle = unknown;
30
-
31
- export interface TimerApi {
32
- setTimeout(handler: () => void, delayMs: number): TimerHandle;
33
- clearTimeout(handle: TimerHandle): void;
34
- }
35
-
36
- const defaultTimers: TimerApi = {
37
- setTimeout: (handler, delayMs) => setTimeout(handler, delayMs),
38
- clearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),
39
- };
22
+ type TimerHandle = ReturnType<typeof setTimeout>;
40
23
 
41
24
  function unrefHandle(handle: TimerHandle): void {
42
25
  if (
@@ -52,8 +35,6 @@ function unrefHandle(handle: TimerHandle): void {
52
35
  export interface CompletionBatcherOptions<T> {
53
36
  emit: (items: T[]) => void;
54
37
  timings?: Partial<CompletionBatchTimings>;
55
- timers?: TimerApi;
56
- now?: () => number;
57
38
  }
58
39
 
59
40
  export interface CompletionBatcher<T> {
@@ -66,22 +47,18 @@ export interface CompletionBatcher<T> {
66
47
  }
67
48
 
68
49
  export function createCompletionBatcher<T>(options: CompletionBatcherOptions<T>): CompletionBatcher<T> {
69
- const timers = options.timers ?? defaultTimers;
70
- const now = options.now ?? Date.now;
71
50
  const timings = { ...DEFAULT_COMPLETION_BATCH_TIMINGS, ...options.timings };
72
51
  let pending: T[] = [];
73
52
  let debounceTimer: TimerHandle | null = null;
74
53
  let maxWaitTimer: TimerHandle | null = null;
75
- let straggler = false;
76
- let lastEmitAt: number | null = null;
77
54
 
78
55
  const clearTimers = (): void => {
79
56
  if (debounceTimer !== null) {
80
- timers.clearTimeout(debounceTimer);
57
+ clearTimeout(debounceTimer);
81
58
  debounceTimer = null;
82
59
  }
83
60
  if (maxWaitTimer !== null) {
84
- timers.clearTimeout(maxWaitTimer);
61
+ clearTimeout(maxWaitTimer);
85
62
  maxWaitTimer = null;
86
63
  }
87
64
  };
@@ -91,25 +68,19 @@ export function createCompletionBatcher<T>(options: CompletionBatcherOptions<T>)
91
68
  if (pending.length === 0) return;
92
69
  const items = pending;
93
70
  pending = [];
94
- lastEmitAt = now();
95
71
  options.emit(items);
96
72
  };
97
73
 
98
74
  return {
99
75
  push(item: T): void {
100
- if (pending.length === 0) {
101
- straggler = lastEmitAt !== null && now() - lastEmitAt < timings.stragglerWindowMs;
102
- }
103
76
  pending.push(item);
104
77
 
105
- if (debounceTimer !== null) timers.clearTimeout(debounceTimer);
106
- const debounceDelay = straggler ? timings.stragglerDebounceMs : timings.debounceMs;
107
- debounceTimer = timers.setTimeout(emitGroup, debounceDelay);
78
+ if (debounceTimer !== null) clearTimeout(debounceTimer);
79
+ debounceTimer = setTimeout(emitGroup, timings.debounceMs);
108
80
  unrefHandle(debounceTimer);
109
81
 
110
82
  if (maxWaitTimer === null) {
111
- const maxWaitDelay = straggler ? timings.stragglerMaxWaitMs : timings.maxWaitMs;
112
- maxWaitTimer = timers.setTimeout(emitGroup, maxWaitDelay);
83
+ maxWaitTimer = setTimeout(emitGroup, timings.maxWaitMs);
113
84
  unrefHandle(maxWaitTimer);
114
85
  }
115
86
  },