npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@alma-harness/loop

v0.12.0

Published

Alma governed turn engine: steps, tool dispatch, delegation and cache hygiene.

Readme

@alma-harness/loop

The internal governed turn engine of Alma — a TypeScript runtime for multi-tenant agents operating on sensitive data. It runs against @alma-harness/core. Hosts use @alma-harness/conversation or the composed @alma-harness/runtime; this package has no standalone agent factory.

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

What it owns

  • The loop — steps, tool dispatch, delegation and lifecycle hooks. The matching-version internal API requires governed execution and a main routing snapshot validated before admission. Conversation owns identity, caps and stores.
  • The volatile suffix, per turn (spec: volatile-per-turn) — volatile is a function called once per turn with the turn's scope, session, intent, trigger and clock; its text joins the static volatile blocks of system as one block appended to the input, marked origin: "harness" so a screen can hide it, persisted and replayed byte-identical so the cache reads through it. The summary transcript omits it. A source failure after admission retains reconciliation_required ownership.
  • Deferred tools and the built-in search (spec: deferred-tools) — a tool registered deferred: true is not advertised until the model asks: while any deferred tool is in the active set, every step advertises search_tools, whose description names them; a search loads the matches for the rest of the turn, logs the load on the access trail by name, and tells the model what it can call now. Privileged core: never in a profile, never advertised to a delegate, tool:pre applies.
  • Provider-executed web search (spec: provider-tools) — providerTools registers the kinds; a profile grants by kind, and cannot name an unregistered one. Each is advertised only to a turn at or below its maxSensitivity (internal by default) and never to a delegate; the call and its results are persisted in the neutral format in provider arrival order and replayed on the next step, each result is one access event (tool: "web_search", resource: "provider:<id>:<n> results", never the query), the stream narrates them, and a pause stop re-sends the turn as another step against maxSteps and the cap. Priced per request from the table, fail-closed. maxUses is a per-turn cap the loop enforces on every provider (spec: what-the-wire-cuts): at the cap, the tool is withheld from the turn's later steps.
  • What the wire cuts (spec: what-the-wire-cuts) — a tool call whose arguments did not parse is answered with an invalid-input result on tool_use while max_tokens withholds the step output; the raw text is never persisted. A media block in the input is egress and lands on the access trail by provider and kind, never by URI: by URL, the adapter hands the URI to the provider to fetch.
  • Output schemas at the hook boundary (spec: provider-request-controls) — step:pre cannot add or change ModelRequest.output. The attempt is refused and recorded as metadata, with no schema content in the trail. Public agent calls do not select structured output yet.
  • The cache's duration (spec: cache-ttl) — a policy choice may carry cache: { ttl: "1h" }; the loop copies it onto every step, and a delegate's, records it on the routing trail and pins it after step:pre like the tier; a one-hour write is priced at its own row, fail-closed.
  • Media by bytes (spec: media-by-bytes) — with media configured, the bytes a block carries, or the product's media.source asked once per ref and turn, go to the provider as base64; the session keeps the ref alone; the trail adds the size; the content type is normalised before the adapter sees it. Without media, a block carrying content is refused (spec: close-066-069-findings). History media on later turns is a placeholder text block by default (the bytes go once) or, with replay: "bytes", the bytes again. maxBytes (32 MiB) prevents oversized media from reaching the provider. A source failure after admission retains uncertainty; it does not authorize a retry.
  • Two faces on one turnrunTurn returns the result when it is done; streamTurn observes settled main-step output and carries the identical result on .result, alongside them. One implementation, so enforcement cannot differ.
  • One-call work lives in @alma-harness/batch and @alma-harness/single-call. The legacy job factories have been removed; batch ownership, original financial receipts and explicit uncertainty now govern those paths (spec: legacy-job-runner-removal).
  • Routines run through this loop but live with the clock: Governed routine adapters live in @alma-harness/schedule since spec: routines-with-the-clock.
  • The long-context policy (longContext) — session rotation with a summary at the cold boundary (§6.6, spec: long-context). A cold start (the last message older than coldAfterMs, default five minutes) over rotateAtTokens summarizes the older history on summaryTier through the delegate path — routed, charged, on the trails — keeps the last keepLastMessages verbatim from a user message, and continues from [summary, tail, input]; the summary is persisted with a marker and every later step's view starts there. Only an attested, settled zero-usage context rejection permits one reactive rotation and retry; an arbitrary provider exception never grants that authority. Never mid-history pruning; absent, the whole log is sent as before.
  • Financial enforcement — the execution composition reads authoritative spend and applies normalized caps. Main calls, delegates, direct calls and summaries settle through original governed receipts; the loop never writes legacy costs.
  • Coordination and observation live in conversation. Durable admission replaces expiring lease takeover. The host retains a busy request's input and key to retry after completion. WhatsApp burst aggregation is also a host responsibility.
  • The cache-hygiene observer (cacheHygieneGuard) — warns when a session keeps cold-writing its prefix, which is a misconfiguration the bill would otherwise report months later.

Where things are

engine.ts defines internal construction and shared result/event types; turn.ts assembles a turn and runs its steps; step.ts applies the hook boundary; dispatch.ts owns tools and nested calls; recall.ts assembles recall. Shared internals, hooks, state, cache hygiene and context helpers remain separate files. There is no legacy stream controller, lease coordinator or charging implementation. The matching-version /internal subpath is not a stable standalone host API.

What it must never do

  • Widen what core permits. Sensitivity narrows through a delegate and never escalates; a hook may not rewrite routing or the advertised tool set, and may lower the per-step output ceiling but never raise it (spec 029). What a step:pre hook touched — honored or refused — lands on the context trail.
  • Reach the filesystem, spawn a subprocess, or ship a shell.
  • Depend on a provider SDK. The neutral message format is the boundary.
  • Narrate the model's reasoning. It is persisted with the assistant message and replayed to the provider; reply and the stream never carry it (spec: reasoning-blocks).

Usage and streaming

Start with the canonical quickstart. Once a canonical runner is configured, both entrypoints require the same explicit identity:

const request = { scope, sessionId, idempotencyKey, input, intent };
const view = await runner.runTurn(request);
// Or observe a new request; a completed retry replays the original result.
const turn = runner.streamTurn(request);
for await (const event of turn) {
  if (event.type === "text") render(event.text);
}
const finalView = await turn.result;

break, iterator return/throw and socket closure detach only the observer. They never abort execution. Explicit execution cancellation or deadline expiry retains uncertainty for reconciliation. A slow reader has a bounded queue and resynchronizes through read; it cannot block the turn.

Text is released per settled main step, after governed output checks and a fresh retention/erasure read. Earlier visible steps may precede a later tool failure or budget stop. No token deltas, delegate text or reasoning are exposed. The final result event and .result use the same canonical view. Invalid input rejects; non-durable routing refusal is not_admitted, overlapping work is busy, and admitted failures are reconciliation_required, not a retryable error envelope.

Testing helpers

@alma-harness/loop/testing ships the scripted ModelClient the loop's own tests run against — deterministic event sequences, no network, no timing. Nothing in it imports vitest.

import { FakeModelClient, scriptedText } from "@alma-harness/loop/testing";

const client = new FakeModelClient([scriptedText("Hello!")]);

Documentation

Docs index · Architecture · Invariants · Quickstart

Apache-2.0

Temperature and output controls remain pinned at step:pre; attempted changes are recorded without schema content. Canonical public calls do not select these controls. Pure context helpers come from @alma-harness/memory/context and retain identity through loop reexports; memory is a matching-version peer dependency.

The execution seam requires the snapshotted main choice. Summaries and children resolve their own routes and retain lineage, usage and root limits. Settled main steps enqueue references without waiting for observers or controlling cancellation.

Explicit delegation refusal

ctx.models.tryDelegate(request) returns either { status: "completed", text, usage } or an immutable { status: "refused", reason }, where reason is depth, sensitivity or tool_grant. A tool may handle that data or return the exact refusal object to emit a bounded isError tool result. Clones and foreign objects are ordinary output. The existing delegate keeps throwing; neither method waives handler/child failures, cancellation or uncertain persistence. Only a live handler can use tryDelegate.

Requests are snapshotted before child routing: plain data properties, a valid tier/sensitivity, well-formed prompt up to 1,000,000 UTF-16 units, and at most 256 tool names of 1–200 units. Accessors, sparse arrays and unknown fields are invalid. Structural refusals precede child policy, hooks and reservation. A normally returned refusal still passes execution health checks and the existing tool:post, output cap and advisory handling. See spec governed-delegation-refusal.

The governed main loop reacts only to conversation's trusted settled rejection marker, before recording/observing rejected output. It rotates at most once; summary and retry use the same root limits. There is no exception-based legacy fallback (spec: safe-context-rejection-rotation).

The optional request toolChoice is also frozen as a copy and pinned, including absence, at step:pre. Hook injection, removal or substitution is refused and recorded as metadata. Canonical conversations do not offer a global forced-tool setting; use the official governed step runner for one-call structured extraction.