@blade-hq/agent-client 2610.0.0-beta.16 → 2610.0.0-beta.17

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/README.md CHANGED
@@ -542,7 +542,7 @@ transformSlashCommand(skillId, prompt, { local: false, installed: false })
542
542
  - **会话回放**:`ReplayState`、`ReplaySpeed`、`ReplayPreview`、`ReplaySnapshot`、`toReplaySnapshot`、`DEFAULT_REPLAY_SPEED`
543
543
  - **会话状态机**:`SessionHub`、`SessionState`、`SendOptions`、`ConnectionStatus`、`AskUserAnswerData`、`AgentLoopInfo`、`ActiveCompactionState`、`createInitialSessionState`、`AgentSessionEventName`
544
544
  - **页面协作**:`EmbeddedChat`、`EmbeddedChatOptions`、`CommandHandler`、`CommandEnvelope`、`InboundAction`、`InboundEnvelope`、`isCommandEnvelope`、`isInboundEnvelope`
545
- - **消息与投影协议**:`MessageContent`、`MessageContentPart`、`TextContentPart`、`ImageUrlContentPart`、`FileContentPart`、`ToolCallInfo`、`ToolBridgeContent`、`CompactionInfo`、`MemoryRefInfo`、`ArchivedFileInfo`、`ArchivedToolCallInfo`、`TurnProjection`、`ContentBlock`、`PatchEnvelope`、`MemoryRef`、`PostChatFollowup`、`FinalArtifact`、`latestPostChatFollowup`、`buildMessageContent`、`normalizeMessageContent`、`isHiddenInternalMessage`、`transformSlashCommand`、`SkillMentionAvailability`、`extractTextAttachments`、`ParsedTextAttachment`、`ParsedTextContext`
545
+ - **消息与投影协议**:`MessageContent`、`MessageContentPart`、`TextContentPart`、`ImageUrlContentPart`、`FileContentPart`、`ToolCallInfo`、`ToolBridgeContent`、`CompactionInfo`、`ContextProjectionData`、`ContextProjectionFields`、`ContextDisplayState`、`ContextAction`、`ContextSourceInfo`、`MemoryRefInfo`、`ArchivedFileInfo`、`ArchivedToolCallInfo`、`TurnProjection`、`ContentBlock`、`PatchEnvelope`、`MemoryRef`、`PostChatFollowup`、`FinalArtifact`、`contextProjectionData`、`getContextDisplayState`、`latestPostChatFollowup`、`buildMessageContent`、`normalizeMessageContent`、`isHiddenInternalMessage`、`transformSlashCommand`、`SkillMentionAvailability`、`extractTextAttachments`、`ParsedTextAttachment`、`ParsedTextContext`
546
546
  - **Solution / 任务协议**:`Solution`、`SolutionAppField`、`SolutionAppState`、`SolutionAppUiConfig`、`SolutionRef`、`PublishedSolutionRef`、`ExistingSolutionRef`、`PreparedSolution`、`PreparedSolutionAsset`、`LayoutType`、`BizRole`、`TaskStatus`、`BackgroundTask`、`BackgroundTaskStopResult`
547
547
  - **Headless**:`HeadlessResource`、`RunOptions`、`RunResult`、`RunTrace`
548
548
  - **低层通道(apps/web 等高级集成)**:`createSocket`、`CreateSocketOptions`、`TypedSocket`、`AsrAudioPayload`、`ClientProjectionBuilder`、`RawEvent`、`acceptedPostChatFollowupCompletesLatestRun`、`reconcileOptimisticUserTurns`
package/dist/index.d.ts CHANGED
@@ -26,6 +26,8 @@ export type { SessionsResource } from "./resources/sessions";
26
26
  export type { CreateSessionRequest, AppCliAttachment, AppCliDefinition, FileEntry, ImportSessionOptions, PaginatedSessionsResult, GlobalSearchConversationResult, GlobalSearchFileResult, GlobalSearchResult, GlobalSearchResultItem, ResultFeedback, ResultFeedbackReason, SessionContextStats, SessionHistory, ShareLinkResult, UploadFileEntry, UploadFilesOptions, } from "./resources/sessions";
27
27
  export type { ArchivedFileInfo, ArchivedToolCallInfo, ChatMessage, CompactionInfo, FileContentPart, ImageUrlContentPart, MemoryRefInfo, MessageContent, MessageContentPart, TextContentPart, ToolBridgeContent, ToolCallInfo, } from "./schemas/message";
28
28
  export { buildMessageContent, contentPreview, extractTextAttachments, getFileParts, getImageParts, getTextContent, groupMessagesByLoop, isHiddenInternalMessage, normalizeMessageContent, transformSlashCommand, } from "./schemas/message-utils";
29
+ export { contextProjectionData, getContextDisplayState, } from "./schemas/context";
30
+ export type { ContextAction, ContextDisplayState, ContextProjectionData, ContextProjectionFields, ContextSourceInfo, } from "./schemas/context";
29
31
  export type { ParsedTextAttachment, ParsedTextContext, SkillMentionAvailability, } from "./schemas/message-utils";
30
32
  export type { ContentBlock, FinalArtifact, MemoryRef, PatchEnvelope, PostChatFollowup, TurnProjection, } from "./schemas/projection";
31
33
  export { latestPostChatFollowup } from "./schemas/projection-utils";
package/dist/index.js CHANGED
@@ -1023,6 +1023,87 @@ function toolStatusFromResult(result) {
1023
1023
  return inferToolStatus(text);
1024
1024
  }
1025
1025
 
1026
+ // src/schemas/context.ts
1027
+ var BUILTIN_CONTEXT_TITLES = {
1028
+ "agent-instructions": "\u9879\u76EE\u8BF4\u660E",
1029
+ "skill-catalog": "\u53EF\u7528\u80FD\u529B",
1030
+ "runtime-context": "\u5F53\u524D\u5DE5\u4F5C\u73AF\u5883"
1031
+ };
1032
+ var BUILTIN_CONTEXT_DETAILS = {
1033
+ "agent-instructions": "\u8FD9\u4E9B\u8BF4\u660E\u4F1A\u5E2E\u52A9\u667A\u80FD\u4F53\u7406\u89E3\u5F53\u524D\u9879\u76EE\u548C\u5DE5\u4F5C\u8981\u6C42\u3002",
1034
+ "skill-catalog": "\u8FD9\u4E9B\u80FD\u529B\u4F1A\u5E2E\u52A9\u667A\u80FD\u4F53\u9009\u62E9\u5408\u9002\u7684\u65B9\u5F0F\u5B8C\u6210\u4EFB\u52A1\u3002",
1035
+ "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"
1036
+ };
1037
+ function nonEmptyString(value) {
1038
+ return typeof value === "string" && value.trim() ? value.trim() : null;
1039
+ }
1040
+ function contextProjectionData(fields) {
1041
+ const contextKind = nonEmptyString(fields.context_kind);
1042
+ const contextKey = nonEmptyString(fields.context_key);
1043
+ const contextName = nonEmptyString(fields.context_name);
1044
+ const contextRevision = nonEmptyString(fields.context_revision);
1045
+ if (!contextKind || !contextKey || !contextName || !contextRevision || typeof fields.context_order !== "number" || !Number.isFinite(fields.context_order) || !fields.context_action) {
1046
+ return null;
1047
+ }
1048
+ return {
1049
+ context_kind: contextKind,
1050
+ context_key: contextKey,
1051
+ context_name: contextName,
1052
+ context_order: fields.context_order,
1053
+ context_action: fields.context_action,
1054
+ context_revision: contextRevision,
1055
+ display_title: nonEmptyString(fields.display_title),
1056
+ display_summary: nonEmptyString(fields.display_summary),
1057
+ sources: Array.isArray(fields.sources) ? fields.sources : []
1058
+ };
1059
+ }
1060
+ function getContextDisplayState(context) {
1061
+ const title = BUILTIN_CONTEXT_TITLES[context.context_kind] ?? nonEmptyString(context.display_title) ?? "\u5BF9\u8BDD\u80CC\u666F";
1062
+ const isRemoved = context.context_action === "removed";
1063
+ 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" : "\u5DF2\u66F4\u65B0\u672C\u6B21\u5BF9\u8BDD\u80CC\u666F");
1064
+ 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";
1065
+ return { title, summary, detail, isRemoved };
1066
+ }
1067
+
1068
+ // src/shared/projection/context.ts
1069
+ function isRecord2(value) {
1070
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1071
+ }
1072
+ function buildContextTurn(entry, sequence) {
1073
+ if (entry.kind !== "context" || !isRecord2(entry.source) || !isRecord2(entry.data)) {
1074
+ return null;
1075
+ }
1076
+ const display = isRecord2(entry.data.display) ? entry.data.display : {};
1077
+ const context = contextProjectionData({
1078
+ context_kind: typeof entry.source.kind === "string" ? entry.source.kind : null,
1079
+ context_key: typeof entry.source.key === "string" ? entry.source.key : null,
1080
+ context_name: typeof entry.data.contribution_name === "string" ? entry.data.contribution_name : null,
1081
+ context_order: typeof entry.data.order === "number" ? entry.data.order : null,
1082
+ context_action: entry.data.action === "published" || entry.data.action === "retained" || entry.data.action === "removed" ? entry.data.action : null,
1083
+ context_revision: typeof entry.data.revision === "string" ? entry.data.revision : null,
1084
+ display_title: typeof display.title === "string" ? display.title : null,
1085
+ display_summary: typeof display.summary === "string" ? display.summary : null,
1086
+ sources: Array.isArray(display.sources) ? display.sources.filter(isRecord2) : []
1087
+ });
1088
+ if (!context) return null;
1089
+ const turnId = typeof entry.id === "string" && entry.id ? entry.id : `context-${sequence}`;
1090
+ return {
1091
+ id: turnId,
1092
+ sequence,
1093
+ turn_id: turnId,
1094
+ loop_id: typeof entry.loop_name === "string" && entry.loop_name ? entry.loop_name : "root",
1095
+ kind: "context",
1096
+ role: "system",
1097
+ status: "completed",
1098
+ blocks: [],
1099
+ tool_calls: [],
1100
+ model: null,
1101
+ usage: null,
1102
+ duration_ms: 0,
1103
+ ...context
1104
+ };
1105
+ }
1106
+
1026
1107
  // src/shared/projection/builder.ts
1027
1108
  function hasChatRunEvent(events) {
1028
1109
  return events.some(
@@ -1117,6 +1198,8 @@ var ClientProjectionBuilder = class {
1117
1198
  return this.onMemoryInject(loopId, payload);
1118
1199
  case "memory:inject:none":
1119
1200
  return null;
1201
+ case "context:entry":
1202
+ return this.onContextEntry(payload);
1120
1203
  case "loop:turn":
1121
1204
  return this.onLoopTurn(loopId, payload);
1122
1205
  case "mode:change":
@@ -1232,6 +1315,11 @@ var ClientProjectionBuilder = class {
1232
1315
  }
1233
1316
  return null;
1234
1317
  }
1318
+ onContextEntry(payload) {
1319
+ if (!isRecord3(payload.entry)) return null;
1320
+ const turn = buildContextTurn(payload.entry, this.nextSeq());
1321
+ return turn ? [{ kind: "upsert", turn }] : null;
1322
+ }
1235
1323
  onLoopTurn(loopId, payload) {
1236
1324
  let turnId = String(payload.turn_id ?? this.nextTurnId(`turn:${loopId}`));
1237
1325
  let memoryRefs = null;
@@ -1288,11 +1376,11 @@ var ClientProjectionBuilder = class {
1288
1376
  loopId,
1289
1377
  toolCallId: optStr(payload.tool_call_id) ?? void 0,
1290
1378
  toolName: toolNameFromPayload(payload) ?? void 0,
1291
- workspacePath: isRecord2(payload.workspace_change) ? optStr(payload.workspace_change.to) ?? void 0 : void 0
1379
+ workspacePath: isRecord3(payload.workspace_change) ? optStr(payload.workspace_change.to) ?? void 0 : void 0
1292
1380
  }
1293
1381
  ];
1294
1382
  const change = payload.workspace_change;
1295
- if (isRecord2(change)) {
1383
+ if (isRecord3(change)) {
1296
1384
  const source = optStr(change.source) ?? "";
1297
1385
  const projectName = optStr(change.project_name) ?? "";
1298
1386
  const fromPath = optStr(change.from) ?? "";
@@ -1754,7 +1842,7 @@ var ClientProjectionBuilder = class {
1754
1842
  return `${prefix}:${this.syntheticCounter}:${hex}`;
1755
1843
  }
1756
1844
  };
1757
- function isRecord2(value) {
1845
+ function isRecord3(value) {
1758
1846
  return typeof value === "object" && value !== null && !Array.isArray(value);
1759
1847
  }
1760
1848
  function optStr(value) {
@@ -1790,7 +1878,7 @@ function projectHistory(entries) {
1790
1878
  const kind = String(entry.kind ?? "");
1791
1879
  const loopId = String(entry.loop_name ?? "root");
1792
1880
  const id = String(entry.id ?? `history-${sequence + 1}`);
1793
- if (kind === "post_chat_followup" && isRecord3(entry.data)) {
1881
+ if (kind === "post_chat_followup" && isRecord4(entry.data)) {
1794
1882
  const assistantEntryId = String(entry.data.assistant_entry_id ?? "");
1795
1883
  if (latestRootMessage?.role === "assistant" && latestRootMessage.turn_id === assistantEntryId) {
1796
1884
  const followup = normalizePostChatFollowup(entry.data, assistantEntryId);
@@ -1798,7 +1886,15 @@ function projectHistory(entries) {
1798
1886
  }
1799
1887
  continue;
1800
1888
  }
1801
- if (kind === "message" && isRecord3(entry.message)) {
1889
+ if (kind === "context") {
1890
+ const turn = buildContextTurn(entry, sequence + 1);
1891
+ if (turn) {
1892
+ sequence += 1;
1893
+ turns.push(turn);
1894
+ }
1895
+ continue;
1896
+ }
1897
+ if (kind === "message" && isRecord4(entry.message)) {
1802
1898
  const message = entry.message;
1803
1899
  const role = String(message.role ?? "");
1804
1900
  if (role === "tool") {
@@ -1830,7 +1926,7 @@ function projectHistory(entries) {
1830
1926
  blocks,
1831
1927
  tool_calls: toolCalls,
1832
1928
  model: stringOrNull(message.model),
1833
- usage: isRecord3(message._usage) ? { ...message._usage } : null,
1929
+ usage: isRecord4(message._usage) ? { ...message._usage } : null,
1834
1930
  duration_ms: numberOrZero(message._duration_ms),
1835
1931
  started_at: typeof entry.timestamp === "string" ? entry.timestamp : void 0
1836
1932
  };
@@ -1849,7 +1945,7 @@ function projectHistory(entries) {
1849
1945
  if (kind === "mode_change") {
1850
1946
  turns.push(markerTurn(id, ++sequence, loopId, "mode_change", entry.data ?? {}));
1851
1947
  } else if (kind === "workspace_change") {
1852
- const change = isRecord3(entry.data) ? entry.data : {};
1948
+ const change = isRecord4(entry.data) ? entry.data : {};
1853
1949
  const source = stringOrNull(change.source) ?? "";
1854
1950
  const projectName = stringOrNull(change.project_name) ?? "";
1855
1951
  const fromPath = stringOrNull(change.from) ?? "";
@@ -1890,7 +1986,7 @@ function projectHistory(entries) {
1890
1986
  } else if (kind === "compaction" || kind === "tool_result_archive") {
1891
1987
  const data = entry.data ?? {};
1892
1988
  const compactionId = String(data.compaction_id ?? entry.id ?? `history-${sequence + 1}`);
1893
- let content = isRecord3(entry.message) ? entry.message.content : void 0;
1989
+ let content = isRecord4(entry.message) ? entry.message.content : void 0;
1894
1990
  if (kind === "tool_result_archive") {
1895
1991
  const archivedCount = Array.isArray(data.archived_files) ? data.archived_files.length : 0;
1896
1992
  content = `<compaction-summary>
@@ -1905,11 +2001,11 @@ function projectHistory(entries) {
1905
2001
  );
1906
2002
  } else if (kind === "child_pause") {
1907
2003
  const data = entry.data ?? {};
1908
- const sourceLoop = isRecord3(data.source_loop) ? data.source_loop : {};
2004
+ const sourceLoop = isRecord4(data.source_loop) ? data.source_loop : {};
1909
2005
  const childLoopId = String(data.child_loop_name ?? sourceLoop.name ?? "");
1910
2006
  const childToolCallId = String(data.child_pause_tool_call_id ?? "");
1911
2007
  const parentToolCallId = String(data.parent_fork_tool_call_id ?? "");
1912
- const pauseToolData = isRecord3(data.pause_tool_data) ? data.pause_tool_data : {};
2008
+ const pauseToolData = isRecord4(data.pause_tool_data) ? data.pause_tool_data : {};
1913
2009
  const description = String(sourceLoop.description ?? "");
1914
2010
  applyAskUserPauseToTurn(
1915
2011
  latestAssistantByLoop.get(childLoopId),
@@ -1997,15 +2093,15 @@ function parseToolArguments(value) {
1997
2093
  if (!value) return {};
1998
2094
  try {
1999
2095
  const parsed = JSON.parse(value);
2000
- return isRecord3(parsed) ? parsed : {};
2096
+ return isRecord4(parsed) ? parsed : {};
2001
2097
  } catch {
2002
2098
  return {};
2003
2099
  }
2004
2100
  }
2005
2101
  function buildToolCalls(value) {
2006
2102
  if (!Array.isArray(value)) return [];
2007
- return value.filter(isRecord3).map((raw) => {
2008
- const fn = isRecord3(raw.function) ? raw.function : {};
2103
+ return value.filter(isRecord4).map((raw) => {
2104
+ const fn = isRecord4(raw.function) ? raw.function : {};
2009
2105
  const name = String(fn.name ?? "");
2010
2106
  return {
2011
2107
  id: String(raw.id ?? ""),
@@ -2025,7 +2121,7 @@ function buildBlocks(message, toolCalls) {
2025
2121
  blocks.push({ type: "text", content: displayContent });
2026
2122
  } else if (Array.isArray(stored)) {
2027
2123
  for (const raw of stored) {
2028
- if (!isRecord3(raw)) continue;
2124
+ if (!isRecord4(raw)) continue;
2029
2125
  const type3 = String(raw.type ?? "");
2030
2126
  if (type3 === "thinking") blocks.push({ type: type3, content: raw.thinking ?? raw.content ?? "" });
2031
2127
  if (type3 === "text") blocks.push({ type: type3, content: raw.text ?? raw.content ?? "" });
@@ -2058,7 +2154,7 @@ function markerTurn(id, sequence, loopId, type3, content, toolCallId = null) {
2058
2154
  duration_ms: 0
2059
2155
  };
2060
2156
  }
2061
- function isRecord3(value) {
2157
+ function isRecord4(value) {
2062
2158
  return typeof value === "object" && value !== null && !Array.isArray(value);
2063
2159
  }
2064
2160
  function stringOrNull(value) {
@@ -2903,10 +2999,10 @@ function inferLoopStatusFromMessages(messages) {
2903
2999
  }
2904
3000
  function inferLoopStatusFromTurns(turns, messages) {
2905
3001
  const latestAgentNotification = [...turns].reverse().flatMap((turn) => turn.blocks).find((block) => {
2906
- if (block.type !== "system_notification" || !isRecord4(block.content)) return false;
3002
+ if (block.type !== "system_notification" || !isRecord5(block.content)) return false;
2907
3003
  return block.content.notification_type === "agent:start" || block.content.notification_type === "agent:end";
2908
3004
  });
2909
- if (latestAgentNotification?.type === "system_notification" && isRecord4(latestAgentNotification.content)) {
3005
+ if (latestAgentNotification?.type === "system_notification" && isRecord5(latestAgentNotification.content)) {
2910
3006
  const notificationType = latestAgentNotification.content.notification_type;
2911
3007
  const status = latestAgentNotification.content.status;
2912
3008
  if (notificationType === "agent:start" || status === "running") return "running";
@@ -2915,17 +3011,47 @@ function inferLoopStatusFromTurns(turns, messages) {
2915
3011
  }
2916
3012
  return inferLoopStatusFromMessages(messages);
2917
3013
  }
2918
- function isRecord4(value) {
3014
+ function isRecord5(value) {
2919
3015
  return typeof value === "object" && value !== null && !Array.isArray(value);
2920
3016
  }
3017
+ function toSelectionMap(value) {
3018
+ if (!isRecord5(value)) return {};
3019
+ const entries = Object.entries(value).map(([questionKey, optionIndexes]) => {
3020
+ if (!Array.isArray(optionIndexes)) return null;
3021
+ const parsedIndexes = optionIndexes.map((item) => typeof item === "number" ? item : Number(item)).filter((item) => Number.isInteger(item));
3022
+ return [Number(questionKey), parsedIndexes];
3023
+ }).filter((entry) => entry !== null);
3024
+ return Object.fromEntries(entries);
3025
+ }
3026
+ function toCustomMap(value) {
3027
+ if (!isRecord5(value)) return {};
3028
+ const entries = Object.entries(value).filter(([, text]) => typeof text === "string").map(([questionKey, text]) => [Number(questionKey), text]);
3029
+ return Object.fromEntries(entries);
3030
+ }
3031
+ function extractAskAnswers(turns) {
3032
+ const answers = {};
3033
+ for (const turn of turns) {
3034
+ for (const block of turn.blocks) {
3035
+ if (block.type !== "ask_user_answer" || typeof block.tool_call_id !== "string") continue;
3036
+ if (!isRecord5(block.content)) continue;
3037
+ const note = typeof block.content.note === "string" ? block.content.note.trim() : "";
3038
+ answers[block.tool_call_id] = {
3039
+ selections: toSelectionMap(block.content.selections),
3040
+ custom: toCustomMap(block.content.custom),
3041
+ ...note ? { note } : {}
3042
+ };
3043
+ }
3044
+ }
3045
+ return answers;
3046
+ }
2921
3047
  function parentForkToolCallIdFromTurn(turn) {
2922
3048
  if (typeof turn.parent_fork_tool_call_id === "string" && turn.parent_fork_tool_call_id.length > 0) {
2923
3049
  return turn.parent_fork_tool_call_id;
2924
3050
  }
2925
3051
  for (const block of turn.blocks) {
2926
- if (block.type !== "system_notification" || !isRecord4(block.content)) continue;
3052
+ if (block.type !== "system_notification" || !isRecord5(block.content)) continue;
2927
3053
  const metadata = block.content.metadata;
2928
- if (!isRecord4(metadata)) continue;
3054
+ if (!isRecord5(metadata)) continue;
2929
3055
  const parentId = metadata.parent_fork_tool_call_id;
2930
3056
  if (typeof parentId === "string" && parentId.length > 0) return parentId;
2931
3057
  }
@@ -2942,16 +3068,16 @@ function buildMessageContent2(turn) {
2942
3068
  }
2943
3069
  function workspaceNotificationContent(turn) {
2944
3070
  const block = turn.blocks.find(
2945
- (candidate) => candidate.type === "system_notification" && isRecord4(candidate.content) && candidate.content.notification_type === "workspace_change"
3071
+ (candidate) => candidate.type === "system_notification" && isRecord5(candidate.content) && candidate.content.notification_type === "workspace_change"
2946
3072
  );
2947
- if (!block || !isRecord4(block.content)) return "";
3073
+ if (!block || !isRecord5(block.content)) return "";
2948
3074
  const title = typeof block.content.title === "string" ? block.content.title.trim() : "";
2949
3075
  const detail = typeof block.content.detail === "string" ? block.content.detail.trim() : "";
2950
3076
  return [title ? `**${title}**` : "", detail].filter(Boolean).join("\n\n");
2951
3077
  }
2952
3078
  function askUserAnswerContent(turn) {
2953
3079
  const answerBlock = turn.blocks.find((block) => block.type === "ask_user_answer");
2954
- if (!answerBlock || !isRecord4(answerBlock.content)) return null;
3080
+ if (!answerBlock || !isRecord5(answerBlock.content)) return null;
2955
3081
  const answer = answerBlock.content.answer;
2956
3082
  return typeof answer === "string" && answer.trim().length > 0 ? answer : null;
2957
3083
  }
@@ -2971,6 +3097,19 @@ function extractModeFromBlocks(blocks) {
2971
3097
  return null;
2972
3098
  }
2973
3099
  function projectionToMessage(turn) {
3100
+ if (turn.kind === "context") {
3101
+ const context = contextProjectionData(turn);
3102
+ if (!context) return null;
3103
+ return {
3104
+ role: "assistant",
3105
+ content: "",
3106
+ kind: "context",
3107
+ context,
3108
+ loop_name: turn.loop_id,
3109
+ entry_id: turn.turn_id,
3110
+ status: turn.status
3111
+ };
3112
+ }
2974
3113
  if (turn.kind === "compaction" && turn.compaction_id) {
2975
3114
  return {
2976
3115
  role: "assistant",
@@ -3143,7 +3282,7 @@ function orderTurns(turns) {
3143
3282
  function reconcileOptimisticUserTurns(currentTurns, incomingTurns) {
3144
3283
  const currentById = new Map(currentTurns.map((turn) => [turn.turn_id, turn]));
3145
3284
  const optimisticUserTurn = currentTurns.find(
3146
- (turn) => turn.role === "user" && turn.turn_id.startsWith("local-user-")
3285
+ (turn) => turn.role === "user" && (turn.kind ?? "message") === "message" && turn.turn_id.startsWith("local-user-")
3147
3286
  );
3148
3287
  let optimisticUserMatched = false;
3149
3288
  return incomingTurns.map((turn) => {
@@ -3152,7 +3291,7 @@ function reconcileOptimisticUserTurns(currentTurns, incomingTurns) {
3152
3291
  const currentRenderId = current.client_render_id;
3153
3292
  return turn.client_render_id || !currentRenderId ? turn : { ...turn, client_render_id: currentRenderId };
3154
3293
  }
3155
- if (!optimisticUserMatched && optimisticUserTurn && turn.role === "user" && !turn.turn_id.startsWith("local-user-")) {
3294
+ if (!optimisticUserMatched && optimisticUserTurn && turn.role === "user" && (turn.kind ?? "message") === "message" && !turn.turn_id.startsWith("local-user-")) {
3156
3295
  optimisticUserMatched = true;
3157
3296
  return { ...turn, client_render_id: optimisticUserTurn.turn_id };
3158
3297
  }
@@ -3188,12 +3327,23 @@ function withTurns(state, turns) {
3188
3327
  });
3189
3328
  const mergedMessages = preservedErrors.length > 0 ? [...messagesWithRenderIds, ...preservedErrors] : messagesWithRenderIds;
3190
3329
  const latestMode = [...orderedTurns].reverse().map((turn) => extractModeFromBlocks(turn.blocks)).find((mode) => mode !== null);
3330
+ const askAnswersFromHistory = extractAskAnswers(orderedTurns);
3331
+ const presentToolCallIds = new Set(
3332
+ orderedTurns.flatMap((turn) => turn.tool_calls.map((toolCall) => toolCall.id))
3333
+ );
3334
+ const askAnswers = { ...askAnswersFromHistory };
3335
+ for (const [toolCallId, answer] of Object.entries(state.askAnswers)) {
3336
+ if (!(toolCallId in askAnswers) && presentToolCallIds.has(toolCallId)) {
3337
+ askAnswers[toolCallId] = answer;
3338
+ }
3339
+ }
3191
3340
  return {
3192
3341
  ...state,
3193
3342
  turns: orderedTurns,
3194
3343
  messages: mergedMessages,
3195
3344
  agentLoops: stableAgentLoops,
3196
3345
  activeCompaction,
3346
+ askAnswers,
3197
3347
  mode: latestMode ?? state.mode,
3198
3348
  // 历史恢复时 chat:end(paused) 可能已不在 socket replay 缓存中;
3199
3349
  // awaiting_answer 是投影里的持久权威状态,必须据此恢复问答交互。
@@ -3283,18 +3433,18 @@ function markStreamingTurns(state, turnStatus, toolCallStatus) {
3283
3433
  // src/session/agent-session.ts
3284
3434
  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";
3285
3435
  var OOM_KEYWORDS_RE = /(?:\b(?:exit_code|ExitedWith|exited with)|退出码)\D*(?<!\d)137(?!\d)/i;
3286
- function isRecord5(value) {
3436
+ function isRecord6(value) {
3287
3437
  return typeof value === "object" && value !== null && !Array.isArray(value);
3288
3438
  }
3289
3439
  function isOomText(value) {
3290
3440
  return typeof value === "string" && OOM_KEYWORDS_RE.test(value);
3291
3441
  }
3292
3442
  function parseJsonRecord(value) {
3293
- if (isRecord5(value)) return value;
3443
+ if (isRecord6(value)) return value;
3294
3444
  if (typeof value !== "string") return null;
3295
3445
  try {
3296
3446
  const parsed = JSON.parse(value);
3297
- return isRecord5(parsed) ? parsed : null;
3447
+ return isRecord6(parsed) ? parsed : null;
3298
3448
  } catch {
3299
3449
  return null;
3300
3450
  }
@@ -3341,7 +3491,7 @@ function hasChatRunEvent2(events) {
3341
3491
  );
3342
3492
  }
3343
3493
  function isUiMetaLike(value) {
3344
- return isRecord5(value) && ("resourceHTML" in value || "resourceUri" in value || "resourceURI" in value);
3494
+ return isRecord6(value) && ("resourceHTML" in value || "resourceUri" in value || "resourceURI" in value);
3345
3495
  }
3346
3496
  var AgentSession = class _AgentSession {
3347
3497
  sessionId;
@@ -3492,7 +3642,7 @@ var AgentSession = class _AgentSession {
3492
3642
  if (this.getAppContext) {
3493
3643
  try {
3494
3644
  const rawContext = await this.getAppContext();
3495
- if (!isRecord5(rawContext)) {
3645
+ if (!isRecord6(rawContext)) {
3496
3646
  throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE JSON \u5BF9\u8C61");
3497
3647
  }
3498
3648
  const serialized = JSON.stringify(rawContext);
@@ -3500,7 +3650,7 @@ var AgentSession = class _AgentSession {
3500
3650
  throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE\u53EF\u5E8F\u5217\u5316\u7684 JSON \u5BF9\u8C61");
3501
3651
  }
3502
3652
  const normalized = JSON.parse(serialized);
3503
- if (!isRecord5(normalized)) {
3653
+ if (!isRecord6(normalized)) {
3504
3654
  throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE JSON \u5BF9\u8C61");
3505
3655
  }
3506
3656
  appContext = normalized;
@@ -3883,9 +4033,9 @@ var AgentSession = class _AgentSession {
3883
4033
  detail,
3884
4034
  status,
3885
4035
  loopId,
3886
- metadata: isRecord5(notification.metadata) ? notification.metadata : void 0
4036
+ metadata: isRecord6(notification.metadata) ? notification.metadata : void 0
3887
4037
  });
3888
- if (notificationType === "bg:started" && isRecord5(notification.metadata)) {
4038
+ if (notificationType === "bg:started" && isRecord6(notification.metadata)) {
3889
4039
  const taskId = typeof notification.metadata.task_id === "string" ? notification.metadata.task_id : "";
3890
4040
  if (taskId) {
3891
4041
  this.emitter.emit("backgroundTask", {
@@ -4015,7 +4165,7 @@ var AgentSession = class _AgentSession {
4015
4165
  title: ui.title ?? "\u5DE5\u5177\u9884\u89C8"
4016
4166
  });
4017
4167
  }
4018
- if (block.type === "system_notification" && isRecord5(block.content)) {
4168
+ if (block.type === "system_notification" && isRecord6(block.content)) {
4019
4169
  this._handleSystemNotification(turn.loop_id, block.content);
4020
4170
  }
4021
4171
  }
@@ -4499,7 +4649,7 @@ function resolveAuthToken(options) {
4499
4649
 
4500
4650
  // src/version.ts
4501
4651
  var SDK_NAME = "agent-client";
4502
- var SDK_VERSION = true ? "2610.0.0-beta.16" : "1.1.1";
4652
+ var SDK_VERSION = true ? "2610.0.0-beta.17" : "1.1.1";
4503
4653
 
4504
4654
  // src/socket.ts
4505
4655
  function withSdkIdentity(auth) {
@@ -4693,7 +4843,7 @@ var BladeClient = class {
4693
4843
  const response = await (this.options.fetchImpl ?? fetch)(url.toString(), {
4694
4844
  method,
4695
4845
  body: init.body,
4696
- credentials: init.credentials ?? "same-origin",
4846
+ credentials: init.credentials ?? "include",
4697
4847
  headers: this.buildHeaders(url, init.headers),
4698
4848
  signal: init.signal
4699
4849
  });
@@ -4911,7 +5061,7 @@ function defaultDocumentBaseUrl() {
4911
5061
  if (typeof location !== "undefined" && location.origin) return `${location.origin}/`;
4912
5062
  return document.baseURI;
4913
5063
  }
4914
- function isRecord6(value) {
5064
+ function isRecord7(value) {
4915
5065
  return typeof value === "object" && value !== null && !Array.isArray(value);
4916
5066
  }
4917
5067
  function normalizeServiceUrl(value) {
@@ -4927,7 +5077,7 @@ function normalizeServiceUrl(value) {
4927
5077
  }
4928
5078
  }
4929
5079
  function parsePlatformEndpoints(value) {
4930
- if (!isRecord6(value) || !isRecord6(value.services)) return null;
5080
+ if (!isRecord7(value) || !isRecord7(value.services)) return null;
4931
5081
  const services = {};
4932
5082
  for (const name of PLATFORM_SERVICE_NAMES) {
4933
5083
  if (!(name in value.services)) continue;
@@ -5094,9 +5244,11 @@ export {
5094
5244
  buildMessageContent,
5095
5245
  connectEmbedded,
5096
5246
  contentPreview,
5247
+ contextProjectionData,
5097
5248
  createInitialSessionState,
5098
5249
  createSocket,
5099
5250
  extractTextAttachments,
5251
+ getContextDisplayState,
5100
5252
  getFileParts,
5101
5253
  getImageParts,
5102
5254
  getTextContent,