Skip to content

fix: inject runtime session key into index.html for published binary#795

Merged
malhotra5 merged 5 commits into
mainfrom
fix/inject-runtime-session-key
May 27, 2026
Merged

fix: inject runtime session key into index.html for published binary#795
malhotra5 merged 5 commits into
mainfrom
fix/inject-runtime-session-key

Conversation

@chuckbutkus
Copy link
Copy Markdown
Contributor

@chuckbutkus chuckbutkus commented May 27, 2026

The globally installed agent-canvas binary starts the agent-server with a persisted session API key (~/.openhands/agent-canvas/session-api-key.txt) as OH_SESSION_API_KEYS_0, making auth required. However, the pre-built static frontend in the npm package has a different (or empty) VITE_SESSION_API_KEY baked in at publish time, so every API request gets 401 Unauthorized.

Fix: static-server.mjs now accepts --session-api-key and injects a tiny bootstrap <script> before in every index.html response. The script seeds the key into localStorage['openhands-agent-server-config'] only if no key is already stored there, so explicit user overrides (via Settings > Agent Server) are always preserved.

dev-with-automation.mjs and dev-static.mjs both pass --session-api-key ${config.sessionApiKey} when spawning the static server, so the runtime key is always available regardless of what was baked into the bundle.

Tests: added 8 new cases to tests/scripts/static-server.test.ts covering parseArgs, injection in direct and SPA-fallback index.html responses, no injection for non-html assets, cache headers, and null key.

  • A human has tested these changes.

Issue Number

#796

How to Test

  1. rm -rf ~/.openhands
  2. docker pull ghcr.io/openhands/agent-canvas:1.0.0-alpha.6
  3. docker run -it --rm
    -p 8000:8000
    -v ~/.openhands:/home/openhands/.openhands
    -v ${PROJECTS_PATH}:/projects
    ghcr.io/openhands/agent-canvas:1.0.0-alpha.6

You should see 401s during the onboarding screens.

  1. rm -rf ~/.openhands
  2. docker pull ghcr.io/openhands/agent-canvas:sha-a44a808
  3. docker run -it --rm
    -p 8000:8000
    -v ~/.openhands:/home/openhands/.openhands
    -v ${PROJECTS_PATH}:/projects
    ghcr.io/openhands/agent-canvas:sha-a44a808

Now it should startup without any issues.

Video/Screenshots

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes


🐳 Docker images for this PR

GHCR package: https://github.com/OpenHands/agent-canvas/pkgs/container/agent-canvas

Component Value
Image ghcr.io/openhands/agent-canvas
Architectures amd64, arm64
Agent Server ghcr.io/openhands/agent-server:1.23.1-python
Automation openhands-automation==1.0.0a5
Commit 0190f14670341e5350ce6f6672f23e590b73ce8a

Pull (multi-arch manifest)

# Multi-arch manifest — Docker automatically pulls the correct architecture
docker pull ghcr.io/openhands/agent-canvas:sha-0190f14

Run

docker run -it --rm \
  -p 8000:8000 \
  ghcr.io/openhands/agent-canvas:sha-0190f14

All tags pushed for this build

ghcr.io/openhands/agent-canvas:sha-0190f14-amd64
ghcr.io/openhands/agent-canvas:fix-inject-runtime-session-key-amd64
ghcr.io/openhands/agent-canvas:pr-795-amd64
ghcr.io/openhands/agent-canvas:sha-0190f14-arm64
ghcr.io/openhands/agent-canvas:fix-inject-runtime-session-key-arm64
ghcr.io/openhands/agent-canvas:pr-795-arm64
ghcr.io/openhands/agent-canvas:sha-0190f14
ghcr.io/openhands/agent-canvas:fix-inject-runtime-session-key
ghcr.io/openhands/agent-canvas:pr-795

About Multi-Architecture Support

  • Each tag (e.g., sha-0190f14) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., sha-0190f14-amd64) are also available if needed

The globally installed agent-canvas binary starts the agent-server with a
persisted session API key (~/.openhands/agent-canvas/session-api-key.txt)
as OH_SESSION_API_KEYS_0, making auth required. However, the pre-built
static frontend in the npm package has a different (or empty)
VITE_SESSION_API_KEY baked in at publish time, so every API request gets
401 Unauthorized.

Fix: static-server.mjs now accepts --session-api-key <key> and injects a
tiny bootstrap <script> before </head> in every index.html response. The
script seeds the key into localStorage['openhands-agent-server-config']
only if no key is already stored there, so explicit user overrides (via
Settings > Agent Server) are always preserved.

dev-with-automation.mjs and dev-static.mjs both pass
--session-api-key ${config.sessionApiKey} when spawning the static server,
so the runtime key is always available regardless of what was baked into
the bundle.

Tests: added 8 new cases to __tests__/scripts/static-server.test.ts
covering parseArgs, injection in direct and SPA-fallback index.html
responses, no injection for non-html assets, cache headers, and null key.

Co-authored-by: openhands <openhands@all-hands.dev>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-canvas Ready Ready Preview, Comment May 27, 2026 2:56pm

Request Review

…n authenticate

The entrypoint computed EFFECTIVE_SESSION_KEY and forwarded it to the
agent-server (OH_SESSION_API_KEYS_0) and automation backends, but did not
pass it to the static-server. As a result the pre-built index.html served
with no session key injected, so every browser API call received 401.

Wire --session-api-key "$EFFECTIVE_SESSION_KEY" into the static-server
launch command so the runtime key is injected into index.html responses
(via the mechanism added in this branch to static-server.mjs).

Co-authored-by: openhands <openhands@all-hands.dev>
@chuckbutkus chuckbutkus marked this pull request as ready for review May 27, 2026 02:50
@chuckbutkus chuckbutkus marked this pull request as draft May 27, 2026 02:51
@chuckbutkus chuckbutkus requested a review from all-hands-bot May 27, 2026 02:51
Copy link
Copy Markdown
Contributor

all-hands-bot commented May 27, 2026

Review complete.

This review was performed through OpenHands Cloud Automation. You can log in and view the conversation here.

Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taste Rating: 🟢 Good taste

Elegant fix for published binary auth failures. Runtime key injection preserves user overrides, includes comprehensive test coverage (8 new cases covering arg parsing, direct/SPA injection, cache headers, null handling), and uses proper security practices (JSON.stringify for escaping, try/catch for graceful degradation).

[RISK ASSESSMENT]

  • [Overall PR] ⚠️ Risk Assessment: 🟢 LOW

Bug fix with comprehensive test coverage, backward compatible (only injects if key not already set), no breaking changes. Cache-Control: no-cache prevents stale keys. Injection wrapped in try/catch for safe localStorage access.


Was this automated review useful? React with 👍 or 👎 to this review to help us measure review quality.
Workflow run: https://github.com/OpenHands/agent-canvas/actions/runs/26487809286

@chuckbutkus chuckbutkus marked this pull request as ready for review May 27, 2026 02:55
Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review — fix: inject runtime session key into index.html for published binary

Solid, targeted fix for the published binary auth problem. The overall approach is well-executed:

  • JSON.stringify for safe key escaping — no XSS risk from the key value itself
  • localStorage guard (if(!_c.sessionApiKey)) correctly preserves explicit user overrides
  • Cache-Control: no-cache on injected responses prevents browsers from caching the stale (un-injected) page
  • Content-Length uses buf.length (byte count) rather than character count — correct for UTF-8
  • Test coverage is thorough and the 8 new cases cover the meaningful paths

A couple of minor points inline.

This review was generated by an AI agent (OpenHands) on behalf of the user through OpenHands Automation. View conversation

Comment thread scripts/static-server.mjs
Comment thread scripts/static-server.mjs
Comment thread docker/entrypoint.sh
Comment thread __tests__/scripts/static-server.test.ts
@chuckbutkus
Copy link
Copy Markdown
Contributor Author

@OpenHands, address the all-hands-bot review suggestions

@openhands-ai
Copy link
Copy Markdown

openhands-ai Bot commented May 27, 2026

I'm on it! chuckbutkus can track my progress at all-hands.dev

- static-server.mjs: add comment clarifying replace() targets first
  </head> only; fall back to inserting before </body> when </head> is
  absent (avoids prepending before <!DOCTYPE html>)
- docker/entrypoint.sh: add comment documenting source of
  EFFECTIVE_SESSION_KEY before the static-server invocation
- static-server.test.ts: add test for </head>-absent fallback path
  confirming injection lands before </body>

Co-authored-by: openhands <openhands@all-hands.dev>
@openhands-ai
Copy link
Copy Markdown

openhands-ai Bot commented May 27, 2026

Summary of Changes

All four all-hands-bot review suggestions on PR #795 were addressed in commit f4691885. Here's a checklist:

✅ Completeness

  • scripts/static-server.mjs — clarify replace() intent: Added comment // replace() targets the first (and only) </head> in well-formed HTML.
  • scripts/static-server.mjs — fix </head>-absent fallback: Changed from prepending before <!DOCTYPE html> to inserting before </body> (with the raw-prepend as a last resort only)
  • docker/entrypoint.sh — document EFFECTIVE_SESSION_KEY source: Added a standalone comment line above the node invocation (inline bash comments after \ continuations are syntactically invalid, so a comment line was used instead)
  • __tests__/scripts/static-server.test.ts — add fallback path test: Added "injects session key into HTML without </head> tag (falls back to </body>)" which verifies the key appears before </body> and not at position 0
  • All 11 tests pass
  • All 4 review threads replied to (with commit reference) and resolved
  • Changes pushed to fix/inject-runtime-session-key

✅ Conciseness

The changes are minimal and surgical — exactly 3 files touched, with only what was needed to satisfy the review feedback. No unrelated changes were made.

Copy link
Copy Markdown
Contributor

all-hands-bot commented May 27, 2026

Review complete.

This review was performed through OpenHands Cloud Automation. You can log in and view the conversation here.

Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taste Rating: 🟢 Good taste

Elegant fix for the published binary auth issue. The runtime key injection is minimal, secure, and well-tested.

Highlights:

  • Solves real production problem (globally installed binaries getting 401s)
  • Security best practices: JSON.stringify escaping, Cache-Control: no-cache, try/catch graceful degradation
  • Preserves user overrides (if(!_c.sessionApiKey) guard)
  • Comprehensive test coverage (8 new cases covering parseArgs, injection paths, cache headers, edge cases)
  • All previous review comments addressed ( fallback, entrypoint comment, replace() clarification)

[RISK ASSESSMENT]

  • [Overall PR] ⚠️ Risk Assessment: 🟢 LOW

Bug fix with comprehensive test coverage, backward compatible (only injects when key provided, preserves user-set overrides), no breaking changes, secure implementation (proper escaping + cache control), all integration points updated.

VERDICT:
Worth merging: Solves a real production bug with a clean, minimal implementation

KEY INSIGHT:
The build-time vs. runtime key mismatch is solved at exactly the right layer — serve-time injection lets the pre-built bundle work with any runtime key without rebuilding.


Was this automated review useful? React with 👍 or 👎 to this review to help us measure review quality.
Workflow run: https://github.com/OpenHands/agent-canvas/actions/runs/26491108264

Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Good taste

Clean, targeted fix for the published binary auth issue. The review_requested event triggered this review; all prior inline feedback has been addressed by the author.


Summary of changes:

  • scripts/static-server.mjs: Adds --session-api-key <key> CLI flag. makeConfigInjectionScript emits a minimal IIFE that seeds the runtime key into localStorage['openhands-agent-server-config'] only if no key is already present — user overrides via Settings are always preserved. serveInjectedIndexHtml injects the script before </head>, falling back to </body>, then a last-resort prepend. Cache-Control: no-cache is set on injected responses; Content-Length is computed from the byte buffer (not character count) — both correct.
  • docker/entrypoint.sh: Passes $EFFECTIVE_SESSION_KEY (defined at line 84 from session-api-key.txt or $SESSION_API_KEY) to the static server. An explanatory comment was added (inline shell comments after \ continuation are invalid bash syntax, so the comment precedes the node call — correct choice).
  • scripts/dev-static.mjs / dev-with-automation.mjs: Both conditionally spread --session-api-key only when config.sessionApiKey is truthy — no behavioral change when the key is absent.
  • Tests: 8 new cases covering parseArgs defaults, --session-api-key parsing, empty-string coercion to null, direct injection, SPA-fallback injection, non-HTML asset passthrough (no injection), Cache-Control: no-cache header, null-key behavior, and the </body>-fallback path for HTML without </head>.

Prior inline comments — all resolved:

Comment Resolution
</head>-absent fallback prepended before <!DOCTYPE html> Fixed: intermediate </body> fallback added; test added
String.replace() replaces first occurrence — add comment Fixed: clarifying comment added above the replace() call
EFFECTIVE_SESSION_KEY definition not visible in diff context Fixed: comment added; variable confirmed defined at line 84
Test for </head>-absent path missing Fixed: test added with <html><body>no-head</body></html> input

[RISK ASSESSMENT]

  • [Overall PR] ⚠️ Risk Assessment: 🟢 LOW
    Backward compatible bug fix — no injection occurs unless --session-api-key is explicitly passed, and the localStorage guard prevents overwriting user-set keys. JSON.stringify escaping is correct (the key is system-generated, alphanumeric). Comprehensive test coverage.

VERDICT:
Worth merging: Solves a real production 401 problem with a clean, minimal implementation. All prior review feedback addressed.

KEY INSIGHT:
Serve-time key injection is the correct layer to fix the build-time vs. runtime key mismatch — the pre-built bundle remains static; the runtime key is woven in at request time without rebuilding.
This review was generated by an AI agent (OpenHands) on behalf of the user through OpenHands Automation. View conversation

Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Good taste

Clean, targeted fix for the published binary auth issue. The review_requested event triggered this review; all prior inline feedback has been addressed by the author.


Summary of changes:

  • scripts/static-server.mjs: Adds --session-api-key <key> CLI flag. makeConfigInjectionScript emits a minimal IIFE that seeds the runtime key into localStorage['openhands-agent-server-config'] only if no key is already present — user overrides via Settings are always preserved. serveInjectedIndexHtml injects the script before </head>, falling back to </body>, then a last-resort prepend. Cache-Control: no-cache is set on injected responses; Content-Length is computed from the byte buffer (not character count) — both correct.
  • docker/entrypoint.sh: Passes $EFFECTIVE_SESSION_KEY (defined at line 84 from session-api-key.txt or $SESSION_API_KEY) to the static server. An explanatory comment was added (inline shell comments after backslash continuation are invalid bash syntax, so the comment precedes the node call — correct choice).
  • scripts/dev-static.mjs / dev-with-automation.mjs: Both conditionally spread --session-api-key only when config.sessionApiKey is truthy — no behavioral change when the key is absent.
  • Tests: 8 new cases covering parseArgs defaults, --session-api-key parsing, empty-string coercion to null, direct injection, SPA-fallback injection, non-HTML asset passthrough (no injection), Cache-Control: no-cache header, null-key behavior, and the </body>-fallback path for HTML without </head>.

Prior inline comments — all resolved:

Comment Resolution
</head>-absent fallback prepended before <!DOCTYPE html> Fixed: intermediate </body> fallback added; test added
String.replace() replaces first occurrence — add clarifying comment Fixed: comment added above the replace() call
EFFECTIVE_SESSION_KEY definition not visible in diff context Fixed: comment added; variable confirmed defined at line 84
Test for </head>-absent path missing Fixed: test added with <html><body>no-head</body></html> input

[RISK ASSESSMENT]

  • [Overall PR] ⚠️ Risk Assessment: 🟢 LOW
    Backward compatible bug fix — no injection occurs unless --session-api-key is explicitly passed, and the localStorage guard prevents overwriting user-set keys. JSON.stringify escaping is correct (the key is system-generated, alphanumeric). Comprehensive test coverage.

VERDICT:
Worth merging: Solves a real production 401 problem with a clean, minimal implementation. All prior review feedback addressed.

KEY INSIGHT:
Serve-time key injection is the correct layer to fix the build-time vs. runtime key mismatch — the pre-built bundle remains static; the runtime key is woven in at request time without rebuilding.

This review was generated by an AI agent (OpenHands) on behalf of the user through OpenHands Automation. View conversation

Copy link
Copy Markdown
Member

@malhotra5 malhotra5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

I'm guessing this worked for some people because their localstorage already had session api key stored from prior sessions?

@github-actions
Copy link
Copy Markdown
Contributor

📸 Snapshot Test Report

Warning

Snapshot comparison step crashed (timeout, OOM, or runner error) — diff results below may be incomplete or absent.
Check the CI logs for the full error output (look for the "Run snapshot comparison" step).

✅ All snapshots match the main branch baselines.

Category Count
🔴 Changed 0
🆕 New 0
✅ Unchanged 73
Total 73
✅ Unchanged snapshots (73)

archived-conversation

  • conversation-panel-with-archived-badges
  • conversation-view-archived
  • conversation-view-sandbox-error

automations

  • automations-delete-modal
  • automations-list-active-inactive
  • automations-no-automations
  • automations-search-no-results

backends-extended

  • backend-add-blank-disabled
  • backend-add-cloud-advanced-open
  • backend-add-cloud-no-key-disabled
  • backend-add-cloud-with-key-enabled
  • backend-add-form-partially-filled
  • backend-add-invalid-url-disabled
  • backend-add-local-ready
  • backend-add-name-only-disabled
  • backend-add-two-column-layout
  • backend-add-whitespace-host-disabled
  • backend-after-switch
  • backend-cancel-nothing-saved
  • backend-dropdown-two-backends
  • backend-edit-prefilled
  • backend-manage-after-removal
  • backend-manage-two-listed
  • backend-remove-cancelled
  • backend-remove-confirmation
  • backend-switch-overlay

backends

  • backend-add-modal
  • backend-manage-modal
  • backend-selector-open

changes-tab

  • changes-deleted-file
  • changes-diff-viewer
  • changes-empty

collapsible-thinking

  • reasoning-content-collapsed
  • reasoning-content-expanded
  • think-action-collapsed
  • think-action-expanded

mcp-page

  • mcp-custom-server-1-editor-open
  • mcp-custom-server-2-url-filled
  • mcp-custom-server-3-all-filled
  • mcp-custom-server-4-installed
  • mcp-custom-server-editor
  • mcp-empty-installed
  • mcp-search-filtered
  • mcp-slack-install-1-marketplace
  • mcp-slack-install-2-modal
  • mcp-slack-install-3-filled
  • mcp-slack-install-4-installed

onboarding

  • onboarding-step-0-choose-agent
  • onboarding-step-1-check-backend
  • onboarding-step-2-setup-llm
  • onboarding-step-3-say-hello

projects-workspace-browser

  • projects-workspace-browser

settings-page

  • add-backend-modal
  • analytics-consent-modal
  • home-screen
  • settings-app-page
  • settings-page

settings-secrets

  • secrets-add-form-filled
  • secrets-add-form
  • secrets-after-save
  • secrets-delete-confirm
  • secrets-list

settings-verification

  • condenser-settings
  • verification-settings-off
  • verification-settings-on

sidebar

  • sidebar-collapsed
  • sidebar-conversation-panel
  • sidebar-filter-menu

skills-page

  • skills-empty
  • skills-loaded
  • skills-no-match
  • skills-search-filtered
  • skills-type-filter

Generated by the Snapshot Tests workflow. This comment was created by an AI agent (OpenHands) on behalf of the repo maintainers.

@malhotra5
Copy link
Copy Markdown
Member

@OpenHands can you figure out why the app was working for some people and not for others? understand the fix made here in order to determine this

@openhands-ai
Copy link
Copy Markdown

openhands-ai Bot commented May 27, 2026

I'm on it! malhotra5 can track my progress at all-hands.dev

Copy link
Copy Markdown
Member

Why It Worked for Some People and Not Others

(This comment was generated by an AI agent, OpenHands, on behalf of the user.)


The bug came down to a single asymmetry: npm run dev (Vite mode) re-compiles the bundle on every startup, while the published npm binary / Docker image serves a pre-built, frozen artifact.

The two code paths

VITE_SESSION_API_KEY priority in src/api/agent-server-config.ts:

1. localStorage['openhands-agent-server-config'].sessionApiKey  ← user-set / injected at runtime
2. import.meta.env.VITE_SESSION_API_KEY                         ← baked in at build time

npm run dev — worked every time ✅

When the launcher starts the stack in Vite mode, it passes the persisted session key as VITE_SESSION_API_KEY in the Vite process's environment. Vite re-substitutes this into the bundle on each startup, so the baked-in default always matched the key the agent-server was configured with. These two always agreed.

Published npm binary / Docker image — broken by default ❌

The build/ directory is created once at publish time (in CI). At that point no real runtime key exists, so VITE_SESSION_API_KEY is empty — it gets compiled into the bundle as an empty string. Then, when a user runs agent-canvas:

  1. The launcher generates (or reads) a random persisted key from ~/.openhands/agent-canvas/session-api-key.txt
  2. The agent-server starts with OH_SESSION_API_KEYS_0=<that-key>auth is now required
  3. The static server serves the frozen build/ files unchanged
  4. The browser loads the pre-built JS, which has VITE_SESSION_API_KEY = "" baked in
  5. localStorage is empty for a new user → no key override
  6. Every API request goes out with no X-Session-API-Key header → 401 Unauthorized

Why it still worked for some static-mode users

Three narrow cases let a user escape the bug before the fix:

Scenario Why it worked
User had previously run npm run dev The correct key was already stored in localStorage from a prior dev session. The static binary re-used the same persisted session-api-key.txt, so the agent-server key matched what was in the browser's storage.
User had manually configured the key in Settings > Agent Server The UI's settings form writes directly to localStorage['openhands-agent-server-config'].sessionApiKey, which takes priority over the baked-in empty value.
Agent-server was started with no auth requirement Only if OH_SESSION_API_KEYS_0 was somehow not set (never happens in practice because the launcher always generates one).

What the fix does

static-server.mjs now accepts --session-api-key <key> and injects a tiny bootstrap <script> before </head> in every index.html response:

(function(){
  var _k = 'openhands-agent-server-config',
      _c = JSON.parse(localStorage.getItem(_k) || '{}');
  if (!_c.sessionApiKey) {          // ← only seed if not already set
    _c.sessionApiKey = "<runtime-key>";
    localStorage.setItem(_k, JSON.stringify(_c));
  }
}());

This closes the gap: on the very first page load (empty localStorage), the correct runtime key is seeded into storage before any app code runs. Subsequent requests use that key and authenticate successfully. The if (!_c.sessionApiKey) guard means any key the user has explicitly configured through the UI is never overwritten.

The Docker entrypoint and both static-mode launcher scripts (dev-with-automation.mjs, dev-static.mjs) were updated to pass --session-api-key ${effectiveSessionKey} to the static server, completing the chain.

@openhands-ai
Copy link
Copy Markdown

openhands-ai Bot commented May 27, 2026

Summary

This was a question to answer, not a code change request. Here's what I did:

Task: Explain why the app worked for some users and not others, based on understanding the fix in PR #795.

Answer delivered (posted as a comment on PR #795):

The root cause was a build-time vs. runtime key mismatch with a single asymmetry at its heart:

  • npm run dev (Vite mode) always worked — Vite re-compiles the bundle on every startup with the current VITE_SESSION_API_KEY, so the frontend and agent-server always shared the same key.

  • Published npm binary / Docker image broke for new users — The build/ directory is frozen at publish time with VITE_SESSION_API_KEY = "" (no runtime key in CI). At runtime, the launcher generates a new random persisted session key and starts the agent-server with it, but the static frontend still sends requests with an empty key → 401 Unauthorized.

Who escaped the bug without the fix:

  1. Users who had previously run npm run dev — the correct key was already seeded in their localStorage from a prior dev session
  2. Users who had manually set the key via Settings > Agent Server — that writes directly to localStorage, overriding the empty baked-in value

The fix closes the gap by having static-server.mjs inject a bootstrap <script> into every index.html response that seeds the runtime key into localStorage on first page load (with a guard that preserves any key the user has explicitly configured).

@malhotra5 malhotra5 merged commit c997566 into main May 27, 2026
15 checks passed
@malhotra5 malhotra5 deleted the fix/inject-runtime-session-key branch May 27, 2026 15:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants