Repository navigation
feat(agent): agent self-registration first slice (DEV-1, API contract rev 1) - #143
Conversation
## Summary Every request now carries `dailybot-cli/<ver> (python/<x.y>; <os>; <install_method>)` so the backend can measure which CLI versions are in use before it changes the registration challenge (DEV-1, API contract rev 1, item CLI-1). ## Change Log - `user_agent()` (cached) and `_base_headers()` in api_client.py - Anonymous, user-scoped, agent-scoped, alt-credential retry and streamed paths carry it; the credential-less storage hops get it too, still with no Authorization or X-API-KEY (a presign's own headers win) - Agent-stamp tests now look for the agent-name header, not any "agent" substring ## Risks - None for compatibility: an additional request header only Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… challenge_expired ## Summary Registration follows API contract rev 1 (DEV-1 items CLI-2 and CLI-3): the challenge answer is the session code times the instruction's word count instead of a hardcoded 52, so the server can vary the wording without breaking new CLIs. ## Change Log - `_solve_challenge` counts words; 52 (`_CHALLENGE_LEGACY_WORD_COUNT`) is only a fallback for an implausibly short instruction; an unreadable one stops with a `dailybot upgrade` hint - Retry once on code `challenge_expired` (legacy "expired" detail only when the server sends no code), always with a fresh challenge - A registration 429 shows the Retry-After wait; the challenge GET waits out a short 429 (up to 10s) and never sleeps through a long one; the POST is never auto-retried because every attempt consumes its challenge - docs/SECURITY.md describes the challenge as a speed bump and the key's scope ## Risks - Registration against today's server is unchanged (52-word wording gives the same answer) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o your human" block ## Summary `dailybot agent register` now renders `claim_expires_at` (API contract rev 1) instead of a hardcoded "30 days", and tells the agent exactly what to hand to the human owner (DEV-1 item CLI-4, partial: `--owner-email` waits for server support). ## Change Log - `format_claim_expiry` renders ISO 8601 as `YYYY-MM-DD HH:MM UTC`; without the field the CLI says "See the claim page for when it expires." - The handoff text says the claim needs a Dailybot web sign-in and warns that the link works like a password - Server-supplied names are escaped before Rich renders them; the API key is saved but never printed ## Risks - Output wording change only Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…contract rev 1 ## Summary Agent commands and `agent register` now dispatch on the server's `code` and fall back to `detail` for unknown or absent codes. ## Change Log - ERROR_CODE_MESSAGES gains the rev-1 codes (challenge_*, invalid_answer, answer_incorrect, missing_fields, reason_*, registration_rate_limited, claim_token_unavailable) - New agent_error_message(): agent-key copy for plan_upgrade_required (scope + upgrade_url) and invalid_credentials (deactivated agent), 429 wait hint - `agent configure` treats a 403 plan_upgrade_required as a valid key - Fixed the stale 401/403 wording in docs/API_REFERENCE.md and docs/SECURITY.md ## Risks - Messages change only for responses that carry a code; old servers keep detail Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ir 0o700 ## Summary credentials.json, config.json, agents.json and the org/plan caches were written with write_text then chmod, leaving a umask-permission window while the file already held a token or key. ## Change Log - New _write_secret_file(): os.open(..., 0o600), then chmod to tighten a pre-existing loose file - get_config_dir() creates the directory 0o700 (an existing one is untouched) - Six tests with chmod disabled under umask 0 ## Risks - None for stored-file schemas: content and names are unchanged Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
## Summary README "Standalone registration" and the API reference now match the contract: the server-reported claim expiry, the claim needing a web sign-in, the least-privilege registered-key scope, the new response fields and the rev-1 error codes. Policy wording is unchanged. ## Change Log - README: no hardcoded "30 days"; claim needs a web login; key scope (agent email/webhook refused) and the free-plan daily cap - API_REFERENCE: word-count challenge, expiry retry, rate limits, register response fields and 400/429 codes, claim preview/claim endpoints (web only), GET /v1/agent/claim-token/ (returns the existing link, no mint) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a wait hint The API throttle can omit Retry-After when it cannot compute a wait; lock in the generic fallback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1 wire shapes Copies the API's challenge wording, register 201 body (isoformat claim_expires_at), 400/429 envelopes with Retry-After, and the registered-key 403/429 bodies, and runs them through the real DailyBotClient with only httpx mocked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
…cret os.open keeps an existing file's mode, so fchmod the descriptor before the first byte (POSIX; Windows falls back to chmod after the write). Applied to _write_secret_file and save_repo_env. AI review round 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ricter expiry match - Registration codes move to REGISTRATION_CODE_MESSAGES, read only by agent commands, so generic names never leak registration copy - APIError carries the credential kind; the deactivated-agent-key copy is shown only when the request sent X-API-KEY - Legacy (no code) challenge retry needs both 'challenge' and 'expired' - A claim expiry without an offset is read as UTC AI review round 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AGENTS.md rule 11, SECURITY.md and STANDARDS.md still taught write_text + chmod. AI review round 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…d is PATCH API #8241 now revokes a self-registered key when an org admin deletes the agent from the dashboard, so the invalid_credentials copy and the API reference say deleted, not deactivated. The registered-key allowlist lists PATCH for agent-messages/read, which is what the client sends. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AI review for
|
There was a problem hiding this comment.
Code review — 0 finding(s): 0 critical · 0 warning · 0 info
Check: ✅ passing — strictness block-on-critical: highest severity info ≤ critical threshold
No findings posted inline.
Summary
Verdict
Looks good — this follow-up is a clean contract wording pass; nothing blocking.
What changed since last review
The delta (d9da1b8 → 3b4546b) only aligns copy and docs with API contract rev 1:
- Agent
invalid_credentialscopy and tests now say an org admin may have deleted the agent (was “deactivated”). - Registered-key scope docs list
PATCH /v1/agent-messages/read/(the client already issuesPATCH; this was a docs/client mismatch fix). - Error-table wording for the same deleted-agent case.
No auth-order, secret-file mode, challenge, or User-Agent behavior changed in this iteration.
Prior finding
The open info on user_agent()’s bare except Exception around detect_install_method() is still open: dailybot_cli/api_client.py is untouched in this delta, and detect_install_method() still only does offline path inspection with an internal "pip" fallback (it does not raise in normal failure modes). Harmless dead catch; not merge-blocking.
Cross-checks
- Wire client already documents/uses
PATCH /v1/agent-messages/read/— docs now match. - Credential-aware
invalid_credentialsmessaging (api_key only) was already in place; tests still cover the login-session non-blame path. - Secret-file
0o600/ config-dir0o700work from earlier commits is unchanged here.
Recommendation: approve
Since last review (d9da1b8 → 3b4546b): resolved 0 · still open 1 · regressed 0 · new 0.
Prior findings
- still open
dailybot_cli/api_client.py:51
Check status: ✅ passing — strictness
block-on-critical, highest severity in effectinfo: highest severityinfo≤ critical threshold.This line is written by the reviewer runtime after the gate ran and matches the check conclusion and the tracking comment. Any recommendation above is the model's advisory opinion, not the gate.
Summary
The CLI half of the agent self-registration first slice (DEV-1, Dailybot V3 → Product board), built against
API contract rev 1 (additive only; API side: PLAN_003_agent_registration_first_slice in the API repo).
Merge order
Merge only after the API PR for DEV-1 is deployed. That PR changes what the API returns: error
codes,claim_expires_at, and the self-registered key scope. This CLI falls back todetailagainst an older API,but the docs and messages describe the rev-1 behaviour.
Change Log
dailybot-cli/<ver> (python/<x.y>; <os>; <install_method>)on every request, including thepresign upload and the storage download. Those two still send no Dailybot credential.
code × len(instruction.split()). 52 is kept only as a sanity fallback.An unreadable instruction gives a clear error and a
dailybot upgradehint.code: challenge_expired(legacydetailmatch when no code). A429 registration_rate_limitedshows theRetry-Afterwait. The challenge GET uses the shared Retry-After helper.claim_expires_at(no hardcoded "30 days"), followed by a"Send this to your human" block. The API key is never printed.
codeand fallback to
detail.plan_upgrade_requiredexplains the registered-key scope and appendsupgrade_url;invalid_credentialsmentions a deactivated agent.agent configureaccepts a key refused only for scope.os.open).The config dir is created 0o700.
GET that returns the existing link, the claim needs a web login, and the key scope. The stale 401/403 wording
is fixed in API_REFERENCE and SECURITY. Self-registration policy wording is unchanged.
Not in this PR (no backend in rev 1):
--owner-email, CLI-6/7/9, theauth_required/claim_required/credential_*codes.Validation
pytest -q→ 3856 passed.tests/tui_app_flows_test.pyand one textual-launch test are excluded locallyonly because
textualis not installed in the dev container; CI installs it.ruff check dailybot_cli tests→ cleanmypy dailybot_cli→ cleanRisks
code; older servers keep showingdetail.🤖 Generated with Claude Code