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

View File

@ -1,6 +1,6 @@
--- ---
display_name: AgentAPI 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 icon: ../../../../.icons/coder.svg
verified: true verified: true
tags: [internal, library] tags: [internal, library]
@ -11,47 +11,47 @@ tags: [internal, library]
> [!CAUTION] > [!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). > 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 ```tf
module "agentapi" { module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder" source = "registry.coder.com/coder/agentapi/coder"
version = "2.4.0" version = "2.5.0"
agent_id = var.agent_id agent_id = var.agent_id
web_app_slug = local.app_slug web_app_slug = local.app_slug
web_app_order = var.order
web_app_group = var.group
web_app_icon = var.icon web_app_icon = var.icon
web_app_display_name = "Goose" web_app_display_name = "Goose"
cli_app_slug = "goose-cli"
cli_app_display_name = "Goose CLI" cli_app_display_name = "Goose CLI"
cli_app_slug = "goose-cli"
module_directory = local.module_directory module_directory = local.module_directory
install_agentapi = var.install_agentapi 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 ```tf
module "agentapi" { module "agentapi" {
# ... other config # ... 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. Disabled by default. Requires agentapi >= v0.12.0.
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:
```tf ```tf
module "agentapi" { module "agentapi" {
@ -60,16 +60,38 @@ module "agentapi" {
} }
``` ```
To override file paths: Custom file paths:
```tf ```tf
module "agentapi" { module "agentapi" {
# ... other config # ... other config
enable_state_persistence = true
state_file_path = "/custom/path/state.json" state_file_path = "/custom/path/state.json"
pid_file_path = "/custom/path/agentapi.pid" 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 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

View File

@ -67,6 +67,13 @@ const setup = async (props?: SetupProps): Promise<{ id: string }> => {
skipAgentAPIMock: props?.skipAgentAPIMock, skipAgentAPIMock: props?.skipAgentAPIMock,
moduleDir: import.meta.dir, 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({ await writeExecutable({
containerId: id, containerId: id,
filePath: "/usr/bin/aiagent", filePath: "/usr/bin/aiagent",

View File

@ -46,7 +46,25 @@ export const setupContainer = async ({
agent_id: "foo", agent_id: "foo",
...vars, ...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 coderEnvVars = extractCoderEnvVars(state);
const id = await runContainer(image ?? "codercom/enterprise-node:latest"); const id = await runContainer(image ?? "codercom/enterprise-node:latest");
return { return {