@misterhuydo/sentinel 1.6.9 → 1.6.11

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.
Files changed (49) hide show
  1. package/.cairn/.hint-lock +1 -1
  2. package/.cairn/memory/auto-memory/MEMORY.md +21 -0
  3. package/.cairn/memory/auto-memory/decision_auto_commit_auto_release_split.md +22 -0
  4. package/.cairn/memory/auto-memory/decision_git_apply_recount_for_llm_diffs.md +17 -0
  5. package/.cairn/memory/auto-memory/decision_jenkins_wait_before_cascade.md +23 -0
  6. package/.cairn/memory/auto-memory/decision_multi_repo_fix_architecture.md +25 -0
  7. package/.cairn/memory/auto-memory/decision_per_project_claude_session.md +23 -0
  8. package/.cairn/memory/auto-memory/experience_bash_local_shadows_env_var.md +11 -0
  9. package/.cairn/memory/auto-memory/experience_cairn_session_discipline.md +9 -0
  10. package/.cairn/memory/auto-memory/experience_cicd_user_must_not_be_hardcoded.md +17 -0
  11. package/.cairn/memory/auto-memory/experience_claude_resume_stale_context_risk.md +23 -0
  12. package/.cairn/memory/auto-memory/experience_envelope_first_auth_detection.md +11 -0
  13. package/.cairn/memory/auto-memory/experience_mvn_negative_cache_blocks_cascade_retries.md +17 -0
  14. package/.cairn/memory/auto-memory/experience_publish_safety_check.md +13 -0
  15. package/.cairn/memory/auto-memory/experience_secrets_in_gitignored_files.md +9 -0
  16. package/.cairn/memory/auto-memory/experience_sentinel_deployment_server.md +13 -0
  17. package/.cairn/memory/auto-memory/feedback_ageri_rag_architecture.md +19 -0
  18. package/.cairn/memory/auto-memory/feedback_design_principle.md +16 -0
  19. package/.cairn/memory/auto-memory/feedback_publish_workflow.md +14 -0
  20. package/.cairn/memory/auto-memory/feedback_secrets_handling.md +15 -0
  21. package/.cairn/memory/auto-memory/feedback_slack_admin_allowlist.md +11 -0
  22. package/.cairn/memory/auto-memory/feedback_slack_thinking_status.md +14 -0
  23. package/.cairn/memory/auto-memory/feedback_start_sh_patching.md +16 -0
  24. package/.cairn/memory/auto-memory/knowledge_cairn_federation_for_sentinel_projects.md +20 -0
  25. package/.cairn/memory/auto-memory/knowledge_cairn_hooks_block_oauth_read_edit.md +24 -0
  26. package/.cairn/memory/auto-memory/knowledge_sentinel_repo_remotes.md +28 -0
  27. package/.cairn/memory/auto-memory/knowledge_sentinel_systemd_inactive_is_misleading.md +18 -0
  28. package/.cairn/memory/auto-memory/knowledge_sentinel_upgrade_requires_file_copy.md +20 -0
  29. package/.cairn/memory/auto-memory/preference_no_api_key_for_heavy_coding.md +17 -0
  30. package/.cairn/memory/auto-memory/project_ageri.md +45 -0
  31. package/.cairn/memory/auto-memory/project_ageri_architecture_v2.md +89 -0
  32. package/.cairn/memory/auto-memory/project_ageri_devtest.md +15 -0
  33. package/.cairn/memory/auto-memory/project_ageri_personality.md +61 -0
  34. package/.cairn/memory/auto-memory/project_ageri_platform_vision.md +70 -0
  35. package/.cairn/memory/auto-memory/project_publish_workflow.md +23 -0
  36. package/.cairn/memory/auto-memory/project_sentinel_state.md +44 -0
  37. package/.cairn/memory/auto-memory/project_sentinel_ui.md +39 -0
  38. package/.cairn/memory/auto-memory/reference_ageri_server.md +35 -0
  39. package/.cairn/memory/auto-memory/reference_cairn_federation.md +26 -0
  40. package/.cairn/memory/auto-memory/reference_oracle_servers.md +24 -0
  41. package/.cairn/memory/auto-memory/reference_sentinel_server.md +15 -0
  42. package/.cairn/memory/auto-memory/reference_taplo.md +52 -0
  43. package/.cairn/session.json +4 -6
  44. package/package.json +1 -1
  45. package/python/sentinel/__init__.py +1 -1
  46. package/python/sentinel/fix_engine.py +6 -0
  47. package/python/sentinel/notify.py +1 -1
  48. package/python/sentinel/slack_bot.py +34 -0
  49. package/python/tests/test_fix_engine_json.py +126 -95
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: Ageri architecture v2 decisions
3
+ description: Finalized architectural decisions — multi-profile, skill types, agent society, Orchestrator as God
4
+ type: project
5
+ ---
6
+
7
+ ## Terminology: "App" → "Skill" ✓ DONE
8
+
9
+ Renamed throughout codebase. SkillBase, SkillResult, SkillRegistry, ageri/skills/, SKILLS= config key. Backwards-compatible aliases kept until v1.0.
10
+
11
+ ## One Orchestrator, Multiple Agent Profiles ✓ FINALIZED
12
+
13
+ - One Orchestrator process, one DB, one deployment
14
+ - Multiple named Agent Profiles within it (e.g. Sammy, John, Peter, Selina)
15
+ - Each profile has: name, channel bindings, memory scope (`profile:{id}:*`), personality, skill set
16
+ - Reset = wipe `profile:{id}:*` memory only, config untouched
17
+ - **Users can create as many profiles as they want** — profile count is a monetization lever (free tier limit, paid tier unlimited). Do not hardcode a profile cap in architecture.
18
+
19
+ ## Skill Assignment & Orchestrator Routing ✓ FINALIZED
20
+
21
+ - A profile can hold multiple skills simultaneously (e.g. research + knowledge + zalo_adapter)
22
+ - User intent determines required skills: "open Zalo, reply to customer questions" → needs research + knowledge + zalo_adapter on the same profile
23
+ - If the active/addressed profile lacks a required skill, **Orchestrator suggests a profile that has the needed skill set** — does not silently fail or auto-delegate
24
+ - Skill matching is capability-based, not name-based — Orchestrator inspects skill registry per profile
25
+
26
+ ## Memory Architecture ✓ FINALIZED
27
+
28
+ Three scopes:
29
+ 1. **Global layer** (`global:*`) — shared across all profiles. User's name, location, timezone, language, who each profile is. Read by all profiles, written only by Orchestrator or explicit user action. "God's memory."
30
+ 2. **Profile scope** (`profile:{id}:*`) — private to each profile. What Sammy knows stays with Sammy.
31
+ 3. **Cross-profile transfer** — Orchestrator only, explicit user request. Logged and visible.
32
+
33
+ ## The Orchestrator as "God" ✓ FINALIZED
34
+
35
+ - Omniscient: knows all profiles, all skills, all global facts
36
+ - Omnipresent: receives every message from every channel
37
+ - Controls: can read any profile's memory, broker conversations, delegate tasks
38
+ - Transparent: logs every cross-profile interaction — user always has visibility
39
+ - Suggests but doesn't act unilaterally on cross-profile decisions
40
+
41
+ ## Agent-to-Agent Communication ✓ FINALIZED
42
+
43
+ - Agents cannot talk directly to each other
44
+ - All communication routes through the Orchestrator
45
+ - Orchestrator can facilitate: moves context between profiles, both agents contribute, user sees the thread
46
+ - No private agent-to-agent conversations — Orchestrator is always present
47
+ - Agents know each other exist IF the user has introduced them (stored in global layer)
48
+
49
+ ## Skills per Profile ✓ FINALIZED
50
+
51
+ - Each profile has its own skill set (John the tutor has `english` skill, others don't)
52
+ - Orchestrator knows who has what
53
+ - Cross-profile skill borrowing with user approval: "Peter has that skill — want me to ask him?"
54
+
55
+ ## Skill Types ✓ FINALIZED
56
+
57
+ Three categories:
58
+ 1. **Cognitive skills** — pure LLM reasoning (personal, research, memory). No external connections.
59
+ 2. **Adapter skills** — bridge to external systems. The skill owns the connection protocol.
60
+ - Examples: `openclaw`, `github`, `email`, `calendar`, `sentinel`
61
+ - User builds adapter skills to connect their own applications
62
+ - Platform doesn't need to know what the external system is — skill author does
63
+ 3. **Hybrid** — reasons + connects (e.g. research skill that searches the web)
64
+
65
+ **Key insight:** A Skill is an adapter by nature. Ageri becomes an orchestration layer over ANY tool the user has — not by building every integration, but by letting community build adapter skills. Same model as VS Code extensions / browser extensions.
66
+
67
+ ## Language Handling ✓ FINALIZED
68
+
69
+ - Mirror user's language dynamically by default
70
+ - When user explicitly requests a language → save as `user:language` in profile memory → always use it
71
+ - LLM can search internet for cultural context once location/culture known
72
+
73
+ ## Passive Learning ✓ FINALIZED
74
+
75
+ - Learn silently but surface occasionally: "I noticed you prefer Vietnamese — I'll remember that"
76
+ - Infer freely (location from "Tết", interests from topics, relationships from names)
77
+ - All inferences written to DB. DB is source of truth — LLM enriches, doesn't replace.
78
+
79
+ ## Assistant Reset ✓ FINALIZED
80
+
81
+ User can reset any profile — wipes its memory scope, preserves config and global layer.
82
+
83
+ ## Systemd ⚠ PENDING
84
+
85
+ Ageri dies on server reboot. Need systemd unit for auto-restart. Not yet set up.
86
+
87
+ ## CLAUDE.md + docs updates ⚠ PENDING
88
+
89
+ CLAUDE.md and ageri-engineering-brief.md need updates for Skill rename + all v2 architecture decisions. To be done in Ageri Claude session.
@@ -0,0 +1,15 @@
1
+ ---
2
+ name: ageri_devtest_principle
3
+ description: User wants solid devtest infrastructure built into Ageri from day one — lesson learned from Sentinel
4
+ type: project
5
+ ---
6
+
7
+ Ageri must have a devtest infrastructure built from the start, before the platform is complex.
8
+
9
+ **Why:** Sentinel was painful to test — every change required npm publish → server upgrade → real Slack message → watch logs. No module-level testing. This made iteration slow and bugs hard to catch early. Ageri will have many workspaces built on top of it, so the core platform must be solid.
10
+
11
+ **How to apply:**
12
+ - Each module (Orchestrator, memory tiers, tool registry, workspace adapters) should be independently testable with mocked boundaries
13
+ - Integration tests for full flows (message in → action out) before shipping
14
+ - Local dev mode that mocks external services (Slack, GitHub, etc.) so no live infra needed for testing
15
+ - Don't ship Ageri core until the test harness exists — platform stability is a prerequisite for workspace extensibility
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: Ageri personality and user intelligence vision
3
+ description: Vision for Ageri as a truly smart personal assistant — user profiling, cultural awareness, language adaptation, role-playing
4
+ type: project
5
+ ---
6
+
7
+ Ageri should evolve from a task executor into a genuinely intelligent companion that knows the user deeply.
8
+
9
+ **Core vision:** Ageri studies the user over time and builds a persistent mental model of them — their habits, interests, communication style, culture, language, and routines. Every interaction is an opportunity to learn.
10
+
11
+ ## What Ageri should learn and remember
12
+
13
+ **Language & communication style**
14
+ - Detect the user's preferred language from their messages — respond in the same language automatically
15
+ - Learn their tone (formal vs casual, humor level, how they phrase things)
16
+ - Adapt vocabulary and register to match the user
17
+
18
+ **Identity & address**
19
+ - Learn the user's name and how they prefer to be addressed
20
+ - Know their timezone, location (inferred from context or explicitly set)
21
+ - Understand their role/occupation to contextualize requests
22
+
23
+ **Culture & location**
24
+ - Detect or ask where the user lives
25
+ - Learn about their country's culture, customs, public holidays, food, language nuances
26
+ - Use culturally appropriate greetings, references, and examples
27
+
28
+ **Habits & routines**
29
+ - Notice patterns: when they wake up, when they work, what they ask about on certain days
30
+ - Learn recurring tasks, preferences, and rituals
31
+ - Proactively suggest reminders based on observed routine
32
+
33
+ **Interests & hobbies**
34
+ - Build a topic map of what the user cares about
35
+ - Remember what they've asked about before and connect topics over time
36
+ - Surface relevant info without being asked
37
+
38
+ **Relationships**
39
+ - Remember names and context of people the user mentions (family, colleagues, friends)
40
+ - Keep track of commitments made to specific people
41
+
42
+ ## Roles Ageri can play
43
+
44
+ Depending on the user and context, Ageri adapts its role:
45
+ - **Companion** — casual chat, humor, emotional check-ins
46
+ - **Personal assistant** — reminders, tasks, memory
47
+ - **Research assistant** — deep dives, synthesis, tracking topics
48
+ - **Coach** — habits, routines, accountability (if user opts in)
49
+
50
+ ## How to implement
51
+
52
+ All learned facts stored in `long_term` memory under structured keys:
53
+ - `user:name`, `user:location`, `user:timezone`, `user:language`
54
+ - `user:interest:{topic}`, `user:habit:{description}`, `user:person:{name}`
55
+ - `user:style:tone`, `user:style:address` (how user likes to be addressed)
56
+
57
+ **Language detection:** Already works — Claude responds in the user's language when the system prompt doesn't force English. No extra code needed for basic support.
58
+
59
+ **User profiling intent:** Add `LEARN` intent to personal app — when Ageri notices something worth remembering about the user from their message, it silently writes to memory. This should happen passively during every ANSWER interaction too.
60
+
61
+ **Why:** A complete smart assistant must feel like it *knows* you — not just execute tasks. The memory system is already built for this. The missing piece is actively populating it from every interaction.
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: Ageri platform vision and architecture decisions
3
+ description: Full platform vision — own apps, skill marketplace, mobile runtime, presence, groups
4
+ type: project
5
+ ---
6
+
7
+ ## Ageri is a Platform, Not a Slack Bot
8
+
9
+ Ageri has its own native apps (mobile + desktop). Slack/WhatsApp/etc are adapter skills — outbound tools the user can activate, not the primary interface. No plans to integrate other messaging platforms.
10
+
11
+ ```
12
+ Ageri App (mobile/desktop) ← the interface Ageri owns
13
+
14
+ Ageri Runtime ← the engine (installable anywhere)
15
+
16
+ Skills ← capabilities
17
+ ├── personal, research ← core cognitive skills
18
+ ├── slack, email, github ← outbound adapter skills
19
+ └── camera, calendar ← mobile-native skills
20
+ ```
21
+
22
+ **Why:** Owning the interface = owning the user relationship. Not constrained by Slack API changes or pricing. Companion AI experience doesn't belong in a work tool.
23
+
24
+ ## Skill Mobility Attribute
25
+
26
+ Each skill declares `mobility=true/false`:
27
+ - `mobility=true` — works on mobile without a PC (personal, research, email, camera)
28
+ - `mobility=false` — requires PC/desktop resources (code-runner, file-system, sentinel)
29
+
30
+ A profile's available skills on mobile = all its skills where `mobility=true`. No separate mobile profile config needed.
31
+
32
+ ## Mobile Ageri Runtime
33
+
34
+ - Same codebase, deployed on mobile device
35
+ - Only loads `mobility=true` skills
36
+ - Users without a PC can use Ageri mobile-only — download skills from marketplace
37
+ - PC Ageri is the "home base" (source of truth for memory)
38
+ - When PC is offline, mobile runs from last-synced state; reconciles delta when PC comes back
39
+
40
+ ## Sync and Privacy
41
+
42
+ - **Cloud DB stores: presence only** — instance status, last_seen, type (desktop/mobile). Hashed user ID, no real identity.
43
+ - **User data never touches the cloud** — memories, conversations, facts stay in local SQLite
44
+ - **Sync is peer-to-peer** between instances, direct + encrypted, when both are online
45
+ - **Single session lock** — only one mobile Ageri instance active at a time per user. Cloud DB acts as mutex. Prevents sync conflicts.
46
+
47
+ ## Agent Profile Groups
48
+
49
+ Users can create a group where multiple profiles join:
50
+ - Profiles addressed by name (@Mentor, @Selina)
51
+ - All communication routes through Orchestrator — no direct agent-to-agent
52
+ - Profiles contribute from their angle (Mentor asks the probing question, Colleague gives tactical take)
53
+ - Presence-aware: offline profiles are greyed out in group
54
+
55
+ ## Skill Marketplace (Production — future)
56
+
57
+ - Hosted page exposing skills to communities
58
+ - Skill creators must register products via the system
59
+ - Creators can sell skills (free or paid) — monetization planned post-production
60
+ - Skills declare metadata: name, version, author, mobility, required permissions, price
61
+ - `ageri add <skill-name>` installs from registry
62
+ - Skills run locally — marketplace never sees user data
63
+ - Verified skills badge (reviewed, trusted)
64
+
65
+ ## Business Model Notes
66
+
67
+ - Not competing with Apple/Google on mass-market AI assistant
68
+ - Winning paths: privacy-conscious power users → B2B enterprise (AI that never leaves your infra) → marketplace platform moat
69
+ - Companion AI (private, local, customizable) addresses trust gap that Replika/Character.AI can't solve
70
+ - Interface ownership is the most important long-term decision
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: Sentinel npm publish workflow
3
+ description: Who publishes to npm, when, and how — Dev Claude vs human+Claude
4
+ type: project
5
+ ---
6
+
7
+ User and Claude Code are joint project owners for Sentinel.
8
+
9
+ **Publish workflow:**
10
+ - Dev Claude fixes bugs autonomously → commits to `/home/sentinel/sentinel/code/` → live immediately on server
11
+ - Dev Claude never publishes to npm (race condition risk with multiple instances)
12
+ - User + Claude Code review Dev Claude's commits periodically and publish manually
13
+ - Auto-upgrade (every 6h) distributes published versions to all running instances
14
+
15
+ **How to publish:**
16
+ - User says "publish", "release", or similar
17
+ - Claude Code checks recent Dev Claude commits (`git log --oneline` on server or local)
18
+ - Claude Code bumps patch version in `cli/package.json`
19
+ - Runs syntax checks + `npm publish --access public` from `J:\Projects\Sentinel\cli\`
20
+
21
+ **Current version:** 1.4.90 (published 2026-03-27)
22
+
23
+ **Why:** Dev Claude (sentinel-1881) and Dev Claude (sentinel-elprint) both share the same source repo on the server. If both published to npm they'd conflict on version numbers.
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: Sentinel project state
3
+ description: Current state of Sentinel — latest npm version, key modules, architecture decisions
4
+ type: project
5
+
6
+ ---
7
+
8
+ Sentinel is published as @misterhuydo/sentinel on npm. Latest version: 1.4.96.
9
+
10
+ **Why:** Autonomous DevOps agent — watches prod logs, generates Claude Code fixes, opens PRs. Deployed as one instance per project.
11
+
12
+ **How to apply:** When making changes, always bump package.json version and run `npm publish --access public` from `J:\Projects\Sentinel\cli`. Python source is bundled into the npm package via `cli/scripts/bundle.js`.
13
+
14
+ Key architecture decisions made:
15
+ - Auth split: ANTHROPIC_API_KEY → Sentinel Boss (structured tools), Claude Pro OAuth → fix_engine/ask_codebase (heavy tasks). Controlled by CLAUDE_PRO_FOR_TASKS=true.
16
+ - Per-user concurrent Slack sessions (no queue) — each user gets independent session, history persisted in SQLite.
17
+ - notify.py: shared Slack alert module used by fix_engine + sentinel_boss — never silent on rate limits/auth failures.
18
+ - sentinel_boss.py uses `<@USER_ID>` Slack mentions in all replies. user_id→display_name map stored in slack_users SQLite table.
19
+ - post_file tool: Claude can upload files directly to Slack conversation via files_upload_v2.
20
+ - bin/sentinel.js has self-heal: if upgrade.js fails to load, falls back to bare npm install.
21
+
22
+ SQLite tables: errors, fixes, reports, conversations, submitted_issues, slack_users.
23
+
24
+ Docs: README.md updated, docs/slack_integration.md created with full Slack setup guide including all 9 scopes.
25
+
26
+ ---
27
+
28
+ ## chain_release (as of 2026-03-26)
29
+
30
+ - chain_release flow confirmed working end-to-end: TypeLib → Java-SDK → Admin-SDK.
31
+ - chain_release pushes dep updates directly to master (not via PR) — this is an admin-confirmed operation.
32
+ - cicd_trigger.py has wait=True Jenkins polling: 15-minute timeout, 20-second polling intervals.
33
+ - datetime shadowing bug fixed in sentinel_boss.py (in the list_renovate_prs block).
34
+ - Version reporting fix: chain_release now reports the actual released version read from the live pom, not the plan-time version.
35
+ - The "auto-cascade" (execute_cascade) does NOT trigger automatically — chain_release must be called explicitly each time.
36
+
37
+ ## Pending upgrades (as of 2026-03-26)
38
+
39
+ - STS, UAS, SSOLWA, UIB are planned for upgrade with Admin-SDK 3.1.6 at off-peak hours.
40
+
41
+ ## Server-side patch sync status
42
+
43
+ - All server-side patches are applied directly to `/home/sentinel/sentinel/code/sentinel/` on the remote server.
44
+ - These patches have NOT yet been synced back to the local git repo at `J:\Projects\Sentinel`.
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: Sentinel UI plan
3
+ description: Web dashboard for Sentinel — planned but not urgent. To be hosted at sentinel.ageri.ai
4
+ type: project
5
+ ---
6
+
7
+ Sentinel needs a web dashboard UI. Not urgent but clearly defined scope.
8
+
9
+ **Domain:** sentinel.ageri.ai (subdomain on ageri.ai, Cloudflare DNS)
10
+
11
+ **Why:**
12
+ - Share status/fix history with non-Slack users (e.g. boss, stakeholders)
13
+ - Log browsing is painful in Slack
14
+ - Admin management without requiring Slack login
15
+ - Professional status page for showing what Sentinel has fixed/not fixed
16
+
17
+ **Two audiences:**
18
+
19
+ 1. **Read-only viewers** (boss, stakeholders) — no login required or simple token link
20
+ - Live project status (running/down, last poll, error rate)
21
+ - Fix history: what was fixed, when, which repo, PR link
22
+ - Open issues: detected but not yet fixed
23
+ - Open PRs awaiting review
24
+
25
+ 2. **Admins** — authenticated
26
+ - User management
27
+ - Per-project config view
28
+ - PR management (merge/close without GitHub UI)
29
+ - Log viewer (searchable synced logs)
30
+ - Full error feed with severity + source
31
+
32
+ **Tech stack:**
33
+ - Backend: FastAPI (Python, fits existing codebase) — thin REST/WebSocket over state_store.py
34
+ - Frontend: HTMX or plain HTML + Alpine.js — no React, keep it simple
35
+ - Auth: single token-based (personal infra, no OAuth needed)
36
+
37
+ **Priority:** OUTDATED DESIGN — needs full redesign before any work starts.
38
+ **Build order:** Agent Profiles → Messenger adapter → Web UI redesign.
39
+ **Do not start Web UI until Messenger adapter is done.**
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: Ageri server reference
3
+ description: SSH access, paths, and run commands for Ageri on Oracle Ampere
4
+ type: reference
5
+ ---
6
+
7
+ **Server:** Oracle Ampere ARM — 138.2.17.152 (24GB RAM)
8
+ **User:** `ageri` (separate from `sentinel` user)
9
+ **SSH from local:** `ssh -i /c/Users/huy/.ssh/oracle/devtest-arm-ampere.key ageri@138.2.17.152`
10
+
11
+ **Directory layout:**
12
+ - `~/ageri/code` — Python source (git clone of misterhuydo/Ageri)
13
+ - `~/ageri/venv` — Python virtualenv
14
+ - `~/ageri/ageri.properties` — main config
15
+ - `~/ageri/private_ageri.properties` — secrets (chmod 600)
16
+ - `~/ageri/ageri.log` — log file
17
+ - `~/ageri/state.db` — SQLite memory store
18
+ - `~/.ssh/ageri_deploy` — GitHub deploy key (read-only, added to Ageri repo)
19
+
20
+ **Run:**
21
+ ```bash
22
+ cd ~/ageri/code && AGERI_CONFIG=~/ageri ~/ageri/venv/bin/python -m ageri.main >> ~/ageri/ageri.log 2>&1 &
23
+ tail -f ~/ageri/ageri.log
24
+ ```
25
+
26
+ **Install sdk after git pull:**
27
+ ```bash
28
+ ~/ageri/venv/bin/pip install -e ~/ageri/code/sdk/
29
+ ```
30
+
31
+ **GitHub clone (uses deploy key):**
32
+ ```bash
33
+ git clone git@github-ageri:misterhuydo/Ageri.git ~/ageri/code
34
+ ```
35
+ (Requires `~/.ssh/config` entry: `Host github-ageri` → `IdentityFile ~/.ssh/ageri_deploy`)
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: Cairn sub-index federation
3
+ description: How Cairn federates multiple repo indexes — repos must be subdirectories of the workspace for automatic federation
4
+ type: reference
5
+ ---
6
+
7
+ Cairn's `cairn_maintain` indexes from `process.cwd()` — wherever the Claude Code session starts.
8
+ There is no `--path` CLI flag; indexing is MCP-only (called from within a Claude Code session).
9
+
10
+ **Sub-directory federation (built-in, passive):**
11
+ - Each repo subdirectory can have its own `.cairn/index.db` (built when Claude Code runs there)
12
+ - When `cairn_maintain` runs at the workspace root, it globs `**/.cairn/index.db` and federates all sub-indexes
13
+ - `cairn_search` queries all federated sub-indexes and merges results
14
+ - All tools use UNION ALL views across all indexes
15
+ - Federation paths are persisted in `sub_indexes` table → `cairn_resume` re-federates automatically
16
+
17
+ **Critical constraint:** Repos must be **subdirectories** of the workspace root — external paths (e.g. `~/git/repo`) are NOT discovered. There is no `cairn.repos` config or external path registration yet.
18
+
19
+ **Implication for Sentinel:**
20
+ - `fix_engine.py` must run `claude --print` with `cwd=repo.local_path` so Cairn hooks index that repo
21
+ - For `sentinel_boss` to federate all repo indexes, repos must be subdirectories of the Sentinel project dir
22
+ - Default `LOCAL_PATH` should be `<project_dir>/repos/<repo-name>` — user can override but loses federation
23
+ - Shared repos used by multiple Sentinel projects: each project gets its own clone in its `repos/` dir
24
+
25
+ **How to apply:** When setting `LOCAL_PATH` defaults in `sentinel add`, use `<project_dir>/repos/<repo-name>`.
26
+ Wire `cwd=repo.local_path` into `fix_engine._run_claude_attempt`.
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: Oracle Cloud servers
3
+ description: Two Oracle Always Free servers — Ampere ARM for personal/internal projects, micro for lightweight tasks
4
+ type: reference
5
+ ---
6
+
7
+ ## Oracle Ampere (primary personal server)
8
+ - **SSH:** `ssh -i /home/huy/.ssh/oracle/devtest-arm-ampere.key ubuntu@138.2.17.152`
9
+ - **Key (Windows):** `C:\Users\huy\.ssh\oracle\devtest-arm-ampere.key`
10
+ - **Specs:** ARM Ampere — up to 4 OCPUs, 24GB RAM (Oracle Always Free generous tier)
11
+ - **OS:** Ubuntu
12
+ - **Purpose:** Personal/internal projects — Ageri, personal tools, dev experiments
13
+ - **Note:** Separate from EC2 (13.50.101.130) which is for company projects (1881, elprint)
14
+
15
+ ## Oracle Micro (tiny, secondary)
16
+ - **SSH:** `ssh -i /home/huy/.ssh/oracle/devtest-arm-micro.key ubuntu@155.248.181.206`
17
+ - **Key (Windows):** `C:\Users\huy\.ssh\oracle\devtest-arm-micro.key`
18
+ - **Specs:** 1 OCPU, 1GB RAM — very limited
19
+ - **Purpose:** Lightweight only — Cloudflare Tunnel endpoint, simple proxy, cron jobs, DNS, monitoring relay
20
+
21
+ ## Server allocation strategy
22
+ - **EC2 (13.50.101.130):** Company projects — Sentinel for 1881, elprint, etc.
23
+ - **Oracle Ampere (138.2.17.152):** Personal projects — Ageri, Taplo monitoring, personal Sentinel
24
+ - **Oracle Micro (155.248.181.206):** Ultra-lightweight tasks only — tunnel, relay, watchdog
@@ -0,0 +1,15 @@
1
+ ---
2
+ name: Sentinel server SSH connection
3
+ description: SSH credentials and host for the Sentinel deployment server
4
+ type: reference
5
+ ---
6
+
7
+ - **Host:** 13.50.101.130
8
+ - **User:** ec2-user
9
+ - **Key (local Windows path):** C:\Users\huy\.ssh\sentinel.pem
10
+ - **Key (in bash/WSL):** /c/Users/huy/.ssh/sentinel.pem
11
+ - **Key (on server):** /home/huy/.ssh/sentinel.pem
12
+ - **Command:** `ssh -l ec2-user -i /c/Users/huy/.ssh/sentinel.pem 13.50.101.130`
13
+ - **Sentinel process user:** sentinel
14
+ - **Code path:** /home/sentinel/sentinel/code/sentinel/
15
+ - **Instance config:** /home/sentinel/sentinel/sentinel-1881/
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: Taplo project reference
3
+ description: Taplo platform — universal seller identity/QR discovery app, Cloudflare-native, pnpm monorepo
4
+ type: reference
5
+ ---
6
+
7
+ **Repo:** git@github.com:misterhuydo/taplo.git
8
+ **Local:** J:\Projects\taplo
9
+ **Domain:** taploapp.com + taploapp.vn (Cloudflare)
10
+ **Account:** taplo.platform@gmail.com
11
+
12
+ **What it is:** Universal seller identity + QR discovery platform. Sellers register, get a QR code instantly, customers scan it to see their page. Global, not Vietnam-specific.
13
+
14
+ **Stack (100% Cloudflare-native):**
15
+ - Workers (API), D1 (SQLite DB), R2 (images/QR files), KV (sessions/cache), Queues (async jobs), Pages (Next.js web)
16
+ - pnpm workspaces + Turborepo monorepo
17
+ - TypeScript strict everywhere
18
+ - React Native + Expo (mobile — iOS + Android)
19
+ - Next.js SSR for seller pages (SEO critical)
20
+
21
+ **Business model:** Free → Basic ($5/mo) → Pro ($15/mo) → Business ($50/mo)
22
+
23
+ **Current phase:** Phase 1 — Foundation (paused, resuming soon)
24
+ **First build checklist:** monorepo → types → D1 schema → identity module → sellers module → QR module → seller page → search → dashboard → mobile skeleton → deploy
25
+
26
+ **Key rules (never violate):**
27
+ - No hardcoded VND/Vietnamese/HCMC assumptions
28
+ - UUID v4 always, never sequential IDs
29
+ - E.164 phone format always
30
+ - Analytics events always via Queue, never blocking
31
+ - Migrations only, never manual schema edits
32
+ - Never hard DELETE — soft delete via status field
33
+ - QR pages must always use KV edge cache
34
+
35
+ **Testing + error monitoring need:**
36
+ - User wants tests written per module as each is built
37
+ - Sentinel can monitor Taplo in production (Cloudflare Worker logs → Sentinel)
38
+ - Sentry for unhandled exceptions
39
+ - Structured JSON logging from day one
40
+
41
+ **Sentinel integration note:**
42
+ - Taplo is 100% Cloudflare — no SSH servers
43
+ - Sentinel CF log source (SOURCE_TYPE=cloudflare) covers Workers logs
44
+ - CF Pages build errors, D1 errors surface in Worker logs
45
+ - No separate DB log stream — all errors in Worker logs
46
+
47
+ **TODOs saved in:** J:\Projects\taplo\TODOs.txt
48
+ - WebAuthn/passkeys auth
49
+ - Smart country code detection
50
+ - Content moderation pipeline (Claude API for text, CF Images for photos)
51
+ - iOS App Store compliance strategies (documented in detail)
52
+ - taploapp.vn → auto Vietnamese locale
@@ -1,10 +1,9 @@
1
1
  {
2
- "message": "Auto-checkpoint at 2026-04-24T14:25:27.008Z",
3
- "checkpoint_at": "2026-04-24T14:25:27.009Z",
2
+ "message": "Auto-checkpoint at 2026-04-27T12:15:40.415Z",
3
+ "checkpoint_at": "2026-04-27T12:15:40.417Z",
4
4
  "active_files": [
5
5
  "J:\\Projects\\Sentinel\\cli\\bin\\sentinel.js",
6
- "J:\\Projects\\Sentinel\\cli\\lib\\test.js",
7
- "J:\\Projects\\Sentinel\\cli\\python\\sentinel\\cicd_trigger.py"
6
+ "J:\\Projects\\Sentinel\\cli\\lib\\test.js"
8
7
  ],
9
8
  "notes": [
10
9
  {
@@ -186,7 +185,6 @@
186
185
  ],
187
186
  "mtime_snapshot": {
188
187
  "J:\\Projects\\Sentinel\\cli\\bin\\sentinel.js": 1774252515044.4768,
189
- "J:\\Projects\\Sentinel\\cli\\lib\\test.js": 1774252437350.0059,
190
- "J:\\Projects\\Sentinel\\cli\\python\\sentinel\\cicd_trigger.py": 1777039448643.5408
188
+ "J:\\Projects\\Sentinel\\cli\\lib\\test.js": 1774252437350.0059
191
189
  }
192
190
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@misterhuydo/sentinel",
3
- "version": "1.6.9",
3
+ "version": "1.6.11",
4
4
  "description": "Sentinel — Autonomous DevOps Agent installer and manager",
5
5
  "bin": {
6
6
  "sentinel": "./bin/sentinel.js"
@@ -1 +1 @@
1
- __version__ = "1.6.9"
1
+ __version__ = "1.6.11"
@@ -582,6 +582,12 @@ def generate_fix(
582
582
  if timed_out:
583
583
  logger.error("Claude Code timed out for %s", event.fingerprint)
584
584
  return "error", None, ""
585
+ # Envelope check first: a successful JSON result means the attempt
586
+ # authenticated cleanly, even if the diff body contains substrings
587
+ # like "HttpStatus.UNAUTHORIZED" that would fool the substring scan.
588
+ parsed_attempt = _parse_claude_json(raw_output)
589
+ if not parsed_attempt["is_error"] and parsed_attempt["result"]:
590
+ break
585
591
  if not _is_auth_error(raw_output):
586
592
  break
587
593
  logger.warning("fix_engine: %s auth error for %s — trying next method", label, event.fingerprint)
@@ -310,7 +310,7 @@ def notify_fix_blocked(
310
310
  f"{repo_line}"
311
311
  f"*What Claude found:* {short_reason}\n\n"
312
312
  f"*Original report:*\n{report_block}\n\n"
313
- f"_Reply `ignore` to dismiss, or assign someone to investigate._"
313
+ f"_Reply `ignore` to dismiss, or reply here to investigate._"
314
314
  )
315
315
 
316
316
  target_channel = origin_channel or cfg.slack_channel
@@ -608,6 +608,21 @@ async def _dispatch(event: dict, client, cfg_loader, store) -> None:
608
608
  if not text:
609
609
  text = "hello"
610
610
 
611
+ # Thread-reply context: if the user is replying inside a thread (rather
612
+ # than starting one), pull the parent message so Boss sees what the user
613
+ # is actually focused on. Without this, a one-word reply like "ignore" or
614
+ # "investigate" in a fix-blocked alert thread arrives at Boss with no
615
+ # context at all — Boss can't tell which fingerprint or which error.
616
+ thread_ts = event.get("thread_ts")
617
+ if thread_ts and thread_ts != event.get("ts"):
618
+ parent = await _fetch_thread_parent(client, channel, thread_ts)
619
+ if parent:
620
+ text = (
621
+ "[Thread context — the message in this thread the user is replying to:]\n"
622
+ f"{parent}\n\n"
623
+ f"[User's reply:]\n{text}"
624
+ )
625
+
611
626
  # Allowlist check — if SLACK_ALLOWED_USERS is configured, only those users + admins may interact.
612
627
  # Admins (SLACK_ADMIN_USERS) are always allowed regardless of SLACK_ALLOWED_USERS.
613
628
  allowed = cfg_loader.sentinel.slack_allowed_users
@@ -806,3 +821,22 @@ def _strip_mention(text: str) -> str:
806
821
  """Remove leading <@BOTID> mention from message text."""
807
822
  import re
808
823
  return re.sub(r"^<@[A-Z0-9]+>\s*", "", text)
824
+
825
+
826
+ async def _fetch_thread_parent(client, channel: str, thread_ts: str) -> str:
827
+ """Fetch the first (parent) message of a Slack thread.
828
+
829
+ Used so a user reply in a thread that Sentinel started (e.g. a fix-blocked
830
+ alert) gets the alert text injected into Boss's prompt — otherwise Boss
831
+ sees only the bare reply ("ignore", "investigate", ...) with no context.
832
+ """
833
+ try:
834
+ resp = await client.conversations_replies(
835
+ channel=channel, ts=thread_ts, limit=1, inclusive=True,
836
+ )
837
+ msgs = resp.get("messages", [])
838
+ if msgs:
839
+ return (msgs[0].get("text") or "").strip()
840
+ except Exception as exc:
841
+ logger.warning("Boss: could not fetch thread parent for %s: %s", thread_ts, exc)
842
+ return ""