@fitchmultz/pi-subagents 0.46.1 → 0.46.2

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 (71) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/agents/agent-management.js +8 -2
  3. package/dist/agents/agents.js +8 -8
  4. package/dist/extension/build-info.js +1 -1
  5. package/dist/extension/index.js +11 -5
  6. package/dist/extension/result-snapshot.js +0 -18
  7. package/dist/history/ingest.js +40 -31
  8. package/dist/history/prune.js +1 -1
  9. package/dist/history/run-projection.js +1 -2
  10. package/dist/history/search-queries.js +8 -2
  11. package/dist/history/store.js +20 -4
  12. package/dist/history/text-chunks.js +1 -2
  13. package/dist/history/transcript-queries.js +12 -6
  14. package/dist/intercom/result-intercom.js +0 -74
  15. package/dist/pi-intercom/broker/broker.js +33 -16
  16. package/dist/pi-intercom/broker/framing.js +67 -17
  17. package/dist/pi-intercom/broker/snapshot-reader.js +29 -8
  18. package/dist/pi-intercom/broker/snapshots.js +144 -41
  19. package/dist/pi-intercom/session-targets.js +0 -1
  20. package/dist/runs/background/async-run-discovery.js +4 -6
  21. package/dist/runs/background/completion-dedupe.js +4 -55
  22. package/dist/runs/background/notify.js +1 -9
  23. package/dist/runs/background/result-watcher.js +1 -1
  24. package/dist/runs/background/runner-child-observer.js +12 -7
  25. package/dist/runs/background/runner-monitor.js +2 -0
  26. package/dist/runs/background/runner-parallel.js +2 -3
  27. package/dist/runs/shared/child-observation-spool.js +8 -3
  28. package/dist/runs/shared/run-restoration.js +4 -5
  29. package/dist/runs/shared/supervisor-questions.js +7 -4
  30. package/dist/shared/journal-frames.js +1 -1
  31. package/dist/slash/command-discovery.js +13 -12
  32. package/dist/tui/agent-history.js +0 -14
  33. package/dist/tui/agent-view.js +0 -1
  34. package/dist/tui/conversation-history.js +10 -1
  35. package/dist/tui/history-cards.js +8 -22
  36. package/docs/intercom.md +5 -1
  37. package/docs/performance.md +72 -3
  38. package/package.json +1 -1
  39. package/src/agents/agent-management.ts +8 -4
  40. package/src/agents/agents.ts +17 -12
  41. package/src/extension/index.ts +12 -5
  42. package/src/extension/result-snapshot.ts +1 -27
  43. package/src/history/ingest.ts +57 -49
  44. package/src/history/prune.ts +1 -1
  45. package/src/history/run-projection.ts +2 -4
  46. package/src/history/search-queries.ts +9 -2
  47. package/src/history/store.ts +21 -5
  48. package/src/history/text-chunks.ts +7 -2
  49. package/src/history/transcript-queries.ts +16 -10
  50. package/src/intercom/result-intercom.ts +0 -103
  51. package/src/pi-intercom/broker/broker.ts +34 -20
  52. package/src/pi-intercom/broker/framing.ts +71 -20
  53. package/src/pi-intercom/broker/snapshot-reader.ts +47 -9
  54. package/src/pi-intercom/broker/snapshots.ts +184 -51
  55. package/src/pi-intercom/session-targets.ts +0 -2
  56. package/src/runs/background/async-run-discovery.ts +4 -10
  57. package/src/runs/background/completion-dedupe.ts +6 -69
  58. package/src/runs/background/notify.ts +2 -12
  59. package/src/runs/background/result-watcher.ts +1 -1
  60. package/src/runs/background/runner-child-observer.ts +19 -11
  61. package/src/runs/background/runner-monitor.ts +2 -0
  62. package/src/runs/background/runner-parallel.ts +2 -3
  63. package/src/runs/shared/child-observation-spool.ts +8 -3
  64. package/src/runs/shared/run-restoration.ts +4 -4
  65. package/src/runs/shared/supervisor-questions.ts +10 -10
  66. package/src/shared/journal-frames.ts +1 -1
  67. package/src/slash/command-discovery.ts +13 -12
  68. package/src/tui/agent-history.ts +1 -18
  69. package/src/tui/agent-view.ts +0 -1
  70. package/src/tui/conversation-history.ts +15 -1
  71. package/src/tui/history-cards.ts +8 -26
@@ -22,6 +22,7 @@ import {
22
22
  import {
23
23
  getRunMetadataDir,
24
24
  migrateSupervisorQuestionSteps,
25
+ readQuestionOwner,
25
26
  readRunJson,
26
27
  saveQuestionOwner,
27
28
  saveRunStatus,
@@ -254,14 +255,13 @@ function nativeLaunchCwd(run: OwnedRun, details: ReadonlyDetails): OwnedRun {
254
255
  }
255
256
 
256
257
  function repairQuestionOwner(run: OwnedRun): void {
257
- let owner: unknown;
258
+ let sessionId: string | undefined;
258
259
  try {
259
- owner = readRunJson(path.join(getRunMetadataDir(run.runId), "question-owner.json"));
260
+ sessionId = readQuestionOwner(run.runId)?.sessionId;
260
261
  } catch {
261
262
  // A genuine receipt can repair unusable supplemental metadata.
262
263
  }
263
- const sessionId = isRecord(owner) ? owner.sessionId : undefined;
264
- if (typeof sessionId !== "string" || sessionId.trim() === "") {
264
+ if (sessionId === undefined || sessionId.trim() === "") {
265
265
  saveQuestionOwner(run.runId, run.ownerSessionId);
266
266
  }
267
267
  }
@@ -216,7 +216,10 @@ export function createSupervisorQuestion(
216
216
  return question;
217
217
  }
218
218
 
219
- export function listRunQuestions(runDir: string): SupervisorQuestionView[] {
219
+ export function listRunQuestions(
220
+ runDir: string,
221
+ ownerSessionId?: string,
222
+ ): SupervisorQuestionView[] {
220
223
  let entries: fs.Dirent[];
221
224
  try {
222
225
  entries = fs.readdirSync(path.join(runDir, "questions"), { withFileTypes: true });
@@ -233,7 +236,10 @@ export function listRunQuestions(runDir: string): SupervisorQuestionView[] {
233
236
  path.join(runDir, "questions", entry.name, "question.json"),
234
237
  parseSupervisorQuestion,
235
238
  );
236
- return question ? [readQuestionState(question, path.dirname(runDir))] : [];
239
+ return question &&
240
+ (ownerSessionId === undefined || question.ownerSessionId === ownerSessionId)
241
+ ? [readQuestionState(question, path.dirname(runDir))]
242
+ : [];
237
243
  });
238
244
  }
239
245
 
@@ -243,9 +249,7 @@ export function listOwnedRunQuestions(
243
249
  runId: string,
244
250
  ): SupervisorQuestionView[] {
245
251
  migrateSupervisorQuestions(ownerSessionId, runId);
246
- return listRunQuestions(getRunMetadataDir(runId)).filter(
247
- (question) => question.ownerSessionId === ownerSessionId,
248
- );
252
+ return listRunQuestions(getRunMetadataDir(runId), ownerSessionId);
249
253
  }
250
254
 
251
255
  export function pendingSupervisorQuestion(input: {
@@ -321,11 +325,7 @@ function* supervisorQuestionSteps(
321
325
  for (const entry of runs) {
322
326
  yield;
323
327
  try {
324
- questions.push(
325
- ...listRunQuestions(path.join(root, entry.name)).filter(
326
- (question) => question.ownerSessionId === ownerSessionId,
327
- ),
328
- );
328
+ questions.push(...listRunQuestions(path.join(root, entry.name), ownerSessionId));
329
329
  } catch (error) {
330
330
  if (runId !== undefined) {
331
331
  throw error;
@@ -172,7 +172,7 @@ export function readJsonProjection(file: string, select: Projection): UnknownRec
172
172
  const projection = new JsonProjection(select, undefined, () => Infinity);
173
173
  try {
174
174
  const end = fs.fstatSync(fd).size;
175
- const bytes = Buffer.allocUnsafe(64 * 1024);
175
+ const bytes = Buffer.allocUnsafe(Math.min(64 * 1024, end));
176
176
  for (let offset = 0; offset < end;) {
177
177
  const count = fs.readSync(fd, bytes, 0, Math.min(bytes.length, end - offset), offset);
178
178
  if (count === 0) {
@@ -1,5 +1,5 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import { discoverAgents, discoverAgentsAll } from "../agents/agents.ts";
2
+ import { discoverAgents, discoverChains } from "../agents/agents.ts";
3
3
  import type { ChainConfig } from "../shared/types/config.ts";
4
4
  import { resolveExecutionCwd } from "../shared/execution-cwd.ts";
5
5
 
@@ -51,21 +51,19 @@ export function makeAgentCompletions(
51
51
  multiAgent: boolean,
52
52
  ): (prefix: string) => Completion[] | null {
53
53
  return (prefix) => {
54
+ const position = multiAgent ? completionPosition(prefix) : { word: prefix, before: "" };
55
+ if (!position || (!multiAgent && prefix.includes(" "))) {
56
+ return null;
57
+ }
54
58
  const cwd = completionCwd(pi, state);
55
59
  if (cwd === null || cwd.length === 0) {
56
60
  return null;
57
61
  }
58
62
  const agents = discoverAgents(cwd, "both", { projectTrusted: projectTrusted(state) }).agents;
59
63
  if (!multiAgent) {
60
- return prefix.includes(" ")
61
- ? null
62
- : agents
63
- .filter((agent) => agent.name.startsWith(prefix))
64
- .map((agent) => ({ value: agent.name, label: agent.name }));
65
- }
66
- const position = completionPosition(prefix);
67
- if (!position) {
68
- return null;
64
+ return agents
65
+ .filter((agent) => agent.name.startsWith(prefix))
66
+ .map((agent) => ({ value: agent.name, label: agent.name }));
69
67
  }
70
68
  const { word: lastWord, before: beforeLastWord } = position;
71
69
  if (lastWord === "->") {
@@ -79,7 +77,7 @@ export function makeAgentCompletions(
79
77
 
80
78
  export function discoverSavedChains(cwd: string, trusted: boolean): ChainConfig[] {
81
79
  const chains = new Map<string, ChainConfig>();
82
- for (const chain of discoverAgentsAll(cwd, { projectTrusted: trusted }).chains) {
80
+ for (const chain of discoverChains(cwd, { projectTrusted: trusted }).chains) {
83
81
  const existing = chains.get(chain.name);
84
82
  const projectOverride = existing?.source === "user" && chain.source === "project";
85
83
  const jsonOverride =
@@ -98,8 +96,11 @@ export function makeChainCompletions(
98
96
  state: SlashDiscoveryState,
99
97
  ): (prefix: string) => Completion[] | null {
100
98
  return (prefix) => {
99
+ if (prefix.includes(" ")) {
100
+ return null;
101
+ }
101
102
  const cwd = completionCwd(pi, state);
102
- if (prefix.includes(" ") || cwd === null || cwd.length === 0) {
103
+ if (cwd === null || cwd.length === 0) {
103
104
  return null;
104
105
  }
105
106
  return discoverSavedChains(cwd, projectTrusted(state))
@@ -1,27 +1,10 @@
1
- import type { SessionEntry } from "@earendil-works/pi-coding-agent";
2
1
  import { stripAcceptanceReport } from "../runs/shared/acceptance-reports.ts";
3
- import { displayHistory, type AgentHistory, type AgentHistoryItem } from "./history-display.ts";
4
- import type { DisplayEntry } from "./history-record.ts";
2
+ import type { AgentHistory, AgentHistoryItem } from "./history-display.ts";
5
3
  import { readableText } from "./history-text.ts";
6
4
  export { readableText } from "./history-text.ts";
7
5
  export { indexedHistory } from "./indexed-history.ts";
8
6
  export type { AgentHistory, AgentHistoryItem } from "./history-display.ts";
9
7
 
10
- /** Read native entries, not a compacted model context: earlier messages remain inspectable. */
11
- export function historyItems(entries: readonly SessionEntry[]): AgentHistory {
12
- const visible: DisplayEntry[] = [];
13
- for (const entry of entries) {
14
- if (
15
- entry.type === "message" ||
16
- entry.type === "custom_message" ||
17
- entry.type === "compaction" ||
18
- entry.type === "branch_summary"
19
- ) {
20
- visible.push(entry.type === "message" ? { ...entry, native: entry.message } : entry);
21
- }
22
- }
23
- return displayHistory(visible);
24
- }
25
8
  function finalMatch(history: AgentHistory, text: string): string | undefined {
26
9
  if (history.finalId !== undefined) {
27
10
  return history.finalId;
@@ -400,5 +400,4 @@ export class AgentViewController {
400
400
  this.lastSaved = "";
401
401
  }
402
402
  }
403
- export { AgentConversation } from "./agent-conversation.ts";
404
403
  export { agentTaskLabel, type AgentTask, type AgentVisit } from "./view-model.ts";
@@ -1,4 +1,5 @@
1
1
  import { errorText } from "./text-values.ts";
2
+ import { isDeepStrictEqual } from "node:util";
2
3
  import {
3
4
  historyStatus,
4
5
  pageNotices,
@@ -183,9 +184,22 @@ export class ConversationHistory {
183
184
  ? { ...item, load: () => this.controller.savedResult(this.key, item.id) }
184
185
  : item,
185
186
  );
187
+ const previous = task.page,
188
+ sameSource =
189
+ previous !== undefined &&
190
+ previous.sourceId === result.page.sourceId &&
191
+ previous.generation === result.page.generation &&
192
+ previous.sessionId === result.page.sessionId &&
193
+ previous.sourceState === result.page.sourceState &&
194
+ isDeepStrictEqual(
195
+ previous.entries.map((entry) => entry.ref),
196
+ result.page.entries.map((entry) => entry.ref),
197
+ );
186
198
  this.seenSource ||= result.page.count > 0;
187
199
  this.controller.applyHistoryPage(this.key, { ...history, items }, result.page, this.seenSource);
188
- this.source.clearCards();
200
+ if (!sameSource) {
201
+ this.source.clearCards();
202
+ }
189
203
  }
190
204
 
191
205
  assignment(): AgentHistoryItem {
@@ -1,4 +1,5 @@
1
1
  import type { AssistantMessage } from "@earendil-works/pi-ai";
2
+ import { isDeepStrictEqual } from "node:util";
2
3
  import {
3
4
  AssistantMessageComponent,
4
5
  UserMessageComponent,
@@ -47,29 +48,9 @@ interface CardLine {
47
48
  readonly contentStart: number;
48
49
  readonly end: number;
49
50
  }
50
- function plainItem(item: AgentHistoryItem): boolean {
51
- return (
52
- item.load === undefined &&
53
- item.assistant === undefined &&
54
- item.previewAssistant === undefined &&
55
- item.call === undefined &&
56
- item.result === undefined
57
- );
58
- }
59
- function plainSignature(item: AgentHistoryItem): string {
60
- return JSON.stringify([
61
- item.kind,
62
- item.title,
63
- item.text,
64
- item.details,
65
- item.diff,
66
- item.model,
67
- item.timestamp,
68
- item.messageId,
69
- ]);
70
- }
71
51
  function sameDisplay(left: AgentHistoryItem, right: AgentHistoryItem): boolean {
72
- return plainItem(left) && plainItem(right) && plainSignature(left) === plainSignature(right);
52
+ // Detail loaders stay fresh in contentItems; only the native display component is reused.
53
+ return isDeepStrictEqual({ ...left, load: undefined }, { ...right, load: undefined });
73
54
  }
74
55
  function previewStop(reason: string | undefined): AssistantMessage["stopReason"] {
75
56
  if (reason === "error" || reason === "aborted" || reason === "length") {
@@ -191,10 +172,11 @@ export class HistoryCards {
191
172
  private card(item: AgentHistoryItem, expanded: boolean, detail: boolean): Component {
192
173
  const signature = `${expanded}:${detail}`,
193
174
  cached = this.components.get(item.id);
194
- if (
195
- cached?.signature === signature &&
196
- (cached.item === item || sameDisplay(cached.item, item))
197
- ) {
175
+ if (cached?.signature === signature && cached.item === item) {
176
+ return cached.component;
177
+ }
178
+ if (cached?.signature === signature && sameDisplay(cached.item, item)) {
179
+ this.components.set(item.id, { ...cached, item });
198
180
  return cached.component;
199
181
  }
200
182
  const component = this.create(item, { expanded, detail });