@alma-harness/core

中文文档
0.41.0发布于 2026/11/04Apache-2.0TypeScriptES Module零依赖近 30 天下载 3447

Alma core contracts: agentic loop, neutral messages, scoped tools, budget, routing policy, tenancy/audit, lifecycle events, routines.

原始使用文档
文档目录8 节
  1. What it owns
  2. What it must never do
  3. Testing helpers
  4. Cost settlement foundation
  5. Single-dispatch evidence
  6. Durable execution journal
  7. Documentation
  8. Session labels

@alma-harness/core

The contracts and the security core of Alma — a TypeScript runtime for multi-tenant agents operating on sensitive data. The engine that runs against them ships as @alma-harness/loop.

Status: pre-1.0. The API is still moving; see the roadmap for where it stands.

What it owns

The vocabulary every other package speaks, and the rules the engine may not bend. What a turn is allowed to do is defined here; the engine that enforces it lives in @alma-harness/loop.

  • Messages (Msg, Block) — the neutral format every provider is translated to and from. A text block with origin: "harness" was appended by the harness (the per-turn volatile suffix, spec: volatile-per-turn), not typed by the person; "human" and "system" mark a recorded fact the person saw from the product's side, a professional's message or an automatic notice (spec: conversation-events). MsgMeta.event is the host's fact id and its kind (input, message or undelivered), unique in a session's log, and MsgMeta.turn the id of the turn that recorded a message. A media block travels by reference: with a MediaSource configured the loop asks the product for the bytes and they go to the provider as base64, the session keeping the ref alone (spec: media-by-bytes); without one the reference is handed to the provider to fetch — egress either way, logged by the loop by kind and size, never by URI (spec: what-the-wire-cuts). The harness itself never fetches it.
  • Tenancy (Scope, scopePath) — {org, uid} bound by closure. The model never sees or chooses it; deriving it from a verified identity is the host application's job.
  • Lifecycle events (LifecycleHooks, Interceptor) — the closed set of typed seams an extension may attach to, capability-narrowing only.
  • Tools (defineTool) — capability by registration. What the model may call is derived from the registry per step, never a list it can extend. Outputs are bounded at registration (maxOutputChars, a default applies): a result enters the transcript once and is re-sent forever, and removing it later costs more than it saves. A tool declared deferred stays out of the prefix until the engine's built-in search loads it for a turn (spec: deferred-tools). A provider-executed tool (ProviderToolSpec, today web_search) is declared on the request, runs on the provider's side, and comes back as two neutral blocks — provider_tool_call and provider_tool_result, provider-tagged, the provider's payload opaque for replay — under a maxSensitivity ceiling, internal by default (spec: provider-tools).
  • Routing (ModelPolicy, Sensitivity, Tier) — complexity × sensitivity, enforced at dispatch and never escalating through a delegate. The policy also says how hard the model may think (ModelChoice.reasoning, spec: reasoning-blocks); the reasoning it produces is a Block that stays backstage — in the session, never in the reply.
  • Budget (BudgetCaps, priceUsage, SpendStore) — dollar caps, fail-closed pricing: an unpriced model never spends, and neither does an unpriced service tier or an unpriced web search (webSearchUsdPerRequest); the table carries tiers and context bands, and pricing follows the tier the wire says served (spec: pricing-tiers). perTurnUsd numbers block, while { usd, onExceeded: "warn" } continues and reports the crossing; session and tenant-day caps are accounted through the SpendStore seam with per-cap policy — warn by default (the turn continues, the crossing is reported), block opt-in.
  • Audit (AuditLog) — access, routing, cost, recall and context trails. Where they are written is swappable; that they are written is not.
  • The rotation marker (MsgMeta.rotation) — a summary message that stands for earlier ones (spec: long-context). The log is never rewritten; the engine's view starts at the marker.
  • Why a turn failed (TurnFailure, FailureKind, ProviderError) — one closed vocabulary across every provider and seam, with the one verdict a product acts on: retryable, whether the same request unchanged may succeed later (spec: error-taxonomy). Adapters throw ProviderError; the engine classifies failures internally. A retryable provider diagnosis does not authorize replay of an admitted canonical operation.
  • A fenced append (AppendOpts, spec: fenced-event-append): a store shared across processes writes nothing unless the given admission of the session is active, checked and held in the append's transaction. In memory it is ignored: the reference writes synchronously, in the tick the runner checks the deadline.
  • Conversation ownership lives in execution's durable operation admission. TurnStore and its expiring lease/claim types are removed. Canonical conversation replay binds input and configuration; uncertainty never grants automatic retry.
  • Request controls (ModelRequest, spec: provider-request-controls) — native JSON schema output and explicit Anthropic cache controls. These are adapter inputs, not validated results or a governed single-call runner. cache: false disables breakpoints; conversationTail: false keeps only the stable system prefix. Other adapters reject explicit cache controls. Output schema changes from step:pre are refused and logged, including attempts to add one.
  • Model jobs (ModelJobClient) — one-call work answered by a provider's batch API (spec: model-jobs). Where a job runs is swappable; that it is scoped, routed, accounted and on the trail is the runner's, in @alma-harness/loop.
  • Routines (Routine, TriggerSource, RoutineStore, OutputSink, RoutineRunStore) — a routine is data: a goal, an intent, a schedule, a profile or none, its own per-run cap, a named destination (§8, spec: routine-runner). Where the schedule fires from, where a result goes and where a run is recorded are seams; claiming a run before executing it, capping it and recording it is the runner's, in @alma-harness/schedule. RoutineRunStore.claim is mandatory and atomic: insert running only if absent, or transition submitted to running for collection. Interrupted claims need reconciliation; see the recovery notes.
  • Memory contracts (EpisodeStore, ProfileStore, MemoryErasure, …) — the interfaces live here because memory integrates with tenancy, audit and routing; the implementations live in @alma-harness/memory.

What it must never do

  • Reach the filesystem, spawn a subprocess, or ship a shell. Ever.
  • Accept a scope from the model, or let a hook rewrite routing, budget or the advertised tool set.
  • Depend on a provider SDK. The neutral message format is the boundary.

Testing helpers

@alma-harness/core/testing ships reference implementations of the contracts this package publishes — a recording AuditLog, and the in-memory SessionStore, SpendStore, CostSettlementStore and RoutineRunStore. Nothing in it imports vitest. The shared storage contract suites every adapter must pass live in @alma-harness/testing; the in-memory memory stores in @alma-harness/memory/testing; the scripted ModelClient that drives the engine in @alma-harness/loop/testing.

import { InMemorySessionStore, RecordingAuditLog } from "@alma-harness/core/testing";

Financial warnings use BudgetCapName (perTurnUsd plus the unchanged PersistentCapName). normalizePerTurnCap validates and copies explicit policy. Routine run/delivery metadata carries capsCrossed; empty run arrays normalize to absent in both reference stores. See spec: financial-warning-policy.

Cost settlement foundation

CostSettlementStore records known priced usage with a host-issued scoped ID. settle returns an applied/replayed receipt; changing its normalized input is a SettlementConflictError. The executor must persist identity before dispatch and retain usage for reconciliation. This store never dispatches a provider. Replayed totals are historical; current budget checks use SpendStore.peek.

normalizeCostSettlement rejects unknown fields and snapshots the closed record before I/O. IDs are 1–200 non-space printable ASCII characters; scope also obeys scopePath. Timestamps require a zone and at most millisecond precision. Optional usage zeros remain distinct from absence. serviceTier is the resolved priced tier; capsCrossed preserves precomputed evidence, without evaluating policy. Consumers (up to 16) are an explicit list, which may be empty.

Scoped get/list read receipts; list defaults to 50 (maximum 100) and filters by operation, model, priced tier and inclusive occurrence interval. Its ascending (at, id) cursor is observational, not a reliable projection offset. pending returns full unacknowledged receipts for a consumer; acknowledge after applying idempotently. Replays never recreate acknowledged obligations. InMemoryCostSettlementStore is volatile and owns separate counters. The Postgres implementation shares existing durable spend counters.

Do not account for one call through both this seam and legacy audit/spend writes. Governed runners use transactional settlement; legacy runners retain their existing writers. Select one accounting path per call. See idempotent-cost-settlement.

Single-dispatch evidence

SingleDispatchModelClient is an opt-in trusted host capability (spec: provider-dispatch-evidence). validate checks its request support without I/O; streamEvidence is single-consumption and reports a bounded request reference and final UsageEvidence separately from content/stop events. A fresh method call is a fresh dispatch: the executor must claim it durably first.

Evidence distinguishes known, unpriced (counts known, attribution unresolved), unknown and pre-aborted not_dispatched. Only known may be priced. The new OpenAI evidence normalizes ordinary input excluding both cache reads and writes, which have separate price terms. Existing legacy usage mappings are unchanged. Drain the stream: early return closes transport but may leave billing unresolved. A provider error's retryable flag is not authorization to redispatch the same operation. No durable runner adopts this capability yet.

Durable execution journal

ExecutionStore (spec: durable-execution-records) keeps a content-free claim, usage evidence and the original pricing snapshot. InMemoryExecutionStore from @alma-harness/core/testing implements the same contract as PostgreSQL. Only the first claim/recovery winner receives a fence; reads and exact retries never do. dispatch must commit before provider egress and cannot replay. receive retains known/unpriced/unknown distinctions. list exposes unresolved scoped metadata; reconcileExpired fences expired prepared/dispatching work, never resends it.

A not_dispatched record carries notDispatchedCause when its transition saw why (spec: not-dispatched-cause): aborted, audit, store, input (the host's loader threw), invalid (refused as configured or loaded) or expired (the sweep closed it). A cause is an observation, not a verdict: only invalid supports suppressing an unchanged retry, and absent or expired means unknown. cancelPrepared(fence, cause?) records the owner's cause once; it refuses expired, which only the sweep records.

Claim retries must reuse the full original input, including prices and timestamps. Recovery leases fence journal writes only; idempotent settlement still requires CostSettlementStore with the original settlementId. This journal neither calls providers nor settles bills, stores responses or marks results complete. Before runner adoption, scoped result storage/erasure and a durable inbox for authoritative usage arriving after fencing are mandatory. Set dispatch deadlines beyond the provider timeout plus a drain grace period; preserve rejected late evidence there.

ExecutionResultStore (spec 081) reserves scoped call/session identities before context reads and stores a closed response envelope under explicit sensitivity, retention and character caps. Erased/expired identities cannot be revived; host cleanup scheduling and audited erasure composition are required. Contracts and content-free errors live here; implementations live in memory and PostgreSQL.

Documentation

Docs index · Architecture · Invariants · Quickstart

UsageInbox preserves scoped usage observations; helpers/reference live in @alma-harness/execution, SQL in postgres. It grants no dispatch or billing authority.

Apache-2.0

Governed journal inputs optionally snapshot governance (caps, consumers, result retention). executionSettlementRequest(record) reconstructs the original request. After governed accounting, settle(fence, {request, receipt}) and complete persist fenced lifecycle transitions; never pass the full governed object with decisions. Recovery may finish settled records, never redispatch; completion is not delivery. Result content/erasure remain separate. New readers precede governed writers. normalizeBudgetCaps, normalizeSpendTotals, normalizeCostReceipt and costReceiptWarnings share closed financial rules across journal and accounting. See governed execution lifecycle.

RoutingEvent and AccessEvent optionally carry operationId/attemptId/callId for governed single calls; turnId retains its original conversation meaning.

ModelRequest.temperature is optional; adapters validate support before egress (spec: explicit-temperature-controls). Execution controls persist finite 0..2 values and normalize negative zero. Absence preserves provider defaults.

tryDelegate returns a DelegateAttempt; forwarding refusals is explicit (spec: governed-delegation-refusal).

Known evidence may attest zero-use Anthropic context rejection (spec: safe-context-rejection-rotation). Ordinary zero usage cannot authorize retry. RejectionProof adds three pre-stream refusals, anthropic_rate_limit_v1, anthropic_overloaded_v1 and openai_rate_limit_v1, each bound to its provider in REJECTION_PROVIDERS, with zero counts and never at the batch tier (spec: pre-stream-rejections).

ModelRequest.toolChoice is optional host-owned generation policy: { type: "auto" | "none" | "required" } or { type: "tool", name: "capture" }. normalizeToolChoice validates and copies its closed shape; validateToolChoice checks the actual advertised ToolSpec[], rejecting missing or ambiguous names. Adapters additionally enforce their documented model/control support. Absence preserves existing behavior; explicit auto stays distinct from absence. Execution controls persist the choice, so changing it under an existing identity conflicts. Selection never authorizes tool execution or personal-memory writes. See governed tool choice.

Session labels

SessionLabelStore (spec: session-labels) keeps a host's attribution for a session — opaque identifiers and flags that map a pseudonymous scope/session back to the host's own records — apart from immutable receipts, so erasure can sever it. normalizeSessionLabels accepts 1–8 entries, keys ^[a-z][a-z0-9_]{0,31}$, values ^[A-Za-z0-9_.:-]{1,128}$: syntactic only, never personal data values. put stores once (equal replay returns the record, different labels throw SessionLabelConflictError); erase(scope, sessionId) tombstones, erase(scope) seals the scope, and either makes later put throw SessionLabelErasedError. Writes and erasure serialize per scope. Labels never drive routing, access, pricing, audit or scope. InMemorySessionLabelStore is in ./testing.

版本与发布记录

@alma-harness/core 共发布 52 个版本,首次发布于 2026/09/01;过去 12 个月发布了 52 个版本。

版本发布日期距今
0.41.0latest2026/11/043 天前
0.40.02026/10/308 天前
0.39.22026/10/309 天前
0.39.12026/10/299 天前
0.39.02026/10/299 天前
0.38.02026/10/2910 天前

关于 @alma-harness/core 的常见问题

@alma-harness/core 是什么?

Alma core contracts: agentic loop, neutral messages, scoped tools, budget, routing policy, tenancy/audit, lifecycle events, routines.

如何安装 @alma-harness/core?

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

@alma-harness/core 支持 TypeScript 吗?

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

@alma-harness/core 支持 ESM 还是 CommonJS?

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

@alma-harness/core 需要什么版本的 Node.js?

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

@alma-harness/core 可以商用吗?

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

@alma-harness/core 的下载量有多少?

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

@alma-harness/core 还在维护吗?

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