@oh-my-pi/pi-coding-agent 14.9.8 → 15.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.
- package/CHANGELOG.md +101 -0
- package/package.json +7 -7
- package/scripts/build-binary.ts +11 -0
- package/scripts/format-prompts.ts +1 -1
- package/src/cli/args.ts +2 -2
- package/src/cli/stats-cli.ts +2 -0
- package/src/cli.ts +24 -1
- package/src/commands/acp.ts +24 -0
- package/src/commands/launch.ts +6 -4
- package/src/commit/agentic/prompts/system.md +1 -1
- package/src/config/model-resolver.ts +30 -0
- package/src/config/settings-schema.ts +61 -9
- package/src/config/settings.ts +18 -1
- package/src/edit/index.ts +22 -1
- package/src/edit/modes/patch.ts +10 -0
- package/src/edit/modes/replace.ts +3 -0
- package/src/edit/renderer.ts +10 -0
- package/src/edit/streaming.ts +1 -1
- package/src/eval/js/context-manager.ts +10 -9
- package/src/eval/js/shared/rewrite-imports.ts +120 -48
- package/src/eval/js/shared/runtime.ts +31 -4
- package/src/eval/js/tool-bridge.ts +43 -21
- package/src/extensibility/extensions/runner.ts +54 -1
- package/src/extensibility/extensions/types.ts +11 -0
- package/src/extensibility/skills.ts +33 -1
- package/src/hashline/grammar.lark +1 -1
- package/src/hashline/input.ts +11 -5
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/issue-pr-protocol.ts +577 -0
- package/src/internal-urls/router.ts +6 -3
- package/src/internal-urls/types.ts +22 -1
- package/src/main.ts +13 -9
- package/src/modes/acp/acp-agent.ts +361 -54
- package/src/modes/acp/acp-client-bridge.ts +152 -0
- package/src/modes/acp/acp-event-mapper.ts +180 -15
- package/src/modes/acp/terminal-auth.ts +37 -0
- package/src/modes/components/read-tool-group.ts +29 -1
- package/src/modes/controllers/command-controller.ts +14 -6
- package/src/modes/controllers/event-controller.ts +24 -11
- package/src/modes/controllers/extension-ui-controller.ts +8 -2
- package/src/modes/controllers/input-controller.ts +72 -39
- package/src/modes/interactive-mode.ts +71 -7
- package/src/modes/rpc/rpc-mode.ts +17 -2
- package/src/modes/types.ts +6 -2
- package/src/modes/utils/ui-helpers.ts +15 -3
- package/src/prompts/agents/designer.md +5 -5
- package/src/prompts/agents/explore.md +7 -7
- package/src/prompts/agents/init.md +9 -9
- package/src/prompts/agents/librarian.md +14 -14
- package/src/prompts/agents/plan.md +4 -4
- package/src/prompts/agents/reviewer.md +5 -5
- package/src/prompts/agents/task.md +10 -10
- package/src/prompts/commands/orchestrate.md +2 -2
- package/src/prompts/compaction/branch-summary.md +3 -3
- package/src/prompts/compaction/compaction-short-summary.md +7 -7
- package/src/prompts/compaction/compaction-summary-context.md +1 -1
- package/src/prompts/compaction/compaction-summary.md +5 -5
- package/src/prompts/compaction/compaction-turn-prefix.md +3 -3
- package/src/prompts/compaction/compaction-update-summary.md +11 -11
- package/src/prompts/memories/consolidation.md +2 -2
- package/src/prompts/memories/read-path.md +1 -1
- package/src/prompts/memories/stage_one_input.md +1 -1
- package/src/prompts/memories/stage_one_system.md +5 -5
- package/src/prompts/review-request.md +4 -4
- package/src/prompts/system/agent-creation-architect.md +17 -17
- package/src/prompts/system/agent-creation-user.md +2 -2
- package/src/prompts/system/commit-message-system.md +2 -2
- package/src/prompts/system/custom-system-prompt.md +2 -2
- package/src/prompts/system/eager-todo.md +6 -6
- package/src/prompts/system/handoff-document.md +1 -1
- package/src/prompts/system/plan-mode-active.md +22 -21
- package/src/prompts/system/plan-mode-approved.md +4 -4
- package/src/prompts/system/plan-mode-compact-instructions.md +16 -0
- package/src/prompts/system/plan-mode-reference.md +2 -2
- package/src/prompts/system/plan-mode-subagent.md +8 -8
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +2 -2
- package/src/prompts/system/project-prompt.md +4 -4
- package/src/prompts/system/subagent-system-prompt.md +7 -7
- package/src/prompts/system/subagent-yield-reminder.md +4 -4
- package/src/prompts/system/system-prompt.md +72 -71
- package/src/prompts/system/ttsr-interrupt.md +1 -1
- package/src/prompts/tools/apply-patch.md +1 -1
- package/src/prompts/tools/ast-edit.md +3 -3
- package/src/prompts/tools/ast-grep.md +3 -3
- package/src/prompts/tools/browser.md +3 -3
- package/src/prompts/tools/checkpoint.md +3 -3
- package/src/prompts/tools/exit-plan-mode.md +2 -2
- package/src/prompts/tools/find.md +3 -3
- package/src/prompts/tools/github.md +2 -5
- package/src/prompts/tools/hashline.md +20 -20
- package/src/prompts/tools/image-gen.md +3 -3
- package/src/prompts/tools/irc.md +1 -1
- package/src/prompts/tools/lsp.md +2 -2
- package/src/prompts/tools/patch.md +6 -6
- package/src/prompts/tools/read.md +7 -7
- package/src/prompts/tools/replace.md +5 -5
- package/src/prompts/tools/retain.md +1 -1
- package/src/prompts/tools/rewind.md +2 -2
- package/src/prompts/tools/search.md +2 -2
- package/src/prompts/tools/ssh.md +2 -2
- package/src/prompts/tools/task.md +12 -6
- package/src/prompts/tools/web-search.md +2 -2
- package/src/prompts/tools/write.md +3 -3
- package/src/sdk.ts +69 -12
- package/src/session/agent-session.ts +231 -22
- package/src/session/client-bridge.ts +81 -0
- package/src/session/compaction/errors.ts +31 -0
- package/src/session/compaction/index.ts +1 -0
- package/src/slash-commands/acp-builtins.ts +46 -0
- package/src/slash-commands/builtin-registry.ts +699 -116
- package/src/slash-commands/helpers/context-report.ts +39 -0
- package/src/slash-commands/helpers/format.ts +23 -0
- package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
- package/src/slash-commands/helpers/mcp.ts +532 -0
- package/src/slash-commands/helpers/parse.ts +85 -0
- package/src/slash-commands/helpers/ssh.ts +193 -0
- package/src/slash-commands/helpers/todo.ts +279 -0
- package/src/slash-commands/helpers/usage-report.ts +91 -0
- package/src/slash-commands/types.ts +126 -0
- package/src/task/executor.ts +10 -3
- package/src/task/index.ts +29 -51
- package/src/task/render.ts +6 -3
- package/src/task/worktree.ts +170 -239
- package/src/tools/bash.ts +176 -2
- package/src/tools/browser/tab-supervisor.ts +13 -13
- package/src/tools/conflict-detect.ts +6 -6
- package/src/tools/fetch.ts +15 -4
- package/src/tools/find.ts +19 -1
- package/src/tools/gh-renderer.ts +0 -12
- package/src/tools/gh.ts +682 -176
- package/src/tools/github-cache.ts +548 -0
- package/src/tools/index.ts +3 -0
- package/src/tools/read.ts +110 -27
- package/src/tools/write.ts +23 -1
- package/src/tui/code-cell.ts +70 -2
- package/src/utils/git.ts +5 -0
- package/src/task/isolation-backend.ts +0 -94
|
@@ -4,7 +4,9 @@ import {
|
|
|
4
4
|
type AgentSideConnection,
|
|
5
5
|
type AuthenticateRequest,
|
|
6
6
|
type AuthenticateResponse,
|
|
7
|
+
type AuthMethod,
|
|
7
8
|
type AvailableCommand,
|
|
9
|
+
type ClientCapabilities,
|
|
8
10
|
type CloseSessionRequest,
|
|
9
11
|
type CloseSessionResponse,
|
|
10
12
|
type ForkSessionRequest,
|
|
@@ -37,27 +39,35 @@ import {
|
|
|
37
39
|
type SetSessionModeResponse,
|
|
38
40
|
type Usage,
|
|
39
41
|
} from "@agentclientprotocol/sdk";
|
|
40
|
-
import type { Model } from "@oh-my-pi/pi-ai";
|
|
42
|
+
import type { AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
|
41
43
|
import { logger, VERSION } from "@oh-my-pi/pi-utils";
|
|
42
|
-
import { disableProvider, enableProvider } from "../../capability";
|
|
44
|
+
import { disableProvider, enableProvider, reset as resetCapabilities } from "../../capability";
|
|
43
45
|
import { Settings } from "../../config/settings";
|
|
46
|
+
import { clearPluginRootsAndCaches, resolveActiveProjectRegistryPath } from "../../discovery/helpers";
|
|
44
47
|
import type { ExtensionUIContext } from "../../extensibility/extensions";
|
|
45
48
|
import { runExtensionCompact } from "../../extensibility/extensions/compact-handler";
|
|
49
|
+
import { buildSkillPromptMessage, getSkillSlashCommandName } from "../../extensibility/skills";
|
|
46
50
|
import { loadSlashCommands } from "../../extensibility/slash-commands";
|
|
47
51
|
import { MCPManager } from "../../mcp/manager";
|
|
48
52
|
import type { MCPServerConfig } from "../../mcp/types";
|
|
49
53
|
import { loadAllExtensions } from "../../modes/components/extensions/state-manager";
|
|
50
54
|
import { theme } from "../../modes/theme/theme";
|
|
51
55
|
import type { AgentSession, AgentSessionEvent } from "../../session/agent-session";
|
|
56
|
+
import { SKILL_PROMPT_MESSAGE_TYPE } from "../../session/messages";
|
|
52
57
|
import {
|
|
53
58
|
SessionManager,
|
|
54
59
|
type SessionInfo as StoredSessionInfo,
|
|
55
60
|
type UsageStatistics,
|
|
56
61
|
} from "../../session/session-manager";
|
|
62
|
+
import { ACP_BUILTIN_SLASH_COMMANDS, executeAcpBuiltinSlashCommand } from "../../slash-commands/acp-builtins";
|
|
57
63
|
import { parseThinkingLevel } from "../../thinking";
|
|
64
|
+
import { createAcpClientBridge } from "./acp-client-bridge";
|
|
58
65
|
import { mapAgentSessionEventToAcpSessionUpdates, mapToolKind } from "./acp-event-mapper";
|
|
66
|
+
import { ACP_TERMINAL_AUTH_FLAG } from "./terminal-auth";
|
|
59
67
|
|
|
60
|
-
const
|
|
68
|
+
const ACP_DEFAULT_MODE_ID = "default";
|
|
69
|
+
const ACP_PLAN_MODE_ID = "plan";
|
|
70
|
+
const DEFAULT_PLAN_FILE_URL = "local://PLAN.md";
|
|
61
71
|
const MODE_CONFIG_ID = "mode";
|
|
62
72
|
const MODEL_CONFIG_ID = "model";
|
|
63
73
|
const THINKING_CONFIG_ID = "thinking";
|
|
@@ -84,7 +94,8 @@ type ManagedSessionRecord = {
|
|
|
84
94
|
session: AgentSession;
|
|
85
95
|
mcpManager: MCPManager | undefined;
|
|
86
96
|
promptTurn: PromptTurnState | undefined;
|
|
87
|
-
|
|
97
|
+
liveMessageId: string | undefined;
|
|
98
|
+
liveMessageProgress: { textEmitted: boolean; thoughtEmitted: boolean } | undefined;
|
|
88
99
|
extensionsConfigured: boolean;
|
|
89
100
|
};
|
|
90
101
|
|
|
@@ -152,6 +163,7 @@ export class AcpAgent implements Agent {
|
|
|
152
163
|
#sessions = new Map<string, ManagedSessionRecord>();
|
|
153
164
|
#disposePromise: Promise<void> | undefined;
|
|
154
165
|
#cleanupRegistered = false;
|
|
166
|
+
#clientCapabilities: ClientCapabilities | undefined;
|
|
155
167
|
|
|
156
168
|
constructor(connection: AgentSideConnection, initialSession: AgentSession, createSession: CreateAcpSession) {
|
|
157
169
|
this.#connection = connection;
|
|
@@ -159,8 +171,25 @@ export class AcpAgent implements Agent {
|
|
|
159
171
|
this.#createSession = createSession;
|
|
160
172
|
}
|
|
161
173
|
|
|
162
|
-
async initialize(
|
|
174
|
+
async initialize(params: InitializeRequest): Promise<InitializeResponse> {
|
|
163
175
|
this.#registerConnectionCleanup();
|
|
176
|
+
this.#clientCapabilities = params.clientCapabilities;
|
|
177
|
+
const authMethods: AuthMethod[] = [
|
|
178
|
+
{
|
|
179
|
+
id: "agent",
|
|
180
|
+
name: "Use existing local credentials",
|
|
181
|
+
description: "Authenticate via the provider keys/OAuth state already configured under ~/.omp.",
|
|
182
|
+
},
|
|
183
|
+
];
|
|
184
|
+
if (params.clientCapabilities?.auth?.terminal === true) {
|
|
185
|
+
authMethods.push({
|
|
186
|
+
type: "terminal",
|
|
187
|
+
id: "terminal",
|
|
188
|
+
name: "Set up Oh My Pi in terminal",
|
|
189
|
+
description: "Launch the omp TUI to add provider keys and select models.",
|
|
190
|
+
args: [ACP_TERMINAL_AUTH_FLAG],
|
|
191
|
+
});
|
|
192
|
+
}
|
|
164
193
|
return {
|
|
165
194
|
protocolVersion: PROTOCOL_VERSION,
|
|
166
195
|
agentInfo: {
|
|
@@ -168,13 +197,7 @@ export class AcpAgent implements Agent {
|
|
|
168
197
|
title: "Oh My Pi",
|
|
169
198
|
version: VERSION,
|
|
170
199
|
},
|
|
171
|
-
authMethods
|
|
172
|
-
{
|
|
173
|
-
id: "agent",
|
|
174
|
-
name: "Agent-managed authentication",
|
|
175
|
-
description: "Oh My Pi uses its existing local authentication and provider configuration.",
|
|
176
|
-
},
|
|
177
|
-
],
|
|
200
|
+
authMethods,
|
|
178
201
|
agentCapabilities: {
|
|
179
202
|
loadSession: true,
|
|
180
203
|
mcpCapabilities: {
|
|
@@ -195,7 +218,15 @@ export class AcpAgent implements Agent {
|
|
|
195
218
|
};
|
|
196
219
|
}
|
|
197
220
|
|
|
198
|
-
async authenticate(
|
|
221
|
+
async authenticate(params: AuthenticateRequest): Promise<AuthenticateResponse> {
|
|
222
|
+
// ACP spec: `methodId` must be one of the methods advertised by `initialize`.
|
|
223
|
+
// Reject anything else so malformed clients fail fast rather than appearing
|
|
224
|
+
// authenticated and surfacing a downstream model failure later.
|
|
225
|
+
const supportsTerminalAuth = this.#clientCapabilities?.auth?.terminal === true;
|
|
226
|
+
const validMethods = supportsTerminalAuth ? ["agent", "terminal"] : ["agent"];
|
|
227
|
+
if (!validMethods.includes(params.methodId)) {
|
|
228
|
+
throw new Error(`Unknown ACP auth method: ${params.methodId}`);
|
|
229
|
+
}
|
|
199
230
|
return {};
|
|
200
231
|
}
|
|
201
232
|
|
|
@@ -206,7 +237,7 @@ export class AcpAgent implements Agent {
|
|
|
206
237
|
sessionId: record.session.sessionId,
|
|
207
238
|
configOptions: this.#buildConfigOptions(record.session),
|
|
208
239
|
models: this.#buildModelState(record.session),
|
|
209
|
-
modes: this.#buildModeState(),
|
|
240
|
+
modes: this.#buildModeState(record.session),
|
|
210
241
|
};
|
|
211
242
|
this.#scheduleBootstrapUpdates(record.session.sessionId);
|
|
212
243
|
return response;
|
|
@@ -219,7 +250,7 @@ export class AcpAgent implements Agent {
|
|
|
219
250
|
const response: LoadSessionResponse = {
|
|
220
251
|
configOptions: this.#buildConfigOptions(record.session),
|
|
221
252
|
models: this.#buildModelState(record.session),
|
|
222
|
-
modes: this.#buildModeState(),
|
|
253
|
+
modes: this.#buildModeState(record.session),
|
|
223
254
|
};
|
|
224
255
|
this.#scheduleBootstrapUpdates(record.session.sessionId);
|
|
225
256
|
return response;
|
|
@@ -242,13 +273,13 @@ export class AcpAgent implements Agent {
|
|
|
242
273
|
};
|
|
243
274
|
}
|
|
244
275
|
|
|
245
|
-
async
|
|
276
|
+
async resumeSession(params: ResumeSessionRequest): Promise<ResumeSessionResponse> {
|
|
246
277
|
this.#assertAbsoluteCwd(params.cwd);
|
|
247
278
|
const record = await this.#resumeManagedSession(params.sessionId, params.cwd, params.mcpServers ?? []);
|
|
248
279
|
const response: ResumeSessionResponse = {
|
|
249
280
|
configOptions: this.#buildConfigOptions(record.session),
|
|
250
281
|
models: this.#buildModelState(record.session),
|
|
251
|
-
modes: this.#buildModeState(),
|
|
282
|
+
modes: this.#buildModeState(record.session),
|
|
252
283
|
};
|
|
253
284
|
this.#scheduleBootstrapUpdates(record.session.sessionId);
|
|
254
285
|
return response;
|
|
@@ -261,13 +292,13 @@ export class AcpAgent implements Agent {
|
|
|
261
292
|
sessionId: record.session.sessionId,
|
|
262
293
|
configOptions: this.#buildConfigOptions(record.session),
|
|
263
294
|
models: this.#buildModelState(record.session),
|
|
264
|
-
modes: this.#buildModeState(),
|
|
295
|
+
modes: this.#buildModeState(record.session),
|
|
265
296
|
};
|
|
266
297
|
this.#scheduleBootstrapUpdates(record.session.sessionId);
|
|
267
298
|
return response;
|
|
268
299
|
}
|
|
269
300
|
|
|
270
|
-
async
|
|
301
|
+
async closeSession(params: CloseSessionRequest): Promise<CloseSessionResponse> {
|
|
271
302
|
const record = this.#sessions.get(params.sessionId);
|
|
272
303
|
if (!record) {
|
|
273
304
|
return {};
|
|
@@ -278,12 +309,17 @@ export class AcpAgent implements Agent {
|
|
|
278
309
|
|
|
279
310
|
async setSessionMode(params: SetSessionModeRequest): Promise<SetSessionModeResponse> {
|
|
280
311
|
const record = this.#getSessionRecord(params.sessionId);
|
|
281
|
-
|
|
282
|
-
throw new Error(`Unsupported ACP mode: ${params.modeId}`);
|
|
283
|
-
}
|
|
312
|
+
this.#applyModeChange(record.session, params.modeId);
|
|
284
313
|
await this.#connection.sessionUpdate({
|
|
285
314
|
sessionId: record.session.sessionId,
|
|
286
|
-
update: this.#buildCurrentModeUpdate(),
|
|
315
|
+
update: this.#buildCurrentModeUpdate(record.session),
|
|
316
|
+
});
|
|
317
|
+
await this.#connection.sessionUpdate({
|
|
318
|
+
sessionId: record.session.sessionId,
|
|
319
|
+
update: {
|
|
320
|
+
sessionUpdate: "config_option_update",
|
|
321
|
+
configOptions: this.#buildConfigOptions(record.session),
|
|
322
|
+
},
|
|
287
323
|
});
|
|
288
324
|
return {};
|
|
289
325
|
}
|
|
@@ -296,9 +332,7 @@ export class AcpAgent implements Agent {
|
|
|
296
332
|
|
|
297
333
|
switch (params.configId) {
|
|
298
334
|
case MODE_CONFIG_ID:
|
|
299
|
-
|
|
300
|
-
throw new Error(`Unsupported ACP mode config value: ${params.value}`);
|
|
301
|
-
}
|
|
335
|
+
this.#applyModeChange(record.session, params.value);
|
|
302
336
|
break;
|
|
303
337
|
case MODEL_CONFIG_ID:
|
|
304
338
|
await this.#setModelById(record.session, params.value);
|
|
@@ -310,6 +344,16 @@ export class AcpAgent implements Agent {
|
|
|
310
344
|
throw new Error(`Unknown ACP config option: ${params.configId}`);
|
|
311
345
|
}
|
|
312
346
|
|
|
347
|
+
// When mode is changed via the generic config-option API, mirror the
|
|
348
|
+
// `current_mode_update` notification that `setSessionMode` emits so
|
|
349
|
+
// ACP clients tracking session-mode state see a consistent transition.
|
|
350
|
+
if (params.configId === MODE_CONFIG_ID) {
|
|
351
|
+
await this.#connection.sessionUpdate({
|
|
352
|
+
sessionId: record.session.sessionId,
|
|
353
|
+
update: this.#buildCurrentModeUpdate(record.session),
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
313
357
|
const configOptions = this.#buildConfigOptions(record.session);
|
|
314
358
|
await this.#connection.sessionUpdate({
|
|
315
359
|
sessionId: record.session.sessionId,
|
|
@@ -356,13 +400,94 @@ export class AcpAgent implements Agent {
|
|
|
356
400
|
void this.#handlePromptEvent(record, event);
|
|
357
401
|
});
|
|
358
402
|
|
|
359
|
-
record
|
|
403
|
+
this.#runPromptOrCommand(record, converted.text, converted.images).catch((error: unknown) => {
|
|
360
404
|
this.#finishPrompt(record, undefined, error);
|
|
361
405
|
});
|
|
362
406
|
|
|
363
407
|
return await pendingPrompt.promise;
|
|
364
408
|
}
|
|
365
409
|
|
|
410
|
+
async #runPromptOrCommand(record: ManagedSessionRecord, text: string, images: AgentImageContent[]): Promise<void> {
|
|
411
|
+
const skillResult = await this.#tryRunSkillCommand(record, text);
|
|
412
|
+
if (skillResult) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const builtinResult = await executeAcpBuiltinSlashCommand(text, {
|
|
417
|
+
session: record.session,
|
|
418
|
+
sessionManager: record.session.sessionManager,
|
|
419
|
+
settings: Settings.instance,
|
|
420
|
+
cwd: record.session.sessionManager.getCwd(),
|
|
421
|
+
output: output => this.#emitCommandOutput(record, output),
|
|
422
|
+
refreshCommands: () => this.#emitAvailableCommandsUpdate(record),
|
|
423
|
+
reloadPlugins: () => this.#reloadPluginState(record),
|
|
424
|
+
notifyTitleChanged: async () => {
|
|
425
|
+
await this.#connection.sessionUpdate({
|
|
426
|
+
sessionId: record.session.sessionId,
|
|
427
|
+
update: {
|
|
428
|
+
sessionUpdate: "session_info_update",
|
|
429
|
+
title: record.session.sessionName,
|
|
430
|
+
updatedAt: new Date().toISOString(),
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
},
|
|
434
|
+
notifyConfigChanged: async () => {
|
|
435
|
+
await this.#connection.sessionUpdate({
|
|
436
|
+
sessionId: record.session.sessionId,
|
|
437
|
+
update: {
|
|
438
|
+
sessionUpdate: "config_option_update",
|
|
439
|
+
configOptions: this.#buildConfigOptions(record.session),
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
},
|
|
443
|
+
});
|
|
444
|
+
if (builtinResult !== false) {
|
|
445
|
+
if ("prompt" in builtinResult) {
|
|
446
|
+
await record.session.prompt(builtinResult.prompt, { images });
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
const promptTurn = record.promptTurn;
|
|
450
|
+
this.#finishPrompt(record, {
|
|
451
|
+
stopReason: "end_turn",
|
|
452
|
+
usage: this.#buildTurnUsage(
|
|
453
|
+
promptTurn?.usageBaseline ??
|
|
454
|
+
this.#cloneUsageStatistics(record.session.sessionManager.getUsageStatistics()),
|
|
455
|
+
record.session.sessionManager.getUsageStatistics(),
|
|
456
|
+
),
|
|
457
|
+
userMessageId: promptTurn?.userMessageId,
|
|
458
|
+
});
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
await record.session.prompt(text, { images });
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
async #tryRunSkillCommand(record: ManagedSessionRecord, text: string): Promise<boolean> {
|
|
466
|
+
if (!text.startsWith("/skill:")) {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
if (!record.session.skillsSettings?.enableSkillCommands) {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
const spaceIndex = text.indexOf(" ");
|
|
473
|
+
const commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);
|
|
474
|
+
const args = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1).trim();
|
|
475
|
+
const skillName = commandName.slice("skill:".length);
|
|
476
|
+
const skill = record.session.skills.find(candidate => candidate.name === skillName);
|
|
477
|
+
if (!skill) {
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
const built = await buildSkillPromptMessage(skill, args);
|
|
481
|
+
await record.session.promptCustomMessage({
|
|
482
|
+
customType: SKILL_PROMPT_MESSAGE_TYPE,
|
|
483
|
+
content: built.message,
|
|
484
|
+
display: true,
|
|
485
|
+
details: built.details,
|
|
486
|
+
attribution: "user",
|
|
487
|
+
});
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
|
|
366
491
|
async cancel(params: { sessionId: string }): Promise<void> {
|
|
367
492
|
const record = this.#getSessionRecord(params.sessionId);
|
|
368
493
|
const promptTurn = record.promptTurn;
|
|
@@ -384,7 +509,7 @@ export class AcpAgent implements Agent {
|
|
|
384
509
|
|
|
385
510
|
async extMethod(method: string, params: { [key: string]: unknown }): Promise<{ [key: string]: unknown }> {
|
|
386
511
|
switch (method) {
|
|
387
|
-
case "
|
|
512
|
+
case "_omp/sessions/listAll": {
|
|
388
513
|
const limit = typeof params.limit === "number" ? Math.max(1, Math.min(5000, params.limit as number)) : 1000;
|
|
389
514
|
const sessions = await SessionManager.listAll();
|
|
390
515
|
const sorted = sessions.sort((l, r) => r.modified.getTime() - l.modified.getTime()).slice(0, limit);
|
|
@@ -393,7 +518,7 @@ export class AcpAgent implements Agent {
|
|
|
393
518
|
total: sessions.length,
|
|
394
519
|
};
|
|
395
520
|
}
|
|
396
|
-
case "
|
|
521
|
+
case "_omp/projects/list": {
|
|
397
522
|
const sessions = await SessionManager.listAll();
|
|
398
523
|
const buckets = new Map<
|
|
399
524
|
string,
|
|
@@ -421,7 +546,7 @@ export class AcpAgent implements Agent {
|
|
|
421
546
|
const projects = Array.from(buckets.values()).sort((a, b) => b.lastActivityAt - a.lastActivityAt);
|
|
422
547
|
return { projects, totalSessions: sessions.length };
|
|
423
548
|
}
|
|
424
|
-
case "
|
|
549
|
+
case "_omp/chats/byCwd": {
|
|
425
550
|
const cwd = typeof params.cwd === "string" ? (params.cwd as string) : undefined;
|
|
426
551
|
if (!cwd) throw new Error("cwd required");
|
|
427
552
|
const limit = typeof params.limit === "number" ? Math.max(1, Math.min(500, params.limit as number)) : 100;
|
|
@@ -429,20 +554,20 @@ export class AcpAgent implements Agent {
|
|
|
429
554
|
const sorted = sessions.sort((l, r) => r.modified.getTime() - l.modified.getTime()).slice(0, limit);
|
|
430
555
|
return { sessions: sorted.map(s => this.#toSessionInfo(s)) };
|
|
431
556
|
}
|
|
432
|
-
case "
|
|
557
|
+
case "_omp/usage": {
|
|
433
558
|
const [firstRecord] = this.#sessions.values();
|
|
434
559
|
const target = firstRecord?.session ?? this.#initialSession;
|
|
435
560
|
const reports = await target.fetchUsageReports();
|
|
436
561
|
return { reports: reports ?? [] };
|
|
437
562
|
}
|
|
438
|
-
case "
|
|
563
|
+
case "_omp/extensions": {
|
|
439
564
|
const cwd = typeof params.cwd === "string" ? (params.cwd as string) : undefined;
|
|
440
565
|
const sm = await Settings.init();
|
|
441
566
|
const disabledIds = (sm.get("disabledExtensions") as string[] | undefined) ?? [];
|
|
442
567
|
const extensions = await loadAllExtensions(cwd, disabledIds);
|
|
443
568
|
return { extensions: extensions as unknown as Array<{ [key: string]: unknown }> };
|
|
444
569
|
}
|
|
445
|
-
case "
|
|
570
|
+
case "_omp/extensions/toggle": {
|
|
446
571
|
const providerId = params.providerId;
|
|
447
572
|
if (typeof providerId !== "string") throw new Error("providerId required");
|
|
448
573
|
if (params.enabled === false) {
|
|
@@ -562,6 +687,7 @@ export class AcpAgent implements Agent {
|
|
|
562
687
|
|
|
563
688
|
async #registerPreparedSession(session: AgentSession, mcpServers: McpServer[]): Promise<ManagedSessionRecord> {
|
|
564
689
|
const record = this.#createManagedSessionRecord(session);
|
|
690
|
+
session.setClientBridge(createAcpClientBridge(this.#connection, session.sessionId, this.#clientCapabilities));
|
|
565
691
|
try {
|
|
566
692
|
await this.#configureExtensions(record);
|
|
567
693
|
await this.#configureMcpServers(record, mcpServers);
|
|
@@ -578,7 +704,8 @@ export class AcpAgent implements Agent {
|
|
|
578
704
|
session,
|
|
579
705
|
mcpManager: undefined,
|
|
580
706
|
promptTurn: undefined,
|
|
581
|
-
|
|
707
|
+
liveMessageId: undefined,
|
|
708
|
+
liveMessageProgress: undefined,
|
|
582
709
|
extensionsConfigured: false,
|
|
583
710
|
};
|
|
584
711
|
}
|
|
@@ -627,33 +754,61 @@ export class AcpAgent implements Agent {
|
|
|
627
754
|
return;
|
|
628
755
|
}
|
|
629
756
|
|
|
757
|
+
this.#prepareLiveAssistantMessage(record, event);
|
|
630
758
|
for (const notification of mapAgentSessionEventToAcpSessionUpdates(event, record.session.sessionId, {
|
|
631
759
|
getMessageId: message => this.#getLiveMessageId(record, message),
|
|
760
|
+
getMessageProgress: message => this.#getLiveMessageProgress(record, message),
|
|
761
|
+
cwd: record.session.sessionManager.getCwd(),
|
|
632
762
|
})) {
|
|
633
763
|
await this.#connection.sessionUpdate(notification);
|
|
634
764
|
}
|
|
765
|
+
this.#clearLiveAssistantMessageAfterEvent(record, event);
|
|
635
766
|
|
|
636
767
|
if (event.type === "agent_end") {
|
|
637
768
|
await this.#emitEndOfTurnUpdates(record);
|
|
638
769
|
this.#finishPrompt(record, {
|
|
639
|
-
stopReason: promptTurn.cancelRequested
|
|
770
|
+
stopReason: this.#resolveStopReason(event, promptTurn.cancelRequested),
|
|
640
771
|
usage: this.#buildTurnUsage(promptTurn.usageBaseline, record.session.sessionManager.getUsageStatistics()),
|
|
641
772
|
userMessageId: promptTurn.userMessageId,
|
|
642
773
|
});
|
|
643
774
|
}
|
|
644
775
|
}
|
|
645
776
|
|
|
777
|
+
#prepareLiveAssistantMessage(record: ManagedSessionRecord, event: AgentSessionEvent): void {
|
|
778
|
+
if (
|
|
779
|
+
(event.type === "message_start" || event.type === "message_update" || event.type === "message_end") &&
|
|
780
|
+
event.message.role === "assistant" &&
|
|
781
|
+
(event.type === "message_start" || !record.liveMessageId || !record.liveMessageProgress)
|
|
782
|
+
) {
|
|
783
|
+
record.liveMessageId = crypto.randomUUID();
|
|
784
|
+
record.liveMessageProgress = { textEmitted: false, thoughtEmitted: false };
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
#clearLiveAssistantMessageAfterEvent(record: ManagedSessionRecord, event: AgentSessionEvent): void {
|
|
789
|
+
if ((event.type === "message_end" && event.message.role === "assistant") || event.type === "agent_end") {
|
|
790
|
+
record.liveMessageId = undefined;
|
|
791
|
+
record.liveMessageProgress = undefined;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
646
795
|
#getLiveMessageId(record: ManagedSessionRecord, message: unknown): string | undefined {
|
|
647
796
|
if (typeof message !== "object" || message === null) {
|
|
648
797
|
return undefined;
|
|
649
798
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
799
|
+
record.liveMessageId ??= crypto.randomUUID();
|
|
800
|
+
return record.liveMessageId;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
#getLiveMessageProgress(
|
|
804
|
+
record: ManagedSessionRecord,
|
|
805
|
+
message: unknown,
|
|
806
|
+
): { textEmitted: boolean; thoughtEmitted: boolean } | undefined {
|
|
807
|
+
if (typeof message !== "object" || message === null) {
|
|
808
|
+
return undefined;
|
|
653
809
|
}
|
|
654
|
-
|
|
655
|
-
record.
|
|
656
|
-
return nextMessageId;
|
|
810
|
+
record.liveMessageProgress ??= { textEmitted: false, thoughtEmitted: false };
|
|
811
|
+
return record.liveMessageProgress;
|
|
657
812
|
}
|
|
658
813
|
|
|
659
814
|
#finishPrompt(record: ManagedSessionRecord, response?: PromptResponse, error?: unknown): void {
|
|
@@ -671,6 +826,48 @@ export class AcpAgent implements Agent {
|
|
|
671
826
|
promptTurn.resolve(response ?? { stopReason: "end_turn" });
|
|
672
827
|
}
|
|
673
828
|
|
|
829
|
+
#resolveStopReason(
|
|
830
|
+
event: Extract<AgentSessionEvent, { type: "agent_end" }>,
|
|
831
|
+
cancelRequested: boolean,
|
|
832
|
+
): PromptResponse["stopReason"] {
|
|
833
|
+
if (cancelRequested) {
|
|
834
|
+
return "cancelled";
|
|
835
|
+
}
|
|
836
|
+
const lastAssistant = [...event.messages]
|
|
837
|
+
.reverse()
|
|
838
|
+
.find((message): message is AssistantMessage => message.role === "assistant");
|
|
839
|
+
const reason = lastAssistant?.stopReason;
|
|
840
|
+
switch (reason) {
|
|
841
|
+
case "aborted":
|
|
842
|
+
return "cancelled";
|
|
843
|
+
case "length":
|
|
844
|
+
return "max_tokens";
|
|
845
|
+
case "error": {
|
|
846
|
+
const errorMessage = lastAssistant?.errorMessage ?? "";
|
|
847
|
+
if (/content[_ ]?filter|refus(al|ed)/i.test(errorMessage)) {
|
|
848
|
+
return "refusal";
|
|
849
|
+
}
|
|
850
|
+
return "end_turn";
|
|
851
|
+
}
|
|
852
|
+
default:
|
|
853
|
+
return "end_turn";
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
async #emitCommandOutput(record: ManagedSessionRecord, text: string): Promise<void> {
|
|
858
|
+
if (!text) {
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
await this.#connection.sessionUpdate({
|
|
862
|
+
sessionId: record.session.sessionId,
|
|
863
|
+
update: {
|
|
864
|
+
sessionUpdate: "agent_message_chunk",
|
|
865
|
+
content: { type: "text", text },
|
|
866
|
+
messageId: crypto.randomUUID(),
|
|
867
|
+
},
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
|
|
674
871
|
#assertAbsoluteCwd(cwd: string): void {
|
|
675
872
|
if (!path.isAbsolute(cwd)) {
|
|
676
873
|
throw new Error(`ACP cwd must be absolute: ${cwd}`);
|
|
@@ -691,6 +888,12 @@ export class AcpAgent implements Agent {
|
|
|
691
888
|
case "resource":
|
|
692
889
|
if ("text" in block.resource) {
|
|
693
890
|
textParts.push(block.resource.text);
|
|
891
|
+
} else if (typeof block.resource.mimeType === "string" && block.resource.mimeType.startsWith("image/")) {
|
|
892
|
+
// `embeddedContext: true` covers both text and blob resources, but
|
|
893
|
+
// blobs aren't directly consumable by the LLM. Route image blobs
|
|
894
|
+
// to the images array so the user's intent survives; everything
|
|
895
|
+
// else falls back to the URI placeholder below.
|
|
896
|
+
images.push({ type: "image", data: block.resource.blob, mimeType: block.resource.mimeType });
|
|
694
897
|
} else {
|
|
695
898
|
textParts.push(`[embedded resource: ${block.resource.uri}]`);
|
|
696
899
|
}
|
|
@@ -710,14 +913,20 @@ export class AcpAgent implements Agent {
|
|
|
710
913
|
}
|
|
711
914
|
|
|
712
915
|
#buildConfigOptions(session: AgentSession): SessionConfigOption[] {
|
|
916
|
+
const currentModeId = this.#getCurrentModeId(session);
|
|
917
|
+
const modeOptions = this.#getAvailableModes(session).map(mode => ({
|
|
918
|
+
value: mode.id,
|
|
919
|
+
name: mode.name,
|
|
920
|
+
description: mode.description,
|
|
921
|
+
}));
|
|
713
922
|
const configOptions: SessionConfigOption[] = [
|
|
714
923
|
{
|
|
715
924
|
id: MODE_CONFIG_ID,
|
|
716
925
|
name: "Mode",
|
|
717
926
|
category: "mode",
|
|
718
927
|
type: "select",
|
|
719
|
-
currentValue:
|
|
720
|
-
options:
|
|
928
|
+
currentValue: currentModeId,
|
|
929
|
+
options: modeOptions,
|
|
721
930
|
},
|
|
722
931
|
];
|
|
723
932
|
|
|
@@ -805,17 +1014,52 @@ export class AcpAgent implements Agent {
|
|
|
805
1014
|
return `${model.provider}/${model.id}`;
|
|
806
1015
|
}
|
|
807
1016
|
|
|
808
|
-
#
|
|
1017
|
+
#getAvailableModes(session: AgentSession): Array<{ id: string; name: string; description: string }> {
|
|
1018
|
+
const modes = [{ id: ACP_DEFAULT_MODE_ID, name: "Default", description: "Standard ACP headless mode" }];
|
|
1019
|
+
if (Settings.instance.get("plan.enabled")) {
|
|
1020
|
+
modes.push({
|
|
1021
|
+
id: ACP_PLAN_MODE_ID,
|
|
1022
|
+
name: "Plan",
|
|
1023
|
+
description: "Read-only planning mode that drafts a plan to a markdown file before any code changes",
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
void session;
|
|
1027
|
+
return modes;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
#getCurrentModeId(session: AgentSession): string {
|
|
1031
|
+
return session.getPlanModeState()?.enabled ? ACP_PLAN_MODE_ID : ACP_DEFAULT_MODE_ID;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
#applyModeChange(session: AgentSession, modeId: string): void {
|
|
1035
|
+
const availableModes = this.#getAvailableModes(session);
|
|
1036
|
+
if (!availableModes.some(mode => mode.id === modeId)) {
|
|
1037
|
+
throw new Error(`Unsupported ACP mode: ${modeId}`);
|
|
1038
|
+
}
|
|
1039
|
+
if (modeId === ACP_PLAN_MODE_ID) {
|
|
1040
|
+
const previous = session.getPlanModeState();
|
|
1041
|
+
session.setPlanModeState({
|
|
1042
|
+
enabled: true,
|
|
1043
|
+
planFilePath: previous?.planFilePath ?? DEFAULT_PLAN_FILE_URL,
|
|
1044
|
+
workflow: previous?.workflow ?? "parallel",
|
|
1045
|
+
reentry: previous !== undefined,
|
|
1046
|
+
});
|
|
1047
|
+
} else {
|
|
1048
|
+
session.setPlanModeState(undefined);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
#buildModeState(session: AgentSession): SessionModeState {
|
|
809
1053
|
return {
|
|
810
|
-
availableModes:
|
|
811
|
-
currentModeId:
|
|
1054
|
+
availableModes: this.#getAvailableModes(session),
|
|
1055
|
+
currentModeId: this.#getCurrentModeId(session),
|
|
812
1056
|
};
|
|
813
1057
|
}
|
|
814
1058
|
|
|
815
|
-
#buildCurrentModeUpdate(): SessionUpdate {
|
|
1059
|
+
#buildCurrentModeUpdate(session: AgentSession): SessionUpdate {
|
|
816
1060
|
return {
|
|
817
1061
|
sessionUpdate: "current_mode_update",
|
|
818
|
-
currentModeId:
|
|
1062
|
+
currentModeId: this.#getCurrentModeId(session),
|
|
819
1063
|
};
|
|
820
1064
|
}
|
|
821
1065
|
|
|
@@ -830,6 +1074,24 @@ export class AcpAgent implements Agent {
|
|
|
830
1074
|
commands.push(command);
|
|
831
1075
|
};
|
|
832
1076
|
|
|
1077
|
+
// Advertise in the order dispatch resolves them: ACP builtins first
|
|
1078
|
+
// (so core commands like `/model`, `/mcp`, `/todo` cannot be shadowed),
|
|
1079
|
+
// then skills, then custom/user commands, then file-based slash
|
|
1080
|
+
// commands. `appendCommand` dedupes by name so earlier entries win.
|
|
1081
|
+
for (const command of ACP_BUILTIN_SLASH_COMMANDS) {
|
|
1082
|
+
appendCommand(command);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
if (session.skillsSettings?.enableSkillCommands) {
|
|
1086
|
+
for (const skill of session.skills) {
|
|
1087
|
+
appendCommand({
|
|
1088
|
+
name: getSkillSlashCommandName(skill),
|
|
1089
|
+
description: skill.description || `Run ${skill.name} skill`,
|
|
1090
|
+
input: { hint: "arguments" },
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
|
|
833
1095
|
for (const command of session.customCommands) {
|
|
834
1096
|
appendCommand({
|
|
835
1097
|
name: command.command.name,
|
|
@@ -854,10 +1116,26 @@ export class AcpAgent implements Agent {
|
|
|
854
1116
|
cwd: session.cwd,
|
|
855
1117
|
title: session.title,
|
|
856
1118
|
updatedAt: session.modified.toISOString(),
|
|
1119
|
+
_meta: {
|
|
1120
|
+
messageCount: session.messageCount,
|
|
1121
|
+
size: session.size,
|
|
1122
|
+
},
|
|
857
1123
|
};
|
|
858
1124
|
}
|
|
859
1125
|
|
|
860
1126
|
#scheduleBootstrapUpdates(sessionId: string): void {
|
|
1127
|
+
// Delay the bootstrap so the client has time to handle the `session/new`
|
|
1128
|
+
// (or `session/load` / `session/resume`) RPC response and register the
|
|
1129
|
+
// new sessionId before we start firing notifications against it. Zed's
|
|
1130
|
+
// agent-client-protocol reader dispatches responses and notifications
|
|
1131
|
+
// to different async tasks; sending the first `available_commands_update`
|
|
1132
|
+
// from `setTimeout(0)` reliably loses the race against the response
|
|
1133
|
+
// handler and Zed logs `Received session notification for unknown
|
|
1134
|
+
// session` then drops the update — leaving the slash-command palette
|
|
1135
|
+
// empty (#1015 follow-up; see zed-industries/zed#55965 for the same
|
|
1136
|
+
// race biting other ACP agents). 50ms is invisible to the operator and
|
|
1137
|
+
// large enough that the response future has scheduled before our timer
|
|
1138
|
+
// fires on stdio-only transports.
|
|
861
1139
|
setTimeout(() => {
|
|
862
1140
|
if (this.#connection.signal.aborted) {
|
|
863
1141
|
return;
|
|
@@ -867,7 +1145,7 @@ export class AcpAgent implements Agent {
|
|
|
867
1145
|
return;
|
|
868
1146
|
}
|
|
869
1147
|
void this.#emitBootstrapUpdates(sessionId, record);
|
|
870
|
-
},
|
|
1148
|
+
}, 50);
|
|
871
1149
|
}
|
|
872
1150
|
|
|
873
1151
|
async #emitBootstrapUpdates(sessionId: string, record: ManagedSessionRecord): Promise<void> {
|
|
@@ -891,6 +1169,33 @@ export class AcpAgent implements Agent {
|
|
|
891
1169
|
});
|
|
892
1170
|
}
|
|
893
1171
|
|
|
1172
|
+
async #emitAvailableCommandsUpdate(record: ManagedSessionRecord): Promise<void> {
|
|
1173
|
+
await this.#connection.sessionUpdate({
|
|
1174
|
+
sessionId: record.session.sessionId,
|
|
1175
|
+
update: {
|
|
1176
|
+
sessionUpdate: "available_commands_update",
|
|
1177
|
+
availableCommands: await this.#buildAvailableCommands(record.session),
|
|
1178
|
+
},
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
/**
|
|
1183
|
+
* Reload plugin/registry state for an ACP session. Mirrors the interactive
|
|
1184
|
+
* `/reload-plugins` and `/move` flows: invalidates the plugin-roots cache,
|
|
1185
|
+
* resets the capability cache, refreshes the session's slash-command state,
|
|
1186
|
+
* then re-advertises commands so the client sees newly installed/disabled
|
|
1187
|
+
* plugins.
|
|
1188
|
+
*/
|
|
1189
|
+
async #reloadPluginState(record: ManagedSessionRecord): Promise<void> {
|
|
1190
|
+
const cwd = record.session.sessionManager.getCwd();
|
|
1191
|
+
const projectPath = await resolveActiveProjectRegistryPath(cwd);
|
|
1192
|
+
clearPluginRootsAndCaches(projectPath ? [projectPath] : undefined);
|
|
1193
|
+
resetCapabilities();
|
|
1194
|
+
const fileCommands = await loadSlashCommands({ cwd });
|
|
1195
|
+
record.session.setSlashCommands(fileCommands);
|
|
1196
|
+
await this.#emitAvailableCommandsUpdate(record);
|
|
1197
|
+
}
|
|
1198
|
+
|
|
894
1199
|
async #emitEndOfTurnUpdates(record: ManagedSessionRecord): Promise<void> {
|
|
895
1200
|
const sessionId = record.session.sessionId;
|
|
896
1201
|
|
|
@@ -981,14 +1286,15 @@ export class AcpAgent implements Agent {
|
|
|
981
1286
|
}
|
|
982
1287
|
|
|
983
1288
|
async #replaySessionHistory(record: ManagedSessionRecord): Promise<void> {
|
|
1289
|
+
const cwd = record.session.sessionManager.getCwd();
|
|
984
1290
|
for (const message of record.session.sessionManager.buildSessionContext().messages as ReplayableMessage[]) {
|
|
985
|
-
for (const notification of this.#messageToReplayNotifications(record.session.sessionId, message)) {
|
|
1291
|
+
for (const notification of this.#messageToReplayNotifications(record.session.sessionId, message, cwd)) {
|
|
986
1292
|
await this.#connection.sessionUpdate(notification);
|
|
987
1293
|
}
|
|
988
1294
|
}
|
|
989
1295
|
}
|
|
990
1296
|
|
|
991
|
-
#messageToReplayNotifications(sessionId: string, message: ReplayableMessage): SessionNotification[] {
|
|
1297
|
+
#messageToReplayNotifications(sessionId: string, message: ReplayableMessage, cwd: string): SessionNotification[] {
|
|
992
1298
|
if (message.role === "assistant") {
|
|
993
1299
|
return this.#replayAssistantMessage(sessionId, message);
|
|
994
1300
|
}
|
|
@@ -1010,7 +1316,7 @@ export class AcpAgent implements Agent {
|
|
|
1010
1316
|
typeof message.toolCallId === "string" &&
|
|
1011
1317
|
typeof message.toolName === "string"
|
|
1012
1318
|
) {
|
|
1013
|
-
return this.#replayToolResult(sessionId, {
|
|
1319
|
+
return this.#replayToolResult(sessionId, cwd, {
|
|
1014
1320
|
...message,
|
|
1015
1321
|
toolCallId: message.toolCallId,
|
|
1016
1322
|
toolName: message.toolName,
|
|
@@ -1102,6 +1408,7 @@ export class AcpAgent implements Agent {
|
|
|
1102
1408
|
|
|
1103
1409
|
#replayToolResult(
|
|
1104
1410
|
sessionId: string,
|
|
1411
|
+
cwd: string,
|
|
1105
1412
|
message: Required<Pick<ReplayableMessage, "toolCallId" | "toolName">> & ReplayableMessage,
|
|
1106
1413
|
): SessionNotification[] {
|
|
1107
1414
|
const args = this.#buildReplayToolArgs(message.details);
|
|
@@ -1123,8 +1430,8 @@ export class AcpAgent implements Agent {
|
|
|
1123
1430
|
},
|
|
1124
1431
|
};
|
|
1125
1432
|
return [
|
|
1126
|
-
...mapAgentSessionEventToAcpSessionUpdates(startEvent, sessionId),
|
|
1127
|
-
...mapAgentSessionEventToAcpSessionUpdates(endEvent, sessionId),
|
|
1433
|
+
...mapAgentSessionEventToAcpSessionUpdates(startEvent, sessionId, { cwd }),
|
|
1434
|
+
...mapAgentSessionEventToAcpSessionUpdates(endEvent, sessionId, { cwd }),
|
|
1128
1435
|
];
|
|
1129
1436
|
}
|
|
1130
1437
|
|