Skip to content

Latest commit

 

History

History
413 lines (304 loc) · 24.3 KB

File metadata and controls

413 lines (304 loc) · 24.3 KB

OpenChrome v1.11.1

Release date: 2026-05-12

Summary

v1.11.1 is a patch release on top of v1.11.0. It contains no new runtime code beyond v1.11.0 — only a comprehensive release note that documents the full cumulative diff from v1.10.4 (the last v1.10.x npm publish) to v1.11.1, plus the version bump.

Treat this release note as the canonical reference for what changed in the v1.11 line. Anyone upgrading from v1.10.x reads this single document and gets the complete picture: every new module, every new MCP surface entry, every behavioral change, and every breaking-change flag they need to verify.

The v1.11 line is the portability-harness contract release. It establishes a durable core / pilot tier split inside openchrome-mcp and ships a substantial batch of harness primitives.


Upgrade

npm install -g openchrome-mcp@1.11.1
openchrome check      # verify the installed CLI and runtime wiring

Fresh install:

npm install -g openchrome-mcp@latest
openchrome check
openchrome info       # optional: print installed version and runtime details

No configuration changes are required to keep your existing v1.10.x experience. The new pilot tier is opt-in via --pilot:

{
  "mcpServers": {
    "openchrome": {
      "command": "openchrome",
      "args": ["serve", "--pilot"]
    }
  }
}

Quick changelog

v1.10.4  →  v1.11.1   (cumulative, this release)

  138 files changed
  +24,209 / -210 lines
  72 first-parent commits
  27 PRs merged (PR #774 → PR #818)
  18 new tracking issues filed
  4 standalone fixes carried forward (#767 #771 #773 #742)
  Tier 1 MCP tool count:  34 → 38
  New MCP resources:       0 → 1   (openchrome://skill-graph/<domain>)
  New runtime deps:        0
  New dev deps:            1       (dependency-cruiser, CI-only)
  New native deps:         0       (argon2 remains the only one)
  npm overrides added:     2       (basic-ftp, ip-address — security)

Breaking changes

No source-code-level breaking changes for tool callers. A v1.10.x ~/.claude/.mcp.json configuration works bit-identically with v1.11.1 when --pilot is unset.

The following are the only observable behavioral changes a v1.10.x user can hit. Each one has a documented migration path.

1. Default headed Chrome auto-launch window placement

v1.10.4 v1.11.1
Default flags --start-maximized + --window-size=1920,1080 --window-position=0,0 + --window-size=1280,900
Window position implicit (window-manager decided) 0,0
Window size 1920×1080 1280×900
Maximized? yes, by default no, unless requested

Why: the v1.10.4 combination could place the window partly off-screen on multi-monitor setups, scaled displays, or narrower work areas (issue #770).

Migration if you depended on the old behavior:

# Keep maximize-on-launch
openchrome serve --auto-launch --start-maximized

# Keep 1920×1080 explicitly
openchrome serve --auto-launch --window-size 1920,1080

# Place on a specific spot (new in v1.11)
openchrome serve --auto-launch --window-bounds 1920,0,1280,900

Equivalent env vars are honored too: OPENCHROME_WINDOW_SIZE, OPENCHROME_WINDOW_POSITION, OPENCHROME_WINDOW_BOUNDS, OPENCHROME_START_MAXIMIZED. Invalid values produce a clear error message and exit code 2.

2. Tier 1 tool count bumped 34 → 38

v1.11.1 adds four new tools to the default Tier 1 surface: oc_assert, oc_evidence_bundle, oc_skill_record, oc_skill_recall.

Migration: if your MCP client (or test harness) pins the Tier 1 tool count, bump the expected value from 34 → 38. The four new tools are additive; no existing tool is removed or renamed.

3. oc trace / SQLite removal (forward-looking)

The v1.11 line implements all new storage (trace, skill graph, skill memory) on JSONL or JSON files + proper-lockfile, not SQLite. v1.11.1 does not include the user-facing trace recorder hooks yet — those land in a follow-up — so no existing trace-consumer code breaks. If you have downstream tooling that reads index.sqlite from a v1.10.x build, that file path is not produced by v1.11.x.

4. Pilot --pilot startup line on stderr

When --pilot is set, the server writes a one-line marker to stderr at startup:

[harness] core+pilot enabled (trace,state_graph,contract_runtime,handoff_persist,perception_voting,skill_curator)

(Or [harness] core only (--pilot not set) when off.)

Migration: if your MCP client captures stderr and parses it for specific patterns, this line is new. It is informational only — the JSON-RPC protocol on stdout is unaffected (the line goes to stderr explicitly because stdout carries the protocol).

5. Issue #721 closed without implementation (per P3)

The previously-proposed macOS Keychain + Windows Credential Manager handoff persistence (#721) is not implemented. Per the new portability-harness contract, OS keychain integration is forbidden because it breaks the "anywhere-compatible" portability promise on headless Linux environments without Secret Service.

Migration: if you were waiting on #721, use the new OPENCHROME_HANDOFF_KEY_FILE opt-in for cross-restart handoff persistence instead (see below).


What ships in the v1.11 line

Standalone fixes (carried forward from v1.10.5 / v1.11.0)

These four landed on main before the v1.11 cleanup work. They are part of v1.11.1 by virtue of sitting between the previous npm publish (v1.10.4) and this one.

PR Title Effect
#767 test: opt-in to allowUnauthenticatedHttp in HTTP transport tests Fixed four inherited HTTP-auth test failures on develop without weakening the v1.10.4 fail-closed production policy. The escape hatch (allowUnauthenticatedHttp: true) is the documented test-only path; production code is untouched.
#771 Fix auto-launched Chrome window bounds placement (closes #770) New --window-size / --window-position / --window-bounds / --start-maximized CLI flags + matching OPENCHROME_WINDOW_* env vars. Default flipped from --start-maximized + 1920×1080 to 0,0 + 1280×900. See breaking-change #1.
#773 fix: improve javascript_tool diagnostics and Shadow DOM helpers for SPA scraping (closes #772) Adds globalThis.__openchrome.querySelectorAllDeep(selector, root?) and globalThis.__pierce(selector) page-context helpers for open shadow roots without monkey-patching document.querySelectorAll. Improved Runtime.evaluate diagnostics when CDP returns a Promise remote. Closed shadow roots remain out of reach.
#742 fix(deps): bump basic-ftp and ip-address via npm overrides (refs #688) Top-level npm overrides for basic-ftp@^5.3.1 and ip-address@^10.1.1 to neutralize one critical and one moderate transitive advisory from the @puppeteer/browsers → proxy-agent chain (GHSA-5rq4-664w-9x2c, GHSA-6v7q-wjvx-w8wg, GHSA-rp42-5vxx-qpwr, GHSA-rpmf-866q-6p89, GHSA-v2v4-37r5-5v8g). npm audit --omit=dev now reports zero production vulnerabilities.

Portability-harness contract (PR #774)

  • docs/roadmap/portability-harness-contract.md — durable contract; every future PR must satisfy it.
  • docs/roadmap/history/openchrome-1.11-cleanup.md — one-time application plan; archived in this release.

The contract defines five principles:

  • P1. Tool server identity — OpenChrome accepts tool calls and returns results; it does not orchestrate, run continuous loops, or manage agent lifecycles. Long-lived background work is permitted only in the pilot tier and only when it serves the tool-call guarantee.
  • P2. Zero-impact harness extension — when --pilot is unset, an optional native dep fails to load, or the per-family flag is off, the v1.10.4 tool surface and behavior are preserved bit-identically.
  • P3. Anywhere-compatible MCP — neither tier may require outbound LLM API calls (Anthropic, OpenAI, etc.), mandatory API keys at boot, platform-specific compile toolchains, OS keychains, or network-attached secret stores. Server-side LLM-driven decisions live in separate npm packages or host-side libraries.
  • P4. Facts versus decisions — the server captures, computes, stores, retrieves, normalizes, and verifies facts. LLM judgment lives outside.
  • P5. Native dependency disciplineargon2 is the only mandatory native runtime dep. better-sqlite3 is not adopted. Any future native dep ships in optionalDependencies with a documented fallback.

src/core/src/pilot/ tier split

src/
├── contracts/      # core contract DSL, evaluators, pHash, evidence helpers
├── core/           # P1–P5 strictly enforced; no flag; backward-compatible
│   ├── trace/
│   ├── skill/
│   ├── skill-memory/
│   └── perception/
├── harness/        # opt-in/flag plumbing and harness runtime helpers
├── tools/          # MCP tool registrations, including new core surfaces
└── pilot/          # opt-in via --pilot; P1/P4 relaxed; P3 still enforced
    ├── runtime/
    ├── handoff/
    ├── voting/
    └── curator/

Import direction is enforced unidirectionally by a dependency-cruiser rule: src/core/** may not import from src/pilot/**. The check runs on every PR (CI step Lint tier boundary, npm run lint:tier).

--pilot CLI flag + per-family sub-flags

openchrome serve                    # default v1.10.x experience
openchrome serve --pilot            # opt into pilot
OPENCHROME_CONTRACT_RUNTIME=0 openchrome serve --pilot   # one family off
Pilot family Sub-flag (env) Default inside --pilot
trace recorder OPENCHROME_TRACE active
skill state graph OPENCHROME_STATE_GRAPH active
contract runtime OPENCHROME_CONTRACT_RUNTIME active
handoff token + persistence OPENCHROME_HANDOFF_PERSIST active
voting framework OPENCHROME_PERCEPTION_VOTING active
skill curator OPENCHROME_SKILL_CURATOR active
skill curator structural merge (Pass 2) OPENCHROME_SKILL_MEM_MERGE inactive (off-by-default; costlier)
handoff key file (cross-restart persistence) OPENCHROME_HANDOFF_KEY_FILE=<path> unset (ephemeral default)

When --pilot is unset, no module from src/pilot/** is loaded into the process (verified by a unit test that inspects the import resolver).

src/harness/flags.ts exposes isPilotEnabled(), six per-family getters, activeFamilies(), logActiveFlags(), and bootstrapPilot(). 32 unit tests cover argv/env parsing, cache behavior, family defaults and overrides, and the lazy-import guarantee.

Core tier additions (unconditional, no flag)

Four new MCP tools and one new MCP resource template register unconditionally. Tier 1 tool count: 34 → 38.

Subsystem Module path New MCP surface Tests
Trace recorder src/core/trace/{storage,redactor,types,index}.ts — JSONL append-only storage (no SQLite), credential redactor (covering JWT / AWS keys / Bearer-Basic schemes / hex-32+ / SSN / URL credential params / sibling-form-field key+value pairs), filesystem-scan session index, per-domain proper-lockfile (recorder CDP hooks land in a follow-up release) 49
Skill state graph src/core/skill/{storage,types,index}.ts — JSON-per-domain storage with full schema (nodes, edges with to_state_distribution), per-domain lockfile coordination, IPv6-safe filename encoding, Windows reserved device-name prefix handling openchrome://skill-graph/<domain> MCP resource (read-only) 33 + 13
Skill memory src/core/skill-memory/{store,stats-resolver,types,index}.ts — JSON-per-domain skill record store, gzipped frozen snapshots, audit-log-backed SkillStatsResolver (streams ~/.openchrome/audit.log in 64 KB chunks, lazy AuditIndex keyed strictly by skill_id) oc_skill_record, oc_skill_recall MCP tools 28 + 24 + 11
Outcome contracts src/contracts/{evaluate,eval-context,validator,types,phash,screenshot-class,png-decode,safe-regex,index}.ts + src/contracts/evaluators/{dom-count,dom-text,logical,network,no-dialog,screenshot-class,url}.ts — DCT-II perceptual hash, screenshot-class registry, zero-dependency PNG decoder oc_assert (single-call verifiable state check), oc_evidence_bundle (DOM + screenshot + network + console + pHash bundle generator) MCP tools 15 + 20
Perception primitives src/core/perception/{metadata,cache,cross-check,image-features,types,index}.ts — perceptual DOM metadata with in-process mtime cache, Sobel edge density, dominant-color (16-bin histogram), sRGB color distance (consumed by oc_evidence_bundle and the pilot voting framework) 31 + 23

oc_assert — single-call verifiable state check

oc_assert({
  contract: <Contract object>,
  args?: Record<string, unknown>,
  evidence?: { snapshot?: unknown },
}) -> {
  verdict: 'pass' | 'fail' | 'inconclusive',
  failed_assertions?: Array<{ name, expected, actual, location? }>,
  evidence_handle?: string,
}

Single call, single return. No retry. No escalation. (Pilot runtime provides those when --pilot is set.)

oc_evidence_bundle — on-demand state snapshot

oc_evidence_bundle({
  include?: ('dom' | 'screenshot' | 'network' | 'console' | 'phash')[],
  network_window_ms?: number,
  evidence?: { snapshot?: unknown },
}) -> { bundle_id, path, size_bytes, parts: string[] }

Default include = ['dom', 'screenshot'], network_window_ms = 5000, console cap 200 entries. Output is a flat directory of named parts (no ZIP — keeps the install dependency-free).

oc_skill_record and oc_skill_recall

oc_skill_record({ domain, name, steps, contract_id, frozen_snapshot? })
  -> { skill_id, stored_at, snapshot_path? }

oc_skill_recall({ domain, contract_id?, limit? })
  -> { skills: SkillRecord[] }

Both are read-only / explicit-write over the JSON skill-memory store. No LLM scoring (the pilot curator does ranking when --pilot is set; core is deterministic last-used-at order).

openchrome://skill-graph/<domain> MCP resource

Read-only JSON snapshot of the per-domain skill graph at request time. In-process mtime cache invalidates when the underlying JSON file changes on disk. URL-encoded domain segment so IPv6 and other unusual hosts round-trip safely.

Pilot tier (opt-in via --pilot)

All pilot modules load lazily through bootstrapPilot() only when --pilot is set. Each family is independently gated by a per-family sub-flag; the default inside pilot is active (except structural merge — off by default since it's costlier).

Family Module Sub-flag Tests
Contract runtime — verdict taxonomy (pass / fail / inconclusive / validation_error / execution_error / budget_exhausted / escalated / aborted_by_hook), exponential-backoff retry, always-settles guarantee, abort handling, async audit emission with .unref()'d timers src/pilot/runtime/{runtime,types,index}.ts OPENCHROME_CONTRACT_RUNTIME 64
Idempotency cache + preemptive cancellation — SHA-256-keyed in-process cache with TTL, canonical JSON stringify so object property order doesn't matter, in-flight registry seeded for epoch-based supersession src/pilot/runtime/idempotency.ts (same flag as runtime) 25
beforeIrreversibleAction hook — fires only for critical: true contracts. Operator-registered hook can return { proceed: true }, { proceed: false, reason }, or { proceed: 'await-human', externalToken }. Hook throws are treated as implicit deny. Default registered hook is a no-op pass-through (preserves v1.10.4 behavior). src/pilot/runtime/before-irreversible.ts (same flag as runtime) 16
Handoff token + manager — base64url-encoded 32-byte tokens (node:crypto.randomBytes), dual-index (bySession, byToken) for O(1) redeem, TTL-based expiry with setInterval+.unref() background prune. src/pilot/handoff/{token,banner,manager,index}.ts OPENCHROME_HANDOFF_PERSIST 15
Handoff persistence (AES-256-GCM) — ephemeral key by default (regenerated at boot; persisted tokens invalidated on restart). Opt-in cross-restart persistence via OPENCHROME_HANDOFF_KEY_FILE=<path> to a 32-byte key file. Wrong key size on the file: graceful fallback to ephemeral. No OS keychain integration. src/pilot/handoff/persistence.ts (same flag as token) 19
Multi-model voting framework — voter-agnostic Voter interface, argsEquivalence() action-arg canonicalization (whitespace, key order, semantic-equivalent selectors), vote() orchestrator with bounded concurrency, per-voter deadlines, one retry per voter. Ships with deterministic test voters. No Anthropic/OpenAI HTTP wrappers — those live in the future openchrome-perception-voters package (#775). src/pilot/voting/{orchestrator,args-equivalence,index}.ts OPENCHROME_PERCEPTION_VOTING 57
Verified skill extractor — turns successful, contract-verified runs into reusable SKILL.md candidates. Idempotent on (graph_node_anchor, contract_id). Atomic writes, per-skill directory lock, sidecar JSON recovery. Prototype-pollution-hardened SKILL.md frontmatter parser. No LLM calls — pure deterministic transform. src/pilot/curator/{extractor,skill-md,types,index}.ts OPENCHROME_SKILL_CURATOR 56
Skill recall ranking — read-only over the core-tier SkillMemoryStore. Rank by successCount DESC, lastUsedAt DESC, skillId ASC. Byte-cap drop policy (8 KB default) for LLM-friendly payload sizes. Frozen-snapshot memoization. src/pilot/curator/recall.ts (same flag as curator) 22
Curator Pass 1 (prune) — two sub-passes: confidence-floor demote/archive (fail_rate > 30%, ≥5 runs, double-demote → archive) and TTL archival (stale 30 d, untouched 60 d). Moves to .archive/<skill_id>/ + reason.json. Never deletes; idempotent. src/pilot/curator/prune.ts (same flag as curator) (in shared 38-test suite)
Curator Pass 3 (promote) — async best-effort. Syncs sidecar runs.count and latest success timestamp into SkillMemoryStore.markUsed. Skips archived and store-unknown skills. src/pilot/curator/promote.ts (same flag as curator) (in shared suite)
Curator Pass 2 (structural sibling merge) — detects (domain, intent) pairs whose step lists are structurally near-duplicate after URL normalization, action-arg canonicalization, optional-step pruning. Aggregates success_count, unions runs.recent oldest-first. Structural-only, no LLM. Gated by OPENCHROME_SKILL_MEM_MERGE=1 (separate from main curator flag because merge is costlier). src/pilot/curator/merge.ts + src/pilot/curator/stop-words.ts OPENCHROME_SKILL_CURATOR + OPENCHROME_SKILL_MEM_MERGE 27
PID-file lock — atomic temp+link write, TTL-based stale reclamation (1 h default), process.kill(pid, 0) liveness probe. Prevents two curators against the same domain even across two openchrome serve processes. src/pilot/curator/lock.ts (same flag as curator) (in shared suite)
Background runnersetInterval with .unref() so it doesn't pin the event loop. Acquires CuratorLock per cycle (skips silently if contested), runs Pass 1 → Pass 3 (Pass 2 if enabled). src/pilot/curator/runner.ts (same flag as curator) (in shared suite)

Out of scope (per P3)

Two pieces from the original epic plans were redirected to separate packages rather than landing here, because their server-side LLM API calls would violate the "anywhere-compatible" portability promise:

  • LLM voting providers (Anthropic + OpenAI HTTP wrappers) — tracked in #775 as the future openchrome-perception-voters package.
  • LLM merge requester for the skill curator — tracked in #776 as the future openchrome-skill-curator-llm package.

The corresponding Voter and MergeRequester interfaces remain in openchrome-mcp so those separate packages can plug in cleanly when they arrive.


Boundary plumbing (PR #787, #788, #789)

Three boundary-enforcement PRs landed before any tier-routed work:

  • #787dependency-cruiser config (.dependency-cruiser.cjs) with the core-must-not-import-pilot rule (severity: error). src/core/index.ts and src/pilot/index.ts placeholders with tier documentation. New npm run lint:tier script.
  • #788lint:tier wired into .github/workflows/ci.yml after the existing eslint step. tests/core/ and tests/pilot/ scaffolding with READMEs.
  • #789--pilot CLI flag plumbing on serve; src/harness/flags.ts with 32 unit tests covering all activation scenarios.

Storage policy and dependency footprint

better-sqlite3 is not adopted. All storage layers added in the v1.11 line use JSONL or JSON files plus proper-lockfile via the existing src/utils/atomic-file.ts helpers (writeFileAtomicSafe, readFileSafe, acquireLock).

The only mandatory native runtime dependency in openchrome-mcp is argon2. One new devDependency added for CI lint: dependency-cruiser@^16. Runtime dependency list is unchanged from v1.10.4:

argon2 ^0.43.0
commander ^12.0.0
jose ^5.10.0
proper-lockfile ^4.1.2
puppeteer-core (rebrowser-puppeteer-core 23.10.3)
uuid ^9.0.0
write-file-atomic ^5.0.1

Top-level npm overrides carried forward from v1.10.4 (PR #742):

"overrides": {
  "basic-ftp": "^5.3.1",
  "ip-address": "^10.1.1"
}

These zero the npm audit --omit=dev production vulnerability count inherited from the @puppeteer/browsers → proxy-agent chain.


Reliability and security carried forward

The v1.10.4 hardening continues unchanged in v1.11.1:

  • HTTP transport fail-closed policy (unauthenticated boots rejected unless allowUnauthenticatedHttp: true is explicitly set)
  • Dashboard REST authz with tenant ownership checks
  • HTTP JSON-RPC batch concurrency bound + RequestQueue timeout clearing
  • File upload root restriction (no arbitrary local paths)
  • Screenshot + vision capture payload guards
  • Same-origin CORS bypass scheme matching + DNS-rebinding defense

New in the v1.11 line:

  • Handoff persistence uses AES-256-GCM with random 12-byte IV per record and 16-byte auth tag. Token filenames are SHA-256 hashed so original tokens never leak via the filesystem.
  • Audit logging is unchanged; SkillStatsResolver walks the audit log in 64 KB chunks with a StringDecoder-backed iterator and builds a lazy AuditIndex keyed strictly by skill_id (prevents sibling contamination).

Cleanup outcomes (the v1.11 backlog burn)

The v1.11 cycle burned through a 35-PR backlog accumulated against four in-flight epics:

  • 35 originally-open PRs all reached terminal state.
  • 27 new PRs merged to develop and main during the cleanup (PR #774 through PR #817 plus this release PR #818).
  • 18 new tracking issues filed. 14 closed by their replacement PRs; 4 remain open for follow-on work (#775 perception voters package, #776 skill curator LLM package, #778 native-deps.md trigger deferred, #779 stale pr/* branch cleanup chore).
  • Three SQLite-backed PRs were rewritten as JSONL/JSON + proper-lockfile per principle P5.
  • Two PRs were redirected to separate-package extraction (#760 → #775, #765 → #776).
  • Zero abandoned work: every closed PR's branch is preserved on the remote (chore #779 will clean up post-release).

Detailed per-PR execution log lives at docs/roadmap/history/openchrome-1.11-cleanup.md. The durable contract remains at docs/roadmap/portability-harness-contract.md.


Verification

  • npm run build: clean (both tsconfig.cli.json and tsconfig.json)
  • npm run lint: clean
  • npm run lint:tier: 0 violations (~310 modules, ~625 dependencies cruised)
  • Full Jest suite: passes on main
  • CI matrix on the release-merge PR: 18/18 green (9 OS×Node combos × 2 base targets)
  • Manual: openchrome serve (no --pilot) returns the v1.10.4 surface plus the 4 new core tools; openchrome serve --pilot registers pilot tools and prints [harness] core+pilot enabled line on stderr

References