@blade-hq/agent-client 2612.0.0-beta.13 → 2612.0.0-beta.15

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.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 { AttachAppOptions, SendOptions } from "./session/agent-session";
7
+ export type { AttachAppOptions, SendOptions, PreparedSend, PreparedSendOptions, DeliveryReceipt } from "./session/agent-session";
8
8
  export { AgentSessionError } from "./session/errors";
9
9
  export type { AgentSessionEvents, AgentSessionEventName } from "./session/events";
10
10
  export type { WorkflowRunEntry, WorkflowRunState } from "./session/workflow";
@@ -33,3 +33,5 @@ export type { ChatGroup, ChatGroupRole, ChatGroupMember, ChatGroupTask, CreateCh
33
33
  export { normalizePluginRefs } from "./schemas/plugin-refs";
34
34
  export type { PluginRef } from "./schemas/plugin-refs";
35
35
  export type { ToolEvidence } from "./schemas/projection";
36
+ export { ToolProvider } from "./tool-provider";
37
+ export type { ToolProviderOptions, ProviderTool, ProviderToolResult } from "./tool-provider";
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  SessionInfo,
10
10
  SessionSetupError,
11
11
  SessionStatus,
12
+ ToolProvider,
12
13
  chatErrorForDisplay,
13
14
  contentPreview,
14
15
  getFileParts,
@@ -19,7 +20,7 @@ import {
19
20
  normalizeTopicAssetRefs,
20
21
  transformSlashCommand,
21
22
  userFacingErrorText
22
- } from "./chunk-KPS6X6QX.js";
23
+ } from "./chunk-7LEZA2WA.js";
23
24
  export {
24
25
  AgentSession,
25
26
  AgentSessionError,
@@ -31,6 +32,7 @@ export {
31
32
  SessionInfo,
32
33
  SessionSetupError,
33
34
  SessionStatus,
35
+ ToolProvider,
34
36
  chatErrorForDisplay,
35
37
  contentPreview,
36
38
  getFileParts,
package/dist/internal.js CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  SessionInfo,
19
19
  SessionSetupError,
20
20
  SessionStatus,
21
+ ToolProvider,
21
22
  acceptedPostChatFollowupCompletesLatestRun,
22
23
  acknowledgeTurnEvents,
23
24
  applyTurnPatch,
@@ -82,7 +83,7 @@ import {
82
83
  withTurns,
83
84
  workflowEntriesFromOutput,
84
85
  workflowNameFromArguments
85
- } from "./chunk-KPS6X6QX.js";
86
+ } from "./chunk-7LEZA2WA.js";
86
87
 
87
88
  // src/polling-backoff.ts
88
89
  var MAX_BACKOFF_MS = 6e4;
@@ -272,6 +273,7 @@ export {
272
273
  SessionStatus,
273
274
  Task,
274
275
  TaskStatus,
276
+ ToolProvider,
275
277
  acceptedPostChatFollowupCompletesLatestRun,
276
278
  acknowledgeTurnEvents,
277
279
  applyTurnPatch,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/polling-backoff.ts","../src/platform-endpoints.ts","../src/commands/protocol.ts","../src/schemas/projection-utils.ts","../src/schemas/solution.ts","../src/schemas/task.ts"],"sourcesContent":["import { BladeApiError } from \"./rest\"\n\nconst MAX_BACKOFF_MS = 60_000\nconst MAX_TIMER_MS = 2_147_483_647\n\n/** Scheduling policy for read-only polling; never sends or replays a request. */\nexport class PollingBackoff {\n private failures = 0\n private nextAt: number | false = 0\n\n constructor(private readonly intervalMs: number) {\n if (!Number.isFinite(intervalMs) || intervalMs <= 0 || intervalMs > MAX_BACKOFF_MS) {\n throw new RangeError(\"Polling interval must be between 0 and 60000 ms\")\n }\n }\n\n reset(): void {\n this.failures = 0\n this.nextAt = 0\n }\n\n failed(error: unknown): void {\n if (\n (error instanceof Error && error.name === \"AbortError\") ||\n (error instanceof BladeApiError && error.status < 500 &&\n error.status !== 408 && error.status !== 429)\n ) {\n this.nextAt = false\n return\n }\n this.failures = Math.min(this.failures + 1, 10)\n const ceiling = Math.min(MAX_BACKOFF_MS, this.intervalMs * 2 ** this.failures)\n const backoff = Math.max(this.intervalMs, ceiling * (0.5 + Math.random() * 0.5))\n const header = error instanceof BladeApiError\n ? error.response.headers.get(\"Retry-After\")?.trim()\n : undefined\n const retryAfter = header\n ? /^\\d+$/.test(header) ? Number(header) * 1000 : Date.parse(header) - Date.now()\n : 0\n const delay = Math.max(backoff, Number.isNaN(retryAfter) ? 0 : retryAfter)\n // Oversized server delays must not overflow browser timers into a hot loop.\n this.nextAt = delay > MAX_TIMER_MS ? false : Date.now() + delay\n }\n\n /** Zero means ready, false means stop, otherwise wait at least this many ms. */\n waitMs(): number | false {\n return this.nextAt === false ? false : Math.max(0, this.nextAt - Date.now())\n }\n}\n","import { isRecord } from \"./shared/type-guards\"\n\nexport const PLATFORM_SERVICE_NAMES = [\n \"os\",\n \"hub\",\n \"agent\",\n \"oauth\",\n \"llmGateway\",\n \"gitea\",\n \"tile\",\n] as const\n\nexport type PlatformServiceName = (typeof PLATFORM_SERVICE_NAMES)[number]\n\nexport interface PlatformEndpoints {\n services: Partial<Record<PlatformServiceName, string>>\n}\n\nexport const EMPTY_PLATFORM_ENDPOINTS: PlatformEndpoints = Object.freeze({\n services: Object.freeze({}),\n})\n\nexport interface LoadPlatformEndpointsOptions {\n baseUrl?: string\n fetchImpl?: typeof fetch\n timeoutMs?: number\n}\n\nfunction defaultDocumentBaseUrl(): string | undefined {\n if (typeof document === \"undefined\") return undefined\n if (document.querySelector(\"base[href]\")) return document.baseURI\n if (typeof location !== \"undefined\" && location.origin) return `${location.origin}/`\n return document.baseURI\n}\n\nfunction normalizeServiceUrl(value: unknown): string | null {\n if (typeof value !== \"string\" || !value.trim()) return null\n if (value.includes(\"?\") || value.includes(\"#\")) return null\n try {\n const url = new URL(value.trim())\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return null\n if (url.username || url.password || url.search || url.hash) return null\n return url.toString().replace(/\\/$/, \"\")\n } catch {\n return null\n }\n}\n\nexport function parsePlatformEndpoints(value: unknown): PlatformEndpoints | null {\n if (!isRecord(value) || !isRecord(value.services)) return null\n\n const services: Partial<Record<PlatformServiceName, string>> = {}\n for (const name of PLATFORM_SERVICE_NAMES) {\n if (!(name in value.services)) continue\n const url = normalizeServiceUrl(value.services[name])\n if (!url) return null\n if (name === \"agent\" && new URL(url).pathname !== \"/\") return null\n services[name] = url\n }\n\n return { services }\n}\n\nexport async function loadPlatformEndpoints(\n options: LoadPlatformEndpointsOptions = {},\n): Promise<PlatformEndpoints> {\n const fetchImpl = options.fetchImpl ?? globalThis.fetch\n const baseUrl = options.baseUrl ?? defaultDocumentBaseUrl()\n if (!fetchImpl || !baseUrl) return EMPTY_PLATFORM_ENDPOINTS\n\n const controller = new AbortController()\n const timeout = globalThis.setTimeout(() => controller.abort(), options.timeoutMs ?? 3_000)\n try {\n const response = await fetchImpl(new URL(\"config.json\", baseUrl), {\n cache: \"no-store\",\n credentials: \"same-origin\",\n headers: { Accept: \"application/json\" },\n signal: controller.signal,\n })\n if (!response.ok) return EMPTY_PLATFORM_ENDPOINTS\n return parsePlatformEndpoints(await response.json()) ?? EMPTY_PLATFORM_ENDPOINTS\n } catch {\n return EMPTY_PLATFORM_ENDPOINTS\n } finally {\n globalThis.clearTimeout(timeout)\n }\n}\n\nexport function resolveServiceUrl(\n endpoints: PlatformEndpoints,\n name: PlatformServiceName,\n path = \"\",\n): string | null {\n const baseUrl = endpoints.services[name]\n if (!baseUrl) return null\n if (!path) return baseUrl\n const pathReference = path.trimStart()\n if (\n path.includes(\"\\\\\") ||\n pathReference.startsWith(\"//\") ||\n /^[a-z][a-z0-9+.-]*:/i.test(pathReference)\n ) {\n return null\n }\n const base = new URL(`${baseUrl}/`)\n const target = new URL(path.replace(/^\\/+/, \"\"), base)\n if (target.origin !== base.origin || !target.pathname.startsWith(base.pathname)) {\n return null\n }\n return target.toString()\n}\n","/**\n * 页面协作协议(对应后端 `_meta.bridge` 通道,见 docs/specs/260417-embed-bridge)。\n *\n * ## action 从哪里来\n *\n * 智能体 → 宿主方向的每一条指令,都源自**技能作者写的工具代码**:工具返回值\n * JSON 里带 `_meta.bridge: { action, payload }`,后端在持久化前把它剥离成独立\n * 事件送达前端(LLM 的对话历史里读不到它)。因此:\n *\n * - action 字符串(如 \"map.highlight\")是**技能作者与宿主页面开发者之间的业务\n * 契约**,LLM 不参与协议、SDK 不做枚举校验;\n * - 宿主页面用 `session.onCommand(\"map.highlight\", fn)` 消费;\n * - 未注册 handler 的 action 会进入有限缓冲等待注册,超限后丢弃并 console 告警。\n *\n * 宿主 → 智能体方向只有三个白名单动作:attach(附件入输入框)/ insertText\n * (追加输入文本)/ send(直接发消息),对应协议层的 addContext / appendInput /\n * sendMessage。\n */\n\nexport interface CommandEnvelope {\n __bladeBridge: true\n direction: \"agent-to-host\"\n action: string\n payload: unknown\n meta: {\n sessionId: string\n toolCallId?: string\n timestamp: number\n }\n}\n\nexport type InboundAction = \"addContext\" | \"appendInput\" | \"sendMessage\"\n\nexport interface InboundEnvelope {\n __bladeBridge: true\n direction: \"host-to-agent\"\n action: InboundAction\n payload: unknown\n}\n\nexport function isCommandEnvelope(value: unknown): value is CommandEnvelope {\n if (typeof value !== \"object\" || value === null) return false\n const record = value as Record<string, unknown>\n return (\n record.__bladeBridge === true &&\n record.direction === \"agent-to-host\" &&\n typeof record.action === \"string\"\n )\n}\n\nexport function isInboundEnvelope(value: unknown): value is InboundEnvelope {\n if (typeof value !== \"object\" || value === null) return false\n const record = value as Record<string, unknown>\n return (\n record.__bladeBridge === true &&\n record.direction === \"host-to-agent\" &&\n (record.action === \"addContext\" || record.action === \"appendInput\" || record.action === \"sendMessage\")\n )\n}\n","import type { PostChatFollowup, TurnProjection } from \"./projection\"\n\n/** 返回最新根会话轮次的小结;新用户消息到达后旧小结立即失效。 */\nexport function latestPostChatFollowup(turns: TurnProjection[]): PostChatFollowup | null {\n for (let index = turns.length - 1; index >= 0; index -= 1) {\n const turn = turns[index]\n if (\n turn.loop_id === \"root\" &&\n (turn.role === \"user\" || turn.role === \"assistant\") &&\n (turn.kind ?? \"message\") === \"message\" &&\n !turn.blocks.some((block) =>\n [\"mode_change\", \"plan_status\", \"ask_user_answer\"].includes(block.type),\n )\n ) {\n return turn.role === \"assistant\" ? (turn.post_chat_followup ?? null) : null\n }\n }\n return null\n}\n","export enum LayoutType {\n Default = \"default\",\n SkillEditor = \"skill-editor\",\n SolutionEditor = \"solution-editor\",\n SolutionApp = \"solution-app\",\n Custom = \"custom\",\n}\n\nexport interface Solution {\n id: string\n name: string\n version: string\n description?: string | null\n source?: \"builtin\" | \"personal\" | string | null\n layout_type: LayoutType | string\n initial_mode?: \"planning\" | \"executing\" | string | null\n initial_message?: string | null\n preview?: { url: string; title?: string | null } | null\n layout?: {\n available?: boolean\n source_dir?: string\n entry?: string\n export?: string\n } | null\n roles?: BizRole[] | null\n data?: Record<string, unknown> | null\n ui_config?: SolutionAppUiConfig | null\n}\n\nexport interface SolutionAppUiConfig {\n branding: {\n name: string\n subtitle?: string\n icon_text: string\n icon_gradient?: string\n }\n list: {\n title: string\n create_button: string\n card?: {\n subtitle?: string\n stats?: Array<{\n label: string\n value: string\n color?: string\n }>\n }\n }\n create: {\n title: string\n description?: string\n submit_button: string\n sections: Array<{\n id: string\n title: string\n description?: string\n fields: Array<SolutionAppField>\n }>\n }\n}\n\nexport type SolutionAppField =\n | {\n id: string\n kind: \"textarea\"\n label: string\n placeholder?: string\n }\n | {\n id: string\n kind: \"file-upload\"\n accept?: string[]\n categories?: Array<{\n label: string\n description: string\n color: string\n }>\n }\n\nexport interface SolutionAppState {\n pipeline: Array<{\n id: string\n label: string\n status: \"done\" | \"active\" | \"pending\" | \"error\"\n }>\n meta: Record<\n string,\n {\n label: string\n value: string\n color?: string\n }\n >\n}\n\nexport interface BizRole {\n id: string\n name: string\n description?: string | null\n preview?: { url: string; title?: string | null } | null\n}\n","import { type } from \"arktype\"\n\nexport const TaskStatus = type(\"'pending' | 'in_progress' | 'done' | 'failed' | 'skipped'\")\nexport type TaskStatus = typeof TaskStatus.infer\n\nexport const Task = type({\n id: \"string\",\n goal: \"string\",\n skills: \"string[]\",\n depends_on: \"string[]\",\n status: TaskStatus,\n result_summary: \"string\",\n number: \"string\",\n})\nexport type Task = typeof Task.infer\n"],"mappings":"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","names":["LayoutType"]}
1
+ {"version":3,"sources":["../src/polling-backoff.ts","../src/platform-endpoints.ts","../src/commands/protocol.ts","../src/schemas/projection-utils.ts","../src/schemas/solution.ts","../src/schemas/task.ts"],"sourcesContent":["import { BladeApiError } from \"./rest\"\n\nconst MAX_BACKOFF_MS = 60_000\nconst MAX_TIMER_MS = 2_147_483_647\n\n/** Scheduling policy for read-only polling; never sends or replays a request. */\nexport class PollingBackoff {\n private failures = 0\n private nextAt: number | false = 0\n\n constructor(private readonly intervalMs: number) {\n if (!Number.isFinite(intervalMs) || intervalMs <= 0 || intervalMs > MAX_BACKOFF_MS) {\n throw new RangeError(\"Polling interval must be between 0 and 60000 ms\")\n }\n }\n\n reset(): void {\n this.failures = 0\n this.nextAt = 0\n }\n\n failed(error: unknown): void {\n if (\n (error instanceof Error && error.name === \"AbortError\") ||\n (error instanceof BladeApiError && error.status < 500 &&\n error.status !== 408 && error.status !== 429)\n ) {\n this.nextAt = false\n return\n }\n this.failures = Math.min(this.failures + 1, 10)\n const ceiling = Math.min(MAX_BACKOFF_MS, this.intervalMs * 2 ** this.failures)\n const backoff = Math.max(this.intervalMs, ceiling * (0.5 + Math.random() * 0.5))\n const header = error instanceof BladeApiError\n ? error.response.headers.get(\"Retry-After\")?.trim()\n : undefined\n const retryAfter = header\n ? /^\\d+$/.test(header) ? Number(header) * 1000 : Date.parse(header) - Date.now()\n : 0\n const delay = Math.max(backoff, Number.isNaN(retryAfter) ? 0 : retryAfter)\n // Oversized server delays must not overflow browser timers into a hot loop.\n this.nextAt = delay > MAX_TIMER_MS ? false : Date.now() + delay\n }\n\n /** Zero means ready, false means stop, otherwise wait at least this many ms. */\n waitMs(): number | false {\n return this.nextAt === false ? false : Math.max(0, this.nextAt - Date.now())\n }\n}\n","import { isRecord } from \"./shared/type-guards\"\n\nexport const PLATFORM_SERVICE_NAMES = [\n \"os\",\n \"hub\",\n \"agent\",\n \"oauth\",\n \"llmGateway\",\n \"gitea\",\n \"tile\",\n] as const\n\nexport type PlatformServiceName = (typeof PLATFORM_SERVICE_NAMES)[number]\n\nexport interface PlatformEndpoints {\n services: Partial<Record<PlatformServiceName, string>>\n}\n\nexport const EMPTY_PLATFORM_ENDPOINTS: PlatformEndpoints = Object.freeze({\n services: Object.freeze({}),\n})\n\nexport interface LoadPlatformEndpointsOptions {\n baseUrl?: string\n fetchImpl?: typeof fetch\n timeoutMs?: number\n}\n\nfunction defaultDocumentBaseUrl(): string | undefined {\n if (typeof document === \"undefined\") return undefined\n if (document.querySelector(\"base[href]\")) return document.baseURI\n if (typeof location !== \"undefined\" && location.origin) return `${location.origin}/`\n return document.baseURI\n}\n\nfunction normalizeServiceUrl(value: unknown): string | null {\n if (typeof value !== \"string\" || !value.trim()) return null\n if (value.includes(\"?\") || value.includes(\"#\")) return null\n try {\n const url = new URL(value.trim())\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return null\n if (url.username || url.password || url.search || url.hash) return null\n return url.toString().replace(/\\/$/, \"\")\n } catch {\n return null\n }\n}\n\nexport function parsePlatformEndpoints(value: unknown): PlatformEndpoints | null {\n if (!isRecord(value) || !isRecord(value.services)) return null\n\n const services: Partial<Record<PlatformServiceName, string>> = {}\n for (const name of PLATFORM_SERVICE_NAMES) {\n if (!(name in value.services)) continue\n const url = normalizeServiceUrl(value.services[name])\n if (!url) return null\n if (name === \"agent\" && new URL(url).pathname !== \"/\") return null\n services[name] = url\n }\n\n return { services }\n}\n\nexport async function loadPlatformEndpoints(\n options: LoadPlatformEndpointsOptions = {},\n): Promise<PlatformEndpoints> {\n const fetchImpl = options.fetchImpl ?? globalThis.fetch\n const baseUrl = options.baseUrl ?? defaultDocumentBaseUrl()\n if (!fetchImpl || !baseUrl) return EMPTY_PLATFORM_ENDPOINTS\n\n const controller = new AbortController()\n const timeout = globalThis.setTimeout(() => controller.abort(), options.timeoutMs ?? 3_000)\n try {\n const response = await fetchImpl(new URL(\"config.json\", baseUrl), {\n cache: \"no-store\",\n credentials: \"same-origin\",\n headers: { Accept: \"application/json\" },\n signal: controller.signal,\n })\n if (!response.ok) return EMPTY_PLATFORM_ENDPOINTS\n return parsePlatformEndpoints(await response.json()) ?? EMPTY_PLATFORM_ENDPOINTS\n } catch {\n return EMPTY_PLATFORM_ENDPOINTS\n } finally {\n globalThis.clearTimeout(timeout)\n }\n}\n\nexport function resolveServiceUrl(\n endpoints: PlatformEndpoints,\n name: PlatformServiceName,\n path = \"\",\n): string | null {\n const baseUrl = endpoints.services[name]\n if (!baseUrl) return null\n if (!path) return baseUrl\n const pathReference = path.trimStart()\n if (\n path.includes(\"\\\\\") ||\n pathReference.startsWith(\"//\") ||\n /^[a-z][a-z0-9+.-]*:/i.test(pathReference)\n ) {\n return null\n }\n const base = new URL(`${baseUrl}/`)\n const target = new URL(path.replace(/^\\/+/, \"\"), base)\n if (target.origin !== base.origin || !target.pathname.startsWith(base.pathname)) {\n return null\n }\n return target.toString()\n}\n","/**\n * 页面协作协议(对应后端 `_meta.bridge` 通道,见 docs/specs/260417-embed-bridge)。\n *\n * ## action 从哪里来\n *\n * 智能体 → 宿主方向的每一条指令,都源自**技能作者写的工具代码**:工具返回值\n * JSON 里带 `_meta.bridge: { action, payload }`,后端在持久化前把它剥离成独立\n * 事件送达前端(LLM 的对话历史里读不到它)。因此:\n *\n * - action 字符串(如 \"map.highlight\")是**技能作者与宿主页面开发者之间的业务\n * 契约**,LLM 不参与协议、SDK 不做枚举校验;\n * - 宿主页面用 `session.onCommand(\"map.highlight\", fn)` 消费;\n * - 未注册 handler 的 action 会进入有限缓冲等待注册,超限后丢弃并 console 告警。\n *\n * 宿主 → 智能体方向只有三个白名单动作:attach(附件入输入框)/ insertText\n * (追加输入文本)/ send(直接发消息),对应协议层的 addContext / appendInput /\n * sendMessage。\n */\n\nexport interface CommandEnvelope {\n __bladeBridge: true\n direction: \"agent-to-host\"\n action: string\n payload: unknown\n meta: {\n sessionId: string\n toolCallId?: string\n timestamp: number\n }\n}\n\nexport type InboundAction = \"addContext\" | \"appendInput\" | \"sendMessage\"\n\nexport interface InboundEnvelope {\n __bladeBridge: true\n direction: \"host-to-agent\"\n action: InboundAction\n payload: unknown\n}\n\nexport function isCommandEnvelope(value: unknown): value is CommandEnvelope {\n if (typeof value !== \"object\" || value === null) return false\n const record = value as Record<string, unknown>\n return (\n record.__bladeBridge === true &&\n record.direction === \"agent-to-host\" &&\n typeof record.action === \"string\"\n )\n}\n\nexport function isInboundEnvelope(value: unknown): value is InboundEnvelope {\n if (typeof value !== \"object\" || value === null) return false\n const record = value as Record<string, unknown>\n return (\n record.__bladeBridge === true &&\n record.direction === \"host-to-agent\" &&\n (record.action === \"addContext\" || record.action === \"appendInput\" || record.action === \"sendMessage\")\n )\n}\n","import type { PostChatFollowup, TurnProjection } from \"./projection\"\n\n/** 返回最新根会话轮次的小结;新用户消息到达后旧小结立即失效。 */\nexport function latestPostChatFollowup(turns: TurnProjection[]): PostChatFollowup | null {\n for (let index = turns.length - 1; index >= 0; index -= 1) {\n const turn = turns[index]\n if (\n turn.loop_id === \"root\" &&\n (turn.role === \"user\" || turn.role === \"assistant\") &&\n (turn.kind ?? \"message\") === \"message\" &&\n !turn.blocks.some((block) =>\n [\"mode_change\", \"plan_status\", \"ask_user_answer\"].includes(block.type),\n )\n ) {\n return turn.role === \"assistant\" ? (turn.post_chat_followup ?? null) : null\n }\n }\n return null\n}\n","export enum LayoutType {\n Default = \"default\",\n SkillEditor = \"skill-editor\",\n SolutionEditor = \"solution-editor\",\n SolutionApp = \"solution-app\",\n Custom = \"custom\",\n}\n\nexport interface Solution {\n id: string\n name: string\n version: string\n description?: string | null\n source?: \"builtin\" | \"personal\" | string | null\n layout_type: LayoutType | string\n initial_mode?: \"planning\" | \"executing\" | string | null\n initial_message?: string | null\n preview?: { url: string; title?: string | null } | null\n layout?: {\n available?: boolean\n source_dir?: string\n entry?: string\n export?: string\n } | null\n roles?: BizRole[] | null\n data?: Record<string, unknown> | null\n ui_config?: SolutionAppUiConfig | null\n}\n\nexport interface SolutionAppUiConfig {\n branding: {\n name: string\n subtitle?: string\n icon_text: string\n icon_gradient?: string\n }\n list: {\n title: string\n create_button: string\n card?: {\n subtitle?: string\n stats?: Array<{\n label: string\n value: string\n color?: string\n }>\n }\n }\n create: {\n title: string\n description?: string\n submit_button: string\n sections: Array<{\n id: string\n title: string\n description?: string\n fields: Array<SolutionAppField>\n }>\n }\n}\n\nexport type SolutionAppField =\n | {\n id: string\n kind: \"textarea\"\n label: string\n placeholder?: string\n }\n | {\n id: string\n kind: \"file-upload\"\n accept?: string[]\n categories?: Array<{\n label: string\n description: string\n color: string\n }>\n }\n\nexport interface SolutionAppState {\n pipeline: Array<{\n id: string\n label: string\n status: \"done\" | \"active\" | \"pending\" | \"error\"\n }>\n meta: Record<\n string,\n {\n label: string\n value: string\n color?: string\n }\n >\n}\n\nexport interface BizRole {\n id: string\n name: string\n description?: string | null\n preview?: { url: string; title?: string | null } | null\n}\n","import { type } from \"arktype\"\n\nexport const TaskStatus = type(\"'pending' | 'in_progress' | 'done' | 'failed' | 'skipped'\")\nexport type TaskStatus = typeof TaskStatus.infer\n\nexport const Task = type({\n id: \"string\",\n goal: \"string\",\n skills: \"string[]\",\n depends_on: \"string[]\",\n status: TaskStatus,\n result_summary: \"string\",\n number: \"string\",\n})\nexport type Task = typeof Task.infer\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,IAAM,iBAAiB;AACvB,IAAM,eAAe;AAGd,IAAM,iBAAN,MAAqB;AAAA,EAI1B,YAA6B,YAAoB;AAApB;AAC3B,QAAI,CAAC,OAAO,SAAS,UAAU,KAAK,cAAc,KAAK,aAAa,gBAAgB;AAClF,YAAM,IAAI,WAAW,iDAAiD;AAAA,IACxE;AAAA,EACF;AAAA,EAJ6B;AAAA,EAHrB,WAAW;AAAA,EACX,SAAyB;AAAA,EAQjC,QAAc;AACZ,SAAK,WAAW;AAChB,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,OAAO,OAAsB;AAC3B,QACG,iBAAiB,SAAS,MAAM,SAAS,gBACzC,iBAAiB,iBAAiB,MAAM,SAAS,OAChD,MAAM,WAAW,OAAO,MAAM,WAAW,KAC3C;AACA,WAAK,SAAS;AACd;AAAA,IACF;AACA,SAAK,WAAW,KAAK,IAAI,KAAK,WAAW,GAAG,EAAE;AAC9C,UAAM,UAAU,KAAK,IAAI,gBAAgB,KAAK,aAAa,KAAK,KAAK,QAAQ;AAC7E,UAAM,UAAU,KAAK,IAAI,KAAK,YAAY,WAAW,MAAM,KAAK,OAAO,IAAI,IAAI;AAC/E,UAAM,SAAS,iBAAiB,gBAC5B,MAAM,SAAS,QAAQ,IAAI,aAAa,GAAG,KAAK,IAChD;AACJ,UAAM,aAAa,SACf,QAAQ,KAAK,MAAM,IAAI,OAAO,MAAM,IAAI,MAAO,KAAK,MAAM,MAAM,IAAI,KAAK,IAAI,IAC7E;AACJ,UAAM,QAAQ,KAAK,IAAI,SAAS,OAAO,MAAM,UAAU,IAAI,IAAI,UAAU;AAEzE,SAAK,SAAS,QAAQ,eAAe,QAAQ,KAAK,IAAI,IAAI;AAAA,EAC5D;AAAA;AAAA,EAGA,SAAyB;AACvB,WAAO,KAAK,WAAW,QAAQ,QAAQ,KAAK,IAAI,GAAG,KAAK,SAAS,KAAK,IAAI,CAAC;AAAA,EAC7E;AACF;;;AC9CO,IAAM,yBAAyB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAQO,IAAM,2BAA8C,OAAO,OAAO;AAAA,EACvE,UAAU,OAAO,OAAO,CAAC,CAAC;AAC5B,CAAC;AAQD,SAAS,yBAA6C;AACpD,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,MAAI,SAAS,cAAc,YAAY,EAAG,QAAO,SAAS;AAC1D,MAAI,OAAO,aAAa,eAAe,SAAS,OAAQ,QAAO,GAAG,SAAS,MAAM;AACjF,SAAO,SAAS;AAClB;AAEA,SAAS,oBAAoB,OAA+B;AAC1D,MAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,EAAG,QAAO;AACvD,MAAI,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,GAAG,EAAG,QAAO;AACvD,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,MAAM,KAAK,CAAC;AAChC,QAAI,IAAI,aAAa,WAAW,IAAI,aAAa,SAAU,QAAO;AAClE,QAAI,IAAI,YAAY,IAAI,YAAY,IAAI,UAAU,IAAI,KAAM,QAAO;AACnE,WAAO,IAAI,SAAS,EAAE,QAAQ,OAAO,EAAE;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,uBAAuB,OAA0C;AAC/E,MAAI,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,MAAM,QAAQ,EAAG,QAAO;AAE1D,QAAM,WAAyD,CAAC;AAChE,aAAW,QAAQ,wBAAwB;AACzC,QAAI,EAAE,QAAQ,MAAM,UAAW;AAC/B,UAAM,MAAM,oBAAoB,MAAM,SAAS,IAAI,CAAC;AACpD,QAAI,CAAC,IAAK,QAAO;AACjB,QAAI,SAAS,WAAW,IAAI,IAAI,GAAG,EAAE,aAAa,IAAK,QAAO;AAC9D,aAAS,IAAI,IAAI;AAAA,EACnB;AAEA,SAAO,EAAE,SAAS;AACpB;AAEA,eAAsB,sBACpB,UAAwC,CAAC,GACb;AAC5B,QAAM,YAAY,QAAQ,aAAa,WAAW;AAClD,QAAM,UAAU,QAAQ,WAAW,uBAAuB;AAC1D,MAAI,CAAC,aAAa,CAAC,QAAS,QAAO;AAEnC,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,UAAU,WAAW,WAAW,MAAM,WAAW,MAAM,GAAG,QAAQ,aAAa,GAAK;AAC1F,MAAI;AACF,UAAM,WAAW,MAAM,UAAU,IAAI,IAAI,eAAe,OAAO,GAAG;AAAA,MAChE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS,EAAE,QAAQ,mBAAmB;AAAA,MACtC,QAAQ,WAAW;AAAA,IACrB,CAAC;AACD,QAAI,CAAC,SAAS,GAAI,QAAO;AACzB,WAAO,uBAAuB,MAAM,SAAS,KAAK,CAAC,KAAK;AAAA,EAC1D,QAAQ;AACN,WAAO;AAAA,EACT,UAAE;AACA,eAAW,aAAa,OAAO;AAAA,EACjC;AACF;AAEO,SAAS,kBACd,WACA,MACA,OAAO,IACQ;AACf,QAAM,UAAU,UAAU,SAAS,IAAI;AACvC,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,gBAAgB,KAAK,UAAU;AACrC,MACE,KAAK,SAAS,IAAI,KAClB,cAAc,WAAW,IAAI,KAC7B,uBAAuB,KAAK,aAAa,GACzC;AACA,WAAO;AAAA,EACT;AACA,QAAM,OAAO,IAAI,IAAI,GAAG,OAAO,GAAG;AAClC,QAAM,SAAS,IAAI,IAAI,KAAK,QAAQ,QAAQ,EAAE,GAAG,IAAI;AACrD,MAAI,OAAO,WAAW,KAAK,UAAU,CAAC,OAAO,SAAS,WAAW,KAAK,QAAQ,GAAG;AAC/E,WAAO;AAAA,EACT;AACA,SAAO,OAAO,SAAS;AACzB;;;ACtEO,SAAS,kBAAkB,OAA0C;AAC1E,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,SAAS;AACf,SACE,OAAO,kBAAkB,QACzB,OAAO,cAAc,mBACrB,OAAO,OAAO,WAAW;AAE7B;AAEO,SAAS,kBAAkB,OAA0C;AAC1E,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,SAAS;AACf,SACE,OAAO,kBAAkB,QACzB,OAAO,cAAc,oBACpB,OAAO,WAAW,gBAAgB,OAAO,WAAW,iBAAiB,OAAO,WAAW;AAE5F;;;ACvDO,SAAS,uBAAuB,OAAkD;AACvF,WAAS,QAAQ,MAAM,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG;AACzD,UAAM,OAAO,MAAM,KAAK;AACxB,QACE,KAAK,YAAY,WAChB,KAAK,SAAS,UAAU,KAAK,SAAS,iBACtC,KAAK,QAAQ,eAAe,aAC7B,CAAC,KAAK,OAAO;AAAA,MAAK,CAAC,UACjB,CAAC,eAAe,eAAe,iBAAiB,EAAE,SAAS,MAAM,IAAI;AAAA,IACvE,GACA;AACA,aAAO,KAAK,SAAS,cAAe,KAAK,sBAAsB,OAAQ;AAAA,IACzE;AAAA,EACF;AACA,SAAO;AACT;;;AClBO,IAAK,aAAL,kBAAKA,gBAAL;AACL,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,iBAAc;AACd,EAAAA,YAAA,oBAAiB;AACjB,EAAAA,YAAA,iBAAc;AACd,EAAAA,YAAA,YAAS;AALC,SAAAA;AAAA,GAAA;;;ACAZ,SAAS,YAAY;AAEd,IAAM,aAAa,KAAK,2DAA2D;AAGnF,IAAM,OAAO,KAAK;AAAA,EACvB,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,gBAAgB;AAAA,EAChB,QAAQ;AACV,CAAC;","names":["LayoutType"]}
@@ -87,6 +87,8 @@ export interface SessionPluginConfigUpdate {
87
87
  remove?: string[];
88
88
  }
89
89
  export interface CreateSessionRequest {
90
+ /** 认证创建独立匿名沙盒;链接持有者可继续聊天,不继承个人资源。 */
91
+ anonymous?: boolean;
90
92
  intent?: string;
91
93
  /** 正式 Solution 来源。Hub 使用 { source: "hub", org, name }。 */
92
94
  solution_ref?: PublishedSolutionRef;
@@ -557,6 +559,15 @@ export declare class SessionsResource {
557
559
  id: string;
558
560
  leaf_id: string;
559
561
  }>;
562
+ listAnonymousSessions(): Promise<Array<{
563
+ id: string;
564
+ intent: string;
565
+ created_at: string;
566
+ status: string;
567
+ }>>;
568
+ deleteAnonymousSession(sessionId: string): Promise<{
569
+ deleted: boolean;
570
+ }>;
560
571
  deleteSession(sessionId: string, options?: {
561
572
  deleteWorkspace?: boolean;
562
573
  }): Promise<{
@@ -87,6 +87,7 @@ export interface ChatMessage {
87
87
  duration_ms?: number;
88
88
  timestamp?: string;
89
89
  entry_id?: string;
90
+ client_request_id?: string;
90
91
  /** Stable React key retained when an optimistic user message is confirmed. */
91
92
  render_id?: string;
92
93
  parent_id?: string | null;
@@ -58,6 +58,7 @@ export declare const SessionInfo: import("arktype/internal/variants/object.ts").
58
58
  created_at: string;
59
59
  updated_at: string;
60
60
  shared?: boolean | undefined;
61
+ anonymous?: boolean | undefined;
61
62
  memory_enabled?: boolean | undefined;
62
63
  is_persistent?: boolean | undefined;
63
64
  initiator?: "sdk" | "ui" | "unknown" | null | undefined;
@@ -84,6 +84,27 @@ export interface SendOptions {
84
84
  */
85
85
  queueWhenRunning?: boolean;
86
86
  }
87
+ /** Durable plain-text input. Persist this JSON value before calling sendPrepared. */
88
+ export interface PreparedSend {
89
+ sessionId: string;
90
+ requestId: string;
91
+ content: string;
92
+ options: PreparedSendOptions;
93
+ }
94
+ export interface PreparedSendOptions {
95
+ mode?: "planning" | "executing";
96
+ model?: string;
97
+ thinkingOverride?: boolean;
98
+ reasoningLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
99
+ }
100
+ export interface DeliveryReceipt {
101
+ requestId: string;
102
+ status: "accepted" | "not_found" | "rejected" | "unknown";
103
+ itemId?: string;
104
+ queueStatus?: string;
105
+ executionId?: string | null;
106
+ message?: string;
107
+ }
87
108
  export declare function sessionStatusFromChatEnd(status: string | undefined): SessionStatus;
88
109
  /**
89
110
  * 一个会话的实时状态机:封装 Socket.IO 订阅、历史 + 流式合流、断线补数,
@@ -265,6 +286,12 @@ export declare class AgentSession {
265
286
  * CLI 安装成功后才保存 getContext;之后每次 send 前恰好读取一次。
266
287
  */
267
288
  attachApp(options: AttachAppOptions): Promise<AppCliAttachment | null>;
289
+ /** Prepare a durable text input without sending any network request. */
290
+ prepareSend(content: string, options?: PreparedSendOptions): PreparedSend;
291
+ /** Durably enqueue an ordinary text input. Retries must reuse the saved request. */
292
+ sendPrepared(request: PreparedSend): Promise<DeliveryReceipt>;
293
+ /** Owner-authorized lookup; receipts remain until the session is deleted. */
294
+ getDelivery(requestId: string): Promise<DeliveryReceipt>;
268
295
  /** 发送一条消息。字符串或多模态内容均可。 */
269
296
  send(content: MessageContent, options?: SendOptions): Promise<boolean>;
270
297
  /**
@@ -0,0 +1,51 @@
1
+ import type { BladeClient } from "./blade-client";
2
+ export interface ProviderTool {
3
+ name: string;
4
+ description?: string;
5
+ inputSchema: Record<string, unknown>;
6
+ handler: (arguments_: Record<string, unknown>, context: {
7
+ sessionId: string;
8
+ signal: AbortSignal;
9
+ }) => Promise<ProviderToolResult>;
10
+ }
11
+ export interface ProviderToolResult {
12
+ content: Array<{
13
+ type: "text";
14
+ text: string;
15
+ }>;
16
+ structuredContent?: Record<string, unknown>;
17
+ isError?: boolean;
18
+ }
19
+ export interface ToolProviderOptions {
20
+ /** Stable application-owned ID, preserved across reconnects. */
21
+ providerId: string;
22
+ /** Secret bound to the session's mcp_sources bearer_token. */
23
+ capability: string;
24
+ tools: ProviderTool[];
25
+ onError?: (error: Error) => void;
26
+ }
27
+ /** Backend-only outbound connection. Reconnects registrations, never tool calls. */
28
+ export declare class ToolProvider {
29
+ private client;
30
+ private options;
31
+ private onDispose;
32
+ readonly url: string;
33
+ private socket?;
34
+ private timer?;
35
+ private registrationTimer?;
36
+ private disposed;
37
+ private registered;
38
+ private attempts;
39
+ private calls;
40
+ private waiters;
41
+ constructor(client: BladeClient, options: ToolProviderOptions, onDispose: () => void);
42
+ /** Resolves only after BA has authenticated and registered this connection. */
43
+ ready(): Promise<void>;
44
+ reconnect(): void;
45
+ private reset;
46
+ private fail;
47
+ private open;
48
+ private schedule;
49
+ private receive;
50
+ dispose(): void;
51
+ }
@@ -109,14 +109,35 @@ export declare namespace ChatAppendPayloadSchema {
109
109
  type SessionId = string;
110
110
  type Message = string;
111
111
  type ClientRequestId = (string | null);
112
+ type Mode = (("planning" | "executing") | null);
113
+ type Model = (string | null);
114
+ type ThinkingOverride = (boolean | null);
115
+ type ReasoningLevel = (("off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max") | null);
112
116
  interface ChatAppendPayload {
113
117
  session_id: SessionId;
114
118
  message: Message;
115
119
  client_request_id?: ClientRequestId;
120
+ prepared_options?: (PreparedSendOptions | null);
116
121
  [k: string]: unknown;
117
122
  }
123
+ interface PreparedSendOptions {
124
+ mode?: Mode;
125
+ model?: Model;
126
+ thinking_override?: ThinkingOverride;
127
+ reasoning_level?: ReasoningLevel;
128
+ }
118
129
  }
119
130
  export type ChatAppendPayload = ChatAppendPayloadSchema.ChatAppendPayload;
131
+ export declare namespace ChatQueueRequestPayloadSchema {
132
+ type SessionId = string;
133
+ type ClientRequestId = string;
134
+ interface ChatQueueRequestPayload {
135
+ session_id: SessionId;
136
+ client_request_id: ClientRequestId;
137
+ [k: string]: unknown;
138
+ }
139
+ }
140
+ export type ChatQueueRequestPayload = ChatQueueRequestPayloadSchema.ChatQueueRequestPayload;
120
141
  export declare namespace ChatCompactPayloadSchema {
121
142
  type SessionId = string;
122
143
  interface ChatCompactPayload {
@@ -598,6 +619,12 @@ export declare namespace ChatQueueAckSchema {
598
619
  type Items = ChatQueueItem[];
599
620
  type Status1 = string;
600
621
  type Message = (string | null);
622
+ type Status2 = ("accepted" | "not_found" | "error");
623
+ type RequestId = (string | null);
624
+ type ItemId = (string | null);
625
+ type QueueStatus = (string | null);
626
+ type ExecutionId = (string | null);
627
+ type Message1 = (string | null);
601
628
  interface ChatQueueAck {
602
629
  session_id: SessionId;
603
630
  revision: Revision;
@@ -606,6 +633,7 @@ export declare namespace ChatQueueAckSchema {
606
633
  items?: Items;
607
634
  status?: Status1;
608
635
  message?: Message;
636
+ delivery?: (ChatDeliveryAck | null);
609
637
  [k: string]: unknown;
610
638
  }
611
639
  interface ChatQueueItem {
@@ -620,12 +648,40 @@ export declare namespace ChatQueueAckSchema {
620
648
  error?: Error;
621
649
  [k: string]: unknown;
622
650
  }
651
+ interface ChatDeliveryAck {
652
+ status: Status2;
653
+ request_id?: RequestId;
654
+ item_id?: ItemId;
655
+ queue_status?: QueueStatus;
656
+ execution_id?: ExecutionId;
657
+ message?: Message1;
658
+ [k: string]: unknown;
659
+ }
623
660
  }
624
661
  export type ChatQueueAck = ChatQueueAckSchema.ChatQueueAck;
662
+ export declare namespace ChatDeliveryAckSchema {
663
+ type Status = ("accepted" | "not_found" | "error");
664
+ type RequestId = (string | null);
665
+ type ItemId = (string | null);
666
+ type QueueStatus = (string | null);
667
+ type ExecutionId = (string | null);
668
+ type Message = (string | null);
669
+ interface ChatDeliveryAck {
670
+ status: Status;
671
+ request_id?: RequestId;
672
+ item_id?: ItemId;
673
+ queue_status?: QueueStatus;
674
+ execution_id?: ExecutionId;
675
+ message?: Message;
676
+ [k: string]: unknown;
677
+ }
678
+ }
679
+ export type ChatDeliveryAck = ChatDeliveryAckSchema.ChatDeliveryAck;
625
680
  export interface ClientToServerEvents {
626
681
  "chat:send": (payload: ChatSendPayload, ack: (response: ChatSendAck) => void) => void;
627
682
  "chat:stop": (payload: ChatStopPayload, ack: (response: ChatStopAck) => void) => void;
628
683
  "chat:append": (payload: ChatAppendPayload, ack: (response: ChatQueueAck) => void) => void;
684
+ "chat:queue:request": (payload: ChatQueueRequestPayload, ack: (response: ChatDeliveryAck) => void) => void;
629
685
  "chat:compact": (payload: ChatCompactPayload, ack: (response: ChatCompactAck) => void) => void;
630
686
  "chat:queue:list": (payload: ChatQueueListPayload, ack: (response: ChatQueueAck) => void) => void;
631
687
  "chat:queue:update": (payload: ChatQueueUpdatePayload, ack: (response: ChatQueueAck) => void) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blade-hq/agent-client",
3
- "version": "2612.0.0-beta.13",
3
+ "version": "2612.0.0-beta.15",
4
4
  "description": "Blade Agent 框架无关客户端:会话实时状态机、REST 通道、弹窗登录、页面协作。浏览器与 Node.js 通用。",
5
5
  "repository": {
6
6
  "type": "git",
package/public-api.md CHANGED
@@ -3,7 +3,7 @@
3
3
  > 本文件由 `scripts/public-api-report.mjs` 生成,请勿手改。
4
4
  > 公共面变更时重新生成本报告,并同步更新 README 对应章节——CI 会校验两者。
5
5
 
6
- 共 106 个公开声明。
6
+ 共 113 个公开声明。
7
7
 
8
8
  ## `AccountComputer` (interface)
9
9
 
@@ -62,6 +62,7 @@ deliverQueuedMessage: (itemId: string, expectedVersion: number) => Promise<Queue
62
62
  dispose: () => void
63
63
  downloadFile: (filePath: string, downloadName?: string) => Promise<void>
64
64
  exitReplay: () => Promise<void>
65
+ getDelivery: (requestId: string) => Promise<DeliveryReceipt>
65
66
  getState: () => SessionState
66
67
  hasOlderHistory: boolean
67
68
  insertText: (text: string) => void
@@ -74,6 +75,7 @@ locateEntry: (entryId: string) => Promise<boolean>
74
75
  off: <K extends keyof AgentSessionEvents>(name: K, handler: (event: AgentSessionEvents[K]) => void) => void
75
76
  on: <K extends keyof AgentSessionEvents>(name: K, handler: (event: AgentSessionEvents[K]) => void) => () => void
76
77
  onCommand: (action: string, handler: CommandHandler) => () => void
78
+ prepareSend: (content: string, options?: PreparedSendOptions) => PreparedSend
77
79
  queueMessage: (content: string) => Promise<QueueOperationResult>
78
80
  refreshQueue: () => Promise<QueueOperationResult>
79
81
  refreshRunningState: () => Promise<void>
@@ -81,6 +83,7 @@ reloadHistory: () => Promise<boolean>
81
83
  reorderQueuedMessages: (expectedRevision: number, orderedIds: string[]) => Promise<QueueOperationResult>
82
84
  resumeQueue: () => Promise<QueueOperationResult>
83
85
  send: (content: MessageContent, options?: SendOptions) => Promise<boolean>
86
+ sendPrepared: (request: PreparedSend) => Promise<DeliveryReceipt>
84
87
  sessionId: string
85
88
  setReplaySpeed: (speed: ReplaySpeed) => Promise<void>
86
89
  stop: () => Promise<Record<string, unknown>>
@@ -200,7 +203,9 @@ blob: (method: HttpMethod, path: string) => Promise<Blob>
200
203
  buildAuthedUrl: (path: string) => string
201
204
  chatGroups: ChatGroupsResource
202
205
  computers: ComputersResource
206
+ connectToolProvider: (options: ToolProviderOptions) => ToolProvider
203
207
  fetch: (method: HttpMethod, path: string, init?: BladeFetchInit, isRetry?: boolean) => Promise<Response>
208
+ forAnonymousSession: (sessionId: string) => BladeClient
204
209
  formData: (method: HttpMethod, path: string, form: FormData, options?: { expectOk?: boolean; onUploadProgress?: (progress: UploadProgress) => void; }) => Promise<Response>
205
210
  hasToken: () => boolean
206
211
  headless: HeadlessResource
@@ -228,8 +233,9 @@ token: string | null
228
233
  ## `BladeClientOptions` (interface)
229
234
 
230
235
  ```ts
236
+ anonymousSessionId: string | undefined
231
237
  baseUrl: string
232
- commandTransport: ((event: string, payload: Record<string, unknown>, send: () => Promise<Record<string, unknown>>) => Promise<Record<string, unknown>>) | undefined
238
+ commandTransport: ((event: string, payload: Record<string, unknown>, send: () => Promise<Record<string, unknown>>, scope?: { anonymousSessionId: string; }) => Promise<Record<string, unknown>>) | undefined
233
239
  fetchImpl: { (input: RequestInfo | URL, init?: RequestInit): Promise<Response>; (input: string | URL | Request, init?: RequestInit): Promise<Response>; (input: string | URL | Request, init?: RequestInit): Promise<Response>; } | undefined
234
240
  onRefreshSuccess: (() => void | Promise<void>) | undefined
235
241
  socketToken: string | (() => string | null | undefined) | undefined
@@ -321,6 +327,7 @@ updated_at: string
321
327
 
322
328
  ```ts
323
329
  blocks: ContentBlock[] | undefined
330
+ client_request_id: string | undefined
324
331
  compaction: CompactionInfo | undefined
325
332
  content: MessageContent
326
333
  context: ContextProjectionData | undefined
@@ -380,6 +387,7 @@ title: string
380
387
 
381
388
  ```ts
382
389
  agent_runtime_id: string | null | undefined
390
+ anonymous: boolean | undefined
383
391
  biz_role_id: string | null | undefined
384
392
  chat_group_id: string | null | undefined
385
393
  compaction_ratio: number | null | undefined
@@ -405,6 +413,17 @@ solution_snapshot_session_id: string | undefined
405
413
  DEFAULT_REPLAY_SPEED: ReplaySpeed
406
414
  ```
407
415
 
416
+ ## `DeliveryReceipt` (interface)
417
+
418
+ ```ts
419
+ executionId: string | null | undefined
420
+ itemId: string | undefined
421
+ message: string | undefined
422
+ queueStatus: string | undefined
423
+ requestId: string
424
+ status: "unknown" | "accepted" | "not_found" | "rejected"
425
+ ```
426
+
408
427
  ## `ExchangeCodeParams` (interface)
409
428
 
410
429
  ```ts
@@ -607,6 +626,24 @@ provider: "plugin"
607
626
  server: string
608
627
  ```
609
628
 
629
+ ## `PreparedSend` (interface)
630
+
631
+ ```ts
632
+ content: string
633
+ options: PreparedSendOptions
634
+ requestId: string
635
+ sessionId: string
636
+ ```
637
+
638
+ ## `PreparedSendOptions` (interface)
639
+
640
+ ```ts
641
+ mode: "planning" | "executing" | undefined
642
+ model: string | undefined
643
+ reasoningLevel: "max" | "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | undefined
644
+ thinkingOverride: boolean | undefined
645
+ ```
646
+
610
647
  ## `ProvidersResponse` (interface)
611
648
 
612
649
  ```ts
@@ -614,6 +651,23 @@ password_enabled: boolean
614
651
  providers: { name: string; authorize_url: string; }[]
615
652
  ```
616
653
 
654
+ ## `ProviderTool` (interface)
655
+
656
+ ```ts
657
+ description: string | undefined
658
+ handler: (arguments_: Record<string, unknown>, context: { sessionId: string; signal: AbortSignal; }) => Promise<ProviderToolResult>
659
+ inputSchema: Record<string, unknown>
660
+ name: string
661
+ ```
662
+
663
+ ## `ProviderToolResult` (interface)
664
+
665
+ ```ts
666
+ content: { type: "text"; text: string; }[]
667
+ isError: boolean | undefined
668
+ structuredContent: Record<string, unknown> | undefined
669
+ ```
670
+
617
671
  ## `QueuedMessage` (interface)
618
672
 
619
673
  ```ts
@@ -780,6 +834,7 @@ solution: SolutionDefinition | undefined
780
834
 
781
835
  ```ts
782
836
  agent_runtime_id: string | null | undefined
837
+ anonymous: boolean | undefined
783
838
  ba_version: string | null | undefined
784
839
  biz_role_id: string | null | undefined
785
840
  chat_group_id: string | null | undefined
@@ -825,6 +880,7 @@ SessionInfo = type({
825
880
  created_at: "string",
826
881
  updated_at: "string",
827
882
  "shared?": "boolean",
883
+ "anonymous?": "boolean",
828
884
  "memory_enabled?": "boolean",
829
885
  "is_persistent?": "boolean",
830
886
  "initiator?": "'ui' | 'sdk' | 'unknown' | null",
@@ -895,6 +951,7 @@ createSession: (intent?: string) => Promise<{ session_id: string; }>
895
951
  createSessionWithRequest: (request: CreateSessionRequest) => Promise<{ session_id: string; }>
896
952
  createShare: (sessionId: string) => Promise<ShareLinkResult>
897
953
  createWithProfile: (params: { intent: string; profile: SessionProfile; }) => Promise<{ session_id: string; }>
954
+ deleteAnonymousSession: (sessionId: string) => Promise<{ deleted: boolean; }>
898
955
  deleteFile: (sessionId: string, filePath: string) => Promise<void>
899
956
  deleteSession: (sessionId: string, options?: { deleteWorkspace?: boolean; }) => Promise<{ deleted: boolean; workspace_deleted?: boolean; }>
900
957
  downloadFile: (sessionId: string, filePath: string, downloadName?: string) => Promise<void>
@@ -920,6 +977,7 @@ getSessionTurns: (sessionId: string) => Promise<TurnProjection[]>
920
977
  getSessionTurnsPage: (sessionId: string, options?: SessionTurnsQuery, init?: RequestInit) => Promise<SessionTurnsPage>
921
978
  getSharedSession: (token: string) => Promise<TurnProjection[]>
922
979
  importSession: (file: File, name?: string, solutionOverride?: string | null | ImportSessionOptions) => Promise<{ session_id: string; }>
980
+ listAnonymousSessions: () => Promise<Array<{ id: string; intent: string; created_at: string; status: string; }>>
923
981
  listBackgroundTasks: (sessionId: string, init?: RequestInit) => Promise<BackgroundTask[]>
924
982
  listDir: (sessionId: string, dirPath: string, showHidden?: boolean) => Promise<FileEntry[]>
925
983
  listResultFeedback: (sessionId: string) => Promise<ResultFeedback[]>
@@ -1095,6 +1153,24 @@ tool_call_id: string
1095
1153
  url: string | undefined
1096
1154
  ```
1097
1155
 
1156
+ ## `ToolProvider` (class)
1157
+
1158
+ ```ts
1159
+ dispose: () => void
1160
+ ready: () => Promise<void>
1161
+ reconnect: () => void
1162
+ url: string
1163
+ ```
1164
+
1165
+ ## `ToolProviderOptions` (interface)
1166
+
1167
+ ```ts
1168
+ capability: string
1169
+ onError: ((error: Error) => void) | undefined
1170
+ providerId: string
1171
+ tools: ProviderTool[]
1172
+ ```
1173
+
1098
1174
  ## `TopicAssetRef` (interface)
1099
1175
 
1100
1176
  ```ts