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

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,126 @@ 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 = nonEmptyString(context.display_title) ?? BUILTIN_CONTEXT_TITLES[context.context_kind] ?? "\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
+ function groupAdjacentContextRuns(kinds) {
1114
+ const runs = [];
1115
+ let current = null;
1116
+ kinds.forEach((kind, index) => {
1117
+ if (kind === null) {
1118
+ current = null;
1119
+ return;
1120
+ }
1121
+ if (current === null) {
1122
+ current = [];
1123
+ runs.push(current);
1124
+ }
1125
+ current.push(index);
1126
+ });
1127
+ return runs;
1128
+ }
1129
+ function getContextGroupDisplayState(contexts) {
1130
+ const count = contexts.length;
1131
+ if (count === 1) {
1132
+ const single = getContextDisplayState(contexts[0]);
1133
+ return { title: single.title, summary: single.summary, count };
1134
+ }
1135
+ const removed = contexts.filter(
1136
+ (context) => context.context_action === "removed"
1137
+ ).length;
1138
+ const updated = count - removed;
1139
+ const parts = [];
1140
+ if (updated > 0) parts.push(`${updated} \u9879\u5DF2\u66F4\u65B0`);
1141
+ if (removed > 0) parts.push(`${removed} \u9879\u5DF2\u505C\u7528`);
1142
+ return {
1143
+ title: "\u5BF9\u8BDD\u80CC\u666F",
1144
+ summary: parts.join(" \xB7 ") || `${count} \u9879`,
1145
+ count
1146
+ };
1147
+ }
1148
+
1149
+ // src/shared/projection/context.ts
1150
+ function isRecord2(value) {
1151
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1152
+ }
1153
+ function buildContextTurn(entry, sequence) {
1154
+ if (entry.kind !== "context" || !isRecord2(entry.source) || !isRecord2(entry.data)) {
1155
+ return null;
1156
+ }
1157
+ const display = isRecord2(entry.data.display) ? entry.data.display : {};
1158
+ const message = isRecord2(entry.message) ? entry.message : {};
1159
+ const context = contextProjectionData({
1160
+ context_kind: typeof entry.source.kind === "string" ? entry.source.kind : null,
1161
+ context_key: typeof entry.source.key === "string" ? entry.source.key : null,
1162
+ context_name: typeof entry.data.contribution_name === "string" ? entry.data.contribution_name : null,
1163
+ context_order: typeof entry.data.order === "number" ? entry.data.order : null,
1164
+ context_action: entry.data.action === "published" || entry.data.action === "retained" || entry.data.action === "removed" ? entry.data.action : null,
1165
+ context_revision: typeof entry.data.revision === "string" ? entry.data.revision : null,
1166
+ display_title: typeof display.title === "string" ? display.title : null,
1167
+ display_summary: typeof display.summary === "string" ? display.summary : null,
1168
+ sources: Array.isArray(display.sources) ? display.sources.filter(isRecord2) : []
1169
+ });
1170
+ if (!context) return null;
1171
+ const turnId = typeof entry.id === "string" && entry.id ? entry.id : `context-${sequence}`;
1172
+ return {
1173
+ id: turnId,
1174
+ sequence,
1175
+ turn_id: turnId,
1176
+ loop_id: typeof entry.loop_name === "string" && entry.loop_name ? entry.loop_name : "root",
1177
+ kind: "context",
1178
+ role: "system",
1179
+ status: "completed",
1180
+ blocks: [],
1181
+ tool_calls: [],
1182
+ model: null,
1183
+ usage: null,
1184
+ duration_ms: 0,
1185
+ context_content: Object.hasOwn(message, "content") ? message.content : null,
1186
+ ...context
1187
+ };
1188
+ }
1189
+
1026
1190
  // src/shared/projection/builder.ts
1027
1191
  function hasChatRunEvent(events) {
1028
1192
  return events.some(
@@ -1058,7 +1222,8 @@ function completedResponseText(content) {
1058
1222
  ).filter((part) => part.type === "text").map((part) => String(part.text ?? "")).join("");
1059
1223
  }
1060
1224
  function replaceTextContent(state, content) {
1061
- if (state.blocks.some((block) => block.type === "text")) return;
1225
+ const currentContent = state.blocks.filter((block) => block.type === "text").map((block) => String(block.content ?? "")).join("");
1226
+ if (currentContent === content) return;
1062
1227
  const replacement = { type: "text", content };
1063
1228
  const blocks = [];
1064
1229
  let inserted = false;
@@ -1078,6 +1243,31 @@ function replaceTextContent(state, content) {
1078
1243
  }
1079
1244
  state.blocks = blocks;
1080
1245
  }
1246
+ function applyResponseBlockOrder(state, value) {
1247
+ if (!Array.isArray(value)) return false;
1248
+ const ordered = [];
1249
+ for (const item of value) {
1250
+ if (typeof item !== "object" || item === null || Array.isArray(item)) continue;
1251
+ const block = item;
1252
+ if (block.type === "text" || block.type === "thinking") {
1253
+ const content = String(block[block.type] ?? "");
1254
+ if (content) ordered.push({ type: block.type, content });
1255
+ continue;
1256
+ }
1257
+ if (block.type !== "tool_call") continue;
1258
+ const toolCallId = String(block.id ?? "");
1259
+ const toolBlock = state.blocks.find(
1260
+ (candidate) => candidate.type === "tool_use" && candidate.tool_call_id === toolCallId
1261
+ );
1262
+ if (toolBlock) ordered.push(toolBlock);
1263
+ }
1264
+ if (ordered.length === 0 && value.length > 0) return false;
1265
+ const unrelatedBlocks = state.blocks.filter(
1266
+ (block) => block.type !== "text" && block.type !== "thinking" && block.type !== "tool_use"
1267
+ );
1268
+ state.blocks = [...ordered, ...unrelatedBlocks];
1269
+ return true;
1270
+ }
1081
1271
  var ClientProjectionBuilder = class {
1082
1272
  activeTurns = /* @__PURE__ */ new Map();
1083
1273
  lastTurnIds = /* @__PURE__ */ new Map();
@@ -1117,6 +1307,8 @@ var ClientProjectionBuilder = class {
1117
1307
  return this.onMemoryInject(loopId, payload);
1118
1308
  case "memory:inject:none":
1119
1309
  return null;
1310
+ case "context:entry":
1311
+ return this.onContextEntry(payload);
1120
1312
  case "loop:turn":
1121
1313
  return this.onLoopTurn(loopId, payload);
1122
1314
  case "mode:change":
@@ -1165,6 +1357,8 @@ var ClientProjectionBuilder = class {
1165
1357
  return this.syncPatch(state);
1166
1358
  case "llm:tool_call:created":
1167
1359
  return this.onToolCallCreated(state, payload);
1360
+ case "llm:tool_call:arguments:delta":
1361
+ return this.onToolCallArgumentsDelta(state, payload);
1168
1362
  case "llm:response:done":
1169
1363
  return this.onResponseDone(state, loopId, payload);
1170
1364
  case "tool:result:delta":
@@ -1232,6 +1426,11 @@ var ClientProjectionBuilder = class {
1232
1426
  }
1233
1427
  return null;
1234
1428
  }
1429
+ onContextEntry(payload) {
1430
+ if (!isRecord3(payload.entry)) return null;
1431
+ const turn = buildContextTurn(payload.entry, this.nextSeq());
1432
+ return turn ? [{ kind: "upsert", turn }] : null;
1433
+ }
1235
1434
  onLoopTurn(loopId, payload) {
1236
1435
  let turnId = String(payload.turn_id ?? this.nextTurnId(`turn:${loopId}`));
1237
1436
  let memoryRefs = null;
@@ -1288,11 +1487,11 @@ var ClientProjectionBuilder = class {
1288
1487
  loopId,
1289
1488
  toolCallId: optStr(payload.tool_call_id) ?? void 0,
1290
1489
  toolName: toolNameFromPayload(payload) ?? void 0,
1291
- workspacePath: isRecord2(payload.workspace_change) ? optStr(payload.workspace_change.to) ?? void 0 : void 0
1490
+ workspacePath: isRecord3(payload.workspace_change) ? optStr(payload.workspace_change.to) ?? void 0 : void 0
1292
1491
  }
1293
1492
  ];
1294
1493
  const change = payload.workspace_change;
1295
- if (isRecord2(change)) {
1494
+ if (isRecord3(change)) {
1296
1495
  const source = optStr(change.source) ?? "";
1297
1496
  const projectName = optStr(change.project_name) ?? "";
1298
1497
  const fromPath = optStr(change.from) ?? "";
@@ -1430,13 +1629,7 @@ var ClientProjectionBuilder = class {
1430
1629
  }
1431
1630
  onToolCallCreated(state, payload) {
1432
1631
  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;
1632
+ return this.onToolCallArgumentsDelta(state, payload);
1440
1633
  }
1441
1634
  const toolCallId = String(payload.id ?? "");
1442
1635
  const toolName = toolNameFromPayload(payload);
@@ -1445,6 +1638,13 @@ var ClientProjectionBuilder = class {
1445
1638
  upsertToolCall(state, toolCallId, toolName, displayName, args);
1446
1639
  return this.syncPatch(state);
1447
1640
  }
1641
+ onToolCallArgumentsDelta(state, payload) {
1642
+ const delta = String(payload.arguments_delta ?? "");
1643
+ const toolCallId = String(payload.id ?? "");
1644
+ if (!delta || !toolCallId) return null;
1645
+ appendToolCallArguments(state, toolCallId, delta);
1646
+ return this.syncPatch(state);
1647
+ }
1448
1648
  onResponseDone(state, loopId, payload) {
1449
1649
  state.responseDone = true;
1450
1650
  const model = optStr(payload.model);
@@ -1452,9 +1652,6 @@ var ClientProjectionBuilder = class {
1452
1652
  if (typeof payload.usage === "object" && payload.usage !== null) {
1453
1653
  state.usage = { ...payload.usage };
1454
1654
  }
1455
- if ("content" in payload) {
1456
- replaceTextContent(state, completedResponseText(payload.content));
1457
- }
1458
1655
  const toolCalls = payload.tool_calls;
1459
1656
  if (Array.isArray(toolCalls)) {
1460
1657
  for (const raw of toolCalls) {
@@ -1472,7 +1669,13 @@ var ClientProjectionBuilder = class {
1472
1669
  );
1473
1670
  }
1474
1671
  }
1475
- if (state.toolCalls.length === 0) {
1672
+ if (!applyResponseBlockOrder(state, payload.response_blocks) && "content" in payload) {
1673
+ replaceTextContent(state, completedResponseText(payload.content));
1674
+ }
1675
+ const isNativeProviderResponse = typeof payload.provider === "string" && payload.provider !== "";
1676
+ if (state.toolCalls.length === 0 || isNativeProviderResponse && state.toolCalls.every(
1677
+ (toolCall) => toolCall.status !== "pending" && toolCall.status !== "awaiting_answer"
1678
+ )) {
1476
1679
  return this.finalize(loopId, "completed");
1477
1680
  }
1478
1681
  return this.syncPatch(state);
@@ -1486,7 +1689,8 @@ var ClientProjectionBuilder = class {
1486
1689
  const durationMs = typeof rawDuration === "number" && rawDuration >= 0 ? Math.round(rawDuration) : null;
1487
1690
  const sl = sourceLoopFor(state.loopId, this.loopDescriptions);
1488
1691
  applyToolResult(state, toolCallId, content, status, durationMs, sl);
1489
- if (this.shouldFinalizeAfterToolResults(state)) {
1692
+ const isNativeProviderResult = typeof payload.provider === "string" && payload.provider !== "";
1693
+ if (!isNativeProviderResult && this.shouldFinalizeAfterToolResults(state)) {
1490
1694
  return this.finalize(loopId, "completed");
1491
1695
  }
1492
1696
  return this.syncPatch(state);
@@ -1527,6 +1731,11 @@ var ClientProjectionBuilder = class {
1527
1731
  const toolName = String(payload.tool_name ?? "");
1528
1732
  const displayName = this.displayNameResolver(toolName);
1529
1733
  upsertToolCall(state, toolCallId, toolName, displayName, "");
1734
+ const relatedToolCallId = String(payload.related_tool_call_id ?? "");
1735
+ if (relatedToolCallId && relatedToolCallId !== toolCallId) {
1736
+ const related = state.toolCalls.find((toolCall) => toolCall.id === relatedToolCallId);
1737
+ if (related) related.status = "awaiting_answer";
1738
+ }
1530
1739
  return this.syncPatch(state);
1531
1740
  }
1532
1741
  onChildPause(state, loopId, payload) {
@@ -1754,7 +1963,7 @@ var ClientProjectionBuilder = class {
1754
1963
  return `${prefix}:${this.syntheticCounter}:${hex}`;
1755
1964
  }
1756
1965
  };
1757
- function isRecord2(value) {
1966
+ function isRecord3(value) {
1758
1967
  return typeof value === "object" && value !== null && !Array.isArray(value);
1759
1968
  }
1760
1969
  function optStr(value) {
@@ -1783,6 +1992,7 @@ function projectHistory(entries) {
1783
1992
  const latestAssistantByLoop = /* @__PURE__ */ new Map();
1784
1993
  const pendingChildPauses = /* @__PURE__ */ new Map();
1785
1994
  const answeredToolCallIds = /* @__PURE__ */ new Set();
1995
+ const nativeToolTurns = /* @__PURE__ */ new Set();
1786
1996
  let latestRootMessage = null;
1787
1997
  let latestRootAssistant = null;
1788
1998
  let sequence = 0;
@@ -1790,7 +2000,7 @@ function projectHistory(entries) {
1790
2000
  const kind = String(entry.kind ?? "");
1791
2001
  const loopId = String(entry.loop_name ?? "root");
1792
2002
  const id = String(entry.id ?? `history-${sequence + 1}`);
1793
- if (kind === "post_chat_followup" && isRecord3(entry.data)) {
2003
+ if (kind === "post_chat_followup" && isRecord4(entry.data)) {
1794
2004
  const assistantEntryId = String(entry.data.assistant_entry_id ?? "");
1795
2005
  if (latestRootMessage?.role === "assistant" && latestRootMessage.turn_id === assistantEntryId) {
1796
2006
  const followup = normalizePostChatFollowup(entry.data, assistantEntryId);
@@ -1798,7 +2008,15 @@ function projectHistory(entries) {
1798
2008
  }
1799
2009
  continue;
1800
2010
  }
1801
- if (kind === "message" && isRecord3(entry.message)) {
2011
+ if (kind === "context") {
2012
+ const turn = buildContextTurn(entry, sequence + 1);
2013
+ if (turn) {
2014
+ sequence += 1;
2015
+ turns.push(turn);
2016
+ }
2017
+ continue;
2018
+ }
2019
+ if (kind === "message" && isRecord4(entry.message)) {
1802
2020
  const message = entry.message;
1803
2021
  const role = String(message.role ?? "");
1804
2022
  if (role === "tool") {
@@ -1830,14 +2048,35 @@ function projectHistory(entries) {
1830
2048
  blocks,
1831
2049
  tool_calls: toolCalls,
1832
2050
  model: stringOrNull(message.model),
1833
- usage: isRecord3(message._usage) ? { ...message._usage } : null,
2051
+ usage: isRecord4(message._usage) ? { ...message._usage } : null,
1834
2052
  duration_ms: numberOrZero(message._duration_ms),
1835
- started_at: typeof entry.timestamp === "string" ? entry.timestamp : void 0
2053
+ started_at: typeof entry.timestamp === "string" ? entry.timestamp : void 0,
2054
+ // fork 子会话的父锚点:历史里落在消息级 _parent_fork_tool_call_id。丢掉它,
2055
+ // 刷新后工作流步骤 fork 就配不回 workflow:run 工具调用(普通 Agent fork 有
2056
+ // root Agent 工具调用兜底配对,工作流 fork 没有,子智能体卡会整体消失)。
2057
+ parent_fork_tool_call_id: stringOrNull(message._parent_fork_tool_call_id)
1836
2058
  };
2059
+ const isNativeToolEntry = role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.some(
2060
+ (toolCall) => isRecord4(toolCall) && typeof toolCall.provider === "string"
2061
+ );
2062
+ const previousTurn = turns[turns.length - 1];
2063
+ if (role === "assistant" && previousTurn?.role === "assistant" && previousTurn.loop_id === loopId && nativeToolTurns.has(previousTurn)) {
2064
+ previousTurn.blocks.push(...turn.blocks);
2065
+ previousTurn.tool_calls.push(...turn.tool_calls);
2066
+ previousTurn.status = turn.status;
2067
+ previousTurn.model = turn.model ?? previousTurn.model;
2068
+ previousTurn.usage = turn.usage ?? previousTurn.usage;
2069
+ previousTurn.duration_ms = turn.duration_ms || previousTurn.duration_ms;
2070
+ if (turn.started_at) previousTurn.started_at = turn.started_at;
2071
+ if (loopId === "root") latestRootMessage = previousTurn;
2072
+ latestAssistantByLoop.set(loopId, previousTurn);
2073
+ continue;
2074
+ }
1837
2075
  if (loopId === "root" && (role === "user" || role === "assistant")) {
1838
2076
  latestRootMessage = turn;
1839
2077
  }
1840
2078
  turns.push(turn);
2079
+ if (isNativeToolEntry) nativeToolTurns.add(turn);
1841
2080
  if (role === "assistant") latestAssistantByLoop.set(loopId, turn);
1842
2081
  if (loopId === "root") {
1843
2082
  if (role === "assistant") latestRootAssistant = turn;
@@ -1846,10 +2085,25 @@ function projectHistory(entries) {
1846
2085
  for (const toolCall of toolCalls) toolOwners.set(toolCall.id, turn);
1847
2086
  continue;
1848
2087
  }
2088
+ if (kind === "tool_result") {
2089
+ const data = entry.data ?? {};
2090
+ const toolCallId = String(data.tool_call_id ?? "");
2091
+ const owner = toolOwners.get(toolCallId) ?? findLatestAssistant(turns, loopId);
2092
+ if (owner && toolCallId) {
2093
+ const rawContent = data.content;
2094
+ const content = typeof rawContent === "string" ? rawContent : Array.isArray(rawContent) ? rawContent : String(rawContent ?? "");
2095
+ const status = data.is_error === true ? "error" : inferToolStatus(typeof content === "string" ? content : JSON.stringify(content));
2096
+ setToolCallStatus(owner, toolCallId, status, content, null);
2097
+ if (!owner.blocks.some((block) => block.type === "tool_result" && block.tool_call_id === toolCallId)) {
2098
+ owner.blocks.push({ type: "tool_result", content, tool_call_id: toolCallId });
2099
+ }
2100
+ }
2101
+ continue;
2102
+ }
1849
2103
  if (kind === "mode_change") {
1850
2104
  turns.push(markerTurn(id, ++sequence, loopId, "mode_change", entry.data ?? {}));
1851
2105
  } else if (kind === "workspace_change") {
1852
- const change = isRecord3(entry.data) ? entry.data : {};
2106
+ const change = isRecord4(entry.data) ? entry.data : {};
1853
2107
  const source = stringOrNull(change.source) ?? "";
1854
2108
  const projectName = stringOrNull(change.project_name) ?? "";
1855
2109
  const fromPath = stringOrNull(change.from) ?? "";
@@ -1890,7 +2144,7 @@ function projectHistory(entries) {
1890
2144
  } else if (kind === "compaction" || kind === "tool_result_archive") {
1891
2145
  const data = entry.data ?? {};
1892
2146
  const compactionId = String(data.compaction_id ?? entry.id ?? `history-${sequence + 1}`);
1893
- let content = isRecord3(entry.message) ? entry.message.content : void 0;
2147
+ let content = isRecord4(entry.message) ? entry.message.content : void 0;
1894
2148
  if (kind === "tool_result_archive") {
1895
2149
  const archivedCount = Array.isArray(data.archived_files) ? data.archived_files.length : 0;
1896
2150
  content = `<compaction-summary>
@@ -1905,11 +2159,11 @@ function projectHistory(entries) {
1905
2159
  );
1906
2160
  } else if (kind === "child_pause") {
1907
2161
  const data = entry.data ?? {};
1908
- const sourceLoop = isRecord3(data.source_loop) ? data.source_loop : {};
2162
+ const sourceLoop = isRecord4(data.source_loop) ? data.source_loop : {};
1909
2163
  const childLoopId = String(data.child_loop_name ?? sourceLoop.name ?? "");
1910
2164
  const childToolCallId = String(data.child_pause_tool_call_id ?? "");
1911
2165
  const parentToolCallId = String(data.parent_fork_tool_call_id ?? "");
1912
- const pauseToolData = isRecord3(data.pause_tool_data) ? data.pause_tool_data : {};
2166
+ const pauseToolData = isRecord4(data.pause_tool_data) ? data.pause_tool_data : {};
1913
2167
  const description = String(sourceLoop.description ?? "");
1914
2168
  applyAskUserPauseToTurn(
1915
2169
  latestAssistantByLoop.get(childLoopId),
@@ -1997,15 +2251,15 @@ function parseToolArguments(value) {
1997
2251
  if (!value) return {};
1998
2252
  try {
1999
2253
  const parsed = JSON.parse(value);
2000
- return isRecord3(parsed) ? parsed : {};
2254
+ return isRecord4(parsed) ? parsed : {};
2001
2255
  } catch {
2002
2256
  return {};
2003
2257
  }
2004
2258
  }
2005
2259
  function buildToolCalls(value) {
2006
2260
  if (!Array.isArray(value)) return [];
2007
- return value.filter(isRecord3).map((raw) => {
2008
- const fn = isRecord3(raw.function) ? raw.function : {};
2261
+ return value.filter(isRecord4).map((raw) => {
2262
+ const fn = isRecord4(raw.function) ? raw.function : {};
2009
2263
  const name = String(fn.name ?? "");
2010
2264
  return {
2011
2265
  id: String(raw.id ?? ""),
@@ -2021,23 +2275,40 @@ function buildBlocks(message, toolCalls) {
2021
2275
  const displayContent = message.role === "user" ? message._display_content : void 0;
2022
2276
  const stored = message._blocks;
2023
2277
  const blocks = [];
2278
+ const storedToolCallIds = /* @__PURE__ */ new Set();
2024
2279
  if (displayContent !== void 0) {
2025
2280
  blocks.push({ type: "text", content: displayContent });
2026
2281
  } else if (Array.isArray(stored)) {
2027
2282
  for (const raw of stored) {
2028
- if (!isRecord3(raw)) continue;
2283
+ if (!isRecord4(raw)) continue;
2029
2284
  const type3 = String(raw.type ?? "");
2030
2285
  if (type3 === "thinking") blocks.push({ type: type3, content: raw.thinking ?? raw.content ?? "" });
2031
2286
  if (type3 === "text") blocks.push({ type: type3, content: raw.text ?? raw.content ?? "" });
2287
+ if (type3 === "tool_call") {
2288
+ const toolCallId = String(raw.id ?? "");
2289
+ const toolCall = toolCalls.find((candidate) => candidate.id === toolCallId);
2290
+ if (!toolCall) continue;
2291
+ storedToolCallIds.add(toolCallId);
2292
+ blocks.push({
2293
+ type: "tool_use",
2294
+ content: toolCall.arguments,
2295
+ tool_call_id: toolCall.id,
2296
+ tool_name: toolCall.tool_name,
2297
+ display_name: toolCall.display_name
2298
+ });
2299
+ }
2032
2300
  }
2033
2301
  } else if (message.content !== void 0 && message.content !== "") {
2034
2302
  blocks.push({ type: "text", content: message.content });
2035
2303
  }
2036
2304
  for (const toolCall of toolCalls) {
2305
+ if (storedToolCallIds.has(toolCall.id)) continue;
2037
2306
  blocks.push({
2038
2307
  type: "tool_use",
2039
- content: { name: toolCall.tool_name, arguments: toolCall.arguments },
2040
- tool_call_id: toolCall.id
2308
+ content: toolCall.arguments,
2309
+ tool_call_id: toolCall.id,
2310
+ tool_name: toolCall.tool_name,
2311
+ display_name: toolCall.display_name
2041
2312
  });
2042
2313
  }
2043
2314
  return blocks;
@@ -2058,7 +2329,7 @@ function markerTurn(id, sequence, loopId, type3, content, toolCallId = null) {
2058
2329
  duration_ms: 0
2059
2330
  };
2060
2331
  }
2061
- function isRecord3(value) {
2332
+ function isRecord4(value) {
2062
2333
  return typeof value === "object" && value !== null && !Array.isArray(value);
2063
2334
  }
2064
2335
  function stringOrNull(value) {
@@ -2094,7 +2365,8 @@ function toCreateSessionPayload(request) {
2094
2365
  solution_snapshot_session_id: request.solution_snapshot_session_id ?? null,
2095
2366
  compaction_ratio: request.compaction_ratio ?? null,
2096
2367
  ...request.runtime_type ? { runtime_type: request.runtime_type } : {},
2097
- ...request.daemon_id ? { daemon_id: request.daemon_id } : {}
2368
+ ...request.daemon_id ? { daemon_id: request.daemon_id } : {},
2369
+ ...request.agent_runtime_id ? { agent_runtime_id: request.agent_runtime_id } : {}
2098
2370
  };
2099
2371
  }
2100
2372
  var builtinSolutionIds = /* @__PURE__ */ new Set([
@@ -2708,6 +2980,36 @@ var LEGACY_FORK_CONTEXT_MARKERS = [
2708
2980
  "\u53EF\u901A\u8FC7 `read_file` \u8BFB\u53D6\u3002",
2709
2981
  "# \u4F60\u7684\u4EFB\u52A1"
2710
2982
  ];
2983
+ var INTERNAL_CHAT_ERROR_PATTERNS = [
2984
+ /\b(?:ENOENT|EACCES|EPERM)\b/i,
2985
+ /\[(?:Errno -?\d+|WinError \d+)\]/i,
2986
+ /\b(?:ECONNRESET|ECONNREFUSED|ETIMEDOUT|EHOSTUNREACH|ENETUNREACH|ENOTFOUND|EAI_AGAIN)\b/i,
2987
+ /\b(?:Connection refused|Connection reset by peer|All connection attempts failed|getaddrinfo failed)\b/i,
2988
+ /(?:^|[\s("'`])\/(?:root|home|Users|tmp|var|opt|etc)\//,
2989
+ /[A-Za-z]:\\(?:Users|Windows|Program Files|AppData)\\/i,
2990
+ /(?:^|\n)Traceback \(most recent call last\):/,
2991
+ /(?:^|\n)\s*at\s+\S+(?:\s+\(|:\d+:\d+)/,
2992
+ /(?:^|\n)File "[^"]+", line \d+/
2993
+ ];
2994
+ var SAFE_CHAT_ERROR_PREFIXES = [
2995
+ "\u6C99\u76D2\u8FD0\u884C\u73AF\u5883\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
2996
+ "\u6A21\u578B\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5\u6A21\u578B\u914D\u7F6E\u540E\u91CD\u8BD5\u3002"
2997
+ ];
2998
+ var GENERIC_CHAT_ERROR = "\u6D88\u606F\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002";
2999
+ function chatErrorForDisplay(message, developer = false) {
3000
+ const normalized = message.trim();
3001
+ if (!developer) {
3002
+ const safePrefix = SAFE_CHAT_ERROR_PREFIXES.find((prefix) => normalized.startsWith(prefix));
3003
+ if (safePrefix) return safePrefix;
3004
+ }
3005
+ if (!developer && /no source matches this model/i.test(normalized)) {
3006
+ return "\u5F53\u524D\u6A21\u578B\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u6216\u91CD\u65B0\u9009\u62E9\u6A21\u578B\u3002";
3007
+ }
3008
+ if (!developer && INTERNAL_CHAT_ERROR_PATTERNS.some((pattern) => pattern.test(normalized))) {
3009
+ return GENERIC_CHAT_ERROR;
3010
+ }
3011
+ return normalized || GENERIC_CHAT_ERROR;
3012
+ }
2711
3013
  function normalizeMessageContent(content) {
2712
3014
  if (typeof content === "string") return content;
2713
3015
  if (Array.isArray(content)) return content;
@@ -2727,10 +3029,10 @@ function isLegacyForkContextText(text) {
2727
3029
  const normalized = text.trim();
2728
3030
  return LEGACY_FORK_CONTEXT_MARKERS.every((marker) => normalized.includes(marker));
2729
3031
  }
2730
- function isHiddenInternalMessage(message) {
3032
+ function isHiddenInternalMessage(message, options = {}) {
2731
3033
  if (message.role !== "user") return false;
2732
3034
  const text = getTextContent(normalizeMessageContent(message.content));
2733
- return isSystemReminderText(text) || isSystemNotificationText(text) || isLegacyForkContextText(text);
3035
+ return isSystemReminderText(text) || isSystemNotificationText(text) || (options.includeLegacyForkContext ?? true) && isLegacyForkContextText(text);
2734
3036
  }
2735
3037
  function buildMessageContent(text, attachments) {
2736
3038
  if (attachments.length === 0) return text;
@@ -2903,10 +3205,10 @@ function inferLoopStatusFromMessages(messages) {
2903
3205
  }
2904
3206
  function inferLoopStatusFromTurns(turns, messages) {
2905
3207
  const latestAgentNotification = [...turns].reverse().flatMap((turn) => turn.blocks).find((block) => {
2906
- if (block.type !== "system_notification" || !isRecord4(block.content)) return false;
3208
+ if (block.type !== "system_notification" || !isRecord5(block.content)) return false;
2907
3209
  return block.content.notification_type === "agent:start" || block.content.notification_type === "agent:end";
2908
3210
  });
2909
- if (latestAgentNotification?.type === "system_notification" && isRecord4(latestAgentNotification.content)) {
3211
+ if (latestAgentNotification?.type === "system_notification" && isRecord5(latestAgentNotification.content)) {
2910
3212
  const notificationType = latestAgentNotification.content.notification_type;
2911
3213
  const status = latestAgentNotification.content.status;
2912
3214
  if (notificationType === "agent:start" || status === "running") return "running";
@@ -2915,17 +3217,47 @@ function inferLoopStatusFromTurns(turns, messages) {
2915
3217
  }
2916
3218
  return inferLoopStatusFromMessages(messages);
2917
3219
  }
2918
- function isRecord4(value) {
3220
+ function isRecord5(value) {
2919
3221
  return typeof value === "object" && value !== null && !Array.isArray(value);
2920
3222
  }
3223
+ function toSelectionMap(value) {
3224
+ if (!isRecord5(value)) return {};
3225
+ const entries = Object.entries(value).map(([questionKey, optionIndexes]) => {
3226
+ if (!Array.isArray(optionIndexes)) return null;
3227
+ const parsedIndexes = optionIndexes.map((item) => typeof item === "number" ? item : Number(item)).filter((item) => Number.isInteger(item));
3228
+ return [Number(questionKey), parsedIndexes];
3229
+ }).filter((entry) => entry !== null);
3230
+ return Object.fromEntries(entries);
3231
+ }
3232
+ function toCustomMap(value) {
3233
+ if (!isRecord5(value)) return {};
3234
+ const entries = Object.entries(value).filter(([, text]) => typeof text === "string").map(([questionKey, text]) => [Number(questionKey), text]);
3235
+ return Object.fromEntries(entries);
3236
+ }
3237
+ function extractAskAnswers(turns) {
3238
+ const answers = {};
3239
+ for (const turn of turns) {
3240
+ for (const block of turn.blocks) {
3241
+ if (block.type !== "ask_user_answer" || typeof block.tool_call_id !== "string") continue;
3242
+ if (!isRecord5(block.content)) continue;
3243
+ const note = typeof block.content.note === "string" ? block.content.note.trim() : "";
3244
+ answers[block.tool_call_id] = {
3245
+ selections: toSelectionMap(block.content.selections),
3246
+ custom: toCustomMap(block.content.custom),
3247
+ ...note ? { note } : {}
3248
+ };
3249
+ }
3250
+ }
3251
+ return answers;
3252
+ }
2921
3253
  function parentForkToolCallIdFromTurn(turn) {
2922
3254
  if (typeof turn.parent_fork_tool_call_id === "string" && turn.parent_fork_tool_call_id.length > 0) {
2923
3255
  return turn.parent_fork_tool_call_id;
2924
3256
  }
2925
3257
  for (const block of turn.blocks) {
2926
- if (block.type !== "system_notification" || !isRecord4(block.content)) continue;
3258
+ if (block.type !== "system_notification" || !isRecord5(block.content)) continue;
2927
3259
  const metadata = block.content.metadata;
2928
- if (!isRecord4(metadata)) continue;
3260
+ if (!isRecord5(metadata)) continue;
2929
3261
  const parentId = metadata.parent_fork_tool_call_id;
2930
3262
  if (typeof parentId === "string" && parentId.length > 0) return parentId;
2931
3263
  }
@@ -2942,16 +3274,16 @@ function buildMessageContent2(turn) {
2942
3274
  }
2943
3275
  function workspaceNotificationContent(turn) {
2944
3276
  const block = turn.blocks.find(
2945
- (candidate) => candidate.type === "system_notification" && isRecord4(candidate.content) && candidate.content.notification_type === "workspace_change"
3277
+ (candidate) => candidate.type === "system_notification" && isRecord5(candidate.content) && candidate.content.notification_type === "workspace_change"
2946
3278
  );
2947
- if (!block || !isRecord4(block.content)) return "";
3279
+ if (!block || !isRecord5(block.content)) return "";
2948
3280
  const title = typeof block.content.title === "string" ? block.content.title.trim() : "";
2949
3281
  const detail = typeof block.content.detail === "string" ? block.content.detail.trim() : "";
2950
3282
  return [title ? `**${title}**` : "", detail].filter(Boolean).join("\n\n");
2951
3283
  }
2952
3284
  function askUserAnswerContent(turn) {
2953
3285
  const answerBlock = turn.blocks.find((block) => block.type === "ask_user_answer");
2954
- if (!answerBlock || !isRecord4(answerBlock.content)) return null;
3286
+ if (!answerBlock || !isRecord5(answerBlock.content)) return null;
2955
3287
  const answer = answerBlock.content.answer;
2956
3288
  return typeof answer === "string" && answer.trim().length > 0 ? answer : null;
2957
3289
  }
@@ -2971,6 +3303,19 @@ function extractModeFromBlocks(blocks) {
2971
3303
  return null;
2972
3304
  }
2973
3305
  function projectionToMessage(turn) {
3306
+ if (turn.kind === "context") {
3307
+ const context = contextProjectionData(turn);
3308
+ if (!context) return null;
3309
+ return {
3310
+ role: "assistant",
3311
+ content: "",
3312
+ kind: "context",
3313
+ context,
3314
+ loop_name: turn.loop_id,
3315
+ entry_id: turn.turn_id,
3316
+ status: turn.status
3317
+ };
3318
+ }
2974
3319
  if (turn.kind === "compaction" && turn.compaction_id) {
2975
3320
  return {
2976
3321
  role: "assistant",
@@ -3140,6 +3485,25 @@ function orderTurns(turns) {
3140
3485
  }
3141
3486
  return turns;
3142
3487
  }
3488
+ function reconcileOptimisticUserTurns(currentTurns, incomingTurns) {
3489
+ const currentById = new Map(currentTurns.map((turn) => [turn.turn_id, turn]));
3490
+ const optimisticUserTurn = currentTurns.find(
3491
+ (turn) => turn.role === "user" && (turn.kind ?? "message") === "message" && turn.turn_id.startsWith("local-user-")
3492
+ );
3493
+ let optimisticUserMatched = false;
3494
+ return incomingTurns.map((turn) => {
3495
+ const current = currentById.get(turn.turn_id);
3496
+ if (current) {
3497
+ const currentRenderId = current.client_render_id;
3498
+ return turn.client_render_id || !currentRenderId ? turn : { ...turn, client_render_id: currentRenderId };
3499
+ }
3500
+ if (!optimisticUserMatched && optimisticUserTurn && turn.role === "user" && (turn.kind ?? "message") === "message" && !turn.turn_id.startsWith("local-user-")) {
3501
+ optimisticUserMatched = true;
3502
+ return { ...turn, client_render_id: optimisticUserTurn.turn_id };
3503
+ }
3504
+ return turn;
3505
+ });
3506
+ }
3143
3507
  function areAgentLoopsEqual(left, right) {
3144
3508
  if (!left) return Object.keys(right).length === 0;
3145
3509
  const leftEntries = Object.entries(left);
@@ -3151,7 +3515,7 @@ function areAgentLoopsEqual(left, right) {
3151
3515
  });
3152
3516
  }
3153
3517
  function withTurns(state, turns) {
3154
- const orderedTurns = orderTurns(turns);
3518
+ const orderedTurns = orderTurns(reconcileOptimisticUserTurns(state.turns, turns));
3155
3519
  const { messages, agentLoops, activeCompaction } = materialize(orderedTurns);
3156
3520
  const isWaitingForInput = orderedTurns.some(
3157
3521
  (turn) => turn.tool_calls.some((toolCall) => toolCall.status === "awaiting_answer")
@@ -3161,14 +3525,31 @@ function withTurns(state, turns) {
3161
3525
  const preservedErrors = lastTurnId ? state.messages.filter(
3162
3526
  (m) => m.role === "error" && typeof m.entry_id === "string" && m.entry_id.startsWith(`${ERROR_ANCHOR_PREFIX}${lastTurnId}:`)
3163
3527
  ) : [];
3164
- const mergedMessages = preservedErrors.length > 0 ? [...messages, ...preservedErrors] : messages;
3528
+ const turnById = new Map(orderedTurns.map((turn) => [turn.turn_id, turn]));
3529
+ const messagesWithRenderIds = messages.map((message) => {
3530
+ if (!message.entry_id) return message;
3531
+ const renderId = turnById.get(message.entry_id)?.client_render_id;
3532
+ return renderId ? { ...message, render_id: renderId } : message;
3533
+ });
3534
+ const mergedMessages = preservedErrors.length > 0 ? [...messagesWithRenderIds, ...preservedErrors] : messagesWithRenderIds;
3165
3535
  const latestMode = [...orderedTurns].reverse().map((turn) => extractModeFromBlocks(turn.blocks)).find((mode) => mode !== null);
3536
+ const askAnswersFromHistory = extractAskAnswers(orderedTurns);
3537
+ const presentToolCallIds = new Set(
3538
+ orderedTurns.flatMap((turn) => turn.tool_calls.map((toolCall) => toolCall.id))
3539
+ );
3540
+ const askAnswers = { ...askAnswersFromHistory };
3541
+ for (const [toolCallId, answer] of Object.entries(state.askAnswers)) {
3542
+ if (!(toolCallId in askAnswers) && presentToolCallIds.has(toolCallId)) {
3543
+ askAnswers[toolCallId] = answer;
3544
+ }
3545
+ }
3166
3546
  return {
3167
3547
  ...state,
3168
3548
  turns: orderedTurns,
3169
3549
  messages: mergedMessages,
3170
3550
  agentLoops: stableAgentLoops,
3171
3551
  activeCompaction,
3552
+ askAnswers,
3172
3553
  mode: latestMode ?? state.mode,
3173
3554
  // 历史恢复时 chat:end(paused) 可能已不在 socket replay 缓存中;
3174
3555
  // awaiting_answer 是投影里的持久权威状态,必须据此恢复问答交互。
@@ -3196,14 +3577,17 @@ function addUserMessage(state, content) {
3196
3577
  }
3197
3578
  function upsertTurn(state, turn) {
3198
3579
  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-"));
3580
+ const [nextTurn] = reconcileOptimisticUserTurns(existing, [turn]);
3581
+ if (nextTurn.client_render_id && turn.role === "user") {
3582
+ existing = existing.filter(
3583
+ (item) => !(item.role === "user" && item.turn_id.startsWith("local-user-"))
3584
+ );
3201
3585
  }
3202
- const index = existing.findIndex((item) => item.turn_id === turn.turn_id);
3586
+ const index = existing.findIndex((item) => item.turn_id === nextTurn.turn_id);
3203
3587
  if (index >= 0) {
3204
- existing[index] = turn;
3588
+ existing[index] = nextTurn;
3205
3589
  } else {
3206
- existing.push(turn);
3590
+ existing.push(nextTurn);
3207
3591
  }
3208
3592
  return withTurns(state, existing);
3209
3593
  }
@@ -3255,18 +3639,18 @@ function markStreamingTurns(state, turnStatus, toolCallStatus) {
3255
3639
  // src/session/agent-session.ts
3256
3640
  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
3641
  var OOM_KEYWORDS_RE = /(?:\b(?:exit_code|ExitedWith|exited with)|退出码)\D*(?<!\d)137(?!\d)/i;
3258
- function isRecord5(value) {
3642
+ function isRecord6(value) {
3259
3643
  return typeof value === "object" && value !== null && !Array.isArray(value);
3260
3644
  }
3261
3645
  function isOomText(value) {
3262
3646
  return typeof value === "string" && OOM_KEYWORDS_RE.test(value);
3263
3647
  }
3264
3648
  function parseJsonRecord(value) {
3265
- if (isRecord5(value)) return value;
3649
+ if (isRecord6(value)) return value;
3266
3650
  if (typeof value !== "string") return null;
3267
3651
  try {
3268
3652
  const parsed = JSON.parse(value);
3269
- return isRecord5(parsed) ? parsed : null;
3653
+ return isRecord6(parsed) ? parsed : null;
3270
3654
  } catch {
3271
3655
  return null;
3272
3656
  }
@@ -3313,7 +3697,7 @@ function hasChatRunEvent2(events) {
3313
3697
  );
3314
3698
  }
3315
3699
  function isUiMetaLike(value) {
3316
- return isRecord5(value) && ("resourceHTML" in value || "resourceUri" in value || "resourceURI" in value);
3700
+ return isRecord6(value) && ("resourceHTML" in value || "resourceUri" in value || "resourceURI" in value);
3317
3701
  }
3318
3702
  var AgentSession = class _AgentSession {
3319
3703
  sessionId;
@@ -3335,6 +3719,7 @@ var AgentSession = class _AgentSession {
3335
3719
  replayQueue = Promise.resolve();
3336
3720
  pendingReplayMessage = null;
3337
3721
  pendingReplayMode;
3722
+ pendingReplayKbIds;
3338
3723
  recentChatEndAt = 0;
3339
3724
  disposed = false;
3340
3725
  getAppContext = null;
@@ -3359,7 +3744,6 @@ var AgentSession = class _AgentSession {
3359
3744
  const session = new _AgentSession(
3360
3745
  sessionId,
3361
3746
  {
3362
- streamTokens: false,
3363
3747
  emitEvent: readonlyError,
3364
3748
  emitWithAck: async () => readonlyError(),
3365
3749
  ensureConnected: readonlyError,
@@ -3464,7 +3848,7 @@ var AgentSession = class _AgentSession {
3464
3848
  if (this.getAppContext) {
3465
3849
  try {
3466
3850
  const rawContext = await this.getAppContext();
3467
- if (!isRecord5(rawContext)) {
3851
+ if (!isRecord6(rawContext)) {
3468
3852
  throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE JSON \u5BF9\u8C61");
3469
3853
  }
3470
3854
  const serialized = JSON.stringify(rawContext);
@@ -3472,7 +3856,7 @@ var AgentSession = class _AgentSession {
3472
3856
  throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE\u53EF\u5E8F\u5217\u5316\u7684 JSON \u5BF9\u8C61");
3473
3857
  }
3474
3858
  const normalized = JSON.parse(serialized);
3475
- if (!isRecord5(normalized)) {
3859
+ if (!isRecord6(normalized)) {
3476
3860
  throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE JSON \u5BF9\u8C61");
3477
3861
  }
3478
3862
  appContext = normalized;
@@ -3486,13 +3870,11 @@ var AgentSession = class _AgentSession {
3486
3870
  let optimisticTurnId = null;
3487
3871
  let optimisticAskAnswer;
3488
3872
  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
- }
3873
+ this.update((state) => {
3874
+ const next = addUserMessage(state, content);
3875
+ optimisticTurnId = next.turns.at(-1)?.turn_id ?? null;
3876
+ return next;
3877
+ });
3496
3878
  this.update((s) => ({ ...s, isStreaming: true }));
3497
3879
  if (askUserAnswer && typeof askUserAnswer.tool_call_id === "string") {
3498
3880
  const { tool_call_id, ...rest } = askUserAnswer;
@@ -3506,6 +3888,7 @@ var AgentSession = class _AgentSession {
3506
3888
  if (!options.replayDecision) {
3507
3889
  this.pendingReplayMessage = content;
3508
3890
  this.pendingReplayMode = options.mode;
3891
+ this.pendingReplayKbIds = options.kbIds;
3509
3892
  }
3510
3893
  const payload = {
3511
3894
  session_id: this.sessionId,
@@ -3516,7 +3899,8 @@ var AgentSession = class _AgentSession {
3516
3899
  thinking_override: options.thinkingOverride,
3517
3900
  whatif: options.whatif,
3518
3901
  replay_decision: options.replayDecision,
3519
- app_context: appContext
3902
+ app_context: appContext,
3903
+ kb_ids: options.kbIds && options.kbIds.length > 0 ? options.kbIds : void 0
3520
3904
  };
3521
3905
  try {
3522
3906
  await this.ensureJoined();
@@ -3552,6 +3936,7 @@ var AgentSession = class _AgentSession {
3552
3936
  rollbackOptimisticSend(optimisticTurnId, askUserAnswer, optimisticAskAnswer, previousAskAnswer, message) {
3553
3937
  this.pendingReplayMessage = null;
3554
3938
  this.pendingReplayMode = void 0;
3939
+ this.pendingReplayKbIds = void 0;
3555
3940
  this.update((current) => {
3556
3941
  let next = optimisticTurnId ? withTurns(
3557
3942
  current,
@@ -3733,7 +4118,7 @@ var AgentSession = class _AgentSession {
3733
4118
  _handleTurnEvents(events) {
3734
4119
  if (!events.length) return;
3735
4120
  const terminalChatEndStatus = extractTerminalChatEndStatus(events);
3736
- if (!terminalChatEndStatus && hasChatRunEvent2(events)) {
4121
+ if (!terminalChatEndStatus && hasChatRunEvent2(events) && this.state.status !== "waiting_for_input") {
3737
4122
  this.markRunningIfIdle();
3738
4123
  }
3739
4124
  if (this.state.turns.length) {
@@ -3805,8 +4190,10 @@ var AgentSession = class _AgentSession {
3805
4190
  _handleReplayMismatch(data) {
3806
4191
  const message = this.pendingReplayMessage ?? data.actual_message ?? "";
3807
4192
  const mode = this.pendingReplayMode;
4193
+ const kbIds = this.pendingReplayKbIds;
3808
4194
  this.pendingReplayMessage = null;
3809
4195
  this.pendingReplayMode = void 0;
4196
+ this.pendingReplayKbIds = void 0;
3810
4197
  const respond = (decision) => {
3811
4198
  void this.runtime.emitWithAck(
3812
4199
  "chat:send",
@@ -3814,7 +4201,8 @@ var AgentSession = class _AgentSession {
3814
4201
  session_id: this.sessionId,
3815
4202
  message,
3816
4203
  mode,
3817
- replay_decision: decision
4204
+ replay_decision: decision,
4205
+ kb_ids: kbIds && kbIds.length > 0 ? kbIds : void 0
3818
4206
  },
3819
4207
  3e4
3820
4208
  ).then((response) => {
@@ -3855,9 +4243,9 @@ var AgentSession = class _AgentSession {
3855
4243
  detail,
3856
4244
  status,
3857
4245
  loopId,
3858
- metadata: isRecord5(notification.metadata) ? notification.metadata : void 0
4246
+ metadata: isRecord6(notification.metadata) ? notification.metadata : void 0
3859
4247
  });
3860
- if (notificationType === "bg:started" && isRecord5(notification.metadata)) {
4248
+ if (notificationType === "bg:started" && isRecord6(notification.metadata)) {
3861
4249
  const taskId = typeof notification.metadata.task_id === "string" ? notification.metadata.task_id : "";
3862
4250
  if (taskId) {
3863
4251
  this.emitter.emit("backgroundTask", {
@@ -3987,7 +4375,7 @@ var AgentSession = class _AgentSession {
3987
4375
  title: ui.title ?? "\u5DE5\u5177\u9884\u89C8"
3988
4376
  });
3989
4377
  }
3990
- if (block.type === "system_notification" && isRecord5(block.content)) {
4378
+ if (block.type === "system_notification" && isRecord6(block.content)) {
3991
4379
  this._handleSystemNotification(turn.loop_id, block.content);
3992
4380
  }
3993
4381
  }
@@ -4093,8 +4481,7 @@ var AgentSession = class _AgentSession {
4093
4481
  {
4094
4482
  session_id: this.sessionId,
4095
4483
  event_format: "raw",
4096
- resume: true,
4097
- stream_tokens: this.runtime.streamTokens
4484
+ resume: true
4098
4485
  },
4099
4486
  5e3
4100
4487
  ).then((response) => {
@@ -4231,7 +4618,6 @@ var SessionHub = class {
4231
4618
  }
4232
4619
  runtimeFor() {
4233
4620
  return {
4234
- streamTokens: this.client.options?.streamTokens !== false,
4235
4621
  emitEvent: (event, data) => {
4236
4622
  ;
4237
4623
  this.client.socket().emit(event, data);
@@ -4471,7 +4857,7 @@ function resolveAuthToken(options) {
4471
4857
 
4472
4858
  // src/version.ts
4473
4859
  var SDK_NAME = "agent-client";
4474
- var SDK_VERSION = true ? "2610.0.0-beta.3" : "1.1.1";
4860
+ var SDK_VERSION = true ? "2610.0.0-beta.31" : "1.1.1";
4475
4861
 
4476
4862
  // src/socket.ts
4477
4863
  function withSdkIdentity(auth) {
@@ -4499,6 +4885,7 @@ var BladeClient = class {
4499
4885
  options;
4500
4886
  auth;
4501
4887
  headless;
4888
+ computers;
4502
4889
  models;
4503
4890
  sessions;
4504
4891
  /** 实时会话中枢:client.sessions.connect() 内部使用,一般不直接访问。 */
@@ -4510,6 +4897,7 @@ var BladeClient = class {
4510
4897
  };
4511
4898
  this.auth = new AuthResource(this);
4512
4899
  this.headless = new HeadlessResource(this);
4900
+ this.computers = new ComputersResource(this);
4513
4901
  this.models = new ModelsResource(this);
4514
4902
  this.sessions = new SessionsResource(this);
4515
4903
  this.hub = new SessionHub(this);
@@ -4864,6 +5252,89 @@ function parseXhrHeaders(rawHeaders) {
4864
5252
  return headers;
4865
5253
  }
4866
5254
 
5255
+ // src/platform-endpoints.ts
5256
+ var PLATFORM_SERVICE_NAMES = [
5257
+ "os",
5258
+ "hub",
5259
+ "agent",
5260
+ "oauth",
5261
+ "llmGateway",
5262
+ "gitea",
5263
+ "tile"
5264
+ ];
5265
+ var EMPTY_PLATFORM_ENDPOINTS = Object.freeze({
5266
+ services: Object.freeze({})
5267
+ });
5268
+ function defaultDocumentBaseUrl() {
5269
+ if (typeof document === "undefined") return void 0;
5270
+ if (document.querySelector("base[href]")) return document.baseURI;
5271
+ if (typeof location !== "undefined" && location.origin) return `${location.origin}/`;
5272
+ return document.baseURI;
5273
+ }
5274
+ function isRecord7(value) {
5275
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5276
+ }
5277
+ function normalizeServiceUrl(value) {
5278
+ if (typeof value !== "string" || !value.trim()) return null;
5279
+ if (value.includes("?") || value.includes("#")) return null;
5280
+ try {
5281
+ const url = new URL(value.trim());
5282
+ if (url.protocol !== "http:" && url.protocol !== "https:") return null;
5283
+ if (url.username || url.password || url.search || url.hash) return null;
5284
+ return url.toString().replace(/\/$/, "");
5285
+ } catch {
5286
+ return null;
5287
+ }
5288
+ }
5289
+ function parsePlatformEndpoints(value) {
5290
+ if (!isRecord7(value) || !isRecord7(value.services)) return null;
5291
+ const services = {};
5292
+ for (const name of PLATFORM_SERVICE_NAMES) {
5293
+ if (!(name in value.services)) continue;
5294
+ const url = normalizeServiceUrl(value.services[name]);
5295
+ if (!url) return null;
5296
+ if (name === "agent" && new URL(url).pathname !== "/") return null;
5297
+ services[name] = url;
5298
+ }
5299
+ return { services };
5300
+ }
5301
+ async function loadPlatformEndpoints(options = {}) {
5302
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
5303
+ const baseUrl = options.baseUrl ?? defaultDocumentBaseUrl();
5304
+ if (!fetchImpl || !baseUrl) return EMPTY_PLATFORM_ENDPOINTS;
5305
+ const controller = new AbortController();
5306
+ const timeout = globalThis.setTimeout(() => controller.abort(), options.timeoutMs ?? 3e3);
5307
+ try {
5308
+ const response = await fetchImpl(new URL("config.json", baseUrl), {
5309
+ cache: "no-store",
5310
+ credentials: "same-origin",
5311
+ headers: { Accept: "application/json" },
5312
+ signal: controller.signal
5313
+ });
5314
+ if (!response.ok) return EMPTY_PLATFORM_ENDPOINTS;
5315
+ return parsePlatformEndpoints(await response.json()) ?? EMPTY_PLATFORM_ENDPOINTS;
5316
+ } catch {
5317
+ return EMPTY_PLATFORM_ENDPOINTS;
5318
+ } finally {
5319
+ globalThis.clearTimeout(timeout);
5320
+ }
5321
+ }
5322
+ function resolveServiceUrl(endpoints, name, path = "") {
5323
+ const baseUrl = endpoints.services[name];
5324
+ if (!baseUrl) return null;
5325
+ if (!path) return baseUrl;
5326
+ const pathReference = path.trimStart();
5327
+ if (path.includes("\\") || pathReference.startsWith("//") || /^[a-z][a-z0-9+.-]*:/i.test(pathReference)) {
5328
+ return null;
5329
+ }
5330
+ const base = new URL(`${baseUrl}/`);
5331
+ const target = new URL(path.replace(/^\/+/, ""), base);
5332
+ if (target.origin !== base.origin || !target.pathname.startsWith(base.pathname)) {
5333
+ return null;
5334
+ }
5335
+ return target.toString();
5336
+ }
5337
+
4867
5338
  // src/commands/protocol.ts
4868
5339
  function isCommandEnvelope(value) {
4869
5340
  if (typeof value !== "object" || value === null) return false;
@@ -4967,7 +5438,9 @@ export {
4967
5438
  BladeApiError,
4968
5439
  BladeClient,
4969
5440
  ClientProjectionBuilder,
5441
+ ComputersResource,
4970
5442
  DEFAULT_REPLAY_SPEED,
5443
+ EMPTY_PLATFORM_ENDPOINTS,
4971
5444
  LayoutType,
4972
5445
  ModelsResource,
4973
5446
  SDK_NAME,
@@ -4980,20 +5453,31 @@ export {
4980
5453
  TaskStatus,
4981
5454
  acceptedPostChatFollowupCompletesLatestRun,
4982
5455
  buildMessageContent,
5456
+ canToggleComputer,
5457
+ chatErrorForDisplay,
5458
+ computerState,
4983
5459
  connectEmbedded,
4984
5460
  contentPreview,
5461
+ contextProjectionData,
4985
5462
  createInitialSessionState,
4986
5463
  createSocket,
4987
5464
  extractTextAttachments,
5465
+ getContextDisplayState,
5466
+ getContextGroupDisplayState,
4988
5467
  getFileParts,
4989
5468
  getImageParts,
4990
5469
  getTextContent,
5470
+ groupAdjacentContextRuns,
4991
5471
  groupMessagesByLoop,
4992
5472
  isCommandEnvelope,
4993
5473
  isHiddenInternalMessage,
4994
5474
  isInboundEnvelope,
4995
5475
  latestPostChatFollowup,
5476
+ loadPlatformEndpoints,
4996
5477
  normalizeMessageContent,
5478
+ reconcileOptimisticUserTurns,
5479
+ resolveServiceUrl,
5480
+ sortComputers,
4997
5481
  toReplaySnapshot,
4998
5482
  transformSlashCommand
4999
5483
  };