@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.
- package/.cairn/.hint-lock +1 -1
- package/.cairn/memory/auto-memory/MEMORY.md +21 -0
- package/.cairn/memory/auto-memory/decision_auto_commit_auto_release_split.md +22 -0
- package/.cairn/memory/auto-memory/decision_git_apply_recount_for_llm_diffs.md +17 -0
- package/.cairn/memory/auto-memory/decision_jenkins_wait_before_cascade.md +23 -0
- package/.cairn/memory/auto-memory/decision_multi_repo_fix_architecture.md +25 -0
- package/.cairn/memory/auto-memory/decision_per_project_claude_session.md +23 -0
- package/.cairn/memory/auto-memory/experience_bash_local_shadows_env_var.md +11 -0
- package/.cairn/memory/auto-memory/experience_cairn_session_discipline.md +9 -0
- package/.cairn/memory/auto-memory/experience_cicd_user_must_not_be_hardcoded.md +17 -0
- package/.cairn/memory/auto-memory/experience_claude_resume_stale_context_risk.md +23 -0
- package/.cairn/memory/auto-memory/experience_envelope_first_auth_detection.md +11 -0
- package/.cairn/memory/auto-memory/experience_mvn_negative_cache_blocks_cascade_retries.md +17 -0
- package/.cairn/memory/auto-memory/experience_publish_safety_check.md +13 -0
- package/.cairn/memory/auto-memory/experience_secrets_in_gitignored_files.md +9 -0
- package/.cairn/memory/auto-memory/experience_sentinel_deployment_server.md +13 -0
- package/.cairn/memory/auto-memory/feedback_ageri_rag_architecture.md +19 -0
- package/.cairn/memory/auto-memory/feedback_design_principle.md +16 -0
- package/.cairn/memory/auto-memory/feedback_publish_workflow.md +14 -0
- package/.cairn/memory/auto-memory/feedback_secrets_handling.md +15 -0
- package/.cairn/memory/auto-memory/feedback_slack_admin_allowlist.md +11 -0
- package/.cairn/memory/auto-memory/feedback_slack_thinking_status.md +14 -0
- package/.cairn/memory/auto-memory/feedback_start_sh_patching.md +16 -0
- package/.cairn/memory/auto-memory/knowledge_cairn_federation_for_sentinel_projects.md +20 -0
- package/.cairn/memory/auto-memory/knowledge_cairn_hooks_block_oauth_read_edit.md +24 -0
- package/.cairn/memory/auto-memory/knowledge_sentinel_repo_remotes.md +28 -0
- package/.cairn/memory/auto-memory/knowledge_sentinel_systemd_inactive_is_misleading.md +18 -0
- package/.cairn/memory/auto-memory/knowledge_sentinel_upgrade_requires_file_copy.md +20 -0
- package/.cairn/memory/auto-memory/preference_no_api_key_for_heavy_coding.md +17 -0
- package/.cairn/memory/auto-memory/project_ageri.md +45 -0
- package/.cairn/memory/auto-memory/project_ageri_architecture_v2.md +89 -0
- package/.cairn/memory/auto-memory/project_ageri_devtest.md +15 -0
- package/.cairn/memory/auto-memory/project_ageri_personality.md +61 -0
- package/.cairn/memory/auto-memory/project_ageri_platform_vision.md +70 -0
- package/.cairn/memory/auto-memory/project_publish_workflow.md +23 -0
- package/.cairn/memory/auto-memory/project_sentinel_state.md +44 -0
- package/.cairn/memory/auto-memory/project_sentinel_ui.md +39 -0
- package/.cairn/memory/auto-memory/reference_ageri_server.md +35 -0
- package/.cairn/memory/auto-memory/reference_cairn_federation.md +26 -0
- package/.cairn/memory/auto-memory/reference_oracle_servers.md +24 -0
- package/.cairn/memory/auto-memory/reference_sentinel_server.md +15 -0
- package/.cairn/memory/auto-memory/reference_taplo.md +52 -0
- package/.cairn/session.json +4 -6
- package/package.json +1 -1
- package/python/sentinel/__init__.py +1 -1
- package/python/sentinel/fix_engine.py +6 -0
- package/python/sentinel/notify.py +1 -1
- package/python/sentinel/slack_bot.py +34 -0
- 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
|
package/.cairn/session.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"message": "Auto-checkpoint at 2026-04-
|
|
3
|
-
"checkpoint_at": "2026-04-
|
|
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 +1 @@
|
|
|
1
|
-
__version__ = "1.6.
|
|
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
|
|
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 ""
|