linkshell-cli 0.2.113 → 0.2.115
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/agent-workspace.d.ts +6 -0
- package/dist/cli/src/runtime/acp/agent-workspace.js +224 -41
- package/dist/cli/src/runtime/acp/agent-workspace.js.map +1 -1
- package/dist/cli/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/runtime/acp/agent-workspace.ts +229 -47
|
@@ -723,6 +723,73 @@ interface ProviderRuntimeCapabilities {
|
|
|
723
723
|
const ALL_REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh"] as const;
|
|
724
724
|
const ALL_PERMISSION_MODES = ["read_only", "workspace_write", "full_access"] as const;
|
|
725
725
|
const CODEX_COMMAND_NAMES = ["plan", "exit-plan", "compact", "clear", "status", "review", "subagents"] as const;
|
|
726
|
+
const CLAUDE_REMOTE_HIDDEN_COMMANDS = new Set([
|
|
727
|
+
"add-dir",
|
|
728
|
+
"agents",
|
|
729
|
+
"allowed-tools",
|
|
730
|
+
"android",
|
|
731
|
+
"app",
|
|
732
|
+
"bashes",
|
|
733
|
+
"branch",
|
|
734
|
+
"bug",
|
|
735
|
+
"checkpoint",
|
|
736
|
+
"chrome",
|
|
737
|
+
"color",
|
|
738
|
+
"config",
|
|
739
|
+
"continue",
|
|
740
|
+
"copy",
|
|
741
|
+
"cost",
|
|
742
|
+
"desktop",
|
|
743
|
+
"diff",
|
|
744
|
+
"doctor",
|
|
745
|
+
"exit",
|
|
746
|
+
"export",
|
|
747
|
+
"extra-usage",
|
|
748
|
+
"feedback",
|
|
749
|
+
"focus",
|
|
750
|
+
"fork",
|
|
751
|
+
"hooks",
|
|
752
|
+
"ide",
|
|
753
|
+
"init",
|
|
754
|
+
"install-github-app",
|
|
755
|
+
"install-slack-app",
|
|
756
|
+
"ios",
|
|
757
|
+
"keybindings",
|
|
758
|
+
"login",
|
|
759
|
+
"logout",
|
|
760
|
+
"mcp",
|
|
761
|
+
"memory",
|
|
762
|
+
"migrate-installer",
|
|
763
|
+
"mobile",
|
|
764
|
+
"model",
|
|
765
|
+
"passes",
|
|
766
|
+
"permissions",
|
|
767
|
+
"plugin",
|
|
768
|
+
"powerup",
|
|
769
|
+
"pr-comments",
|
|
770
|
+
"privacy-settings",
|
|
771
|
+
"quit",
|
|
772
|
+
"rc",
|
|
773
|
+
"release-notes",
|
|
774
|
+
"remote-control",
|
|
775
|
+
"remote-env",
|
|
776
|
+
"resume",
|
|
777
|
+
"rewind",
|
|
778
|
+
"settings",
|
|
779
|
+
"statusline",
|
|
780
|
+
"stickers",
|
|
781
|
+
"tasks",
|
|
782
|
+
"teleport",
|
|
783
|
+
"terminal-setup",
|
|
784
|
+
"theme",
|
|
785
|
+
"tp",
|
|
786
|
+
"tui",
|
|
787
|
+
"undo",
|
|
788
|
+
"upgrade",
|
|
789
|
+
"vim",
|
|
790
|
+
"voice",
|
|
791
|
+
"web-setup",
|
|
792
|
+
]);
|
|
726
793
|
const CLAUDE_BUILT_IN_COMMANDS: Array<{ name: string; description: string; argsMode?: AgentCommandDescriptor["argsMode"]; destructive?: boolean }> = [
|
|
727
794
|
{ name: "add-dir", description: "Add a working directory for file access", argsMode: "required" },
|
|
728
795
|
{ name: "agents", description: "Manage agent configurations", argsMode: "none" },
|
|
@@ -827,6 +894,10 @@ const CLAUDE_BUILT_IN_COMMANDS: Array<{ name: string; description: string; argsM
|
|
|
827
894
|
{ name: "web-setup", description: "Connect your GitHub account to Claude Code on the web", argsMode: "none" },
|
|
828
895
|
];
|
|
829
896
|
|
|
897
|
+
function isClaudeRemoteFriendlyCommand(name: string): boolean {
|
|
898
|
+
return !CLAUDE_REMOTE_HIDDEN_COMMANDS.has(name.replace(/^\/+/, ""));
|
|
899
|
+
}
|
|
900
|
+
|
|
830
901
|
function commandId(provider: AgentProvider, name: string, source: AgentCommandSource = "built_in"): string {
|
|
831
902
|
return `${provider}:${source}:${name.replace(/^\/+/, "")}`;
|
|
832
903
|
}
|
|
@@ -946,15 +1017,17 @@ function defaultProviderCommands(provider: AgentProvider, cwd: string, enabled:
|
|
|
946
1017
|
}));
|
|
947
1018
|
}
|
|
948
1019
|
if (provider === "claude") {
|
|
949
|
-
const builtIns = CLAUDE_BUILT_IN_COMMANDS
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1020
|
+
const builtIns = CLAUDE_BUILT_IN_COMMANDS
|
|
1021
|
+
.filter((entry) => isClaudeRemoteFriendlyCommand(entry.name))
|
|
1022
|
+
.map((entry) => makeCommand({
|
|
1023
|
+
provider,
|
|
1024
|
+
name: entry.name,
|
|
1025
|
+
description: entry.description,
|
|
1026
|
+
argsMode: entry.argsMode,
|
|
1027
|
+
destructive: entry.destructive,
|
|
1028
|
+
disabledReason,
|
|
1029
|
+
executionKind: "prompt",
|
|
1030
|
+
}));
|
|
958
1031
|
const custom = customClaudeCommands(cwd).map((command) => ({
|
|
959
1032
|
...command,
|
|
960
1033
|
disabledReason: command.disabledReason ?? disabledReason,
|
|
@@ -980,7 +1053,12 @@ function mergeCommands(...groups: Array<AgentCommandDescriptor[] | undefined>):
|
|
|
980
1053
|
for (const group of groups) {
|
|
981
1054
|
for (const command of group ?? []) {
|
|
982
1055
|
const key = `${command.provider ?? ""}:${command.name}`;
|
|
983
|
-
|
|
1056
|
+
const existing = map.get(key);
|
|
1057
|
+
map.set(key, {
|
|
1058
|
+
...existing,
|
|
1059
|
+
...command,
|
|
1060
|
+
disabledReason: command.disabledReason ?? existing?.disabledReason,
|
|
1061
|
+
});
|
|
984
1062
|
}
|
|
985
1063
|
}
|
|
986
1064
|
return [...map.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -998,6 +1076,7 @@ function runtimeCommands(provider: AgentProvider, value: unknown): AgentCommandD
|
|
|
998
1076
|
return commandsValue
|
|
999
1077
|
.map((entry) => {
|
|
1000
1078
|
if (typeof entry === "string") {
|
|
1079
|
+
if (provider === "claude" && !isClaudeRemoteFriendlyCommand(entry)) return undefined;
|
|
1001
1080
|
return makeCommand({
|
|
1002
1081
|
provider,
|
|
1003
1082
|
name: entry,
|
|
@@ -1010,6 +1089,7 @@ function runtimeCommands(provider: AgentProvider, value: unknown): AgentCommandD
|
|
|
1010
1089
|
const record = asRecord(entry);
|
|
1011
1090
|
const name = firstString(record, ["name", "command", "id"]);
|
|
1012
1091
|
if (!name) return undefined;
|
|
1092
|
+
if (provider === "claude" && !isClaudeRemoteFriendlyCommand(name)) return undefined;
|
|
1013
1093
|
return makeCommand({
|
|
1014
1094
|
provider,
|
|
1015
1095
|
name,
|
|
@@ -1127,6 +1207,7 @@ export class AgentWorkspaceProxy {
|
|
|
1127
1207
|
private error: string | undefined;
|
|
1128
1208
|
private activeConversationId: string | undefined;
|
|
1129
1209
|
private currentTurnIds = new Map<string, string>();
|
|
1210
|
+
private turnConversationIds = new Map<string, string>();
|
|
1130
1211
|
private conversations = new Map<string, AgentConversation>();
|
|
1131
1212
|
private conversationByAgentSessionId = new Map<string, string>();
|
|
1132
1213
|
private timelines = new Map<string, AgentTimelineItem[]>();
|
|
@@ -1136,6 +1217,7 @@ export class AgentWorkspaceProxy {
|
|
|
1136
1217
|
private permissionSources = new Map<string, string>();
|
|
1137
1218
|
private pendingStructuredInputs = new Map<string, { conversationId: string; input: AgentStructuredInput }>();
|
|
1138
1219
|
private structuredInputWaiters = new Map<string, PendingStructuredInputWaiter>();
|
|
1220
|
+
private itemConversationIds = new Map<string, string>();
|
|
1139
1221
|
private toolConversationIds = new Map<string, string>();
|
|
1140
1222
|
|
|
1141
1223
|
constructor(
|
|
@@ -1197,7 +1279,7 @@ export class AgentWorkspaceProxy {
|
|
|
1197
1279
|
sessionId: conversation?.agentSessionId,
|
|
1198
1280
|
turnId: this.currentTurnIds.get(payload.conversationId),
|
|
1199
1281
|
});
|
|
1200
|
-
this.
|
|
1282
|
+
this.forgetCurrentTurn(payload.conversationId);
|
|
1201
1283
|
this.updateConversationStatus(payload.conversationId, "idle");
|
|
1202
1284
|
this.emitStatus(payload.conversationId, "idle", "已停止");
|
|
1203
1285
|
break;
|
|
@@ -1689,7 +1771,7 @@ export class AgentWorkspaceProxy {
|
|
|
1689
1771
|
this.conversationByAgentSessionId.set(nextAgentSessionId, conversation.id);
|
|
1690
1772
|
}
|
|
1691
1773
|
const turnId = this.extractTurnId(result);
|
|
1692
|
-
if (turnId) this.
|
|
1774
|
+
if (turnId) this.rememberTurnConversationId(conversation.id, turnId);
|
|
1693
1775
|
if (conversation.status === "running" && protocol !== "codex-app-server") {
|
|
1694
1776
|
this.updateConversationStatus(conversation.id, "idle");
|
|
1695
1777
|
}
|
|
@@ -1927,7 +2009,7 @@ export class AgentWorkspaceProxy {
|
|
|
1927
2009
|
process.stderr.write(`[agent:v2] ${method} ${stringify(params).slice(0, 500)}\n`);
|
|
1928
2010
|
}
|
|
1929
2011
|
if (method === "initialized") {
|
|
1930
|
-
const conversationId = this.conversationIdFromParams(params) ?? this.
|
|
2012
|
+
const conversationId = this.conversationIdFromParams(params) ?? this.fallbackConversationId();
|
|
1931
2013
|
const provider = conversationId ? this.conversations.get(conversationId)?.provider : this.input.availableProviders[0];
|
|
1932
2014
|
if (provider) {
|
|
1933
2015
|
const commands = runtimeCommands(provider, params);
|
|
@@ -1953,7 +2035,7 @@ export class AgentWorkspaceProxy {
|
|
|
1953
2035
|
return;
|
|
1954
2036
|
}
|
|
1955
2037
|
|
|
1956
|
-
const conversationId = this.conversationIdFromParams(params) ?? this.
|
|
2038
|
+
const conversationId = this.conversationIdFromParams(params) ?? this.fallbackConversationId();
|
|
1957
2039
|
if (method === "item/tool/requestUserInput" || method === "tool/requestUserInput") {
|
|
1958
2040
|
this.handleStructuredInput(params);
|
|
1959
2041
|
return;
|
|
@@ -1978,14 +2060,14 @@ export class AgentWorkspaceProxy {
|
|
|
1978
2060
|
if (method === "turn/started") {
|
|
1979
2061
|
if (conversationId) {
|
|
1980
2062
|
const turnId = this.extractTurnId(params);
|
|
1981
|
-
if (turnId) this.
|
|
2063
|
+
if (turnId) this.rememberTurnConversationId(conversationId, turnId);
|
|
1982
2064
|
this.updateConversationStatus(conversationId, "running");
|
|
1983
2065
|
}
|
|
1984
2066
|
return;
|
|
1985
2067
|
}
|
|
1986
2068
|
if (method === "turn/completed") {
|
|
1987
2069
|
if (conversationId) {
|
|
1988
|
-
this.
|
|
2070
|
+
this.forgetCurrentTurn(conversationId, this.extractTurnId(params));
|
|
1989
2071
|
this.updateConversationStatus(conversationId, "idle");
|
|
1990
2072
|
}
|
|
1991
2073
|
return;
|
|
@@ -2044,7 +2126,7 @@ export class AgentWorkspaceProxy {
|
|
|
2044
2126
|
private handleAgentMessageDelta(params: unknown): void {
|
|
2045
2127
|
const raw = asRecord(params);
|
|
2046
2128
|
if (!raw) return;
|
|
2047
|
-
const conversationId = this.conversationIdFromParams(raw) ?? this.
|
|
2129
|
+
const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
|
|
2048
2130
|
if (!conversationId) return;
|
|
2049
2131
|
const itemId = firstString(raw, ["itemId", "id", "messageId"]) ?? id("msg");
|
|
2050
2132
|
const delta = firstString(raw, ["delta", "text", "content"]);
|
|
@@ -2068,7 +2150,7 @@ export class AgentWorkspaceProxy {
|
|
|
2068
2150
|
|
|
2069
2151
|
private handlePlanUpdated(params: unknown): void {
|
|
2070
2152
|
const raw = asRecord(params);
|
|
2071
|
-
const conversationId = this.conversationIdFromParams(raw) ?? this.
|
|
2153
|
+
const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
|
|
2072
2154
|
if (!conversationId) return;
|
|
2073
2155
|
const plan = Array.isArray(raw?.plan) ? raw.plan : [];
|
|
2074
2156
|
const steps = plan
|
|
@@ -2097,7 +2179,7 @@ export class AgentWorkspaceProxy {
|
|
|
2097
2179
|
private handlePlanDelta(params: unknown): void {
|
|
2098
2180
|
const raw = asRecord(params);
|
|
2099
2181
|
if (!raw) return;
|
|
2100
|
-
const conversationId = this.conversationIdFromParams(raw) ?? this.
|
|
2182
|
+
const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
|
|
2101
2183
|
if (!conversationId) return;
|
|
2102
2184
|
const itemId = firstString(raw, ["itemId", "id"]) ?? "plan";
|
|
2103
2185
|
const delta = firstString(raw, ["delta", "text"]);
|
|
@@ -2119,23 +2201,25 @@ export class AgentWorkspaceProxy {
|
|
|
2119
2201
|
private handleItemStarted(params: unknown): void {
|
|
2120
2202
|
const item = extractItem(params);
|
|
2121
2203
|
if (!item) return;
|
|
2122
|
-
const
|
|
2204
|
+
const sourceConversationId = this.conversationIdFromParams(params);
|
|
2205
|
+
const routedItem = sourceConversationId ? { ...item, conversationId: sourceConversationId } : item;
|
|
2206
|
+
const itemType = firstString(routedItem, ["type"]);
|
|
2123
2207
|
const normalizedItemType = normalizedIdentifier(itemType);
|
|
2124
2208
|
if (normalizedItemType === "agentmessage" || normalizedItemType === "assistantmessage") {
|
|
2125
|
-
this.handleCompletedMessageItem(
|
|
2209
|
+
this.handleCompletedMessageItem(routedItem, true);
|
|
2126
2210
|
return;
|
|
2127
2211
|
}
|
|
2128
2212
|
if (normalizedItemType === "plan") {
|
|
2129
|
-
this.handlePlanUpdated({ plan: [
|
|
2213
|
+
this.handlePlanUpdated({ plan: [routedItem], conversationId: sourceConversationId });
|
|
2130
2214
|
return;
|
|
2131
2215
|
}
|
|
2132
2216
|
if (isSubagentItemType(itemType)) {
|
|
2133
|
-
this.handleSubagentItem(
|
|
2217
|
+
this.handleSubagentItem(routedItem, "running", true);
|
|
2134
2218
|
return;
|
|
2135
2219
|
}
|
|
2136
|
-
if (this.handleSemanticSystemItem(
|
|
2137
|
-
const conversationId = this.conversationIdFromParams(
|
|
2138
|
-
const toolCall = this.toolCallFromItem(
|
|
2220
|
+
if (this.handleSemanticSystemItem(routedItem, "running", true)) return;
|
|
2221
|
+
const conversationId = this.conversationIdFromParams(routedItem) ?? this.fallbackConversationId();
|
|
2222
|
+
const toolCall = this.toolCallFromItem(routedItem, "running");
|
|
2139
2223
|
if (!conversationId || !toolCall) return;
|
|
2140
2224
|
this.toolConversationIds.set(toolCall.id, conversationId);
|
|
2141
2225
|
this.upsertTool(conversationId, toolCall);
|
|
@@ -2144,23 +2228,25 @@ export class AgentWorkspaceProxy {
|
|
|
2144
2228
|
private handleItemCompleted(params: unknown): void {
|
|
2145
2229
|
const item = extractItem(params);
|
|
2146
2230
|
if (!item) return;
|
|
2147
|
-
const
|
|
2231
|
+
const sourceConversationId = this.conversationIdFromParams(params);
|
|
2232
|
+
const routedItem = sourceConversationId ? { ...item, conversationId: sourceConversationId } : item;
|
|
2233
|
+
const itemType = firstString(routedItem, ["type"]);
|
|
2148
2234
|
const normalizedItemType = normalizedIdentifier(itemType);
|
|
2149
2235
|
if (normalizedItemType === "agentmessage" || normalizedItemType === "assistantmessage") {
|
|
2150
|
-
this.handleCompletedMessageItem(
|
|
2236
|
+
this.handleCompletedMessageItem(routedItem, false);
|
|
2151
2237
|
return;
|
|
2152
2238
|
}
|
|
2153
2239
|
if (normalizedItemType === "plan") {
|
|
2154
|
-
this.handlePlanDelta({ ...
|
|
2240
|
+
this.handlePlanDelta({ ...routedItem, delta: firstString(routedItem, ["text", "content", "message"]) });
|
|
2155
2241
|
return;
|
|
2156
2242
|
}
|
|
2157
2243
|
if (isSubagentItemType(itemType)) {
|
|
2158
|
-
this.handleSubagentItem(
|
|
2244
|
+
this.handleSubagentItem(routedItem, normalizeToolStatus(routedItem.status, true), false);
|
|
2159
2245
|
return;
|
|
2160
2246
|
}
|
|
2161
|
-
if (this.handleSemanticSystemItem(
|
|
2162
|
-
const conversationId = this.conversationIdFromParams(
|
|
2163
|
-
const toolCall = this.toolCallFromItem(
|
|
2247
|
+
if (this.handleSemanticSystemItem(routedItem, normalizeToolStatus(routedItem.status, true), false)) return;
|
|
2248
|
+
const conversationId = this.conversationIdFromParams(routedItem) ?? this.fallbackConversationId();
|
|
2249
|
+
const toolCall = this.toolCallFromItem(routedItem, normalizeToolStatus(routedItem.status, true));
|
|
2164
2250
|
if (!conversationId || !toolCall) return;
|
|
2165
2251
|
const bufferedOutput = this.toolOutputBuffers.get(toolCall.id);
|
|
2166
2252
|
if (bufferedOutput && !toolCall.output) toolCall.output = bufferedOutput;
|
|
@@ -2176,7 +2262,8 @@ export class AgentWorkspaceProxy {
|
|
|
2176
2262
|
const conversationId =
|
|
2177
2263
|
this.conversationIdFromParams(raw) ??
|
|
2178
2264
|
this.toolConversationIds.get(itemId) ??
|
|
2179
|
-
this.
|
|
2265
|
+
this.itemConversationIds.get(itemId) ??
|
|
2266
|
+
this.fallbackConversationId();
|
|
2180
2267
|
if (!conversationId) return;
|
|
2181
2268
|
const output = appendCapped(this.toolOutputBuffers.get(itemId), delta, 6000);
|
|
2182
2269
|
this.toolOutputBuffers.set(itemId, output);
|
|
@@ -2198,7 +2285,8 @@ export class AgentWorkspaceProxy {
|
|
|
2198
2285
|
const conversationId =
|
|
2199
2286
|
this.conversationIdFromParams(raw) ??
|
|
2200
2287
|
this.toolConversationIds.get(itemId) ??
|
|
2201
|
-
this.
|
|
2288
|
+
this.itemConversationIds.get(itemId) ??
|
|
2289
|
+
this.fallbackConversationId();
|
|
2202
2290
|
if (!conversationId) return;
|
|
2203
2291
|
const output =
|
|
2204
2292
|
extractDiffText(raw) ??
|
|
@@ -2217,7 +2305,7 @@ export class AgentWorkspaceProxy {
|
|
|
2217
2305
|
private handleTurnDiffUpdated(params: unknown): void {
|
|
2218
2306
|
const raw = asRecord(params);
|
|
2219
2307
|
if (!raw) return;
|
|
2220
|
-
const conversationId = this.conversationIdFromParams(raw) ?? this.
|
|
2308
|
+
const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
|
|
2221
2309
|
if (!conversationId) return;
|
|
2222
2310
|
const diff = extractDiffText(raw);
|
|
2223
2311
|
if (!diff) return;
|
|
@@ -2247,7 +2335,8 @@ export class AgentWorkspaceProxy {
|
|
|
2247
2335
|
const conversationId =
|
|
2248
2336
|
this.conversationIdFromParams(raw) ??
|
|
2249
2337
|
this.toolConversationIds.get(processId) ??
|
|
2250
|
-
this.
|
|
2338
|
+
this.itemConversationIds.get(processId) ??
|
|
2339
|
+
this.fallbackConversationId();
|
|
2251
2340
|
if (!conversationId) return;
|
|
2252
2341
|
const output = appendCapped(this.toolOutputBuffers.get(processId), delta, 6000);
|
|
2253
2342
|
this.toolOutputBuffers.set(processId, output);
|
|
@@ -2264,7 +2353,7 @@ export class AgentWorkspaceProxy {
|
|
|
2264
2353
|
|
|
2265
2354
|
private handleAutoApprovalReview(params: unknown, streaming: boolean): void {
|
|
2266
2355
|
const raw = asRecord(params) ?? {};
|
|
2267
|
-
const conversationId = this.conversationIdFromParams(raw) ?? this.
|
|
2356
|
+
const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
|
|
2268
2357
|
if (!conversationId) return;
|
|
2269
2358
|
const itemId = firstString(raw, ["itemId", "id", "reviewId"]) ?? "auto-approval-review";
|
|
2270
2359
|
const existing = this.findItem(conversationId, itemId);
|
|
@@ -2294,7 +2383,7 @@ export class AgentWorkspaceProxy {
|
|
|
2294
2383
|
}
|
|
2295
2384
|
|
|
2296
2385
|
private handleCompletedMessageItem(item: Record<string, unknown>, streaming: boolean): void {
|
|
2297
|
-
const conversationId = this.conversationIdFromParams(item) ?? this.
|
|
2386
|
+
const conversationId = this.conversationIdFromParams(item) ?? this.fallbackConversationId();
|
|
2298
2387
|
if (!conversationId) return;
|
|
2299
2388
|
const itemId = firstString(item, ["id"]) ?? id("msg");
|
|
2300
2389
|
const existing = this.findItem(conversationId, itemId);
|
|
@@ -2321,7 +2410,7 @@ export class AgentWorkspaceProxy {
|
|
|
2321
2410
|
firstString(raw, ["delta", "text", "content", "message"]) ??
|
|
2322
2411
|
firstString(nested, ["delta", "text", "content", "message"]);
|
|
2323
2412
|
if (!text) return;
|
|
2324
|
-
const conversationId = this.conversationIdFromParams(raw) ?? this.
|
|
2413
|
+
const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
|
|
2325
2414
|
if (!conversationId) return;
|
|
2326
2415
|
if (firstString(raw, ["toolName", "tool", "name"])) {
|
|
2327
2416
|
this.upsertTool(conversationId, {
|
|
@@ -2358,7 +2447,7 @@ export class AgentWorkspaceProxy {
|
|
|
2358
2447
|
): boolean {
|
|
2359
2448
|
const itemType = firstString(item, ["type"]);
|
|
2360
2449
|
const normalized = normalizedIdentifier(itemType);
|
|
2361
|
-
const conversationId = this.conversationIdFromParams(item) ?? this.
|
|
2450
|
+
const conversationId = this.conversationIdFromParams(item) ?? this.fallbackConversationId();
|
|
2362
2451
|
if (!conversationId) return false;
|
|
2363
2452
|
const itemId = firstString(item, ["id", "itemId"]) ?? id("item");
|
|
2364
2453
|
const existing = this.findItem(conversationId, itemId);
|
|
@@ -2413,7 +2502,7 @@ export class AgentWorkspaceProxy {
|
|
|
2413
2502
|
status: AgentToolCall["status"],
|
|
2414
2503
|
streaming: boolean,
|
|
2415
2504
|
): void {
|
|
2416
|
-
const conversationId = this.conversationIdFromParams(item) ?? this.
|
|
2505
|
+
const conversationId = this.conversationIdFromParams(item) ?? this.fallbackConversationId();
|
|
2417
2506
|
if (!conversationId) return;
|
|
2418
2507
|
const subagent = decodeSubagentAction(item, status);
|
|
2419
2508
|
if (!subagent) return;
|
|
@@ -2439,7 +2528,7 @@ export class AgentWorkspaceProxy {
|
|
|
2439
2528
|
|
|
2440
2529
|
private handleStructuredInput(params: unknown, waitForResponse = false): Promise<unknown> | void {
|
|
2441
2530
|
const raw = asRecord(params) ?? {};
|
|
2442
|
-
const conversationId = this.conversationIdFromParams(raw) ?? this.
|
|
2531
|
+
const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
|
|
2443
2532
|
const source = firstString(raw, ["method", "source", "requestMethod"]);
|
|
2444
2533
|
const formatResponse = source === "mcpServer/elicitation/request"
|
|
2445
2534
|
? formatMcpElicitationResponse
|
|
@@ -2510,7 +2599,7 @@ export class AgentWorkspaceProxy {
|
|
|
2510
2599
|
source?: string,
|
|
2511
2600
|
): Promise<unknown> | void {
|
|
2512
2601
|
const raw = asRecord(params) ?? {};
|
|
2513
|
-
const conversationId = this.conversationIdFromParams(raw) ?? this.
|
|
2602
|
+
const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
|
|
2514
2603
|
if (!conversationId) return waitForResponse ? Promise.resolve({ outcome: { outcome: "cancelled" } }) : undefined;
|
|
2515
2604
|
const requestId = firstString(raw, ["requestId", "id", "permissionId"]) ?? id("perm");
|
|
2516
2605
|
const rawToolCall = asRecord(raw.toolCall) ?? raw;
|
|
@@ -2646,6 +2735,7 @@ export class AgentWorkspaceProxy {
|
|
|
2646
2735
|
}
|
|
2647
2736
|
|
|
2648
2737
|
private addItem(conversationId: string, item: AgentTimelineItem): void {
|
|
2738
|
+
this.rememberItemConversationId(conversationId, item);
|
|
2649
2739
|
const timeline = this.timelines.get(conversationId) ?? [];
|
|
2650
2740
|
timeline.push(item);
|
|
2651
2741
|
timeline.sort((a, b) => a.createdAt - b.createdAt);
|
|
@@ -2657,6 +2747,7 @@ export class AgentWorkspaceProxy {
|
|
|
2657
2747
|
}
|
|
2658
2748
|
|
|
2659
2749
|
private upsertItem(conversationId: string, item: AgentTimelineItem): void {
|
|
2750
|
+
this.rememberItemConversationId(conversationId, item);
|
|
2660
2751
|
const timeline = this.timelines.get(conversationId) ?? [];
|
|
2661
2752
|
const index = timeline.findIndex((entry) => entry.id === item.id);
|
|
2662
2753
|
if (index >= 0) timeline[index] = item;
|
|
@@ -2684,6 +2775,8 @@ export class AgentWorkspaceProxy {
|
|
|
2684
2775
|
};
|
|
2685
2776
|
this.toolConversationIds.set(toolCall.id, conversationId);
|
|
2686
2777
|
this.toolConversationIds.set(nextToolCall.id, conversationId);
|
|
2778
|
+
this.itemConversationIds.set(toolCall.id, conversationId);
|
|
2779
|
+
this.itemConversationIds.set(nextToolCall.id, conversationId);
|
|
2687
2780
|
const kind: AgentTimelineKind = nextToolCall.name.includes("文件")
|
|
2688
2781
|
? "file_change"
|
|
2689
2782
|
: nextToolCall.name.includes("命令")
|
|
@@ -2783,6 +2876,22 @@ export class AgentWorkspaceProxy {
|
|
|
2783
2876
|
this.toolConversationIds.set(toolId, newId);
|
|
2784
2877
|
}
|
|
2785
2878
|
}
|
|
2879
|
+
for (const [turnId, conversationId] of this.turnConversationIds) {
|
|
2880
|
+
if (conversationId === oldId) {
|
|
2881
|
+
this.turnConversationIds.set(turnId, newId);
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
const currentTurnId = this.currentTurnIds.get(oldId);
|
|
2885
|
+
if (currentTurnId) {
|
|
2886
|
+
this.currentTurnIds.delete(oldId);
|
|
2887
|
+
this.currentTurnIds.set(newId, currentTurnId);
|
|
2888
|
+
this.turnConversationIds.set(currentTurnId, newId);
|
|
2889
|
+
}
|
|
2890
|
+
for (const [itemId, conversationId] of this.itemConversationIds) {
|
|
2891
|
+
if (conversationId === oldId) {
|
|
2892
|
+
this.itemConversationIds.set(itemId, newId);
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2786
2895
|
if (this.activeConversationId === oldId) {
|
|
2787
2896
|
this.activeConversationId = newId;
|
|
2788
2897
|
}
|
|
@@ -2861,13 +2970,86 @@ export class AgentWorkspaceProxy {
|
|
|
2861
2970
|
|
|
2862
2971
|
private conversationIdFromParams(params: unknown): string | undefined {
|
|
2863
2972
|
const raw = asRecord(params);
|
|
2864
|
-
|
|
2865
|
-
|
|
2973
|
+
if (!raw) return undefined;
|
|
2974
|
+
const directConversationId = firstString(raw, ["conversationId"]);
|
|
2975
|
+
if (directConversationId && this.conversations.has(directConversationId)) {
|
|
2976
|
+
return directConversationId;
|
|
2977
|
+
}
|
|
2866
2978
|
const threadId = firstString(raw, ["threadId", "sessionId", "agentSessionId"]);
|
|
2867
|
-
if (threadId)
|
|
2979
|
+
if (threadId) {
|
|
2980
|
+
const conversationId = this.conversationByAgentSessionId.get(threadId);
|
|
2981
|
+
if (conversationId) return conversationId;
|
|
2982
|
+
}
|
|
2983
|
+
const agentSessionId = this.extractSessionId(raw);
|
|
2984
|
+
if (agentSessionId) {
|
|
2985
|
+
const conversationId = this.conversationByAgentSessionId.get(agentSessionId);
|
|
2986
|
+
if (conversationId) return conversationId;
|
|
2987
|
+
}
|
|
2988
|
+
const turnId = this.extractTurnId(raw) ?? firstString(raw, ["turnId"]);
|
|
2989
|
+
if (turnId) {
|
|
2990
|
+
const conversationId = this.turnConversationIds.get(turnId);
|
|
2991
|
+
if (conversationId) return conversationId;
|
|
2992
|
+
}
|
|
2993
|
+
const itemId = firstString(raw, [
|
|
2994
|
+
"itemId",
|
|
2995
|
+
"messageId",
|
|
2996
|
+
"toolCallId",
|
|
2997
|
+
"processId",
|
|
2998
|
+
"callId",
|
|
2999
|
+
"requestId",
|
|
3000
|
+
"permissionId",
|
|
3001
|
+
"id",
|
|
3002
|
+
]);
|
|
3003
|
+
if (itemId) {
|
|
3004
|
+
const conversationId =
|
|
3005
|
+
this.itemConversationIds.get(itemId) ??
|
|
3006
|
+
this.toolConversationIds.get(itemId);
|
|
3007
|
+
if (conversationId) return conversationId;
|
|
3008
|
+
}
|
|
3009
|
+
for (const nested of [raw.params, raw.item, raw.message, raw.toolCall, raw.command, raw.event]) {
|
|
3010
|
+
const nestedRecord = asRecord(nested);
|
|
3011
|
+
if (!nestedRecord || nestedRecord === raw) continue;
|
|
3012
|
+
const conversationId = this.conversationIdFromParams(nestedRecord);
|
|
3013
|
+
if (conversationId) return conversationId;
|
|
3014
|
+
}
|
|
2868
3015
|
return undefined;
|
|
2869
3016
|
}
|
|
2870
3017
|
|
|
3018
|
+
private fallbackConversationId(): string | undefined {
|
|
3019
|
+
const liveConversations = [...this.conversations.values()].filter((conversation) =>
|
|
3020
|
+
conversation.status === "running" || conversation.status === "waiting_permission",
|
|
3021
|
+
);
|
|
3022
|
+
return liveConversations.length === 1 ? liveConversations[0]?.id : undefined;
|
|
3023
|
+
}
|
|
3024
|
+
|
|
3025
|
+
private rememberTurnConversationId(conversationId: string, turnId: string): void {
|
|
3026
|
+
this.currentTurnIds.set(conversationId, turnId);
|
|
3027
|
+
this.turnConversationIds.set(turnId, conversationId);
|
|
3028
|
+
}
|
|
3029
|
+
|
|
3030
|
+
private forgetCurrentTurn(conversationId: string, turnId?: string): void {
|
|
3031
|
+
const currentTurnId = this.currentTurnIds.get(conversationId);
|
|
3032
|
+
this.currentTurnIds.delete(conversationId);
|
|
3033
|
+
if (turnId) this.turnConversationIds.delete(turnId);
|
|
3034
|
+
if (currentTurnId && currentTurnId !== turnId) this.turnConversationIds.delete(currentTurnId);
|
|
3035
|
+
}
|
|
3036
|
+
|
|
3037
|
+
private rememberItemConversationId(conversationId: string, item: AgentTimelineItem): void {
|
|
3038
|
+
const keys = [
|
|
3039
|
+
item.id,
|
|
3040
|
+
item.itemId,
|
|
3041
|
+
item.toolCall?.id,
|
|
3042
|
+
item.permission?.requestId,
|
|
3043
|
+
item.structuredInput?.requestId,
|
|
3044
|
+
].filter((key): key is string => Boolean(key));
|
|
3045
|
+
for (const key of keys) {
|
|
3046
|
+
this.itemConversationIds.set(key, conversationId);
|
|
3047
|
+
}
|
|
3048
|
+
if (item.turnId) {
|
|
3049
|
+
this.turnConversationIds.set(item.turnId, conversationId);
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
|
|
2871
3053
|
private handleProviderExit(provider: AgentProvider, message: string): void {
|
|
2872
3054
|
this.clients.delete(provider);
|
|
2873
3055
|
this.agentProtocols.delete(provider);
|