linkshell-cli 0.2.109 → 0.2.111
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/dist/cli/src/runtime/acp/acp-client.d.ts +5 -0
- package/dist/cli/src/runtime/acp/acp-client.js +17 -0
- package/dist/cli/src/runtime/acp/acp-client.js.map +1 -1
- package/dist/cli/src/runtime/acp/agent-workspace.d.ts +3 -0
- package/dist/cli/src/runtime/acp/agent-workspace.js +445 -6
- package/dist/cli/src/runtime/acp/agent-workspace.js.map +1 -1
- package/dist/cli/src/runtime/acp/claude-sdk-client.d.ts +1 -0
- package/dist/cli/src/runtime/acp/claude-sdk-client.js.map +1 -1
- package/dist/cli/src/runtime/acp/claude-stream-json-client.d.ts +1 -0
- package/dist/cli/src/runtime/acp/claude-stream-json-client.js +2 -0
- package/dist/cli/src/runtime/acp/claude-stream-json-client.js.map +1 -1
- package/dist/cli/src/runtime/bridge-session.js +2 -1
- package/dist/cli/src/runtime/bridge-session.js.map +1 -1
- package/dist/cli/tsconfig.tsbuildinfo +1 -1
- package/dist/shared-protocol/src/index.d.ts +1573 -448
- package/dist/shared-protocol/src/index.js +34 -0
- package/dist/shared-protocol/src/index.js.map +1 -1
- package/package.json +3 -3
- package/src/runtime/acp/acp-client.ts +20 -0
- package/src/runtime/acp/agent-workspace.ts +521 -5
- package/src/runtime/acp/claude-sdk-client.ts +1 -0
- package/src/runtime/acp/claude-stream-json-client.ts +2 -0
- package/src/runtime/bridge-session.ts +2 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentFraming, AgentProtocol } from "./provider-resolver.js";
|
|
2
2
|
type AgentPermissionMode = "read_only" | "workspace_write" | "full_access";
|
|
3
|
+
type AgentCollaborationMode = "default" | "plan";
|
|
3
4
|
export declare class AcpClient {
|
|
4
5
|
private readonly transport;
|
|
5
6
|
private readonly protocol;
|
|
@@ -31,6 +32,7 @@ export declare class AcpClient {
|
|
|
31
32
|
model?: string;
|
|
32
33
|
reasoningEffort?: string;
|
|
33
34
|
permissionMode?: AgentPermissionMode;
|
|
35
|
+
collaborationMode?: AgentCollaborationMode;
|
|
34
36
|
cwd: string;
|
|
35
37
|
}): Promise<unknown>;
|
|
36
38
|
cancel(input: {
|
|
@@ -43,6 +45,9 @@ export declare class AcpClient {
|
|
|
43
45
|
outcome: "allow" | "deny";
|
|
44
46
|
optionId?: string;
|
|
45
47
|
}): void;
|
|
48
|
+
compact(input: {
|
|
49
|
+
sessionId: string;
|
|
50
|
+
}): Promise<unknown>;
|
|
46
51
|
stop(): void;
|
|
47
52
|
}
|
|
48
53
|
export {};
|
|
@@ -96,11 +96,22 @@ export class AcpClient {
|
|
|
96
96
|
}
|
|
97
97
|
prompt(input) {
|
|
98
98
|
if (this.protocol === "codex-app-server") {
|
|
99
|
+
const collaborationSettings = {
|
|
100
|
+
...(input.model ? { model: input.model } : {}),
|
|
101
|
+
...(input.reasoningEffort ? { reasoning_effort: input.reasoningEffort } : {}),
|
|
102
|
+
};
|
|
103
|
+
const collaborationMode = input.collaborationMode && input.collaborationMode !== "default"
|
|
104
|
+
? {
|
|
105
|
+
mode: input.collaborationMode,
|
|
106
|
+
...(Object.keys(collaborationSettings).length > 0 ? { settings: collaborationSettings } : {}),
|
|
107
|
+
}
|
|
108
|
+
: undefined;
|
|
99
109
|
return this.transport.request("turn/start", {
|
|
100
110
|
threadId: input.sessionId,
|
|
101
111
|
model: input.model,
|
|
102
112
|
effort: input.reasoningEffort,
|
|
103
113
|
permissions: permissionsForMode(input.permissionMode, input.cwd),
|
|
114
|
+
collaborationMode,
|
|
104
115
|
input: input.content.map((block) => {
|
|
105
116
|
const raw = block;
|
|
106
117
|
if (raw.type === "image" && raw.data) {
|
|
@@ -136,6 +147,12 @@ export class AcpClient {
|
|
|
136
147
|
respondPermission(input) {
|
|
137
148
|
this.transport.notify("session/respond_permission", input);
|
|
138
149
|
}
|
|
150
|
+
compact(input) {
|
|
151
|
+
if (this.protocol === "codex-app-server") {
|
|
152
|
+
return this.transport.request("thread/compact/start", { threadId: input.sessionId });
|
|
153
|
+
}
|
|
154
|
+
return Promise.reject(new Error("Native compact is not supported by this provider."));
|
|
155
|
+
}
|
|
139
156
|
stop() {
|
|
140
157
|
this.transport.stop();
|
|
141
158
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acp-client.js","sourceRoot":"","sources":["../../../../../src/runtime/acp/acp-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"acp-client.js","sourceRoot":"","sources":["../../../../../src/runtime/acp/acp-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -40,6 +40,9 @@ export declare class AgentWorkspaceProxy {
|
|
|
40
40
|
private openConversation;
|
|
41
41
|
private openFailure;
|
|
42
42
|
private sendPrompt;
|
|
43
|
+
private commandForConversation;
|
|
44
|
+
private executeCommand;
|
|
45
|
+
private executeNativeCommand;
|
|
43
46
|
private handleRequest;
|
|
44
47
|
private handleNotification;
|
|
45
48
|
private handleAgentMessageDelta;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { basename, join, relative } from "node:path";
|
|
2
4
|
import { createEnvelope, parseTypedPayload, } from "@linkshell/protocol";
|
|
3
5
|
import { AcpClient } from "./acp-client.js";
|
|
4
6
|
import { ClaudeSdkClient } from "./claude-sdk-client.js";
|
|
@@ -509,6 +511,222 @@ function providerLabel(provider) {
|
|
|
509
511
|
}
|
|
510
512
|
const ALL_REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh"];
|
|
511
513
|
const ALL_PERMISSION_MODES = ["read_only", "workspace_write", "full_access"];
|
|
514
|
+
const CODEX_COMMAND_NAMES = ["plan", "exit-plan", "compact", "clear", "status", "review", "subagents"];
|
|
515
|
+
const CLAUDE_BUILT_IN_COMMANDS = [
|
|
516
|
+
{ name: "add-dir", description: "Add additional working directories" },
|
|
517
|
+
{ name: "agents", description: "Manage subagents" },
|
|
518
|
+
{ name: "bug", description: "Report a Claude Code bug" },
|
|
519
|
+
{ name: "clear", description: "Clear conversation context", argsMode: "none", destructive: true },
|
|
520
|
+
{ name: "compact", description: "Compact conversation history" },
|
|
521
|
+
{ name: "config", description: "Open configuration" },
|
|
522
|
+
{ name: "cost", description: "Show usage cost" },
|
|
523
|
+
{ name: "doctor", description: "Check Claude Code health" },
|
|
524
|
+
{ name: "exit", description: "Exit Claude Code", argsMode: "none", destructive: true },
|
|
525
|
+
{ name: "export", description: "Export conversation" },
|
|
526
|
+
{ name: "help", description: "Show help" },
|
|
527
|
+
{ name: "ide", description: "Manage IDE integration" },
|
|
528
|
+
{ name: "init", description: "Create or update CLAUDE.md" },
|
|
529
|
+
{ name: "login", description: "Sign in" },
|
|
530
|
+
{ name: "logout", description: "Sign out" },
|
|
531
|
+
{ name: "mcp", description: "Manage MCP servers" },
|
|
532
|
+
{ name: "memory", description: "Edit memory files" },
|
|
533
|
+
{ name: "model", description: "Switch model" },
|
|
534
|
+
{ name: "permissions", description: "Manage permissions" },
|
|
535
|
+
{ name: "pr-comments", description: "Fetch PR comments" },
|
|
536
|
+
{ name: "release-notes", description: "Show release notes" },
|
|
537
|
+
{ name: "resume", description: "Resume a conversation" },
|
|
538
|
+
{ name: "review", description: "Review local changes" },
|
|
539
|
+
{ name: "security-review", description: "Run a security review" },
|
|
540
|
+
{ name: "status", description: "Show status" },
|
|
541
|
+
{ name: "statusline", description: "Configure status line" },
|
|
542
|
+
{ name: "terminal-setup", description: "Configure terminal integration" },
|
|
543
|
+
{ name: "upgrade", description: "Upgrade Claude Code" },
|
|
544
|
+
{ name: "vim", description: "Toggle vim mode" },
|
|
545
|
+
];
|
|
546
|
+
function commandId(provider, name, source = "built_in") {
|
|
547
|
+
return `${provider}:${source}:${name.replace(/^\/+/, "")}`;
|
|
548
|
+
}
|
|
549
|
+
function commandTitle(name) {
|
|
550
|
+
return `/${name.replace(/^\/+/, "")}`;
|
|
551
|
+
}
|
|
552
|
+
function makeCommand(input) {
|
|
553
|
+
const cleanName = input.name.replace(/^\/+/, "");
|
|
554
|
+
const source = input.source ?? "built_in";
|
|
555
|
+
return {
|
|
556
|
+
id: commandId(input.provider, cleanName, source),
|
|
557
|
+
name: cleanName,
|
|
558
|
+
title: commandTitle(cleanName),
|
|
559
|
+
description: input.description,
|
|
560
|
+
provider: input.provider,
|
|
561
|
+
source,
|
|
562
|
+
category: input.category,
|
|
563
|
+
argsMode: input.argsMode ?? "optional",
|
|
564
|
+
requiresIdle: input.requiresIdle,
|
|
565
|
+
destructive: input.destructive,
|
|
566
|
+
disabledReason: input.disabledReason,
|
|
567
|
+
executionKind: input.executionKind ?? "prompt",
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function commandFromMarkdownFile(provider, root, filePath, source) {
|
|
571
|
+
if (!filePath.endsWith(".md"))
|
|
572
|
+
return undefined;
|
|
573
|
+
const rel = relative(root, filePath).replace(/\\/g, "/").replace(/\.md$/i, "");
|
|
574
|
+
const name = rel.split("/").filter(Boolean).join(":");
|
|
575
|
+
if (!name)
|
|
576
|
+
return undefined;
|
|
577
|
+
let description;
|
|
578
|
+
try {
|
|
579
|
+
const text = readFileSync(filePath, "utf8");
|
|
580
|
+
description = text.split(/\r?\n/).map((line) => line.trim()).find((line) => line && !line.startsWith("---"))?.slice(0, 160);
|
|
581
|
+
}
|
|
582
|
+
catch {
|
|
583
|
+
description = undefined;
|
|
584
|
+
}
|
|
585
|
+
return makeCommand({
|
|
586
|
+
provider,
|
|
587
|
+
name,
|
|
588
|
+
description: description || "Custom Claude command",
|
|
589
|
+
source,
|
|
590
|
+
category: source === "project" ? "Project commands" : "User commands",
|
|
591
|
+
argsMode: "raw",
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
function walkMarkdownCommands(provider, root, source) {
|
|
595
|
+
if (!existsSync(root))
|
|
596
|
+
return [];
|
|
597
|
+
const result = [];
|
|
598
|
+
const walk = (dir) => {
|
|
599
|
+
let entries = [];
|
|
600
|
+
try {
|
|
601
|
+
entries = readdirSync(dir);
|
|
602
|
+
}
|
|
603
|
+
catch {
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
for (const entry of entries) {
|
|
607
|
+
const path = join(dir, entry);
|
|
608
|
+
let stat;
|
|
609
|
+
try {
|
|
610
|
+
stat = statSync(path);
|
|
611
|
+
}
|
|
612
|
+
catch {
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
if (stat.isDirectory())
|
|
616
|
+
walk(path);
|
|
617
|
+
else if (stat.isFile()) {
|
|
618
|
+
const command = commandFromMarkdownFile(provider, root, path, source);
|
|
619
|
+
if (command)
|
|
620
|
+
result.push(command);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
walk(root);
|
|
625
|
+
return result;
|
|
626
|
+
}
|
|
627
|
+
function customClaudeCommands(cwd) {
|
|
628
|
+
const projectCommands = walkMarkdownCommands("claude", join(cwd, ".claude", "commands"), "project");
|
|
629
|
+
const userCommands = walkMarkdownCommands("claude", join(homedir(), ".claude", "commands"), "user");
|
|
630
|
+
return [...projectCommands, ...userCommands];
|
|
631
|
+
}
|
|
632
|
+
function defaultProviderCommands(provider, cwd, enabled) {
|
|
633
|
+
const disabledReason = enabled ? undefined : `${providerLabel(provider)} 未安装或启动失败`;
|
|
634
|
+
if (provider === "codex") {
|
|
635
|
+
return CODEX_COMMAND_NAMES.map((name) => makeCommand({
|
|
636
|
+
provider,
|
|
637
|
+
name,
|
|
638
|
+
source: "linkshell",
|
|
639
|
+
category: name === "plan" || name === "exit-plan" ? "Modes" : "Codex",
|
|
640
|
+
description: {
|
|
641
|
+
"plan": "Enter Codex plan mode",
|
|
642
|
+
"exit-plan": "Exit Codex plan mode",
|
|
643
|
+
compact: "Compact the current thread",
|
|
644
|
+
clear: "Start a fresh Codex thread",
|
|
645
|
+
status: "Show LinkShell agent status",
|
|
646
|
+
review: "Ask Codex to review local changes",
|
|
647
|
+
subagents: "Insert a delegation prompt",
|
|
648
|
+
}[name],
|
|
649
|
+
argsMode: name === "review" || name === "subagents" ? "optional" : "none",
|
|
650
|
+
destructive: name === "clear",
|
|
651
|
+
disabledReason,
|
|
652
|
+
executionKind: name === "review" || name === "subagents" ? "prompt" : "native",
|
|
653
|
+
}));
|
|
654
|
+
}
|
|
655
|
+
if (provider === "claude") {
|
|
656
|
+
const builtIns = CLAUDE_BUILT_IN_COMMANDS.map((entry) => makeCommand({
|
|
657
|
+
provider,
|
|
658
|
+
name: entry.name,
|
|
659
|
+
description: entry.description,
|
|
660
|
+
argsMode: entry.argsMode,
|
|
661
|
+
destructive: entry.destructive,
|
|
662
|
+
disabledReason,
|
|
663
|
+
executionKind: "prompt",
|
|
664
|
+
}));
|
|
665
|
+
const custom = customClaudeCommands(cwd).map((command) => ({
|
|
666
|
+
...command,
|
|
667
|
+
disabledReason: command.disabledReason ?? disabledReason,
|
|
668
|
+
}));
|
|
669
|
+
return [...builtIns, ...custom];
|
|
670
|
+
}
|
|
671
|
+
return [
|
|
672
|
+
makeCommand({
|
|
673
|
+
provider,
|
|
674
|
+
name: "status",
|
|
675
|
+
source: "linkshell",
|
|
676
|
+
category: "LinkShell",
|
|
677
|
+
description: "Show LinkShell agent status",
|
|
678
|
+
argsMode: "none",
|
|
679
|
+
disabledReason,
|
|
680
|
+
executionKind: "native",
|
|
681
|
+
}),
|
|
682
|
+
];
|
|
683
|
+
}
|
|
684
|
+
function mergeCommands(...groups) {
|
|
685
|
+
const map = new Map();
|
|
686
|
+
for (const group of groups) {
|
|
687
|
+
for (const command of group ?? []) {
|
|
688
|
+
const key = `${command.provider ?? ""}:${command.name}`;
|
|
689
|
+
map.set(key, { ...map.get(key), ...command });
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
return [...map.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
693
|
+
}
|
|
694
|
+
function runtimeCommands(provider, value) {
|
|
695
|
+
const raw = asRecord(value);
|
|
696
|
+
const commandsValue = Array.isArray(value) ? value :
|
|
697
|
+
Array.isArray(raw?.commands) ? raw.commands :
|
|
698
|
+
Array.isArray(raw?.slashCommands) ? raw.slashCommands :
|
|
699
|
+
Array.isArray(raw?.slash_commands) ? raw.slash_commands :
|
|
700
|
+
Array.isArray(raw?.available_commands) ? raw.available_commands :
|
|
701
|
+
[];
|
|
702
|
+
return commandsValue
|
|
703
|
+
.map((entry) => {
|
|
704
|
+
if (typeof entry === "string") {
|
|
705
|
+
return makeCommand({
|
|
706
|
+
provider,
|
|
707
|
+
name: entry,
|
|
708
|
+
description: undefined,
|
|
709
|
+
source: "built_in",
|
|
710
|
+
argsMode: "raw",
|
|
711
|
+
executionKind: "prompt",
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
const record = asRecord(entry);
|
|
715
|
+
const name = firstString(record, ["name", "command", "id"]);
|
|
716
|
+
if (!name)
|
|
717
|
+
return undefined;
|
|
718
|
+
return makeCommand({
|
|
719
|
+
provider,
|
|
720
|
+
name,
|
|
721
|
+
description: firstString(record, ["description", "summary"]),
|
|
722
|
+
source: "built_in",
|
|
723
|
+
category: firstString(record, ["category", "group"]),
|
|
724
|
+
argsMode: "raw",
|
|
725
|
+
executionKind: "prompt",
|
|
726
|
+
});
|
|
727
|
+
})
|
|
728
|
+
.filter((entry) => Boolean(entry));
|
|
729
|
+
}
|
|
512
730
|
function parseModelListCapabilities(value) {
|
|
513
731
|
const raw = asRecord(value);
|
|
514
732
|
const modelsValue = Array.isArray(value) ? value :
|
|
@@ -641,6 +859,11 @@ export class AgentWorkspaceProxy {
|
|
|
641
859
|
await this.sendPrompt(payload);
|
|
642
860
|
break;
|
|
643
861
|
}
|
|
862
|
+
case "agent.v2.command.execute": {
|
|
863
|
+
const payload = parseTypedPayload("agent.v2.command.execute", envelope.payload);
|
|
864
|
+
await this.executeCommand(payload);
|
|
865
|
+
break;
|
|
866
|
+
}
|
|
644
867
|
case "agent.v2.cancel": {
|
|
645
868
|
const payload = parseTypedPayload("agent.v2.cancel", envelope.payload);
|
|
646
869
|
const conversation = this.conversations.get(payload.conversationId);
|
|
@@ -820,6 +1043,7 @@ export class AgentWorkspaceProxy {
|
|
|
820
1043
|
model: remote.model ?? existing?.model,
|
|
821
1044
|
reasoningEffort: existing?.reasoningEffort,
|
|
822
1045
|
permissionMode: existing?.permissionMode,
|
|
1046
|
+
collaborationMode: existing?.collaborationMode,
|
|
823
1047
|
status: existing?.status ?? "idle",
|
|
824
1048
|
archived: existing?.archived ?? false,
|
|
825
1049
|
lastMessagePreview: existing?.lastMessagePreview,
|
|
@@ -848,6 +1072,8 @@ export class AgentWorkspaceProxy {
|
|
|
848
1072
|
const isClaudeFallback = protocol === "claude-stream-json";
|
|
849
1073
|
const supportsPermission = enabled && !isClaudeFallback;
|
|
850
1074
|
const supportsReasoningEffort = enabled && !isClaudeFallback;
|
|
1075
|
+
const commands = mergeCommands(defaultProviderCommands(provider, this.input.cwd, enabled), runtimeCapabilities?.commands);
|
|
1076
|
+
const currentMode = [...this.conversations.values()].find((conversation) => conversation.provider === provider)?.collaborationMode;
|
|
851
1077
|
return {
|
|
852
1078
|
id: provider,
|
|
853
1079
|
label: providerLabel(provider),
|
|
@@ -863,6 +1089,12 @@ export class AgentWorkspaceProxy {
|
|
|
863
1089
|
? runtimeCapabilities?.reasoningEfforts ?? [...ALL_REASONING_EFFORTS]
|
|
864
1090
|
: [],
|
|
865
1091
|
permissionModes: supportsPermission ? [...ALL_PERMISSION_MODES] : [],
|
|
1092
|
+
commands,
|
|
1093
|
+
modes: runtimeCapabilities?.modes ?? (provider === "codex" ? [
|
|
1094
|
+
{ id: "default", title: "Default", description: "Run normal implementation turns" },
|
|
1095
|
+
{ id: "plan", title: "Plan", description: "Discuss and produce an implementation plan first" },
|
|
1096
|
+
] : []),
|
|
1097
|
+
currentMode,
|
|
866
1098
|
features: {
|
|
867
1099
|
images: supportsImages,
|
|
868
1100
|
permissions: supportsPermission,
|
|
@@ -911,7 +1143,7 @@ export class AgentWorkspaceProxy {
|
|
|
911
1143
|
let agentSessionId = payload.agentSessionId;
|
|
912
1144
|
let existingConversation = (payload.conversationId ? this.conversations.get(payload.conversationId) : undefined) ??
|
|
913
1145
|
(agentSessionId ? this.conversations.get(this.conversationByAgentSessionId.get(agentSessionId) ?? "") : undefined);
|
|
914
|
-
if (existingConversation && existingConversation.status !== "error") {
|
|
1146
|
+
if (existingConversation && existingConversation.status !== "error" && existingConversation.agentSessionId) {
|
|
915
1147
|
if (payload.conversationId && existingConversation.id !== payload.conversationId) {
|
|
916
1148
|
existingConversation = this.adoptConversationId(existingConversation.id, payload.conversationId);
|
|
917
1149
|
}
|
|
@@ -943,6 +1175,7 @@ export class AgentWorkspaceProxy {
|
|
|
943
1175
|
model: payload.model ?? existingConversation?.model,
|
|
944
1176
|
reasoningEffort: payload.reasoningEffort ?? existingConversation?.reasoningEffort,
|
|
945
1177
|
permissionMode: payload.permissionMode ?? existingConversation?.permissionMode,
|
|
1178
|
+
collaborationMode: payload.collaborationMode ?? existingConversation?.collaborationMode,
|
|
946
1179
|
status: "idle",
|
|
947
1180
|
archived: existingConversation?.archived ?? false,
|
|
948
1181
|
lastMessagePreview: existingConversation?.status === "error" ? undefined : existingConversation?.lastMessagePreview,
|
|
@@ -976,6 +1209,7 @@ export class AgentWorkspaceProxy {
|
|
|
976
1209
|
model: payload.model,
|
|
977
1210
|
reasoningEffort: payload.reasoningEffort,
|
|
978
1211
|
permissionMode: payload.permissionMode,
|
|
1212
|
+
collaborationMode: payload.collaborationMode,
|
|
979
1213
|
status: "error",
|
|
980
1214
|
archived: false,
|
|
981
1215
|
lastMessagePreview: message,
|
|
@@ -1001,11 +1235,29 @@ export class AgentWorkspaceProxy {
|
|
|
1001
1235
|
async sendPrompt(payload) {
|
|
1002
1236
|
const conversation = this.conversations.get(payload.conversationId) ??
|
|
1003
1237
|
await this.openConversation({ conversationId: payload.conversationId });
|
|
1004
|
-
if (!conversation
|
|
1238
|
+
if (!conversation)
|
|
1239
|
+
return;
|
|
1240
|
+
if (!conversation.agentSessionId) {
|
|
1241
|
+
this.addItem(payload.conversationId, {
|
|
1242
|
+
id: id("error"),
|
|
1243
|
+
conversationId: payload.conversationId,
|
|
1244
|
+
type: "error",
|
|
1245
|
+
error: "Agent session 尚未就绪,消息没有发送。请重新打开对话后再试。",
|
|
1246
|
+
createdAt: Date.now(),
|
|
1247
|
+
});
|
|
1005
1248
|
return;
|
|
1249
|
+
}
|
|
1006
1250
|
const client = this.clientForProvider(conversation.provider);
|
|
1007
|
-
if (!client)
|
|
1251
|
+
if (!client) {
|
|
1252
|
+
this.addItem(conversation.id, {
|
|
1253
|
+
id: id("error"),
|
|
1254
|
+
conversationId: conversation.id,
|
|
1255
|
+
type: "error",
|
|
1256
|
+
error: `${providerLabel(conversation.provider)} 未连接,消息没有发送。`,
|
|
1257
|
+
createdAt: Date.now(),
|
|
1258
|
+
});
|
|
1008
1259
|
return;
|
|
1260
|
+
}
|
|
1009
1261
|
const protocol = this.protocolForProvider(conversation.provider);
|
|
1010
1262
|
if (payload.contentBlocks.some((block) => block.type === "image") && !protocolSupportsImages(protocol)) {
|
|
1011
1263
|
conversation.status = "idle";
|
|
@@ -1023,6 +1275,7 @@ export class AgentWorkspaceProxy {
|
|
|
1023
1275
|
conversation.model = payload.model ?? conversation.model;
|
|
1024
1276
|
conversation.reasoningEffort = payload.reasoningEffort ?? conversation.reasoningEffort;
|
|
1025
1277
|
conversation.permissionMode = payload.permissionMode ?? conversation.permissionMode;
|
|
1278
|
+
conversation.collaborationMode = payload.collaborationMode ?? conversation.collaborationMode;
|
|
1026
1279
|
conversation.status = "running";
|
|
1027
1280
|
conversation.lastActivityAt = Date.now();
|
|
1028
1281
|
this.activeConversationId = conversation.id;
|
|
@@ -1045,6 +1298,7 @@ export class AgentWorkspaceProxy {
|
|
|
1045
1298
|
model: payload.model,
|
|
1046
1299
|
reasoningEffort: payload.reasoningEffort,
|
|
1047
1300
|
permissionMode: payload.permissionMode,
|
|
1301
|
+
collaborationMode: payload.collaborationMode ?? conversation.collaborationMode,
|
|
1048
1302
|
cwd: conversation.cwd,
|
|
1049
1303
|
});
|
|
1050
1304
|
const nextAgentSessionId = this.extractSessionId(result);
|
|
@@ -1073,6 +1327,176 @@ export class AgentWorkspaceProxy {
|
|
|
1073
1327
|
});
|
|
1074
1328
|
}
|
|
1075
1329
|
}
|
|
1330
|
+
commandForConversation(conversation, commandId) {
|
|
1331
|
+
const runtimeCapabilities = this.providerCapabilities.get(conversation.provider);
|
|
1332
|
+
const commands = mergeCommands(defaultProviderCommands(conversation.provider, conversation.cwd, true), runtimeCapabilities?.commands);
|
|
1333
|
+
return commands.find((command) => command.id === commandId ||
|
|
1334
|
+
command.name === commandId ||
|
|
1335
|
+
`/${command.name}` === commandId);
|
|
1336
|
+
}
|
|
1337
|
+
async executeCommand(payload) {
|
|
1338
|
+
const conversation = this.conversations.get(payload.conversationId) ??
|
|
1339
|
+
await this.openConversation({ conversationId: payload.conversationId });
|
|
1340
|
+
if (!conversation)
|
|
1341
|
+
return;
|
|
1342
|
+
if (!conversation.agentSessionId) {
|
|
1343
|
+
this.addItem(payload.conversationId, {
|
|
1344
|
+
id: id("error"),
|
|
1345
|
+
conversationId: payload.conversationId,
|
|
1346
|
+
type: "error",
|
|
1347
|
+
error: "Agent session 尚未就绪,命令没有执行。请重新打开对话后再试。",
|
|
1348
|
+
createdAt: Date.now(),
|
|
1349
|
+
});
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
const command = this.commandForConversation(conversation, payload.commandId);
|
|
1353
|
+
if (!command) {
|
|
1354
|
+
this.addItem(conversation.id, {
|
|
1355
|
+
id: id("error"),
|
|
1356
|
+
conversationId: conversation.id,
|
|
1357
|
+
type: "error",
|
|
1358
|
+
error: `未知命令:${payload.commandId}`,
|
|
1359
|
+
createdAt: Date.now(),
|
|
1360
|
+
});
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
if (command.disabledReason) {
|
|
1364
|
+
this.addItem(conversation.id, {
|
|
1365
|
+
id: id("error"),
|
|
1366
|
+
conversationId: conversation.id,
|
|
1367
|
+
type: "error",
|
|
1368
|
+
error: command.disabledReason,
|
|
1369
|
+
createdAt: Date.now(),
|
|
1370
|
+
});
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
const rawText = payload.rawText?.trim() || `/${command.name}${payload.args?.trim() ? ` ${payload.args.trim()}` : ""}`;
|
|
1374
|
+
if (command.executionKind === "prompt") {
|
|
1375
|
+
await this.sendPrompt({
|
|
1376
|
+
conversationId: conversation.id,
|
|
1377
|
+
clientMessageId: payload.clientMessageId,
|
|
1378
|
+
contentBlocks: [{ type: "text", text: rawText }],
|
|
1379
|
+
model: conversation.model,
|
|
1380
|
+
reasoningEffort: conversation.reasoningEffort,
|
|
1381
|
+
permissionMode: conversation.permissionMode,
|
|
1382
|
+
collaborationMode: conversation.collaborationMode,
|
|
1383
|
+
});
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
this.addItem(conversation.id, {
|
|
1387
|
+
id: payload.clientMessageId,
|
|
1388
|
+
conversationId: conversation.id,
|
|
1389
|
+
type: "message",
|
|
1390
|
+
kind: "chat",
|
|
1391
|
+
role: "user",
|
|
1392
|
+
content: [{ type: "text", text: rawText }],
|
|
1393
|
+
text: rawText,
|
|
1394
|
+
metadata: { commandId: command.id, commandExecutionKind: command.executionKind },
|
|
1395
|
+
createdAt: Date.now(),
|
|
1396
|
+
});
|
|
1397
|
+
if (command.executionKind === "local_ui") {
|
|
1398
|
+
this.emitStatus(conversation.id, "idle", `${command.title} 已由移动端处理。`);
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
await this.executeNativeCommand(conversation, command, payload.args?.trim());
|
|
1402
|
+
}
|
|
1403
|
+
async executeNativeCommand(conversation, command, args) {
|
|
1404
|
+
const client = this.clientForProvider(conversation.provider);
|
|
1405
|
+
const now = Date.now();
|
|
1406
|
+
try {
|
|
1407
|
+
if (command.name === "status") {
|
|
1408
|
+
this.emitStatus(conversation.id, conversation.status, `${providerLabel(conversation.provider)} · ${conversation.collaborationMode === "plan" ? "Plan mode" : "Default mode"} · ${conversation.cwd}`);
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
if (conversation.provider !== "codex") {
|
|
1412
|
+
this.addItem(conversation.id, {
|
|
1413
|
+
id: id("error"),
|
|
1414
|
+
conversationId: conversation.id,
|
|
1415
|
+
type: "error",
|
|
1416
|
+
error: `${command.title} 暂无 ${providerLabel(conversation.provider)} 原生实现。`,
|
|
1417
|
+
createdAt: now,
|
|
1418
|
+
});
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
if (command.name === "plan" || command.name === "exit-plan") {
|
|
1422
|
+
conversation.collaborationMode = command.name === "plan" ? "plan" : "default";
|
|
1423
|
+
conversation.status = "idle";
|
|
1424
|
+
conversation.lastMessagePreview = command.name === "plan" ? "已进入 Plan mode" : "已退出 Plan mode";
|
|
1425
|
+
conversation.lastActivityAt = now;
|
|
1426
|
+
this.emitConversation(conversation);
|
|
1427
|
+
this.sendCapabilities();
|
|
1428
|
+
this.emitStatus(conversation.id, "idle", command.name === "plan"
|
|
1429
|
+
? "已进入 Plan mode。下一条消息会先制定计划。"
|
|
1430
|
+
: "已退出 Plan mode。");
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
if (command.name === "compact") {
|
|
1434
|
+
if (!(client instanceof AcpClient))
|
|
1435
|
+
throw new Error("当前 Codex runtime 不支持原生 compact。");
|
|
1436
|
+
conversation.status = "running";
|
|
1437
|
+
this.emitConversation(conversation);
|
|
1438
|
+
this.addItem(conversation.id, {
|
|
1439
|
+
id: id("compact"),
|
|
1440
|
+
conversationId: conversation.id,
|
|
1441
|
+
type: "status",
|
|
1442
|
+
kind: "context_compaction",
|
|
1443
|
+
text: "正在压缩上下文",
|
|
1444
|
+
status: "running",
|
|
1445
|
+
isStreaming: true,
|
|
1446
|
+
createdAt: now,
|
|
1447
|
+
});
|
|
1448
|
+
await client.compact({ sessionId: conversation.agentSessionId });
|
|
1449
|
+
this.updateConversationStatus(conversation.id, "idle", "上下文压缩完成");
|
|
1450
|
+
this.emitStatus(conversation.id, "idle", "上下文压缩完成。");
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
if (command.name === "clear") {
|
|
1454
|
+
if (!client)
|
|
1455
|
+
throw new Error("Agent provider 不在线。");
|
|
1456
|
+
const result = await client.newSession({ cwd: conversation.cwd });
|
|
1457
|
+
const nextAgentSessionId = this.extractSessionId(result) ?? id("agent-session");
|
|
1458
|
+
if (conversation.agentSessionId)
|
|
1459
|
+
this.conversationByAgentSessionId.delete(conversation.agentSessionId);
|
|
1460
|
+
conversation.agentSessionId = nextAgentSessionId;
|
|
1461
|
+
conversation.collaborationMode = "default";
|
|
1462
|
+
conversation.status = "idle";
|
|
1463
|
+
conversation.lastMessagePreview = "上下文已重置";
|
|
1464
|
+
conversation.lastActivityAt = now;
|
|
1465
|
+
this.conversationByAgentSessionId.set(nextAgentSessionId, conversation.id);
|
|
1466
|
+
this.timelines.set(conversation.id, []);
|
|
1467
|
+
this.emitConversation(conversation);
|
|
1468
|
+
this.emitStatus(conversation.id, "idle", "上下文已重置,已创建新的 Codex thread。");
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
if (command.name === "review" || command.name === "subagents") {
|
|
1472
|
+
const prompt = command.name === "review"
|
|
1473
|
+
? args || "Review the current local changes."
|
|
1474
|
+
: args || "Run subagents for distinct tasks in parallel when useful, then synthesize the results.";
|
|
1475
|
+
await this.sendPrompt({
|
|
1476
|
+
conversationId: conversation.id,
|
|
1477
|
+
clientMessageId: id(command.name),
|
|
1478
|
+
contentBlocks: [{ type: "text", text: prompt }],
|
|
1479
|
+
model: conversation.model,
|
|
1480
|
+
reasoningEffort: conversation.reasoningEffort,
|
|
1481
|
+
permissionMode: conversation.permissionMode,
|
|
1482
|
+
collaborationMode: conversation.collaborationMode,
|
|
1483
|
+
});
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
throw new Error(`命令暂未实现:/${command.name}`);
|
|
1487
|
+
}
|
|
1488
|
+
catch (error) {
|
|
1489
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1490
|
+
this.updateConversationStatus(conversation.id, "error", message);
|
|
1491
|
+
this.addItem(conversation.id, {
|
|
1492
|
+
id: id("error"),
|
|
1493
|
+
conversationId: conversation.id,
|
|
1494
|
+
type: "error",
|
|
1495
|
+
error: message,
|
|
1496
|
+
createdAt: Date.now(),
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1076
1500
|
handleRequest(method, params) {
|
|
1077
1501
|
if (method === "item/tool/requestUserInput" || method === "tool/requestUserInput") {
|
|
1078
1502
|
return this.handleStructuredInput(params, true);
|
|
@@ -1092,8 +1516,23 @@ export class AgentWorkspaceProxy {
|
|
|
1092
1516
|
if (this.input.verbose) {
|
|
1093
1517
|
process.stderr.write(`[agent:v2] ${method} ${stringify(params).slice(0, 500)}\n`);
|
|
1094
1518
|
}
|
|
1095
|
-
if (method === "initialized"
|
|
1096
|
-
|
|
1519
|
+
if (method === "initialized") {
|
|
1520
|
+
const conversationId = this.conversationIdFromParams(params) ?? this.activeConversationId;
|
|
1521
|
+
const provider = conversationId ? this.conversations.get(conversationId)?.provider : this.input.availableProviders[0];
|
|
1522
|
+
if (provider) {
|
|
1523
|
+
const commands = runtimeCommands(provider, params);
|
|
1524
|
+
if (commands.length > 0) {
|
|
1525
|
+
const existing = this.providerCapabilities.get(provider);
|
|
1526
|
+
this.providerCapabilities.set(provider, {
|
|
1527
|
+
...(existing ?? {}),
|
|
1528
|
+
commands: mergeCommands(existing?.commands, commands),
|
|
1529
|
+
});
|
|
1530
|
+
this.sendCapabilities();
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
if (method.startsWith("account/") ||
|
|
1097
1536
|
method.startsWith("mcpServer/startupStatus/") ||
|
|
1098
1537
|
method === "thread/status/changed" ||
|
|
1099
1538
|
method === "thread/tokenUsage/updated" ||
|