Skip to content

itx: execution events — two-event durable record for script runs#1442

Merged
jonastemplestein merged 4 commits into
mainfrom
itx-execution-events
Jun 10, 2026
Merged

itx: execution events — two-event durable record for script runs#1442
jonastemplestein merged 4 commits into
mainfrom
itx-execution-events

Conversation

@jonastemplestein

@jonastemplestein jonastemplestein commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Stacked on #1441 (McpClient). Part of the codemode rip-out sequence (itx-next.md §4).

What

The two-event execution record that replaces codemode's six-event protocol:

  • events.iterate.com/itx/execution-requested { executionId, code, vars, context }
  • events.iterate.com/itx/execution-completed { executionId, ok, result | error+stack, durationMs, context }

Both land on the owning project's /itx stream (D9-consistent: context id in the payload).

Record-only mode: the events are the durable history, not the transport — callers get the outcome from the return value/HTTP response, and everything between the two events is invisible to the stream. Appends are best-effort, matching the registry's audit posture. Recorded results are bounded (64k chars, truncated with a preview beyond that); the full value still returns to the caller.

Mechanically: the loader harness moves out of fetch.ts into a shared src/itx/run.ts (runItxScript()), /api/itx/run becomes a thin resolve-and-delegate shim, and the response now includes the executionId for correlation. Global-context scripts record no events (no owning project stream).

The shared runner is the function the upcoming codemode-drop PR points the inbound-MCP exec_js and agent execution paths at.

Testing

typecheck / lint / format green. New e2e test runs a script via /api/itx/run and asserts both events land on the /itx stream with the returned executionId and the correct outcome payload.

🤖 Generated with Claude Code


Note

Medium Risk
Touches dynamic code execution and stream audit payloads; auth stays at the existing /api/itx/run boundary, but execution records and API response shape change for all script runs.

Overview
Introduces record-only script execution auditing: each project-context run appends execution-requested then execution-completed on the project /itx stream (replacing codemode’s six-event protocol), while callers still get the outcome from the HTTP/return value.

The loader harness moves from fetch.ts into shared runItxScript() in run.ts; POST /api/itx/run only resolves access and delegates. Responses now include executionId (and on errors too) for correlation. The runner captures isolate console lines, truncates large results on the stream (64k), always emits completed even on loader failures, and skips stream writes for global (admin-only) scripts.

A new e2e test runs a script via /api/itx/run and asserts both events match the returned executionId and result.

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

Semaphore

Status: released
Commit: f43fee8
Preview: https://semaphore.iterate-preview-7.com
Summary: Preview app released.
Workflow run
Updated: 2026-06-10T14:07:20.109Z

@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 00bfb0e. Configure here.

Comment thread apps/os/src/itx/run.ts
jonastemplestein added a commit that referenced this pull request Jun 10, 2026
Bugbot finding on #1442: loader.load/getEntrypoint threw outside the
outcome try/catch, leaving a dangling execution-requested record and an
executionId-less HTTP error. One try/catch now spans loading through
running.

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

Copy link
Copy Markdown
Contributor Author

Bugbot finding addressed in 9a7224d: one try/catch now spans loader.load → getEntrypoint → run, so a loader failure produces an ok:false outcome and the matching execution-completed event instead of a dangling execution-requested record.

jonastemplestein added a commit that referenced this pull request Jun 10, 2026
Bugbot finding on #1442: loader.load/getEntrypoint threw outside the
outcome try/catch, leaving a dangling execution-requested record and an
executionId-less HTTP error. One try/catch now spans loading through
running.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Base automatically changed from itx-mcp-client to main June 10, 2026 13:54
jonastemplestein and others added 4 commits June 10, 2026 14:55
Adds events.iterate.com/itx/execution-requested|completed (the pair that
replaces codemode's six-event execution protocol, itx-next.md §4) and a
shared runner, src/itx/run.ts, extracted from /api/itx/run:

- runItxScript() loads the one-off isolate (env.ITERATE scoped to the
  target context, globalOutbound = ProjectEgress for project scripts),
  appends execution-requested before and execution-completed after, and
  returns the outcome with an executionId.
- Record-only mode: the events are history, not transport — appends are
  best-effort (D1 posture), recorded results bounded at 64k chars.
- /api/itx/run becomes a thin shim (resolve + access-check, delegate) and
  now returns the executionId alongside the result.
- Global-context scripts record no events (no owning project stream).

The shared runner is what the codemode-drop PR points the inbound-MCP
exec_js and agent execution paths at.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Scripts' console.log/warn/error lines come back in the outcome (and the
completed event, bounded at 200 lines) — exec_js parity for the codemode
drop. runItxScript gains record?: { namespace, path } so callers whose
loop lives on another stream (the agent) can record there; default stays
the project /itx stream.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bugbot finding on #1442: loader.load/getEntrypoint threw outside the
outcome try/catch, leaving a dangling execution-requested record and an
executionId-less HTTP error. One try/catch now spans loading through
running.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jonastemplestein jonastemplestein merged commit a5d5eb2 into main Jun 10, 2026
9 checks passed
@jonastemplestein jonastemplestein deleted the itx-execution-events branch June 10, 2026 14:05
jonastemplestein added a commit that referenced this pull request Jun 10, 2026
Bugbot finding on #1442: loader.load/getEntrypoint threw outside the
outcome try/catch, leaving a dangling execution-requested record and an
executionId-less HTTP error. One try/catch now spans loading through
running.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jonastemplestein added a commit that referenced this pull request Jun 10, 2026
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 scripts** — `async ({ 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 all** — `request()` 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](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!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).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
d0c631f. 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:28:35.934Z",
      "headSha": "d0c631fac58817753474480f5926e72e6c09d814",
      "message": null,
      "publicUrl": "https://os.iterate-preview-3.com",
"runUrl": "https://github.com/iterate/iterate/actions/runs/27282903658",
      "shortSha": "d0c631f"
    },
    "semaphore": {
      "appDisplayName": "Semaphore",
      "appSlug": "semaphore",
      "status": "deployed",
      "updatedAt": "2026-06-10T14:13:37.187Z",
      "headSha": "071375e4a5b3d9f546ea4c6caae492d60891f6d3",
      "message": null,
      "publicUrl": "https://semaphore.iterate-preview-3.com",
"runUrl": "https://github.com/iterate/iterate/actions/runs/27282171935",
      "shortSha": "071375e"
    }
  },
  "environmentConfigLease": {
    "dopplerConfig": "preview_3",
    "leasedUntil": 1781104923528,
    "leaseId": "9fd945b0-2dbe-4589-83ce-0dd8370a9739",
    "slug": "preview-3",
    "type": "environment-config-lease"
  }
}
-->
<!-- /CLOUDFLARE_PREVIEW_STATE -->
Lease: `preview-3`
Doppler config: `preview_3`
Type: `environment-config-lease`
Leased until: 2026-06-10T15:22:03.528Z

### OS
Status: deployed
Commit: `d0c631f`
Preview: https://os.iterate-preview-3.com
[Workflow
run](https://github.com/iterate/iterate/actions/runs/27282903658)
Updated: 2026-06-10T14:28:35.934Z

### Semaphore
Status: deployed
Commit: `071375e`
Preview: https://semaphore.iterate-preview-3.com
[Workflow
run](https://github.com/iterate/iterate/actions/runs/27282171935)
Updated: 2026-06-10T14:13:37.187Z
<!-- /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