linkshell-cli 0.2.114 → 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.
@@ -10,6 +10,7 @@ export declare class AgentWorkspaceProxy {
10
10
  private error;
11
11
  private activeConversationId;
12
12
  private currentTurnIds;
13
+ private turnConversationIds;
13
14
  private conversations;
14
15
  private conversationByAgentSessionId;
15
16
  private timelines;
@@ -19,6 +20,7 @@ export declare class AgentWorkspaceProxy {
19
20
  private permissionSources;
20
21
  private pendingStructuredInputs;
21
22
  private structuredInputWaiters;
23
+ private itemConversationIds;
22
24
  private toolConversationIds;
23
25
  constructor(input: {
24
26
  sessionId: string;
@@ -81,6 +83,10 @@ export declare class AgentWorkspaceProxy {
81
83
  private updateConversationStatus;
82
84
  private sendSnapshot;
83
85
  private conversationIdFromParams;
86
+ private fallbackConversationId;
87
+ private rememberTurnConversationId;
88
+ private forgetCurrentTurn;
89
+ private rememberItemConversationId;
84
90
  private handleProviderExit;
85
91
  private cancelPendingPermissions;
86
92
  private extractSessionId;
@@ -967,6 +967,7 @@ export class AgentWorkspaceProxy {
967
967
  error;
968
968
  activeConversationId;
969
969
  currentTurnIds = new Map();
970
+ turnConversationIds = new Map();
970
971
  conversations = new Map();
971
972
  conversationByAgentSessionId = new Map();
972
973
  timelines = new Map();
@@ -976,6 +977,7 @@ export class AgentWorkspaceProxy {
976
977
  permissionSources = new Map();
977
978
  pendingStructuredInputs = new Map();
978
979
  structuredInputWaiters = new Map();
980
+ itemConversationIds = new Map();
979
981
  toolConversationIds = new Map();
980
982
  constructor(input) {
981
983
  this.input = input;
@@ -1026,7 +1028,7 @@ export class AgentWorkspaceProxy {
1026
1028
  sessionId: conversation?.agentSessionId,
1027
1029
  turnId: this.currentTurnIds.get(payload.conversationId),
1028
1030
  });
1029
- this.currentTurnIds.delete(payload.conversationId);
1031
+ this.forgetCurrentTurn(payload.conversationId);
1030
1032
  this.updateConversationStatus(payload.conversationId, "idle");
1031
1033
  this.emitStatus(payload.conversationId, "idle", "已停止");
1032
1034
  break;
@@ -1463,7 +1465,7 @@ export class AgentWorkspaceProxy {
1463
1465
  }
1464
1466
  const turnId = this.extractTurnId(result);
1465
1467
  if (turnId)
1466
- this.currentTurnIds.set(conversation.id, turnId);
1468
+ this.rememberTurnConversationId(conversation.id, turnId);
1467
1469
  if (conversation.status === "running" && protocol !== "codex-app-server") {
1468
1470
  this.updateConversationStatus(conversation.id, "idle");
1469
1471
  }
@@ -1670,7 +1672,7 @@ export class AgentWorkspaceProxy {
1670
1672
  process.stderr.write(`[agent:v2] ${method} ${stringify(params).slice(0, 500)}\n`);
1671
1673
  }
1672
1674
  if (method === "initialized") {
1673
- const conversationId = this.conversationIdFromParams(params) ?? this.activeConversationId;
1675
+ const conversationId = this.conversationIdFromParams(params) ?? this.fallbackConversationId();
1674
1676
  const provider = conversationId ? this.conversations.get(conversationId)?.provider : this.input.availableProviders[0];
1675
1677
  if (provider) {
1676
1678
  const commands = runtimeCommands(provider, params);
@@ -1693,7 +1695,7 @@ export class AgentWorkspaceProxy {
1693
1695
  method === "mcpServer/oauthLogin/completed") {
1694
1696
  return;
1695
1697
  }
1696
- const conversationId = this.conversationIdFromParams(params) ?? this.activeConversationId;
1698
+ const conversationId = this.conversationIdFromParams(params) ?? this.fallbackConversationId();
1697
1699
  if (method === "item/tool/requestUserInput" || method === "tool/requestUserInput") {
1698
1700
  this.handleStructuredInput(params);
1699
1701
  return;
@@ -1720,14 +1722,14 @@ export class AgentWorkspaceProxy {
1720
1722
  if (conversationId) {
1721
1723
  const turnId = this.extractTurnId(params);
1722
1724
  if (turnId)
1723
- this.currentTurnIds.set(conversationId, turnId);
1725
+ this.rememberTurnConversationId(conversationId, turnId);
1724
1726
  this.updateConversationStatus(conversationId, "running");
1725
1727
  }
1726
1728
  return;
1727
1729
  }
1728
1730
  if (method === "turn/completed") {
1729
1731
  if (conversationId) {
1730
- this.currentTurnIds.delete(conversationId);
1732
+ this.forgetCurrentTurn(conversationId, this.extractTurnId(params));
1731
1733
  this.updateConversationStatus(conversationId, "idle");
1732
1734
  }
1733
1735
  return;
@@ -1784,7 +1786,7 @@ export class AgentWorkspaceProxy {
1784
1786
  const raw = asRecord(params);
1785
1787
  if (!raw)
1786
1788
  return;
1787
- const conversationId = this.conversationIdFromParams(raw) ?? this.activeConversationId;
1789
+ const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
1788
1790
  if (!conversationId)
1789
1791
  return;
1790
1792
  const itemId = firstString(raw, ["itemId", "id", "messageId"]) ?? id("msg");
@@ -1809,7 +1811,7 @@ export class AgentWorkspaceProxy {
1809
1811
  }
1810
1812
  handlePlanUpdated(params) {
1811
1813
  const raw = asRecord(params);
1812
- const conversationId = this.conversationIdFromParams(raw) ?? this.activeConversationId;
1814
+ const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
1813
1815
  if (!conversationId)
1814
1816
  return;
1815
1817
  const plan = Array.isArray(raw?.plan) ? raw.plan : [];
@@ -1841,7 +1843,7 @@ export class AgentWorkspaceProxy {
1841
1843
  const raw = asRecord(params);
1842
1844
  if (!raw)
1843
1845
  return;
1844
- const conversationId = this.conversationIdFromParams(raw) ?? this.activeConversationId;
1846
+ const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
1845
1847
  if (!conversationId)
1846
1848
  return;
1847
1849
  const itemId = firstString(raw, ["itemId", "id"]) ?? "plan";
@@ -1865,24 +1867,26 @@ export class AgentWorkspaceProxy {
1865
1867
  const item = extractItem(params);
1866
1868
  if (!item)
1867
1869
  return;
1868
- const itemType = firstString(item, ["type"]);
1870
+ const sourceConversationId = this.conversationIdFromParams(params);
1871
+ const routedItem = sourceConversationId ? { ...item, conversationId: sourceConversationId } : item;
1872
+ const itemType = firstString(routedItem, ["type"]);
1869
1873
  const normalizedItemType = normalizedIdentifier(itemType);
1870
1874
  if (normalizedItemType === "agentmessage" || normalizedItemType === "assistantmessage") {
1871
- this.handleCompletedMessageItem(item, true);
1875
+ this.handleCompletedMessageItem(routedItem, true);
1872
1876
  return;
1873
1877
  }
1874
1878
  if (normalizedItemType === "plan") {
1875
- this.handlePlanUpdated({ plan: [item] });
1879
+ this.handlePlanUpdated({ plan: [routedItem], conversationId: sourceConversationId });
1876
1880
  return;
1877
1881
  }
1878
1882
  if (isSubagentItemType(itemType)) {
1879
- this.handleSubagentItem(item, "running", true);
1883
+ this.handleSubagentItem(routedItem, "running", true);
1880
1884
  return;
1881
1885
  }
1882
- if (this.handleSemanticSystemItem(item, "running", true))
1886
+ if (this.handleSemanticSystemItem(routedItem, "running", true))
1883
1887
  return;
1884
- const conversationId = this.conversationIdFromParams(item) ?? this.activeConversationId;
1885
- const toolCall = this.toolCallFromItem(item, "running");
1888
+ const conversationId = this.conversationIdFromParams(routedItem) ?? this.fallbackConversationId();
1889
+ const toolCall = this.toolCallFromItem(routedItem, "running");
1886
1890
  if (!conversationId || !toolCall)
1887
1891
  return;
1888
1892
  this.toolConversationIds.set(toolCall.id, conversationId);
@@ -1892,24 +1896,26 @@ export class AgentWorkspaceProxy {
1892
1896
  const item = extractItem(params);
1893
1897
  if (!item)
1894
1898
  return;
1895
- const itemType = firstString(item, ["type"]);
1899
+ const sourceConversationId = this.conversationIdFromParams(params);
1900
+ const routedItem = sourceConversationId ? { ...item, conversationId: sourceConversationId } : item;
1901
+ const itemType = firstString(routedItem, ["type"]);
1896
1902
  const normalizedItemType = normalizedIdentifier(itemType);
1897
1903
  if (normalizedItemType === "agentmessage" || normalizedItemType === "assistantmessage") {
1898
- this.handleCompletedMessageItem(item, false);
1904
+ this.handleCompletedMessageItem(routedItem, false);
1899
1905
  return;
1900
1906
  }
1901
1907
  if (normalizedItemType === "plan") {
1902
- this.handlePlanDelta({ ...item, delta: firstString(item, ["text", "content", "message"]) });
1908
+ this.handlePlanDelta({ ...routedItem, delta: firstString(routedItem, ["text", "content", "message"]) });
1903
1909
  return;
1904
1910
  }
1905
1911
  if (isSubagentItemType(itemType)) {
1906
- this.handleSubagentItem(item, normalizeToolStatus(item.status, true), false);
1912
+ this.handleSubagentItem(routedItem, normalizeToolStatus(routedItem.status, true), false);
1907
1913
  return;
1908
1914
  }
1909
- if (this.handleSemanticSystemItem(item, normalizeToolStatus(item.status, true), false))
1915
+ if (this.handleSemanticSystemItem(routedItem, normalizeToolStatus(routedItem.status, true), false))
1910
1916
  return;
1911
- const conversationId = this.conversationIdFromParams(item) ?? this.activeConversationId;
1912
- const toolCall = this.toolCallFromItem(item, normalizeToolStatus(item.status, true));
1917
+ const conversationId = this.conversationIdFromParams(routedItem) ?? this.fallbackConversationId();
1918
+ const toolCall = this.toolCallFromItem(routedItem, normalizeToolStatus(routedItem.status, true));
1913
1919
  if (!conversationId || !toolCall)
1914
1920
  return;
1915
1921
  const bufferedOutput = this.toolOutputBuffers.get(toolCall.id);
@@ -1927,7 +1933,8 @@ export class AgentWorkspaceProxy {
1927
1933
  return;
1928
1934
  const conversationId = this.conversationIdFromParams(raw) ??
1929
1935
  this.toolConversationIds.get(itemId) ??
1930
- this.activeConversationId;
1936
+ this.itemConversationIds.get(itemId) ??
1937
+ this.fallbackConversationId();
1931
1938
  if (!conversationId)
1932
1939
  return;
1933
1940
  const output = appendCapped(this.toolOutputBuffers.get(itemId), delta, 6000);
@@ -1949,7 +1956,8 @@ export class AgentWorkspaceProxy {
1949
1956
  const itemId = firstString(raw, ["itemId", "id"]) ?? id("file");
1950
1957
  const conversationId = this.conversationIdFromParams(raw) ??
1951
1958
  this.toolConversationIds.get(itemId) ??
1952
- this.activeConversationId;
1959
+ this.itemConversationIds.get(itemId) ??
1960
+ this.fallbackConversationId();
1953
1961
  if (!conversationId)
1954
1962
  return;
1955
1963
  const output = extractDiffText(raw) ??
@@ -1968,7 +1976,7 @@ export class AgentWorkspaceProxy {
1968
1976
  const raw = asRecord(params);
1969
1977
  if (!raw)
1970
1978
  return;
1971
- const conversationId = this.conversationIdFromParams(raw) ?? this.activeConversationId;
1979
+ const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
1972
1980
  if (!conversationId)
1973
1981
  return;
1974
1982
  const diff = extractDiffText(raw);
@@ -1998,7 +2006,8 @@ export class AgentWorkspaceProxy {
1998
2006
  return;
1999
2007
  const conversationId = this.conversationIdFromParams(raw) ??
2000
2008
  this.toolConversationIds.get(processId) ??
2001
- this.activeConversationId;
2009
+ this.itemConversationIds.get(processId) ??
2010
+ this.fallbackConversationId();
2002
2011
  if (!conversationId)
2003
2012
  return;
2004
2013
  const output = appendCapped(this.toolOutputBuffers.get(processId), delta, 6000);
@@ -2015,7 +2024,7 @@ export class AgentWorkspaceProxy {
2015
2024
  }
2016
2025
  handleAutoApprovalReview(params, streaming) {
2017
2026
  const raw = asRecord(params) ?? {};
2018
- const conversationId = this.conversationIdFromParams(raw) ?? this.activeConversationId;
2027
+ const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
2019
2028
  if (!conversationId)
2020
2029
  return;
2021
2030
  const itemId = firstString(raw, ["itemId", "id", "reviewId"]) ?? "auto-approval-review";
@@ -2044,7 +2053,7 @@ export class AgentWorkspaceProxy {
2044
2053
  this.updateConversationPreview(conversationId, streaming ? "正在审查自动授权" : "已完成自动授权审查", streaming ? "running" : undefined);
2045
2054
  }
2046
2055
  handleCompletedMessageItem(item, streaming) {
2047
- const conversationId = this.conversationIdFromParams(item) ?? this.activeConversationId;
2056
+ const conversationId = this.conversationIdFromParams(item) ?? this.fallbackConversationId();
2048
2057
  if (!conversationId)
2049
2058
  return;
2050
2059
  const itemId = firstString(item, ["id"]) ?? id("msg");
@@ -2072,7 +2081,7 @@ export class AgentWorkspaceProxy {
2072
2081
  firstString(nested, ["delta", "text", "content", "message"]);
2073
2082
  if (!text)
2074
2083
  return;
2075
- const conversationId = this.conversationIdFromParams(raw) ?? this.activeConversationId;
2084
+ const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
2076
2085
  if (!conversationId)
2077
2086
  return;
2078
2087
  if (firstString(raw, ["toolName", "tool", "name"])) {
@@ -2105,7 +2114,7 @@ export class AgentWorkspaceProxy {
2105
2114
  handleSemanticSystemItem(item, status, streaming) {
2106
2115
  const itemType = firstString(item, ["type"]);
2107
2116
  const normalized = normalizedIdentifier(itemType);
2108
- const conversationId = this.conversationIdFromParams(item) ?? this.activeConversationId;
2117
+ const conversationId = this.conversationIdFromParams(item) ?? this.fallbackConversationId();
2109
2118
  if (!conversationId)
2110
2119
  return false;
2111
2120
  const itemId = firstString(item, ["id", "itemId"]) ?? id("item");
@@ -2153,7 +2162,7 @@ export class AgentWorkspaceProxy {
2153
2162
  return false;
2154
2163
  }
2155
2164
  handleSubagentItem(item, status, streaming) {
2156
- const conversationId = this.conversationIdFromParams(item) ?? this.activeConversationId;
2165
+ const conversationId = this.conversationIdFromParams(item) ?? this.fallbackConversationId();
2157
2166
  if (!conversationId)
2158
2167
  return;
2159
2168
  const subagent = decodeSubagentAction(item, status);
@@ -2180,7 +2189,7 @@ export class AgentWorkspaceProxy {
2180
2189
  }
2181
2190
  handleStructuredInput(params, waitForResponse = false) {
2182
2191
  const raw = asRecord(params) ?? {};
2183
- const conversationId = this.conversationIdFromParams(raw) ?? this.activeConversationId;
2192
+ const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
2184
2193
  const source = firstString(raw, ["method", "source", "requestMethod"]);
2185
2194
  const formatResponse = source === "mcpServer/elicitation/request"
2186
2195
  ? formatMcpElicitationResponse
@@ -2246,7 +2255,7 @@ export class AgentWorkspaceProxy {
2246
2255
  }
2247
2256
  handlePermission(params, waitForResponse, source) {
2248
2257
  const raw = asRecord(params) ?? {};
2249
- const conversationId = this.conversationIdFromParams(raw) ?? this.activeConversationId;
2258
+ const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
2250
2259
  if (!conversationId)
2251
2260
  return waitForResponse ? Promise.resolve({ outcome: { outcome: "cancelled" } }) : undefined;
2252
2261
  const requestId = firstString(raw, ["requestId", "id", "permissionId"]) ?? id("perm");
@@ -2360,6 +2369,7 @@ export class AgentWorkspaceProxy {
2360
2369
  });
2361
2370
  }
2362
2371
  addItem(conversationId, item) {
2372
+ this.rememberItemConversationId(conversationId, item);
2363
2373
  const timeline = this.timelines.get(conversationId) ?? [];
2364
2374
  timeline.push(item);
2365
2375
  timeline.sort((a, b) => a.createdAt - b.createdAt);
@@ -2370,6 +2380,7 @@ export class AgentWorkspaceProxy {
2370
2380
  this.emitItem(conversationId, item);
2371
2381
  }
2372
2382
  upsertItem(conversationId, item) {
2383
+ this.rememberItemConversationId(conversationId, item);
2373
2384
  const timeline = this.timelines.get(conversationId) ?? [];
2374
2385
  const index = timeline.findIndex((entry) => entry.id === item.id);
2375
2386
  if (index >= 0)
@@ -2398,6 +2409,8 @@ export class AgentWorkspaceProxy {
2398
2409
  };
2399
2410
  this.toolConversationIds.set(toolCall.id, conversationId);
2400
2411
  this.toolConversationIds.set(nextToolCall.id, conversationId);
2412
+ this.itemConversationIds.set(toolCall.id, conversationId);
2413
+ this.itemConversationIds.set(nextToolCall.id, conversationId);
2401
2414
  const kind = nextToolCall.name.includes("文件")
2402
2415
  ? "file_change"
2403
2416
  : nextToolCall.name.includes("命令")
@@ -2482,6 +2495,22 @@ export class AgentWorkspaceProxy {
2482
2495
  this.toolConversationIds.set(toolId, newId);
2483
2496
  }
2484
2497
  }
2498
+ for (const [turnId, conversationId] of this.turnConversationIds) {
2499
+ if (conversationId === oldId) {
2500
+ this.turnConversationIds.set(turnId, newId);
2501
+ }
2502
+ }
2503
+ const currentTurnId = this.currentTurnIds.get(oldId);
2504
+ if (currentTurnId) {
2505
+ this.currentTurnIds.delete(oldId);
2506
+ this.currentTurnIds.set(newId, currentTurnId);
2507
+ this.turnConversationIds.set(currentTurnId, newId);
2508
+ }
2509
+ for (const [itemId, conversationId] of this.itemConversationIds) {
2510
+ if (conversationId === oldId) {
2511
+ this.itemConversationIds.set(itemId, newId);
2512
+ }
2513
+ }
2485
2514
  if (this.activeConversationId === oldId) {
2486
2515
  this.activeConversationId = newId;
2487
2516
  }
@@ -2549,14 +2578,87 @@ export class AgentWorkspaceProxy {
2549
2578
  }
2550
2579
  conversationIdFromParams(params) {
2551
2580
  const raw = asRecord(params);
2552
- const agentSessionId = this.extractSessionId(raw);
2553
- if (agentSessionId)
2554
- return this.conversationByAgentSessionId.get(agentSessionId);
2581
+ if (!raw)
2582
+ return undefined;
2583
+ const directConversationId = firstString(raw, ["conversationId"]);
2584
+ if (directConversationId && this.conversations.has(directConversationId)) {
2585
+ return directConversationId;
2586
+ }
2555
2587
  const threadId = firstString(raw, ["threadId", "sessionId", "agentSessionId"]);
2556
- if (threadId)
2557
- return this.conversationByAgentSessionId.get(threadId);
2588
+ if (threadId) {
2589
+ const conversationId = this.conversationByAgentSessionId.get(threadId);
2590
+ if (conversationId)
2591
+ return conversationId;
2592
+ }
2593
+ const agentSessionId = this.extractSessionId(raw);
2594
+ if (agentSessionId) {
2595
+ const conversationId = this.conversationByAgentSessionId.get(agentSessionId);
2596
+ if (conversationId)
2597
+ return conversationId;
2598
+ }
2599
+ const turnId = this.extractTurnId(raw) ?? firstString(raw, ["turnId"]);
2600
+ if (turnId) {
2601
+ const conversationId = this.turnConversationIds.get(turnId);
2602
+ if (conversationId)
2603
+ return conversationId;
2604
+ }
2605
+ const itemId = firstString(raw, [
2606
+ "itemId",
2607
+ "messageId",
2608
+ "toolCallId",
2609
+ "processId",
2610
+ "callId",
2611
+ "requestId",
2612
+ "permissionId",
2613
+ "id",
2614
+ ]);
2615
+ if (itemId) {
2616
+ const conversationId = this.itemConversationIds.get(itemId) ??
2617
+ this.toolConversationIds.get(itemId);
2618
+ if (conversationId)
2619
+ return conversationId;
2620
+ }
2621
+ for (const nested of [raw.params, raw.item, raw.message, raw.toolCall, raw.command, raw.event]) {
2622
+ const nestedRecord = asRecord(nested);
2623
+ if (!nestedRecord || nestedRecord === raw)
2624
+ continue;
2625
+ const conversationId = this.conversationIdFromParams(nestedRecord);
2626
+ if (conversationId)
2627
+ return conversationId;
2628
+ }
2558
2629
  return undefined;
2559
2630
  }
2631
+ fallbackConversationId() {
2632
+ const liveConversations = [...this.conversations.values()].filter((conversation) => conversation.status === "running" || conversation.status === "waiting_permission");
2633
+ return liveConversations.length === 1 ? liveConversations[0]?.id : undefined;
2634
+ }
2635
+ rememberTurnConversationId(conversationId, turnId) {
2636
+ this.currentTurnIds.set(conversationId, turnId);
2637
+ this.turnConversationIds.set(turnId, conversationId);
2638
+ }
2639
+ forgetCurrentTurn(conversationId, turnId) {
2640
+ const currentTurnId = this.currentTurnIds.get(conversationId);
2641
+ this.currentTurnIds.delete(conversationId);
2642
+ if (turnId)
2643
+ this.turnConversationIds.delete(turnId);
2644
+ if (currentTurnId && currentTurnId !== turnId)
2645
+ this.turnConversationIds.delete(currentTurnId);
2646
+ }
2647
+ rememberItemConversationId(conversationId, item) {
2648
+ const keys = [
2649
+ item.id,
2650
+ item.itemId,
2651
+ item.toolCall?.id,
2652
+ item.permission?.requestId,
2653
+ item.structuredInput?.requestId,
2654
+ ].filter((key) => Boolean(key));
2655
+ for (const key of keys) {
2656
+ this.itemConversationIds.set(key, conversationId);
2657
+ }
2658
+ if (item.turnId) {
2659
+ this.turnConversationIds.set(item.turnId, conversationId);
2660
+ }
2661
+ }
2560
2662
  handleProviderExit(provider, message) {
2561
2663
  this.clients.delete(provider);
2562
2664
  this.agentProtocols.delete(provider);