feat(aauth): keypair format standardization (#36) + grant revocation primitives (#34)#41
Open
markmhendrickson wants to merge 19 commits into
Open
feat(aauth): keypair format standardization (#36) + grant revocation primitives (#34)#41markmhendrickson wants to merge 19 commits into
markmhendrickson wants to merge 19 commits into
Conversation
Two-phase design: Phase 1 sends a shallow Telegram briefing at 05:30 Madrid time with today's calendar + known attendee context; Phase 2 spawns per-meeting Claude agents for deep participant research, agenda/goals/talking-points, Neotoma task creation, and checkpoint_brief storage. Fixes log filename sanitization for event titles containing '/'. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Onychomys now knows to retrieve Cotinga's checkpoint_briefs from Neotoma when asked about today's meetings, rather than re-running the daemon. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n at 05:30 Cotinga now runs at 05:00 to store checkpoint_briefs before the digest. morning-brief runs at 05:30, polls Neotoma for Cotinga's briefs (up to 20min), spawns a one-shot Claude agent in Onychomys voice to compose the digest, and sends to Telegram. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t Madrid time) Removes the 48-hour lookahead window that was including tomorrow's events in the morning briefing. Also picks up plist updates (log paths, PATH, NEOTOMA_BASE_URL). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eotoma stub creation Deep-prep agents now search Gmail (gws) and LinkedIn for contact details when an attendee isn't found in Neotoma, then store a fully enriched person entity rather than a bare stub. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…personal/PR#2 - Add .claude/skills/analyze-meeting/SKILL.md: 11-step meeting analysis (participants via Calendar/Neotoma, recap email via gws, proposed_github_issue entities, default repo markmhendrickson/ateles) - Add execution/scripts/import_audio_from_desktop.py: migrated from personal repo; replaces --analyze flag with --no-analyze; emits ANALYZE_MEETING_TRIGGER lines for the calling agent to fan out /analyze-meeting per transcription - Add execution/scripts/backfill_analyze_private_docs.py: batch driver for /analyze backfill over docs/private; dedupes against Neotoma; emits ANALYZE_TRIGGER JSON lines - Add execution/scripts/meeting-recording-control.sh: copied from personal repo; required by strix.py at startup (absence caused crash loop) - Fix execution/daemons/strix/com.ateles.strix.plist: update python path to .venv/bin/python3, add VIRTUAL_ENV + PYTHONPATH so launchd finds venv packages (rumps), update log paths to ~/Library/Logs/ateles/strix.log Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ooltip Remove the two-item dropdown; clicking the status bar icon now directly toggles recording on/off. Hover shows "Strix: recording active — click to stop" or "Strix: idle — click to start recording". Implementation: a one-shot Timer fires 100ms after startup to reach into the NSStatusItem, clear its menu, and wire a PyObjC ClickTarget as the direct action target (sendActionOn_ mask covers both mouse buttons). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…end failures; load openclaw .env in send.mjs - Cotinga and morning-brief now acquire a PID lockfile on startup and release it on exit, preventing launchd double-launch races - telegram_send() now logs exit code and stderr when send.mjs fails, instead of silently swallowing errors - send.mjs now also loads ~/repos/openclaw/.env so TELEGRAM_BOT_TOKEN is found without manual env var configuration Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s with summary After recording stops, the control script now sends: - 📝 [strix] Transcription done (Xm Ys).\n<first 400 chars of transcript> - 🎙️ [strix] Diarization + transcription done (Xm Ys).\n<...> when diarization ran - ❌ [strix] Transcription failed. if transcription errors Also adds a _telegram() helper in the script and fixes the venv python path to prefer .venv/bin/python3 (matching the launchd plist). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace send.mjs subprocess with direct Python urllib Bot API call — subprocess path silently failed on every launchd run; now logs message_id or explicit HTTP/exception error on failure - Remove StreamHandler(stdout) from logger — plist routes stdout+stderr to same file, causing every log line to appear twice - Filter shallow briefing to meetings-only (skip personal calendar blocks with no external attendees) - Remove *bold* Markdown wrapping on event titles — emoji+spaces break Telegram's legacy Markdown parser - Fix 'Deep briefs will arrive' footer: only shown when meetings present - Fix --thread-id → --topic flag in both telegram_send() and deep-prep prompt (send.mjs uses --topic) - Expand deep-prep prompt with company research, recent news/publications, Neotoma/Ateles overlap analysis, and live convergence sections Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- meeting-recording-control.sh: fall back to personal venv when ateles .venv lacks numpy/audio deps; find personal venv automatically - Symlink record_meeting_audio.py, transcribe_audio.py, extract_meeting_frames.py from personal repo into ateles/execution/scripts - tyto.py: fix Priority.ERROR→BLOCKER, add _load_env(), handle system.mp4 track name, find transcribe_audio.py in personal repo fallback - tyto plist: use personal venv python, set correct PATH (nvm, local/bin), point to prod Neotoma - strix.py: control script path resolves correctly to ateles repo Recording pipeline now fully operational: start/stop/transcribe/Neotoma store. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ine filter - Fetch from start-of-day (not now) so overnight events (flights, etc.) are included - Replace meetings-only filter with routine-block filter: show all events except personal blocks (Wake, Work, Busy, Prepare for bed, Lights out, etc.) - 📌 icon for solo events, 🤝 for events with attendees; deep-prep only for latter - "Deep briefs" footer only shown when at least one attendee-meeting has deep-prep - Add _is_routine() with emoji-stripping for reliable title matching Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Deduplicate events by normalised title (strips non-alphanumeric, lowercases) so multi-calendar duplicates (e.g. all-day + timed version of same event) only appear once - Detect all-day events via start.date vs start.dateTime; show "all day" instead of "00:00" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- meeting-recording-control.sh: stop loading personal/.env (it has RECORD_MEETING_SKIP_TRANSCRIBE=1 which disabled transcription) - Default RECORD_MEETING_DEVICE to "System-wide capture" (Rogue Amoeba virtual device that captures all system audio regardless of output routing, even when AirPods are active) instead of BlackHole - Default RECORD_MEETING_MIC to "Studio Display Microphone" instead of system default (AirPods Max) which has noise cancellation issues - Default RECORD_MEETING_SKIP_TRANSCRIBE to 0 (enable transcription) - All three defaults are overridable via env vars; explicitly exported so they reach the record_meeting_audio.py subprocess Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…primitives (#34) Issue #36 — Standardize daemon keypair on-disk format: - aauth_signer.py: from_key_file probes <name>.jwk.json (canonical JWK) first, falls back to <name>.json (legacy PEM); auto-detects by kty+d fields. New _load_private_key_jwk reconstructs EC P-256 key from base64url x/y/d scalars via cryptography. - mint_daemon_keypair.py: generates ES256 P-256 keypair, writes canonical <name>.jwk.json (mode 0600) to ateles-private/keys/. - docs/aauth/keys.md: canonical layout, legacy format, rotation, JWKS plan. Issue #34 — Revocation primitives (suspend/revoke + per-capability): - grant_checker.py: GrantChecker loads agent_grant entities by aauth_sub; is_active/is_suspended/is_revoked/check_capability; suspend_grant, restore_grant, revoke_grant write state via corrections API with reason + timestamp. Permissive fallback when Neotoma unreachable. - manage_grants.py: CLI list/suspend/restore/revoke. - daemon_runtime __init__: export GrantChecker, AgentGrant, grant helpers. - formica.py + neotoma_agent.py: grant check at startup (revoked=abort, suspended=warn+disable dispatch); grant-suspend guard in dispatch path; ATELES_PARTICIPATION_REF passed in subprocess env (#23 prep). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace single primary-calendar fetch with parallel multi-calendar fetch using ThreadPoolExecutor. Queries markmhendrickson@gmail.com, Tontitos, and Family calendars; merges and deduplicates by event id; sorts by start time. Excludes Birthdays and holiday feeds (noise). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Consolidates 53 documentation files from the personal monorepo into ateles as part of phasing out ~/repos/personal. Subdirectory structure preserved (cursor_rules, developer, health, on_demand, outreach, testing). personal/docs/data_types.md renamed to legacy_data_types_inventory.md to avoid collision with ateles's existing swarm-governance data_types.md (different document — record-count inventory vs. schema reference). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Moves git-hooks, linters (non-colliding), nosync utilities, and standalone helper scripts from personal/scripts/ into ateles/scripts/. ateles's newer check_file_naming.py and security_audit.py are kept (personal copies were stale). update_readme_release_status.py and setup_claude_skills.sh land at scripts/ to match existing ateles doc/linter references. Dropped as obsolete: submodule-management scripts (auth/ensure/init_submodules) and MCP-config generators tied to personal's old submodule layout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
move_airdrop_to_desktop.py + setup_airdrop_to_desktop.sh. The com.markmhendrickson.airdrop-to-desktop launchd plist will be repointed to ateles/scripts/. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #36. Closes #34.
#36 — Standardize daemon keypair on-disk format + JWKS endpoint
lib/daemon_runtime/aauth_signer.py:from_key_filenow probes<name>.jwk.json(canonical JWK) first, falls back to<name>.json(legacy PEM). Auto-detects format bykty+dfields. New_load_private_key_jwkreconstructs an EC P-256 private key from base64urlx/y/dscalars viacryptography.execution/scripts/mint_daemon_keypair.py: generates an ES256 P-256 keypair and writes the canonical<name>.jwk.json(mode 0600) intoateles-private/keys/. Refuses to overwrite an existing file.docs/aauth/keys.md: canonical layout, legacy format, the existing 8 keypairs, rotation flow, and the planned JWKS endpoint.#34 — Revocation primitives (suspend/revoke + per-capability)
lib/daemon_runtime/grant_checker.py:GrantCheckerloadsagent_grantentities byaauth_suband exposesis_active()/is_suspended()/is_revoked()/check_capability(cap). Module-levelsuspend_grant/restore_grant/revoke_grantwrite status back via the Neotoma corrections API with reason + timestamp. Permissive fallback when Neotoma is unreachable (advisory until PS-layer enforcement, Implement R3r3_conditionalsupport in Neotoma's AAuth verifier #30).execution/scripts/manage_grants.py: CLI —list [--sub],suspend <id> [--reason],restore <id>,revoke <id> [--reason].lib/daemon_runtime/__init__.py: exports the new symbols.ATELES_PARTICIPATION_REFpassed in subprocess env (prep for ateles#19 part 4: auto-stamp retrieval_event from mcpsrv_neotoma #23 retrieval attribution).Verification
ast.parse.grant_checkerpublic API surface and__init__exports validated.Out of scope / follow-ups
agent_grantschema to carry per-entry capability status in Neotoma.🤖 Generated with Claude Code