# Code Mode

`pkg/codemode` is a Go port of the TypeScript AI SDK's `@ai-sdk/code-mode`
package. It lets a model write JavaScript that calls host tools
programmatically instead of making one tool call per model turn ("code
mode"). The model's source runs in an isolated QuickJS sandbox compiled to
WebAssembly and executed with [wazero](https://wazero.io/) — no cgo
required.

**Experimental:** this package is experimental, mirroring the TypeScript
package's own `experimental_` naming convention on every exported entry
point. Its API may change in a minor version. Go names drop the
`experimental_` prefix.

See [Known differences: code-mode never-settling promises and in-flight
limits](https://goaisdk.com/docs/migration-guides/known-differences.md#code-mode-never-settling-promises-and-in-flight-limits)
for the behavior differences from the TypeScript (V8-based) runtime.

## Package

```go
import "github.com/digitallysavvy/go-ai/pkg/codemode"
```

## Entry points

### RunCodeMode

```go
func RunCodeMode(ctx context.Context, input RunInput) (interface{}, error)
```

Runs code-mode JavaScript directly, without wrapping it as an AI SDK tool.
The source runs as the body of an async function (top-level `await`/`return`
are supported) inside a fresh QuickJS sandbox with `input.Options`'
execution limits applied. Returns the sandboxed program's JSON-decoded
return value on completion, a `*Interrupt` (as the returned `interface{}`,
with a nil error) if execution paused on a host tool call, or an error.

```go
type RunInput struct {
    JS                   string
    Tools                ToolSet // map[string]types.Tool, reachable as tools.<name>(input)
    ToolExecutionOptions *types.ToolExecutionOptions
    Options              *Options
    Continuation         *Continuation        // resumes a prior interrupt
    InterruptResolution  *InterruptResolution // resolution for Continuation's next pending interrupt
}
```

### CreateCodeModeTool

```go
func CreateCodeModeTool(tools ToolSet, options Options) types.Tool
```

Creates an AI SDK `types.Tool` (name `"codeMode"`) that executes code-mode
JavaScript in an isolated sandbox, with `tools` reachable inside the
sandbox as `tools.<name>(input)`. Drop the result straight into
`GenerateTextOptions.Tools`.

### CodeModeTool (tool caller)

```go
func CodeModeTool(options ToolCallerOptions) types.Tool
```

Creates a code-mode tool for use with `ai.ExperimentalToolCallers`, so other
tools can invoke code-mode as a caller rather than the model calling it
directly.

Messages a tool caller adds to the conversation (for example the code-mode
tool catalog with `ToolDiscoveryConversation`) persist across steps in
`GenerateText`, `StreamText` and `ToolLoopAgent`, and `PrepareStep`'s
`InitialMessages` stays the raw prompt, as in TS.

## TypeScript in model-written code

Before running, code-mode strips TypeScript annotations from the source with
Node `stripTypeScriptTypes` semantics, as TS code-mode does:

- type annotations, generics (including generic arrow functions such as
  `<T,>(x: T) => x`) and default type parameters
- `as` and `satisfies` expressions, and non-null assertions (`x!`)
- `interface` and `type` declarations, `import type` / `export type`
- class modifiers (`public`, `private`, `protected`, `readonly`,
  `override`, `declare`, `abstract`), `declare` statements, and a
  function's `this` parameter

Syntax that has no type-only erasure, such as `enum`, isn't rewritten. If
stripping fails for any reason, the source runs unchanged, and the
JavaScript engine reports a syntax error if it isn't valid JavaScript.
Plain JavaScript is never modified.

## Tool catalog order

The tool catalog and prompt that code-mode generates list tools in
declaration order, matching TS. With `CodeModeTool` and
`ai.ExperimentalToolCallers`, that is the order of the tools passed to the
call: `types.ToolCallerDefinition.Bind` and `PrepareModelMessage` receive
an ordered `[]types.Tool`. `CreateCodeModeTool`, `BuildCodeModeToolDescription`
and `BuildCodeModeToolCatalogMessage` take a `ToolSet` (a Go map, which has
no order), so they list tools alphabetically.

## Interrupts and approvals (Code-mode part 2)

Host tool calls made from inside the sandbox can pause execution (an
interrupt) or require approval before running. These functions manage that
lifecycle; see the package's Go doc comments for full semantics.

```go
func RequestCodeModeInterrupt(payload InterruptPayload) error
func IsCodeModeInterrupt(value interface{}, security ...ContinuationSecurityOptions) bool
func GetCodeModeInterrupt(result interface{}, security ...ContinuationSecurityOptions) *Interrupt
func UnwrapCodeModeResult(result interface{}, security ...ContinuationSecurityOptions) UnwrappedResult
func ContinueCodeModeInterrupt(ctx context.Context, interrupt Interrupt, resolution interface{}, tools ToolSet, options *Options, toolExecutionOptions *types.ToolExecutionOptions) (interface{}, error)

func IsCodeModeApprovalInterrupt(value interface{}, security ...ContinuationSecurityOptions) bool
func ContinueCodeModeApproval(ctx context.Context, interrupt ApprovalInterrupt, response ApprovalResponse, tools ToolSet, options *Options, toolExecutionOptions *types.ToolExecutionOptions) (interface{}, error)
func GetCodeModeApprovalResponse(messages []types.Message, interrupt ApprovalInterrupt) *ApprovalResponse
func ToCodeModeApprovalMessages(interrupt ApprovalInterrupt) []types.Message

func SetCodeModeContinuationSigningKey(key []byte, maxAge time.Duration) error
```

### Concurrent approvals (`Promise.all`)

With `Options.Approval` set to `&ApprovalOptions{Mode: ApprovalModeInterrupt}`,
tool calls that need approval and start concurrently (for example inside one
`Promise.all`) are batched, as in TS. Each such call stays pending until the
sandbox's job queue is idle, and then all of them are returned together:

- `RunCodeMode` (or the tool) returns an approval `*Interrupt` whose
  `Continuation.PendingInterruptions` lists every call in the batch.
- Resolve the entries one at a time with `ContinueCodeModeApproval`, passing
  `ApprovalResponse{ApprovalID: interrupt.InterruptID, Approved: ...}`. Each
  call returns the next pending `*Interrupt` until the batch is complete.
- No tool in the batch runs until every entry is resolved. If any entry is
  denied, none of them run, and the result is a `*ToolApprovalDeniedError`.

Calls that don't need approval run immediately and aren't part of the
batch. `MaxInFlightBridgeRequests` counts the calls in a batch. A promise
that never settles while no tool call is pending fails fast with a
`*ProtocolError` instead of waiting for the execution timeout.

Continuations are signed (HMAC-SHA256, TS-compatible canonical envelope) so
they can safely round-trip through client-visible state between requests.
`SetCodeModeContinuationSigningKey` sets the process-wide signing key; a
random key is generated at process start if it's never called.

## Vendored dependency

`pkg/codemode` vendors a patched copy of `fastschema/qjs` v0.0.6 at
`pkg/internal/third_party/qjs` (MIT license) to fix two memory-read bugs
and to add a job-queue quiescence hook (used to batch concurrent approval
requests) and an option that disables module imports in the engine (code-mode
sandboxes run with it on, so `import()` and the native `qjs:*` modules are
unavailable). `qjs.wasm` is rebuilt from pinned, checksum-verified sources by
`pkg/internal/third_party/qjs/build/build.sh`.
`github.com/tetratelabs/wazero` is a direct dependency.

## See Also

- [Known differences from the TypeScript AI SDK](https://goaisdk.com/docs/migration-guides/known-differences.md)
- [Migrating from v0.4.x to v0.5.0](https://goaisdk.com/docs/migration-guides/from-v0.4-to-v0.5.md)
