fix(agentapi): update tests for coder-utils pattern and rewrite README

- test-util.ts: find coder_script by run_on_start to pick the
  coder-utils install script instead of the shutdown script
- main.test.ts: mock coder CLI for coder exp sync calls
- README.md: rewrite in the style of claude-code and codex READMEs
  with feature list, examples, and troubleshooting section
This commit is contained in:
Jay Kumar
2026-05-13 05:03:42 +00:00
parent b8ae549102
commit 28a4d4a4e3
3 changed files with 69 additions and 22 deletions
+43 -21
View File
@@ -1,6 +1,6 @@
---
display_name: AgentAPI
description: Building block for modules that need to run an AgentAPI server
description: Building block for modules that need to run an AgentAPI server.
icon: ../../../../.icons/coder.svg
verified: true
tags: [internal, library]
@@ -11,47 +11,47 @@ tags: [internal, library]
> [!CAUTION]
> We do not recommend using this module directly. Instead, please consider using one of our [Tasks-compatible AI agent modules](https://registry.coder.com/modules?search=tag%3Atasks).
The AgentAPI module is a building block for modules that need to run an AgentAPI server. It is intended primarily for internal use by Coder to create modules compatible with Tasks.
The AgentAPI module is a building block for modules that need to run an [AgentAPI](https://github.com/coder/agentapi) server. It is intended primarily for internal use by Coder to create modules compatible with [Tasks](https://coder.com/docs/ai-coder/tasks).
```tf
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "2.4.0"
version = "2.5.0"
agent_id = var.agent_id
web_app_slug = local.app_slug
web_app_order = var.order
web_app_group = var.group
web_app_icon = var.icon
web_app_display_name = "Goose"
cli_app_slug = "goose-cli"
cli_app_display_name = "Goose CLI"
cli_app_slug = "goose-cli"
module_directory = local.module_directory
install_agentapi = var.install_agentapi
}
```
## Task log snapshot
## Features
Captures the last 10 messages from AgentAPI when a task workspace stops. This allows viewing conversation history while the task is paused.
- **Web and CLI apps**: creates `coder_app` resources for browser-based chat and terminal attachment
- **Task log snapshot**: captures the last 10 conversation messages when a workspace stops, enabling offline viewing while the task is paused
- **State persistence**: optionally saves and restores AgentAPI conversation state across workspace restarts (requires agentapi >= v0.12.0)
- **Script orchestration**: uses [coder-utils](https://registry.coder.com/modules/coder/coder-utils) for `coder exp sync` based script ordering so downstream modules can serialize their own scripts behind this module
To enable for task workspaces:
## Examples
### Task log snapshot
Enabled by default. Captures the last 10 messages from AgentAPI when a task workspace stops.
```tf
module "agentapi" {
# ... other config
task_log_snapshot = true # default: true
task_log_snapshot = true # default
}
```
## State Persistence
### State persistence
AgentAPI can save and restore conversation state across workspace restarts.
This is disabled by default and requires agentapi binary >= v0.12.0.
State and PID files are stored in the `module_directory` alongside other module files (e.g. `$HOME/.coder-modules/coder/claude-code/agentapi-state.json`).
To enable:
Disabled by default. Requires agentapi >= v0.12.0.
```tf
module "agentapi" {
@@ -60,16 +60,38 @@ module "agentapi" {
}
```
To override file paths:
Custom file paths:
```tf
module "agentapi" {
# ... other config
state_file_path = "/custom/path/state.json"
pid_file_path = "/custom/path/agentapi.pid"
enable_state_persistence = true
state_file_path = "/custom/path/state.json"
pid_file_path = "/custom/path/agentapi.pid"
}
```
### Script serialization
The module outputs `scripts`, an ordered list of `coder exp sync` names. Downstream modules can use these to serialize their own `coder_script` resources behind the install pipeline:
```tf
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
# ...
}
output "scripts" {
value = module.agentapi.scripts
}
```
## For module developers
For a complete example of how to use this module, see the [Goose module](https://github.com/coder/registry/blob/main/registry/coder/modules/goose/main.tf).
For a complete example of how to build a module on top of AgentAPI, see the [Goose module](https://github.com/coder/registry/blob/main/registry/coder/modules/goose/main.tf).
## Troubleshooting
- Install logs are written to `~/.coder-modules/coder/agentapi/logs/install.log`
- AgentAPI server logs are written to `~/.coder-modules/coder/agentapi/agentapi-start.log`
- Check `agentapi --version` to verify the installed binary version
@@ -67,6 +67,13 @@ const setup = async (props?: SetupProps): Promise<{ id: string }> => {
skipAgentAPIMock: props?.skipAgentAPIMock,
moduleDir: import.meta.dir,
});
// Mock `coder` CLI so `coder exp sync` calls from coder-utils wrappers
// succeed without a real control plane.
await writeExecutable({
containerId: id,
filePath: "/usr/bin/coder",
content: "#!/bin/bash\nexit 0\n",
});
await writeExecutable({
containerId: id,
filePath: "/usr/bin/aiagent",
+19 -1
View File
@@ -46,7 +46,25 @@ export const setupContainer = async ({
agent_id: "foo",
...vars,
});
const coderScript = findResourceInstance(state, "coder_script");
// Find the run_on_start script. With coder-utils the install script lives
// inside a module and the shutdown script is a separate resource, so we
// pick the first coder_script that has run_on_start = true.
let coderScript: { script: string; [k: string]: unknown } | undefined;
for (const resource of state.resources) {
if (resource.type !== "coder_script") continue;
for (const instance of resource.instances) {
const attrs = instance.attributes as Record<string, unknown>;
if (attrs.run_on_start === true) {
coderScript = attrs as { script: string; [k: string]: unknown };
break;
}
}
if (coderScript) break;
}
if (!coderScript) {
// Fallback to original behavior for backwards compatibility.
coderScript = findResourceInstance(state, "coder_script");
}
const coderEnvVars = extractCoderEnvVars(state);
const id = await runContainer(image ?? "codercom/enterprise-node:latest");
return {