@constal/sdk

中文文档
6.11.0发布于 2026/10/31Apache-2.0TypeScriptES Module零依赖近 30 天下载 3757

Public TypeScript SDK for authoring Constal agents, integrations, policies, channels, and credential providers

原始使用文档
文档目录7 节
  1. Public surface
  2. Example Connection
  3. Accepted Resource contracts
  4. Skills
  5. Ledger addresses and delivery
  6. Session schedules
  7. Model evaluation

@constal/sdk

The public TypeScript SDK for building on Constal.

npm install @constal/sdk

The package has one supported import path:

import { agent, gateway, credentialProvider } from "@constal/sdk";

Deep imports are not part of the API.

Public surface

The root export contains the contracts needed to author:

  • Agents, subtasks, Tools, durable state, waits, stages, and ledger views
  • Channels and Auth Providers
  • executable Policies and governance constraints
  • Connection packages through gateway(...), including model, MCP, and Training Provider connections
  • Credential Provider packages through credentialProvider(...)
  • Evals, Scorers, training recipes, memory operations, and sandbox handles
  • CRNs, selectors, Resource requirements, schemas, hashes, and recovery-aware errors

Connection code receives GatewayContext/IntegrationContext: declared Credentials, bounded egress, package state, immutable artifact storage, and audit emission. Credential Provider code receives the same bounded capabilities through CredentialProviderContext.

gateway(...) and credentialProvider(...) return validated packages suitable for managed deployment. Users implement the package contract; Constal supplies its execution environment.

Example Connection

import { gateway } from "@constal/sdk";

export default gateway({
  id: "ticket-connection",
  version: "1.0.0",
  kind: "service",
  displayName: "Ticket connection",
  description: "Read tickets from the configured account.",
  credentialSlots: { account: { title: "Account Credential" } },
  capabilities: [{
    id: "example.ticket-read",
    version: "1",
    hash: "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
  }],
  billing: { upstreamPayer: "tenant" },
  configSchema: { type: "object", additionalProperties: false },
  egress: { rules: [{
    operations: ["ticket.get"],
    origin: "https://api.example.com",
    pathPrefix: "/tickets/",
    methods: ["GET"],
  }] },
  catalog: () => [{
    op: "ticket.get",
    description: "Read one ticket.",
    schema: { type: "object" },
    resultSchema: { type: "object" },
    effect: "read-only",
    recovery: { kind: "repeat" },
    async: false,
    usage: { meter: "none", priceTable: null },
  }],
  async invoke(request, context) {
    const credential = await context.secret("account");
    return context.egress({
      url: `https://api.example.com/tickets/${encodeURIComponent(String((request.args as { id: string }).id))}`,
      method: "GET",
      headers: { authorization: `Bearer ${credential.value}` },
      maximumResponseBytes: 1_048_576,
      maximumRedirects: 0,
    });
  },
});

Accepted Resource contracts

const bindings = Object.keys(ctx.resources);
const model = await ctx.describeResource("model");
const operations = (await ctx.describeResource("api"))?.operations;

The accessor records a local-step observation of one accepted Resource binding. It requires no Resource or provider lookup. Replaying that position returns the same observation; a later call can observe a safe-point binding update.

Results include public operation contracts, credential slot names, and advertised metadata for pinned Models. They omit credential references/material, private configuration, and control-plane operations. Returned objects are independent copies. A configured operation is not a promise of authorization: Policy still applies when it is invoked.

For model-selected operations, add the optional resourceTools to the Agent's ordinary Tool catalog:

import { resourceTools } from "@constal/sdk";

export const tools = { ...resourceTools };

resource_list returns accepted binding names and CRNs without loading every catalog. resource_describe takes { binding } and returns that Resource's public operations. resource_invoke accepts the described operation and its arguments:

await resourceTools.resource_invoke.run({
  binding: "tickets",
  operation: "tool.find",
  arguments: { query: "Open support requests" },
}, ctx);

These Tools use the normal invocation, authorization, and asynchronous Handle contracts. Results and errors are preserved. They do not install packages, create Resources, change bindings, acquire credentials themselves, or retry failed operations. Use the Constal API Resource for installation and rebind; an accepted rebind receipt is not proof that the current frame has the new pin.

Skills

A skills Resource is one library of Agent Skills, installed by deploying an archive that holds constal.skills.json. skills(binding, sandbox) returns three Tools over the library the Agent binds as binding (default skills), named from the binding with . and - as _:

import { agent, skills } from "@constal/sdk";

const library = skills("skills", "sandbox");

export default agent({
  id: "maintainer", version: "1.0.0", model: "model",
  tools: library,
  async onMessage(request, ctx) {
    return ctx.turn({ system: "You maintain the project's software.", context: { request }, tools: Object.keys(library) });
  },
});

skills_find and skills_read are the library's find and read operations; their schemas and descriptions come from its catalog at deployment. skills_run bundles the skill, copies its folder once per bundle into the workspace of the sandbox pool bound as sandbox (default sandbox), at /workspace/skills/<ref>, and runs one command there; it answers the folder's path and the command's result. It needs the library's bundle and the pool's exec and putFile, under the pool's own Policy. Call skills() once per bound library.

Ledger addresses and delivery

A ledger address names a Session's main ledger, agent:<agent>/session:<session>, or a spawned loop's, agent:<agent>/session:<session>/spawn:<handle>, within the Run's tenant, namespace, and Customer. ctx.run.ledger is the current Run's own address (null for an operator branch, an eval fork, a compensation, or a loop one of them spawned, and for a Run of an existing Session whose id has a / or a control character or is longer than 256 characters, which ingress no longer accepts for a new one), and the Handle ctx.spawn returns names its loop in ledger, the same across the spawn's retries. A Run omits of and to for its own ledger; session:<id> is refused.

const notes = await ctx.ledger.view("notes", { of: "agent:memory/session:team" });
await ctx.commit({ text: "Fix the failing build", data: { issue: 7 } },
  { to: "agent:task/session:fix-7", deliver: "live", delegate: true });
const investigation = ctx.spawn(investigate, { issue: 7 });
await ctx.commit("Also check the flaky test", { to: investigation.ledger, deliver: "live" });
const woken = ctx.await("next", { afterRun: "delivery", timeout: 3_600_000 });

ctx.ledger.view reads another ledger at its head. ctx.commit with to is the one way agent code delivers to another ledger: deliver: "queue", the default, queues the item; "live" steers the addressed Run or starts one. A live item is a non-empty string or { text, data? }, and a delivery to a spawn is live. delegate: true makes this Session the owner of the receiving Session, which takes its owner only before its first Run. A delegation to a Session whose live Run's cancel awaits its Driver waits behind that Run and starts the next one; any other live delivery steers that Run. The platform then delivers to the owner, as a RunReport, each end of that Session's root Runs (complete with its result, failed, stopped) but one a stop of the owner's ledger caused, each hold of one at its turn or spend limit (held), and each refused or dropped start of one whose event did not come from the owner's Session (refused). A delivery refused when it reaches its Session, or failing there for good, is reported to its sender as a RunReport with status refused. ctx.await(label, { afterRun: "delivery" }) settles when a delivery steers the waiting Run and resolves to { seq, eventId } of the steer that woke it; the delivery itself stays in the ledger. A Run that a delivery or a report starts belongs to its sender's end-to-end run, as does a Run started through the Constal API from a Run: performance reads a Channel's, a schedule's or an API call's Run and every Run it leads to together.

Policy decides a view of another Session (ledger:read; the pages its cursor then reads need no further check) and a delivery to another Session at its commit (agent:invoke on the receiving Agent, session:steer when live, run:cancel when delegating). A refused view or delivery throws its refusal, which is journaled, so a replay throws the same error at the same position: a Policy denial is PolicyDecisionFailed with its decisionHash, and any other refusal an Error with the platform's message and status.

Session schedules

Schedule a future invocation of the current Agent and Session:

await ctx.schedule("check-pr-42", {
  after: "10m",
  input: { kind: "check-pr", repository: "acme/api", pullRequest: 42 },
});

The current Run can finish. The Session retains the schedule and delivers the unchanged input to a new Run when due. Use every: "15m" for a fixed cadence, or every: "day", at: "09:00", timezone: "America/Los_Angeles" for a daily check. Recurring schedules preserve missed occurrences by default; explicitly request missed: "coalesce" when one check should cover an accumulated backlog.

ctx.listSchedules() pages the owning Session's schedules; calling schedule() again with the same name replaces future work. ctx.cancelSchedule(name) stops future work and identifies accepted or unresolved deliveries. Use ctx.await() for sleeping within the current Run.

See Schedule Agent follow-ups for replay, delivery, authority, and cancellation semantics.

Model evaluation

ctx.evaluate judges one state against typed questions through an evaluation Model such as typesafe-ai/jev. Every question is answered independently with constrained values and probabilities, never generated prose, and the call is journaled and priced like a model call without consuming a turn.

import { agent, noulDecision } from "@constal/sdk";

export default agent({
  id: "triage", version: "1.0.0", model: "model",
  async onMessage(ticket, ctx) {
    const { answers } = await ctx.evaluate({
      model: "judge",
      state: { ticket },
      questions: {
        team: { type: "choice", instructions: "Which team owns `ticket`?",
          criteria: { billing: "Charges, invoices, refunds", technical: { what: "Bugs and outages", not_for: "Charges" } } },
        severity: { type: "score", instructions: "How severe is the problem in `ticket`?",
          criteria: ["Cosmetic", "Degraded", "Customer is blocked"] },
        urgent: { type: "noul", instructions: "Does `ticket` convey urgency?" },
      },
    });
    if (answers.team.confidence < 0.5) return { route: "human" };
    return { route: answers.team.choice, severity: answers.severity.score, urgent: noulDecision(answers.urgent, 0.7) };
  },
});

Bind the evaluation Model like any other ("judge": "crn:...:model/jev"), or pass gateway with an explicit model identifier. instructions, Choice option descriptions, Score levels, and Noul criteria all accept JSON structure. Choice answers keep the literal option keys of their criteria, Score answers carry a legend, and Noul answers are a probability.

See Evaluate state with a Model for a complete package manifest, answer shapes, and verification steps.

版本与发布记录

@constal/sdk 共发布 42 个版本,首次发布于 2026/08/25;过去 12 个月发布了 42 个版本。

版本发布日期距今
6.11.0latest2026/10/3110 天前
6.10.02026/10/3110 天前
6.9.12026/10/2714 天前
6.9.02026/10/2714 天前
6.8.02026/10/2615 天前
6.7.02026/10/2516 天前

关于 @constal/sdk 的常见问题

@constal/sdk 是什么?

Public TypeScript SDK for authoring Constal agents, integrations, policies, channels, and credential providers

如何安装 @constal/sdk?

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

@constal/sdk 支持 TypeScript 吗?

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

@constal/sdk 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@constal/sdk 可以商用吗?

@constal/sdk 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@constal/sdk 的下载量有多少?

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

@constal/sdk 还在维护吗?

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