Skip to content

ToggleLogic Free 2.2.0

Choose a tag to compare

@alharlow111 alharlow111 released this 22 Oct 13:28
· 22 commits to main since this release
Immutable release. Only release title and notes can be modified.

2.2.0 — 2026-10-22

Milestone M1 part 1 — host adapters and the gateway-neutral Decision API, built
in the steps of docs/design/M1-HOST-ADAPTERS-AND-DECISION-API.md §7 (steps 1–5
and 8). OpenClaw routing is unchanged at every step. Behavior changes, all
below: the audit outcome for unchanged-model decisions (D28), the notification
interpreter default, and the new-session marker folder fix.

  • Step 2 — injected paths, clock and state store (no behavior change).
    New host-neutral src/core/paths.js (createPaths({ home, stateDir })),
    src/core/clock.js and src/core/state-store.js (atomic JSON documents;
    a missing document reads as null, a corrupt one throws). The OpenClaw
    adapter (src/adapters/openclaw/paths.js) resolves HOME and
    OPENCLAW_STATE_DIR exactly as 2.1.x and injects one paths object into
    the audit log, routing log, usage events, cost log, pricing cache, balances,
    owner override, governed escalation and the Intelligence detector. A test
    fails if anything under src/core/ reads the environment, the OS home or
    the host SDK.
  • Step 3 — decide() extracted into src/core/decide.js (no behavior
    change).
    createDecisionCore({ policy, services }) runs the decision order
    of design §4.3 on a DecisionRequest (requested, defaultRef,
    candidates, fallbacks) and returns a togglelogic.decision/v1 response
    (decision, reason, rationale, engine). The OpenClaw interceptor is
    now toDecisionRequest → decide → applyDecision; preflight caching,
    duplicate-prompt suppression and session-store provenance stay in the
    adapter, and host state is still read at the same point in the order.
    Proof: a recorded-decision replay fixture (38 scenarios, every selection
    reason, recorded from the step-2 code) must match exactly: return value,
    routing record, audit events, usage events, warnings and seam calls.
  • Step 4 — OpenClaw adapter (no routing behavior change). Everything
    OpenClaw-specific now lives in src/adapters/openclaw/: the plugin entry
    (entry.js, definePluginEntry), hook registration and service wiring
    (register.js), the routing hook (interceptor.js), session-store
    provenance (session-store.js), the conversation-access check and the
    owner-override notification helper. src/index.js only carries the version
    label and builds the adapter. The old module paths re-export the new ones, so
    imports keep working. A test fails if OpenClaw-only code appears outside the
    adapter.
  • Fixed: new-session marker folder. It now follows the same state-path
    rules as every other ToggleLogic file (HOME, and OPENCLAW_STATE_DIR with
    ~ expansion). Before, a ~/… state dir produced a literal ~ folder.
    With OPENCLAW_STATE_DIR unset the folder is unchanged.
  • Step 5 — Decision API v1 responses, decision matrix and replay.
    decide() now returns a response that validates against
    docs/design/schemas/decision-response.v1.schema.json: it adds
    policyDigest (sha256: of the canonical policy JSON) and costEstimate
    (dollars for the model that will run, only when the caller supplies
    usageEstimate and a pricing service; otherwise priced: false, usd: null
    and a named unpricedReason — never a $0 stand-in), and omits empty
    provider/model instead of sending null. The internal recorded flag is
    gone from the response. New src/core/request.js maps a v1 wire request to
    the core request; content is never copied. New
    togglelogic-decide replay --log <routing.jsonl> --config <file> [--json]
    re-runs recorded routing decisions against a policy and prints the
    differences (exit 2 when any differ). Records that depended on state the log
    does not carry (owner override, the Intelligence seam, provider availability,
    dispatch errors, or a host task label the log did not carry when the policy
    routes on labels) are counted by reason, not guessed at. Replay reads only
    and writes nothing. Invalid token counts and pricing-service errors give
    invalid_usage_estimate and pricing_error; decide() never throws on a
    cost estimate. Tests: a 112-row decision matrix (modes × requested source ×
    provider availability) validates every request and response against the v1
    schemas, requires the OpenClaw hook to return exactly the core's decision,
    and proves request content never reaches a response, routing record, audit
    event or warning.
  • Changed (D28): audit outcome for unchanged-model decisions. A
    routing.decision audit event for a decision that leaves the model unchanged
    is now noop; 2.1.x reported success when the passthrough came from mode
    dispatch (passthrough mode, no configured or cheap route, Intelligence
    declined, shadow or unavailable). Routing is unchanged. The replay fixture
    was re-recorded and differs from the step-2 recording only in that field, in
    those 7 scenarios.
  • Step 8 — Changed: owner-override notification interpreter is
    configurable.
    New ownerOverride.askInterpreter (default python3,
    resolved from the gateway's PATH; a value containing / or starting with
    ~ is a file path). 2.1.x always ran /usr/bin/python3; to keep exactly
    that, set askInterpreter to /usr/bin/python3. Only matters when
    features.ownerOverrideAsk is enabled. The plugin manifest accepts the new
    key.
  • Step 8 — Deprecated: src/enforcement/bounded-executor.js. It was never
    wired into the plugin. It is now excluded from the npm package and stays in
    the repository only for its tests; the next major removes it.
  • Fixed (tests only): the packaging check no longer spawns npm. The test
    that proves the deprecated bounded executor stays out of the package now
    asserts the files ordering directly instead of running npm pack in a child
    process, which raced with other test files under parallel runs. The packed
    file list itself is still verified by npm pack --dry-run in every release
    task. No product-code or behavior change.