[codex] cut over OS streams runtime#1370
Merged
Merged
Conversation
f500f88 to
6b47757
Compare
6b47757 to
91e9017
Compare
91e9017 to
879a9fd
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 879a9fd. Configure here.
879a9fd to
194ff16
Compare
194ff16 to
2f6cd72
Compare
This was referenced Jun 5, 2026
jonastemplestein
added a commit
that referenced
this pull request
Jun 5, 2026
Slack-routed agent streams never registered the LLM processors (agent-chat/agent/provider), so a user message landed as an agent/input-added event that nothing consumed — the agent never replied. Regression from #1370 (streams runtime cutover): the routed bootstrap used to subscribe a callable to AgentDurableObject.afterAppend (which woke the agent and registered its processors via onInstanceWake); the cutover replaced it with a built-in agent-host processor that never wakes the agent for its own stream. What actually starts a processor is the subscription-configured event, which was never appended for the LLM processors on routed streams. Fix: - agent-host now wakes the AgentDurableObject for its own stream on stream/created (ensureAgentRunnerForOwnStream); onInstanceWake registers agent-chat/agent/LLM + setup events, whose subscription-configured events Stream#reconcileOutboundConnections then dials into runners. Verified the runner replays from offset 0, so agent-host reliably sees stream/created (always offset 1). - Align the bootstrap agent-host subscription key with the canonical AgentDurableObject key so the two declarations dedupe to a single runner. - Use the new-runtime event prefix (events.iterate.com/stream/) in the OS call-sites that compare against new-runtime core events: the child-stream-created check (was the legacy /core/ prefix, which the new runtime never emits), the project agents-root jsonata matcher, and the stream-composer UI examples. Scope note: a broader /core/ -> /stream/ rename across the shared package and the separate events.iterate.com app was reverted — it broke the events runtime e2e (append 500s) and is unrelated to this bug. Tracked as a follow-up. Verified: full-repo typecheck, oxlint, oxfmt, affected OS unit tests pass. Not yet deployed/round-tripped on a preview. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jonastemplestein
added a commit
that referenced
this pull request
Jun 5, 2026
Slack-routed agent streams never registered the LLM processors (agent-chat/agent/provider), so a user message landed as an agent/input-added event that nothing consumed — the agent never replied. Regression from #1370 (streams runtime cutover): the routed bootstrap used to subscribe a callable to AgentDurableObject.afterAppend (which woke the agent and registered its processors via onInstanceWake); the cutover replaced it with a built-in agent-host processor that never wakes the agent for its own stream. What actually starts a processor is the subscription-configured event, which was never appended for the LLM processors on routed streams. Fix: - agent-host now wakes the AgentDurableObject for its own stream on stream/created (ensureAgentRunnerForOwnStream); onInstanceWake registers agent-chat/agent/LLM + setup events, whose subscription-configured events Stream#reconcileOutboundConnections then dials into runners. Verified the runner replays from offset 0, so agent-host reliably sees stream/created (always offset 1). - Align the bootstrap agent-host subscription key with the canonical AgentDurableObject key so the two declarations dedupe to a single runner. - Use the new-runtime event prefix (events.iterate.com/stream/) in the OS call-sites that compare against new-runtime core events: the child-stream-created check (was the legacy /core/ prefix, which the new runtime never emits), the project agents-root jsonata matcher, and the stream-composer UI examples. Scope note: a broader /core/ -> /stream/ rename across the shared package and the separate events.iterate.com app was reverted — it broke the events runtime e2e (append 500s) and is unrelated to this bug. Tracked as a follow-up. Verified: full-repo typecheck, oxlint, oxfmt, affected OS unit tests pass. Not yet deployed/round-tripped on a preview. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jonastemplestein
added a commit
that referenced
this pull request
Jun 5, 2026
## What was broken Slack agents stopped responding. A user `@mention` becomes an `agent/input-added` event on the routed agent stream, but **nothing consumes it** — the LLM processors (`agent-chat` / `agent` / the provider processor) were never registered on Slack-routed streams. Observed live on `templestein2` stream `/agents/slack/c09trdv61v4/ts-1780670924-517029`: `slack-agent` ran (produced the input), but no `agent`/LLM processor existed, so no reply. ## Root cause — regression from #1370 (streams runtime cutover) - **Old runtime:** `routedStreamBootstrapEvents` subscribed a *callable* to `AGENT` DO `afterAppend`. Invoking it woke `AgentDurableObject` → `onInstanceWake` registered `agent-chat`/`agent`/LLM/`agent-host` + seeded setup events. - **After #1370:** that was replaced with a built-in `agent-host` processor whose `afterAppend` only runs `ensureChildAgentRunner` (+ codemode handlers). It never wakes the agent for its own stream, so the LLM processors are never registered. - What actually *starts* a processor is the `subscription-configured` event (`Stream#reconcileOutboundConnections` dials a runner per subscription key). Those were never appended for the LLM processors on routed streams. - Compounding: `ensureChildAgentRunner` compared against the legacy `events.iterate.com/core/child-stream-created`, but the new runtime emits `events.iterate.com/stream/child-stream-created`. Dashboard-created agents were unaffected because `new.tsx` explicitly subscribes the full processor set. **PR #1371 would not have fixed this** — it only adds `stream-processor-registered` *marker* events (which don't start processors) and only to the UI flow. ## The fix - **Wake the agent for routed streams** (`ensureAgentRunnerForOwnStream`): when `agent-host` runs on a routed agent stream, on the `stream/created` event it initializes that stream's `AgentDurableObject` → `onInstanceWake` registers the LLM processors and setup events; the resulting `subscription-configured` events are what `reconcileOutboundConnections` dials. Verified the runner replays from offset 0 (`replayAfterOffset: snapshot?.offset ?? 0`), so `agent-host` reliably sees `stream/created` (always offset 1). - **Dedupe the agent-host runner:** align the bootstrap `agent-host` subscription key with the canonical `AgentDurableObject` key (runner DOs are keyed by `${namespace}:${path}:${subscriptionKey}`), so the two declarations resolve to one runner. - **Use the new-runtime `events.iterate.com/stream/` prefix** at the OS call-sites that compare against new-runtime core events: the broken `child-stream-created` check (local constants), the project agents-root jsonata matcher, and the stream-composer UI examples. ## Verification - Full-repo `pnpm typecheck` (18 projects), `oxlint`, `oxfmt` - Affected OS unit tests pass - Not yet deployed / round-tripped on a preview — recommend a preview deploy + one test `@mention` before prod (`Preview / e2e` is otherwise skipped on PRs). ## Scope note An earlier revision also did a broad `events.iterate.com/core/` → `/stream/` rename across the shared package and the separate **events.iterate.com** app. That **broke the events runtime e2e** (append → 500) and is unrelated to this bug, so it was reverted out of this PR. If we still want the events platform on the `/stream/` prefix it needs its own investigation — tracked as a follow-up. ## Follow-ups (found while auditing #1370) - `ProjectDurableObject.afterAppend` is **orphaned**: the old runtime forwarded lifecycle events to the project config-worker `afterAppend` hook; the new built-in `project-lifecycle` processor has no `afterAppend` and nothing calls it. - `CodemodeSession.afterAppend` is orphaned (likely benign — resolves locally via `appendAndConsume`). - `agents.e2e.test.ts` has vacuous `/core/error-occurred` assertions; worth real Slack-path e2e coverage so this can't regress silently. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CLOUDFLARE_PREVIEW --> ## Environment Config Lease <!-- CLOUDFLARE_PREVIEW_STATE --> <!-- { "apps": { "os": { "appDisplayName": "OS", "appSlug": "os", "status": "deployed", "updatedAt": "2026-06-05T19:21:55.576Z", "headSha": "2ba51d83ff4b86b9ca37a30b463cd3cd9e1f4d54", "message": null, "publicUrl": "https://os.iterate-preview-2.com", "runUrl": "https://github.com/iterate/iterate/actions/runs/27035212234", "shortSha": "2ba51d8" } }, "environmentConfigLease": { "dopplerConfig": "preview_2", "leasedUntil": 1780690796123, "leaseId": "23930c5a-2e1b-49b4-b10a-c83b489c0794", "slug": "preview-2", "type": "environment-config-lease" } } --> <!-- /CLOUDFLARE_PREVIEW_STATE --> Lease: `preview-2` Doppler config: `preview_2` Type: `environment-config-lease` Leased until: 2026-06-05T20:19:56.123Z ### OS Status: deployed Commit: `2ba51d8` Preview: https://os.iterate-preview-2.com [Workflow run](https://github.com/iterate/iterate/actions/runs/27035212234) Updated: 2026-06-05T19:21:55.576Z <!-- /CLOUDFLARE_PREVIEW --> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches agent stream bootstrap and durable-object initialization on every routed agent stream; wrong event matching or wake ordering could affect non-Slack agents, but changes are narrowly scoped to host processor and Slack routing. > > **Overview** > Restores **Slack-routed agent streams** after the streams runtime cutover (#1370): routed streams only got `slack-agent` + `agent-host`, so `agent/input-added` events were never consumed because LLM processors were never registered. > > **`ensureAgentRunnerForOwnStream`** initializes the stream’s `AgentDurableObject` on `events.iterate.com/stream/created` (via `agent-host` `afterAppend`, using **`keepAlive`** to avoid deadlocking catch-up). That runs `onInstanceWake`, which appends the LLM processor subscriptions and setup events. > > **Slack bootstrap** now uses **`agentProcessorSubscriptionConfiguredEvent`** so the routed `agent-host` subscription key matches `AgentDurableObject`, deduping to a single runner. > > **Event type alignment** for the new runtime: `child-stream-created` and related core lifecycle types use `events.iterate.com/stream/…` instead of legacy `…/core/…` in agent host logic, the agents-root jsonata matcher, and stream composer presets. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 2ba51d8. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Cuts
apps/osover to the stream Durable Object andStreamProcessorRunnerfrompackages/streams, removing the old OS usage of the shared stream DO/processor mixins. The existing OS domain behaviour stays in place, but stream storage, live subscriptions, and processor delivery now go through the new package runtime.What changed
StreamProcessorRunnerDurable Object.StreamsCapability.stream()to usewithStreamConnectionFromWorkers()and pass an RpcTarget sink intostream.subscribe().docs/streams-os-migration/and ADRs.Validation
pnpm typecheckpnpm testpnpm format:checkpnpm exec oxlint . --threads 1 --deny-warningspnpm --dir apps/os sqlfu:checkpnpm --dir apps/os test:project-ingresspnpm --dir apps/os test:codemode-sessionpnpm --dir apps/os test:project-mcp-server-connectiondoppler run --project os --config dev_jonas -- pnpm --dir apps/os e2e -t "creates a disposable project and uses project streams through oRPC" --runThe workerd suites still print existing sourcemap/websocket close noise, but exit successfully.
Environment Config Lease
No active environment config lease.
OS
Status: released
Commit:
2f6cd72Preview: https://os.iterate-preview-2.com
Summary: Preview app released.
Workflow run
Updated: 2026-06-05T14:02:33.817Z
Note
High Risk
This is a hard cutover of core stream storage, live subscriptions, and processor delivery across agents, codemode, projects, repos, and Slack; behavior depends on polling catch-up and is explicitly not a backwards-compatible data migration.
Overview
Cuts
apps/osover to@iterate-com/streamsfor stream storage and processor delivery, replacing the sharedStreamDurableObjectpluswithStreamProcessor/withStreamProcessorRunnermixins.The
STREAMbinding now targets the packageStreamdurable object, and a newSTREAM_PROCESSOR_RUNNERbinding hosts a standaloneStreamProcessorRunnerthat wires built-in, capnweb-websocket subscriptions (subscriptionKey+processorSlug) instead of callableafterAppendRPC on domain DOs.new-stream-runtime.tsadapts legacy OS event/cursor shapes to the package stream RPC.Agent, codemode, project, repo, Slack integration, and Slack agent DOs drop in-DO processor runners; they append
stream/subscription-configuredevents, then poll runnerruntimeState()until catch-up. Agent-specific stream logic (codemode side effects, child agents) moves into anagent-hostprocessor insideStreamProcessorRunner.Project stream APIs (
StreamsCapability, oRPCstreamEvents) usewithStreamConnectionFromWorkersand livestream.subscribesinks; stream listing walks initialized paths from runtime state instead of D1 catalog. CONTEXT.md records the POC cutover (no legacy subscription translation, histories may be discarded).An admin e2e now exercises create/list/append/read/live-stream on a project stream via oRPC.
Reviewed by Cursor Bugbot for commit 2f6cd72. Bugbot is set up for automated code reviews on this repo. Configure here.