@alma-harness/single-call

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

Governed one-dispatch model execution for Alma.

原始使用文档
文档目录3 节
  1. Governed model steps
  2. Explicit tool selection
  3. Structured single calls

@alma-harness/single-call

Governed one-dispatch model calls. The text/JSON runner is tool-free; the structured step runner preserves native tool calls. Use matching core, execution, providers, memory and PostgreSQL packages. Node >=22. This engine is separate from the legacy conversation and batch runners.

createSingleCallRunner(config) owns policy, versioned prices, monetary caps, complete cache controls, output schema/validator and technical limits. run accepts a verified scope/session/operation key/input revision/intent, an optional AbortSignal and a lazy loadInput(): Promise<Msg[]>. The host resolves identity and immutable input authorization; never forward browser-supplied scope/revision without that resolution. Only text user/assistant messages are supported.

Supply ExecutionStore, ExecutionResultStore, UsageInbox, GovernedCostSettlementStore, a SpendStore.peek reading the same counters, and an awaited AuditLog. Use InMemoryGovernedCostSettlementStore.peek with its own volatile settlement store; PostgresSpendStore and PostgresCostSettlementStore share the durable tables. Only governed settlement writes financial rows. AuditLog.cost and SpendStore.add are never invoked by this engine. Routing/access carry operation/attempt/call IDs.

Configuration includes policyVersion, priceVersion, outputContractVersion, prices, caps, consumers, system, maxTokens, maxInputChars, maxOutputChars, deadlineMs, resultRetentionMs, clients and onBackgroundError. Optional cache overrides the policy cache, including false; omitted uses the policy choice. Optional output is {name, schema, validate} for native JSON; validate must synchronously return true. Configuration data is snapshotted; host version identifiers must describe immutable policy/output contracts. Clients/policy/validator are trusted capabilities. Use the providers package's explicit SingleDispatch constructors: default clients with automatic retries are rejected. No fallback, repair, tools or user streaming.

Results are reserved before loading context. Duplicate keys read status or the original authorized envelope, never load input or redispatch. Changed session, intent, revision or policy/output/price version conflicts. Monetary warnings preserve results. Explicit block caps retain preflight/post-call blocking; known cost is always settled before a successful output is suppressed.

read(scope,key) returns null or a view: in_progress, reconciliation_required, not_dispatched or completed. Completed includes the current authorized envelope, original financial receipt and historical journal metadata. Erased/expired output is unavailable even when the historical completion was successful. Persistence failure rejects; reread/recover resolves what committed without a provider retry.

A not_dispatched view's record.notDispatchedCause says why the claimed call was never sent to the provider, when that was observed: only invalid (refused as configured or loaded) supports suppressing an unchanged retry; input is the host loader's own failure; aborted, audit and store usually pass; absent or expired means unknown. Retry only under a new key, with a bounded policy. The structured runner's root-only view has no record and no cause (spec: not-dispatched-cause).

recover(scope,key,{leaseMs}) acquires an eligible accounting-only fence, uses the original policy/prices/day and completes available or unavailable output. A policy refusal during unfinished recovery leaves completion pending rather than replacing a previously stored envelope. It never fetches input. Call reconcileExpired(scope,{limit?}) from the host scheduler to fence overdue prepared/dispatching executions. Unknown/unpriced evidence requires operator reconciliation; late inbox evidence does not automatically rewrite it.

reconcileUsage(scope,{limit?,after?}) acknowledges only exact observations whose completed accounting or not-dispatched proof is durable. Page size defaults to50, maximum100. It returns scanned/acknowledged counts and the last inspected cursor. Restart each pass from the beginning to see late commits. Unknown/conflicting observations remain pending. Financial projections have independent consumers. The optional third argument { skip(operationKey) } leaves rows pending. Runtime maintenance uses it to keep a governed call's usage on the step path, which also checks root accounting (spec: runtime-maintenance).

The public run promise is bounded by deadline/abort, including hanging dependencies. A dispatched stream remains drained for late evidence; background errors reach the required metadata-only onBackgroundError callback. Process death or a permanently hung custom adapter cannot guarantee final usage arrival. Hosts monitor unresolved records and schedule recovery; this package starts no persistent worker.

Migrate journal, results, inbox, governed costs and audit before deploying the matching readers/writers. Result retention must allow execution/recovery and its erasure surface must be registered by the host. Completion does not mean a business mutation or portal/WhatsApp delivery happened: those have independent identities. Erasure can still race egress after the final reservation check; stored content cannot be resurrected. See spec.

Optional temperature is a host configuration snapshot, persisted with execution controls before dispatch. Provider validation applies before claim. Replay and accounting recovery retain the original value; callers cannot override it. Bump policyVersion when changing this immutable configuration. See the providers package's deliberately bounded support matrix (spec: explicit-temperature-controls).

Governed model steps

createGovernedStepRunner(config) is the official buffered primitive for a one-call structured response and the primitive adopted by conversations. It accepts a trusted, already planned OperationCallInput and root fence, plus a lazy loadRequest and routing rationale. Supply the same execution/result/ inbox/settlement/spend/audit capabilities, OperationTreeStore, OperationAccountingStore, single-dispatch clients and runTimeoutMs/maxRequestChars/maxOutputChars (optional maxOutputChars). The host builds registry-derived tools and enforces authorization before handing it over. Input prices, policy, controls, IDs and retention are immutable original snapshots.

The root slot is reserved before journal claim; result policy and correlated audit precede content loading. One dispatch produces one governed charge plus one root receipt. No legacy financial writer, tool handler, session append or channel send runs here. Warn preserves output; configured block caps use the inclusive root cost. A successful view includes the original receipts and step: neutral text, toolCalls, reasoning, providerBlocks, stop and an order index preserving their interleaving. Text ranges use UTF-16 offsets; adjacent text deltas are coalesced. This is backstage harness data, not a public reply. The adopting loop must hide reasoning and authorize tools. Opaque JSON is preserved; invalid/non-JSON/oversized output cannot authorize tools. Malformed tool arguments retain an empty input and boolean marker, never raw text.

Why output is invalid (spec: invalid-output-reason). The run that settles a call invalid carries invalidReason, the first check the collector refused, in a closed name (INVALID_OUTPUT_REASONS): output_chars, reasoning_chars, text_malformed, tool_call, block_malformed, stop_conflict or event_type. It never carries content. Nothing stores it: read, replay and recover have none. Text and tool/provider block JSON count toward maxOutputChars; reasoning block JSON (an OpenAI encrypted_content and its summary) counts toward maxOutputChars alongside text and tools, at most 1,000,000 (spec: remove-reasoning-ceiling). Optional maxConsecutiveReasoning detects a step whose model emits more than that many consecutive reasoning events without any text_delta or tool_call: the collector marks the output invalid with cause reasoning_loop on the first event past the limit (spec: reasoning-loop-detection), and the runner stops reading the stream there (spec: loop-abort-unsettled). Providers report usage only at the end, so the cut call's cost is unknown: it ends reconciliation_required, with no settlement or root receipt, for the operator to reconcile, and that run's view carries invalidReason (the first refusal's name). When the terminal usage came before the cut, the call settles invalid with its cost. Any other refusal is read to its end. An adapter that ignores its signal is read to its end or to runTimeoutMs.

read({scope,rootKey,operationKey}) and recover(key,{leaseMs}) bind the call to its registered root. Both financial receipts are required for completed status; recover repairs missing aggregation even if the journal is already complete. Recovery loads no request and dispatches no provider/tool. A fresh run checks the records its own writes returned instead of reading them back, and reads only the content again (spec: turn-rereads). Lost output becomes unavailable; erased/expired content cannot be regenerated. Root closure prevents new admission but does not cancel a previously reserved call before its deadline. The root owner closes/reconciles roots and owns whole-turn/effect uncertainty.

reconcileUsage({scope,rootKey},query?) acknowledges exact evidence only when that root's completion/accounting (or not-dispatched proof) is durable. It scans bounded scoped inbox pages; restart subsequent passes to see late commits. The public run is bounded by runTimeoutMs (max ten minutes), external abort and, for new calls, the original deadline. Its background drain reports metadata-only failures. The conversation package adopts this step; legacy runners remain separate. isStepInterruption(error) is true only of a rejection this runner raised itself (its timeout, its deadline or its caller's signal) while no store write of the stream had failed; a store's own AbortError is not one (spec: call-retry). See spec.

A clean attested Anthropic context rejection stores a metadata-only refusal after the original zero settlement and root receipt. Any content (even rejected by the collector), missing reference, repeated/different stop or stream failure invalidates the proof. Accounting recovery never resumes a conversation automatically (spec: safe-context-rejection-rotation).

A pre-stream rejection proof (spec: pre-stream-rejections) needs a reference, no content, no stop and the stream's failure. The call then completes with outcome unavailable and stopReason: null, settled at zero with its root receipt, in the text, structured and step runners. These calls used to read reconciliation_required. Any other combination with the proof is unknown/invalid.

Explicit tool selection

Set the same toolChoice on call.execution.controls and the lazy ModelRequest, for example {type:"tool",name:"extract_record"}. The request's tools must come from the host-authorized registry. The primitive validates the selected name against these actual ToolSpecs even when a custom client validator does nothing; provider validation additionally applies its documented support matrix before network access. Selection never executes a tool or grants permission to do so.

Choices are immutable execution metadata. An identical replay returns original receipts and retained output without loading input; a changed choice (including absent versus explicit auto) under the same identity conflicts. Accounting recovery never dispatches another request. Older records without toolChoice remain readable. The text/JSON createSingleCallRunner rejects toolChoice on configuration or run input; use createGovernedStepRunner for structured tool responses.

A completed view alone is insufficient: inspect result.outcome, step.stop, the expected name in step.toolCalls and its malformed marker before validating its arguments against the business schema. Refusal, truncation, unavailable output and malformed arguments must not be treated as a valid extraction. Non-success views may withhold partial blocks while retaining the original stop and financial receipt.

Structured single calls

createStructuredCallRunner(config) is the one-call structured response with the text runner's call shape: run({scope, sessionId, operationKey, inputRevision, intent, loadInput, signal?}). Use it for a forced tool acting as an extraction schema. Configuration is the text runner's, without output, plus trees, accounting, trusted registry tools and an optional toolChoice. Tool names are validated and deduplicated at construction.

The runner owns the operation root, the identities, the replay check and the closure. Every identifier is alma-structured-v1:<kind>:<sha256> over the scope and the host key: bounded, domain-separated and never embedding the key. That prefix is reserved; do not use it for other keys. Replay compares the root's immutable binding without its deadline, so no occurrence time is needed. The binding covers session, intent, input revision and a digest of all configuration data, including prices and tools. Changes in trusted policy.resolve behavior cannot be digested: bump policyVersion. The session/day block preflight runs before any root exists.

A completed view is the unchanged governed step view: inspect result.outcome, step.stop, the selected call and its malformed marker before using it. A failed root closure never hides a completed result: closure is detached housekeeping, and an unclosed root expires and is reconciled. Without an execution record, read reports not_dispatched only for a non-active root with zero reserved calls, proven by the store under its root lock. A reserved call without a journal record reads reconciliation_required; an active root reads in_progress, whatever the application clock says. One key is one call: unusable outcomes are final, so retry with a new key.

recover(scope, key, {leaseMs}) and reconcileUsage(scope, key, query?) take the host key. reconcileExpired(scope, {limit?}) is scope-wide and returns {roots, executions}. See spec.

版本与发布记录

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

版本发布日期距今
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/single-call 的常见问题

@alma-harness/single-call 是什么?

Governed one-dispatch model execution for Alma.

如何安装 @alma-harness/single-call?

在项目目录运行 npm install @alma-harness/single-call,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@alma-harness/core(^0.41.0)、@alma-harness/execution(^0.41.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@alma-harness/single-call 支持 TypeScript 吗?

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

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

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

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

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

@alma-harness/single-call 可以商用吗?

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

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

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

@alma-harness/single-call 还在维护吗?

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