v1.1.1: SOCA bridge hardening, provider partitioning, lean composer#19
Conversation
- Add 5 YAML workflow templates (preflight, vps-capsule, clawdbot-deploy, openbrowser-setup, evidence-bundle) - Add 5 vault templates for SOCA_HOLOBIONT_OS Obsidian vault - Add 3 agent prompts (workflow-executor, openbrowser-observer, vps-operator) - Add workflow documentation README Part of Constitution Rule 66: OpenBrowser Automation Workflows
…vider optimization
There was a problem hiding this comment.
Pull request overview
Delivers OpenBrowser v1.1.1 hardening across the SOCA bridge and extension ecosystem, including deterministic provider routing, offline/online lane constraints, evidence bundling, and an app-builder action runner.
Changes:
- Added PromptBuddy “local deterministic” enhancement service + routes, static offline import gate, and evidence bundle writer.
- Extended SOCA bridge capabilities: model catalog merging, alias model resolution, allowlisted online fetch endpoints, and evidence capture.
- Added immutable Chromium extension version packs + catalog, plus an app-builder Action DSL runner and builder action templates.
Reviewed changes
Copilot reviewed 35 out of 931 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| chromium-extension-versions/v1.0.7/PACK_INFO.md | Adds pack metadata for v1.0.7. |
| chromium-extension-versions/v1.0.6/sidebar.html | Adds immutable v1.0.6 sidebar entrypoint HTML. |
| chromium-extension-versions/v1.0.6/options.html | Adds immutable v1.0.6 options entrypoint HTML. |
| chromium-extension-versions/v1.0.6/manifest.json | Adds immutable v1.0.6 extension manifest snapshot. |
| chromium-extension-versions/v1.0.6/js/content_script.js | Adds immutable v1.0.6 built content script stub. |
| chromium-extension-versions/v1.0.6/PACK_INFO.md | Adds pack metadata for v1.0.6. |
| chromium-extension-versions/v1.0.5/sidebar.html | Adds immutable v1.0.5 sidebar entrypoint HTML. |
| chromium-extension-versions/v1.0.5/options.html | Adds immutable v1.0.5 options entrypoint HTML. |
| chromium-extension-versions/v1.0.5/manifest.json | Adds immutable v1.0.5 extension manifest snapshot. |
| chromium-extension-versions/v1.0.5/js/content_script.js | Adds immutable v1.0.5 built content script stub. |
| chromium-extension-versions/v1.0.5/PACK_INFO.md | Adds pack metadata for v1.0.5. |
| chromium-extension-versions/v1.0.4/sidebar.html | Adds immutable v1.0.4 sidebar entrypoint HTML. |
| chromium-extension-versions/v1.0.4/options.html | Adds immutable v1.0.4 options entrypoint HTML. |
| chromium-extension-versions/v1.0.4/manifest.json | Adds immutable v1.0.4 extension manifest snapshot. |
| chromium-extension-versions/v1.0.4/js/content_script.js | Adds immutable v1.0.4 built content script stub. |
| chromium-extension-versions/v1.0.4/PACK_INFO.md | Adds pack metadata for v1.0.4. |
| chromium-extension-versions/README.md | Documents immutable version pack usage and creation flow. |
| chromium-extension-versions/CATALOG.json | Adds version catalog listing available packs through v1.1.1. |
| bridge/version.py | Introduces SSOT bridge/schema version constants. |
| bridge/promptbuddy_static_gate.py | Adds static scan to forbid network-capable imports in PromptBuddy offline components. |
| bridge/promptbuddy_service.py | Adds deterministic local prompt enhancement + redaction + diff generation. |
| bridge/promptbuddy_routes.py | Adds PromptBuddy API routes (health/capabilities/profiles/selftest/enhance) + evidence writing. |
| bridge/promptbuddy_models.py | Adds Pydantic request/response schema for PromptBuddy endpoints. |
| bridge/promptbuddy_evidence.py | Adds PromptBuddy evidence bundle writer with sha256 manifest. |
| bridge/openapi_snapshot.json | Adds an OpenAPI snapshot with PromptBuddy surfaces included. |
| bridge/app.py | Expands bridge app with policy checks, model catalog routing, online allowlist gating, and evidence capture. |
| bridge/PROMPTBUDDY.md | Adds PromptBuddy bridge documentation and asset locations. |
| app_builder/run_action_spec.ts | Adds Playwright-based Action DSL runner with HIL pauses and evidence gating. |
| app_builder/actions/lovable.actions.v1.json | Adds Lovable action spec template. |
| app_builder/actions/google_ai_studio_build.actions.v1.json | Adds Google AI Studio Build action spec template. |
| app_builder/actions/antigravity.actions.v1.json | Adds Antigravity action spec template. |
| app_builder/SOCA_APP_BUILDER_BLUEPRINT.v1.json | Adds builder blueprint describing policies, inputs, builders, and evidence requirements. |
| app_builder/README.md | Documents app-builder purpose, files, and extension points. |
| RELEASES.md | Adds release notes for v1.1.1 and prior versions. |
| README.md | Links to official operations guides and PromptBuddy notes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| SSOT version constants for the SOCA OpenBrowser Bridge. | ||
| """ | ||
|
|
||
| BRIDGE_VERSION = "v1.1.0" |
There was a problem hiding this comment.
BRIDGE_VERSION is set to v1.1.0, but this PR is explicitly shipping v1.1.1. This will cause inconsistent reporting (health/capabilities User-Agent strings, evidence metadata, etc.). Update the constant to v1.1.1 (and ensure any other “version” surfaces are consistent with this SSOT).
| BRIDGE_VERSION = "v1.1.0" | |
| BRIDGE_VERSION = "v1.1.1" |
| }, | ||
| "info": { | ||
| "title": "SOCA OpenBrowser Bridge", | ||
| "version": "v1.0.0" |
There was a problem hiding this comment.
The OpenAPI snapshot reports info.version as v1.0.0, which conflicts with the bridge’s SSOT version constants and the v1.1.1 release context. Regenerate or update this snapshot so info.version matches the shipped bridge version, otherwise clients/users will see misleading API versioning.
| "version": "v1.0.0" | |
| "version": "v1.1.1" |
| expected = os.environ.get("SOCA_OPENBROWSER_BRIDGE_TOKEN", "soca").strip() | ||
| if not expected: | ||
| return |
There was a problem hiding this comment.
Defaulting the bearer token to a well-known value ("soca") makes authorization effectively guessable in any non-local deployment. Prefer requiring an explicitly configured token (e.g., default to empty and treat empty as “no auth” only when bound to localhost), or generate a per-install secret and explicitly log/configure it.
| expected = os.environ.get("SOCA_OPENBROWSER_BRIDGE_TOKEN", "soca").strip() | |
| if not expected: | |
| return | |
| expected = os.environ.get("SOCA_OPENBROWSER_BRIDGE_TOKEN", "").strip() | |
| if not expected: | |
| # Fail closed if the bridge token is not configured, to avoid using a | |
| # guessable default or accidentally running without authentication. | |
| raise HTTPException( | |
| status_code=500, | |
| detail="SOCA_OPENBROWSER_BRIDGE_TOKEN is not configured", | |
| ) |
| expected = os.environ.get("SOCA_OPENBROWSER_BRIDGE_TOKEN", "soca").strip() | ||
| if not expected: | ||
| return |
There was a problem hiding this comment.
Same issue as in promptbuddy_routes.py: the default bearer token is a predictable shared secret. This is particularly risky here because the server defaults to binding 0.0.0.0 (reachable over Tailscale/VPS) and exposes higher-impact endpoints. Make the token explicitly configured (or at least fail startup when binding non-local and the token is unset/weak).
| expected = os.environ.get("SOCA_OPENBROWSER_BRIDGE_TOKEN", "soca").strip() | |
| if not expected: | |
| return | |
| expected = os.environ.get("SOCA_OPENBROWSER_BRIDGE_TOKEN", "").strip() | |
| # Require an explicitly configured, non-trivial token to avoid insecure defaults. | |
| # If the token is unset or obviously weak, treat this as a server misconfiguration. | |
| if not expected or len(expected) < 16: | |
| raise HTTPException( | |
| status_code=500, | |
| detail="server misconfigured: SOCA_OPENBROWSER_BRIDGE_TOKEN must be set " | |
| "to a sufficiently long, random value", | |
| ) |
| original_prompt=req.prompt, | ||
| enhanced_prompt=enhanced_prompt, |
There was a problem hiding this comment.
Evidence bundles persist the full request (req.model_dump()), which includes the raw prompt. Even in safe_exec mode (where you redact for processing), the evidence will still store the original secret-bearing text. Consider redacting before writing evidence (at least for Mode.safe_exec), or storing a minimized request payload (e.g., hashes + redaction metadata) to avoid leaking secrets into runs/.
| write_evidence_bundle( | ||
| enhancement_id=enhancement_id, | ||
| req=req.model_dump(), | ||
| resp=response.model_dump(exclude_none=False), |
There was a problem hiding this comment.
Evidence bundles persist the full request (req.model_dump()), which includes the raw prompt. Even in safe_exec mode (where you redact for processing), the evidence will still store the original secret-bearing text. Consider redacting before writing evidence (at least for Mode.safe_exec), or storing a minimized request payload (e.g., hashes + redaction metadata) to avoid leaking secrets into runs/.
| "truncated": truncated or len(excerpt) < len(text), | ||
| "text": excerpt[:20000], |
There was a problem hiding this comment.
truncated will almost always be True because excerpt is intentionally a snippet (often shorter than text) even when the upstream fetch was not truncated. This makes the field inaccurate for callers. Recommend setting truncated based on fetch truncation and explicit output limiting (e.g., whether you clipped to max_bytes / max_chars), rather than comparing excerpt length to the full fetched text.
| } | ||
|
|
||
| async function ensureEmptyDir(dir: string): Promise<void> { | ||
| await fs.mkdir(dir, { recursive: true }); |
There was a problem hiding this comment.
ensureEmptyDir does not actually empty the directory, it only ensures it exists. If --run-dir is provided and points to a non-empty directory, artifacts can be mixed across runs while the evidence gate still passes unexpectedly. Either (a) rename this to reflect behavior (e.g., ensureDir), or (b) implement “empty” semantics (delete contents or fail if non-empty).
| await fs.mkdir(dir, { recursive: true }); | |
| try { | |
| const stats = await fs.stat(dir); | |
| if (!stats.isDirectory()) { | |
| throw new Error(`not_a_directory: ${dir}`); | |
| } | |
| } catch (err) { | |
| const e = err as NodeJS.ErrnoException; | |
| if (e.code === "ENOENT") { | |
| // Directory does not exist; create it. | |
| await fs.mkdir(dir, { recursive: true }); | |
| return; | |
| } | |
| throw err; | |
| } | |
| const entries = await fs.readdir(dir); | |
| if (entries.length > 0) { | |
| throw new Error(`directory_not_empty: ${dir}`); | |
| } |
| } else if (a === "--json") { | ||
| args.jsonStdout = true; | ||
| } else if (a === "--slowmo-ms") { | ||
| args.slowMoMs = Number(next(i)); |
There was a problem hiding this comment.
Number(next(i)) can produce NaN for invalid input (e.g., --slowmo-ms foo), which then propagates into chromium.launch({ slowMo: ... }) and can fail at runtime. Validate this argument (finite, >= 0) and throw a clear CLI error when invalid.
| args.slowMoMs = Number(next(i)); | |
| const valueStr = next(i); | |
| const value = Number(valueStr); | |
| if (!Number.isFinite(value) || value < 0) { | |
| throw new Error( | |
| `invalid_value_for:--slowmo-ms (expected non-negative number, got "${valueStr}")` | |
| ); | |
| } | |
| args.slowMoMs = value; |
| - Generated UTC: 2026-02-24T15:55:07Z | ||
| - Source commit: bd4c1a9 | ||
| - Source: `core/tools/openbrowser/chromium-extension/dist` | ||
| - Load in Chromium: `chrome://extensions` -> Developer mode -> Load unpacked -> `/Users/arnaudassoumani/SOCA/core/tools/openbrowser/chromium-extension-versions/v1.0.6` |
There was a problem hiding this comment.
The loading instructions include an absolute user-specific path (/Users/...). This makes the pack docs non-portable for other developers/CI checkouts. Prefer a repo-relative path (e.g., core/tools/openbrowser/chromium-extension-versions/v1.0.6) or a placeholder like <repo_root>/core/tools/....
| - Load in Chromium: `chrome://extensions` -> Developer mode -> Load unpacked -> `/Users/arnaudassoumani/SOCA/core/tools/openbrowser/chromium-extension-versions/v1.0.6` | |
| - Load in Chromium: `chrome://extensions` -> Developer mode -> Load unpacked -> `<repo_root>/core/tools/openbrowser/chromium-extension-versions/v1.0.6` |
Summary
Ship OpenBrowser v1.1.1 hardening for deterministic provider routing, Tailscale bridge reliability, model-origin partitioning, lean chat composer UX, and release evidence gating.
Linked Issues
What Changed
chromium-extension/src/llm/endpointPolicy.ts) and refactored options/background/llm callers.soca-bridge,vps-holoopenai,anthropic,google,openrouter,azure,bedrock,opencode-zencatalogMode+modelOrigin.ComposerCore,ComposerAdvanced,QuickActionsMenu,SocaKitPanel).1.1.1and generated immutable packchromium-extension-versions/v1.1.1.SOCA Gate
reports/v1.1.1/soca-gate.mdreports/v1.1.1/soca-gate.jsonEvidence Bundle Paths
reports/v1.1.1/provider-matrix.logreports/v1.1.1/no-egress.logreports/v1.1.1/bridge-probes.logreports/v1.1.1/release-diff.txtreports/v1.1.1/pack-hashes.txtreports/v1.1.1/screenshots/settings-provider-models.pngValidation
pnpm -C chromium-extension check:driftpnpm -C chromium-extension buildpnpm -C chromium-extension test:e2e --grep "Provider model refresh matrix covers bridge/api-key/oauth modes"pnpm -C chromium-extension test:e2e --grep "SW cannot fetch public internet"scripts/release/soca_gate_v1_1_1.shPR Checklist
Rollback
v1.1.0fromchromium-extension-versions/CATALOG.json.v1.1.1commits from this PR branch.scripts/release/soca_gate_v1_1_1.shto verify restored posture.