agent-afk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -0
- package/README.md +675 -0
- package/dist/cli.mjs +1607 -0
- package/dist/index.mjs +1252 -0
- package/dist/telegram.mjs +1270 -0
- package/package.json +77 -0
package/LICENSE
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
UNLICENSED
|
package/README.md
ADDED
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
# Agent AFK CLI
|
|
2
|
+
|
|
3
|
+
> A TypeScript CLI, daemon, and Telegram bot for running Claude via `@anthropic-ai/claude-agent-sdk` — ships seven orchestration skills as subagents and mirrors the `agent-framework-private` plugin surface.
|
|
4
|
+
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🚀 **Real Claude Agent SDK integration** — native subagents, hooks, elicitations, cost guardrails
|
|
10
|
+
- 🧩 **Seven orchestration skills as CLI subagents** — `/mint`, `/diagnose`, `/shadow-verify`, `/forge`, `/parallelize`, `/forge-gate-check`, `/forge-l2-eval`
|
|
11
|
+
- 🔌 **Plugin skill-router** — skills under `~/.afk/plugins/*/skills/` auto-exposed as slash commands
|
|
12
|
+
- 🏠 **AFK-scoped config** — `~/.afk/` independent of `~/.claude/`, with `afk plugin install/update/list/remove`
|
|
13
|
+
- 💬 **Three surfaces** — interactive REPL, daemon, Telegram bot sharing one session manager
|
|
14
|
+
- 📊 **Routing telemetry** — every subagent dispatch appended to `~/.claude/agent-framework/routing-decisions.jsonl`
|
|
15
|
+
- 🤖 **Multiple Claude models** — Opus, Sonnet, Haiku
|
|
16
|
+
- 🔓 **Bypass permissions mode** — no prompts, fully automated tool execution
|
|
17
|
+
- 🛡️ **Type-safe** — TypeScript strict mode
|
|
18
|
+
|
|
19
|
+
## Table of Contents
|
|
20
|
+
|
|
21
|
+
- [Installation](#installation)
|
|
22
|
+
- [Quick Start](#quick-start)
|
|
23
|
+
- [Usage](#usage)
|
|
24
|
+
- [Orchestration Skills](#orchestration-skills)
|
|
25
|
+
- [Scripts](#scripts)
|
|
26
|
+
- [Configuration](#configuration)
|
|
27
|
+
- [Plugins & Slash Commands](#plugins--slash-commands)
|
|
28
|
+
- [Bypass Permissions Mode](#bypass-permissions-mode)
|
|
29
|
+
- [Development](#development)
|
|
30
|
+
- [Troubleshooting](#troubleshooting)
|
|
31
|
+
- [API Reference](#api-reference)
|
|
32
|
+
- [License](#license)
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
### Prerequisites
|
|
37
|
+
|
|
38
|
+
- **Node.js ≥ 18.0.0**
|
|
39
|
+
- **pnpm** — this project's lockfile is pnpm-specific. Running `npm install` will desync it.
|
|
40
|
+
- Fastest path: `corepack enable` (bundled with Node ≥ 16.9), then use `pnpm` directly.
|
|
41
|
+
- Or install globally: `npm install -g pnpm@latest`.
|
|
42
|
+
- A valid Anthropic API key ([console.anthropic.com/settings/keys](https://console.anthropic.com/settings/keys))
|
|
43
|
+
|
|
44
|
+
### Install from source
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
git clone https://github.com/griffinwork40/agent-afk.git
|
|
48
|
+
cd agent-afk
|
|
49
|
+
corepack enable # optional, pins pnpm to the repo's version
|
|
50
|
+
pnpm install
|
|
51
|
+
pnpm build
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
1. **Configure your API key:**
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
cp .env.example .env
|
|
60
|
+
# Edit .env and set ANTHROPIC_API_KEY
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
2. **Build:**
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pnpm build
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
3. **Run your first command:**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pnpm start -- chat "What is 2+2?"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
4. **Check status:**
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pnpm start:status
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
> Runtime state (sessions, plugins, SDK settings) lives under `~/.afk/`, not `~/.claude/`. See [Configuration → Config Home](#config-home-afk) below.
|
|
82
|
+
|
|
83
|
+
## Usage
|
|
84
|
+
|
|
85
|
+
### Quick Aliases
|
|
86
|
+
|
|
87
|
+
- `afk c` — alias for `afk chat`
|
|
88
|
+
- `afk i` — alias for `afk interactive`
|
|
89
|
+
- `afk s` — alias for `afk status`
|
|
90
|
+
|
|
91
|
+
### CLI Commands
|
|
92
|
+
|
|
93
|
+
The `afk` CLI exposes seven top-level commands registered in `src/cli/index.ts`:
|
|
94
|
+
|
|
95
|
+
| Command | Purpose |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `chat` | Single-turn message |
|
|
98
|
+
| `interactive` | REPL with full Agent SDK features |
|
|
99
|
+
| `status` | Connection, API-key, model, bypass-mode status |
|
|
100
|
+
| `config` | Dump resolved configuration |
|
|
101
|
+
| `daemon` | Long-running headless agent (see `src/agent/daemon/`) |
|
|
102
|
+
| `login` | OAuth flow for `console.anthropic.com` |
|
|
103
|
+
| `plugin` | Manage `~/.afk/plugins/` (install / update / list / remove / enable / disable) |
|
|
104
|
+
|
|
105
|
+
#### Chat (single message)
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
pnpm start -- chat "What is the capital of France?"
|
|
109
|
+
pnpm start -- chat "Hello" --model opus
|
|
110
|
+
pnpm start -- chat "Test" --format json
|
|
111
|
+
pnpm start -- chat "Tell me a story" --stream
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### Interactive mode
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
pnpm start:interactive
|
|
118
|
+
pnpm start -- interactive --model haiku
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
In interactive mode: type messages, Claude responds with full tool access, Ctrl+C exits.
|
|
122
|
+
|
|
123
|
+
#### Status
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pnpm start:status
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Shows SDK connection, API-key validation, default model, bypass-mode state.
|
|
130
|
+
|
|
131
|
+
Supports `--format json` for machine-readable output.
|
|
132
|
+
|
|
133
|
+
#### View configuration
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
pnpm start -- config
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Supports `--format json` for machine-readable output.
|
|
140
|
+
|
|
141
|
+
#### System Health Check
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
npm start -- doctor
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Run a self-check on environment, API keys, and configuration. Use `--format json` for machine-readable output.
|
|
148
|
+
|
|
149
|
+
#### Shell Completion
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npm start -- completion <zsh|bash|fish>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Print a shell completion script. To enable completions, append to your shell rc file:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
afk completion zsh >> ~/.zshrc
|
|
159
|
+
afk completion bash >> ~/.bashrc
|
|
160
|
+
afk completion fish >> ~/.config/fish/conf.d/afk.fish
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### Plugin Management
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
npm start -- plugin list
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Supports `--format json` for machine-readable output.
|
|
170
|
+
|
|
171
|
+
### Telegram Bot
|
|
172
|
+
|
|
173
|
+
Run Claude as a Telegram bot with full Agent SDK capabilities:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# 1. Get a bot token from @BotFather on Telegram
|
|
177
|
+
# - Open Telegram, search @BotFather
|
|
178
|
+
# - Send /newbot and follow instructions, copy the token
|
|
179
|
+
# 2. Add to .env: TELEGRAM_BOT_TOKEN=1234567890:ABCdefGHI...
|
|
180
|
+
# 3. Build and run
|
|
181
|
+
pnpm build
|
|
182
|
+
pnpm telegram
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Bot commands:**
|
|
186
|
+
- `/start` — welcome
|
|
187
|
+
- `/reset` — clear conversation
|
|
188
|
+
- `/model opus|sonnet|haiku` — switch model
|
|
189
|
+
|
|
190
|
+
Send any other message to chat. The bot has full tool access via bypass mode.
|
|
191
|
+
|
|
192
|
+
Background-service helpers:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
pnpm telegram:start
|
|
196
|
+
pnpm telegram:stop
|
|
197
|
+
pnpm telegram:status
|
|
198
|
+
pnpm telegram:restart
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Orchestration Skills
|
|
202
|
+
|
|
203
|
+
agent-afk ships seven built-in subagent orchestrators plus `devils-advocate` (in development). These are **skill-router-dispatched**: typing `/mint add dark mode` in the REPL parses the slash form, resolves it to a skill handler under `src/skills/<name>/`, and dispatches a fresh subagent via `SubagentManager.forkSubagent()`. Every dispatch is logged to `~/.claude/agent-framework/routing-decisions.jsonl`.
|
|
204
|
+
|
|
205
|
+
| Skill | Purpose |
|
|
206
|
+
|---|---|
|
|
207
|
+
| `/mint` | End-to-end feature/refactor pipeline: spec → research → plan → parallelize → build → verify → heal → ship |
|
|
208
|
+
| `/diagnose` | Parallel hypothesis generation + validation for bugs and failing tests |
|
|
209
|
+
| `/shadow-verify` | Adversarial re-derivation of sub-agent claims before you act on them |
|
|
210
|
+
| `/forge` | Generate new skills autonomously, gated by L1 capability evals |
|
|
211
|
+
| `/parallelize` | Transform a linear plan into dependency-aware parallel waves |
|
|
212
|
+
| `/forge-gate-check` | Report whether `/forge` is thawed; rerun the L1 eval harness |
|
|
213
|
+
| `/forge-l2-eval` | Run L2 capability evals (live sub-agent verdict probes) |
|
|
214
|
+
|
|
215
|
+
The same seven skills ship in two surfaces:
|
|
216
|
+
|
|
217
|
+
- **CLI surface** (this repo) — TypeScript handlers under `src/skills/<name>/` invoked via `skill-router`.
|
|
218
|
+
- **Plugin surface** — prompt-based `SKILL.md` files under `agent-framework-private/skills/<name>/`, invoked inside a Claude Code session.
|
|
219
|
+
|
|
220
|
+
Vendored subagents (`qualify`, `research-agent`, `contract`) live under `src/skills/_agents/` and are kept byte-equal with the upstream copies — drift is caught by `tests/skills/_agents/vendored.test.ts`.
|
|
221
|
+
|
|
222
|
+
See the workspace-root [`SYSTEM.md`](../SYSTEM.md) for the topology, skill dependency graph, and multi-prompt loading convention.
|
|
223
|
+
|
|
224
|
+
## Scripts
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
# Build / dev
|
|
228
|
+
pnpm build # tsc && node scripts/copy-prompts.js (markdown prompts → dist/)
|
|
229
|
+
pnpm dev # tsx watch src/cli/index.ts
|
|
230
|
+
pnpm start # node dist/cli/index.js
|
|
231
|
+
pnpm start:chat # shortcut for `chat`
|
|
232
|
+
pnpm start:interactive # shortcut for `interactive`
|
|
233
|
+
pnpm start:status # shortcut for `status`
|
|
234
|
+
pnpm clean # rm -rf dist
|
|
235
|
+
|
|
236
|
+
# Testing
|
|
237
|
+
pnpm test # vitest run (all)
|
|
238
|
+
pnpm test:integration # integration tests only
|
|
239
|
+
pnpm test:e2e # end-to-end tests only
|
|
240
|
+
pnpm test:coverage # with coverage report
|
|
241
|
+
pnpm test:watch # watch mode
|
|
242
|
+
pnpm lint # tsc --noEmit (type-check only)
|
|
243
|
+
|
|
244
|
+
# Telegram daemon
|
|
245
|
+
pnpm telegram # run in foreground
|
|
246
|
+
pnpm telegram:start # background service (launchd/systemd-style wrapper)
|
|
247
|
+
pnpm telegram:stop
|
|
248
|
+
pnpm telegram:status
|
|
249
|
+
pnpm telegram:restart
|
|
250
|
+
|
|
251
|
+
# SDK dependency auditing
|
|
252
|
+
pnpm audit:sdk # regenerate docs/sdk-dependency.md snapshot
|
|
253
|
+
pnpm audit:sdk:check # CI gate — fail if new SDK symbols appear without a lock update
|
|
254
|
+
pnpm audit:sdk:update-lock # update .sdk-dependency.lock.json allowlist
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
agent-afk is the only subrepo in its workspace that imports `@anthropic-ai/claude-agent-sdk` / `@anthropic-ai/sdk` directly. The `audit:sdk*` scripts track that surface mechanically and fail CI on unauthorized drift. See [`docs/sdk-dependency.md`](docs/sdk-dependency.md) and [`.sdk-dependency.lock.json`](.sdk-dependency.lock.json). When adding a new SDK import, run `pnpm audit:sdk:update-lock` and edit the lock entry's `reason` with a one-line justification before committing.
|
|
258
|
+
|
|
259
|
+
## Configuration
|
|
260
|
+
|
|
261
|
+
### Config Home (`~/.afk/`)
|
|
262
|
+
|
|
263
|
+
agent-afk keeps its configuration and user-scope runtime state under `~/.afk/`, fully independent of Claude Code's `~/.claude/`. AFK resolves plugins, agents, commands, skills, and `settings.json` directly from that home:
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
~/.afk/
|
|
267
|
+
config/ afk.env, afk.config.json
|
|
268
|
+
state/ sessions/, todos/, daemon/
|
|
269
|
+
logs/
|
|
270
|
+
cache/
|
|
271
|
+
plugins/ user-installed Claude plugins (starts empty)
|
|
272
|
+
agents/ user-defined subagents (SDK)
|
|
273
|
+
commands/ user-defined slash commands (SDK)
|
|
274
|
+
skills/ user-defined skills (SDK)
|
|
275
|
+
settings.json SDK settings (model, permissions, MCP, etc.)
|
|
276
|
+
agent-framework/ forge telemetry + ceiling ledger + briefs
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
You can delete `~/.claude/` entirely and agent-afk still runs.
|
|
280
|
+
|
|
281
|
+
### Environment Variables
|
|
282
|
+
|
|
283
|
+
Create a `.env` file in the project root:
|
|
284
|
+
|
|
285
|
+
```env
|
|
286
|
+
# Required
|
|
287
|
+
ANTHROPIC_API_KEY=sk-ant-api03-...
|
|
288
|
+
|
|
289
|
+
# Optional
|
|
290
|
+
CLAUDE_MODEL=sonnet # opus | sonnet | haiku (default sonnet)
|
|
291
|
+
TELEGRAM_BOT_TOKEN=1234567890:ABC...
|
|
292
|
+
|
|
293
|
+
# Cost guardrails (see SDK-native features below)
|
|
294
|
+
AFK_MAX_BUDGET_USD=5.00
|
|
295
|
+
AFK_TASK_BUDGET=100000
|
|
296
|
+
|
|
297
|
+
# Prompt cache (anthropic-direct provider) — see Prompt Caching below
|
|
298
|
+
AFK_DISABLE_PROMPT_CACHE= # 1 | true | yes | on disables; unset = enabled
|
|
299
|
+
AFK_PROMPT_CACHE_TTL=1h # 5m | 1h (default 1h)
|
|
300
|
+
|
|
301
|
+
# Optional: Color output control
|
|
302
|
+
# Set NO_COLOR=1 to disable colors (per https://no-color.org)
|
|
303
|
+
# Unset or leave empty for auto-detection (disables in CI or piped output)
|
|
304
|
+
NO_COLOR=
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Prompt Caching (anthropic-direct provider)
|
|
308
|
+
|
|
309
|
+
The `anthropic-direct` provider stamps two `cache_control` breakpoints per request — one at the end of `system` (which implicitly caches `tools + system` together) and one at the end of the last `messages[]` entry. The end-of-messages marker floats forward each turn; cache lookup walks back over prefix-hash matches up to a 20-block window, so the moving marker still hits prior cache writes within a tool-use loop and across consecutive turns.
|
|
310
|
+
|
|
311
|
+
Defaults are tuned for `agent-afk`'s long-lived surfaces (daemon, Telegram bot) which often idle past the 5-minute window:
|
|
312
|
+
|
|
313
|
+
| Variable | Values | Default | Effect |
|
|
314
|
+
|---|---|---|---|
|
|
315
|
+
| `AFK_DISABLE_PROMPT_CACHE` | `1` / `true` / `yes` / `on` (case-insensitive) | unset (cache enabled) | Disables both breakpoints; useful for A/B comparisons and debugging cache-attribution issues |
|
|
316
|
+
| `AFK_PROMPT_CACHE_TTL` | `5m` / `1h` | `1h` | TTL for both breakpoints. Anything other than `5m` or `1h` falls back to the default |
|
|
317
|
+
|
|
318
|
+
Markers never leak back into stored history — `cache-policy.ts` clones-and-stamps so the canonical `messages` array stays marker-free across iterations (accumulating markers would break prefix-hash matching). Implementation: [`src/agent/providers/anthropic-direct/cache-policy.ts`](src/agent/providers/anthropic-direct/cache-policy.ts).
|
|
319
|
+
|
|
320
|
+
### Supported Models
|
|
321
|
+
|
|
322
|
+
- **opus** — most capable, for complex tasks
|
|
323
|
+
- **sonnet** — balanced performance and speed (default)
|
|
324
|
+
- **haiku** — fastest, best for simple tasks
|
|
325
|
+
|
|
326
|
+
## Plugins & Slash Commands
|
|
327
|
+
|
|
328
|
+
### Installing plugins
|
|
329
|
+
|
|
330
|
+
The `/plugin` slash command (and marketplace-based install) is a Claude Code CLI feature, not an Agent SDK feature — see [anthropics/claude-code#15071](https://github.com/anthropics/claude-code/issues/15071). Inside an agent-afk session those commands don't exist, and marketplace installs run from Claude Code still land in `~/.claude/plugins/`. AFK ships its own `afk plugin` subcommand that keeps everything under `~/.afk/plugins/`:
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
# Install from a GitHub shorthand (expands to https://github.com/<owner>/<repo>.git)
|
|
334
|
+
afk plugin install anthropics/claude-plugins-official
|
|
335
|
+
|
|
336
|
+
# Install from an explicit git URL (https or ssh)
|
|
337
|
+
afk plugin install https://github.com/example/my-plugin.git
|
|
338
|
+
|
|
339
|
+
# Install from a local checkout (symlinks into ~/.afk/plugins/)
|
|
340
|
+
afk plugin install ~/Projects/personal_projects/agent-workspace/agent-framework-private
|
|
341
|
+
|
|
342
|
+
# Pin to a specific tag, branch, or SHA
|
|
343
|
+
afk plugin install owner/repo --ref v1.2.3
|
|
344
|
+
|
|
345
|
+
# List, update, and disable without deleting
|
|
346
|
+
afk plugin list
|
|
347
|
+
afk plugin update # all plugins
|
|
348
|
+
afk plugin update claude-plugins-official
|
|
349
|
+
afk plugin disable claude-plugins-official
|
|
350
|
+
afk plugin enable claude-plugins-official
|
|
351
|
+
afk plugin remove claude-plugins-official
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
By default `install` picks the highest semver git tag, falling back to the default branch when no tag parses as semver. State lives in `~/.afk/plugins/.index.json` — the scanner reads it at session startup and skips any entry with `enabled: false`.
|
|
355
|
+
|
|
356
|
+
Advanced: AFK still auto-discovers any plugin dropped into `~/.afk/plugins/<name>/` by hand (git clone, symlink, or copy). The directory just has to contain `.claude-plugin/plugin.json`; the scanner walks up to 5 levels deep so Claude Code's `cache/<marketplace>/<plugin>/<version>/` layout also works.
|
|
357
|
+
|
|
358
|
+
Plugin state (telemetry, ledger, briefs) writes to `~/.afk/agent-framework/` in the AFK runtime.
|
|
359
|
+
|
|
360
|
+
### Plugin skills as slash commands
|
|
361
|
+
|
|
362
|
+
Every skill loaded from `~/.afk/plugins/<plugin>/skills/*/SKILL.md` is exposed automatically in the interactive REPL as `/<skill-name>`. There is no per-skill handler code — agent-afk asks the SDK for its skill catalog at session start (`session.supportedCommands()`) and registers a passthrough handler per entry. Typing `/mint add dark mode` pipes the raw line into the SDK turn loop; the subprocess parses the slash form natively and dispatches to the plugin's skill, exactly the way Claude Code does it.
|
|
363
|
+
|
|
364
|
+
- `/help` — list all available slash commands (built-in + plugin-loaded)
|
|
365
|
+
- `/skills` — discover skills loaded from plugins
|
|
366
|
+
- `/reload-plugins` — reload after editing SKILL.md files on disk
|
|
367
|
+
|
|
368
|
+
Implementation lives in `src/cli/slash/plugin-skills.ts`; see the module header for the flow.
|
|
369
|
+
|
|
370
|
+
### SDK-native features surfaced in the REPL
|
|
371
|
+
|
|
372
|
+
agent-afk wires several Claude Agent SDK capabilities that Claude Code exposes natively, so they feel the same here:
|
|
373
|
+
|
|
374
|
+
- **`/agents`** — lists Task-tool subagents loaded by the SDK (plugin + user + project scope). Agents are not user-invokable slashes; they're dispatch targets the model picks via the Task tool. The list shows name, description, and model override when present. Refresh with `/reload-plugins` after editing `~/.afk/agents/` or plugin agent definitions.
|
|
375
|
+
- **`/tokens`** — renders the authoritative SDK breakdown of context usage: total vs model max, auto-compact threshold, top categories, system tools, MCP tools, agents, skills, slash commands, and the last-turn API usage. Falls back to local-stats aggregation when the SDK call can't be served (e.g., before the subprocess is warm).
|
|
376
|
+
- **Status-line context %** — sampled every 3 turns from `session.getContextUsage()`, cached between samples, degrades gracefully on transient failures. See `src/cli/context-sampler.ts`.
|
|
377
|
+
- **Progress banners** — when the SDK emits `task_progress` events (long subagent runs, multi-tool flows), they render inline as `◦ description (stats)` plus an indented summary when present. Telegram forwards the same lines with the existing edit-throttle, and prompt suggestions trail as `💡` lines below the response. Enabled by default via `agentProgressSummaries: true`.
|
|
378
|
+
- **Cost guardrails** — pass `--max-budget-usd <n>` (or set `AFK_MAX_BUDGET_USD`) to abort the session cleanly on cost breach. `--task-budget <tokens>` (or `AFK_TASK_BUDGET`) is an advisory per-task token hint surfaced to the model so it can pace itself. Both work across `afk interactive`, `afk chat`, and the Telegram bot.
|
|
379
|
+
- **MCP elicitations** — when an MCP server requests OAuth consent (e.g. Supabase re-auth), the REPL prints the server name, message, and URL, then asks `Continue? [y/N]`. Empty answer cancels; `n` declines; `y` accepts. Form-mode elicitations are auto-declined in v1 (tracked in `todo.md`). Handler is installed via `elicitationRouter.install(...)`; bridges can install their own.
|
|
380
|
+
|
|
381
|
+
## Bypass Permissions Mode
|
|
382
|
+
|
|
383
|
+
### What is it?
|
|
384
|
+
|
|
385
|
+
By default, the Claude Agent SDK asks for permission before executing tools like:
|
|
386
|
+
- Running bash commands
|
|
387
|
+
- Reading/writing files
|
|
388
|
+
- Making web requests
|
|
389
|
+
- Etc.
|
|
390
|
+
|
|
391
|
+
**Agent AFK CLI enables `bypassPermissions: true`** which:
|
|
392
|
+
- ✅ Skips all permission prompts
|
|
393
|
+
- ✅ Allows fully automated workflows
|
|
394
|
+
- ✅ Perfect for CLI/scripting use cases
|
|
395
|
+
- ⚠️ **Requires trust** — Claude has full tool access
|
|
396
|
+
|
|
397
|
+
### Security Considerations
|
|
398
|
+
|
|
399
|
+
When bypass mode is enabled:
|
|
400
|
+
- Claude can execute bash commands
|
|
401
|
+
- Claude can read and write files in accessible directories
|
|
402
|
+
- Claude can make web requests
|
|
403
|
+
- Claude can use grep, glob, and other system tools
|
|
404
|
+
|
|
405
|
+
**Use in trusted environments only.** This mode is designed for:
|
|
406
|
+
- Personal CLI tools
|
|
407
|
+
- Automation scripts
|
|
408
|
+
- Development environments
|
|
409
|
+
- Trusted agent workflows
|
|
410
|
+
|
|
411
|
+
### Default Allowed Tools
|
|
412
|
+
|
|
413
|
+
From `src/agent/session/query-options.ts`:
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
[
|
|
417
|
+
'Bash',
|
|
418
|
+
'Read',
|
|
419
|
+
'Write',
|
|
420
|
+
'Edit',
|
|
421
|
+
'Glob',
|
|
422
|
+
'Grep',
|
|
423
|
+
'WebSearch',
|
|
424
|
+
'WebFetch',
|
|
425
|
+
'LS',
|
|
426
|
+
'Task',
|
|
427
|
+
'BashOutput',
|
|
428
|
+
]
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
Override per-session by passing `config.tools.allowedTools` to `AgentSession`, or disable individual tools via `config.tools.disallowedTools`.
|
|
432
|
+
|
|
433
|
+
## Development
|
|
434
|
+
|
|
435
|
+
### Project Structure
|
|
436
|
+
|
|
437
|
+
```
|
|
438
|
+
agent-afk/
|
|
439
|
+
├── src/
|
|
440
|
+
│ ├── cli/
|
|
441
|
+
│ │ ├── index.ts # CLI entry (commander)
|
|
442
|
+
│ │ └── commands/ # chat, interactive, status, config, daemon, login, plugin, skill-router
|
|
443
|
+
│ ├── agent/
|
|
444
|
+
│ │ ├── session.ts # AgentSession barrel
|
|
445
|
+
│ │ ├── session/ # agent-session, query-options, …
|
|
446
|
+
│ │ ├── subagent.ts # SubagentManager barrel
|
|
447
|
+
│ │ ├── subagent/ # forkSubagent implementation
|
|
448
|
+
│ │ ├── subagent-hooks.ts
|
|
449
|
+
│ │ ├── routing-telemetry.ts # appends routing-decisions.jsonl
|
|
450
|
+
│ │ ├── daemon/ # long-running headless agent
|
|
451
|
+
│ │ ├── plugins/ # afk plugin install / update / remove / index-store
|
|
452
|
+
│ │ ├── providers/ # provider abstraction
|
|
453
|
+
│ │ ├── elicitation-router.ts
|
|
454
|
+
│ │ ├── hook-registry.ts, hooks.ts, default-hook-registry.ts
|
|
455
|
+
│ │ ├── permissions.ts, abort-graph.ts, dag.ts, message-queue.ts
|
|
456
|
+
│ │ ├── output-extractor.ts, plugins-scanner.ts, timeout.ts
|
|
457
|
+
│ │ ├── shadow-verify-nudge.ts
|
|
458
|
+
│ │ └── types.ts, types/
|
|
459
|
+
│ ├── skills/
|
|
460
|
+
│ │ ├── mint/ # /mint
|
|
461
|
+
│ │ ├── diagnose/ # /diagnose
|
|
462
|
+
│ │ ├── shadow-verify/ # /shadow-verify
|
|
463
|
+
│ │ ├── forge/ # /forge
|
|
464
|
+
│ │ ├── parallelize/ # /parallelize
|
|
465
|
+
│ │ ├── forge-gate-check/ # /forge-gate-check
|
|
466
|
+
│ │ ├── forge-l2-eval/ # /forge-l2-eval
|
|
467
|
+
│ │ ├── devils-advocate/ # in development
|
|
468
|
+
│ │ ├── _agents/ # vendored subagents (qualify, research-agent, contract)
|
|
469
|
+
│ │ ├── _lib/ # prompt-loader, shared helpers
|
|
470
|
+
│ │ ├── example-template/ # scaffold for new skills
|
|
471
|
+
│ │ └── index.ts
|
|
472
|
+
│ ├── telegram/ # telegram bridge
|
|
473
|
+
│ ├── telegram.ts # telegram bot entry
|
|
474
|
+
│ ├── utils/
|
|
475
|
+
│ ├── paths.ts
|
|
476
|
+
│ └── index.ts
|
|
477
|
+
├── tests/
|
|
478
|
+
│ ├── integration/
|
|
479
|
+
│ └── e2e/
|
|
480
|
+
├── scripts/
|
|
481
|
+
│ ├── copy-prompts.js # bundles src/**/*.md into dist/ after tsc
|
|
482
|
+
│ ├── audit-sdk-dependency.ts
|
|
483
|
+
│ └── telegram-manager.sh
|
|
484
|
+
├── docs/
|
|
485
|
+
│ └── sdk-dependency.md # committed SDK symbol snapshot
|
|
486
|
+
├── .sdk-dependency.lock.json # SDK symbol allowlist (CI-gated)
|
|
487
|
+
├── pnpm-lock.yaml
|
|
488
|
+
├── package.json
|
|
489
|
+
├── tsconfig.json
|
|
490
|
+
├── vitest.config.ts
|
|
491
|
+
└── README.md
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Build Process
|
|
495
|
+
|
|
496
|
+
```bash
|
|
497
|
+
pnpm clean && pnpm build # full clean rebuild
|
|
498
|
+
pnpm dev # auto-rebuild (tsx watch)
|
|
499
|
+
pnpm lint # type-check without emitting
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
`pnpm build` runs `tsc` and then `scripts/copy-prompts.js`, which copies every `src/**/*.md` file into `dist/` at matching relative paths. Skills read their prompts via `readFileSync` at import time, so those markdown files must live next to the compiled `.js` output.
|
|
503
|
+
|
|
504
|
+
### SDK Dependency Tracking
|
|
505
|
+
|
|
506
|
+
agent-afk is the only subrepo in its workspace that imports from `@anthropic-ai/claude-agent-sdk` or `@anthropic-ai/sdk`. That surface is tracked mechanically:
|
|
507
|
+
|
|
508
|
+
- [`docs/sdk-dependency.md`](docs/sdk-dependency.md) — committed snapshot of every tracked symbol, its files, and runtime-vs-type classification.
|
|
509
|
+
- [`.sdk-dependency.lock.json`](.sdk-dependency.lock.json) — allowlist with per-symbol `reason` fields. CI fails when a new symbol or kind-change appears without a lock update.
|
|
510
|
+
- `~/.afk/agent-framework/sdk-dependency-telemetry.jsonl` — append-only log of symbol-count deltas and SHA over time.
|
|
511
|
+
|
|
512
|
+
When adding a new SDK import: run `pnpm audit:sdk:update-lock`, then edit the generated lock entry's `reason` with a one-line justification before committing.
|
|
513
|
+
|
|
514
|
+
### Testing
|
|
515
|
+
|
|
516
|
+
```bash
|
|
517
|
+
pnpm test # all
|
|
518
|
+
pnpm test:coverage # with coverage
|
|
519
|
+
pnpm test:watch # watch mode
|
|
520
|
+
pnpm test:integration # integration only
|
|
521
|
+
pnpm test:e2e # e2e only
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
## Troubleshooting
|
|
525
|
+
|
|
526
|
+
### API Key Issues
|
|
527
|
+
|
|
528
|
+
**Error: `invalid x-api-key`**
|
|
529
|
+
|
|
530
|
+
Your API key is invalid or expired. Get a new one at [console.anthropic.com/settings/keys](https://console.anthropic.com/settings/keys).
|
|
531
|
+
|
|
532
|
+
```bash
|
|
533
|
+
# Test your API key
|
|
534
|
+
node -e "
|
|
535
|
+
import('@anthropic-ai/sdk').then(async (Anthropic) => {
|
|
536
|
+
const dotenv = await import('dotenv');
|
|
537
|
+
dotenv.config();
|
|
538
|
+
const client = new Anthropic.default({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
539
|
+
const message = await client.messages.create({
|
|
540
|
+
max_tokens: 10,
|
|
541
|
+
messages: [{ role: 'user', content: 'Hi' }],
|
|
542
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
543
|
+
});
|
|
544
|
+
console.log('✓ API Key works!');
|
|
545
|
+
}).catch(e => console.error('✗ API Key error:', e.message));
|
|
546
|
+
"
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
**Error: `ANTHROPIC_API_KEY not found`**
|
|
550
|
+
|
|
551
|
+
Check that:
|
|
552
|
+
1. `.env` file exists in project root
|
|
553
|
+
2. Contains `ANTHROPIC_API_KEY=sk-ant-...`
|
|
554
|
+
3. No quotes around the key value
|
|
555
|
+
4. No trailing spaces
|
|
556
|
+
|
|
557
|
+
### Build Issues
|
|
558
|
+
|
|
559
|
+
**TypeScript errors:**
|
|
560
|
+
|
|
561
|
+
```bash
|
|
562
|
+
pnpm clean
|
|
563
|
+
rm -rf node_modules
|
|
564
|
+
pnpm install
|
|
565
|
+
pnpm build
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
**Module not found:**
|
|
569
|
+
|
|
570
|
+
```bash
|
|
571
|
+
pnpm build
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### Runtime Issues
|
|
575
|
+
|
|
576
|
+
**`Cannot send message: session is closed`** — the session was closed or timed out. Create a new one.
|
|
577
|
+
|
|
578
|
+
**Timeout errors** — increase max turns:
|
|
579
|
+
|
|
580
|
+
```bash
|
|
581
|
+
pnpm start -- chat "complex task" --max-turns 50
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
**`Maximum turns exceeded`** — the conversation hit the turn limit (a safety feature). Raise `--max-turns` if needed.
|
|
585
|
+
|
|
586
|
+
## API Reference
|
|
587
|
+
|
|
588
|
+
### AgentSession
|
|
589
|
+
|
|
590
|
+
```typescript
|
|
591
|
+
import { AgentSession } from './agent/session.js';
|
|
592
|
+
|
|
593
|
+
const session = new AgentSession({
|
|
594
|
+
model: 'sonnet',
|
|
595
|
+
apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
596
|
+
maxTurns: 100,
|
|
597
|
+
systemPrompt: 'You are a helpful assistant.',
|
|
598
|
+
tools: {
|
|
599
|
+
allowedTools: ['Bash', 'Read', 'Write'],
|
|
600
|
+
},
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
// Single message
|
|
604
|
+
const response = await session.sendMessage('Hello!');
|
|
605
|
+
console.log(response.content);
|
|
606
|
+
|
|
607
|
+
// Streaming (async iterator)
|
|
608
|
+
for await (const event of session.sendMessageStream('Tell me a story')) {
|
|
609
|
+
process.stdout.write(event.text ?? '');
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Runtime control
|
|
613
|
+
await session.interrupt(); // cancel in-flight turn
|
|
614
|
+
await session.setModel('opus'); // hot-swap model
|
|
615
|
+
await session.setPermissionMode('acceptEdits'); // switch permission mode
|
|
616
|
+
|
|
617
|
+
// State accessors
|
|
618
|
+
session.state; // 'idle' | 'processing' | 'streaming' | 'closed'
|
|
619
|
+
session.sessionId; // string | undefined
|
|
620
|
+
session.abortSignal; // AbortSignal
|
|
621
|
+
const history = session.getHistory();
|
|
622
|
+
|
|
623
|
+
// Cleanup
|
|
624
|
+
await session.close();
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
### Types
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
interface AgentConfig {
|
|
631
|
+
model: 'opus' | 'sonnet' | 'haiku';
|
|
632
|
+
apiKey: string;
|
|
633
|
+
maxTurns?: number;
|
|
634
|
+
systemPrompt?: string;
|
|
635
|
+
tools?: {
|
|
636
|
+
allowedTools?: string[];
|
|
637
|
+
disallowedTools?: string[];
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
interface Message {
|
|
642
|
+
role: 'user' | 'assistant';
|
|
643
|
+
content: string;
|
|
644
|
+
timestamp?: Date;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
type SessionState = 'idle' | 'processing' | 'streaming' | 'closed';
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
## Contributing
|
|
651
|
+
|
|
652
|
+
Contributions welcome. Standard flow:
|
|
653
|
+
|
|
654
|
+
1. Fork the repository
|
|
655
|
+
2. Create a feature branch
|
|
656
|
+
3. Make your changes
|
|
657
|
+
4. Add tests
|
|
658
|
+
5. Run `pnpm test && pnpm lint`
|
|
659
|
+
6. Open a pull request
|
|
660
|
+
|
|
661
|
+
New orchestration skills, CI gate changes, and ceiling-ledger conventions are documented in the workspace-root [`SYSTEM.md`](../SYSTEM.md).
|
|
662
|
+
|
|
663
|
+
## License
|
|
664
|
+
|
|
665
|
+
MIT © Griffin Long
|
|
666
|
+
|
|
667
|
+
## Acknowledgments
|
|
668
|
+
|
|
669
|
+
- Built with [@anthropic-ai/claude-agent-sdk](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk)
|
|
670
|
+
- CLI framework: [Commander.js](https://github.com/tj/commander.js)
|
|
671
|
+
- Testing: [Vitest](https://vitest.dev/)
|
|
672
|
+
|
|
673
|
+
---
|
|
674
|
+
|
|
675
|
+
**Note:** This tool uses bypass permissions mode. Only use in trusted environments where you're comfortable giving Claude full tool access.
|