@astralform/js

中文文档
9.13.0发布于 2026/10/25MITTypeScriptESM + CJS零依赖构建来源已验证近 30 天下载 2220

Astralform JS SDK - 在WebMCP支持下的AI代理管弦乐

中文使用文档
文档目录17 节
  1. 安装
  2. 快速启动
  3. 特征
  4. 配置
  5. 活动
  6. 客户端工具
  7. 核准工具
  8. UI 协议
  9. 多代理人
  10. 对话管理
  11. 启用客户端工具
  12. 低级客户端
  13. 自定义存储
  14. 处理错误
  15. 框架整合
  16. 发展
  17. 许可证

@ astralform/js (英语)

Java脚本/ Type脚本 SDK 用于 星状 ——AI代理管弦乐与SSE流和客户端-侧工具执行.

安装

npm install @astralform/js

快速启动

import { ChatSession } from "@astralform/js";

const session = new ChatSession({
  apiKey: "your-api-key",
  userId: "user-123",
});

session.on((event) => {
  switch (event.type) {
    case "block_delta":
      if (event.delta.channel === "text") {
        process.stdout.write(event.delta.text);
      }
      break;
    case "message_stop":
      console.log("\nDone!");
      break;
    case "error":
      console.error(`${event.code}: ${event.message}`);
      break;
  }
});

await session.connect();

// A conversation's first message names its model — one the agent's team has
// connected. Later messages in the same conversation may omit it and reuse the
// conversation's model.
const models = await session.client.getModels(); // the list backs a model picker
const model = models[0];
if (!model) throw new Error("Connect a model provider for this agent first.");
await session.send("What is the capital of France?", {
  provider: model.provider,
  model: model.model,
});
session.disconnect();

以下示例略去 provider 财务报告和审定财务报表 model 简洁。 第一次谈话时 消息,如上所示: 更新的 Astralform 服务器拒绝一个新的对话 没有名号,每个服务器都接受一个名号.

特征

  • SSE 流线 ——通过Server-Sent Evolutions实时按键响应
  • 客户端工具 —— LLM 可以调用, 在您的应用程序中本地执行的注册工具
  • 核准工具 - 答复 tool_approval_requested 执行前的事件
  • UI 协议 – MCP 风格嵌入式资源的可插件渲染器(A2UI等)
  • 多代理人 ——给特定代理人的路由信息或让主管选择
  • 对话管理 ——创建,切换,删除并恢复对话
  • 事件重播 - 将持续的电讯事件转换成 ChatEvent编号
  • 无依赖 - 仅使用本地API(fetch, (中文(简体) ), ReadableStream, (中文(简体) ), crypto(中文(简体) )
  • 通用 ——无害环境管理+CJS,在浏览器和节点18+工作

配置

import { ChatSession } from "@astralform/js";

const session = new ChatSession({
  apiKey: "your-api-key",   // Required — Astralform agent API key
  userId: "user-123",       // Required — identifies the end user
  baseURL: "http://localhost:8000", // Optional — defaults to https://api.astralform.ai
  fetch: customFetch,       // Optional — custom fetch implementation
  timeoutMs: 30_000,        // Optional — REST request deadline, defaults to 30s
});

timeoutMs 将每个 REST 请求结束到结束 —— 连接、 信头和 正文读取 - 用 ConnectionError 当它过期。 文件上传和 sSE 溪流是免责的:两者都是设计上的长期运行,而溪流也是免责的 自己携带 AbortSignal。 。 。 .

活动

订阅事件 .on(),它返回一个未订阅的函数。 SDK 向前键入 ChatEvent 每起电线事件——消费者从溪流中建立自己的块/消息状态.

const unsubscribe = session.on((event) => {
  switch (event.type) {
    case "connected":
      // Session connected, agent status and tools loaded
      break;

    // --- Turn lifecycle ---
    case "message_start":
      // New turn: event.turnId, event.model, event.agentDisplayName
      break;
    case "block_start":
      // A content block opened: event.kind ("text" | "thinking" | "tool_use" | ...)
      break;
    case "block_delta":
      // Streaming chunk. Narrow by event.delta.channel:
      //   - "text": event.delta.text
      //   - "thinking": event.delta.text
      //   - "input" / "input_arg": partial tool input
      //   - "output": interpreter stdout/stderr/progress
      //   - "status": "executing" | "awaiting_client_result" | "awaiting_approval" | "denied"
      break;
    case "block_stop":
      // Block finished. event.status === "awaiting_client_result" means
      // a client-side tool is ready to run (see "Client-Side Tools" below).
      break;
    case "message_stop":
      // Turn complete: event.stopReason, event.usage, event.totalMs, event.jobId
      break;

    // --- Custom events (typed variants) ---
    case "subagent_start":
    case "subagent_stop":
      // event.agent (AgentIdentity), event.taskCallId
      break;
    case "todo_update":
      // event.todos (TodoItem[])
      break;
    case "title_generated":
      // event.title
      break;
    case "context_warning":
      // event.severity, event.utilizationPct, event.remainingTokens, ...
      break;
    case "memory_recall":
    case "memory_update":
      // Backend memory subsystem surfaced to the UI
      break;
    case "tool_approval_requested":
      // Respond via client.submitToolApproval(...)
      break;
    case "asset_created":
    case "attachment_staged":
    case "workspace_ready":
    case "desktop_stream":
      // Workspace / asset pipeline events
      break;
    case "state_changed":
      // event.state ("queued" | "running" | "waiting_for_tool" | ...)
      break;
    case "custom":
      // Unknown custom event — forward-compat passthrough
      break;

    // --- Transport / errors ---
    case "retry":
    case "stall":
    case "keepalive":
      break;
    case "error":
      // event.code, event.message, event.blockPath
      break;
    case "disconnected":
      break;
  }
});

// Later: unsubscribe()

客户端工具

LLM可以引用的注册工具. 工具名称 必须 开始 mcp_ 所以后端会把它们传送到客户端执行.

session.toolRegistry.registerTool(
  "mcp_get_current_time",
  "Get the current date and time",
  {
    type: "object",
    properties: {
      timezone: {
        type: "string",
        description: "IANA timezone (e.g. America/New_York)",
      },
    },
  },
  async (args) => {
    const tz = (args.timezone as string) || "UTC";
    return new Date().toLocaleString("en-US", { timeZone: tz });
  },
);

await session.send("What time is it in Tokyo?");
// The LLM calls mcp_get_current_time → SDK executes it → result sent back → LLM responds

工具执行流程被自动处理 :

  1. LLM 请求客户端工具( 有线 : block_start(kind="tool_use"),然后流出 block_delta(channel="input")(中文(简体) )
  2. 后端信号准备运行 block_stop(status="awaiting_client_result") —— 说 final.call_id, (中文(简体) ), final.tool_name,以及 final.input 带解析参数
  3. SDK 本地引用注册处理器
  4. SDK将结果张贴到 /v1/tool-result
  5. SDK 为 LLM 的最终响应继续SSE流

观察者可以通过观看来显示“运行...” UIs block_start(kind="tool_use") 财务报告和审定财务报表 block_stop(status="awaiting_client_result") 直接说.

核准工具

当一个工具需要用户批准时,后端会发出 a tool_approval_requested 事件,而不是开始 awaiting_client_result。响应 client.submitToolApproval(...)数字 :

session.on(async (event) => {
  if (event.type === "tool_approval_requested") {
    const ok = confirm(`Allow ${event.toolName}? (${event.reason ?? ""})`);
    await session.client.submitToolApproval({
      job_id: session.currentJobId ?? "",
      call_id: event.callId,
      decision: ok ? "allow" : "deny",
      scope: "once", // "once" | "conversation" | "always"
    });
  }
});

UI 协议

当后端使丰富的UI表面(今天的A2UI,将来的其他协议),工具输出会以MCP风格的嵌入资源包裹而来. 按 MIME 类型键登记一个框架特定渲染器 :

import { ChatSession, parseEmbeddedResource } from "@astralform/js";

await session.connect();

// Gate registration on the agent's configured protocol.
if (session.agentStatus?.uiComponents.enabled) {
  session.protocols.register({
    mimeType: session.agentStatus.uiComponents.mimeType!,
    render: (payload) => {
      /* framework-specific render */
    },
  });
}

// Inside your tool-result block handler:
session.on((event) => {
  if (event.type === "block_stop" && event.final) {
    const resource = parseEmbeddedResource(event.final.output);
    if (resource) {
      const adapter = session.protocols.get(resource.mimeType);
      adapter?.render(resource.payload);
    }
  }
});

SDK从未进口出一个渲染器——适配器是消费者在读取上收缩的不透明的手柄. 适配器被丢弃 disconnect()。 。 。 .

多代理人

向特定代理发送消息 :

await session.connect();

// List available agents
console.log(session.agents);

// Send to a specific agent
await session.send("Help me debug this", { agentName: "debugger" });

对话管理

// Create a new conversation
const id = await session.createNewConversation();

// Switch to an existing conversation (loads messages from backend)
await session.switchConversation("conversation-id");

// Delete a conversation. Rejects if the server refused it — a 404 counts as
// already deleted and resolves, anything else leaves the conversation in place.
try {
  await session.deleteConversation("conversation-id");
} catch (err) {
  // Still there: tell the user rather than removing it from your own UI.
}

// Edit and resend from a checkpoint
await session.resendFromCheckpoint("message-id", "Updated message");

// Access state
session.conversationId;   // Current conversation ID
session.conversations;    // All conversations
session.messages;         // Messages in current conversation

启用客户端工具

客户工具通过 session.toolRegistry.registerTool(...) 仅在会话启用集中其名称时运行 :

// Enable / disable a registered client tool
session.toggleClientTool("mcp_get_current_time"); // returns true if now enabled

// Inspect the enabled set
session.enabledClientTools; // Set<string>

平台层面的功能(如计划模式)通过 send 选项。 Web搜索不需要选项:当代理搜索功能启用服务器侧时,代理决定每个任务是否搜索.

await session.send("Research the latest on WebGPU", {
  planMode: true,
});

低级客户端

对于没有会话状态管理的直接 API 访问 :

import { AstralformClient } from "@astralform/js";

const client = new AstralformClient({
  apiKey: "your-api-key",
  userId: "user-123",
});

// REST endpoints
const status = await client.getAgentStatus();
const conversations = await client.getConversations();
const messages = await client.getMessages("conversation-id");
const agents = await client.getAgents();
const skills = await client.getSkills();
const commands = await client.listSkillCommands();

// Job-based streaming
const job = await client.createJob({
  message: "Hello",
  provider: "anthropic", // a new conversation names its model
  model: "claude-sonnet-5",
});
for await (const event of client.streamJobEvents(job.job_id)) {
  const data = JSON.parse(event.data);
  if (data.type === "block_delta" && data.delta.channel === "text") {
    process.stdout.write(data.delta.text);
  }
}

自定义存储

SDK默认使用内存存储. 执行 ChatStorage 持久性:

import { ChatSession, type ChatStorage } from "@astralform/js";

const myStorage: ChatStorage = {
  fetchConversations: async () => { /* ... */ },
  fetchConversation: async (id) => { /* ... */ },
  createConversation: async (id, title) => { /* ... */ },
  updateConversationTitle: async (id, title) => { /* ... */ },
  deleteConversation: async (id) => { /* ... */ },
  fetchMessages: async (conversationId) => { /* ... */ },
  addMessage: async (message, conversationId) => { /* ... */ },
  updateMessageStatus: async (id, status) => { /* ... */ },
  deleteMessage: async (id) => { /* ... */ },
};

const session = new ChatSession(config, myStorage);

处理错误

HTTP 呼叫 (connect, (中文(简体) ), submitToolApproval, (中文(简体) ), createJob,.) 抛出打出的错误:

import {
  AuthenticationError, // 401 — invalid API key
  RateLimitError, // 429 — rate limit exceeded
  LLMNotConfiguredError, // LLM provider not set up
  ServerError, // 5xx or unexpected errors — carries `status` when it came from a response
  ConnectionError, // Network failures
  StreamAbortedError, // Stream cancelled via disconnect()
} from "@astralform/js";

try {
  await session.connect();
} catch (err) {
  if (err instanceof AuthenticationError) {
    // Redirect to login
  }
}

以打字方式从 SSE 流火中到达的错误 error 结构形状的事件——无 Error 实例被包裹 :

session.on((event) => {
  if (event.type === "error") {
    // event.code, event.message, event.blockPath
    if (event.code === "rate_limit_exceeded") {
      // Show backoff UI
    }
  }
});

框架整合

SDK是无头的——它与任何UI框架合作. 以下是反应的例子:

import { ChatSession, type ChatEvent } from "@astralform/js";
import { useEffect, useRef, useState } from "react";

function useChat(apiKey: string, userId: string) {
  const sessionRef = useRef<ChatSession>();
  const [messages, setMessages] = useState<string[]>([]);
  const [streaming, setStreaming] = useState("");

  useEffect(() => {
    const session = new ChatSession({ apiKey, userId });
    sessionRef.current = session;

    session.on((event: ChatEvent) => {
      switch (event.type) {
        case "block_delta":
          if (event.delta.channel === "text") {
            setStreaming((s) => s + event.delta.text);
          }
          break;
        case "message_stop":
          setStreaming((s) => {
            setMessages((m) => [...m, s]);
            return "";
          });
          break;
      }
    });

    session.connect();
    return () => session.disconnect();
  }, [apiKey, userId]);

  const send = (text: string) => sessionRef.current?.send(text);

  return { messages, streaming, send };
}

发展

npm install        # Install dependencies
npm run build      # Build ESM + CJS + types
npm test           # Run tests
npm run typecheck  # Type check

许可证

麻省理工学院

版本与发布记录

@astralform/js 共发布 66 个版本,首次发布于 2026/03/17;过去 12 个月发布了 66 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
9.13.0latest2026/10/2513 天前
9.12.02026/10/2513 天前
9.11.02026/10/2414 天前
9.10.02026/10/2315 天前
9.9.02026/10/2018 天前
9.8.32026/10/1820 天前

关于 @astralform/js 的常见问题

@astralform/js 是什么?

Astralform JS SDK - 在WebMCP支持下的AI代理管弦乐

如何安装 @astralform/js?

在项目目录运行 npm install @astralform/js,或使用 pnpm add / yarn add / bun add。

@astralform/js 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@astralform/js 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@astralform/js 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@astralform/js 可以商用吗?

@astralform/js 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@astralform/js 的下载量有多少?

根据 npm 官方下载统计,@astralform/js 在 2026/10/07 – 2026/11/05 这 30 天内被下载 2,220 次,日均约 74 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@astralform/js 还在维护吗?

最新版本 9.13.0 发布于 2026/10/25(13 天前);至今共发布 66 个版本,其中过去 12 个月发布了 66 个;首次发布于 2026/03/17。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。