# Recoding Live


You can change kli's behavior in the session you are already in, without restarting it. The recode operations below are ordinary functions: one swaps a whole extension, two hot-patch a single behavior in place. Each takes the running `context` and the object it acts on, and each is gated by a capability, so a restricted session can deny it.

This page assumes you have written or loaded the extension you want to change. For the extension shape itself, see [Write your first Lisp extension](/kli/extend/lisp-extensions/write-your-first). For why a running kli can do this at all, see [The live image](/kli/concepts/the-live-image).

## Where the recode call runs

A recode needs the live `context` and `protocol`. Both arrive as arguments wherever your extension's code already runs: a command handler is called as `(command arguments context &key call-id on-update)`, and an effect installer as `(protocol contribution context)`. Call the recode functions from inside one of those, where the objects are already in hand. The examples below show a command handler that does the recode when the user types its command.

To reach the active protocol from a context, use `(kli:active-protocol context)`; to find a live object by its id, use `(kli:find-live-object (kli:context-registry context) id)`.

## Swap a whole extension

Use `recode-extension` to replace one extension with a new version while everything else keeps running. It deactivates the old extension, then activates the new source. If activation fails, it re-activates the original, so the session is left on the version that worked rather than in a half-installed state.

```lisp
(command "swap-greeter"
  :description "Replace the greeter extension with its next version."
  :handler (lambda (command arguments context &key call-id on-update)
             (declare (ignore command arguments call-id on-update))
             (let* ((protocol (kli:active-protocol context))
                    (extension (kli:find-live-object
                                (kli:context-registry context)
                                :greeter)))
               (kli/ext:recode-extension protocol extension
                                         #'greeter-v2 context)
               (reply "Greeter swapped."))))
```

The third argument is the new source: the same kind of value you would activate an extension from, such as a manifest thunk or a `defextension` name. Deactivation retracts the old extension's tools, commands, providers, and methods together; activation installs the new one's. A consumer of the extension sees the swap as a single step.

`recode-extension` requires the `image/recode` capability. Granting `image/recode` also grants `manifest/install` and `manifest/retract`, because a recode is a retract followed by an install. A session whose `capabilities` array omits `image/recode` cannot swap extensions; see [Restrict what kli can do](/kli/guides/restrict-what-kli-can-do).

## Hot-patch a behavior cell

When you want to change one function rather than a whole extension, patch a behavior cell. A behavior cell holds a single function behind a fault barrier, and `recode-behavior` swaps that function in place. The cell keeps its identity, its version counter increments, and callers go on calling the same cell.

```lisp
(let ((cell (kli:find-live-object
             (kli:context-registry context)
             :my-behavior)))
  (kli/tui/core:recode-behavior cell :function #'my-new-function))
```

`recode-behavior` takes the cell and keyword arguments:

- `:function` — the new function to run.
- `:version` — set the version explicitly; omit it to increment by one.
- `:state` — replace the cell's state.
- `:metadata` — replace the cell's metadata.
- `:capabilities` — replace the capability list the cell declares.

`recode-behavior` requires the `behavior/hotpatch` capability. Passing `:state` additionally requires `behavior/state`, because changing live state is a stronger act than swapping the function; a session can be allowed to patch functions while still being denied state edits. The cell's fault policy and fault fallback are set when the cell is built and a recode cannot touch them, so a patched function that throws is still contained by the barrier it was installed behind.

### Patch a terminal-UI behavior

Terminal-UI components expose their behaviors through `recode-tui-behavior`, a generic that dispatches on what you hand it. Given a behavior cell, it delegates to `recode-behavior` with the same keyword arguments. Given a concrete UI object — an editor, the transcript, the input decoder, a frame renderer — the owning extension specializes it to find the right cell and patch that.

```lisp
(kli/tui/core:recode-tui-behavior
 (kli:find-live-object (kli:context-registry context) :editor)
 :function #'my-editor-input-handler)
```

The gating is the same: `behavior/hotpatch`, plus `behavior/state` when you pass `:state`. The UI behaviors that ship — editor input and paste, transcript scrollback, input decoding, frame rendering — each declare `behavior/hotpatch` and `behavior/state`, so a restricted session can permit or deny patching them as a group.

## Recode a policy in place

Some behavior is not a whole extension or a single behavior cell but a field of a policy on a live service. The session's context transform, which decides what extra messages are spliced into each turn, is one such policy. `kli/agent/session:recode-context-transform-policy` rebuilds that policy with one field replaced and leaves the rest intact. The new function runs behind the session fault barrier, so a transform that throws yields no extra messages rather than breaking the turn.

cairn uses this in tree to splice live task context into every turn. Its `cairn-context` effect reads the service's current `extra-messages-fn`, saves it, and recodes the policy to a function that appends cairn's task messages onto whatever the previous one returned:

```lisp
(let ((service (kli:find-live-object (kli:context-registry context)
                                     :agent-session-service)))
  (when service
    (let ((previous (getf (funcall (kli/agent/session:session-context-transform-policy
                                    service)
                                   :inspect)
                          :extra-messages-fn)))
      (kli/agent/session:recode-context-transform-policy
       service
       :extra-messages-fn
       (lambda ()
         (append (and previous (funcall previous))
                 (cairn-extra-messages context))))
      (list :service service :previous-fn previous))))
```

The effect's retractor reverses it exactly, recoding the same field back to the saved `previous-fn`. The session never restarts and never loses its scrollback: one field of a live policy is swapped, and swapped back on retract. This is the second pillar in production, [rewrite without restarting, keep the state](/kli/concepts/extensions-all-the-way-down). Unlike the kernel recodes above, it is a plain function on the agent-session service rather than a capability-gated kernel op; an extension reaches it through the live object and keeps the saved state on its own contribution.

## Snapshot the active protocol

A snapshot captures the active protocol as durable data: the list of installed extensions in activation order, the protocol's storage, and the serializable slot state of every contributed live object. Take one before a recode you are unsure about, or to move a session's state to another image.

```lisp
(let ((snapshot (kli/ext:provider-call
                 (kli/ext:require-capability-provider
                  (kli:active-protocol context)
                  :runtime/snapshot)
                 :snapshot-context context)))
  snapshot)
```

A snapshot is honest about what it cannot carry. A value it cannot serialize is named, not encoded lossily: an extension with no reconstructable manifest is listed under `:unrestorable-extensions`, storage entries it skipped under `:skipped-storage`, and per-object slots it dropped under `:skipped-slots`. What it skips is code-derived structure that reinstalling the manifests rebuilds, so the snapshot records the names rather than the bytes.

To restore, call `:restore-active-protocol` with a snapshot. A still-registered protocol is rehydrated in place; a protocol that was discarded, or one absent because the image restarted, is rebuilt from scratch by installing the captured manifests in order and rehydrating the captured storage and slot state.

```lisp
(kli/ext:provider-call
 (kli/ext:require-capability-provider
  (kli:active-protocol context)
  :runtime/snapshot)
 :restore-active-protocol context snapshot)
```

`snapshot-context` requires `protocol/snapshot` and `restore-active-protocol` requires `protocol/restore`. A session can be allowed to capture state without being allowed to overwrite it.

## The capabilities each step needs

A session that omits one of these from its `capabilities` array is denied that step; a session with the key absent runs fully permissioned and can do all of them. For the full model and how to set the array, see [Permissions and capabilities](/kli/concepts/capabilities-and-fault-barriers).

| Operation | Capability | Notes |
| --- | --- | --- |
| Evaluate a form ad hoc | `image/eval` | Gates the `/eval` command and `eval` tool, the way to run a recode form without writing an extension. |
| Swap an extension | `image/recode` | Implies `manifest/install` and `manifest/retract`. |
| Patch a behavior function | `behavior/hotpatch` | Covers `recode-behavior` and `recode-tui-behavior`. |
| Change a behavior's state | `behavior/state` | Required in addition when you pass `:state`. |
| Capture a snapshot | `protocol/snapshot` | — |
| Restore a snapshot | `protocol/restore` | — |

## Next

- The capability names and the tools each one gates: [Capabilities](/kli/config/capabilities).
- How a faulting recode stays contained instead of killing the session: [Permissions and capabilities](/kli/concepts/capabilities-and-fault-barriers).
- The full `defextension` grammar: [Extension reference](/kli/extend/lisp-extensions/anatomy).
