Skip to content

codemode rip, part 1: inbound MCP exec_js runs on itx#1445

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

codemode rip, part 1: inbound MCP exec_js runs on itx#1445
jonastemplestein merged 3 commits into
mainfrom
rip-codemode

Conversation

@jonastemplestein

@jonastemplestein jonastemplestein commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Stacked on #1442. First slice of the codemode rip-out (itx-next.md §4): the inbound MCP server's exec_js no longer touches codemode.

What

  • exec_js scripts are itx scriptsasync ({ itx }) => { ... } — run through the shared runItxScript harness, with the two-event execution record landing on the MCP session's stream. Console capture and result formatting preserved.
  • Each (MCP session, project) gets a child context (ctx_…, the prototype chain in action) seeded with three caps: ai (raw AI binding ref), os (OrpcCapability, path-call), gmail (GmailCapability, members). The caps' instructions feed both the exec_js tool description and itx.describe().
  • OrpcCapability gains a real call({ path, args }); the legacy executeCodemodeFunctionCall delegates to it. GmailCapability needs no adapter at allrequest() is a real method and members-replay reaches it.
  • Security: the registry now force-injects projectId into loopback dial props (alongside cap/context), so dialable entrypoints that scope by project can never be pointed at someone else's project by a definer. Orpc + Gmail added to DIALABLE_LOOPBACKS with that contract documented.
  • Deleted from the MCP DO: tool-provider registrations, codemode session start/wait, the stream-decoding result watcher, debug provider limits (~200 lines).

Codemode itself still exists (agent loop + web UI consume it); parts 2 and 3 take those, then the domain deletes.

Testing

typecheck / lint / format / unit (208 tests) green. e2e exec_js coverage rides the existing inbound-MCP suite against a deployment.

🤖 Generated with Claude Code


Note

High Risk
Changes authenticated remote code execution and project-bound API/Gmail loopback dialing; registry-injected projectId is the main cross-tenant guard.

Overview
Inbound MCP exec_js no longer uses codemode sessions, tool-provider registrations, or stream polling for completion. Scripts run as async ({ itx }) => { ... } via runItxScript, with execution recorded on the MCP session stream and the same console/result formatting for tool responses.

Each (MCP session, project) gets a persisted child itx context (ITX_CONTEXT) parented to the project, seeded with ai, os (path-call → OrpcCapability), and gmail (members → GmailCapability). Cap instructions drive both the exec_js tool docs and itx.describe().

OrpcCapability adds call({ path, args }) for itx path-call; legacy executeCodemodeFunctionCall delegates to it (with mount-aware listProcedures for old callers). DIALABLE_LOOPBACKS now includes OrpcCapability and GmailCapability, and the registry force-injects projectId on loopback dial props so project-scoped entrypoints cannot be aimed at another project. codemode-event-payload helpers are removed from this path (~200 lines of MCP DO codemode wiring deleted).

Reviewed by Cursor Bugbot for commit d0c631f. 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: d0c631f
Preview: https://os.iterate-preview-3.com
Summary: Preview app released.
Workflow run
Updated: 2026-06-10T14:31:47.308Z

Semaphore

Status: released
Commit: 071375e
Preview: https://semaphore.iterate-preview-3.com
Summary: Preview app released.
Workflow run
Updated: 2026-06-10T14:31:38.132Z

@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 1 potential issue.

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 185a081. Configure here.

Comment thread apps/os/src/domains/codemode/example-capabilities.ts
jonastemplestein added a commit that referenced this pull request Jun 10, 2026
Bugbot finding on #1445: the legacy executeCodemodeFunctionCall delegated
listProcedures to call(), which namespaces typings by cap name — legacy
mount-agnostic callers got typings that no longer matched their mount path.

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

Copy link
Copy Markdown
Contributor Author

Bugbot finding addressed in 947173d: the legacy executeCodemodeFunctionCall keeps the caller's mount path as the listProcedures typings namespace; only the itx path-call namespaces by cap name. (The red Preview/deploy check is the preview-slot pool being exhausted — claimEnvironmentConfigLease 409, not a code failure; it clears as slots free up.)

jonastemplestein added a commit that referenced this pull request Jun 10, 2026
Bugbot finding on #1445: the legacy executeCodemodeFunctionCall delegated
listProcedures to call(), which namespaces typings by cap name — legacy
mount-agnostic callers got typings that no longer matched their mount path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Base automatically changed from itx-execution-events to main June 10, 2026 14:05
jonastemplestein added a commit that referenced this pull request Jun 10, 2026
Bugbot finding on #1445: the legacy executeCodemodeFunctionCall delegated
listProcedures to call(), which namespaces typings by cap name — legacy
mount-agnostic callers got typings that no longer matched their mount path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jonastemplestein and others added 2 commits June 10, 2026 15:08
ProjectMcpServerConnection no longer touches codemode:

- exec_js scripts are itx scripts (async ({ itx }) => …) run through the
  shared runItxScript harness, recorded as itx/execution-requested|completed
  on the MCP session's stream.
- Each (MCP session, project) gets a child context (ctx_…) seeded with
  ai (raw AI binding), os (OrpcCapability, path-call), and gmail
  (GmailCapability, members) — instructions feed both the tool description
  and itx describe().
- OrpcCapability gains a real call({ path, args }) (the legacy
  executeCodemodeFunctionCall delegates to it); GmailCapability needs no
  adapter — request() is a real method and members-replay reaches it.
- Registry now force-injects projectId into loopback dial props, so
  DIALABLE_LOOPBACKS entrypoints that scope by project (Orpc, Gmail) are
  spoof-proof; both added to the allowlist with the contract documented.
- Deleted from the MCP DO: provider registrations, codemode session
  start/wait, the stream-decode result watcher, debug provider limits.

Codemode itself still exists (agents + UI); parts 2/3 take those.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bugbot finding on #1445: the legacy executeCodemodeFunctionCall delegated
listProcedures to call(), which namespaces typings by cap name — legacy
mount-agnostic callers got typings that no longer matched their mount path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jonastemplestein jonastemplestein merged commit 8096899 into main Jun 10, 2026
9 checks passed
@jonastemplestein jonastemplestein deleted the rip-codemode branch June 10, 2026 14:29
jonastemplestein added a commit that referenced this pull request Jun 10, 2026
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](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!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.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5b92858. 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": "deployed",
      "updatedAt": "2026-06-10T14:47:53.981Z",
      "headSha": "5b92858e912f9d5f460284f9e3fffc6dfaa5c16e",
      "message": null,
      "publicUrl": "https://os.iterate-preview-4.com",
"runUrl": "https://github.com/iterate/iterate/actions/runs/27284064516",
      "shortSha": "5b92858"
    },
    "semaphore": {
      "appDisplayName": "Semaphore",
      "appSlug": "semaphore",
      "status": "deployed",
      "updatedAt": "2026-06-10T14:35:21.942Z",
      "headSha": "7b6c9e26a49ca380f25b45b218a47f371abed888",
      "message": null,
      "publicUrl": "https://semaphore.iterate-preview-4.com",
"runUrl": "https://github.com/iterate/iterate/actions/runs/27283580966",
      "shortSha": "7b6c9e2"
    }
  },
  "environmentConfigLease": {
    "dopplerConfig": "preview_4",
    "leasedUntil": 1781106059566,
    "leaseId": "108459b4-bf1b-4271-a050-cf9442fa648b",
    "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-10T15:40:59.566Z

### OS
Status: deployed
Commit: `5b92858`
Preview: https://os.iterate-preview-4.com
[Workflow
run](https://github.com/iterate/iterate/actions/runs/27284064516)
Updated: 2026-06-10T14:47:53.981Z

### Semaphore
Status: deployed
Commit: `7b6c9e2`
Preview: https://semaphore.iterate-preview-4.com
[Workflow
run](https://github.com/iterate/iterate/actions/runs/27283580966)
Updated: 2026-06-10T14:35:21.942Z
<!-- /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