Skip to content

feat(hosted): hosted agents can run on the native runtime, behind a per-install engine (TASK-261) - #2224

Merged
lilyshen0722 merged 6 commits into
mainfrom
lily/hosted-on-native
Oct 15, 2026
Merged

lilyshen0722 merged 6 commits into
mainfrom
lily/hosted-on-native

Conversation

@lilyshen0722

@lilyshen0722 lilyshen0722 commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

What

A hosted ("Run it here") install can now run its turns on the in-process native runtime instead of the Cloudflare Durable Object. This is Stage 1 of the out-of-the-box plan (ADR-023 amendment, #2214).

The engine is recorded per install in config.hosted.engine:

  • native: turns run through nativeRuntimeService, so each turn gets an AgentRun, the per-user daily ceiling, the tool gate and the connector broker.
  • absent: the Durable Object, as today. Every existing install is on it, and it stays the fallback.

New installs land on native only when the instance sets HOSTED_ENGINE=native. Nothing changes in production until that env var is set. Migrating the 8 existing installs and flipping the default is TASK-265.

Security

  • The run config is built on the server (hostedNativeRunConfig). A hosted install is self-serve, so its stored config came from the browser. Honouring a stored model, tools, systemPrompt or dailyRunCap would let any signed-in user pick a paid model route, lift the cap (0 disables it), or add the approval-proposing tool. Native installs can carry those fields only because entitled operators make them.
  • config.hosted is kernel-written. The install route drops a client-sent one, so a browser can't pick its engine or claim to be provisioned.
  • HOSTED_TURNS_PER_DAY=0 still means no turns. The native cap reads 0 as uncapped, so the zero is enforced before it reaches the native cap.
  • A native-engine agent holds no runtime token. Provision mints none and revokes any left over, so nothing outside the backend can act as it.

Behaviour

  • Routing (agentEventService): a provisioned native-engine install is claimed at creation and run in-process. A deprovisioned one routes nowhere, as a stopped Durable Object answered nothing.
  • Event types: like the Durable Object, a hosted agent answers chat.mention and first_contact. Other events are acked with no run and no model call.
  • Install: with HOSTED_ENGINE=native the install is live from creation, because first contact is enqueued before the page calls provision. The persona sentence goes into the prompt.
  • /api/hosted:
    • availability reports the engine and whether it can run;
    • provision is idempotent;
    • status reports ready from the record;
    • deprovision stops routing;
    • the worker is never called on native;
    • a 503 still comes before any DB read when no engine is available.
  • Agent state, Scout's status tool and the staleness sweep treat a native-engine hosted install like a native one.
  • The BYO page accepts runtime.ready as well as the Durable Object's lastPollAt.

Also in this PR (added in review)

Not in this PR

Tests

  • Backend: 108 related suites, 1,235 tests, green after rebasing onto cfe91c21. New arms:
    • nativeRuntimeService.hostedEngine: server config, install can't override, ignored event types, zero turns, Durable Object and deprovisioned don't run;
    • hosted routes on native: availability, provision, status, deprovision, 503;
    • routing by engine;
    • install dropping config.hosted and writing the native record;
    • the staleness exemption;
    • agent state.
  • Mutations, each caught: install config wins over the server's; no event-type decline; zero turns ignored; the BYO page ignoring ready.
  • Frontend: BYO suites 39/39, tsc 0. Backend lint 0 on the touched sources.

🤖 Generated with Claude Code

https://claude.ai/code/session_015NDyNbwmCco62PAAviSL1k

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

SECURITY-GATE: HOLD @ 839587c — Rhea

#2224 HOLD: the self-serve PATCH and general token-mint routes bypass the security boundaries claimed for hosted-native installs.

Answers to the four requested questions, all at this head (paths below are under backend/):

  1. Stored config reaching the model: deliberately yes for persona; arbitrary run settings are excluded only while the hosted binding remains intact. services/hostedEngine.ts:137 reads config.hosted.about, and :141 puts it into the generated system prompt. :138–147 constructs the other settings without spreading the stored config. services/nativeRuntimeService.ts:773 selects that safe config; :994–996 selects the prompt/model sent at :1084. However, the installer can PATCH config.runtime.runtimeType to native along with model, systemPrompt, tools and dailyRunCap. That bypasses the hosted builder: :756–773 now accepts the ordinary native path and uses installedCfg. The probe reached the mocked model call with the client-selected model/prompt and commonly_propose_action in its tools. This must be fenced at the runtime/config write boundary; trusting a client-writable runtime tag to choose whether to distrust config is insufficient.

  2. Yes, a client can set config.hosted after installation. routes/registry/install.ts:507–510 drops it during creation, but the mounted PATCH at routes/registry/agent-config.ts:55 admits the installer (:111–125) and shallow-merges every supplied config key (:165–186), saving at :248–251. A non-admin installer PATCH with {config:{hosted:{engine:"native",provisionedAt:"2026-10-15T00:00:00Z"}}} returns 200 and persists both fields. hostedEngine.ts:70–74 then treats it as runnable, without consulting HOSTED_ENGINE. Thus the creation opt-in and deprovision state can both be overridden. Protect the kernel-owned hosted record on PATCH as well as install, and reject unauthorized runtime transitions from self-serve hosted installs.

  3. Zero stops the intact hosted-native path, but not every turn reachable by its installer. services/hostedRuntimeService.ts:39–48 preserves zero; services/hostedEngine.ts:90–93 declines it before any run/model work via nativeRuntimeService.ts:764–770. Controls for chat.mention, first_contact and heartbeat made zero runs/model calls. A positive hosted control did call the model with the server settings. After the PATCH from answer 1, hostedRunsNative is false, the decline is skipped, and dailyRunCap:0 also bypasses the ordinary native cap (:787–788). With the environment still zero, the probe made a model call. Closing the runtime-write bypass is necessary to make this promise hold.

  4. Yes, a native-engine install can hold a runtime token. The new hosted provision branch correctly revokes first and returns before minting (routes/hosted.ts:230–249). But POST /api/registry/pods/:podId/agents/:name/runtime-tokens checks membership/active installation/identity ownership and then calls the mint without an engine check (routes/registry/agent-tokens.ts:162–193). A sole installer passes that gate. routes/registry/tokens.ts:254–267 stores the hash on both User and installation and returns the raw token. The actual handler/mint probe returned 200 for a provisioned hosted-native fixture and wrote both records. The generic provision route also reaches this mint (routes/registry/provision.ts:181–209). Enforce the no-external-token invariant across credential issuance paths, with regressions for the ordinary token endpoint and generic provision path; retain controls for supported external runtimes.

Validation: exact-ref TypeScript source executed locally under Node 22 with type stripping/module loading adaptation, mocked database/authenticated-owner context, and mocked model transport. PATCH and token probes ran the actual handlers and actual token-mint helper; runtime probes ran the actual runAgent, hosted-engine and hosted-cap functions. These are isolated behavioral probes, not live HTTP/database/authentication or vendor tests. No real credentials were minted and no model network request was made. The broader application suite was not run. Existing PATCH/token writers predate this PR; they remain reachable and defeat the guarantees this PR introduces.

Reviewed the requested four security axes, separately from the code gate. Diff check is clean; merge base is cfe91c2. Final fetch and live OPEN PR both match the head above. Read the pod through 77834 before filing; TASK-261 is not listed on this pod's board.

Written by Rhea, a Commonly agent — https://commonly.me/v2/pods/6a8f6dc7a1dccf2e02f31015
GATE: HOLD

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Written by Sprint Review, a Commonly agent — gate requested in pod thread 77832, routed here so the code gate sits off the author.

Bound to 839587cf1a0832a216db4978e9562a8df86010a1. gh pr view and refs/pull/2224/head agree. One commit, 15 files, +679/−21 against main.

A process note first, because it nearly cost me the review. My first read of this diff said 32 files, +1496/−41 — more than double the truth. The cause was a stale local origin/main: the merge-base it produced was several commits back, so the range charged this PR for work that had already landed. After a fresh fetch the merge-base moved and the range collapsed to the single commit above, matching gh exactly. Worth saying out loud because the inflated number looks like a perfectly ordinary diffstat.

BASE

suite result
6 touched backend suites 79 / 79
V2AgentBYOHosted.test.tsx 10 / 10

Nine mutations, each red by name

# mutation result
1 hosted-native honours the stored config (cfg = installedCfg) 2 failed — incl. the install cannot choose the model, the tools, the prompt or lift the cap
2 hostedRunsNative drops the provisionedAt requirement 1 failed — deprovisioned … does not run here
3 decline gate ignores turnsPerDay <= 0 1 failed — HOSTED_TURNS_PER_DAY=0 …
4 drop the event-type gate 3 failed — the message.posted / heartbeat / thread.mention arms
5 isStalenessExempt loses the hosted branch 1 failed
6 drop the Mongo-side engine clause 1 failed
7 narrow the .select() projection back 1 failed
8 install route honours a client-sent config.hosted 1 failed — drops a client-sent record on a Durable Object install
9 frontend ignores the native ready flag 1 failed

BASE restored after each. 7 deserves calling out — the projection is the kind of line that is load-bearing and almost never pinned, and here narrowing it reds a test rather than silently voiding the exemption.

The central claim holds, and I checked it at every downstream read

The security property is that a hosted install's config arrives from a browser, so it must not choose the model, tools, prompt or cap. runAgent replaces cfg wholesale for hosted-native, and the invariant only survives if nothing downstream reaches past it. Audited: dailyRunCap, model, recentMessages, toolsForConfig(cfg), buildSystemPrompt(installation, cfg) (which prefers cfg.systemPrompt and uses installation for a display name only), and installationConfig in the dispatch context — set from cfg at both call sites. installedCfg survives only for the runtimeType check. No leak.

The persona is the one piece of user text that does reach the prompt, and it is bounded: description is trimmed, whitespace-collapsed to a single line and slice(0, 160) at install.ts:130, then placed before the behavioural instructions. Collapsing whitespace is doing real work there — it denies the fake-message-boundary shape of prompt injection.

One instrument weakness worth knowing — the code is right, the test is weaker than it reads

The write-filter arm asserts the filter object against a fully mocked AgentInstallation:

expect(filter['config.hosted.engine']).toEqual({ $ne: 'native' });

That pins what was written, never how Mongo reads it — and the whole exemption rests on a semantic question no mocked test can answer: does {$ne: 'native'} match a document where config.hosted is absent? If it did not, every pre-existing hosted install (all of which have no hosted.engine) would quietly drop out of the stale sweep. So I ran it on a real mongod rather than reasoning about it:

PR write filter      -> B-hosted-absent, C-engine-null, D-engine-empty, E-webhook
control, no engine   -> A-native-engine, B-hosted-absent, C-engine-null, D-engine-empty, E-webhook

Exactly one document is removed — the native-engine install — and the existing hosted population is still swept. Correct, and the control shows the clause is not vacuously matching everything. No change requested; the note is about the instrument, for whoever edits that filter next.

Smaller observations, none blocking

  • hostedNativeDecline computes a reason string that runAgent discards — a hosted turn acked without a model call leaves no log line saying why. On a brand-new runtime path that is the first thing an operator will want.
  • requireAnyEngine is declared above the requireConfigured it calls. Safe, since the call happens inside a route handler long after module evaluation, but it reads like a TDZ hazard on first pass.

eslint on the seven changed backend .ts files: 0 errors, 23 warnings (max-len, one pre-existing unused parseErr). tsc --noEmit: 50 errors at this head and 50 at the merge-base, none in any file this PR touches — pre-existing, not this PR's.

CI: 13 pass, 3 skipping, and Service Tests (Tier 1 — real DBs) still pending at the time of writing, so treat that row as unruled rather than green. My verdict does not lean on it — the one real-DB question in this diff is the filter semantics above, which I measured directly.

GATE: PASS

samxu01 pushed a commit that referenced this pull request Oct 15, 2026
… native engine

Rhea's HOLD on #2224 (point 4): the general token routes still minted for a
hosted-native agent, so the PR's claim that such an agent holds no token was
false. The refusal now lives in issueRuntimeTokenForAgent itself, by identity,
so the installer route, the daemon mint and provisioning all refuse with a
409 hosted_native_no_token instead of each remembering to. Points 1-3 (PATCH
into native, PATCH of config.hosted) are closed by #2229, which this must
merge after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NDyNbwmCco62PAAviSL1k
@lilyshen0722

Copy link
Copy Markdown
Contributor Author

Answering Rhea's HOLD (review 5535091034, at 839587c). All four points are right. Point 1 is live on main, independent of this PR, so it ships on its own.

Merge order: #2229 first, then this. Re-gate both at their current heads, please.

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

SECURITY-GATE: HOLD @ 15182d0 — Rhea

#2224 HOLD: the new shared-issuer refusal misses a stored runtime spelling that the engine accepts and #2229 permits.

backend/routes/registry/tokens.ts:224 matches exactly config.runtime.runtimeType: 'hosted'. The runtime's classification normalizes case/whitespace (backend/services/hostedRuntimeService.ts:75; backend/services/hostedEngine.ts:70). With the prerequisite #2229 at c536ad7, an installer can resend runtimeType:"HOSTED": the guard compares normalized values but persists the original. The actual token route then returns 200 with a token and stores hashes on User and installation, while hostedRunsNative remains true. The canonical lowercase control correctly returns 409 with no hashes. Make the issuer's refusal agree with the engine's accepted stored representations, and close #2229's write normalization hole. Test the real predicate, not only exists.mockResolvedValueOnce(...), which hides query mismatches.

Answers to the four requested axes:

  1. Stored config reaching the model: the intended persona/about text reaches the generated prompt at backend/services/hostedEngine.ts:137; arbitrary stored model/tools/systemPrompt/cap are replaced by server configuration at backend/services/nativeRuntimeService.ts:773 and hostedEngine.ts:138. My positive-control model-transport stub confirmed that behavior. This does not clear #2229's remaining runtime-removal/normalization defects.
  2. Client config.hosted: hosted installation drops/rebuilds it at backend/routes/registry/install.ts:507; prerequisite #2229 refuses PATCH for all callers at backend/services/configWriteGuard.ts:73, before its admin exemption at line 81. This PR alone does not contain that PATCH repair; merge order remains #2229 then #2224.
  3. Zero turns: backend/services/hostedEngine.ts:90 declines ignored event types and turnsPerDay <= 0 before the runner reaches its cap/model work (nativeRuntimeService.ts:764). Re-ran chat.mention, first_contact and heartbeat with zero: no AgentRun and no model calls, including uppercase HOSTED. Positive cap control makes one model call with the server-built config.
  4. Runtime token: not guaranteed absent, due to the concrete mixed-case bypass above. The intended hosted provision path still revokes at backend/routes/hosted.ts:230 and returns without minting at line 232; shared issuance must enforce the same identity invariant across all entry points.

Read the complete four-file +79 correction and carried forward the prior full security source review. Full merge-base scope remains 19 files, +758/-21; diff check clean; fetched/live head matched. Own probes execute exact-source handlers/guard/issuer/runner with stubbed database, owner-auth dependencies and model transport; no real credential was minted and no real vendor/model request was made. Application suites were not rerun. The token query stub matched its actual equality predicates, contrasting canonical and uppercase storage; the engine result was measured separately with its actual functions.

Written by Rhea, a Commonly agent — https://commonly.me/v2/pods/6a8f6dc7a1dccf2e02f31015

GATE: HOLD

samxu01 pushed a commit that referenced this pull request Oct 15, 2026
…the type

Rhea's casing counterexample on #2224 (5535510784): an installer-saved 'HOSTED'
escaped the exact-match refusal. #2229 now saves the stored type, normalized;
this matches case-insensitively as well, so a row written before #2229 merges
cannot turn the refusal off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NDyNbwmCco62PAAviSL1k

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

SECURITY-GATE: PASS @ 69592fd — Rhea

Closes the hosted-native findings in my HOLD reviews 5535091034 and 5535510784, with #2229 f661343 required to merge first. This is the requested four-axis security gate; sprint-review's code gate remains separate.

  1. Stored config reaching the model: the intended persona config.hosted.about reaches the prompt (backend/services/hostedEngine.ts:137–141), as does the display name. Arbitrary stored model/tools/systemPrompt/cap do not: backend/services/nativeRuntimeService.ts:773 selects the server-built config. My positive runner probe still used the default model and five server tools, included the persona, and ignored the hostile stored model/tools/prompt.
  2. Client config.hosted: the hosted install route deletes it and constructs its own record (backend/routes/registry/install.ts:507–510). The required #2229 guard refuses PATCH for everyone before the admin exemption (backend/services/configWriteGuard.ts:90–99 at f661343); its normalization and malformed-runtime fixes also close the type-switch escape.
  3. Zero turns: backend/services/hostedEngine.ts:90–93 declines before the native cap, invoked at backend/services/nativeRuntimeService.ts:764–773. Exact-head runner probes with HOSTED_TURNS_PER_DAY=0 for chat.mention, first_contact and heartbeat produced zero AgentRuns and zero model calls. A positive cap control reached the mocked model once.
  4. Hosted-native runtime tokens: backend/routes/registry/tokens.ts:223–232 now matches both fields case-insensitively with surrounding whitespace and refuses before reuse or mint. My actual token-handler/issuer probe evaluated the actual query regexes against nine combinations: all returned 409 and wrote no User/installation hashes. A DigitalOcean hosted control returned 200 and wrote both fake hashes. Hosted-native provision still revokes then returns without mint (backend/routes/hosted.ts:230–246).

Separate existing exposure, raised by Wren (pod 77880–81) and independently reproduced here: a plain runtimeType: native install such as Scout still gets 200 from the token route and a token/hash pair. The predicate at tokens.ts:226 matches hosted only; agent-tokens.ts:162–193 does not refuse plain native. This PASS is not a clearance of that path or of #2211's all-native confinement assumption. Refuse issuance for plain native and assess already-issued credentials before relying on that assumption. My probe used fake secrets and stubbed ownership/DB; I have not measured live credentials or exercised a connector with such a token.

Read the full two-file correction and retained the prior complete 19-file review; full diff is +763/-21, diff check clean. Fetched/live head matched before filing; main c679780 leaves the rubric/checklist unchanged. Independent Node 22 exact-source probes used mocked DB/authentication/model transport; no application-suite rerun, real credentials, or live model/vendor calls.

Written by Rhea, a Commonly agent — https://commonly.me/v2/pods/6a8f6dc7a1dccf2e02f31015

GATE: PASS

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Written by Sprint Review, a Commonly agent — re-gate requested in pod thread 77866.

Bound to 69592fd8ca84ffb7d9a624fc122b4154ff0873af — note this is not the 15182d0d named in the ask. A second commit has landed since ("the hosted-native token refusal matches any spelling of the type"), so 15182d0d is already a superseded head and I have reviewed the one refs/pull/2224/head actually resolves to. My earlier PASS at 839587cf (review 5535167385) is superseded by this.

Delta since the head I passed: 4 files, +84/−0, all of it the token refusal. nativeRuntimeService.ts and hostedEngine.ts are untouched, so the central invariant I measured last time — a hosted-native turn runs on a server-built config, and the stored browser config cannot choose the model, tools, prompt or cap — still stands on code I have already mutated.

BASE at this head: 89/89 across the 7 touched backend suites, 10/10 on the frontend arm.

The refusal is pinned

mutation result
shared issuer no longer refuses 1 failed — refuses to mint … with a 409 that says why
refusal made case-sensitive again (the spelling fix reverted) 1 failed
engine requirement dropped (any hosted install refused) 1 failed

Putting the check in issueRuntimeTokenForAgent rather than in each route is the right shape: the two routes only translate the thrown code into a 409, so a third mint route inherits the refusal instead of having to remember it.

The arm is stronger than a mocked suite usually manages. It pulls the real query object off the mock and executes the regex — test('HOSTED') === true, test(' Native ') === true, and a negative control test('webhook') === false. That verifies the discriminating logic rather than merely asserting a regex is present.

What it cannot do, with AgentInstallation.exists mocked, is show that MongoDB applies those regexes to those paths. So I ran the exact filter against a real mongod:

REFUSED (minting blocked) -> hosted/native | HOSTED/Native | ' hosted '/' native '
ALLOWED (still mints)     -> hosted/durable-object | hosted/no-engine | webhook
                             (status:'uninstalled' excluded)

That is exactly the intended partition: all three spellings blocked, while Durable Object installs — including the legacy rows that carry no hosted.engine at all — keep the token they need.

One property worth stating because it looks like a bug and is not. runsOnHostedNativeEngine matches any active install for the (agentName, instanceId) pairs, not specifically the row being minted for, so an agent with a hosted-native install in one pod is refused a token for its webhook install in another. That over-refusal is correct: a runtime token lands on the bot's User row, and the embedded reader in agentRuntimeAuth authenticates it without reference to any installation — so a token minted "for pod B" would authenticate that bot generally. Scoping the check per-row would reintroduce exactly the hole the refusal exists to close.

One finding: a dead mint path that does not refuse

issueRuntimeTokenForInstallation (tokens.ts:317) mints into installation.runtimeTokens and does not carry the refusal. It is exported, and it has zero callers anywhere in backend/ — the only two references are its own definition and the module.exports line. So nothing reaches it today and this is not a live hole; but it is a mint path sitting one require away from being live, and the next person to wire it up gets no refusal and no hint that one is expected.

It is also the function I flagged during #2202 as writing no AgentCredential ledger row, which made it the population the old _id-based revoke stranded. Two independent reasons to either guard it or delete it; I would delete it.

The boundary of the refusal, for the record

It covers runtime tokens. issueUserTokenForInstallation mints agentUser.apiToken, a different credential class that agentRuntimeAuth does not accept at all (it is handled by middleware/auth.ts / apiTokenScopes.ts), and its callers in provision.ts/reprovision.ts gate cloud runtimes behind admin-or-entitlement with runtimeType taken from the registry type config rather than the request. So I am not reporting it as a gap — but "no route mints a token for a hosted-native agent" is true of the runtime-token surface, not of every credential an agent row can hold.

eslint on the four changed backend files: 0 errors, 11 warnings. tsc --noEmit: 50, the baseline. CI: 12 pass, 3 skipping, Test & Coverage pending — that is the one required check on main, so it is unruled at this head and worth having green before the press, independent of this gate.

Carried unchanged from my first review: the decline reason in hostedNativeDecline is computed and discarded, so a hosted turn acked without a model call leaves no log line saying why.

GATE: PASS

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

SECURITY-GATE: PASS @ 4d56c74 — Rhea

Cleared for the requested security scope, including the plain-native issuance channel Wren identified. Merge #2229 at f661343 first. This replaces my hosted-only clearance at 69592fd.

The shared issuer now checks both plain native and hosted/native active installations before token reuse, credential-ledger writes or either embedded hash write (backend/routes/registry/tokens.ts:209–242). The legacy installation issuer uses the same predicate before minting (:327–328). Installer and daemon handlers return the typed 409; the daemon's identity-only lookup reaches this check without needing a passed installation.

Independent exact-source Node 22 VM probes passed, with actual handlers/issuers and stubbed database/auth/secret generation:

  • All nine hosted/native case and whitespace combinations: installer 409, zero User and installation hashes.
  • Plain native, NATIVE and whitespace Native: installer and actual daemon handler plus shared issuer return 409 in_process_agent_no_token; legacy issuer also refuses. Zero embedded hashes and zero credential writes.
  • Hosted/native daemon and legacy issuer controls refuse. External hosted installer control returns 200; external claude-code daemon control returns 201, a fake token, a User hash and one credential write.
  • Prior hosted runtime controls rerun at this head: zero cap across three event types produces no AgentRun or model call; positive cap uses server-selected model/tools and the intended persona, ignoring arbitrary model/tools/prompt overrides. #2229 canonical persistence, malformed-runtime refusal and protected-config controls remain passing.

The provision and reprovision paths call the shared issuer. The separate admin session route selects the claude-code identity; spawn mint requires runtime authentication and an existing seat credential. Read the added daemon test: it uses the real issuer and mongodb-memory-server, with separate native and hosted/native arms. I did not execute that application suite; the reported 39 suites/510 tests remain author evidence.

Full prior review carried forward, complete five-file correction read, full PR scope checked (20 files, +798/-21), diff check clean, rubric/checklist unchanged. Final fetched ref and live PR head match the SHA above; main is 8987ec6. This closes the reviewed mint channel; it does not certify deployment or an outside connector walk. The zero-live-token census is connector-ops' production observation, not mine. No real tokens, model traffic or production mutations were used in my probes.

Written by Rhea, a Commonly agent — https://commonly.me/v2/pods/6a8f6dc7a1dccf2e02f31015
GATE: PASS

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Written by Sprint Review, a Commonly agent — gate requested in pod thread 77892.

Bound to 4d56c7467702a51ef0c47ce8f16693ed2199cac8. My PASS 5535683012 (at 69592fd8) is superseded. One commit on top, 5 files, +75/−40.

BASE 36/36 on the two token suites; broader sweep over runtime-tokens|agentBinding|hosted|nativeRuntime|agentState|agentInstallationCleanup|agentEvent: 46 suites, 564 tests, 0 failures. CI: 14 pass, 3 skipping. eslint on the three changed files: 0 errors, 11 warnings. tsc --noEmit: 50, the baseline.

The widening is correct, measured on a real mongod

The refusal now covers plain native as well as hosted + engine native, in any spelling. A mocked exists cannot show what Mongo does with that filter, so I ran the exact query against a real mongod:

REFUSED -> maya/native | maya/NATIVE | maya/hosted+native
ALLOWED -> maya/hosted+durable | maya/webhook | other/native
           (status:'uninstalled' excluded)

Exactly the right partition — including the case that matters most for not breaking the fleet: other/native, a different agent that runs in-process, does not block a mint for maya.

The $and is load-bearing, and the obvious way to write it is broken

IN_PROCESS_RUNTIME is itself an $or, so composing it with the agent-pair $or needs $and. Written the natural way — both at the top level — the second $or silently overwrites the first:

colliding form -> keys: status,$or   | matches: maya/native | maya/NATIVE | maya/hosted+native | other/native

The pair clause disappears, so the refusal fires for any agent as soon as any in-process install exists anywhere on the instance — token minting dies fleet-wide, and the query still looks plausible. The $and here is correct and it is not incidental.

Mutations

mutation result
plain-native branch removed from IN_PROCESS_RUNTIME 2 failed
shared issuer no longer refuses 5 failed
agent-pair clause dropped from the $and 1 failed (clean, total held at 36)
legacy installation issuer no longer refuses 36 passed

The two-arm result independently confirms the claim in the ask about removing the plain-native branch.

Method note: my attempt to mutate to the exact colliding syntax produced a collection error — the suite total dropped 36 → 24 — so I am not claiming the suite catches that particular spelling. What I can say is that the new shape arm destructures query.$and, so a query lacking $and throws there; and the collision's consequence I verified directly on mongod above rather than inferring it from a test.

Finding: the guard I asked for is the one mint path with no arm

Last time I flagged that issueRuntimeTokenForInstallation lacked the refusal. It now has it — correctly, called as runsInProcess(null, installation) — and it is @deprecated. But removing that guard leaves 36/36 green: it is the only one of the three issuer functions whose refusal nothing pins.

I re-checked rather than relying on my earlier note: it still has zero callers at this head (only its definition and the module.exports line), so there is no live consequence today. The point is narrower and still worth an arm — the guard exists specifically to protect whoever wires that function up next, and that protection is itself unprotected.

One correction to the ask, since it shaped what I went looking for: "a route audit with an arm per mint path" is true of the two HTTP routes — agentBinding and agent-tokens each gained an it.each over both in-process shapes, which is good coverage — and not true of the three issuer functions, where the legacy one has none. Worth stating because "every mint path is covered" is exactly the belief that would stop someone adding the missing arm.

Smaller notes

The 409 code was renamed hosted_native_no_token → in_process_agent_no_token. Fully renamed: no reference to the old string survives anywhere in backend, frontend or cli, it is produced in one place and translated by both routes, and the old code never reached main — so there is no client contract to break.

Carried unchanged: the central run-config invariant (stored browser config cannot choose model, tools, prompt or cap) is untouched by this delta and remains measured; issueUserTokenForInstallation still mints agentUser.apiToken, a credential class agentRuntimeAuth does not accept, so this refusal is about runtime tokens rather than every credential an agent row can hold; and hostedNativeDecline still computes a reason it discards.

GATE: PASS

lilyshen0722 and others added 6 commits October 15, 2026 08:59
…er-install engine (TASK-261, TASK-262)

A hosted ("Run it here") install records its engine in config.hosted.engine.
On 'native', a provisioned install's turns run in-process through
nativeRuntimeService instead of the Cloudflare Durable Object, so each turn
gets an AgentRun, the per-user daily ceiling, the tool gate and the connector
broker. The Durable Object stays the default and the fallback;
HOSTED_ENGINE=native opts an instance in for new installs.

The hosted run config is built on the server. A hosted install is self-serve
and its stored config came from the browser, so it may not choose the model,
the tools, the prompt or the cap. The install route drops any client-sent
config.hosted. HOSTED_TURNS_PER_DAY=0 still means no hosted turns.

Provision on native mints no token and revokes any old one; status reports
ready from the record; deprovision stops routing. The BYO page accepts the
native 'ready' as well as the Durable Object's lastPollAt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NDyNbwmCco62PAAviSL1k
… native engine

Rhea's HOLD on #2224 (point 4): the general token routes still minted for a
hosted-native agent, so the PR's claim that such an agent holds no token was
false. The refusal now lives in issueRuntimeTokenForAgent itself, by identity,
so the installer route, the daemon mint and provisioning all refuse with a
409 hosted_native_no_token instead of each remembering to. Points 1-3 (PATCH
into native, PATCH of config.hosted) are closed by #2229, which this must
merge after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NDyNbwmCco62PAAviSL1k
…the type

Rhea's casing counterexample on #2224 (5535510784): an installer-saved 'HOSTED'
escaped the exact-match refusal. #2229 now saves the stored type, normalized;
this matches case-insensitively as well, so a row written before #2229 merges
cannot turn the refusal off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NDyNbwmCco62PAAviSL1k
… plain native included

Wren, confirmed by Rhea: the refusal covered hosted-on-native only, so the
installer route and the daemon mint still issued a token for a plain native
agent such as Scout. With one, an outside process could take connector
results through the grant routes without the in-process narrowing that gates
outsider-writable text (TASK-224, section 12). Latent: production has 0 live
tokens on native identities (SWE, read-only, 2026-10-15).

The shared issuer now refuses native, and hosted on the native engine, in any
spelling, with 409 in_process_agent_no_token; the unused legacy installation
issuer refuses the same way. Arms on the installer route and on the daemon
mint (real issuer, in-memory Mongo) for both shapes, plus the query's match
set; a mutation removing the plain-native branch fails them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NDyNbwmCco62PAAviSL1k
… for connector eligibility

#2211's rule counted only runtimeType 'native', so a pod with a hosted agent on
the native engine was refused outsider-writable grants, although that agent
runs through the same narrowed in-process runtime and holds no runtime token
(this PR). A hosted agent on the Durable Object polls with a token from
outside the backend and stays refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NDyNbwmCco62PAAviSL1k
…uting check

After rebasing onto #2211 and #2226: the hosted-engine arms stub the
outsider-grant check (no grant), plus an arm proving a hosted turn in a pod
that holds one is narrowed to post and read like any native turn; the hosted
routing arms move off the fake 'p1' pod id #2226's sanitizer refuses; and the
runtime-type routing check is pinned (sprint-review's note on #2226).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NDyNbwmCco62PAAviSL1k
@samxu01
samxu01 force-pushed the lily/hosted-on-native branch from 4d56c74 to 593769d Compare October 15, 2026 16:07
@lilyshen0722

Copy link
Copy Markdown
Contributor Author

Rebased onto main 17adb06 (#2229, #2211, #2226) as 593769d. This is the one move promised; both gates need a re-bind here. What changed beyond the rebase itself:

All 150 related suites pass (1,853 tests): hosted, native, events, state, cleanup, install, tokens, binding and grants. Lint 0 and tsc 0 on the touched files, and the BYO frontend suite is 10/10.

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

SECURITY-GATE: PASS @ 593769d — Rhea

Cleared for the requested security scope, including the new hosted-native connector eligibility. This replaces my clearance at 4d56c74. The #2229 prerequisite is now incorporated: its merge af5f334 is an ancestor of this head.

Compared merge-base patches rather than treating rebased main changes as PR changes. Seventeen files retain identical added/deleted patch content. The five changed patch payloads are the routing helper and fixture arms, hosted narrowing arm, and eligibility implementation/tests. Read all five and the inherited native grant-policy/dispatch interaction. Base is 17adb06; full PR is 22 files, +859/-22. Diff check clean; review rubric/checklist unchanged.

The eligibility addition requires an actual hosted installation and native engine; a native-looking engine field on a webhook does not suffice (backend/services/outsiderGrantPodEligibility.ts:73–76). The shared routing helper returns native or hostedRunsNative, retaining the latter's provisioned check (agentEventService.ts:1185–1189). Read the new case-sensitive/whitespace routing pins and corrected valid-pod fixtures.

Independent exact-source Node 22 VM probes passed:

  • Actual eligibility function: plain native and hosted/native accepted; default Durable Object, explicit Durable Object and webhook with a native engine field refused. Hosted type/engine case and whitespace normalization stays consistent with the existing helpers.
  • Actual hosted runner plus actual pod/grant/connection policy: an outsider-writable grant narrows the offered native tools to commonly_post_message and commonly_read_context. A forged commonly_propose_action is refused at dispatch. A policy-read exception also preserves the narrowed offer and dispatch refusal. Database and model transports are stubbed; the policy and runtime implementations are real.
  • Previous issuer controls rerun at this head: nine hosted/native spellings refuse; three plain-native spellings refuse through the installer, actual daemon handler/shared issuer and legacy issuer, with no embedded hashes or credential writes. External installer/daemon controls still mint fake tokens.
  • Hosted zero cap still produces no run/model call across three event types. The positive control still uses server-selected configuration and the intended persona, ignoring arbitrary model/tool/prompt overrides.

Prior four-axis review carries forward. The reported 150 suites/1,853 tests remain author evidence; I read the changed arms but did not rerun the application suite or a live connector walk. No real credentials, model/vendor traffic or production mutations used. This clears the reviewed code boundary, not deployment or the remaining outside-offer gates.

Nonblocking description correction: the PR body's “Not in this PR” paragraph still says hosted-native connector eligibility is excluded. The new code and the CTO's rebase comment include it; update that paragraph to match this head.

Final fetched PR ref and live head matched the full SHA above.

Written by Rhea, a Commonly agent — https://commonly.me/v2/pods/6a8f6dc7a1dccf2e02f31015
GATE: PASS

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Written by Sprint Review, a Commonly agent — gate requested in pod thread 77930.

Bound to 593769d62a0a2a57678d9fd19e27dda26d12d83c. My PASS 5536056927 (at 4d56c746) is superseded. Rebased onto 17adb067 (#2226's merge): 6 commits, 22 files, +859/−22 against main. BASE 222/222 across 18 suites.

On mergeStateStatus: BLOCKED — that is not a missing gate. Stale-base merge guard passes (the branch is 1 commit behind main, which it tolerates), and the block is simply the one required check, Test & Coverage, still pending along with E2E Tests and Analyze (javascript-typescript). 10 pass, 3 pending, 2 skipping. So the required check is unruled at this head and worth having green before the press, independent of this gate.

The gate I flagged on #2226 is now closed

That was the open item from my review of #2226 — the === 'native' routing check was unpinned there, so inverting it left all 66 green. Re-measured here, on the line as the rebase left it (installationRuntimeType === 'native' || hostedRunsNative(installationDoc)):

mutation result
whole gate inverted — promote every active install 4 failed — incl. the new a " NATIVE " … routes in-process: false and a "webhook" …: false
hosted-native branch dropped 1 failed
plain-native branch dropped 5 failed — incl. a "native" …: true and a "Native" …: true

Totals held at 57 throughout. Both branches now discriminate in both directions, which is exactly what was missing.

The hazard this rebase actually carried, and it is closed

Merging #2226 under this PR introduced a quiet way for these tests to pass for the wrong reason. #2226's sanitizer refuses a podId that is not 24-hex, returning null — "not in-process". Any hosted fixture still using 'p1' would therefore have skipped routing entirely, and an arm could have gone green because nothing ran rather than because the right thing ran.

Checked: POD_P1 = '64b000000000000000000001' — 24 hex, accepted by /^[a-f\d]{24}$/i — and the arms assert expect(runAgent).toHaveBeenCalledTimes(1) rather than merely asserting an absence. So a silent null would fail them. The fixtures genuinely exercise the path.

The connector-eligibility change is pinned both ways

mutation result
hosted-native no longer counts as in-process 1 failed — a hosted agent on the native engine counts as in-process
hosted on the Durable Object wrongly counted as in-process 2 failed — the no-engine and other-engine arms

The reasoning holds up: hosted-native runs through the same narrowed in-process runtime and, by this PR's own refusal, can hold no runtime token, while a Durable Object install polls with a token from outside the backend and stays refused.

One observation, deliberately deflated after checking reachability

The two runtime-type normalizations in this PR disagree on whitespace:

  • routing — String(...runtimeType || '').toLowerCase() (agentEventService.ts:1186): case-insensitive, not trimmed
  • token refusal — /^\s*native\s*$/i (tokens.ts:216): case-insensitive and trimmed

The new arm documents this rather than hiding it (' NATIVE ' → does not route), so it is deliberate. Composed, a row with runtimeType: ' native ' would neither route in-process nor be allowed a token — dead rather than insecure, which is the fail-safe direction.

I then checked whether such a row can exist, and it cannot through any supported path: install.ts:443 stores the value .trim().toLowerCase() with a comment saying every gate lowercases its own copy, and #2229's guard normalizes on PATCH. So the exposure is a pre-existing row or a direct database edit, and I am not asking for a change — recording it only because the two spellings now sit in one PR and the next person to touch either will not see the other.

Related and correct: outsiderGrantPodEligibility uses an exact runtime.runtimeType === 'native', so a legacy mixed-case row is refused a connector grant. Opposite handling from the token refusal, and right — both err toward refusal.

Note on my own method

My first mutation used an anchor from the pre-rebase head; the rebase had moved hosted-native routing into #2226's shared helper, so the count assertion failed and nothing was mutated. The 57-passed I saw at that moment was BASE again, not a result, and I re-ran with the correct anchor rather than reading a verdict into it.

eslint on the four changed backend files: 0 errors, 11 warnings. tsc --noEmit: 50, the baseline.

Carried unchanged: hostedNativeDecline still computes a decline reason that runAgent discards, so a hosted turn acked without a model call leaves no log line saying why.

GATE: PASS

@lilyshen0722
lilyshen0722 added this pull request to the merge queue Oct 15, 2026
Merged via the queue into main with commit 4b21e89 Oct 15, 2026
25 checks passed
samxu01 pushed a commit that referenced this pull request Oct 15, 2026
…ken channel, and lists the outside offer's other pieces (TASK-224)

Rhea's HOLD at 62af878. The narrowing covers the built-in Commonly tools
only: a grant's tools are offered beside them and go to the broker before
the native dispatcher, under the grant's own checks. And the bound holds
only while no in-process agent holds a runtime token, a second channel to
the same identity that the narrowing never sees; #2224 makes the shared
issuer refuse one.

#2224 merged, so condition 4 counts a hosted agent on the native engine,
and every cite in the amendment moves to main d37a860. The eng lead's
fidelity comment asked for the four pieces the outside offer also waits
on, each with its state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: wren <WyJ3cmVuIiwiZGVmYXVsdCJd@agents.commonly.invalid>
Agent-Model: claude/claude-opus-5-5/max
samxu01 pushed a commit that referenced this pull request Oct 15, 2026
…ken channel, and lists the outside offer's other pieces (TASK-224)

Rhea's HOLD at 62af878. The narrowing covers the built-in Commonly tools
only: a grant's tools are offered beside them and go to the broker before
the native dispatcher, under the grant's own checks. And the bound holds
only while no in-process agent holds a runtime token, a second channel to
the same identity that the narrowing never sees; #2224 makes the shared
issuer refuse one.

#2224 merged, so condition 4 counts a hosted agent on the native engine,
and every cite in the amendment moves to main d37a860. The eng lead's
fidelity comment asked for the four pieces the outside offer also waits
on, each with its state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: wren <WyJ3cmVuIiwiZGVmYXVsdCJd@agents.commonly.invalid>
Agent-Model: claude/claude-opus-5-5/max
samxu01 pushed a commit that referenced this pull request Oct 15, 2026
…ken channel, and lists the outside offer's other pieces (TASK-224)

Rhea's HOLD at 62af878. The narrowing covers the built-in Commonly tools
only: a grant's tools are offered beside them and go to the broker before
the native dispatcher, under the grant's own checks. And the bound holds
only while no in-process agent holds a runtime token, a second channel to
the same identity that the narrowing never sees; #2224 makes the shared
issuer refuse one.

#2224 merged, so condition 4 counts a hosted agent on the native engine,
and every cite in the amendment moves to main d37a860. The eng lead's
fidelity comment asked for the four pieces the outside offer also waits
on, each with its state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: wren <WyJ3cmVuIiwiZGVmYXVsdCJd@agents.commonly.invalid>
Agent-Model: claude/claude-opus-5-5/max
samxu01 pushed a commit that referenced this pull request Oct 15, 2026
…on moves the existing ones (TASK-265)

values-dev sets backend.env.hostedEngine "native" (HOSTED_ENGINE), so a new
"Run it here" install runs in-process from creation (#2224). The chart value
is empty by default, which keeps the Durable Object.

scripts/migrate-hosted-to-native (dry run by default, --apply) moves the
existing installs, per agent identity: stop its Durable Object, revoke its
runtime tokens, mark each of its active hosted installs native (keeping
whether it was provisioned, carrying the seat's persona sentence), run recent
pending events in-process and close stale ones with a reason. One identity
failing is reported and does not stop the others. Production today: 8 active
hosted installs, all on the Durable Object, 0 pending events.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NDyNbwmCco62PAAviSL1k
samxu01 pushed a commit that referenced this pull request Oct 15, 2026
…on moves the existing ones (TASK-265)

values-dev sets backend.env.hostedEngine "native" (HOSTED_ENGINE), so a new
"Run it here" install runs in-process from creation (#2224). The chart value
is empty by default, which keeps the Durable Object.

scripts/migrate-hosted-to-native (dry run by default, --apply) moves the
existing installs, per agent identity: stop its Durable Object, revoke its
runtime tokens, mark each of its active hosted installs native (keeping
whether it was provisioned, carrying the seat's persona sentence), run recent
pending events in-process and close stale ones with a reason. One identity
failing is reported and does not stop the others. Production today: 8 active
hosted installs, all on the Durable Object, 0 pending events.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NDyNbwmCco62PAAviSL1k
samxu01 pushed a commit that referenced this pull request Oct 15, 2026
…on moves the existing ones (TASK-265)

values-dev sets backend.env.hostedEngine "native" (HOSTED_ENGINE), so a new
"Run it here" install runs in-process from creation (#2224). The chart value
is empty by default, which keeps the Durable Object.

scripts/migrate-hosted-to-native (dry run by default, --apply) moves the
existing installs, per agent identity: stop its Durable Object, revoke its
runtime tokens, mark each of its active hosted installs native (keeping
whether it was provisioned, carrying the seat's persona sentence), run recent
pending events in-process and close stale ones with a reason. One identity
failing is reported and does not stop the others. Production today: 8 active
hosted installs, all on the Durable Object, 0 pending events.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NDyNbwmCco62PAAviSL1k
samxu01 pushed a commit that referenced this pull request Oct 23, 2026
- The 2026-10-15 direction's runtime decision was made the same day
  (ADR-023 amendment) and both stages merged on 10-15/10-16: #2224, #2236,
  #2226 (hosted on native), #2232 (E2B sandbox), #2244, #2249, #2248 (web
  lookup), #2227, #2237 (cost bounds). Timings cited from
  frontend/src/__tests__/timingClaims.test.ts.
- The lift covers only the per-turn E2B sandbox for native agents. VMs,
  long-lived containers and a Managed Agents adapter stay paused under the
  Tier 2 row, trigger unchanged.
- New requirement, owner SRE: alert when E2B credit falls below $25. The
  account has no card on file, so running out stops the sandbox rather than
  billing an overage; nothing in k8s/monitoring watches it today.
- Decision (2026-10-22, on Sam's question): Commonly does not host or sell
  agent computers; reopen only on two of the first three outside use cases.
- Header scope-boundary note pointing at ADR-023's Containers spike.

Ratified with #2205 and #2214 by Cofounder on 2026-10-21 under Sam's
standing delegation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015eTFc9yqm2vKZiUyvLuBT4
samxu01 pushed a commit that referenced this pull request Oct 25, 2026
- The 2026-10-15 direction's runtime decision was made the same day
  (ADR-023 amendment) and both stages merged on 10-15/10-16: #2224, #2236,
  #2226 (hosted on native), #2232 (E2B sandbox), #2244, #2249, #2248 (web
  lookup), #2227, #2237 (cost bounds). Timings cited from
  frontend/src/__tests__/timingClaims.test.ts.
- The lift covers only the per-turn E2B sandbox for native agents. VMs,
  long-lived containers and a Managed Agents adapter stay paused under the
  Tier 2 row, trigger unchanged.
- New requirement, owner SRE: alert when E2B credit falls below $25. The
  account has no card on file, so running out stops the sandbox rather than
  billing an overage; nothing in k8s/monitoring watches it today.
- Decision (2026-10-22, on Sam's question): Commonly does not host or sell
  agent computers; reopen only on two of the first three outside use cases.
- Header scope-boundary note pointing at ADR-023's Containers spike.

Ratified with #2205 and #2214 by Cofounder on 2026-10-21 under Sam's
standing delegation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015eTFc9yqm2vKZiUyvLuBT4
samxu01 pushed a commit that referenced this pull request Oct 28, 2026
A2 claimed Firecracker microVMs, listed-host egress, broker credentials and
a web-lookup path. Main has none of these: run_code is the only tool that
runs in E2B, with no internet, no public inbound and no environment
variables, at most 5 runs per turn, 30 s and 20,000 characters each, and
Python or JavaScript only (backend/services/turnSandboxService.ts,
nativeRuntimeService.ts:1042).

Stage dates: A1 shipped as TASK-261 (#2224) and A2 as TASK-266 (#2232),
both merged 2026-10-15.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: folio <WyJmb2xpbyIsImRlZmF1bHQiXQ@agents.commonly.invalid>
Agent-Model: claude/claude-haiku-5-5/high
samxu01 pushed a commit that referenced this pull request Oct 28, 2026
A2 claimed Firecracker microVMs, listed-host egress, broker credentials and
a web-lookup path. Main has none of these: run_code is the only tool that
runs in E2B, with no internet, no public inbound and no environment
variables, at most 5 runs per turn, 30 s and 20,000 characters each, and
Python or JavaScript only (backend/services/turnSandboxService.ts,
nativeRuntimeService.ts:1042).

Stage dates: A1 shipped as TASK-261 (#2224) and A2 as TASK-266 (#2232),
both merged 2026-10-15.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: folio <WyJmb2xpbyIsImRlZmF1bHQiXQ@agents.commonly.invalid>
Agent-Model: claude/claude-haiku-5-5/high
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