@blade-hq/agent-client 1.1.12-beta.4 → 1.2.0-beta.1

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
@@ -196,6 +196,26 @@ chat.dispose() // 彻底释放(什么时候
196
196
 
197
197
  ### 页面协作:让智能体和你的页面互动
198
198
 
199
+ **页面状态与业务后端 → 智能体**:
200
+
201
+ ```ts
202
+ await chat.attachApp({
203
+ cli: {
204
+ command: "library",
205
+ runtime: "python",
206
+ source: new URL("./agent/library_cli.py", location.href),
207
+ },
208
+ getContext: () => ({
209
+ route: location.pathname,
210
+ selectedBookId: bookStore.getState().selectedBookId,
211
+ }),
212
+ })
213
+ ```
214
+
215
+ `getContext` 在每次 `send()` 前执行一次,适合路由、当前选中对象等轻量页面状态;它不做响应式依赖追踪。完整业务数据由开发者自己的 CLI 查询。`command` 是智能体在 Bash 中直接执行的命令名,不会套在 `blade` 命令下。
216
+
217
+ `runtime: "python"` 使用沙盒系统 Python 运行单文件脚本;`runtime: "executable"` 接受能在 Linux 沙盒运行的可执行文件。`source` 可传 `Blob`、`File` 或浏览器可读取的 URL。应用 CLI 安装成功后随会话工作区保留;运行时可通过 `BLADE_APP_CONTEXT_FILE` 读取同一份页面上下文 JSON。
218
+
199
219
  **智能体 → 页面**(例如智能体让地图高亮某个点):
200
220
 
201
221
  ```ts
@@ -365,7 +385,7 @@ function Message({ message }: { message: ChatMessage }) {
365
385
  - **SDK 身份**:`SDK_NAME`、`SDK_VERSION`
366
386
  - **声明式会话**:`SessionDefinition`、`SolutionDefinition`、`SkillDefinition`、`SessionConfig`、`TextFile`、`SessionSetupError`、`SessionSetupStage`
367
387
  - **模型目录**:`ModelsResource`、`ModelCatalog`、`ModelOption`
368
- - **会话资源(REST)**:`SessionsResource`、`CreateSessionRequest`、`PaginatedSessionsResult`、`SessionHistory`、`SessionContextStats`、`ShareLinkResult`、`FileEntry`、`UploadFileEntry`、`UploadFilesOptions`、`SessionProfile`、`SessionDetail`、`SessionInfo`、`SessionStatus`、`SessionPortMapping`、`ModeId`、`TemplateId`、`PrimarySkillSnapshot`、`PrimarySkillParallelMode`
388
+ - **会话资源(REST)**:`SessionsResource`、`CreateSessionRequest`、`AppCliDefinition`、`AppCliAttachment`、`AttachAppOptions`、`PaginatedSessionsResult`、`SessionHistory`、`SessionContextStats`、`ShareLinkResult`、`FileEntry`、`UploadFileEntry`、`UploadFilesOptions`、`SessionProfile`、`SessionDetail`、`SessionInfo`、`SessionStatus`、`SessionPortMapping`、`ModeId`、`TemplateId`、`PrimarySkillSnapshot`、`PrimarySkillParallelMode`
369
389
  - **会话状态机**:`SessionHub`、`SessionState`、`SendOptions`、`ConnectionStatus`、`AskUserAnswerData`、`AgentLoopInfo`、`ActiveCompactionState`、`createInitialSessionState`、`AgentSessionEventName`
370
390
  - **页面协作**:`EmbeddedChat`、`EmbeddedChatOptions`、`CommandHandler`、`CommandEnvelope`、`InboundAction`、`InboundEnvelope`、`isCommandEnvelope`、`isInboundEnvelope`
371
391
  - **消息与投影协议**:`MessageContent`、`MessageContentPart`、`TextContentPart`、`ImageUrlContentPart`、`FileContentPart`、`ToolCallInfo`、`ToolBridgeContent`、`CompactionInfo`、`MemoryRefInfo`、`ArchivedFileInfo`、`ArchivedToolCallInfo`、`TurnProjection`、`ContentBlock`、`PatchEnvelope`、`MemoryRef`、`buildMessageContent`、`normalizeMessageContent`、`isHiddenInternalMessage`、`transformSlashCommand`、`extractTextAttachments`、`ParsedTextAttachment`、`ParsedTextContext`
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ export { BladeApiError } from "./rest";
4
4
  export type { LoginOptions, LoginResult, TokenStorageMode } from "./auth-login";
5
5
  export { SDK_NAME, SDK_VERSION } from "./version";
6
6
  export { AgentSession } from "./session/agent-session";
7
- export type { SendOptions } from "./session/agent-session";
7
+ export type { AttachAppOptions, SendOptions } from "./session/agent-session";
8
8
  export { SessionHub } from "./session/hub";
9
9
  export type { AgentSessionEvents, AgentSessionEventName } from "./session/events";
10
10
  export { SessionSetupError } from "./session/definition";
@@ -21,7 +21,7 @@ export type { HeadlessResource } from "./resources/headless";
21
21
  export { ModelsResource } from "./resources/models";
22
22
  export type { ModelCatalog, ModelOption } from "./resources/models";
23
23
  export type { SessionsResource } from "./resources/sessions";
24
- export type { CreateSessionRequest, FileEntry, PaginatedSessionsResult, SessionContextStats, SessionHistory, ShareLinkResult, UploadFileEntry, UploadFilesOptions, } from "./resources/sessions";
24
+ export type { CreateSessionRequest, AppCliAttachment, AppCliDefinition, FileEntry, PaginatedSessionsResult, SessionContextStats, SessionHistory, ShareLinkResult, UploadFileEntry, UploadFilesOptions, } from "./resources/sessions";
25
25
  export type { ArchivedFileInfo, ArchivedToolCallInfo, ChatMessage, CompactionInfo, FileContentPart, ImageUrlContentPart, MemoryRefInfo, MessageContent, MessageContentPart, TextContentPart, ToolBridgeContent, ToolCallInfo, } from "./schemas/message";
26
26
  export { buildMessageContent, contentPreview, extractTextAttachments, getFileParts, getImageParts, getTextContent, groupMessagesByLoop, isHiddenInternalMessage, normalizeMessageContent, transformSlashCommand, } from "./schemas/message-utils";
27
27
  export type { ParsedTextAttachment, ParsedTextContext } from "./schemas/message-utils";
package/dist/index.js CHANGED
@@ -2106,6 +2106,30 @@ var SessionsResource = class {
2106
2106
  );
2107
2107
  return response.json();
2108
2108
  }
2109
+ /** 把应用自己的业务 CLI 安装到会话沙盒,并保留开发者声明的命令名。 */
2110
+ async attachAppCli(sessionId, definition) {
2111
+ let source;
2112
+ if (definition.source instanceof Blob) {
2113
+ source = definition.source;
2114
+ } else {
2115
+ const response2 = await globalThis.fetch(definition.source.toString());
2116
+ if (!response2.ok) {
2117
+ throw new Error(`\u8BFB\u53D6\u5E94\u7528 CLI \u5931\u8D25\uFF1AHTTP ${response2.status}`);
2118
+ }
2119
+ source = await response2.blob();
2120
+ }
2121
+ const filename = definition.filename ?? (typeof File !== "undefined" && definition.source instanceof File ? definition.source.name : void 0) ?? (definition.runtime === "python" ? "app-cli.py" : "app-cli");
2122
+ const form = new FormData();
2123
+ form.append("command", definition.command);
2124
+ form.append("runtime", definition.runtime);
2125
+ form.append("file", source, filename);
2126
+ const response = await this.client.formData(
2127
+ "POST",
2128
+ `/api/sessions/${sessionId}/app-cli`,
2129
+ form
2130
+ );
2131
+ return response.json();
2132
+ }
2109
2133
  async deleteFile(sessionId, filePath) {
2110
2134
  await this.client.json("DELETE", `/api/sessions/${sessionId}/files/${encodeURIComponent(filePath)}`);
2111
2135
  }
@@ -2936,6 +2960,8 @@ var AgentSession = class _AgentSession {
2936
2960
  pendingReplayMode;
2937
2961
  recentChatEndAt = 0;
2938
2962
  disposed = false;
2963
+ getAppContext = null;
2964
+ sendPending = false;
2939
2965
  deliveredCommandKeys = /* @__PURE__ */ new Set();
2940
2966
  deliveredToolPreviews = /* @__PURE__ */ new Map();
2941
2967
  oomNotifiedKeys = /* @__PURE__ */ new Set();
@@ -2962,6 +2988,7 @@ var AgentSession = class _AgentSession {
2962
2988
  listDir: async () => readonlyError(),
2963
2989
  countFiles: async () => readonlyError(),
2964
2990
  downloadFile: async () => readonlyError(),
2991
+ attachAppCli: async () => readonlyError(),
2965
2992
  onDispose: () => void 0
2966
2993
  },
2967
2994
  "connected"
@@ -3016,18 +3043,58 @@ var AgentSession = class _AgentSession {
3016
3043
  return this.commands.onCommand(action, handler);
3017
3044
  }
3018
3045
  // ---------------------------------------------------------------- 动作
3046
+ /**
3047
+ * 声明当前应用提供的页面上下文和业务 CLI。
3048
+ *
3049
+ * CLI 安装成功后才保存 getContext;之后每次 send 前恰好读取一次。
3050
+ */
3051
+ async attachApp(options) {
3052
+ const attachment = options.cli ? await this.runtime.attachAppCli(this.sessionId, options.cli) : null;
3053
+ this.getAppContext = options.getContext;
3054
+ return attachment;
3055
+ }
3019
3056
  /** 发送一条消息。字符串或多模态内容均可。 */
3020
3057
  async send(content, options = {}) {
3021
3058
  this.runtime.ensureConnected();
3022
- if (this.state.isStreaming) {
3059
+ if (this.state.isStreaming || this.sendPending) {
3023
3060
  this.update((s) => addErrorMessage(s, "\u5F53\u524D\u56DE\u590D\u5C1A\u672A\u7ED3\u675F\uFF0C\u8BF7\u7A0D\u540E\u518D\u53D1\u9001\u3002"));
3024
3061
  return;
3025
3062
  }
3063
+ this.sendPending = true;
3064
+ try {
3065
+ await this.sendUnlocked(content, options);
3066
+ } finally {
3067
+ this.sendPending = false;
3068
+ }
3069
+ }
3070
+ async sendUnlocked(content, options) {
3026
3071
  await this.ensureIdleBeforeSend();
3027
3072
  if (this.state.isStreaming) {
3028
3073
  this.update((s) => addErrorMessage(s, "\u5F53\u524D\u56DE\u590D\u5C1A\u672A\u7ED3\u675F\uFF0C\u8BF7\u7A0D\u540E\u518D\u53D1\u9001\u3002"));
3029
3074
  return;
3030
3075
  }
3076
+ let appContext;
3077
+ if (this.getAppContext) {
3078
+ try {
3079
+ const rawContext = await this.getAppContext();
3080
+ if (!isRecord5(rawContext)) {
3081
+ throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE JSON \u5BF9\u8C61");
3082
+ }
3083
+ const serialized = JSON.stringify(rawContext);
3084
+ if (serialized === void 0) {
3085
+ throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE\u53EF\u5E8F\u5217\u5316\u7684 JSON \u5BF9\u8C61");
3086
+ }
3087
+ const normalized = JSON.parse(serialized);
3088
+ if (!isRecord5(normalized)) {
3089
+ throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE JSON \u5BF9\u8C61");
3090
+ }
3091
+ appContext = normalized;
3092
+ } catch (error) {
3093
+ const message = error instanceof Error && error.message ? `\u8BFB\u53D6\u5E94\u7528\u4E0A\u4E0B\u6587\u5931\u8D25\uFF1A${error.message}` : "\u8BFB\u53D6\u5E94\u7528\u4E0A\u4E0B\u6587\u5931\u8D25\uFF0C\u6D88\u606F\u672A\u53D1\u9001";
3094
+ this.update((s) => addErrorMessage(s, message));
3095
+ return;
3096
+ }
3097
+ }
3031
3098
  const askUserAnswer = options.askUserAnswer;
3032
3099
  if (!(askUserAnswer && typeof askUserAnswer.tool_call_id === "string")) {
3033
3100
  this.update((s) => addUserMessage(s, content));
@@ -3052,7 +3119,8 @@ var AgentSession = class _AgentSession {
3052
3119
  model: options.model,
3053
3120
  thinking_override: options.thinkingOverride,
3054
3121
  whatif: options.whatif,
3055
- replay_decision: options.replayDecision
3122
+ replay_decision: options.replayDecision,
3123
+ app_context: appContext
3056
3124
  };
3057
3125
  try {
3058
3126
  await this.ensureJoined();
@@ -3633,6 +3701,7 @@ var SessionHub = class {
3633
3701
  listDir: (sessionId, dirPath) => this.client.sessions.listDir(sessionId, dirPath),
3634
3702
  countFiles: (sessionId, dirPath) => this.client.sessions.countFiles(sessionId, dirPath),
3635
3703
  downloadFile: (sessionId, filePath, downloadName) => this.client.sessions.downloadFile(sessionId, filePath, downloadName),
3704
+ attachAppCli: (sessionId, definition) => this.client.sessions.attachAppCli(sessionId, definition),
3636
3705
  onDispose: (session) => {
3637
3706
  if (this.sessions.get(session.sessionId) === session) {
3638
3707
  this.sessions.delete(session.sessionId);
@@ -3825,7 +3894,7 @@ function resolveAuthToken(options) {
3825
3894
 
3826
3895
  // src/version.ts
3827
3896
  var SDK_NAME = "agent-client";
3828
- var SDK_VERSION = true ? "1.1.12-beta.4" : "1.1.1";
3897
+ var SDK_VERSION = true ? "1.2.0-beta.1" : "1.1.1";
3829
3898
 
3830
3899
  // src/socket.ts
3831
3900
  function withSdkIdentity(auth) {