@dyyz1993/pi-coding-agent 0.74.45 → 0.74.47

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 (33) hide show
  1. package/dist/core/agent-session.d.ts.map +1 -1
  2. package/dist/core/agent-session.js +13 -0
  3. package/dist/core/agent-session.js.map +1 -1
  4. package/dist/extensions/auto-memory/__tests__/extract-result.test.ts +42 -0
  5. package/dist/extensions/auto-memory/__tests__/prefetch-history.test.ts +136 -0
  6. package/dist/extensions/auto-memory/__tests__/prompts.test.ts +29 -0
  7. package/dist/extensions/auto-memory/__tests__/skip-rules.test.ts +366 -0
  8. package/dist/extensions/auto-memory/contract.d.ts +16 -0
  9. package/dist/extensions/auto-memory/contract.d.ts.map +1 -1
  10. package/dist/extensions/auto-memory/contract.js.map +1 -1
  11. package/dist/extensions/auto-memory/contract.ts +16 -0
  12. package/dist/extensions/auto-memory/index.ts +134 -13
  13. package/dist/extensions/auto-memory/prompts.ts +10 -0
  14. package/dist/extensions/auto-memory/skip-rules.ts +2 -0
  15. package/dist/extensions/bash-ext/index.ts +855 -845
  16. package/dist/extensions/claude-hooks-compat/index.ts +12 -7
  17. package/dist/extensions/coordinator/handler.test.ts +388 -123
  18. package/dist/extensions/coordinator/handler.ts +78 -12
  19. package/dist/extensions/coordinator/index.ts +267 -198
  20. package/dist/extensions/coordinator/types.d.ts +16 -0
  21. package/dist/extensions/coordinator/types.d.ts.map +1 -1
  22. package/dist/extensions/coordinator/types.js.map +1 -1
  23. package/dist/extensions/coordinator/types.ts +57 -49
  24. package/dist/extensions/lsp/lsp/index.ts +15 -9
  25. package/dist/extensions/lsp/lsp/lsp-clangd-e2e.test.ts +229 -0
  26. package/dist/extensions/message-bridge/index.ts +14 -11
  27. package/dist/extensions/session-supervisor/index.ts +14 -8
  28. package/dist/extensions/subagent-v2/index.ts +58 -42
  29. package/dist/extensions/todo-ext/index.ts +7 -3
  30. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  31. package/dist/modes/rpc/rpc-mode.js +9 -1
  32. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  33. package/package.json +1 -1
@@ -37,6 +37,7 @@ export interface CoordinatorChannelContract extends ChannelContract {
37
37
  params: {
38
38
  task: string;
39
39
  title?: string;
40
+ projectPath?: string;
40
41
  };
41
42
  return: DelegateCreateResult;
42
43
  };
@@ -65,11 +66,26 @@ export interface CoordinatorChannelContract extends ChannelContract {
65
66
  ok: boolean;
66
67
  };
67
68
  };
69
+ session_delegate_remove: {
70
+ params: {
71
+ sessionId: string;
72
+ };
73
+ return: {
74
+ ok: boolean;
75
+ };
76
+ };
77
+ session_delegate_clear_stopped: {
78
+ params: Record<string, never>;
79
+ return: {
80
+ removed: number;
81
+ };
82
+ };
68
83
  session_delegate_fork: {
69
84
  params: {
70
85
  sessionId: string;
71
86
  task: string;
72
87
  title?: string;
88
+ projectPath?: string;
73
89
  };
74
90
  return: DelegateCreateResult;
75
91
  };
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChannelContract } from \"@dyyz1993/pi-coding-agent\";\n\nexport const COORDINATOR_CHANNEL_NAME = \"coordinator\";\n\nexport type SessionStatus = \"idle\" | \"streaming\" | \"stopped\" | \"completed\";\n\nexport interface DelegatedTask {\n\tsessionId: string;\n\ttitle: string;\n\ttask: string;\n\tprojectPath: string;\n\tdispatchedAt: number;\n\tstatus: SessionStatus;\n\tcompletedAt?: number;\n\tresult?: string;\n}\n\nexport interface DelegateCreateResult {\n\tsessionId: string;\n\tstatus: \"started\" | \"already_running\";\n}\n\nexport interface DelegateSendResult {\n\tdelivered: boolean;\n\ttargetStatus: \"active\" | \"started\" | \"not_found\";\n}\n\nexport interface DelegateListResult {\n\ttasks: DelegatedTask[];\n}\n\nexport interface DelegateStatusExt {\n\ttask: DelegatedTask | null;\n\tisCompacting?: boolean;\n\tcontextUsage?: { tokens: number | null; contextWindow: number; percent: number | null };\n}\n\nexport interface CoordinatorChannelContract extends ChannelContract {\n\tmethods: {\n\t\tsession_delegate: {\n\t\t\tparams: { task: string; title?: string };\n\t\t\treturn: DelegateCreateResult;\n\t\t};\n\t\tsession_delegate_send: {\n\t\t\tparams: { targetSessionId: string; message: string };\n\t\t\treturn: DelegateSendResult;\n\t\t};\n\t\tsession_delegate_status: {\n\t\t\tparams: { sessionId: string };\n\t\t\treturn: DelegateStatusExt;\n\t\t};\n\t\tsession_delegate_list: {\n\t\t\tparams: Record<string, never>;\n\t\t\treturn: DelegateListResult;\n\t\t};\n\t\tsession_delegate_stop: {\n\t\t\tparams: { sessionId: string };\n\t\t\treturn: { ok: boolean };\n\t\t};\n\t\tsession_delegate_fork: {\n\t\t\tparams: { sessionId: string; task: string; title?: string };\n\t\t\treturn: DelegateCreateResult;\n\t\t};\n\t};\n\tevents: {\n\t\tmessage_received: { fromSessionId: string; message: string };\n\t\ttask_started: { sessionId: string; title: string; task: string };\n\t\ttask_stopped: { sessionId: string };\n\t\ttask_completed: { sessionId: string; result?: string };\n\t\ttask_error: { sessionId: string; error: string };\n\t};\n}\n"]}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChannelContract } from \"@dyyz1993/pi-coding-agent\";\n\nexport const COORDINATOR_CHANNEL_NAME = \"coordinator\";\n\nexport type SessionStatus = \"idle\" | \"streaming\" | \"stopped\" | \"completed\";\n\nexport interface DelegatedTask {\n sessionId: string;\n title: string;\n task: string;\n projectPath: string;\n dispatchedAt: number;\n status: SessionStatus;\n completedAt?: number;\n result?: string;\n}\n\nexport interface DelegateCreateResult {\n sessionId: string;\n status: \"started\" | \"already_running\";\n}\n\nexport interface DelegateSendResult {\n delivered: boolean;\n targetStatus: \"active\" | \"started\" | \"not_found\";\n}\n\nexport interface DelegateListResult {\n tasks: DelegatedTask[];\n}\n\nexport interface DelegateStatusExt {\n task: DelegatedTask | null;\n isCompacting?: boolean;\n contextUsage?: { tokens: number | null; contextWindow: number; percent: number | null };\n}\n\nexport interface CoordinatorChannelContract extends ChannelContract {\n methods: {\n session_delegate: {\n params: { task: string; title?: string; projectPath?: string };\n return: DelegateCreateResult;\n };\n session_delegate_send: {\n params: { targetSessionId: string; message: string };\n return: DelegateSendResult;\n };\n session_delegate_status: {\n params: { sessionId: string };\n return: DelegateStatusExt;\n };\n session_delegate_list: {\n params: Record<string, never>;\n return: DelegateListResult;\n };\n session_delegate_stop: {\n params: { sessionId: string };\n return: { ok: boolean };\n };\n session_delegate_remove: {\n params: { sessionId: string };\n return: { ok: boolean };\n };\n session_delegate_clear_stopped: {\n params: Record<string, never>;\n return: { removed: number };\n };\n session_delegate_fork: {\n params: { sessionId: string; task: string; title?: string; projectPath?: string };\n return: DelegateCreateResult;\n };\n };\n events: {\n message_received: { fromSessionId: string; message: string };\n task_started: { sessionId: string; title: string; task: string };\n task_stopped: { sessionId: string };\n task_completed: { sessionId: string; result?: string };\n task_error: { sessionId: string; error: string };\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,wBAAwB,GAAG,aAAa,CAAC","sourcesContent":["import type { ChannelContract } from \"@dyyz1993/pi-coding-agent\";\n\nexport const COORDINATOR_CHANNEL_NAME = \"coordinator\";\n\nexport type SessionStatus = \"idle\" | \"streaming\" | \"stopped\" | \"completed\";\n\nexport interface DelegatedTask {\n\tsessionId: string;\n\ttitle: string;\n\ttask: string;\n\tprojectPath: string;\n\tdispatchedAt: number;\n\tstatus: SessionStatus;\n\tcompletedAt?: number;\n\tresult?: string;\n}\n\nexport interface DelegateCreateResult {\n\tsessionId: string;\n\tstatus: \"started\" | \"already_running\";\n}\n\nexport interface DelegateSendResult {\n\tdelivered: boolean;\n\ttargetStatus: \"active\" | \"started\" | \"not_found\";\n}\n\nexport interface DelegateListResult {\n\ttasks: DelegatedTask[];\n}\n\nexport interface DelegateStatusExt {\n\ttask: DelegatedTask | null;\n\tisCompacting?: boolean;\n\tcontextUsage?: { tokens: number | null; contextWindow: number; percent: number | null };\n}\n\nexport interface CoordinatorChannelContract extends ChannelContract {\n\tmethods: {\n\t\tsession_delegate: {\n\t\t\tparams: { task: string; title?: string };\n\t\t\treturn: DelegateCreateResult;\n\t\t};\n\t\tsession_delegate_send: {\n\t\t\tparams: { targetSessionId: string; message: string };\n\t\t\treturn: DelegateSendResult;\n\t\t};\n\t\tsession_delegate_status: {\n\t\t\tparams: { sessionId: string };\n\t\t\treturn: DelegateStatusExt;\n\t\t};\n\t\tsession_delegate_list: {\n\t\t\tparams: Record<string, never>;\n\t\t\treturn: DelegateListResult;\n\t\t};\n\t\tsession_delegate_stop: {\n\t\t\tparams: { sessionId: string };\n\t\t\treturn: { ok: boolean };\n\t\t};\n\t\tsession_delegate_fork: {\n\t\t\tparams: { sessionId: string; task: string; title?: string };\n\t\t\treturn: DelegateCreateResult;\n\t\t};\n\t};\n\tevents: {\n\t\tmessage_received: { fromSessionId: string; message: string };\n\t\ttask_started: { sessionId: string; title: string; task: string };\n\t\ttask_stopped: { sessionId: string };\n\t\ttask_completed: { sessionId: string; result?: string };\n\t\ttask_error: { sessionId: string; error: string };\n\t};\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,wBAAwB,GAAG,aAAa,CAAC","sourcesContent":["import type { ChannelContract } from \"@dyyz1993/pi-coding-agent\";\n\nexport const COORDINATOR_CHANNEL_NAME = \"coordinator\";\n\nexport type SessionStatus = \"idle\" | \"streaming\" | \"stopped\" | \"completed\";\n\nexport interface DelegatedTask {\n sessionId: string;\n title: string;\n task: string;\n projectPath: string;\n dispatchedAt: number;\n status: SessionStatus;\n completedAt?: number;\n result?: string;\n}\n\nexport interface DelegateCreateResult {\n sessionId: string;\n status: \"started\" | \"already_running\";\n}\n\nexport interface DelegateSendResult {\n delivered: boolean;\n targetStatus: \"active\" | \"started\" | \"not_found\";\n}\n\nexport interface DelegateListResult {\n tasks: DelegatedTask[];\n}\n\nexport interface DelegateStatusExt {\n task: DelegatedTask | null;\n isCompacting?: boolean;\n contextUsage?: { tokens: number | null; contextWindow: number; percent: number | null };\n}\n\nexport interface CoordinatorChannelContract extends ChannelContract {\n methods: {\n session_delegate: {\n params: { task: string; title?: string; projectPath?: string };\n return: DelegateCreateResult;\n };\n session_delegate_send: {\n params: { targetSessionId: string; message: string };\n return: DelegateSendResult;\n };\n session_delegate_status: {\n params: { sessionId: string };\n return: DelegateStatusExt;\n };\n session_delegate_list: {\n params: Record<string, never>;\n return: DelegateListResult;\n };\n session_delegate_stop: {\n params: { sessionId: string };\n return: { ok: boolean };\n };\n session_delegate_remove: {\n params: { sessionId: string };\n return: { ok: boolean };\n };\n session_delegate_clear_stopped: {\n params: Record<string, never>;\n return: { removed: number };\n };\n session_delegate_fork: {\n params: { sessionId: string; task: string; title?: string; projectPath?: string };\n return: DelegateCreateResult;\n };\n };\n events: {\n message_received: { fromSessionId: string; message: string };\n task_started: { sessionId: string; title: string; task: string };\n task_stopped: { sessionId: string };\n task_completed: { sessionId: string; result?: string };\n task_error: { sessionId: string; error: string };\n };\n}\n"]}
@@ -5,68 +5,76 @@ export const COORDINATOR_CHANNEL_NAME = "coordinator";
5
5
  export type SessionStatus = "idle" | "streaming" | "stopped" | "completed";
6
6
 
7
7
  export interface DelegatedTask {
8
- sessionId: string;
9
- title: string;
10
- task: string;
11
- projectPath: string;
12
- dispatchedAt: number;
13
- status: SessionStatus;
14
- completedAt?: number;
15
- result?: string;
8
+ sessionId: string;
9
+ title: string;
10
+ task: string;
11
+ projectPath: string;
12
+ dispatchedAt: number;
13
+ status: SessionStatus;
14
+ completedAt?: number;
15
+ result?: string;
16
16
  }
17
17
 
18
18
  export interface DelegateCreateResult {
19
- sessionId: string;
20
- status: "started" | "already_running";
19
+ sessionId: string;
20
+ status: "started" | "already_running";
21
21
  }
22
22
 
23
23
  export interface DelegateSendResult {
24
- delivered: boolean;
25
- targetStatus: "active" | "started" | "not_found";
24
+ delivered: boolean;
25
+ targetStatus: "active" | "started" | "not_found";
26
26
  }
27
27
 
28
28
  export interface DelegateListResult {
29
- tasks: DelegatedTask[];
29
+ tasks: DelegatedTask[];
30
30
  }
31
31
 
32
32
  export interface DelegateStatusExt {
33
- task: DelegatedTask | null;
34
- isCompacting?: boolean;
35
- contextUsage?: { tokens: number | null; contextWindow: number; percent: number | null };
33
+ task: DelegatedTask | null;
34
+ isCompacting?: boolean;
35
+ contextUsage?: { tokens: number | null; contextWindow: number; percent: number | null };
36
36
  }
37
37
 
38
38
  export interface CoordinatorChannelContract extends ChannelContract {
39
- methods: {
40
- session_delegate: {
41
- params: { task: string; title?: string };
42
- return: DelegateCreateResult;
43
- };
44
- session_delegate_send: {
45
- params: { targetSessionId: string; message: string };
46
- return: DelegateSendResult;
47
- };
48
- session_delegate_status: {
49
- params: { sessionId: string };
50
- return: DelegateStatusExt;
51
- };
52
- session_delegate_list: {
53
- params: Record<string, never>;
54
- return: DelegateListResult;
55
- };
56
- session_delegate_stop: {
57
- params: { sessionId: string };
58
- return: { ok: boolean };
59
- };
60
- session_delegate_fork: {
61
- params: { sessionId: string; task: string; title?: string };
62
- return: DelegateCreateResult;
63
- };
64
- };
65
- events: {
66
- message_received: { fromSessionId: string; message: string };
67
- task_started: { sessionId: string; title: string; task: string };
68
- task_stopped: { sessionId: string };
69
- task_completed: { sessionId: string; result?: string };
70
- task_error: { sessionId: string; error: string };
71
- };
39
+ methods: {
40
+ session_delegate: {
41
+ params: { task: string; title?: string; projectPath?: string };
42
+ return: DelegateCreateResult;
43
+ };
44
+ session_delegate_send: {
45
+ params: { targetSessionId: string; message: string };
46
+ return: DelegateSendResult;
47
+ };
48
+ session_delegate_status: {
49
+ params: { sessionId: string };
50
+ return: DelegateStatusExt;
51
+ };
52
+ session_delegate_list: {
53
+ params: Record<string, never>;
54
+ return: DelegateListResult;
55
+ };
56
+ session_delegate_stop: {
57
+ params: { sessionId: string };
58
+ return: { ok: boolean };
59
+ };
60
+ session_delegate_remove: {
61
+ params: { sessionId: string };
62
+ return: { ok: boolean };
63
+ };
64
+ session_delegate_clear_stopped: {
65
+ params: Record<string, never>;
66
+ return: { removed: number };
67
+ };
68
+ session_delegate_fork: {
69
+ params: { sessionId: string; task: string; title?: string; projectPath?: string };
70
+ return: DelegateCreateResult;
71
+ };
72
+ };
73
+ events: {
74
+ message_received: { fromSessionId: string; message: string };
75
+ task_started: { sessionId: string; title: string; task: string };
76
+ task_stopped: { sessionId: string };
77
+ task_completed: { sessionId: string; result?: string };
78
+ task_error: { sessionId: string; error: string };
79
+ };
72
80
  }
@@ -71,15 +71,21 @@ export default function lspExtension(pi: ExtensionAPI): void {
71
71
  })),
72
72
  }));
73
73
 
74
- pi.sendMessage(
75
- {
76
- customType: "lsp_diagnostics",
77
- content: `[LSP] Post-edit diagnostics found issues in ${results.length} file(s): ${summary}.\nPlease review and fix the issues listed below.`,
78
- display: true,
79
- details: { files: fileSummaries },
80
- },
81
- { triggerTurn: true },
82
- );
74
+ try {
75
+ pi.sendMessage(
76
+ {
77
+ customType: "lsp_diagnostics",
78
+ content: `[LSP] Post-edit diagnostics found issues in ${results.length} file(s): ${summary}.\nPlease review and fix the issues listed below.`,
79
+ display: true,
80
+ details: { files: fileSummaries },
81
+ },
82
+ { triggerTurn: true },
83
+ );
84
+ } catch (err) {
85
+ const msg = err instanceof Error ? err.message : String(err);
86
+ if (msg.includes("stale")) return;
87
+ throw err;
88
+ }
83
89
  });
84
90
 
85
91
  let idleCleanupTimer: ReturnType<typeof setTimeout> | undefined;
@@ -0,0 +1,229 @@
1
+ import { tmpdir } from "node:os";
2
+ import { mkdir, writeFile, rm } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { describe, expect, it, vi, beforeAll, afterAll } from "vitest";
5
+ import type { ExtensionAPI } from "@dyyz1993/pi-coding-agent";
6
+ import lspExtensionDefault from "./index.js";
7
+
8
+ const TEST_DIR = "/tmp/lsp-clangd-test";
9
+ const TEST_FILE = "test.c";
10
+
11
+ function createMockPi() {
12
+ const handlers: Record<string, Array<(event: any, ctx: any) => any>> = {};
13
+ const registeredTools = new Map<string, any>();
14
+ const channelSendFn = vi.fn();
15
+ const registerCommandFn = vi.fn();
16
+ let channelOnReceiveHandler: ((data: unknown) => void) | null = null;
17
+ let currentChannel: {
18
+ name: string;
19
+ send: (data: unknown) => void;
20
+ onReceive: (handler: (data: unknown) => void) => () => void;
21
+ invoke: (data: unknown, timeoutMs?: number) => Promise<unknown>;
22
+ call: (method: string, params: Record<string, unknown>, timeoutMs?: number) => Promise<unknown>;
23
+ } | null = null;
24
+
25
+ const pi = {
26
+ on: vi.fn((event: string, handler: any) => {
27
+ if (!handlers[event]) handlers[event] = [];
28
+ handlers[event].push(handler);
29
+ }),
30
+ callLLM: vi.fn(async () => "{}"),
31
+ callLLMStructured: vi.fn(async () => ({})),
32
+ forkAgent: vi.fn(async () => ({ text: "", usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 } })),
33
+ once: vi.fn(),
34
+ emit: vi.fn(),
35
+ setStatus: vi.fn(),
36
+ registerProvider: vi.fn(),
37
+ unregisterProvider: vi.fn(),
38
+ events: { on: vi.fn(), off: vi.fn(), emit: vi.fn(), once: vi.fn() },
39
+ registerChannel: vi.fn(() => {
40
+ currentChannel = {
41
+ name: "lsp",
42
+ send: channelSendFn,
43
+ onReceive: vi.fn((handler: (data: unknown) => void) => {
44
+ channelOnReceiveHandler = handler;
45
+ return () => { channelOnReceiveHandler = null; };
46
+ }),
47
+ invoke: vi.fn(async (data: unknown) => {
48
+ if (!channelOnReceiveHandler) return {};
49
+ const msg = data as Record<string, unknown>;
50
+ const invokeId = msg.__invokeId as string;
51
+ return new Promise((resolve) => {
52
+ const orig = channelSendFn.getMockImplementation() ?? channelSendFn;
53
+ channelSendFn.mockImplementation((response: unknown) => {
54
+ const resp = response as Record<string, unknown>;
55
+ if (resp?.invokeId === invokeId) {
56
+ channelSendFn.mockImplementation(orig as any);
57
+ resolve(response);
58
+ }
59
+ });
60
+ channelOnReceiveHandler!(data);
61
+ });
62
+ }),
63
+ call: vi.fn(async (method: string, params: Record<string, unknown>, _timeoutMs?: number) => {
64
+ if (!channelOnReceiveHandler) return {};
65
+ const invokeId = `invoke_${method}_${Date.now()}`;
66
+ return new Promise((resolve) => {
67
+ const orig = channelSendFn.getMockImplementation() ?? channelSendFn;
68
+ channelSendFn.mockImplementation((response: unknown) => {
69
+ const resp = response as Record<string, unknown>;
70
+ if (resp?.invokeId === invokeId) {
71
+ channelSendFn.mockImplementation(orig as any);
72
+ resolve(response);
73
+ }
74
+ });
75
+ channelOnReceiveHandler!({ __call: method, invokeId, ...params });
76
+ });
77
+ }),
78
+ };
79
+ return currentChannel;
80
+ }),
81
+ registerTool: vi.fn((tool: any) => {
82
+ registeredTools.set(tool.name, tool);
83
+ }),
84
+ registerCommand: registerCommandFn,
85
+ appendEntry: vi.fn(),
86
+ sendMessage: vi.fn(),
87
+ off: vi.fn(),
88
+ } as unknown as ExtensionAPI;
89
+
90
+ return {
91
+ pi,
92
+ handlers,
93
+ registeredTools,
94
+ channelSend: channelSendFn,
95
+ registerCommandFn,
96
+ getCurrentChannel: () => currentChannel,
97
+ };
98
+ }
99
+
100
+ async function fireSessionStart(
101
+ mock: ReturnType<typeof createMockPi>,
102
+ cwd: string,
103
+ ): Promise<void> {
104
+ for (const h of mock.handlers.session_start ?? []) {
105
+ await h(
106
+ {},
107
+ {
108
+ sessionManager: { getBranch: () => [] },
109
+ hasUI: false,
110
+ ui: { notify: vi.fn() },
111
+ cwd,
112
+ isIdle: () => true,
113
+ signal: undefined,
114
+ abort: () => {},
115
+ hasPendingMessages: () => false,
116
+ shutdown: () => {},
117
+ getContextUsage: () => undefined,
118
+ compact: () => {},
119
+ getSystemPrompt: () => "",
120
+ model: undefined,
121
+ },
122
+ );
123
+ }
124
+ }
125
+
126
+ async function fireSessionShutdown(mock: ReturnType<typeof createMockPi>): Promise<void> {
127
+ for (const h of mock.handlers.session_shutdown ?? []) {
128
+ await h({}, {});
129
+ }
130
+ }
131
+
132
+ async function fireToolResult(
133
+ mock: ReturnType<typeof createMockPi>,
134
+ filePath: string,
135
+ toolName: "write" | "edit" = "write",
136
+ ): Promise<any> {
137
+ const results: any[] = [];
138
+ for (const h of mock.handlers.tool_result ?? []) {
139
+ const result = await h(
140
+ {
141
+ type: "tool_result",
142
+ toolCallId: "tc_e2e_1",
143
+ toolName,
144
+ input: { path: filePath },
145
+ content: [{ type: "text", text: `File written: ${filePath}` }],
146
+ isError: false,
147
+ details: undefined,
148
+ },
149
+ {
150
+ cwd: TEST_DIR,
151
+ ui: { notify: vi.fn() },
152
+ },
153
+ );
154
+ if (result) results.push(result);
155
+ }
156
+ return results;
157
+ }
158
+
159
+ describe("clangd E2E integration", () => {
160
+ const originalCwd = process.cwd();
161
+
162
+ beforeAll(async () => {
163
+ await mkdir(join(TEST_DIR, ".pi"), { recursive: true });
164
+ await writeFile(join(TEST_DIR, ".pi", "lsp.json"), JSON.stringify({
165
+ servers: [
166
+ {
167
+ name: "clangd",
168
+ command: ["clangd"],
169
+ fileTypes: [".c", ".h", ".cpp", ".hpp", ".cc", ".cxx"],
170
+ },
171
+ ],
172
+ }));
173
+ await writeFile(join(TEST_DIR, TEST_FILE), `#include <stdio.h>\n\nint main() {\n int x = "hello";\n printf("%d\\n", x);\n return 0;\n}\n`);
174
+ process.chdir(TEST_DIR);
175
+ });
176
+
177
+ afterAll(async () => {
178
+ process.chdir(originalCwd);
179
+ });
180
+
181
+ it(
182
+ "starts clangd, detects type error in test.c via diagnostics",
183
+ async () => {
184
+ const mock = createMockPi();
185
+ lspExtensionDefault(mock.pi);
186
+
187
+ await fireSessionStart(mock, TEST_DIR);
188
+
189
+ const channel = mock.getCurrentChannel();
190
+ expect(channel).not.toBeNull();
191
+
192
+ const statusResult = await channel!.call("getStatus", {});
193
+ console.log("[e2e] Status after session_start:", JSON.stringify(statusResult, null, 2));
194
+
195
+ const status = statusResult as any;
196
+ expect(status.state).toBeDefined();
197
+
198
+ const readyServers = (status.servers as any[])?.filter((s: any) => s.state === "ready") ?? [];
199
+ console.log(`[e2e] Ready servers: ${readyServers.length}`);
200
+ for (const s of readyServers) {
201
+ console.log(`[e2e] - ${s.name} [${(s.fileTypes ?? []).join(",")}] state=${s.state}`);
202
+ }
203
+
204
+ if (readyServers.length === 0) {
205
+ console.log("[e2e] No clangd server became ready — skipping diagnostics check");
206
+ console.log("[e2e] All servers:", JSON.stringify(status.servers, null, 2));
207
+ }
208
+
209
+ expect(readyServers.length).toBeGreaterThanOrEqual(1);
210
+
211
+ const toolResults = await fireToolResult(mock, TEST_FILE);
212
+ console.log("[e2e] tool_result handler results:", JSON.stringify(toolResults, null, 2));
213
+
214
+ const diagnosticsContent = toolResults.find(
215
+ (r: any) => r?.content?.some?.((c: any) => c.text?.includes("[LSP]")),
216
+ );
217
+ if (diagnosticsContent) {
218
+ console.log("[e2e] Diagnostics found in tool_result response:");
219
+ for (const c of diagnosticsContent.content) {
220
+ console.log(c.text);
221
+ }
222
+ }
223
+
224
+ await fireSessionShutdown(mock);
225
+ console.log("[e2e] Session shutdown complete");
226
+ },
227
+ 30_000,
228
+ );
229
+ });
@@ -139,6 +139,7 @@ export default function messageBridgeExtension(pi: any) {
139
139
 
140
140
  pi.on("ui", async (event: any, ctx: any) => {
141
141
  if (event.method === "notify") {
142
+ if (event.message == null) return undefined;
142
143
  pushAndWait(event.message, sessionId).catch((err) => console.debug("[message-bridge] notify push failed:", err instanceof Error ? err.message : err));
143
144
  return undefined;
144
145
  }
@@ -148,7 +149,7 @@ export default function messageBridgeExtension(pi: any) {
148
149
  pushAndWait(question, sessionId)
149
150
  .then((answer) => {
150
151
  const confirmed = parseConfirmAnswer(answer);
151
- ctx.respondUI(event.id, { action: "responded", confirmed });
152
+ try { ctx.respondUI(event.id, { action: "responded", confirmed }); } catch (e) { if (!/stale/i.test(e instanceof Error ? e.message : "")) throw e; }
152
153
  })
153
154
  .catch((err) => console.debug("[message-bridge] confirm push failed:", err instanceof Error ? err.message : err));
154
155
  return undefined;
@@ -160,13 +161,15 @@ export default function messageBridgeExtension(pi: any) {
160
161
  const question = buildSelectQuestion(event.title, options, multiple);
161
162
  pushAndWait(question, sessionId)
162
163
  .then((answer) => {
163
- if (multiple) {
164
- const values = parseMultiSelectAnswer(answer, options);
165
- ctx.respondUI(event.id, { action: "responded", value: values });
166
- } else {
167
- const value = parseSelectAnswer(answer);
168
- ctx.respondUI(event.id, { action: "responded", value });
169
- }
164
+ try {
165
+ if (multiple) {
166
+ const values = parseMultiSelectAnswer(answer, options);
167
+ ctx.respondUI(event.id, { action: "responded", value: values });
168
+ } else {
169
+ const value = parseSelectAnswer(answer);
170
+ ctx.respondUI(event.id, { action: "responded", value });
171
+ }
172
+ } catch (e) { if (!/stale/i.test(e instanceof Error ? e.message : "")) throw e; }
170
173
  })
171
174
  .catch((err) => console.debug("[message-bridge] select push failed:", err instanceof Error ? err.message : err));
172
175
  return undefined;
@@ -178,7 +181,7 @@ export default function messageBridgeExtension(pi: any) {
178
181
  : event.title;
179
182
  pushAndWait(question, sessionId)
180
183
  .then((answer) => {
181
- ctx.respondUI(event.id, { action: "responded", value: answer });
184
+ try { ctx.respondUI(event.id, { action: "responded", value: answer }); } catch (e) { if (!/stale/i.test(e instanceof Error ? e.message : "")) throw e; }
182
185
  })
183
186
  .catch((err) => console.debug("[message-bridge] input push failed:", err instanceof Error ? err.message : err));
184
187
  return undefined;
@@ -190,7 +193,7 @@ export default function messageBridgeExtension(pi: any) {
190
193
  : event.title;
191
194
  pushAndWait(question, sessionId)
192
195
  .then((answer) => {
193
- ctx.respondUI(event.id, { action: "responded", value: answer });
196
+ try { ctx.respondUI(event.id, { action: "responded", value: answer }); } catch (e) { if (!/stale/i.test(e instanceof Error ? e.message : "")) throw e; }
194
197
  })
195
198
  .catch((err) => console.debug("[message-bridge] editor push failed:", err instanceof Error ? err.message : err));
196
199
  return undefined;
@@ -214,7 +217,7 @@ export default function messageBridgeExtension(pi: any) {
214
217
  .then((id) => pullAnswer(id))
215
218
  .then((answer) => {
216
219
  if (answer?.trim()) {
217
- pi.sendUserMessage(answer.trim());
220
+ try { pi.sendUserMessage(answer.trim()); } catch (e) { if (!/stale/i.test(e instanceof Error ? e.message : "")) throw e; }
218
221
  }
219
222
  })
220
223
  .catch((err) => console.debug("[message-bridge] agent_end push failed:", err instanceof Error ? err.message : err));
@@ -369,14 +369,20 @@ export default function sessionSupervisorExtension(pi: ExtensionAPI) {
369
369
 
370
370
  currentState = "continuing";
371
371
  emitStatusChanged();
372
- pi.sendMessage(
373
- {
374
- customType: "supervisor_continue",
375
- content: continueMessage,
376
- display: true,
377
- },
378
- { triggerTurn: true },
379
- );
372
+ try {
373
+ pi.sendMessage(
374
+ {
375
+ customType: "supervisor_continue",
376
+ content: continueMessage,
377
+ display: true,
378
+ },
379
+ { triggerTurn: true },
380
+ );
381
+ } catch (err) {
382
+ const msg = err instanceof Error ? err.message : String(err);
383
+ if (/stale|abort/i.test(msg)) return;
384
+ throw err;
385
+ }
380
386
  });
381
387
  }
382
388