athena-claude-coder 0.4.1__tar.gz
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.
- athena_claude_coder-0.4.1/.gitignore +274 -0
- athena_claude_coder-0.4.1/PKG-INFO +308 -0
- athena_claude_coder-0.4.1/README.md +289 -0
- athena_claude_coder-0.4.1/pyproject.toml +59 -0
- athena_claude_coder-0.4.1/scripts/athena-claude +162 -0
- athena_claude_coder-0.4.1/src/athena_claude_coder/__init__.py +17 -0
- athena_claude_coder-0.4.1/src/athena_claude_coder/app.py +492 -0
- athena_claude_coder-0.4.1/src/athena_claude_coder/auth.py +45 -0
- athena_claude_coder-0.4.1/src/athena_claude_coder/main.py +113 -0
- athena_claude_coder-0.4.1/src/athena_claude_coder/permissions.py +222 -0
- athena_claude_coder-0.4.1/src/athena_claude_coder/redact.py +75 -0
- athena_claude_coder-0.4.1/src/athena_claude_coder/runner.py +1090 -0
- athena_claude_coder-0.4.1/src/athena_claude_coder/schema.py +62 -0
- athena_claude_coder-0.4.1/src/athena_claude_coder/settings.py +149 -0
- athena_claude_coder-0.4.1/src/athena_claude_coder/store.py +650 -0
- athena_claude_coder-0.4.1/src/athena_claude_coder/transcript.py +326 -0
- athena_claude_coder-0.4.1/src/athena_claude_coder/worktrees.py +151 -0
- athena_claude_coder-0.4.1/tests/__init__.py +1 -0
- athena_claude_coder-0.4.1/tests/conftest.py +306 -0
- athena_claude_coder-0.4.1/tests/test_guest_token.py +202 -0
- athena_claude_coder-0.4.1/tests/test_handoff.py +402 -0
- athena_claude_coder-0.4.1/tests/test_launcher.py +348 -0
- athena_claude_coder-0.4.1/tests/test_main.py +131 -0
- athena_claude_coder-0.4.1/tests/test_permissions.py +260 -0
- athena_claude_coder-0.4.1/tests/test_protocol_contract.py +540 -0
- athena_claude_coder-0.4.1/tests/test_runner_lifecycle.py +771 -0
- athena_claude_coder-0.4.1/tests/test_sdk_post_merge.py +64 -0
- athena_claude_coder-0.4.1/tests/test_store.py +343 -0
- athena_claude_coder-0.4.1/tests/test_transcript.py +531 -0
- athena_claude_coder-0.4.1/tests/test_worktrees.py +660 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# remove boilerplate repos
|
|
2
|
+
mock_api/
|
|
3
|
+
react-scripts_TS/
|
|
4
|
+
simple-nextjs_TS/
|
|
5
|
+
simple-react-srcipts_TS/
|
|
6
|
+
.DS_Store
|
|
7
|
+
.vercel/output/builds.json
|
|
8
|
+
.vercel
|
|
9
|
+
c2poutput
|
|
10
|
+
meeting_recordings/
|
|
11
|
+
node_modules
|
|
12
|
+
|
|
13
|
+
# Ignore Terraform files
|
|
14
|
+
.terraform/
|
|
15
|
+
*.tfstate
|
|
16
|
+
*.tfstate.*
|
|
17
|
+
backend/notebooks/gmailoauth/token.json
|
|
18
|
+
.next*
|
|
19
|
+
.vscode*
|
|
20
|
+
!.vscode/extensions.json
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# papyrus
|
|
24
|
+
papyrus/**/dist/**/*
|
|
25
|
+
__pycache__
|
|
26
|
+
**/.ipynb_checkpoints/**/*
|
|
27
|
+
papyrus/jupyter-server-assistant-api/jupyter_server_assistant_api/static/
|
|
28
|
+
|
|
29
|
+
agora/download.pdf
|
|
30
|
+
|
|
31
|
+
.gait/custom-merge-driver.sh
|
|
32
|
+
|
|
33
|
+
.pre-commit-config.yaml
|
|
34
|
+
|
|
35
|
+
# Environment files
|
|
36
|
+
.env.production
|
|
37
|
+
.aider*
|
|
38
|
+
.env
|
|
39
|
+
# Hades env files are safe to commit (no secrets — just URLs and mode flags)
|
|
40
|
+
!hades/.env
|
|
41
|
+
!hades/.env.production
|
|
42
|
+
!hades/.env.example
|
|
43
|
+
commit-review.zsh
|
|
44
|
+
|
|
45
|
+
# CI logs output
|
|
46
|
+
/scripts/logs/
|
|
47
|
+
|
|
48
|
+
converter-olympus/dist
|
|
49
|
+
*.bun-build
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
/agora/scripts/yjs/
|
|
53
|
+
agora/converter-olympus/src/doc/tiptap.json
|
|
54
|
+
agora/converter-olympus/src/doc/markdown.html
|
|
55
|
+
agora/tmp/*
|
|
56
|
+
olympus/.eslint_fixed_files
|
|
57
|
+
olympus/.eslint_processing_files
|
|
58
|
+
olympus/.eslint_files_with_issues
|
|
59
|
+
olympus/.eslint_lock
|
|
60
|
+
precommit-prettier.log
|
|
61
|
+
precommit-biome.log
|
|
62
|
+
|
|
63
|
+
# Playwright
|
|
64
|
+
integration-tests/test-results/
|
|
65
|
+
integration-tests/playwright-report/
|
|
66
|
+
integration-tests/blob-report/
|
|
67
|
+
integration-tests/playwright/.cache/
|
|
68
|
+
olympus/test-results/
|
|
69
|
+
olympus/playwright-report/
|
|
70
|
+
olympus/playwright/.auth/
|
|
71
|
+
olympus/playwright/.cache/
|
|
72
|
+
agora/scripts/.migration_config.json
|
|
73
|
+
agora/scripts/.checkpoint_analysis_config.json
|
|
74
|
+
agora/scripts/.thread_cleanup_config.json
|
|
75
|
+
agora/scripts/.windmill_update_config.json
|
|
76
|
+
agora/ryvn/tmpenv
|
|
77
|
+
agora/tmpenv
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# Last chosen Doppler config (script configuration)
|
|
81
|
+
.last_doppler_config
|
|
82
|
+
|
|
83
|
+
# Last chosen Infisical environment (script configuration)
|
|
84
|
+
.last_infisical_env
|
|
85
|
+
|
|
86
|
+
# Setup script hash files
|
|
87
|
+
.last_*_hash
|
|
88
|
+
|
|
89
|
+
# Setup completion markers
|
|
90
|
+
.setup_complete
|
|
91
|
+
|
|
92
|
+
agora/scripts/.interactive_runner_last
|
|
93
|
+
agora/scripts/.aop_backfill_config.json
|
|
94
|
+
agora/scripts/.workflow_runner_last.json
|
|
95
|
+
|
|
96
|
+
# Act workflow runner secrets
|
|
97
|
+
agora/scripts/.act.secrets
|
|
98
|
+
|
|
99
|
+
# Temporary reference repo for comments UI update
|
|
100
|
+
next-comments-template/
|
|
101
|
+
agora/test-results/
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# SWE directories
|
|
105
|
+
swe/**/dist
|
|
106
|
+
swe/**/.turbo
|
|
107
|
+
# Claude Code local commands (user-specific, generated)
|
|
108
|
+
.claude/commands/local/
|
|
109
|
+
.branch-prefix
|
|
110
|
+
|
|
111
|
+
# Plans generated by models (untracked plans stay ignored; force-add specific files as needed)
|
|
112
|
+
docs/plans/*
|
|
113
|
+
# Customer-SSO computer-asset viewers (#31218) — design investigation & recommended architecture — deliberately committed
|
|
114
|
+
!docs/plans/2026-08-26-customer-sso-computer-asset-viewers-design.md
|
|
115
|
+
# Computer Time Travel v1 (snapshots + rollback) approved implementation plan — deliberately committed
|
|
116
|
+
!docs/plans/2026-08-27-computer-time-travel-snapshots.md
|
|
117
|
+
!docs/plans/2026-09-02-talos-shared-store-stability-thesis.md
|
|
118
|
+
!docs/plans/2026-08-31-microsandbox-boundary-secrets-design.md
|
|
119
|
+
!docs/plans/2026-09-01-talos-v2-golive-readiness-audit.md
|
|
120
|
+
# Computer SSH: identity-based access + `athena ssh` CLI + wake-on-connect — approved direction, deliberately committed
|
|
121
|
+
!docs/plans/2026-09-02-computer-ssh-identity-and-cli.md
|
|
122
|
+
# CLI browser login (RFC 8628 device authorization) — wire contract + security record, deliberately committed
|
|
123
|
+
!docs/plans/2026-09-02-cli-browser-login.md
|
|
124
|
+
!docs/plans/2026-08-31-office-addin-voice-ws-relay-spike.md
|
|
125
|
+
!docs/plans/2026-08-31-drain-auto-continue-superseded-gate-fix.md
|
|
126
|
+
# Admin dashboard consolidation audit — the stay/move/remove record behind the tab removals — deliberately committed
|
|
127
|
+
!docs/plans/2026-09-01-admin-dashboard-consolidation-audit.md
|
|
128
|
+
!docs/plans/2026-09-02-athena-agent-next-batch.md
|
|
129
|
+
!docs/plans/2026-09-02-claude-code-coding-worker-proposal.md
|
|
130
|
+
!docs/plans/2026-09-02-claude-code-coding-worker-implementation-plan.md
|
|
131
|
+
# Customer-SSO viewers (#31218) — post-implementation review & revised plan — deliberately committed
|
|
132
|
+
!docs/plans/2026-08-26-customer-sso-viewers-revised-plan.md
|
|
133
|
+
# Talos v2 hardening-audit roadmap (2026-08-29) — deliberately committed
|
|
134
|
+
!docs/plans/2026-08-29-talos-v2-hardening-roadmap.md
|
|
135
|
+
# Session Sandbox Toolkit (grants + placements) approved implementation plan — deliberately committed
|
|
136
|
+
!docs/plans/2026-08-28-session-sandbox-toolkit-grants-placements.md
|
|
137
|
+
# Talos production stability findings and overhaul plan — deliberately committed
|
|
138
|
+
!docs/plans/2026-08-25-canonical-source-identity-wave1.md
|
|
139
|
+
# BYO environments (Devin-style) + seamless GitHub connect program plan — deliberately committed
|
|
140
|
+
!docs/plans/2026-08-25-byo-environments-and-seamless-github.md
|
|
141
|
+
!docs/plans/2026-07-29-talos-production-stability-overhaul.md
|
|
142
|
+
# Computer warm adoption on a shared JuiceFS store (~1-2s spin-up) — deliberately committed
|
|
143
|
+
!docs/plans/2026-08-20-computer-shared-juicefs-warm-adoption.md
|
|
144
|
+
# Talos M2 completion plan, referenced from issue #28028 — deliberately committed
|
|
145
|
+
!docs/plans/2026-07-30-talos-m2-completion.md
|
|
146
|
+
!docs/plans/2026-08-04-agent-service-identity-and-vaults.md
|
|
147
|
+
# Prometheus catalog provider plan — deliberately committed
|
|
148
|
+
!docs/plans/2026-07-30-prometheus-catalog-asset.md
|
|
149
|
+
# SQL-in-notebooks (catalog-connected SQL cells) plan — deliberately committed
|
|
150
|
+
!docs/plans/2026-07-31-sql-in-notebooks.md
|
|
151
|
+
# Collab-agent orchestration layer (tasks/triggers/monitors/channels) plan — deliberately committed
|
|
152
|
+
!docs/plans/2026-08-01-collab-agent-orchestration-layer.md
|
|
153
|
+
# Environments-on-Talos program plan (environment primitive, phases 1-6) — deliberately committed
|
|
154
|
+
!docs/plans/2026-08-04-environments-on-talos-program-plan.md
|
|
155
|
+
# Environments program shipped-vs-remaining status record — deliberately committed
|
|
156
|
+
!docs/plans/2026-08-04-environments-program-status.md
|
|
157
|
+
# In-house Paragon-parity program plan (direct connectors) — deliberately committed
|
|
158
|
+
!docs/plans/2026-08-13-inhouse-paragon-parity-program.md
|
|
159
|
+
# 2026-08-19 refactoring audit backlog (issue #30277) — deliberately committed
|
|
160
|
+
!docs/plans/2026-08-19-refactoring-audit.md
|
|
161
|
+
# rnc audit artifacts — deliberately committed, kept genuinely un-ignored (not just force-tracked)
|
|
162
|
+
!docs/plans/rnc-phase0-harness/
|
|
163
|
+
!docs/plans/rnc-phase0-harness/**
|
|
164
|
+
!docs/plans/2026-06-12-rnc-ops-inventory.md
|
|
165
|
+
!docs/plans/2026-06-12-rnc-audit-remediation-plan.md
|
|
166
|
+
!docs/plans/2026-06-13-rnc-ws7-sharding-reconciliation.md
|
|
167
|
+
!docs/plans/2026-06-13-rnc-ws7-ytablestore-design.md
|
|
168
|
+
# Twilio SMS collab-agent channel design doc — deliberately committed
|
|
169
|
+
!docs/plans/2026-06-13-twilio-sms-collab-agent-channel.md
|
|
170
|
+
# Collab-agent calendar-invite attendance (Recall v1.11) design doc — deliberately committed
|
|
171
|
+
!docs/plans/2026-06-15-collab-agent-calendar-invite-attendance.md
|
|
172
|
+
# Collab-agent calendar-invite Phase 0 spike artifacts — deliberately committed
|
|
173
|
+
!docs/plans/calendar-invite-phase0/
|
|
174
|
+
!docs/plans/calendar-invite-phase0/**
|
|
175
|
+
# Tool Asset (Windmill runtime) design doc — deliberately committed
|
|
176
|
+
!docs/plans/2026-06-16-tool-asset-windmill-runtime.md
|
|
177
|
+
# Tool Asset (Custom Tool UI) design doc — deliberately committed
|
|
178
|
+
!docs/plans/2026-06-16-tool-asset-custom-ui.md
|
|
179
|
+
# Treasury credential-delegation design proposal — deliberately committed
|
|
180
|
+
!docs/plans/2026-06-22-treasury-credential-delegation-proposal.md
|
|
181
|
+
# OpenFGA rollout-readiness punch-list — deliberately committed (referenced by the guide)
|
|
182
|
+
!docs/plans/2026-06-25-openfga-rollout-readiness-punchlist.md
|
|
183
|
+
# OpenFGA DB-vs-store reconciliation/GC job design — deliberately committed
|
|
184
|
+
!docs/plans/2026-06-25-fga-db-vs-store-reconciliation-design.md
|
|
185
|
+
# Agora/Olympus preview pipeline build-time audit — deliberately committed
|
|
186
|
+
!docs/plans/2026-07-02-agora-olympus-preview-build-time-audit.md
|
|
187
|
+
# Katten destructive-AOP smoke-testing design doc — deliberately committed
|
|
188
|
+
!docs/plans/2026-07-07-katten-destructive-aop-smoke-testing.md
|
|
189
|
+
# Postgres connection & operation audit (2026-07-10) — deliberately committed
|
|
190
|
+
!docs/plans/2026-07-10-db-connection-audit.md
|
|
191
|
+
# Deep-agent runtime parity investigation findings (2026-07-21) — deliberately committed
|
|
192
|
+
!docs/plans/2026-07-21-deep-agent-runtime-parity-findings.md
|
|
193
|
+
# AOP orchestration refactor findings & plan (2026-07-24) — deliberately committed
|
|
194
|
+
!docs/plans/2026-07-24-aop-orchestration-refactor.md
|
|
195
|
+
# Mobile HITL approval push design (2026-07-26) — deliberately committed
|
|
196
|
+
!docs/plans/2026-07-26-mobile-hitl-approval-push.md
|
|
197
|
+
|
|
198
|
+
# Talos production stability milestones are long-lived implementation records.
|
|
199
|
+
!docs/plans/2026-07-30-talos-m4-rollout-readiness.md
|
|
200
|
+
!docs/plans/2026-07-30-talos-m3-completion-plan.md
|
|
201
|
+
|
|
202
|
+
!docs/plans/2026-08-01-talos-drain-barrier.md
|
|
203
|
+
|
|
204
|
+
!docs/plans/2026-08-21-event-loop-stall-remediation.md
|
|
205
|
+
|
|
206
|
+
!docs/plans/2026-08-24-sandbox-stack-audit-remediation.md
|
|
207
|
+
# Agent-sandbox strategy execution plan (companion to talos/docs/AGENT_SANDBOX_STRATEGY.md) — deliberately committed
|
|
208
|
+
!docs/plans/2026-08-25-agent-sandbox-strategy-execution.md
|
|
209
|
+
# AWS cost optimization private plaintext. Commit encrypted bundles only.
|
|
210
|
+
aws-cost-optimization/private/
|
|
211
|
+
aws-cost-optimization/shared/context.md
|
|
212
|
+
aws-cost-optimization/keys/*.pem
|
|
213
|
+
aws-cost-optimization/keys/*.pem*
|
|
214
|
+
!aws-cost-optimization/keys/aws-cost-public.pem
|
|
215
|
+
aws-cost-optimization/*.decrypted.md
|
|
216
|
+
aws-cost-optimization/.decrypt-private.*
|
|
217
|
+
aws-cost-optimization/.env
|
|
218
|
+
aws-cost-optimization/.env.*
|
|
219
|
+
!aws-cost-optimization/.env.example
|
|
220
|
+
|
|
221
|
+
# Next.js generated files
|
|
222
|
+
olympus/next-env.d.ts
|
|
223
|
+
agora/.pytest_cache
|
|
224
|
+
olympus/.turbo
|
|
225
|
+
.turbo
|
|
226
|
+
|
|
227
|
+
# SDK build output
|
|
228
|
+
packages/react/dist
|
|
229
|
+
packages/references/dist
|
|
230
|
+
packages/chat-sdk/dist
|
|
231
|
+
athenaintel-admin/**/dist
|
|
232
|
+
|
|
233
|
+
# Root bun workspace — only the root bun.lock is canonical. Reject any
|
|
234
|
+
# reintroduction of per-package lockfiles from other package managers.
|
|
235
|
+
pnpm-lock.yaml
|
|
236
|
+
pnpm-workspace.yaml
|
|
237
|
+
package-lock.json
|
|
238
|
+
yarn.lock
|
|
239
|
+
# desktop/ and hades/ are intentionally NOT part of the bun workspace —
|
|
240
|
+
# electron-builder's "install production dependencies" step shells out to
|
|
241
|
+
# $npm_execpath, which under bun resolves to the bun native binary and node
|
|
242
|
+
# then chokes parsing it as JS. Both projects keep their original lockfiles:
|
|
243
|
+
# desktop → npm (package-lock.json), hades → pnpm (pnpm-lock.yaml).
|
|
244
|
+
!desktop/package-lock.json
|
|
245
|
+
!hades/pnpm-lock.yaml
|
|
246
|
+
# Nested bun.lock files (root bun.lock wins)
|
|
247
|
+
agora/converter-olympus/bun.lock
|
|
248
|
+
packages/*/bun.lock
|
|
249
|
+
pptx-studio/**/bun.lock
|
|
250
|
+
olympus/bun.lock
|
|
251
|
+
keryx/bun.lock
|
|
252
|
+
|
|
253
|
+
# TypeScript build info
|
|
254
|
+
*.tsbuildinfo
|
|
255
|
+
|
|
256
|
+
# Deep Agents CLI (langchain-ai/deepagents)
|
|
257
|
+
.deepagents/
|
|
258
|
+
|
|
259
|
+
# Local git worktrees created by agent workflows
|
|
260
|
+
.worktrees/
|
|
261
|
+
.cmux/
|
|
262
|
+
|
|
263
|
+
# Chart dependencies are rebuilt by the release workflow before packaging.
|
|
264
|
+
helm/charts/athenaintel-sandbox/charts/*.tgz
|
|
265
|
+
helm/charts/athenaintel-orpheus/charts/
|
|
266
|
+
|
|
267
|
+
# Agent workflow scratch artifacts (never commit)
|
|
268
|
+
conversation_history/
|
|
269
|
+
/tmp/
|
|
270
|
+
!docs/plans/2026-08-26-computer-fork-and-vm-download.md
|
|
271
|
+
!docs/plans/2026-08-26-computer-use-on-computer-assets.md
|
|
272
|
+
# Mobile release pipeline repair (EAS quota) + OTA updates and mobile backlog — deliberately committed
|
|
273
|
+
!docs/plans/2026-08-27-mobile-app-pipeline-and-ota.md
|
|
274
|
+
!docs/plans/2026-09-02-warm-rnc-engine-service-design.md
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: athena-claude-coder
|
|
3
|
+
Version: 0.4.1
|
|
4
|
+
Summary: In-guest Agent Protocol server that drives the Claude Agent SDK for Athena coding tasks
|
|
5
|
+
License: Proprietary
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: aiosqlite<1,>=0.20
|
|
8
|
+
Requires-Dist: claude-agent-sdk==0.2.151
|
|
9
|
+
Requires-Dist: fastapi<1,>=0.115
|
|
10
|
+
Requires-Dist: pydantic<3,>=2.7
|
|
11
|
+
Requires-Dist: uvicorn<1,>=0.30
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
14
|
+
Requires-Dist: langgraph-sdk==0.4.2; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
17
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# athena-claude-coder
|
|
21
|
+
|
|
22
|
+
The in-guest **Agent Protocol** server that drives the **Claude Agent SDK** for
|
|
23
|
+
Athena coding tasks ([ADR 0006](../../docs/decisions/0006-claude-code-coding-worker.md)).
|
|
24
|
+
|
|
25
|
+
The Athena deep agent delegates repository work to a `claude-coder` async
|
|
26
|
+
subagent (`deepagents` `AsyncSubAgent`). Its calls travel through the agora
|
|
27
|
+
coding-worker proxy (`/api/coding-worker/{asset_id}/…`), which authorizes every
|
|
28
|
+
request, wakes the computer, ensures this server is running, mints a router
|
|
29
|
+
grant, and forwards to loopback port `46100` inside the tenant's Talos v2
|
|
30
|
+
computer. Here, one `ClaudeSDKClient` per thread works in the checkout at
|
|
31
|
+
`/workspace/template` and answers with a compact structured result.
|
|
32
|
+
|
|
33
|
+
| Route | Who calls it | What it does |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `GET /ok` | the ensure command, the proxy | **unauthenticated** liveness `{ok, version}`; adds `busy`, `active_thread_id`, `owner`, `owner_since`, `owner_thread_id` only when the guest token header matches (never 401) |
|
|
36
|
+
| `POST /threads` | `threads.create` | create a task thread (the proxy supplies the id) |
|
|
37
|
+
| `GET /threads/{thread_id}` | `threads.get` | the `Thread` shape; `values.messages[-1].content` is the result JSON |
|
|
38
|
+
| `GET /threads/{thread_id}/state` | `threads.get_state` | `{values, next, metadata}` (parity) |
|
|
39
|
+
| `POST /threads/{thread_id}/runs` | `runs.create` | start a Claude turn; `multitask_strategy=interrupt` supersedes a live one |
|
|
40
|
+
| `GET /threads/{thread_id}/runs/{run_id}` | `runs.get` | the `Run` shape; `error` carries the reason on `error`/`interrupted` |
|
|
41
|
+
| `POST /threads/{thread_id}/runs/{run_id}/cancel` | `runs.cancel` | interrupt + bounded drain → 204; idempotent |
|
|
42
|
+
| `PUT /internal/credential` | the proxy | rotate the task owner key on disk → 204 |
|
|
43
|
+
| `POST /internal/handoff/request` | the `athena-claude` launcher | interrupt + drain the live run, lock the session for a human → `{thread_id, claude_session_id, cwd, owner}` |
|
|
44
|
+
| `POST /internal/handoff/return` | the launcher on exit | release the lock, record the session id the human ended on → `{thread_id, owner}` |
|
|
45
|
+
| `GET /threads/{thread_id}/transcript?after&limit` | the proxy (task card) | Claude Code's own session transcript for the thread, projected and redacted → `{thread_id, claude_session_id, total, entries}` |
|
|
46
|
+
|
|
47
|
+
Nothing else exists: no LangGraph in the guest, no streaming endpoints, no
|
|
48
|
+
OpenAPI docs.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
From a checkout — what the environment bake does today:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
uv tool install --python 3.12 /workspace/template/python-sdk/athena-claude-coder
|
|
56
|
+
athena-claude-coder --version
|
|
57
|
+
athena-claude-coder serve
|
|
58
|
+
install -m 0755 /workspace/template/python-sdk/athena-claude-coder/scripts/athena-claude /opt/athena/bin/athena-claude
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
From PyPI — **only after the first human-triggered publish**. Nothing publishes
|
|
62
|
+
automatically: a human dispatches
|
|
63
|
+
`.github/workflows/publish-athena-claude-coder.yml` from `staging` with
|
|
64
|
+
`confirm=publish` and the `version` that equals `__version__` in
|
|
65
|
+
`src/athena_claude_coder/__init__.py` (the upload job runs in the `pypi` GitHub
|
|
66
|
+
environment, which holds the token and its approval/branch rules); until that
|
|
67
|
+
has happened for a version, `athena-claude-coder==<version>` does not resolve.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
uv tool install --python 3.12 athena-claude-coder==<version>
|
|
71
|
+
athena-claude-coder --version # must print that version; the proxy refuses guests below its minimum
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The wheel carries the server only. The `athena-claude` human launcher
|
|
75
|
+
(`scripts/athena-claude`) is not in it — install it from the checkout (the line
|
|
76
|
+
above) or from the bake runbook's inline copy.
|
|
77
|
+
|
|
78
|
+
`claude-agent-sdk==0.2.151` is pinned exactly and bundles the Claude Code CLI.
|
|
79
|
+
|
|
80
|
+
## Configuration
|
|
81
|
+
|
|
82
|
+
Everything comes from the environment; the proxy's ensure command exports it
|
|
83
|
+
before `pm2` starts the server.
|
|
84
|
+
|
|
85
|
+
| Variable | Default | Meaning |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| `ATHENA_CODING_WORKER_PORT` | `46100` | loopback port (never public; the router grant targets it) |
|
|
88
|
+
| `ATHENA_CODING_WORKER_STATE_DIR` | `/workspace/.claude-state` | SQLite, run logs, credential, `settings.json`; also `CLAUDE_CONFIG_DIR` |
|
|
89
|
+
| `ATHENA_CODING_WORKER_REPO_DIR` | `/workspace/template` | Claude's `cwd` |
|
|
90
|
+
| `ATHENA_CODING_WORKER_DRAIN_SECONDS` | `30` | how long an interrupt waits for the turn's terminal message before killing the task |
|
|
91
|
+
| `ATHENA_CODING_WORKER_MAX_RUN_HOURS` | `6` | runs older than this are interrupted by the sweep |
|
|
92
|
+
| `ATHENA_CODING_WORKER_TOKEN` | — | shared secret; **required** for every route except `GET /ok` |
|
|
93
|
+
|
|
94
|
+
## The gate
|
|
95
|
+
|
|
96
|
+
This server is not the security boundary — the microVM, the agora proxy
|
|
97
|
+
(per-request `get_asset_with_permission_check` + ABAC EDIT) and the gateway
|
|
98
|
+
budget are. Two things here are still load-bearing:
|
|
99
|
+
|
|
100
|
+
- **Guest token.** Every route except `GET /ok` requires
|
|
101
|
+
`X-Athena-Coding-Worker-Token` to equal `ATHENA_CODING_WORKER_TOKEN`
|
|
102
|
+
(constant-time compare) → otherwise `401`. If the variable is unset the
|
|
103
|
+
server fails closed: those routes answer `503 {"detail": "guest token not
|
|
104
|
+
configured"}`. `GET /ok` never 401s, but without a matching token it says
|
|
105
|
+
only `{ok, version}`; the proxy's ensure command proves the running server
|
|
106
|
+
adopted the current token with `GET
|
|
107
|
+
/threads/00000000-0000-4000-8000-000000000000` → `404` (wrong token `401`,
|
|
108
|
+
unset `503`).
|
|
109
|
+
- **`can_use_tool`.** Fail-closed defence in depth for Claude's tool calls:
|
|
110
|
+
read-only tools pass, writes are confined to the checkout, the state dir and
|
|
111
|
+
`/tmp`, destructive shell verbs are never allowed, and `git push`,
|
|
112
|
+
`gh pr …`, publishing and force operations need an explicit
|
|
113
|
+
`AUTHORIZED: <action>` line in the task text. The credential files under the
|
|
114
|
+
state dir are off-limits to every tool (and the search tools may not be
|
|
115
|
+
pointed at the state dir root); shell references to them or to the
|
|
116
|
+
`athena-claude-key` helper are refused.
|
|
117
|
+
- **One live task per computer.** Every task shares one checkout and one
|
|
118
|
+
credential file, so the envelope's `max_concurrent_tasks` is clamped to 1
|
|
119
|
+
until worktree-per-task lands (plan P2.4); a second thread gets `409
|
|
120
|
+
computer busy`. The slot is reserved before anything is written, so a
|
|
121
|
+
create that loses a race leaves no transcript entry, run row or credential.
|
|
122
|
+
|
|
123
|
+
## Credentials
|
|
124
|
+
|
|
125
|
+
The run's `config.configurable.athena` envelope — injected by the proxy, never
|
|
126
|
+
trusted from anywhere else — carries the task owner key. The server writes it
|
|
127
|
+
to `<state_dir>/credential` (mode `0600`, atomic temp+rename, raw key only) and
|
|
128
|
+
`<state_dir>/credential.json` (`expires_at`, `budget_user_id`,
|
|
129
|
+
`anthropic_base_url`). Claude reads the key through the `apiKeyHelper`
|
|
130
|
+
(`/opt/athena/bin/athena-claude-key`, installed by the bake) named in the
|
|
131
|
+
`settings.json` that `serve` writes; the server process never passes it as an
|
|
132
|
+
environment variable.
|
|
133
|
+
|
|
134
|
+
`serve` also **removes** `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`,
|
|
135
|
+
`CLAUDE_CODE_OAUTH_TOKEN` and `CLAUDE_CODE_API_KEY_FILE_DESCRIPTOR` from its
|
|
136
|
+
own environment before any client is built: `ClaudeAgentOptions.env` is merged
|
|
137
|
+
*over* `os.environ` and cannot unset a key, and the computer's own gateway key
|
|
138
|
+
is in the inherited environment.
|
|
139
|
+
|
|
140
|
+
## Runs, results, durability
|
|
141
|
+
|
|
142
|
+
- **One consumer.** Only the run task iterates `client.receive_response()`.
|
|
143
|
+
Interrupts (cancel, `multitask_strategy=interrupt`, the sweep, shutdown) set a
|
|
144
|
+
flag and call `client.interrupt()`; the same loop drains the interrupted
|
|
145
|
+
turn's `ResultMessage` and records `interrupted`. The drain is bounded by
|
|
146
|
+
`ATHENA_CODING_WORKER_DRAIN_SECONDS`; past it the task is cancelled and the
|
|
147
|
+
SDK closes the subprocess.
|
|
148
|
+
- **Result JSON.** The last AI message is the compact result
|
|
149
|
+
(`status ∈ completed|blocked|failed`, `summary`, `branch`, `commit_sha`,
|
|
150
|
+
`changed_files`, `tests`, `artifacts`, `blockers`, `questions`, plus
|
|
151
|
+
`claude_session_id`, `cost_usd`, `num_turns`, `terminal_reason`, `run_id`).
|
|
152
|
+
Budget exhaustion is `status: blocked` with `blockers: ["budget exhausted"]`.
|
|
153
|
+
A run is `success` whenever Claude produced a terminal result — including
|
|
154
|
+
error results — because the parent only reads the message on `success`; a
|
|
155
|
+
run is `error` only when the SDK or process failed.
|
|
156
|
+
- **Same thread = same Claude session.** The session id from the `init` message
|
|
157
|
+
is persisted immediately; every later run on the thread resumes it.
|
|
158
|
+
- **Suspend-safe.** State is SQLite plus files on the persisted tree. At boot
|
|
159
|
+
every `pending`/`running` row becomes `interrupted` (`error: restart`) with
|
|
160
|
+
its session id intact; the sweep interrupts runs older than
|
|
161
|
+
`ATHENA_CODING_WORKER_MAX_RUN_HOURS`.
|
|
162
|
+
|
|
163
|
+
## Human handoff (P2.1)
|
|
164
|
+
|
|
165
|
+
A human in the computer terminal (Olympus terminal tab or ssh) can take a
|
|
166
|
+
task's Claude session over and give it back, on the **same** session: both
|
|
167
|
+
sides share `CLAUDE_CONFIG_DIR` and the checkout, so the next SDK run resumes
|
|
168
|
+
exactly where the human stopped.
|
|
169
|
+
|
|
170
|
+
| State (`sessions.owner`) | Entered by | What the SDK may do |
|
|
171
|
+
|---|---|---|
|
|
172
|
+
| `sdk` | a run starting | — (it is the run) |
|
|
173
|
+
| `human` | `POST /internal/handoff/request`: the live run is interrupted with reason `handoff`, drained through the single stream consumer, and recorded as a **`success`** whose last AI message is `{"status": "blocked", "summary": "Handed off to a human in the computer terminal", "blockers": ["human_active"], "claude_session_id", "run_id"}` — `success` so `check_async_task` reads it | `runs.create`/`reserve` → `409 human holds the session`, nothing written |
|
|
174
|
+
| `none` | `POST /internal/handoff/return` (records the session id the human ended on), a run finishing, or a restart clearing a dead `sdk` holder | start a run; it passes `resume=<claude_session_id>` |
|
|
175
|
+
|
|
176
|
+
Both routes are idempotent and need the guest token. `owner_since` is the
|
|
177
|
+
moment the lock was taken (`null` for `none`) and doubles as the lock's
|
|
178
|
+
**lease**: the request returns it (with `acquired: true` the first time,
|
|
179
|
+
`false` while a human already holds the session), the return must present
|
|
180
|
+
it, and a return with a stale lease — or one arriving while the SDK owns the
|
|
181
|
+
thread — is a no-op. A human lock also occupies the computer's one task slot,
|
|
182
|
+
so other threads are refused with `409 computer busy` while it is held. The
|
|
183
|
+
request carries the launcher's `launcher_pid`; a lock whose launcher process
|
|
184
|
+
is gone (closed terminal, suspended computer) is released at startup, before
|
|
185
|
+
every reservation and by the sweep. `takeover: true` re-mints the lease for a
|
|
186
|
+
second terminal; the earlier launcher's return then does nothing. The
|
|
187
|
+
authenticated `GET /ok` reports `owner` and `owner_since` for the live thread,
|
|
188
|
+
else the human-held or most recent session, and names that thread in
|
|
189
|
+
`owner_thread_id` (`null` when no session exists) — once a handoff has
|
|
190
|
+
drained the run and `active_thread_id` is gone, this is what ties a human
|
|
191
|
+
lock to its task.
|
|
192
|
+
|
|
193
|
+
`scripts/athena-claude` is the launcher the bake installs at
|
|
194
|
+
`/opt/athena/bin/athena-claude`: it reads the guest token from the running
|
|
195
|
+
server's pm2 environment (`PM2_HOME=/workspace/.pm2-athena-coder pm2 jlist`,
|
|
196
|
+
never printed), requests the handoff, runs `claude --resume <session>` (or a
|
|
197
|
+
fresh `--session-id` it chooses) with the SDK's exact environment and cwd, and
|
|
198
|
+
on exit — any exit code, via `trap`, retried while the worker is unreachable —
|
|
199
|
+
posts the return with the lease and the session id it ended on. It refuses
|
|
200
|
+
to start a second writer when another terminal holds the session
|
|
201
|
+
(`ATHENA_CLAUDE_TAKEOVER=1` takes it over). `ATHENA_CODING_WORKER_THREAD_ID`
|
|
202
|
+
picks a thread explicitly; the default is the live one, else the most recent
|
|
203
|
+
session.
|
|
204
|
+
|
|
205
|
+
## Concurrency
|
|
206
|
+
|
|
207
|
+
The run envelope's `max_concurrent_tasks` (the workspace policy) sets the
|
|
208
|
+
number of **task slots**, capped at `HARD_MAX_CONCURRENT_TASKS = 4`. With one
|
|
209
|
+
slot (the default) a task runs in the checkout itself (`ATHENA_CODING_WORKER_REPO_DIR`),
|
|
210
|
+
exactly as before. With more, every task thread gets its own **git worktree**:
|
|
211
|
+
|
|
212
|
+
- `git worktree add /workspace/worktrees/<thread12> -b athena/<thread12>` from the
|
|
213
|
+
checkout's current `HEAD` on the thread's first run (`thread12` = first 12
|
|
214
|
+
characters of the thread id); the path is persisted as the thread's `cwd`, so
|
|
215
|
+
every later run — and a human handoff — lands in the same worktree. An
|
|
216
|
+
existing worktree or branch (crash between create and persist, or a
|
|
217
|
+
garbage-collected worktree) is adopted / re-attached, never recreated.
|
|
218
|
+
- A thread occupies one slot while it has a live run, a reservation or a human
|
|
219
|
+
holder; the (N+1)th concurrent thread is refused with `409 computer busy`, a
|
|
220
|
+
follow-up run on a thread that holds a slot reuses it. Slot indices are
|
|
221
|
+
stable while a thread holds its slot; `GET /ok` reports `slots: {used, total}`,
|
|
222
|
+
`active_thread_ids` (oldest first) and keeps `active_thread_id` (the oldest
|
|
223
|
+
live one); `busy` means every slot is taken.
|
|
224
|
+
- Each task's system prompt names its dev-server port, `46200 + slot index`,
|
|
225
|
+
and warns that other tasks may be using the neighbouring ports.
|
|
226
|
+
- Each task reads **its own credential**: `runs.create` writes the envelope's
|
|
227
|
+
owner key to `<state_dir>/credentials/<thread_id>` as well as to the shared
|
|
228
|
+
`credential` file, and the run's `--settings` file
|
|
229
|
+
(`settings-<thread_id>.json`) names an `apiKeyHelper` that `cat`s that copy,
|
|
230
|
+
so concurrent tasks never bill each other's key. The shared file and the
|
|
231
|
+
baked helper stay for the human launcher; `PUT /internal/credential` may
|
|
232
|
+
carry `thread_id` to rotate a task's copy too.
|
|
233
|
+
- A human handoff takes a slot exactly like a run (refused with `409 computer
|
|
234
|
+
busy` beyond the policy) and a never-run thread handed to a human gets its
|
|
235
|
+
own worktree under a multi-slot policy.
|
|
236
|
+
- Threads that already work in the checkout (first run under one slot, or any
|
|
237
|
+
0.2.0 row) keep it for transcript continuity; when the policy grows, at most
|
|
238
|
+
one such thread may be active at a time — a second is refused with `409
|
|
239
|
+
computer busy` rather than allowed to share the tree. Two thread ids that
|
|
240
|
+
share their first 12 characters get distinct worktrees (the second takes a
|
|
241
|
+
longer name).
|
|
242
|
+
- **GC** runs on the sweep cadence and removes a worktree only when its thread
|
|
243
|
+
is terminal (no live run, no reservation, owner `none`), the worktree is clean
|
|
244
|
+
(`git status --porcelain` empty *and* no commit ahead of the checkout's
|
|
245
|
+
`HEAD`) and its session has been idle for more than 24 h; eligibility is
|
|
246
|
+
re-checked under the runner lock right before the removal, so a thread
|
|
247
|
+
claimed meanwhile keeps its worktree. A dirty or ahead worktree is kept and
|
|
248
|
+
logged once. The `athena/<thread12>` branch is never deleted — it is the
|
|
249
|
+
work product — and the checkout itself is never touched.
|
|
250
|
+
|
|
251
|
+
## Transcript route
|
|
252
|
+
|
|
253
|
+
`GET /threads/{thread_id}/transcript?after=<int>&limit=<int>` (guest token
|
|
254
|
+
required) reads Claude Code's own session transcript for the thread — the
|
|
255
|
+
JSONL under `CLAUDE_CONFIG_DIR/projects/<project-slug>/<claude_session_id>.jsonl`,
|
|
256
|
+
where the project slug follows the thread's cwd (its worktree under a
|
|
257
|
+
multi-slot policy). Agora reads a task's turns *through* this route instead of
|
|
258
|
+
the guest pushing them into the Athena session: the session bridge has no
|
|
259
|
+
credential a guest could hold. The read is never taken under the runner lock
|
|
260
|
+
and runs in a worker thread.
|
|
261
|
+
|
|
262
|
+
- `404 thread not found` for an unknown thread; `404 no transcript for this
|
|
263
|
+
thread yet` while the thread has no `claude_session_id` or the file does not
|
|
264
|
+
exist yet.
|
|
265
|
+
- One entry per content block of every user/assistant message:
|
|
266
|
+
`{seq, role: "user"|"assistant", kind: "text"|"tool_use"|"tool_result"|"thinking",
|
|
267
|
+
text, tool_name?, ts?}`. `seq` is a stable 0-based index over the whole file,
|
|
268
|
+
`text` is clipped to 4000 characters (a `tool_use` renders compactly — the
|
|
269
|
+
Bash command, the Read/Edit/Write path, the Grep pattern — and a
|
|
270
|
+
`tool_result` carries the result text and the name of the tool it answers),
|
|
271
|
+
`ts` is the entry timestamp. Summary, system and meta lines are skipped.
|
|
272
|
+
- The projection follows the **live conversation** the way the pinned SDK's
|
|
273
|
+
session reader finds it: the most recent turn no later record descends
|
|
274
|
+
from, and its `parentUuid` ancestors. Turns of an abandoned branch (a
|
|
275
|
+
rewind in the terminal during a handoff) and subagent `isSidechain` records
|
|
276
|
+
are left out. Unlike the SDK, a `compact_boundary` is crossed through its
|
|
277
|
+
`logicalParentUuid`, so the turns before an auto-compaction stay in the
|
|
278
|
+
card and the `isCompactSummary` message appears in their flow — following
|
|
279
|
+
the SDK here would make `total` collapse at every compaction. `seq` is
|
|
280
|
+
therefore stable while the conversation only grows; should the live leaf
|
|
281
|
+
move to another branch, earlier seqs can shift and `total` can shrink — a
|
|
282
|
+
consumer that sees `total` below its cursor re-reads from `after=0`.
|
|
283
|
+
- Two passes over the file: one for the `uuid`/`parentUuid` graph (a few
|
|
284
|
+
fields per record), one that projects only live records and redacts and
|
|
285
|
+
clips only the requested window — memory is the graph plus the page, not
|
|
286
|
+
the transcript.
|
|
287
|
+
- `after` returns entries with `seq >= after` (negative → 0); `limit` is
|
|
288
|
+
clamped to `1..500`, default 200; `total` always counts the whole file.
|
|
289
|
+
- Credential shapes are redacted before anything leaves the guest
|
|
290
|
+
(`redact.py`, mirroring the dcode mirror's list): `sk-…` keys, Bearer
|
|
291
|
+
and Basic authorization, `x-api-key` headers, environment-style assignments
|
|
292
|
+
whose name says credential (`…_API_KEY=`, `…_ACCESS_KEY=`, `…_SECRET=`,
|
|
293
|
+
`…_TOKEN=`, `…_PASSWORD=` — the guest's own `ATHENA_*`/`ANTHROPIC_*`
|
|
294
|
+
variables, `AWS_SESSION_TOKEN`, `GITHUB_TOKEN`, …), GitHub/AWS/Slack/Stripe
|
|
295
|
+
shapes, PEM blocks and URL userinfo.
|
|
296
|
+
|
|
297
|
+
## Tests
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
cd python-sdk/athena-claude-coder
|
|
301
|
+
pip install -e ".[dev]"
|
|
302
|
+
python -m pytest -v --tb=short && ruff check . && ruff format --check .
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
The suite never spawns a Claude process: a fake `ClaudeSDKClient` drives the
|
|
306
|
+
runner, and the protocol contract is exercised through the real
|
|
307
|
+
`langgraph_sdk` client on an ASGI transport. Tests marked `post_merge` need a
|
|
308
|
+
real Claude and are skipped unless `ATHENA_CODING_WORKER_POST_MERGE=1`.
|