@blade-hq/agent-client 2610.0.0-beta.3 → 2610.0.0-beta.30

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/index.js CHANGED
@@ -352,8 +352,7 @@ var HeadlessResource = class {
352
352
  socket.on("system:error", onSystemError);
353
353
  socket.emit("session:subscribe", {
354
354
  session_id,
355
- event_format: "raw",
356
- stream_tokens: this.client.options.streamTokens !== false
355
+ event_format: "raw"
357
356
  });
358
357
  socket.emit(
359
358
  "chat:send",
@@ -425,6 +424,50 @@ function ensureSocketConnected(socket, timeoutMs, detail) {
425
424
  });
426
425
  }
427
426
 
427
+ // src/resources/computers.ts
428
+ function computerState(computer) {
429
+ if (computer.is_primary) return "primary";
430
+ if (computer.enabled) return "enabled";
431
+ return computer.online ? "idle" : "offline";
432
+ }
433
+ function canToggleComputer(computer) {
434
+ return !computer.is_primary;
435
+ }
436
+ function sortComputers(computers) {
437
+ return [...computers].sort((a, b) => {
438
+ if (a.enabled !== b.enabled) return a.enabled ? -1 : 1;
439
+ if (a.online !== b.online) return a.online ? -1 : 1;
440
+ return a.label.localeCompare(b.label);
441
+ });
442
+ }
443
+ var ComputersResource = class {
444
+ constructor(client) {
445
+ this.client = client;
446
+ }
447
+ client;
448
+ /**
449
+ * 列出当前用户的全部电脑,带上"在不在线"和"这次会话能不能用"。
450
+ * 服务端只查库,电脑离线时同样返回,所以拿得到"它离线了"这个结论。
451
+ */
452
+ list(sessionId) {
453
+ return this.client.json(
454
+ "GET",
455
+ `/api/sessions/${encodeURIComponent(sessionId)}/computers`
456
+ );
457
+ }
458
+ /**
459
+ * 启动或停止一台电脑。只改这次会话的可用范围,不会碰远程机器,
460
+ * 电脑离线时同样能登记,等它上线就直接可用。
461
+ */
462
+ setEnabled(sessionId, computer, enabled) {
463
+ return this.client.json(
464
+ "PUT",
465
+ `/api/sessions/${encodeURIComponent(sessionId)}/computers/${encodeURIComponent(computer)}/enabled`,
466
+ { enabled }
467
+ );
468
+ }
469
+ };
470
+
428
471
  // src/resources/models.ts
429
472
  var ModelsResource = class {
430
473
  constructor(client) {
@@ -473,6 +516,7 @@ var SessionInfo = type({
473
516
  "disable_tools?": "string[]",
474
517
  "runtime_type?": "string | null",
475
518
  "daemon_id?": "string | null",
519
+ "agent_runtime_id?": "string | null",
476
520
  "workspace_path?": "string | null",
477
521
  "match?": "unknown"
478
522
  });
@@ -1023,6 +1067,91 @@ function toolStatusFromResult(result) {
1023
1067
  return inferToolStatus(text);
1024
1068
  }
1025
1069
 
1070
+ // src/schemas/context.ts
1071
+ var BUILTIN_CONTEXT_TITLES = {
1072
+ "agent-instructions": "\u9879\u76EE\u8BF4\u660E",
1073
+ "skill-catalog": "\u53EF\u7528\u80FD\u529B",
1074
+ "runtime-context": "\u5F53\u524D\u5DE5\u4F5C\u73AF\u5883",
1075
+ "workspace-context": "\u5F53\u524D\u5DE5\u4F5C\u533A\u73AF\u5883"
1076
+ };
1077
+ var BUILTIN_CONTEXT_DETAILS = {
1078
+ "agent-instructions": "\u8FD9\u4E9B\u8BF4\u660E\u4F1A\u5E2E\u52A9\u667A\u80FD\u4F53\u7406\u89E3\u5F53\u524D\u9879\u76EE\u548C\u5DE5\u4F5C\u8981\u6C42\u3002",
1079
+ "skill-catalog": "\u8FD9\u4E9B\u80FD\u529B\u4F1A\u5E2E\u52A9\u667A\u80FD\u4F53\u9009\u62E9\u5408\u9002\u7684\u65B9\u5F0F\u5B8C\u6210\u4EFB\u52A1\u3002",
1080
+ "runtime-context": "\u8FD9\u4E9B\u4FE1\u606F\u4F1A\u5E2E\u52A9\u667A\u80FD\u4F53\u4E86\u89E3\u5F53\u524D\u6587\u4EF6\u5939\u548C\u8FD0\u884C\u6761\u4EF6\u3002",
1081
+ "workspace-context": "\u8FD9\u4E9B\u4FE1\u606F\u4F1A\u5E2E\u52A9\u667A\u80FD\u4F53\u4E86\u89E3\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u3001\u6587\u4EF6\u548C\u8FD0\u884C\u73AF\u5883\u3002"
1082
+ };
1083
+ function nonEmptyString(value) {
1084
+ return typeof value === "string" && value.trim() ? value.trim() : null;
1085
+ }
1086
+ function contextProjectionData(fields) {
1087
+ const contextKind = nonEmptyString(fields.context_kind);
1088
+ const contextKey = nonEmptyString(fields.context_key);
1089
+ const contextName = nonEmptyString(fields.context_name);
1090
+ const contextRevision = nonEmptyString(fields.context_revision);
1091
+ if (!contextKind || !contextKey || !contextName || !contextRevision || typeof fields.context_order !== "number" || !Number.isFinite(fields.context_order) || !fields.context_action) {
1092
+ return null;
1093
+ }
1094
+ return {
1095
+ context_kind: contextKind,
1096
+ context_key: contextKey,
1097
+ context_name: contextName,
1098
+ context_order: fields.context_order,
1099
+ context_action: fields.context_action,
1100
+ context_revision: contextRevision,
1101
+ display_title: nonEmptyString(fields.display_title),
1102
+ display_summary: nonEmptyString(fields.display_summary),
1103
+ sources: Array.isArray(fields.sources) ? fields.sources : []
1104
+ };
1105
+ }
1106
+ function getContextDisplayState(context) {
1107
+ const title = BUILTIN_CONTEXT_TITLES[context.context_kind] ?? nonEmptyString(context.display_title) ?? "\u5BF9\u8BDD\u80CC\u666F";
1108
+ const isRemoved = context.context_action === "removed";
1109
+ const summary = nonEmptyString(context.display_summary) ?? (isRemoved ? "\u5DF2\u505C\u6B62\u7528\u4E8E\u540E\u7EED\u5BF9\u8BDD" : context.context_action === "retained" ? "\u7EE7\u7EED\u7528\u4E8E\u540E\u7EED\u5BF9\u8BDD" : context.context_kind === "agent-instructions" ? "\u5DF2\u66F4\u65B0\u672C\u6B21\u5BF9\u8BDD\u4F7F\u7528\u7684\u9879\u76EE\u8BF4\u660E" : context.context_kind === "skill-catalog" ? "\u5DF2\u66F4\u65B0\u672C\u6B21\u53EF\u7528\u80FD\u529B" : context.context_kind === "runtime-context" ? "\u5DF2\u66F4\u65B0\u672C\u6B21\u5DE5\u4F5C\u73AF\u5883" : context.context_kind === "workspace-context" ? "\u5DF2\u66F4\u65B0\u672C\u6B21\u5DE5\u4F5C\u533A\u73AF\u5883" : "\u5DF2\u66F4\u65B0\u672C\u6B21\u5BF9\u8BDD\u80CC\u666F");
1110
+ const detail = isRemoved ? `${title}\u5DF2\u4E0D\u518D\u7528\u4E8E\u540E\u7EED\u5BF9\u8BDD\u3002` : BUILTIN_CONTEXT_DETAILS[context.context_kind] ?? "\u8FD9\u4E9B\u4FE1\u606F\u4F1A\u5E2E\u52A9\u667A\u80FD\u4F53\u7406\u89E3\u5F53\u524D\u4EFB\u52A1\u3002";
1111
+ return { title, summary, detail, isRemoved };
1112
+ }
1113
+
1114
+ // src/shared/projection/context.ts
1115
+ function isRecord2(value) {
1116
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1117
+ }
1118
+ function buildContextTurn(entry, sequence) {
1119
+ if (entry.kind !== "context" || !isRecord2(entry.source) || !isRecord2(entry.data)) {
1120
+ return null;
1121
+ }
1122
+ const display = isRecord2(entry.data.display) ? entry.data.display : {};
1123
+ const message = isRecord2(entry.message) ? entry.message : {};
1124
+ const context = contextProjectionData({
1125
+ context_kind: typeof entry.source.kind === "string" ? entry.source.kind : null,
1126
+ context_key: typeof entry.source.key === "string" ? entry.source.key : null,
1127
+ context_name: typeof entry.data.contribution_name === "string" ? entry.data.contribution_name : null,
1128
+ context_order: typeof entry.data.order === "number" ? entry.data.order : null,
1129
+ context_action: entry.data.action === "published" || entry.data.action === "retained" || entry.data.action === "removed" ? entry.data.action : null,
1130
+ context_revision: typeof entry.data.revision === "string" ? entry.data.revision : null,
1131
+ display_title: typeof display.title === "string" ? display.title : null,
1132
+ display_summary: typeof display.summary === "string" ? display.summary : null,
1133
+ sources: Array.isArray(display.sources) ? display.sources.filter(isRecord2) : []
1134
+ });
1135
+ if (!context) return null;
1136
+ const turnId = typeof entry.id === "string" && entry.id ? entry.id : `context-${sequence}`;
1137
+ return {
1138
+ id: turnId,
1139
+ sequence,
1140
+ turn_id: turnId,
1141
+ loop_id: typeof entry.loop_name === "string" && entry.loop_name ? entry.loop_name : "root",
1142
+ kind: "context",
1143
+ role: "system",
1144
+ status: "completed",
1145
+ blocks: [],
1146
+ tool_calls: [],
1147
+ model: null,
1148
+ usage: null,
1149
+ duration_ms: 0,
1150
+ context_content: Object.hasOwn(message, "content") ? message.content : null,
1151
+ ...context
1152
+ };
1153
+ }
1154
+
1026
1155
  // src/shared/projection/builder.ts
1027
1156
  function hasChatRunEvent(events) {
1028
1157
  return events.some(
@@ -1058,7 +1187,8 @@ function completedResponseText(content) {
1058
1187
  ).filter((part) => part.type === "text").map((part) => String(part.text ?? "")).join("");
1059
1188
  }
1060
1189
  function replaceTextContent(state, content) {
1061
- if (state.blocks.some((block) => block.type === "text")) return;
1190
+ const currentContent = state.blocks.filter((block) => block.type === "text").map((block) => String(block.content ?? "")).join("");
1191
+ if (currentContent === content) return;
1062
1192
  const replacement = { type: "text", content };
1063
1193
  const blocks = [];
1064
1194
  let inserted = false;
@@ -1078,6 +1208,31 @@ function replaceTextContent(state, content) {
1078
1208
  }
1079
1209
  state.blocks = blocks;
1080
1210
  }
1211
+ function applyResponseBlockOrder(state, value) {
1212
+ if (!Array.isArray(value)) return false;
1213
+ const ordered = [];
1214
+ for (const item of value) {
1215
+ if (typeof item !== "object" || item === null || Array.isArray(item)) continue;
1216
+ const block = item;
1217
+ if (block.type === "text" || block.type === "thinking") {
1218
+ const content = String(block[block.type] ?? "");
1219
+ if (content) ordered.push({ type: block.type, content });
1220
+ continue;
1221
+ }
1222
+ if (block.type !== "tool_call") continue;
1223
+ const toolCallId = String(block.id ?? "");
1224
+ const toolBlock = state.blocks.find(
1225
+ (candidate) => candidate.type === "tool_use" && candidate.tool_call_id === toolCallId
1226
+ );
1227
+ if (toolBlock) ordered.push(toolBlock);
1228
+ }
1229
+ if (ordered.length === 0 && value.length > 0) return false;
1230
+ const unrelatedBlocks = state.blocks.filter(
1231
+ (block) => block.type !== "text" && block.type !== "thinking" && block.type !== "tool_use"
1232
+ );
1233
+ state.blocks = [...ordered, ...unrelatedBlocks];
1234
+ return true;
1235
+ }
1081
1236
  var ClientProjectionBuilder = class {
1082
1237
  activeTurns = /* @__PURE__ */ new Map();
1083
1238
  lastTurnIds = /* @__PURE__ */ new Map();
@@ -1117,6 +1272,8 @@ var ClientProjectionBuilder = class {
1117
1272
  return this.onMemoryInject(loopId, payload);
1118
1273
  case "memory:inject:none":
1119
1274
  return null;
1275
+ case "context:entry":
1276
+ return this.onContextEntry(payload);
1120
1277
  case "loop:turn":
1121
1278
  return this.onLoopTurn(loopId, payload);
1122
1279
  case "mode:change":
@@ -1165,6 +1322,8 @@ var ClientProjectionBuilder = class {
1165
1322
  return this.syncPatch(state);
1166
1323
  case "llm:tool_call:created":
1167
1324
  return this.onToolCallCreated(state, payload);
1325
+ case "llm:tool_call:arguments:delta":
1326
+ return this.onToolCallArgumentsDelta(state, payload);
1168
1327
  case "llm:response:done":
1169
1328
  return this.onResponseDone(state, loopId, payload);
1170
1329
  case "tool:result:delta":
@@ -1232,6 +1391,11 @@ var ClientProjectionBuilder = class {
1232
1391
  }
1233
1392
  return null;
1234
1393
  }
1394
+ onContextEntry(payload) {
1395
+ if (!isRecord3(payload.entry)) return null;
1396
+ const turn = buildContextTurn(payload.entry, this.nextSeq());
1397
+ return turn ? [{ kind: "upsert", turn }] : null;
1398
+ }
1235
1399
  onLoopTurn(loopId, payload) {
1236
1400
  let turnId = String(payload.turn_id ?? this.nextTurnId(`turn:${loopId}`));
1237
1401
  let memoryRefs = null;
@@ -1288,11 +1452,11 @@ var ClientProjectionBuilder = class {
1288
1452
  loopId,
1289
1453
  toolCallId: optStr(payload.tool_call_id) ?? void 0,
1290
1454
  toolName: toolNameFromPayload(payload) ?? void 0,
1291
- workspacePath: isRecord2(payload.workspace_change) ? optStr(payload.workspace_change.to) ?? void 0 : void 0
1455
+ workspacePath: isRecord3(payload.workspace_change) ? optStr(payload.workspace_change.to) ?? void 0 : void 0
1292
1456
  }
1293
1457
  ];
1294
1458
  const change = payload.workspace_change;
1295
- if (isRecord2(change)) {
1459
+ if (isRecord3(change)) {
1296
1460
  const source = optStr(change.source) ?? "";
1297
1461
  const projectName = optStr(change.project_name) ?? "";
1298
1462
  const fromPath = optStr(change.from) ?? "";
@@ -1430,13 +1594,7 @@ var ClientProjectionBuilder = class {
1430
1594
  }
1431
1595
  onToolCallCreated(state, payload) {
1432
1596
  if ("arguments_delta" in payload) {
1433
- const delta = String(payload.arguments_delta ?? "");
1434
- const toolCallId2 = String(payload.id ?? "");
1435
- if (delta && toolCallId2) {
1436
- appendToolCallArguments(state, toolCallId2, delta);
1437
- return this.syncPatch(state);
1438
- }
1439
- return null;
1597
+ return this.onToolCallArgumentsDelta(state, payload);
1440
1598
  }
1441
1599
  const toolCallId = String(payload.id ?? "");
1442
1600
  const toolName = toolNameFromPayload(payload);
@@ -1445,6 +1603,13 @@ var ClientProjectionBuilder = class {
1445
1603
  upsertToolCall(state, toolCallId, toolName, displayName, args);
1446
1604
  return this.syncPatch(state);
1447
1605
  }
1606
+ onToolCallArgumentsDelta(state, payload) {
1607
+ const delta = String(payload.arguments_delta ?? "");
1608
+ const toolCallId = String(payload.id ?? "");
1609
+ if (!delta || !toolCallId) return null;
1610
+ appendToolCallArguments(state, toolCallId, delta);
1611
+ return this.syncPatch(state);
1612
+ }
1448
1613
  onResponseDone(state, loopId, payload) {
1449
1614
  state.responseDone = true;
1450
1615
  const model = optStr(payload.model);
@@ -1452,9 +1617,6 @@ var ClientProjectionBuilder = class {
1452
1617
  if (typeof payload.usage === "object" && payload.usage !== null) {
1453
1618
  state.usage = { ...payload.usage };
1454
1619
  }
1455
- if ("content" in payload) {
1456
- replaceTextContent(state, completedResponseText(payload.content));
1457
- }
1458
1620
  const toolCalls = payload.tool_calls;
1459
1621
  if (Array.isArray(toolCalls)) {
1460
1622
  for (const raw of toolCalls) {
@@ -1472,7 +1634,13 @@ var ClientProjectionBuilder = class {
1472
1634
  );
1473
1635
  }
1474
1636
  }
1475
- if (state.toolCalls.length === 0) {
1637
+ if (!applyResponseBlockOrder(state, payload.response_blocks) && "content" in payload) {
1638
+ replaceTextContent(state, completedResponseText(payload.content));
1639
+ }
1640
+ const isNativeProviderResponse = typeof payload.provider === "string" && payload.provider !== "";
1641
+ if (state.toolCalls.length === 0 || isNativeProviderResponse && state.toolCalls.every(
1642
+ (toolCall) => toolCall.status !== "pending" && toolCall.status !== "awaiting_answer"
1643
+ )) {
1476
1644
  return this.finalize(loopId, "completed");
1477
1645
  }
1478
1646
  return this.syncPatch(state);
@@ -1486,7 +1654,8 @@ var ClientProjectionBuilder = class {
1486
1654
  const durationMs = typeof rawDuration === "number" && rawDuration >= 0 ? Math.round(rawDuration) : null;
1487
1655
  const sl = sourceLoopFor(state.loopId, this.loopDescriptions);
1488
1656
  applyToolResult(state, toolCallId, content, status, durationMs, sl);
1489
- if (this.shouldFinalizeAfterToolResults(state)) {
1657
+ const isNativeProviderResult = typeof payload.provider === "string" && payload.provider !== "";
1658
+ if (!isNativeProviderResult && this.shouldFinalizeAfterToolResults(state)) {
1490
1659
  return this.finalize(loopId, "completed");
1491
1660
  }
1492
1661
  return this.syncPatch(state);
@@ -1527,6 +1696,11 @@ var ClientProjectionBuilder = class {
1527
1696
  const toolName = String(payload.tool_name ?? "");
1528
1697
  const displayName = this.displayNameResolver(toolName);
1529
1698
  upsertToolCall(state, toolCallId, toolName, displayName, "");
1699
+ const relatedToolCallId = String(payload.related_tool_call_id ?? "");
1700
+ if (relatedToolCallId && relatedToolCallId !== toolCallId) {
1701
+ const related = state.toolCalls.find((toolCall) => toolCall.id === relatedToolCallId);
1702
+ if (related) related.status = "awaiting_answer";
1703
+ }
1530
1704
  return this.syncPatch(state);
1531
1705
  }
1532
1706
  onChildPause(state, loopId, payload) {
@@ -1754,7 +1928,7 @@ var ClientProjectionBuilder = class {
1754
1928
  return `${prefix}:${this.syntheticCounter}:${hex}`;
1755
1929
  }
1756
1930
  };
1757
- function isRecord2(value) {
1931
+ function isRecord3(value) {
1758
1932
  return typeof value === "object" && value !== null && !Array.isArray(value);
1759
1933
  }
1760
1934
  function optStr(value) {
@@ -1783,6 +1957,7 @@ function projectHistory(entries) {
1783
1957
  const latestAssistantByLoop = /* @__PURE__ */ new Map();
1784
1958
  const pendingChildPauses = /* @__PURE__ */ new Map();
1785
1959
  const answeredToolCallIds = /* @__PURE__ */ new Set();
1960
+ const nativeToolTurns = /* @__PURE__ */ new Set();
1786
1961
  let latestRootMessage = null;
1787
1962
  let latestRootAssistant = null;
1788
1963
  let sequence = 0;
@@ -1790,7 +1965,7 @@ function projectHistory(entries) {
1790
1965
  const kind = String(entry.kind ?? "");
1791
1966
  const loopId = String(entry.loop_name ?? "root");
1792
1967
  const id = String(entry.id ?? `history-${sequence + 1}`);
1793
- if (kind === "post_chat_followup" && isRecord3(entry.data)) {
1968
+ if (kind === "post_chat_followup" && isRecord4(entry.data)) {
1794
1969
  const assistantEntryId = String(entry.data.assistant_entry_id ?? "");
1795
1970
  if (latestRootMessage?.role === "assistant" && latestRootMessage.turn_id === assistantEntryId) {
1796
1971
  const followup = normalizePostChatFollowup(entry.data, assistantEntryId);
@@ -1798,7 +1973,15 @@ function projectHistory(entries) {
1798
1973
  }
1799
1974
  continue;
1800
1975
  }
1801
- if (kind === "message" && isRecord3(entry.message)) {
1976
+ if (kind === "context") {
1977
+ const turn = buildContextTurn(entry, sequence + 1);
1978
+ if (turn) {
1979
+ sequence += 1;
1980
+ turns.push(turn);
1981
+ }
1982
+ continue;
1983
+ }
1984
+ if (kind === "message" && isRecord4(entry.message)) {
1802
1985
  const message = entry.message;
1803
1986
  const role = String(message.role ?? "");
1804
1987
  if (role === "tool") {
@@ -1830,14 +2013,31 @@ function projectHistory(entries) {
1830
2013
  blocks,
1831
2014
  tool_calls: toolCalls,
1832
2015
  model: stringOrNull(message.model),
1833
- usage: isRecord3(message._usage) ? { ...message._usage } : null,
2016
+ usage: isRecord4(message._usage) ? { ...message._usage } : null,
1834
2017
  duration_ms: numberOrZero(message._duration_ms),
1835
2018
  started_at: typeof entry.timestamp === "string" ? entry.timestamp : void 0
1836
2019
  };
2020
+ const isNativeToolEntry = role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.some(
2021
+ (toolCall) => isRecord4(toolCall) && typeof toolCall.provider === "string"
2022
+ );
2023
+ const previousTurn = turns[turns.length - 1];
2024
+ if (role === "assistant" && previousTurn?.role === "assistant" && previousTurn.loop_id === loopId && nativeToolTurns.has(previousTurn)) {
2025
+ previousTurn.blocks.push(...turn.blocks);
2026
+ previousTurn.tool_calls.push(...turn.tool_calls);
2027
+ previousTurn.status = turn.status;
2028
+ previousTurn.model = turn.model ?? previousTurn.model;
2029
+ previousTurn.usage = turn.usage ?? previousTurn.usage;
2030
+ previousTurn.duration_ms = turn.duration_ms || previousTurn.duration_ms;
2031
+ if (turn.started_at) previousTurn.started_at = turn.started_at;
2032
+ if (loopId === "root") latestRootMessage = previousTurn;
2033
+ latestAssistantByLoop.set(loopId, previousTurn);
2034
+ continue;
2035
+ }
1837
2036
  if (loopId === "root" && (role === "user" || role === "assistant")) {
1838
2037
  latestRootMessage = turn;
1839
2038
  }
1840
2039
  turns.push(turn);
2040
+ if (isNativeToolEntry) nativeToolTurns.add(turn);
1841
2041
  if (role === "assistant") latestAssistantByLoop.set(loopId, turn);
1842
2042
  if (loopId === "root") {
1843
2043
  if (role === "assistant") latestRootAssistant = turn;
@@ -1846,10 +2046,25 @@ function projectHistory(entries) {
1846
2046
  for (const toolCall of toolCalls) toolOwners.set(toolCall.id, turn);
1847
2047
  continue;
1848
2048
  }
2049
+ if (kind === "tool_result") {
2050
+ const data = entry.data ?? {};
2051
+ const toolCallId = String(data.tool_call_id ?? "");
2052
+ const owner = toolOwners.get(toolCallId) ?? findLatestAssistant(turns, loopId);
2053
+ if (owner && toolCallId) {
2054
+ const rawContent = data.content;
2055
+ const content = typeof rawContent === "string" ? rawContent : Array.isArray(rawContent) ? rawContent : String(rawContent ?? "");
2056
+ const status = data.is_error === true ? "error" : inferToolStatus(typeof content === "string" ? content : JSON.stringify(content));
2057
+ setToolCallStatus(owner, toolCallId, status, content, null);
2058
+ if (!owner.blocks.some((block) => block.type === "tool_result" && block.tool_call_id === toolCallId)) {
2059
+ owner.blocks.push({ type: "tool_result", content, tool_call_id: toolCallId });
2060
+ }
2061
+ }
2062
+ continue;
2063
+ }
1849
2064
  if (kind === "mode_change") {
1850
2065
  turns.push(markerTurn(id, ++sequence, loopId, "mode_change", entry.data ?? {}));
1851
2066
  } else if (kind === "workspace_change") {
1852
- const change = isRecord3(entry.data) ? entry.data : {};
2067
+ const change = isRecord4(entry.data) ? entry.data : {};
1853
2068
  const source = stringOrNull(change.source) ?? "";
1854
2069
  const projectName = stringOrNull(change.project_name) ?? "";
1855
2070
  const fromPath = stringOrNull(change.from) ?? "";
@@ -1890,7 +2105,7 @@ function projectHistory(entries) {
1890
2105
  } else if (kind === "compaction" || kind === "tool_result_archive") {
1891
2106
  const data = entry.data ?? {};
1892
2107
  const compactionId = String(data.compaction_id ?? entry.id ?? `history-${sequence + 1}`);
1893
- let content = isRecord3(entry.message) ? entry.message.content : void 0;
2108
+ let content = isRecord4(entry.message) ? entry.message.content : void 0;
1894
2109
  if (kind === "tool_result_archive") {
1895
2110
  const archivedCount = Array.isArray(data.archived_files) ? data.archived_files.length : 0;
1896
2111
  content = `<compaction-summary>
@@ -1905,11 +2120,11 @@ function projectHistory(entries) {
1905
2120
  );
1906
2121
  } else if (kind === "child_pause") {
1907
2122
  const data = entry.data ?? {};
1908
- const sourceLoop = isRecord3(data.source_loop) ? data.source_loop : {};
2123
+ const sourceLoop = isRecord4(data.source_loop) ? data.source_loop : {};
1909
2124
  const childLoopId = String(data.child_loop_name ?? sourceLoop.name ?? "");
1910
2125
  const childToolCallId = String(data.child_pause_tool_call_id ?? "");
1911
2126
  const parentToolCallId = String(data.parent_fork_tool_call_id ?? "");
1912
- const pauseToolData = isRecord3(data.pause_tool_data) ? data.pause_tool_data : {};
2127
+ const pauseToolData = isRecord4(data.pause_tool_data) ? data.pause_tool_data : {};
1913
2128
  const description = String(sourceLoop.description ?? "");
1914
2129
  applyAskUserPauseToTurn(
1915
2130
  latestAssistantByLoop.get(childLoopId),
@@ -1997,15 +2212,15 @@ function parseToolArguments(value) {
1997
2212
  if (!value) return {};
1998
2213
  try {
1999
2214
  const parsed = JSON.parse(value);
2000
- return isRecord3(parsed) ? parsed : {};
2215
+ return isRecord4(parsed) ? parsed : {};
2001
2216
  } catch {
2002
2217
  return {};
2003
2218
  }
2004
2219
  }
2005
2220
  function buildToolCalls(value) {
2006
2221
  if (!Array.isArray(value)) return [];
2007
- return value.filter(isRecord3).map((raw) => {
2008
- const fn = isRecord3(raw.function) ? raw.function : {};
2222
+ return value.filter(isRecord4).map((raw) => {
2223
+ const fn = isRecord4(raw.function) ? raw.function : {};
2009
2224
  const name = String(fn.name ?? "");
2010
2225
  return {
2011
2226
  id: String(raw.id ?? ""),
@@ -2021,23 +2236,40 @@ function buildBlocks(message, toolCalls) {
2021
2236
  const displayContent = message.role === "user" ? message._display_content : void 0;
2022
2237
  const stored = message._blocks;
2023
2238
  const blocks = [];
2239
+ const storedToolCallIds = /* @__PURE__ */ new Set();
2024
2240
  if (displayContent !== void 0) {
2025
2241
  blocks.push({ type: "text", content: displayContent });
2026
2242
  } else if (Array.isArray(stored)) {
2027
2243
  for (const raw of stored) {
2028
- if (!isRecord3(raw)) continue;
2244
+ if (!isRecord4(raw)) continue;
2029
2245
  const type3 = String(raw.type ?? "");
2030
2246
  if (type3 === "thinking") blocks.push({ type: type3, content: raw.thinking ?? raw.content ?? "" });
2031
2247
  if (type3 === "text") blocks.push({ type: type3, content: raw.text ?? raw.content ?? "" });
2248
+ if (type3 === "tool_call") {
2249
+ const toolCallId = String(raw.id ?? "");
2250
+ const toolCall = toolCalls.find((candidate) => candidate.id === toolCallId);
2251
+ if (!toolCall) continue;
2252
+ storedToolCallIds.add(toolCallId);
2253
+ blocks.push({
2254
+ type: "tool_use",
2255
+ content: toolCall.arguments,
2256
+ tool_call_id: toolCall.id,
2257
+ tool_name: toolCall.tool_name,
2258
+ display_name: toolCall.display_name
2259
+ });
2260
+ }
2032
2261
  }
2033
2262
  } else if (message.content !== void 0 && message.content !== "") {
2034
2263
  blocks.push({ type: "text", content: message.content });
2035
2264
  }
2036
2265
  for (const toolCall of toolCalls) {
2266
+ if (storedToolCallIds.has(toolCall.id)) continue;
2037
2267
  blocks.push({
2038
2268
  type: "tool_use",
2039
- content: { name: toolCall.tool_name, arguments: toolCall.arguments },
2040
- tool_call_id: toolCall.id
2269
+ content: toolCall.arguments,
2270
+ tool_call_id: toolCall.id,
2271
+ tool_name: toolCall.tool_name,
2272
+ display_name: toolCall.display_name
2041
2273
  });
2042
2274
  }
2043
2275
  return blocks;
@@ -2058,7 +2290,7 @@ function markerTurn(id, sequence, loopId, type3, content, toolCallId = null) {
2058
2290
  duration_ms: 0
2059
2291
  };
2060
2292
  }
2061
- function isRecord3(value) {
2293
+ function isRecord4(value) {
2062
2294
  return typeof value === "object" && value !== null && !Array.isArray(value);
2063
2295
  }
2064
2296
  function stringOrNull(value) {
@@ -2094,7 +2326,8 @@ function toCreateSessionPayload(request) {
2094
2326
  solution_snapshot_session_id: request.solution_snapshot_session_id ?? null,
2095
2327
  compaction_ratio: request.compaction_ratio ?? null,
2096
2328
  ...request.runtime_type ? { runtime_type: request.runtime_type } : {},
2097
- ...request.daemon_id ? { daemon_id: request.daemon_id } : {}
2329
+ ...request.daemon_id ? { daemon_id: request.daemon_id } : {},
2330
+ ...request.agent_runtime_id ? { agent_runtime_id: request.agent_runtime_id } : {}
2098
2331
  };
2099
2332
  }
2100
2333
  var builtinSolutionIds = /* @__PURE__ */ new Set([
@@ -2708,6 +2941,36 @@ var LEGACY_FORK_CONTEXT_MARKERS = [
2708
2941
  "\u53EF\u901A\u8FC7 `read_file` \u8BFB\u53D6\u3002",
2709
2942
  "# \u4F60\u7684\u4EFB\u52A1"
2710
2943
  ];
2944
+ var INTERNAL_CHAT_ERROR_PATTERNS = [
2945
+ /\b(?:ENOENT|EACCES|EPERM)\b/i,
2946
+ /\[(?:Errno -?\d+|WinError \d+)\]/i,
2947
+ /\b(?:ECONNRESET|ECONNREFUSED|ETIMEDOUT|EHOSTUNREACH|ENETUNREACH|ENOTFOUND|EAI_AGAIN)\b/i,
2948
+ /\b(?:Connection refused|Connection reset by peer|All connection attempts failed|getaddrinfo failed)\b/i,
2949
+ /(?:^|[\s("'`])\/(?:root|home|Users|tmp|var|opt|etc)\//,
2950
+ /[A-Za-z]:\\(?:Users|Windows|Program Files|AppData)\\/i,
2951
+ /(?:^|\n)Traceback \(most recent call last\):/,
2952
+ /(?:^|\n)\s*at\s+\S+(?:\s+\(|:\d+:\d+)/,
2953
+ /(?:^|\n)File "[^"]+", line \d+/
2954
+ ];
2955
+ var SAFE_CHAT_ERROR_PREFIXES = [
2956
+ "\u6C99\u76D2\u8FD0\u884C\u73AF\u5883\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
2957
+ "\u6A21\u578B\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5\u6A21\u578B\u914D\u7F6E\u540E\u91CD\u8BD5\u3002"
2958
+ ];
2959
+ var GENERIC_CHAT_ERROR = "\u6D88\u606F\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002";
2960
+ function chatErrorForDisplay(message, developer = false) {
2961
+ const normalized = message.trim();
2962
+ if (!developer) {
2963
+ const safePrefix = SAFE_CHAT_ERROR_PREFIXES.find((prefix) => normalized.startsWith(prefix));
2964
+ if (safePrefix) return safePrefix;
2965
+ }
2966
+ if (!developer && /no source matches this model/i.test(normalized)) {
2967
+ return "\u5F53\u524D\u6A21\u578B\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u6216\u91CD\u65B0\u9009\u62E9\u6A21\u578B\u3002";
2968
+ }
2969
+ if (!developer && INTERNAL_CHAT_ERROR_PATTERNS.some((pattern) => pattern.test(normalized))) {
2970
+ return GENERIC_CHAT_ERROR;
2971
+ }
2972
+ return normalized || GENERIC_CHAT_ERROR;
2973
+ }
2711
2974
  function normalizeMessageContent(content) {
2712
2975
  if (typeof content === "string") return content;
2713
2976
  if (Array.isArray(content)) return content;
@@ -2903,10 +3166,10 @@ function inferLoopStatusFromMessages(messages) {
2903
3166
  }
2904
3167
  function inferLoopStatusFromTurns(turns, messages) {
2905
3168
  const latestAgentNotification = [...turns].reverse().flatMap((turn) => turn.blocks).find((block) => {
2906
- if (block.type !== "system_notification" || !isRecord4(block.content)) return false;
3169
+ if (block.type !== "system_notification" || !isRecord5(block.content)) return false;
2907
3170
  return block.content.notification_type === "agent:start" || block.content.notification_type === "agent:end";
2908
3171
  });
2909
- if (latestAgentNotification?.type === "system_notification" && isRecord4(latestAgentNotification.content)) {
3172
+ if (latestAgentNotification?.type === "system_notification" && isRecord5(latestAgentNotification.content)) {
2910
3173
  const notificationType = latestAgentNotification.content.notification_type;
2911
3174
  const status = latestAgentNotification.content.status;
2912
3175
  if (notificationType === "agent:start" || status === "running") return "running";
@@ -2915,17 +3178,47 @@ function inferLoopStatusFromTurns(turns, messages) {
2915
3178
  }
2916
3179
  return inferLoopStatusFromMessages(messages);
2917
3180
  }
2918
- function isRecord4(value) {
3181
+ function isRecord5(value) {
2919
3182
  return typeof value === "object" && value !== null && !Array.isArray(value);
2920
3183
  }
3184
+ function toSelectionMap(value) {
3185
+ if (!isRecord5(value)) return {};
3186
+ const entries = Object.entries(value).map(([questionKey, optionIndexes]) => {
3187
+ if (!Array.isArray(optionIndexes)) return null;
3188
+ const parsedIndexes = optionIndexes.map((item) => typeof item === "number" ? item : Number(item)).filter((item) => Number.isInteger(item));
3189
+ return [Number(questionKey), parsedIndexes];
3190
+ }).filter((entry) => entry !== null);
3191
+ return Object.fromEntries(entries);
3192
+ }
3193
+ function toCustomMap(value) {
3194
+ if (!isRecord5(value)) return {};
3195
+ const entries = Object.entries(value).filter(([, text]) => typeof text === "string").map(([questionKey, text]) => [Number(questionKey), text]);
3196
+ return Object.fromEntries(entries);
3197
+ }
3198
+ function extractAskAnswers(turns) {
3199
+ const answers = {};
3200
+ for (const turn of turns) {
3201
+ for (const block of turn.blocks) {
3202
+ if (block.type !== "ask_user_answer" || typeof block.tool_call_id !== "string") continue;
3203
+ if (!isRecord5(block.content)) continue;
3204
+ const note = typeof block.content.note === "string" ? block.content.note.trim() : "";
3205
+ answers[block.tool_call_id] = {
3206
+ selections: toSelectionMap(block.content.selections),
3207
+ custom: toCustomMap(block.content.custom),
3208
+ ...note ? { note } : {}
3209
+ };
3210
+ }
3211
+ }
3212
+ return answers;
3213
+ }
2921
3214
  function parentForkToolCallIdFromTurn(turn) {
2922
3215
  if (typeof turn.parent_fork_tool_call_id === "string" && turn.parent_fork_tool_call_id.length > 0) {
2923
3216
  return turn.parent_fork_tool_call_id;
2924
3217
  }
2925
3218
  for (const block of turn.blocks) {
2926
- if (block.type !== "system_notification" || !isRecord4(block.content)) continue;
3219
+ if (block.type !== "system_notification" || !isRecord5(block.content)) continue;
2927
3220
  const metadata = block.content.metadata;
2928
- if (!isRecord4(metadata)) continue;
3221
+ if (!isRecord5(metadata)) continue;
2929
3222
  const parentId = metadata.parent_fork_tool_call_id;
2930
3223
  if (typeof parentId === "string" && parentId.length > 0) return parentId;
2931
3224
  }
@@ -2942,16 +3235,16 @@ function buildMessageContent2(turn) {
2942
3235
  }
2943
3236
  function workspaceNotificationContent(turn) {
2944
3237
  const block = turn.blocks.find(
2945
- (candidate) => candidate.type === "system_notification" && isRecord4(candidate.content) && candidate.content.notification_type === "workspace_change"
3238
+ (candidate) => candidate.type === "system_notification" && isRecord5(candidate.content) && candidate.content.notification_type === "workspace_change"
2946
3239
  );
2947
- if (!block || !isRecord4(block.content)) return "";
3240
+ if (!block || !isRecord5(block.content)) return "";
2948
3241
  const title = typeof block.content.title === "string" ? block.content.title.trim() : "";
2949
3242
  const detail = typeof block.content.detail === "string" ? block.content.detail.trim() : "";
2950
3243
  return [title ? `**${title}**` : "", detail].filter(Boolean).join("\n\n");
2951
3244
  }
2952
3245
  function askUserAnswerContent(turn) {
2953
3246
  const answerBlock = turn.blocks.find((block) => block.type === "ask_user_answer");
2954
- if (!answerBlock || !isRecord4(answerBlock.content)) return null;
3247
+ if (!answerBlock || !isRecord5(answerBlock.content)) return null;
2955
3248
  const answer = answerBlock.content.answer;
2956
3249
  return typeof answer === "string" && answer.trim().length > 0 ? answer : null;
2957
3250
  }
@@ -2971,6 +3264,19 @@ function extractModeFromBlocks(blocks) {
2971
3264
  return null;
2972
3265
  }
2973
3266
  function projectionToMessage(turn) {
3267
+ if (turn.kind === "context") {
3268
+ const context = contextProjectionData(turn);
3269
+ if (!context) return null;
3270
+ return {
3271
+ role: "assistant",
3272
+ content: "",
3273
+ kind: "context",
3274
+ context,
3275
+ loop_name: turn.loop_id,
3276
+ entry_id: turn.turn_id,
3277
+ status: turn.status
3278
+ };
3279
+ }
2974
3280
  if (turn.kind === "compaction" && turn.compaction_id) {
2975
3281
  return {
2976
3282
  role: "assistant",
@@ -3140,6 +3446,25 @@ function orderTurns(turns) {
3140
3446
  }
3141
3447
  return turns;
3142
3448
  }
3449
+ function reconcileOptimisticUserTurns(currentTurns, incomingTurns) {
3450
+ const currentById = new Map(currentTurns.map((turn) => [turn.turn_id, turn]));
3451
+ const optimisticUserTurn = currentTurns.find(
3452
+ (turn) => turn.role === "user" && (turn.kind ?? "message") === "message" && turn.turn_id.startsWith("local-user-")
3453
+ );
3454
+ let optimisticUserMatched = false;
3455
+ return incomingTurns.map((turn) => {
3456
+ const current = currentById.get(turn.turn_id);
3457
+ if (current) {
3458
+ const currentRenderId = current.client_render_id;
3459
+ return turn.client_render_id || !currentRenderId ? turn : { ...turn, client_render_id: currentRenderId };
3460
+ }
3461
+ if (!optimisticUserMatched && optimisticUserTurn && turn.role === "user" && (turn.kind ?? "message") === "message" && !turn.turn_id.startsWith("local-user-")) {
3462
+ optimisticUserMatched = true;
3463
+ return { ...turn, client_render_id: optimisticUserTurn.turn_id };
3464
+ }
3465
+ return turn;
3466
+ });
3467
+ }
3143
3468
  function areAgentLoopsEqual(left, right) {
3144
3469
  if (!left) return Object.keys(right).length === 0;
3145
3470
  const leftEntries = Object.entries(left);
@@ -3151,7 +3476,7 @@ function areAgentLoopsEqual(left, right) {
3151
3476
  });
3152
3477
  }
3153
3478
  function withTurns(state, turns) {
3154
- const orderedTurns = orderTurns(turns);
3479
+ const orderedTurns = orderTurns(reconcileOptimisticUserTurns(state.turns, turns));
3155
3480
  const { messages, agentLoops, activeCompaction } = materialize(orderedTurns);
3156
3481
  const isWaitingForInput = orderedTurns.some(
3157
3482
  (turn) => turn.tool_calls.some((toolCall) => toolCall.status === "awaiting_answer")
@@ -3161,14 +3486,31 @@ function withTurns(state, turns) {
3161
3486
  const preservedErrors = lastTurnId ? state.messages.filter(
3162
3487
  (m) => m.role === "error" && typeof m.entry_id === "string" && m.entry_id.startsWith(`${ERROR_ANCHOR_PREFIX}${lastTurnId}:`)
3163
3488
  ) : [];
3164
- const mergedMessages = preservedErrors.length > 0 ? [...messages, ...preservedErrors] : messages;
3489
+ const turnById = new Map(orderedTurns.map((turn) => [turn.turn_id, turn]));
3490
+ const messagesWithRenderIds = messages.map((message) => {
3491
+ if (!message.entry_id) return message;
3492
+ const renderId = turnById.get(message.entry_id)?.client_render_id;
3493
+ return renderId ? { ...message, render_id: renderId } : message;
3494
+ });
3495
+ const mergedMessages = preservedErrors.length > 0 ? [...messagesWithRenderIds, ...preservedErrors] : messagesWithRenderIds;
3165
3496
  const latestMode = [...orderedTurns].reverse().map((turn) => extractModeFromBlocks(turn.blocks)).find((mode) => mode !== null);
3497
+ const askAnswersFromHistory = extractAskAnswers(orderedTurns);
3498
+ const presentToolCallIds = new Set(
3499
+ orderedTurns.flatMap((turn) => turn.tool_calls.map((toolCall) => toolCall.id))
3500
+ );
3501
+ const askAnswers = { ...askAnswersFromHistory };
3502
+ for (const [toolCallId, answer] of Object.entries(state.askAnswers)) {
3503
+ if (!(toolCallId in askAnswers) && presentToolCallIds.has(toolCallId)) {
3504
+ askAnswers[toolCallId] = answer;
3505
+ }
3506
+ }
3166
3507
  return {
3167
3508
  ...state,
3168
3509
  turns: orderedTurns,
3169
3510
  messages: mergedMessages,
3170
3511
  agentLoops: stableAgentLoops,
3171
3512
  activeCompaction,
3513
+ askAnswers,
3172
3514
  mode: latestMode ?? state.mode,
3173
3515
  // 历史恢复时 chat:end(paused) 可能已不在 socket replay 缓存中;
3174
3516
  // awaiting_answer 是投影里的持久权威状态,必须据此恢复问答交互。
@@ -3196,14 +3538,17 @@ function addUserMessage(state, content) {
3196
3538
  }
3197
3539
  function upsertTurn(state, turn) {
3198
3540
  let existing = [...state.turns];
3199
- if (turn.role === "user" && !turn.turn_id.startsWith("local-user-")) {
3200
- existing = existing.filter((t) => !t.turn_id.startsWith("local-user-"));
3541
+ const [nextTurn] = reconcileOptimisticUserTurns(existing, [turn]);
3542
+ if (nextTurn.client_render_id && turn.role === "user") {
3543
+ existing = existing.filter(
3544
+ (item) => !(item.role === "user" && item.turn_id.startsWith("local-user-"))
3545
+ );
3201
3546
  }
3202
- const index = existing.findIndex((item) => item.turn_id === turn.turn_id);
3547
+ const index = existing.findIndex((item) => item.turn_id === nextTurn.turn_id);
3203
3548
  if (index >= 0) {
3204
- existing[index] = turn;
3549
+ existing[index] = nextTurn;
3205
3550
  } else {
3206
- existing.push(turn);
3551
+ existing.push(nextTurn);
3207
3552
  }
3208
3553
  return withTurns(state, existing);
3209
3554
  }
@@ -3255,18 +3600,18 @@ function markStreamingTurns(state, turnStatus, toolCallStatus) {
3255
3600
  // src/session/agent-session.ts
3256
3601
  var OOM_MESSAGE = "\u6C99\u76D2\u5185\u5B58\u4F7F\u7528\u8D85\u51FA\u9650\u5236\uFF0C\u5DF2\u81EA\u52A8\u91CD\u542F\u3002\u5982\u679C\u7ECF\u5E38\u89E6\u53D1\uFF0C\u53EF\u4EE5\u8054\u7CFB\u7BA1\u7406\u5458\u8C03\u6574\u989D\u5EA6\u3002";
3257
3602
  var OOM_KEYWORDS_RE = /(?:\b(?:exit_code|ExitedWith|exited with)|退出码)\D*(?<!\d)137(?!\d)/i;
3258
- function isRecord5(value) {
3603
+ function isRecord6(value) {
3259
3604
  return typeof value === "object" && value !== null && !Array.isArray(value);
3260
3605
  }
3261
3606
  function isOomText(value) {
3262
3607
  return typeof value === "string" && OOM_KEYWORDS_RE.test(value);
3263
3608
  }
3264
3609
  function parseJsonRecord(value) {
3265
- if (isRecord5(value)) return value;
3610
+ if (isRecord6(value)) return value;
3266
3611
  if (typeof value !== "string") return null;
3267
3612
  try {
3268
3613
  const parsed = JSON.parse(value);
3269
- return isRecord5(parsed) ? parsed : null;
3614
+ return isRecord6(parsed) ? parsed : null;
3270
3615
  } catch {
3271
3616
  return null;
3272
3617
  }
@@ -3313,7 +3658,7 @@ function hasChatRunEvent2(events) {
3313
3658
  );
3314
3659
  }
3315
3660
  function isUiMetaLike(value) {
3316
- return isRecord5(value) && ("resourceHTML" in value || "resourceUri" in value || "resourceURI" in value);
3661
+ return isRecord6(value) && ("resourceHTML" in value || "resourceUri" in value || "resourceURI" in value);
3317
3662
  }
3318
3663
  var AgentSession = class _AgentSession {
3319
3664
  sessionId;
@@ -3335,6 +3680,7 @@ var AgentSession = class _AgentSession {
3335
3680
  replayQueue = Promise.resolve();
3336
3681
  pendingReplayMessage = null;
3337
3682
  pendingReplayMode;
3683
+ pendingReplayKbIds;
3338
3684
  recentChatEndAt = 0;
3339
3685
  disposed = false;
3340
3686
  getAppContext = null;
@@ -3359,7 +3705,6 @@ var AgentSession = class _AgentSession {
3359
3705
  const session = new _AgentSession(
3360
3706
  sessionId,
3361
3707
  {
3362
- streamTokens: false,
3363
3708
  emitEvent: readonlyError,
3364
3709
  emitWithAck: async () => readonlyError(),
3365
3710
  ensureConnected: readonlyError,
@@ -3464,7 +3809,7 @@ var AgentSession = class _AgentSession {
3464
3809
  if (this.getAppContext) {
3465
3810
  try {
3466
3811
  const rawContext = await this.getAppContext();
3467
- if (!isRecord5(rawContext)) {
3812
+ if (!isRecord6(rawContext)) {
3468
3813
  throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE JSON \u5BF9\u8C61");
3469
3814
  }
3470
3815
  const serialized = JSON.stringify(rawContext);
@@ -3472,7 +3817,7 @@ var AgentSession = class _AgentSession {
3472
3817
  throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE\u53EF\u5E8F\u5217\u5316\u7684 JSON \u5BF9\u8C61");
3473
3818
  }
3474
3819
  const normalized = JSON.parse(serialized);
3475
- if (!isRecord5(normalized)) {
3820
+ if (!isRecord6(normalized)) {
3476
3821
  throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE JSON \u5BF9\u8C61");
3477
3822
  }
3478
3823
  appContext = normalized;
@@ -3486,13 +3831,11 @@ var AgentSession = class _AgentSession {
3486
3831
  let optimisticTurnId = null;
3487
3832
  let optimisticAskAnswer;
3488
3833
  let previousAskAnswer;
3489
- if (!(askUserAnswer && typeof askUserAnswer.tool_call_id === "string")) {
3490
- this.update((state) => {
3491
- const next = addUserMessage(state, content);
3492
- optimisticTurnId = next.turns.at(-1)?.turn_id ?? null;
3493
- return next;
3494
- });
3495
- }
3834
+ this.update((state) => {
3835
+ const next = addUserMessage(state, content);
3836
+ optimisticTurnId = next.turns.at(-1)?.turn_id ?? null;
3837
+ return next;
3838
+ });
3496
3839
  this.update((s) => ({ ...s, isStreaming: true }));
3497
3840
  if (askUserAnswer && typeof askUserAnswer.tool_call_id === "string") {
3498
3841
  const { tool_call_id, ...rest } = askUserAnswer;
@@ -3506,6 +3849,7 @@ var AgentSession = class _AgentSession {
3506
3849
  if (!options.replayDecision) {
3507
3850
  this.pendingReplayMessage = content;
3508
3851
  this.pendingReplayMode = options.mode;
3852
+ this.pendingReplayKbIds = options.kbIds;
3509
3853
  }
3510
3854
  const payload = {
3511
3855
  session_id: this.sessionId,
@@ -3516,7 +3860,8 @@ var AgentSession = class _AgentSession {
3516
3860
  thinking_override: options.thinkingOverride,
3517
3861
  whatif: options.whatif,
3518
3862
  replay_decision: options.replayDecision,
3519
- app_context: appContext
3863
+ app_context: appContext,
3864
+ kb_ids: options.kbIds && options.kbIds.length > 0 ? options.kbIds : void 0
3520
3865
  };
3521
3866
  try {
3522
3867
  await this.ensureJoined();
@@ -3552,6 +3897,7 @@ var AgentSession = class _AgentSession {
3552
3897
  rollbackOptimisticSend(optimisticTurnId, askUserAnswer, optimisticAskAnswer, previousAskAnswer, message) {
3553
3898
  this.pendingReplayMessage = null;
3554
3899
  this.pendingReplayMode = void 0;
3900
+ this.pendingReplayKbIds = void 0;
3555
3901
  this.update((current) => {
3556
3902
  let next = optimisticTurnId ? withTurns(
3557
3903
  current,
@@ -3733,7 +4079,7 @@ var AgentSession = class _AgentSession {
3733
4079
  _handleTurnEvents(events) {
3734
4080
  if (!events.length) return;
3735
4081
  const terminalChatEndStatus = extractTerminalChatEndStatus(events);
3736
- if (!terminalChatEndStatus && hasChatRunEvent2(events)) {
4082
+ if (!terminalChatEndStatus && hasChatRunEvent2(events) && this.state.status !== "waiting_for_input") {
3737
4083
  this.markRunningIfIdle();
3738
4084
  }
3739
4085
  if (this.state.turns.length) {
@@ -3805,8 +4151,10 @@ var AgentSession = class _AgentSession {
3805
4151
  _handleReplayMismatch(data) {
3806
4152
  const message = this.pendingReplayMessage ?? data.actual_message ?? "";
3807
4153
  const mode = this.pendingReplayMode;
4154
+ const kbIds = this.pendingReplayKbIds;
3808
4155
  this.pendingReplayMessage = null;
3809
4156
  this.pendingReplayMode = void 0;
4157
+ this.pendingReplayKbIds = void 0;
3810
4158
  const respond = (decision) => {
3811
4159
  void this.runtime.emitWithAck(
3812
4160
  "chat:send",
@@ -3814,7 +4162,8 @@ var AgentSession = class _AgentSession {
3814
4162
  session_id: this.sessionId,
3815
4163
  message,
3816
4164
  mode,
3817
- replay_decision: decision
4165
+ replay_decision: decision,
4166
+ kb_ids: kbIds && kbIds.length > 0 ? kbIds : void 0
3818
4167
  },
3819
4168
  3e4
3820
4169
  ).then((response) => {
@@ -3855,9 +4204,9 @@ var AgentSession = class _AgentSession {
3855
4204
  detail,
3856
4205
  status,
3857
4206
  loopId,
3858
- metadata: isRecord5(notification.metadata) ? notification.metadata : void 0
4207
+ metadata: isRecord6(notification.metadata) ? notification.metadata : void 0
3859
4208
  });
3860
- if (notificationType === "bg:started" && isRecord5(notification.metadata)) {
4209
+ if (notificationType === "bg:started" && isRecord6(notification.metadata)) {
3861
4210
  const taskId = typeof notification.metadata.task_id === "string" ? notification.metadata.task_id : "";
3862
4211
  if (taskId) {
3863
4212
  this.emitter.emit("backgroundTask", {
@@ -3987,7 +4336,7 @@ var AgentSession = class _AgentSession {
3987
4336
  title: ui.title ?? "\u5DE5\u5177\u9884\u89C8"
3988
4337
  });
3989
4338
  }
3990
- if (block.type === "system_notification" && isRecord5(block.content)) {
4339
+ if (block.type === "system_notification" && isRecord6(block.content)) {
3991
4340
  this._handleSystemNotification(turn.loop_id, block.content);
3992
4341
  }
3993
4342
  }
@@ -4093,8 +4442,7 @@ var AgentSession = class _AgentSession {
4093
4442
  {
4094
4443
  session_id: this.sessionId,
4095
4444
  event_format: "raw",
4096
- resume: true,
4097
- stream_tokens: this.runtime.streamTokens
4445
+ resume: true
4098
4446
  },
4099
4447
  5e3
4100
4448
  ).then((response) => {
@@ -4231,7 +4579,6 @@ var SessionHub = class {
4231
4579
  }
4232
4580
  runtimeFor() {
4233
4581
  return {
4234
- streamTokens: this.client.options?.streamTokens !== false,
4235
4582
  emitEvent: (event, data) => {
4236
4583
  ;
4237
4584
  this.client.socket().emit(event, data);
@@ -4471,7 +4818,7 @@ function resolveAuthToken(options) {
4471
4818
 
4472
4819
  // src/version.ts
4473
4820
  var SDK_NAME = "agent-client";
4474
- var SDK_VERSION = true ? "2610.0.0-beta.3" : "1.1.1";
4821
+ var SDK_VERSION = true ? "2610.0.0-beta.30" : "1.1.1";
4475
4822
 
4476
4823
  // src/socket.ts
4477
4824
  function withSdkIdentity(auth) {
@@ -4499,6 +4846,7 @@ var BladeClient = class {
4499
4846
  options;
4500
4847
  auth;
4501
4848
  headless;
4849
+ computers;
4502
4850
  models;
4503
4851
  sessions;
4504
4852
  /** 实时会话中枢:client.sessions.connect() 内部使用,一般不直接访问。 */
@@ -4510,6 +4858,7 @@ var BladeClient = class {
4510
4858
  };
4511
4859
  this.auth = new AuthResource(this);
4512
4860
  this.headless = new HeadlessResource(this);
4861
+ this.computers = new ComputersResource(this);
4513
4862
  this.models = new ModelsResource(this);
4514
4863
  this.sessions = new SessionsResource(this);
4515
4864
  this.hub = new SessionHub(this);
@@ -4864,6 +5213,89 @@ function parseXhrHeaders(rawHeaders) {
4864
5213
  return headers;
4865
5214
  }
4866
5215
 
5216
+ // src/platform-endpoints.ts
5217
+ var PLATFORM_SERVICE_NAMES = [
5218
+ "os",
5219
+ "hub",
5220
+ "agent",
5221
+ "oauth",
5222
+ "llmGateway",
5223
+ "gitea",
5224
+ "tile"
5225
+ ];
5226
+ var EMPTY_PLATFORM_ENDPOINTS = Object.freeze({
5227
+ services: Object.freeze({})
5228
+ });
5229
+ function defaultDocumentBaseUrl() {
5230
+ if (typeof document === "undefined") return void 0;
5231
+ if (document.querySelector("base[href]")) return document.baseURI;
5232
+ if (typeof location !== "undefined" && location.origin) return `${location.origin}/`;
5233
+ return document.baseURI;
5234
+ }
5235
+ function isRecord7(value) {
5236
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5237
+ }
5238
+ function normalizeServiceUrl(value) {
5239
+ if (typeof value !== "string" || !value.trim()) return null;
5240
+ if (value.includes("?") || value.includes("#")) return null;
5241
+ try {
5242
+ const url = new URL(value.trim());
5243
+ if (url.protocol !== "http:" && url.protocol !== "https:") return null;
5244
+ if (url.username || url.password || url.search || url.hash) return null;
5245
+ return url.toString().replace(/\/$/, "");
5246
+ } catch {
5247
+ return null;
5248
+ }
5249
+ }
5250
+ function parsePlatformEndpoints(value) {
5251
+ if (!isRecord7(value) || !isRecord7(value.services)) return null;
5252
+ const services = {};
5253
+ for (const name of PLATFORM_SERVICE_NAMES) {
5254
+ if (!(name in value.services)) continue;
5255
+ const url = normalizeServiceUrl(value.services[name]);
5256
+ if (!url) return null;
5257
+ if (name === "agent" && new URL(url).pathname !== "/") return null;
5258
+ services[name] = url;
5259
+ }
5260
+ return { services };
5261
+ }
5262
+ async function loadPlatformEndpoints(options = {}) {
5263
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
5264
+ const baseUrl = options.baseUrl ?? defaultDocumentBaseUrl();
5265
+ if (!fetchImpl || !baseUrl) return EMPTY_PLATFORM_ENDPOINTS;
5266
+ const controller = new AbortController();
5267
+ const timeout = globalThis.setTimeout(() => controller.abort(), options.timeoutMs ?? 3e3);
5268
+ try {
5269
+ const response = await fetchImpl(new URL("config.json", baseUrl), {
5270
+ cache: "no-store",
5271
+ credentials: "same-origin",
5272
+ headers: { Accept: "application/json" },
5273
+ signal: controller.signal
5274
+ });
5275
+ if (!response.ok) return EMPTY_PLATFORM_ENDPOINTS;
5276
+ return parsePlatformEndpoints(await response.json()) ?? EMPTY_PLATFORM_ENDPOINTS;
5277
+ } catch {
5278
+ return EMPTY_PLATFORM_ENDPOINTS;
5279
+ } finally {
5280
+ globalThis.clearTimeout(timeout);
5281
+ }
5282
+ }
5283
+ function resolveServiceUrl(endpoints, name, path = "") {
5284
+ const baseUrl = endpoints.services[name];
5285
+ if (!baseUrl) return null;
5286
+ if (!path) return baseUrl;
5287
+ const pathReference = path.trimStart();
5288
+ if (path.includes("\\") || pathReference.startsWith("//") || /^[a-z][a-z0-9+.-]*:/i.test(pathReference)) {
5289
+ return null;
5290
+ }
5291
+ const base = new URL(`${baseUrl}/`);
5292
+ const target = new URL(path.replace(/^\/+/, ""), base);
5293
+ if (target.origin !== base.origin || !target.pathname.startsWith(base.pathname)) {
5294
+ return null;
5295
+ }
5296
+ return target.toString();
5297
+ }
5298
+
4867
5299
  // src/commands/protocol.ts
4868
5300
  function isCommandEnvelope(value) {
4869
5301
  if (typeof value !== "object" || value === null) return false;
@@ -4967,7 +5399,9 @@ export {
4967
5399
  BladeApiError,
4968
5400
  BladeClient,
4969
5401
  ClientProjectionBuilder,
5402
+ ComputersResource,
4970
5403
  DEFAULT_REPLAY_SPEED,
5404
+ EMPTY_PLATFORM_ENDPOINTS,
4971
5405
  LayoutType,
4972
5406
  ModelsResource,
4973
5407
  SDK_NAME,
@@ -4980,11 +5414,16 @@ export {
4980
5414
  TaskStatus,
4981
5415
  acceptedPostChatFollowupCompletesLatestRun,
4982
5416
  buildMessageContent,
5417
+ canToggleComputer,
5418
+ chatErrorForDisplay,
5419
+ computerState,
4983
5420
  connectEmbedded,
4984
5421
  contentPreview,
5422
+ contextProjectionData,
4985
5423
  createInitialSessionState,
4986
5424
  createSocket,
4987
5425
  extractTextAttachments,
5426
+ getContextDisplayState,
4988
5427
  getFileParts,
4989
5428
  getImageParts,
4990
5429
  getTextContent,
@@ -4993,7 +5432,11 @@ export {
4993
5432
  isHiddenInternalMessage,
4994
5433
  isInboundEnvelope,
4995
5434
  latestPostChatFollowup,
5435
+ loadPlatformEndpoints,
4996
5436
  normalizeMessageContent,
5437
+ reconcileOptimisticUserTurns,
5438
+ resolveServiceUrl,
5439
+ sortComputers,
4997
5440
  toReplaySnapshot,
4998
5441
  transformSlashCommand
4999
5442
  };