@glyphh-ai/glyphh

中文文档
3.6.0发布于 2026/11/01MITTypeScriptES Module零依赖构建来源已验证

Ada for TypeScript and JavaScript: typed decisions from your own precedents, from Ada Cloud (https://ada.glyphh.ai) with an API key; and a client for your local rrotor, the runtime that hosts your programs and CLIs.

原始使用文档
文档目录5 节
  1. From nothing to a decision
  2. Typed outputs and unions
  3. Ada versions
  4. Your local runtime: programs and CLIs
  5. License

@glyphh-ai/glyphh

Ada for TypeScript and JavaScript: your models in Ada Cloud, https://ada.glyphh.ai, called with an API key. Node 18 or later (or any runtime with fetch), no dependencies.

npm install @glyphh-ai/glyphh
import { Ada, LimitError } from "@glyphh-ai/glyphh";

const ada = new Ada();          // GLYPHH_API_KEY from the environment; or new Ada({ key: "glyphh_ak_..." })

const d = await ada.decide("tickets", { ticket: { what: { plan: "pro", text: "charged twice" } } });
if (d.ask) await sendToAPerson(d);   // a person should decide: d.outcome is the best guess, for them to see
else act(d.outcome);                 // d.probabilities, d.expected_cost, d.receipts: the precedents it stands on

const tickets = ada.model("tickets");
await tickets.record({ ticket: {...} }, "refund");              // a confirmed outcome: the next case learns from it

The key is made on the platform's API Keys page (https://platform.glyphh.ai), or by Claude through the Glyphh connector; it acts in your org, with the clearance it was made with. GLYPHH_ADA_URL moves the address (a local server under test); 3.0's GLYPHH_RUNTIME_URL and GLYPHH_RUNTIME_KEY are still read.

From nothing to a decision

const tickets = await ada.createModel("tickets", spec);       // the spec as JSON: layers, segments, roles, output
const job = await tickets.importFile("history.csv", {          // a path (Node), or the bytes with { format }
  mapping: { Plan: "ticket.what.plan", Result: "outcome" },
  onProgress: (j) => console.log(j.state, j.percent),
});
console.log(job.result.added, job.result.refused, job.result.refusals.slice(0, 3));

const bt = await tickets.backtest([{ settings: { word_evidence: 0 }, name: "no word evidence" }], { holdout: { newest: 200 } });
await tickets.settings({ word_evidence: 0 }, { note: "cost 8% less on the last 200", backtest: bt.id });   // a new version
await tickets.activate(1);                                     // and back
await tickets.export({ to: "tickets.zip" });                   // everything, as a zip: never locked in

Typed outputs and unions

On ada-1-2-beta, a named output's kind says what its answer is: score, noul, number, many or choice (the default), and when makes an output a branch of a union. Record each in its own terms (Outcome); read what its kind adds:

await tickets.recordMany([{ data, outcomes: { kind: "billing", refund: 120, urgent: true, tags: ["web"] } }]);
const d = await ada.decide("tickets", data, { outputs: ["kind", "severity", "refund", "urgent", "tags"] });
d.outputs?.urgent.p;          // a noul's P(true)
d.outputs?.refund.value;      // a number's expected value
d.outputs?.severity.applies;  // false: a bug's severity does not arise for a billing case
d.outputs?.tags.outcome;      // a many output's list

Ada versions

Every model version runs one Ada version, named ada-<major>-<minor> (-beta for a beta): ada-1-0 is the latest stable today, ada-1-1-beta a beta. A model's answers are always computed by its own Ada version; moving to another is your choice, made with a backtest in front of you.

const { latest_stable, versions } = await ada.ada.versions();  // newest first: name, status, dates, notes, your models on it
const tickets = await ada.createModel("tickets", spec);        // the latest stable; { ada: "ada-1-1-beta" } to opt in to a beta
const info = await tickets.info();                             // info.ada, info.ada_status, info.ada_notice; each of versions() has .ada too

const bt = await tickets.backtest([], { ada: "ada-1-1-beta" }); // the active spec, learned and scored on that Ada
console.log(bt.result.active.cost, "->", bt.result.candidates[0].cost);
const moved = await tickets.upgrade({ ada: "ada-1-1-beta", note: "2 fewer wrong on the last 200", backtest: bt.id });
await tickets.activate(info.version);                          // and back, on the Ada it ran before

upgrade() with no ada moves to the latest stable; an older version is a downgrade, allowed while it is kept. A beta is never the default. A deprecated version still answers until its end date, then the model is moved to the latest stable for you (never left unable to answer); while it runs one, or just after it was moved, the client calls new Ada({ onWarning }) (default console.warn, once per client and notice). A name refused is never substituted: AdaVersionUnknownError, AdaVersionRetiredError, AdaVersionDeprecatedError (for a new model or as an upgrade's target), AdaBetaClosedError, AdaVersionUnavailableError, each with ada and latest (the latest stable, the fix).

The SDK speaks contract SUPPORTED_CONTRACT (5) and was made against SUPPORTED_ADA (ada-1-0, ada-1-1-beta); newer names the server lists work as they are. A server that speaks a newer contract is refused, never read half-understood: ClientTooOldError (contract, supported), saying to npm install @glyphh-ai/glyphh@latest.

Adadecide(model, data, { output, outputs, include: ["facts"], clearance }) (POST /v1/decide); models(), model(name), createModel(name, spec, { ada }), deleteModel(name); ada.versions(), ada.latestStable(); job(id), wait(job, { timeout, onProgress }), download(job); usage()
AdaModelinfo() (ada, ada_status, ada_notice), spec(), setSpec(spec, { note, backtest }), settings() and settings(changes, { note, backtest }), versions() (each with ada, kernel), activate(version), upgrade({ ada, note, backtest, wait }), delete(); recordMany(cases) (a thousand a request, in chunks), grade(id, value, { output }), forget(id); decide(data, ...); learn(), learnJob(), calibrate(), backtest(candidates, { ada, holdout, output }); importFile(path or bytes, { format, mapping, partial, learn }), export({ format, to })
Decisionoutcome, probabilities, expected_cost, ask, receipts, facts, outputs, model, version, ada; reads as 3.0's Result too (status, data)
AdaVersionslatest_stable, contract, versions (each name, kernel, status, beta, open, released, deprecated, ends, changes_answers, aliases, summary, notes, models)
Jobid, model, kind, state (awaiting, queued, running, done, failed), percent, result, error, download, upload
Usageplan, month (YYYY-MM, UTC), resets_at (YYYY-MM-DD), models, precedents (used, max), requests (used, max, over, overage: past the month metered on Basic, Pro and Team, refused on Free), requests_per_second; null is by contract, no cap
AdaErrorcode, message, status, detail, retryAfter, traceId; typed by code: AuthError, LimitError (reason, used, max, plan, upgrade, resetsAt), RateError (retryAfter from Retry-After, max; waited out while the retry deadline allows), InvalidError, ModelMissingError, JobMissingError, SpecError, SpecLimitError, SpecReadingError, FewRolesError, MappingError, BusyError, NotReadyError, NoChangeError, IdempotencyError, VersionGoneError, AdaTimeoutError, UnavailableError, JobFailedError (a job that ended failed), AdaVersionError and its kinds (above), ClientTooOldError

Every call that runs long is a job, waited for with its progress unless { wait: false }. Every write carries an Idempotency-Key, so the client sends again what is safe to: a read, or a write, while Ada is unreachable for a moment (E_UNAVAILABLE, E_TIMEOUT, a 502 to 504), and anything refused with 429, with backoff and Retry-After, for up to new Ada({ retryDeadline }) seconds (default 30; 0: never).

SDK 3.0's calls still work: record, query, facts, probabilities, learn and call(fn, inputs) go through POST /v1/runs and answer a Result (run_id, status, data, ...). The cloud answers every run at once, so a decision never waits for input: it says ask.

The API and its answers' shapes are in contract/contract.json.

Your local runtime: programs and CLIs

Runtime is a client for rrotor, the runtime you install on your own machine (a Docker image, bound to 127.0.0.1:7777; the glyphh plugin's rrotor skill installs it from Claude). It is not Ada Cloud: it hosts your programs (a glyphh.json manifest and program.py, sandboxed, with a local secrets vault, cron schedules, a SQLite database and approvals) and your CLIs (a kind of program, "kind": "cli", run one command at a time in the same sandbox). Glyphh only grants it a licence; with none it runs as Community.

import { Runtime } from "@glyphh-ai/glyphh";

const rt = new Runtime();                          // RROTOR_URL or http://127.0.0.1:7777; RROTOR_TOKEN or ~/.glyphh/rrotor/token
await rt.deploy({ path: "./daily_sales" });         // the folder's HEAD commit (Node), or { repo, commit, token? }
let run = await rt.call("daily_sales", "start", { day: "2026-10-30" });
if (run.status === "needs_approval") run = await rt.approve(run.run_id);   // only on the person's yes
await rt.setSecret("github", "GH_TOKEN", token);   // kept encrypted on the runtime; never read back
const out = await rt.runCli("github", ["pr", "list"]);   // out.data.exit_code, stdout, stderr
Runtimeprograms(), program(name), deploy({ path, commit? } | { repo, commit, token? } | { bundle, commit }); call(program, fn, inputs, { version, clearance }), resume(runId, answer), approve(runId, approved?), cancel(runId), run(runId), log(runId), approvals(); secrets(program), setSecret, deleteSecret; schedules(), pauseSchedule, resumeSchedule; clis(), runCli(cli, args, { stdin, files }); licence(), setLicence(key), health()
Run, CliOutput, RunInfo, Program, ProgramVersion, Approval, Secret, Schedule, Cli, LicenceThe API's answers, typed
RrotorErrorAn AdaError too; typed by code: RrotorAuthError, RrotorForbiddenError, LocalOnlyError, ProgramMissingError, FunctionMissingError, RunMissingError, ScheduleMissingError, ManifestError, DeployError, InputsError, RunStateError, NotProgramError, NotCliError, CliDeniedError, NotHeadError, LicenceError, NotInPlanError; E_UNREACHABLE when rrotor is not running

Reads are sent again while rrotor is unreachable for a moment; a write never is. A self-hosted rrotor is new Runtime({ url: "https://rrotor.example.com", token: "<a runtime key>" }). Its shapes are in contract/runtime.json, apart from Ada's contract.

License

MIT (LICENSE).

版本与发布记录

@glyphh-ai/glyphh 共发布 8 个版本,首次发布于 2026/10/19;过去 12 个月发布了 8 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
3.6.0latest2026/11/012 天前
3.5.02026/10/303 天前
3.4.02026/10/294 天前
3.3.02026/10/2310 天前
3.2.02026/10/2311 天前
3.1.02026/10/2014 天前

关于 @glyphh-ai/glyphh 的常见问题

@glyphh-ai/glyphh 是什么?

用于TypeScript和JavaScript的Ada:从您自己的先例中键入决策,来自Ada Cloud (https://ada.glyphh.ai),并附有API密钥;以及本地rrotor的客户端,即托管程序和cli的运行时。

如何安装 @glyphh-ai/glyphh?

在项目目录运行 npm install @glyphh-ai/glyphh,或使用 pnpm add / yarn add / bun add。

@glyphh-ai/glyphh 支持 TypeScript 吗?

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

@glyphh-ai/glyphh 支持 ESM 还是 CommonJS?

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

@glyphh-ai/glyphh 需要什么版本的 Node.js?

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

@glyphh-ai/glyphh 可以商用吗?

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

@glyphh-ai/glyphh 还在维护吗?

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