Skip to content

codemode rip, part 2: the agent loop runs on itx#1446

Merged
jonastemplestein merged 4 commits into
mainfrom
rip-codemode-agents
Jun 10, 2026
Merged

codemode rip, part 2: the agent loop runs on itx#1446
jonastemplestein merged 4 commits into
mainfrom
rip-codemode-agents

Conversation

@jonastemplestein

@jonastemplestein jonastemplestein commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Stacked on #1445. The agent's execution engine moves from CodemodeSession to the itx runner.

What

  • Each agent gets an itx child context (ctx_…) seeded with its tools as caps: chat + debug via a new AgentToolsCapability loopback forwarder, plus ai / os / gmail. One agent/capability-noted event per cap lands on the agent stream, so the LLM keeps learning its tools from stream history exactly as before (the agent processor renders these into model-visible context).
  • LLM code fences run through runItxScript, detached — the two-event execution record on the agent stream is the durable trace, and itx/execution-completed turns into agent input (the yaml block now includes console output). LLM scripts stay written as async (ctx) => …: ctx IS the itx handle — ctx.chat, ctx.ai, ctx.os, ctx.gmail, ctx.fetch all line up name-for-name.
  • Enqueued executions: itx/execution-requested with enqueued: true is a queue entry the agent-host runs (first taste of processor mode from itx-next §4). Slack bang commands use this path.
  • Security: AgentToolsCapability derives the agent DO name from registry-injected projectId + definer's agentPath — a raw durable-object ref would have let a definer address another project's agent by name.
  • Slack agent: status side effects key on the itx events; thread-route etiquette becomes a capability-noted event.

Behavior changes

  • ctx.slack.agent.threadInfo() is dropped (it was an event-mediated tool, a codemode-only mechanism). The webhook payload already carries channel/thread_ts — the old instructions said as much — and it can return as a dialable cap (SlackAgentToolsCapability) if missed.
  • Function-call-level events are gone; execution granularity is requested/completed. Slack "is using tools…" status now spans the whole script run.
  • Detached execution has no retry-on-DO-restart (codemode's processor had at-least-once); record-only semantics, noted in itx-next §4 as the processor-mode follow-up.

Testing

typecheck / lint / format green; unit suite updated to the new events (207 passing). Needs a deployed e2e pass for the agent loop — the existing agents e2e suite covers it once this stack deploys to a preview.

🤖 Generated with Claude Code


Note

High Risk
Touches core agent execution, tool dialing, and Slack paths; detached itx runs lack codemode-style retry on DO restart, and security relies on registry-injected projectId in AgentToolsCapability.

Overview
Replaces CodemodeSession with an itx child context per agent: tools are registered as caps (chat/debug via new AgentToolsCapability, plus ai, os, gmail, slack, agents), and agent/capability-noted events on the agent stream replace codemode/tool-provider-registered for LLM-visible tool docs.

Agent-host runs LLM code fences through runItxScript (detached) and maps itx/execution-completed back to agent input (including console logs). Slack bang commands enqueue itx/execution-requested with enqueued: true. SlackCapability gains an itx call path; ctx.slack.agent.threadInfo and codemode function-call events are removed.

runItxScript accepts optional executionId and recordRequested: false when the queue entry already exists.

Reviewed by Cursor Bugbot for commit 5b92858. Bugbot is set up for automated code reviews on this repo. Configure here.

Environment Config Lease

No active environment config lease.

OS

Status: released
Commit: 5b92858
Preview: https://os.iterate-preview-4.com
Summary: Preview app released.
Workflow run
Updated: 2026-06-10T14:51:38.665Z

Semaphore

Status: released
Commit: 7b6c9e2
Preview: https://semaphore.iterate-preview-4.com
Summary: Preview app released.
Workflow run
Updated: 2026-06-10T14:51:28.574Z

Comment thread apps/os/src/domains/agents/durable-objects/agent-durable-object.ts
jonastemplestein added a commit that referenced this pull request Jun 10, 2026
Bugbot findings: (#1446 High) the agent loop shipped without the slack
cap, breaking Slack bang commands and LLM replies at this point in the
stack — the slack (full guidance text, including the quiet-reply rule and
the Promise.all acknowledgment pattern, addressing the #1447 Medium) and
agents caps move down from part 3 to where the loop lands, with the
SlackCapability call() adapter and allowlist entries. (#1447 High at its
source) ensureItxContext seeding is now versioned: bump
AGENT_CONTEXT_CAPS_VERSION and existing agents re-define caps on next
wake (defines upsert; capability-noted idempotency keys dedupe).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread apps/os/src/domains/agents/durable-objects/agent-durable-object.ts
Comment thread apps/os/src/domains/agents/durable-objects/agent-durable-object.ts
@jonastemplestein

Copy link
Copy Markdown
Contributor Author

Bugbot High addressed in bdda255 — and it was a fair catch of bad stacking on my part: the slack + agents caps (plus the SlackCapability call() adapter and allowlist entries) moved down from part 3 into this PR, so the agent loop is Slack-capable at every point in the stack. The slack instructions use the full recovered guidance text (quiet-reply rule + Promise.all acknowledgment pattern). Also added here, at its source: versioned context seeding (AGENT_CONTEXT_CAPS_VERSION) so existing agents re-seed caps on the next wake after any caps change — that was #1447's High.

jonastemplestein added a commit that referenced this pull request Jun 10, 2026
Bugbot findings: (#1446 High) the agent loop shipped without the slack
cap, breaking Slack bang commands and LLM replies at this point in the
stack — the slack (full guidance text, including the quiet-reply rule and
the Promise.all acknowledgment pattern, addressing the #1447 Medium) and
agents caps move down from part 3 to where the loop lands, with the
SlackCapability call() adapter and allowlist entries. (#1447 High at its
source) ensureItxContext seeding is now versioned: bump
AGENT_CONTEXT_CAPS_VERSION and existing agents re-define caps on next
wake (defines upsert; capability-noted idempotency keys dedupe).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jonastemplestein jonastemplestein force-pushed the rip-codemode branch 2 times, most recently from 8a172a0 to 071375e Compare June 10, 2026 14:08
Base automatically changed from rip-codemode to main June 10, 2026 14:29
jonastemplestein and others added 3 commits June 10, 2026 15:30
The agent's execution engine is now the itx runner; the CodemodeSession
DO is no longer in the loop:

- Agent DO: ensureCodemodeSession → ensureItxContext. Each agent gets a
  child context (ctx_…) seeded with its tools as caps: chat + debug via
  the new AgentToolsCapability loopback forwarder (DO names derive from
  registry-injected projectId — a definer can only reach agents in the
  context's own project), plus ai / os / gmail. One
  agent/capability-noted event per cap lands on the agent stream so the
  LLM keeps learning its tools from history.
- Agent-host processor: LLM code fences run DETACHED through
  runItxScript (record on the agent stream; the completed event is the
  durable trace); itx/execution-completed turns into agent input
  (yaml block now includes console logs). LLM scripts stay written as
  `async (ctx) => …` — ctx IS the itx handle, names line up.
- Enqueued executions: itx/execution-requested with `enqueued: true` is
  a queue entry the agent-host runs (runItxScript gains executionId +
  recordRequested params) — Slack bang commands use this.
- Slack-agent processor: thread-route etiquette becomes a
  capability-noted event; status side effects key on itx/execution-*;
  the event-mediated ctx.slack.agent.threadInfo() tool is DROPPED for
  now (the webhook payload carries channel/thread_ts; restore later as
  a dialable cap if missed).
- Agent processor: consumes agent/capability-noted instead of
  codemode/tool-provider-registered; rendering and event-type
  explanation updated; contracts drop the codemode dep.

Tests updated to the new events; threadInfo test removed with the
feature. Part 3 deletes the codemode domain itself.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bugbot findings: (#1446 High) the agent loop shipped without the slack
cap, breaking Slack bang commands and LLM replies at this point in the
stack — the slack (full guidance text, including the quiet-reply rule and
the Promise.all acknowledgment pattern, addressing the #1447 Medium) and
agents caps move down from part 3 to where the loop lands, with the
SlackCapability call() adapter and allowlist entries. (#1447 High at its
source) ensureItxContext seeding is now versioned: bump
AGENT_CONTEXT_CAPS_VERSION and existing agents re-define caps on next
wake (defines upsert; capability-noted idempotency keys dedupe).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…te; knip)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5b92858. Configure here.

invocation: { kind: "event" },
name: "slack",
instructions:
"Use ctx.slack.agent.threadInfo() only when you need route context that is not already in the Slack webhook payload. Slack agents MUST respond on the same thread_ts that received the message; otherwise they will not receive responses from that thread. Unless explicitly required, always include thread_ts in Slack replies. Do not post to Slack unless the bot was explicitly mentioned, a user directly asks or instructs you, or the surrounding thread context clearly calls for agent action. Normal Slack replies can use channel/thread_ts from the webhook event directly.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Duplicate Slack capability notes

Medium Severity

Slack-backed agents now get two agent/capability-noted events for the same slack tool: one from ensureItxContext (full API and Promise.all guidance) and another when thread route is configured (shorter etiquette text). The agent processor renders both into model context with conflicting instructions.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5b92858. Configure here.

});
await this.ctx.storage.put("itxContextId", contextId);
await this.ctx.storage.put("itxContextCapsVersion", AGENT_CONTEXT_CAPS_VERSION);
return contextId;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Concurrent itx context seeding

Medium Severity

ensureItxContext is not serialized. Overlapping calls (e.g. multiple detached runAgentItxScript runs via getItxContextId before storage writes finish) can each mint a new ctx id, define caps on different contexts, and race on itxContextId persistence.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5b92858. Configure here.

@jonastemplestein jonastemplestein merged commit 4d716d1 into main Jun 10, 2026
11 checks passed
@jonastemplestein jonastemplestein deleted the rip-codemode-agents branch June 10, 2026 14:49
jonastemplestein added a commit that referenced this pull request Jun 10, 2026
Stacked on #1446. The codemode domain is deleted — net **-8.3k lines**.

## What went where

- **`CodemodeSession` DO → tombstone.** The class + `CODEMODE_SESSION`
binding deliberately keep existing: production streams carry durable
callable subscribers that dial the namespace by name, and a vanished
class is exactly the 2026-06-10 legacy-subscriber outage. The tombstone
accepts `requestStreamSubscription` dials and no-ops (with a warning
log). Proper removal = follow-up with a DO deletion migration + stream
subscriber cleanup, after this soaks.
- **`AiCapability` / `OrpcCapability`** →
`src/rpc-targets/os-capabilities.ts` (they're itx caps now, not codemode
examples).
- **`ExecuteCodemodeFunctionCallInput`** →
`src/rpc-targets/legacy-codemode-call.ts`; capability entrypoints keep
the legacy method while callers migrate to `call({ path, args })`.
`SlackCapability` gains a real `call()`.
- **Agent contexts gain `slack` + `agents` caps** (SlackCapability
path-call with the old etiquette instructions, AgentCapability members
for pipelined subagents) — restoring the full pre-itx provider surface.
- **Per-context workspaces**: `ctx.workspace` on a child context now
maps to workspace `itx:<contextId>` (derived identically in `handle.ts`
and the agent's workspace prep). Existing agents re-clone iterate-config
once into the new workspace id.
- **UI/oRPC**: `project.codemode` contract + router, codemode-sessions
routes, session controls, and the new-agent tool-provider compiler are
deleted — the Agent DO seeds caps (and the LLM-visible capability notes)
on wake, so the form stops compiling registration events.
- slack-integration bootstrap stops subscribing codemode to routed
streams.

## Test changes

- Deleted: codemode e2e suites, `codemode-builder`, and
`e2e-test-map.e2e.test.ts` (built entirely on the codemode fixture; the
itx e2e suite covers the replacement model).
- `agents.e2e` converted to itx events. Coverage honestly lost with the
per-call event mechanism: function-call-level assertions (workspace op
sequence, slack-call payload echo, debug-output sanitization) — those
now verify via generated code + execution-completed `ok` + the real side
effects.
- Unit: 205 passing; repo-wide typecheck/lint/format green.

## Follow-ups (noted in itx-next.md territory)

- prd/preview cleanup: tombstone soak → DO deletion migration +
subscriber-event cleanup.
- `executeCodemodeFunctionCall` legacy methods on capability entrypoints
→ delete once nothing dials them.
- `packages/shared/src/codemode/*` (json-schema type generation) still
used by OrpcCapability's listProcedures — rename/move later.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **High Risk**
> Large removal of script execution, providers, and oRPC routes touches
agent/Slack flows and production stream subscribers; behavior changes
until tombstone subscribers are cleaned up.
> 
> **Overview**
> **Removes the codemode domain** (~8k lines): the `CodemodeSession` DO
implementation, stream processor, default/example providers, UI session
controls, and the entire **`project.codemode` oRPC surface** (sessions,
`executeScript`, `streamEvents`, `describe`).
> 
> **`CodemodeSession` becomes a tombstone** — same class/binding so
existing stream callable subscribers keep dialing without outage;
`requestStreamSubscription` no-ops with a warning until subscriber
cleanup lands.
> 
> **Agents run on itx instead of codemode events:** script runs use
`runItxScript` with `convention: "ctx"` and direct LLM code; workspaces
are **`itx:<contextId>`** (aligned with the itx handle), with
single-flight `ensureItxContext`. Capability types move to
**`rpc-targets/legacy-codemode-call`** and **`os-capabilities`**;
tests/assertions shift from `codemode/*` and per-call function events to
**`itx/execution-*`** and **`agent/capability-noted`**.
> 
> **E2E:** codemode suites, `CodemodeBuilder`, and `e2e-test-map` are
deleted; `agents.e2e` is updated for itx (some granular function-call
assertions dropped).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
24365a0. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- CLOUDFLARE_PREVIEW -->
## Environment Config Lease
<!-- CLOUDFLARE_PREVIEW_STATE -->
<!--
{
  "apps": {
    "os": {
      "appDisplayName": "OS",
      "appSlug": "os",
      "status": "tests-failed",
      "updatedAt": "2026-06-10T15:05:24.078Z",
      "headSha": "9dd5fb619c994aadf847645bf0c6d7ee4f1aa7c1",
"message": "...(truncated)\ns\nAssertionError: expected 'Error' to be
'ItxError' // Object.is equality\n\nExpected: \"ItxError\"\nReceived:
\"Error\"\n\n ❯ src/itx/e2e/itx.e2e.test.ts:397:23\n 395| );\n 396|
expect(error).not.toBeNull();\n 397|
expect(error!.name).toBe(\"ItxError\");\n | ^\n 398|
expect(error!.code).toBe(\"NOT_FOUND\");\n 399|
expect(error!.details).toEqual({ projectIdOrSlug:
\"definitely-not-a-…\n\n⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯\n\n\n\n>
@iterate-com/os@0.0.1 e2e /home/runner/work/iterate/iterate/apps/os\n>
vitest --config e2e/vitest.config.ts -t 'OS preview
smoke'\n\n[vitest-artifacts] run root: /tmp/os-e2e-OV4oT8\n[vitest] run
slug: os-vitest-run-20260610-150101\n\n RUN v4.0.15
/home/runner/work/iterate/iterate/apps/os\n\nstdout |
e2e/vitest/preview-smoke.e2e.test.ts > OS preview smoke\nOS preview
smoke passed for https://os.iterate-preview-9.com/\n\n ✓
e2e/vitest/preview-smoke.e2e.test.ts (1 test) 6957ms\n ✓ OS preview
smoke 6956ms\n ↓ e2e/vitest/admin-project.e2e.test.ts (1 test | 1
skipped)\n ↓ e2e/vitest/agents.e2e.test.ts (7 tests | 7 skipped)\n\n
Test Files 1 passed | 2 skipped (3)\n Tests 1 passed | 8 skipped (9)\n
Start at 15:01:01\n Duration 7.56s (transform 142ms, setup 0ms, import
348ms, tests 6.96s, environment 0ms)\n\n\n> @iterate-com/os@0.0.1
e2e:itx /home/runner/work/iterate/iterate/apps/os\n> vitest run --config
src/itx/e2e/vitest.config.ts --project node\n\n[vitest-artifacts] run
root: /tmp/os-itx-e2e-5Kcek5\n[vitest] run slug:
os-vitest-run-20260610-150109\n\n RUN v4.0.15
/home/runner/work/iterate/iterate/apps/os\n\n ❯ node
src/itx/e2e/itx.e2e.test.ts (10 tests | 1 failed | 1 skipped) 99422ms\n
✓ itx scripts run identically over Cap'n Web and /api/itx/run 13584ms\n
✓ the five-step capability flow: provide live, call, promote durable,
call from a script 13702ms\n ✓ platform bindings are dialable
capabilities (raw + wrapped) 11602ms\n ↓ the first-party McpClient cap
bridges a remote MCP server\n ✓ script executions leave a two-event
record on the /itx stream 13149ms\n ✓ worker caps hold a correctly
scoped itx of their own 13039ms\n ✓ members caps auto-proxy every public
method/getter at any depth 9466ms\n ✓ one dynamic worker cap calls
another's methods through its own itx 9256ms\n × kernel errors cross
capnweb as ItxError-shaped errors with codes 1572ms\n ✓ revoked and
offline caps fail with instructive errors 10686ms\n ✓ node
src/itx/e2e/itx-fork.e2e.test.ts (3 tests) 66888ms\n ✓ fork: child caps
shadow the parent, misses delegate up the chain 15899ms\n ✓ fork narrows
access: a session cannot reach sibling projects 23563ms\n ✓ fork: child
worker caps run with the owning project's authority 24677ms\n ✓ node
src/itx/e2e/itx-egress.e2e.test.ts (3 tests) 39982ms\n ✓ itx.fetch
substitutes secrets through project egress (explicit door) 10155ms\n ✓
bare fetch() in a project itx script goes through egress (implicit door)
14358ms\n ✓ bare fetch() inside a worker cap goes through egress
(implicit door) 13693ms\n ✓ node src/itx/e2e/itx-http.e2e.test.ts (2
tests) 33070ms\n ✓ facet caps keep private durable state across
invocations 9914ms\n ✓ HTTP-exposed caps serve their own hostname:
admin, share URL, public 20529ms\n ✓ node
src/itx/e2e/itx-subscribe.e2e.test.ts (1 test) 14363ms\n ✓ subscribe
replays history, tails live appends, and unsubscribes 12306ms\n\n Test
Files 1 failed | 4 passed (5)\n Tests 1 failed | 17 passed | 1 skipped
(19)\n Start at 15:01:09\n Duration 254.34s (transform 80ms, setup 0ms,
import 187ms, tests 253.72s, environment 0ms)\n\n\n::error
file=/home/runner/work/iterate/iterate/apps/os/src/itx/e2e/itx.e2e.test.ts,title=[node]
src/itx/e2e/itx.e2e.test.ts > kernel errors cross capnweb as
ItxError-shaped errors with codes,line=397,column=23::AssertionError:
expected 'Error' to be 'ItxError' // Object.is equality%0A%0AExpected:
\"ItxError\"%0AReceived: \"Error\"%0A%0A ❯
src/itx/e2e/itx.e2e.test.ts:397:23%0A%0A\n ELIFECYCLE  Command failed
with exit code 1.",
      "publicUrl": "https://os.iterate-preview-9.com",
"runUrl": "https://github.com/iterate/iterate/actions/runs/27284870355",
      "shortSha": "9dd5fb6"
    },
    "semaphore": {
      "appDisplayName": "Semaphore",
      "appSlug": "semaphore",
      "status": "deployed",
      "updatedAt": "2026-06-10T14:56:06.306Z",
      "headSha": "9dd5fb619c994aadf847645bf0c6d7ee4f1aa7c1",
      "message": null,
      "publicUrl": "https://semaphore.iterate-preview-9.com",
"runUrl": "https://github.com/iterate/iterate/actions/runs/27284870355",
      "shortSha": "9dd5fb6"
    }
  },
  "environmentConfigLease": {
    "dopplerConfig": "preview_9",
    "leasedUntil": 1781109314547,
    "leaseId": "eee2cb43-98d1-4d7e-b76a-9508a07fcfc1",
    "slug": "preview-9",
    "type": "environment-config-lease"
  }
}
-->
<!-- /CLOUDFLARE_PREVIEW_STATE -->
Lease: `preview-9`
Doppler config: `preview_9`
Type: `environment-config-lease`
Leased until: 2026-06-10T16:35:14.547Z

### OS
Status: tests failed
Commit: `9dd5fb6`
Preview: https://os.iterate-preview-9.com
Summary: AssertionError: expected 'Error' to be 'ItxError' // Object.is
equality
[Workflow
run](https://github.com/iterate/iterate/actions/runs/27284870355)
Updated: 2026-06-10T15:05:24.078Z

<details>
<summary>Failure details</summary>

<pre>...(truncated)
s
AssertionError: expected 'Error' to be 'ItxError' // Object.is equality

Expected: "ItxError"
Received: "Error"

 ❯ src/itx/e2e/itx.e2e.test.ts:397:23
    395|   );
    396|   expect(error).not.toBeNull();
    397|   expect(error!.name).toBe("ItxError");
       |                       ^
    398|   expect(error!.code).toBe("NOT_FOUND");
399| expect(error!.details).toEqual({ projectIdOrSlug:
"definitely-not-a-…

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯



&gt; @iterate-com/os@0.0.1 e2e /home/runner/work/iterate/iterate/apps/os
&gt; vitest --config e2e/vitest.config.ts -t 'OS preview smoke'

[vitest-artifacts] run root: /tmp/os-e2e-OV4oT8
[vitest] run slug: os-vitest-run-20260610-150101

 RUN  v4.0.15 /home/runner/work/iterate/iterate/apps/os

stdout | e2e/vitest/preview-smoke.e2e.test.ts &gt; OS preview smoke
OS preview smoke passed for https://os.iterate-preview-9.com/

 ✓ e2e/vitest/preview-smoke.e2e.test.ts (1 test) 6957ms
   ✓ OS preview smoke  6956ms
 ↓ e2e/vitest/admin-project.e2e.test.ts (1 test | 1 skipped)
 ↓ e2e/vitest/agents.e2e.test.ts (7 tests | 7 skipped)

 Test Files  1 passed | 2 skipped (3)
      Tests  1 passed | 8 skipped (9)
   Start at  15:01:01
Duration 7.56s (transform 142ms, setup 0ms, import 348ms, tests 6.96s,
environment 0ms)


&gt; @iterate-com/os@0.0.1 e2e:itx
/home/runner/work/iterate/iterate/apps/os
&gt; vitest run --config src/itx/e2e/vitest.config.ts --project node

[vitest-artifacts] run root: /tmp/os-itx-e2e-5Kcek5
[vitest] run slug: os-vitest-run-20260610-150109

 RUN  v4.0.15 /home/runner/work/iterate/iterate/apps/os

❯ node src/itx/e2e/itx.e2e.test.ts (10 tests | 1 failed | 1 skipped)
99422ms
✓ itx scripts run identically over Cap'n Web and /api/itx/run 13584ms
✓ the five-step capability flow: provide live, call, promote durable,
call from a script 13702ms
✓ platform bindings are dialable capabilities (raw + wrapped) 11602ms
   ↓ the first-party McpClient cap bridges a remote MCP server
✓ script executions leave a two-event record on the /itx stream 13149ms
   ✓ worker caps hold a correctly scoped itx of their own  13039ms
✓ members caps auto-proxy every public method/getter at any depth 9466ms
✓ one dynamic worker cap calls another's methods through its own itx
9256ms
× kernel errors cross capnweb as ItxError-shaped errors with codes
1572ms
   ✓ revoked and offline caps fail with instructive errors  10686ms
 ✓  node  src/itx/e2e/itx-fork.e2e.test.ts (3 tests) 66888ms
✓ fork: child caps shadow the parent, misses delegate up the chain
15899ms
✓ fork narrows access: a session cannot reach sibling projects 23563ms
✓ fork: child worker caps run with the owning project's authority
24677ms
 ✓  node  src/itx/e2e/itx-egress.e2e.test.ts (3 tests) 39982ms
✓ itx.fetch substitutes secrets through project egress (explicit door)
10155ms
✓ bare fetch() in a project itx script goes through egress (implicit
door) 14358ms
✓ bare fetch() inside a worker cap goes through egress (implicit door)
13693ms
 ✓  node  src/itx/e2e/itx-http.e2e.test.ts (2 tests) 33070ms
   ✓ facet caps keep private durable state across invocations  9914ms
✓ HTTP-exposed caps serve their own hostname: admin, share URL, public
20529ms
 ✓  node  src/itx/e2e/itx-subscribe.e2e.test.ts (1 test) 14363ms
✓ subscribe replays history, tails live appends, and unsubscribes
12306ms

 Test Files  1 failed | 4 passed (5)
      Tests  1 failed | 17 passed | 1 skipped (19)
   Start at  15:01:09
Duration 254.34s (transform 80ms, setup 0ms, import 187ms, tests
253.72s, environment 0ms)


::error
file=/home/runner/work/iterate/iterate/apps/os/src/itx/e2e/itx.e2e.test.ts,title=[node]
src/itx/e2e/itx.e2e.test.ts &gt; kernel errors cross capnweb as
ItxError-shaped errors with codes,line=397,column=23::AssertionError:
expected 'Error' to be 'ItxError' // Object.is equality%0A%0AExpected:
"ItxError"%0AReceived: "Error"%0A%0A ❯
src/itx/e2e/itx.e2e.test.ts:397:23%0A%0A
 ELIFECYCLE  Command failed with exit code 1.</pre>

</details>

### Semaphore
Status: deployed
Commit: `9dd5fb6`
Preview: https://semaphore.iterate-preview-9.com
[Workflow
run](https://github.com/iterate/iterate/actions/runs/27284870355)
Updated: 2026-06-10T14:56:06.306Z
<!-- /CLOUDFLARE_PREVIEW -->

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
jonastemplestein added a commit that referenced this pull request Jun 10, 2026
Final follow-up to the codemode rip (#1445/#1446/#1447). MCP's
streamable HTTP transport is a stateless protocol — fetch with metadata
— so nothing about an MCP client belongs in a Durable Object.

## What

- **`OutboundMcpFromOurClientCapability` DO deleted** — class, worker
export, alchemy namespace + `OUTBOUND_MCP_FROM_OUR_CLIENT_CAPABILITY`
binding, vitest harness wiring. Nothing has dialed it since the codemode
rip (its only caller was the deleted provider registration).
- **Core helpers promoted** to `src/itx/caps/mcp-client-core.ts` with
clean names: `connectMcp` (custom-fetch aware, closes on failed
connect), `listMcpTools`, `executeMcpToolCall`. `McpClient` dedupes onto
them; the mock-server unit test moved and adapted.
- **The DO allusion is gone from the docs**: `McpClient`'s header now
states statelessness as the design ("connect → call → close, per
invocation; deliberately no Durable Object anywhere in this path")
instead of presenting connection caching as a future optimization.

## Deploy note

This is the repo's first Durable Object class deletion. The DO had no
SQLite (in-memory cache only) and no inbound subscribers, so deletion is
data-safe; alchemy is expected to emit the `deleted_classes` migration
on deploy — **this PR's preview deploy is the proof**. If it refuses,
the fallback is a tombstone class like `CodemodeSession`'s.

## Testing

Repo typecheck / lint / knip / unit suite green locally (incl. the moved
mock-server core test). The `McpClient` e2e remains gated on a reachable
MCP server.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CLOUDFLARE_PREVIEW -->
## Environment Config Lease
<!-- CLOUDFLARE_PREVIEW_STATE -->
<!--
{
  "apps": {},
  "environmentConfigLease": {
    "dopplerConfig": "preview_4",
    "leasedUntil": 1781109925654,
    "leaseId": "f7427e44-72e2-4e04-8617-122e174b3c57",
    "slug": "preview-4",
    "type": "environment-config-lease"
  }
}
-->
<!-- /CLOUDFLARE_PREVIEW_STATE -->
Lease: `preview-4`
Doppler config: `preview_4`
Type: `environment-config-lease`
Leased until: 2026-06-10T16:45:25.654Z
<!-- /CLOUDFLARE_PREVIEW -->

Co-authored-by: Claude Fable 5 <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