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.
@@ -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.map((entry) => makeCommand({
950
- provider,
951
- name: entry.name,
952
- description: entry.description,
953
- argsMode: entry.argsMode,
954
- destructive: entry.destructive,
955
- disabledReason,
956
- executionKind: "prompt",
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
- map.set(key, { ...map.get(key), ...command });
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.currentTurnIds.delete(payload.conversationId);
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.currentTurnIds.set(conversation.id, turnId);
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.activeConversationId;
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.activeConversationId;
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.currentTurnIds.set(conversationId, turnId);
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.currentTurnIds.delete(conversationId);
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.activeConversationId;
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.activeConversationId;
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.activeConversationId;
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 itemType = firstString(item, ["type"]);
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(item, true);
2209
+ this.handleCompletedMessageItem(routedItem, true);
2126
2210
  return;
2127
2211
  }
2128
2212
  if (normalizedItemType === "plan") {
2129
- this.handlePlanUpdated({ plan: [item] });
2213
+ this.handlePlanUpdated({ plan: [routedItem], conversationId: sourceConversationId });
2130
2214
  return;
2131
2215
  }
2132
2216
  if (isSubagentItemType(itemType)) {
2133
- this.handleSubagentItem(item, "running", true);
2217
+ this.handleSubagentItem(routedItem, "running", true);
2134
2218
  return;
2135
2219
  }
2136
- if (this.handleSemanticSystemItem(item, "running", true)) return;
2137
- const conversationId = this.conversationIdFromParams(item) ?? this.activeConversationId;
2138
- const toolCall = this.toolCallFromItem(item, "running");
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 itemType = firstString(item, ["type"]);
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(item, false);
2236
+ this.handleCompletedMessageItem(routedItem, false);
2151
2237
  return;
2152
2238
  }
2153
2239
  if (normalizedItemType === "plan") {
2154
- this.handlePlanDelta({ ...item, delta: firstString(item, ["text", "content", "message"]) });
2240
+ this.handlePlanDelta({ ...routedItem, delta: firstString(routedItem, ["text", "content", "message"]) });
2155
2241
  return;
2156
2242
  }
2157
2243
  if (isSubagentItemType(itemType)) {
2158
- this.handleSubagentItem(item, normalizeToolStatus(item.status, true), false);
2244
+ this.handleSubagentItem(routedItem, normalizeToolStatus(routedItem.status, true), false);
2159
2245
  return;
2160
2246
  }
2161
- if (this.handleSemanticSystemItem(item, normalizeToolStatus(item.status, true), false)) return;
2162
- const conversationId = this.conversationIdFromParams(item) ?? this.activeConversationId;
2163
- const toolCall = this.toolCallFromItem(item, normalizeToolStatus(item.status, true));
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.activeConversationId;
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.activeConversationId;
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.activeConversationId;
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.activeConversationId;
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.activeConversationId;
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.activeConversationId;
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.activeConversationId;
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.activeConversationId;
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.activeConversationId;
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.activeConversationId;
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.activeConversationId;
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
- const agentSessionId = this.extractSessionId(raw);
2865
- if (agentSessionId) return this.conversationByAgentSessionId.get(agentSessionId);
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) return this.conversationByAgentSessionId.get(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);