Skip to content

[evergreen] Flue-native agent type — opencomputer (gateway / package / CLI / docs)#489

Draft
ZIJ wants to merge 17 commits into
mainfrom
flue-native
Draft

[evergreen] Flue-native agent type — opencomputer (gateway / package / CLI / docs)#489
ZIJ wants to merge 17 commits into
mainfrom
flue-native

Conversation

@ZIJ

@ZIJ ZIJ commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Evergreen integration branch for the Flue-native agent type — opencomputer lanes (L2 gateway, L4 CLI, L5 @opencomputer/flue, docs).

  • Spec: oc-bg-agents/.agents/design/013-flue-native-agent-type.md + 014-agent-session-contract.md. Plan/status: work/flue-native-buildout.md.
  • Never merge here — Igor merges the evergreen → main as a unit when the slice is complete.
  • Lane agents: open small PRs with base = flue-native (not main).
  • Scope: the productionized gateway (proven in spike/oc-gateway spike(L2): thin per-session gateway over OpenRouter — Contract #1 [DO NOT MERGE] #486, which stays a DO-NOT-MERGE reference); the @opencomputer/flue scaffolding (default app.ts, ocSandbox, registerProvider inside defineAgent, export const route, a prompt-caching-safe default model, publish/repo tools); the CLI deploy path; the re-scaffolded starter + docs.

🤖 Draft evergreen PR.

ZIJ and others added 2 commits July 5, 2026 20:54
Long-lived integration branch for the Flue-native agent type in opencomputer
(L2 gateway, L4 CLI, L5 @opencomputer/flue package, docs). Spec: oc-bg-agents
.agents/design/013 + 014; plan: work/flue-native-buildout.md. Lane PRs target
this branch (base=flue-native); Igor merges the evergreen -> main as a unit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaces the serveOC/brain-box package with the Flue CF-native shape (design 013
§4/§5). A stock `flue build --target cloudflare` app opts into OC via:

- useOcGateway(ctx) + DEFAULT_MODEL (claude-haiku-4.5, prompt-caching-safe): point
  managed anthropic at env.OC_GATEWAY. Called INSIDE the defineAgent initializer
  (top-level is tree-shaken — 1a).
- route: the HTTP-transport opt-in every OC agent must export.
- ocSandbox(env): a durable OC-fleet SandboxApi (files/exec over the OC sandbox
  HTTP API; stat/mkdir/rm via shell, mirroring cloudflareSandbox).
- ocRepoTools(env): publish_pull_request (repo plane, used by W10).
- ./app: default hosting app (flue() + /health + observe→OC_INGEST). ./wire:
  telemetry-only side-effect for apps with their own app.ts.

Builds + typechecks clean against @flue/runtime@1.0.0-beta.9. Verified end-to-end:
the re-scaffolded oc-flue-starter `flue build --target cloudflare`s with zero
hand-editing and the wiring (useOcGateway/registerProvider, ocSandbox, /health,
haiku model) is present in the bundle (not tree-shaken).

TOKEN SEAM — OPEN, needs orchestrator decision (see gateway.ts): Flue's
registerProvider takes only a static apiKey and its getApiKey(providerId) callback
gets no request context, and the provider registry is isolate-global (shared across
co-located DO instances) — so a per-SESSION token/header/baseUrl via registerProvider
RACES. Wired the buildable env shape; robust per-session attribution needs the
upstream `headers(ctx)` ask.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ZIJ and others added 2 commits July 5, 2026 23:45
Productionizes the 1a spike (spike/oc-gateway, #486) into the flue-native
gateway per buildout contract #1 / design 013 §4. Thin OC Worker over
OpenRouter: verify a per-session token → epoch-fence + on-path budget gate
→ inject the org's OR key → cache-safe + usage-accounted forward →
sub-meter. Org spend stays on the org's single OR key → the existing
OpenRouter→Autumn cron (nothing pushed to Autumn); the gateway only adds
per-session sub-metering + enforcement.

What changed vs the spike:
- Token: HS256 → EdDSA (Ed25519). The minter holds the private key; the
  gateway holds only GATEWAY_TOKEN_PUBLIC_KEY (base64url raw). Alg pinned to
  EdDSA (rejects none/HS256 swaps).
- Lease-epoch fence (close the freeze-flags): the SessionBudget DO tracks a
  monotonic max_epoch; a token with a stale `ep` → 401 token_superseded; a
  newer epoch supersedes older in-flight tokens. DO-serialized.
- Org OR key from the credential store, not the KV/SPIKE stand-in: a
  dedicated internal sessions-api seam (mirrors the edge's dedicated-secret
  key hand-off), cached per-org in-isolate (60s). TEST_OR_KEY override for
  the acceptance run. The L3 route (resolveManagedSecret) is flagged for
  sessions-api.
- Prompt-caching safety: strip cache_control for caching-unsafe models
  (claude-3-haiku → Bedrock 400s) via an env-extensible denylist.
- Kept: usage:{include:true}, 402 budget_exceeded, metering by token.sub,
  the µ$ counter + /add idempotency on the OR generation id.

Tests: 23 green (vitest) — EdDSA/alg-pin/tamper/expired, SessionBudget
fence + budget + idempotency, cache_control strip, cost extraction, and the
full on-path flow through the real handler.

Live acceptance (2026-07-05, real OpenRouter via wrangler dev --local,
throwaway $1-capped OR key minted from the provisioning key and deleted
after): a real claude-haiku-4.5 turn completed gateway → OpenRouter (200,
no key in the response, token != key); per-session budget refused on-path
(402); cache_control stripped so claude-3-haiku succeeds via the gateway
where a direct OR call 400s; epoch fence returns 401 token_superseded.

Seam to confirm with the orchestrator (W1): EdDSA claim set + the mint side
(default: per-turn token via getApiKey). L3 seam to build: the internal
org-OR-key route. Not merged — Igor merges opencomputer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…oken, org+agt enforcement)

Reshapes the productionized gateway to the RESOLVED token seam (2026-07-05, option b) and
the co-location refinement, and lands a consistent, compiling tree. The prior commit
(0b5f580) captured a mid-reshape snapshot (new token.ts + old index.ts) that does not
compile; this commit completes the reshape. buildout contract #1 / design 013 §4.

Token seam (was per-session {sub,bud}; now per-DEPLOY):
- token.ts: claims are {org, agt, iat, exp, ep?} — no sub:session, no bud. EdDSA (Ed25519),
  alg-pinned; the minter (W7) holds the private key, the gateway only the public key.
  verifyDeployToken/mintDeployToken/DeployClaims.
- Session identity rides the X-OC-Session request header (Flue's static apiKey can't carry
  per-session data). It is BEST-EFFORT: Flue's provider registry is isolate-global and CF
  co-locates many session-DOs of one script per isolate, so per-session data injected via
  registerProvider races. Therefore hard enforcement is at the race-free org+agt grain.

Enforcement (co-location refinement):
- SpendCounter DO (renamed from SessionBudget; generic keyed µ$ counter + gate). HARD 402 at
  the org+agt grain (keyed agt:${org}:${agt}, from the token). Best-effort per-session
  tracking keyed sess:${X-OC-Session} — recorded for visibility, NEVER gated (a race must not
  wrongly 402 a legit session). Budget looked up server-side (provisioned or the gateway
  default), never carried in the token. Exact per-session enforcement is deferred to an
  upstream Flue per-request resolver (tracked ask, off the critical path).
- DeployLease DO (new): per-(org,agt) lease-epoch floor. A token below the floor is fenced
  (401 token_superseded); the floor auto-rises on a higher-epoch token (rotation) and via
  POST /admin/lease/bump (revoke without redeploy).
- Admin routes (guarded by GATEWAY_ADMIN_SECRET): /admin/agent/budget (provision org+agt cap),
  /admin/lease/bump (revoke).

Unchanged from the spike: org OR key from the dedicated sessions-api seam (orgkey.ts;
TEST_OR_KEY override), usage:{include:true}, cache_control strip for caching-unsafe models,
cost extraction (JSON+SSE), org spend stays on the existing OpenRouter→Autumn cron (gateway
pushes NOTHING to Autumn).

Tests: 31 green (vitest) — EdDSA/alg-pin/tamper/expired/missing-claims, SpendCounter
gate+provision+idempotency, DeployLease fence+bump, org+agt hard enforcement, co-location
(two sessions share the org+agt cap), per-session tracked-but-never-gated, cache_control
strip, admin provision.

Live acceptance (2026-07-06, real OpenRouter via wrangler dev, throwaway $1-capped OR key
minted from the provisioning key and DELETEd after): a real claude-haiku-4.5 turn completed
gateway → OpenRouter (200, model claude-4.5-haiku, cost echoed; no OR key / deploy token in
the response); org+agt budget refused on-path (402 budget_exceeded, spent 0.000088 >
budget 0.00001); a stale lease epoch was fenced (401 token_superseded); no-token and
bad-admin-secret → 401.

opencomputer = PR-only; do NOT merge (Igor merges). Base = flue-native, into #489.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ZIJ and others added 6 commits July 7, 2026 20:57
W6 live bring-up hit three defects W4's bundle-grep acceptance missed.
Fixed at the package layer so the scaffolded starter needs no workarounds.

1. DEFAULT_MODEL dot->dash. `anthropic/claude-haiku-4.5` is absent from
   pi-ai's model catalog, so pi-ai can't derive max output tokens and
   defaults max_tokens=1 -> empty completions. Use the catalog id
   `anthropic/claude-haiku-4-5` (dashes; OpenRouter routes it too).

2. ctx.env empty on the CF build. On `flue build --target cloudflare`
   the real Worker bindings live on the ambient `cloudflare:workers`
   env, not the per-agent `ctx.env` Flue threads in (that is empty for
   OC bindings), so OC_GATEWAY was unset -> the anthropic provider never
   registered ("Unknown model specifier"). New cf-env.ts reads the
   ambient env via a guarded lazy import (falls back to the passed env
   for local dev / node); useOcGateway + ocSandbox read through it.

3. Default `@opencomputer/flue/app` 500s. app.ts mounted flue() from
   `@flue/runtime/routing`; the generated CF entry seeds the runtime via
   configureFlueRuntime from `@flue/runtime/internal` and its no-app.ts
   path builds the app with createDefaultFlueApp() from that same entry.
   Compose via createDefaultFlueApp() so flue()'s module-scoped
   runtimeConfig is the instance the build configures (+ keep /health and
   installOcObserver) -> no "flue() route invoked before runtime was
   configured".

Verified: scaffolded starter `flue build --target cloudflare` + wrangler
dev -> GET /health 200; POST /agents turn 202 -> model call reaches the
gateway with model=claude-haiku-4-5, max_tokens=64000 (not 1); full turn
settles completed with a non-empty agent message.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… turns health panel

Extends the existing Sessions UI (runtime-agnostic; renders flue and brain-box
sessions alike):
- Sessions list: Runtime badge (flue accented) + Spend column, from the session's
  agent_snapshot.runtime + usage. Adds agent_snapshot to the web SessionSchema and
  a display-only flue label to lib/runtimes (kept OUT of the create picker).
- Conversation viewer: distinct rendering for agent.thinking, tool results
  (exec.completed AND flue's tool.result), turn.failed, and a body-spill affordance
  (content_ref/body_truncated). Keeps the Conversation/All level filter + live SSE tail.
- Submission health: new turns panel (GET /v3/sessions/:id/turns) — per-turn state,
  yield_reason, timing, usage, error. Recovery stays cancel + steer + webhook redeliver
  (no fake retry — v3 has no turn re-drive endpoint).
- Per-session spend: Spend/Tokens/Events metric cards on detail, derived defensively
  from the opaque usage object (flue meters at the gateway → renders '—').
- Data layer: getSessionTurns/getSessionResult wrappers + schemas; lib/usage helpers;
  preview mocks extended so the new surfaces render with no backend.

Tier-2 note: /v3 has no streaming-parts/delta mode — tiering is the level field
(internal/progress/user); 'Tier-2 fidelity' = faithful internal-level trace.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@socket-security

socket-security Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​flue/​runtime@​1.0.0-beta.9781009496100
Addednpm/​vitest@​3.2.7981007999100
Addednpm/​@​types/​node@​22.20.11001008196100
Addednpm/​valibot@​1.4.21001009991100
Addednpm/​wrangler@​4.107.0981009296100
Addednpm/​hono@​4.12.28991009796100

View full report

@socket-security

socket-security Bot commented Jul 7, 2026

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm just-bash is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: ?npm/@flue/runtime@1.0.0-beta.9npm/just-bash@3.1.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/just-bash@3.1.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm strtok3 is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: ?npm/@flue/runtime@1.0.0-beta.9npm/strtok3@10.3.5

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/strtok3@10.3.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm typebox is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: ?npm/@flue/runtime@1.0.0-beta.9npm/typebox@1.1.38

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/typebox@1.1.38. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

ZIJ and others added 7 commits July 8, 2026 00:50
Rework the flue branch of `oc agent deploy` for the WfP Durable-Object model
(design 013 §6). Replaces the old oc-flue-build -> dist-oc tar.gz -> R2 presigned
PUT -> {framework_artifact_digest} flow with: credscan (kept) -> `flue build
--target cloudflare` -> POST the built Worker module + generated wrangler to the CP,
which owns compose/mint/WfP-upload/canary-verify/activate. The CLI holds no CF creds
or signing keys.

deployFlue now POSTs /v3/agents/<id>/deployments with input.type=inline +
flue_module={filename,contentB64}, flue_wrangler=<generated wrangler.json object>,
flue_agent_name=<agent.toml name>; activate=!--no-activate. Reuses pollDeployment
(verifying -> ready|failed). Rollback/revisions/status verbs unchanged.

- module filename is read from the generated wrangler's `main` (robust: it must
  equal the CP's metadata.main_module), not hardcoded
- output discovery handles `flue build`'s real nested dist/<app>/ layout (and a flat
  dist/); wrangler.json is strict JSON, parsed + forwarded verbatim
- removed the tar/digest/R2 machinery (bundle, net/http, io/fs)
- test rewritten: TestDeployFlueDoEndToEnd drives the real deployFlue against a fake
  `flue` build + fake CP and asserts the well-formed DO body + verifying->ready poll;
  credscan block test kept. go build/vet/test green, gofmt clean.

NOTE for the orchestrator: real `flue build --target cloudflare` emits a MULTI-module
worker (main index.js + no_bundle + assets/*.js). The current single `flue_module`
contract carries only the entry; the CP-side upload must accept the assets/ tree (a
module map) for the tenant to actually boot. Flagged — CP/contract owns this.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… update)

The oc-deploy contract changed to keep the API host byte-free: no base64 module
bytes in the JSON. deployFlue now tar.gz's the WHOLE `flue build --target cloudflare`
output dir (entry module + the no_bundle assets/ tree), stages it in R2 via the
presigned PUT (POST /v3/agents/:id/artifacts), and POSTs the deployment with
{ flue_bundle_digest: "sha256:<hex of the tar.gz>", flue_wrangler, flue_agent_name }.

- restores the tar/R2 machinery (bundle.Pack/Digest, presigned PUT, already_uploaded
  skip); drops flue_module/contentB64 entirely
- the tar is rooted at the wrangler's dir so wrangler.main (index.js) + assets/ sit at
  the bundle root for the off-host runner's WfP upload
- staging the whole dir resolves the earlier multi-module finding (index.js + the
  assets/*.js chunk tree travel together)
- matches the CP contract on sessions-api flue-w7-deploy (core/flue-do-deploy.ts
  enqueueFlueDoDeploy: byte-free enqueue; digest = sha256 of the tar.gz)
- test rewritten to assert the byte-exact whole-dir upload chain + the byte-free DO
  body (flue_bundle_digest, no flue_module). go build/vet/test green, gofmt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… seam)

The per-deploy OC_SESSION_TOKEN is a secret surfaced on the CF ambient env only
once the request ALS is entered — not at defineAgent-init (DO-construction
scope), where useOcGateway ran and read it falsy -> "No API key for provider:
anthropic". Move the apiKey bind into the exported `route` middleware (run
scope, the same phase ocSandbox reads the token and succeeds), once per isolate.
The token is per-deploy (identical for every session), so the isolate-scoped
provider registry holds one static value with no cross-session race — no
upstream per-request resolver needed. useOcGateway still registers the baseUrl
at init so the model specifier resolves.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Validated end-to-end (live agent turn via the OC gateway). Correction to the
run-scope bind: the per-deploy token secret is NOT on the CF ambient env — it
lives on the per-REQUEST env (c.env), the same source ocSandbox reads. The
route middleware reads OC_SESSION_TOKEN from c.env by DIRECT property access
(never spread c.env — the CF env proxy throws on spread) and OC_GATEWAY from
the ambient snapshot, binding the provider apiKey once per isolate before the
turn model call. Also reconciles the token-seam header comment to the real
mechanism and drops a duplicate doc comment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant