@made-by-moonlight/athene-core 0.9.1
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/LICENSE +22 -0
- package/README.md +241 -0
- package/dist/activity-events.d.ts +42 -0
- package/dist/activity-events.d.ts.map +1 -0
- package/dist/activity-events.js +192 -0
- package/dist/activity-events.js.map +1 -0
- package/dist/activity-log.d.ts +71 -0
- package/dist/activity-log.d.ts.map +1 -0
- package/dist/activity-log.js +203 -0
- package/dist/activity-log.js.map +1 -0
- package/dist/activity-signal.d.ts +20 -0
- package/dist/activity-signal.d.ts.map +1 -0
- package/dist/activity-signal.js +91 -0
- package/dist/activity-signal.js.map +1 -0
- package/dist/agent-report.d.ts +148 -0
- package/dist/agent-report.d.ts.map +1 -0
- package/dist/agent-report.js +516 -0
- package/dist/agent-report.js.map +1 -0
- package/dist/agent-selection.d.ts +31 -0
- package/dist/agent-selection.d.ts.map +1 -0
- package/dist/agent-selection.js +69 -0
- package/dist/agent-selection.js.map +1 -0
- package/dist/agent-workspace-hooks.d.ts +74 -0
- package/dist/agent-workspace-hooks.d.ts.map +1 -0
- package/dist/agent-workspace-hooks.js +988 -0
- package/dist/agent-workspace-hooks.js.map +1 -0
- package/dist/atomic-write.d.ts +6 -0
- package/dist/atomic-write.d.ts.map +1 -0
- package/dist/atomic-write.js +49 -0
- package/dist/atomic-write.js.map +1 -0
- package/dist/cleanup-stack.d.ts +37 -0
- package/dist/cleanup-stack.d.ts.map +1 -0
- package/dist/cleanup-stack.js +45 -0
- package/dist/cleanup-stack.js.map +1 -0
- package/dist/code-review-manager.d.ts +118 -0
- package/dist/code-review-manager.d.ts.map +1 -0
- package/dist/code-review-manager.js +719 -0
- package/dist/code-review-manager.js.map +1 -0
- package/dist/code-review-store.d.ts +114 -0
- package/dist/code-review-store.d.ts.map +1 -0
- package/dist/code-review-store.js +346 -0
- package/dist/code-review-store.js.map +1 -0
- package/dist/config-generator.d.ts +84 -0
- package/dist/config-generator.d.ts.map +1 -0
- package/dist/config-generator.js +295 -0
- package/dist/config-generator.js.map +1 -0
- package/dist/config.d.ts +55 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +852 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon-children.d.ts +55 -0
- package/dist/daemon-children.d.ts.map +1 -0
- package/dist/daemon-children.js +435 -0
- package/dist/daemon-children.js.map +1 -0
- package/dist/dashboard-notifications.d.ts +42 -0
- package/dist/dashboard-notifications.d.ts.map +1 -0
- package/dist/dashboard-notifications.js +123 -0
- package/dist/dashboard-notifications.js.map +1 -0
- package/dist/events-db.d.ts +39 -0
- package/dist/events-db.d.ts.map +1 -0
- package/dist/events-db.js +185 -0
- package/dist/events-db.js.map +1 -0
- package/dist/feature-flags.d.ts +2 -0
- package/dist/feature-flags.d.ts.map +1 -0
- package/dist/feature-flags.js +9 -0
- package/dist/feature-flags.js.map +1 -0
- package/dist/feedback-tools.d.ts +97 -0
- package/dist/feedback-tools.d.ts.map +1 -0
- package/dist/feedback-tools.js +161 -0
- package/dist/feedback-tools.js.map +1 -0
- package/dist/file-lock.d.ts +5 -0
- package/dist/file-lock.d.ts.map +1 -0
- package/dist/file-lock.js +59 -0
- package/dist/file-lock.js.map +1 -0
- package/dist/format-automated-comments.d.ts +18 -0
- package/dist/format-automated-comments.d.ts.map +1 -0
- package/dist/gh-trace.d.ts +57 -0
- package/dist/gh-trace.d.ts.map +1 -0
- package/dist/gh-trace.js +320 -0
- package/dist/gh-trace.js.map +1 -0
- package/dist/git-activity.d.ts +10 -0
- package/dist/git-activity.d.ts.map +1 -0
- package/dist/git-activity.js +30 -0
- package/dist/git-activity.js.map +1 -0
- package/dist/global-config.d.ts +1085 -0
- package/dist/global-config.d.ts.map +1 -0
- package/dist/global-config.js +1067 -0
- package/dist/global-config.js.map +1 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/key-value.d.ts +7 -0
- package/dist/key-value.d.ts.map +1 -0
- package/dist/key-value.js +24 -0
- package/dist/key-value.js.map +1 -0
- package/dist/lifecycle-manager.d.ts +22 -0
- package/dist/lifecycle-manager.d.ts.map +1 -0
- package/dist/lifecycle-manager.js +2813 -0
- package/dist/lifecycle-manager.js.map +1 -0
- package/dist/lifecycle-state.d.ts +28 -0
- package/dist/lifecycle-state.d.ts.map +1 -0
- package/dist/lifecycle-state.js +446 -0
- package/dist/lifecycle-state.js.map +1 -0
- package/dist/lifecycle-status-decisions.d.ts +85 -0
- package/dist/lifecycle-status-decisions.d.ts.map +1 -0
- package/dist/lifecycle-status-decisions.js +262 -0
- package/dist/lifecycle-status-decisions.js.map +1 -0
- package/dist/lifecycle-transition.d.ts +81 -0
- package/dist/lifecycle-transition.d.ts.map +1 -0
- package/dist/lifecycle-transition.js +207 -0
- package/dist/lifecycle-transition.js.map +1 -0
- package/dist/metadata.d.ts +54 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +484 -0
- package/dist/metadata.js.map +1 -0
- package/dist/migration/storage-v2.d.ts +76 -0
- package/dist/migration/storage-v2.d.ts.map +1 -0
- package/dist/migration/storage-v2.js +1614 -0
- package/dist/migration/storage-v2.js.map +1 -0
- package/dist/notification-data.d.ts +135 -0
- package/dist/notification-data.d.ts.map +1 -0
- package/dist/notification-data.js +204 -0
- package/dist/notification-data.js.map +1 -0
- package/dist/notification-observability.d.ts +21 -0
- package/dist/notification-observability.d.ts.map +1 -0
- package/dist/notification-observability.js +154 -0
- package/dist/notification-observability.js.map +1 -0
- package/dist/notifier-resolution.d.ts +14 -0
- package/dist/notifier-resolution.d.ts.map +1 -0
- package/dist/notifier-resolution.js +23 -0
- package/dist/notifier-resolution.js.map +1 -0
- package/dist/observability.d.ts +100 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +535 -0
- package/dist/observability.js.map +1 -0
- package/dist/opencode-agents-md.d.ts +3 -0
- package/dist/opencode-agents-md.d.ts.map +1 -0
- package/dist/opencode-agents-md.js +40 -0
- package/dist/opencode-agents-md.js.map +1 -0
- package/dist/opencode-config.d.ts +2 -0
- package/dist/opencode-config.d.ts.map +1 -0
- package/dist/opencode-config.js +17 -0
- package/dist/opencode-config.js.map +1 -0
- package/dist/opencode-session-id.d.ts +2 -0
- package/dist/opencode-session-id.d.ts.map +1 -0
- package/dist/opencode-session-id.js +12 -0
- package/dist/opencode-session-id.js.map +1 -0
- package/dist/opencode-shared.d.ts +80 -0
- package/dist/opencode-shared.d.ts.map +1 -0
- package/dist/opencode-shared.js +202 -0
- package/dist/opencode-shared.js.map +1 -0
- package/dist/orchestrator-prompt.d.ts +19 -0
- package/dist/orchestrator-prompt.d.ts.map +1 -0
- package/dist/orchestrator-prompt.js +130 -0
- package/dist/orchestrator-prompt.js.map +1 -0
- package/dist/orchestrator-session-strategy.d.ts +5 -0
- package/dist/orchestrator-session-strategy.d.ts.map +1 -0
- package/dist/orchestrator-session-strategy.js +13 -0
- package/dist/orchestrator-session-strategy.js.map +1 -0
- package/dist/paths.d.ts +145 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +288 -0
- package/dist/paths.js.map +1 -0
- package/dist/platform.d.ts +32 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +211 -0
- package/dist/platform.js.map +1 -0
- package/dist/plugin-registry.d.ts +15 -0
- package/dist/plugin-registry.d.ts.map +1 -0
- package/dist/plugin-registry.js +499 -0
- package/dist/plugin-registry.js.map +1 -0
- package/dist/portfolio-projects.d.ts +7 -0
- package/dist/portfolio-projects.d.ts.map +1 -0
- package/dist/portfolio-projects.js +65 -0
- package/dist/portfolio-projects.js.map +1 -0
- package/dist/portfolio-registry.d.ts +42 -0
- package/dist/portfolio-registry.d.ts.map +1 -0
- package/dist/portfolio-registry.js +311 -0
- package/dist/portfolio-registry.js.map +1 -0
- package/dist/portfolio-routing.d.ts +5 -0
- package/dist/portfolio-routing.d.ts.map +1 -0
- package/dist/portfolio-routing.js +24 -0
- package/dist/portfolio-routing.js.map +1 -0
- package/dist/portfolio-session-service.d.ts +15 -0
- package/dist/portfolio-session-service.d.ts.map +1 -0
- package/dist/portfolio-session-service.js +206 -0
- package/dist/portfolio-session-service.js.map +1 -0
- package/dist/process-cache.d.ts +32 -0
- package/dist/process-cache.d.ts.map +1 -0
- package/dist/process-cache.js +44 -0
- package/dist/process-cache.js.map +1 -0
- package/dist/project-resolver.d.ts +5 -0
- package/dist/project-resolver.d.ts.map +1 -0
- package/dist/project-resolver.js +20 -0
- package/dist/project-resolver.js.map +1 -0
- package/dist/prompt-builder.d.ts +42 -0
- package/dist/prompt-builder.d.ts.map +1 -0
- package/dist/prompt-builder.js +182 -0
- package/dist/prompt-builder.js.map +1 -0
- package/dist/prompts/orchestrator.md.js +4 -0
- package/dist/prompts/orchestrator.md.js.map +1 -0
- package/dist/query-activity-events.d.ts +42 -0
- package/dist/query-activity-events.d.ts.map +1 -0
- package/dist/query-activity-events.js +170 -0
- package/dist/query-activity-events.js.map +1 -0
- package/dist/recovery/actions.d.ts +7 -0
- package/dist/recovery/actions.d.ts.map +1 -0
- package/dist/recovery/index.d.ts +8 -0
- package/dist/recovery/index.d.ts.map +1 -0
- package/dist/recovery/logger.d.ts +12 -0
- package/dist/recovery/logger.d.ts.map +1 -0
- package/dist/recovery/manager.d.ts +24 -0
- package/dist/recovery/manager.d.ts.map +1 -0
- package/dist/recovery/scanner.d.ts +11 -0
- package/dist/recovery/scanner.d.ts.map +1 -0
- package/dist/recovery/types.d.ts +170 -0
- package/dist/recovery/types.d.ts.map +1 -0
- package/dist/recovery/validator.d.ts +8 -0
- package/dist/recovery/validator.d.ts.map +1 -0
- package/dist/report-watcher.d.ts +93 -0
- package/dist/report-watcher.d.ts.map +1 -0
- package/dist/report-watcher.js +182 -0
- package/dist/report-watcher.js.map +1 -0
- package/dist/scm-webhook-utils.d.ts +6 -0
- package/dist/scm-webhook-utils.d.ts.map +1 -0
- package/dist/scm-webhook-utils.js +36 -0
- package/dist/scm-webhook-utils.js.map +1 -0
- package/dist/session-manager.d.ts +22 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +3077 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/spawn-target.d.ts +23 -0
- package/dist/spawn-target.d.ts.map +1 -0
- package/dist/spawn-target.js +39 -0
- package/dist/spawn-target.js.map +1 -0
- package/dist/storage-key.d.ts +9 -0
- package/dist/storage-key.d.ts.map +1 -0
- package/dist/storage-key.js +59 -0
- package/dist/storage-key.js.map +1 -0
- package/dist/tmux.d.ts +39 -0
- package/dist/tmux.d.ts.map +1 -0
- package/dist/tmux.js +141 -0
- package/dist/tmux.js.map +1 -0
- package/dist/types.d.ts +1496 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +215 -0
- package/dist/types.js.map +1 -0
- package/dist/update-cache.d.ts +59 -0
- package/dist/update-cache.d.ts.map +1 -0
- package/dist/update-cache.js +77 -0
- package/dist/update-cache.js.map +1 -0
- package/dist/utils/metadata-flatten.d.ts +3 -0
- package/dist/utils/metadata-flatten.d.ts.map +1 -0
- package/dist/utils/metadata-flatten.js +18 -0
- package/dist/utils/metadata-flatten.js.map +1 -0
- package/dist/utils/pr.d.ts +7 -0
- package/dist/utils/pr.d.ts.map +1 -0
- package/dist/utils/pr.js +97 -0
- package/dist/utils/pr.js.map +1 -0
- package/dist/utils/session-from-metadata.d.ts +16 -0
- package/dist/utils/session-from-metadata.d.ts.map +1 -0
- package/dist/utils/session-from-metadata.js +87 -0
- package/dist/utils/session-from-metadata.js.map +1 -0
- package/dist/utils/session-id.d.ts +4 -0
- package/dist/utils/session-id.d.ts.map +1 -0
- package/dist/utils/session-id.js +9 -0
- package/dist/utils/session-id.js.map +1 -0
- package/dist/utils/validation.d.ts +9 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +45 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/utils.d.ts +65 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +189 -0
- package/dist/utils.js.map +1 -0
- package/dist/version-compare.d.ts +27 -0
- package/dist/version-compare.d.ts.map +1 -0
- package/dist/version-compare.js +121 -0
- package/dist/version-compare.js.map +1 -0
- package/dist/windows-pty-registry.d.ts +27 -0
- package/dist/windows-pty-registry.d.ts.map +1 -0
- package/dist/windows-pty-registry.js +109 -0
- package/dist/windows-pty-registry.js.map +1 -0
- package/package.json +110 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Composio, Inc.
|
|
4
|
+
Copyright (c) 2026 slievr (Athene fork)
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# @made-by-moonlight/athene-core
|
|
2
|
+
|
|
3
|
+
Core services, types, and configuration for the Athene system.
|
|
4
|
+
|
|
5
|
+
## What's Here
|
|
6
|
+
|
|
7
|
+
- **`src/types.ts`** — All TypeScript interfaces (Runtime, Agent, Workspace, Tracker, SCM, Notifier, Terminal, Session, events)
|
|
8
|
+
- **`src/services/`** — Core services (SessionManager, LifecycleManager, PluginRegistry)
|
|
9
|
+
- **`src/config.ts`** — Configuration loading + Zod schemas
|
|
10
|
+
- **`src/utils/`** — Shared utilities (shell escaping, metadata parsing, etc.)
|
|
11
|
+
|
|
12
|
+
## Key Files
|
|
13
|
+
|
|
14
|
+
### `src/types.ts` — The Source of Truth
|
|
15
|
+
|
|
16
|
+
Every interface the system uses is defined here. If you're working on any part of the orchestrator, start by reading this file.
|
|
17
|
+
|
|
18
|
+
**Main interfaces:**
|
|
19
|
+
|
|
20
|
+
- `Runtime` — where sessions execute (tmux on Unix, `process` / ConPTY via node-pty on Windows, docker, k8s)
|
|
21
|
+
- `Agent` — AI coding tool adapter (claude-code, codex, aider)
|
|
22
|
+
- `Workspace` — code isolation (worktree, clone)
|
|
23
|
+
- `Tracker` — issue tracking (GitHub Issues, Linear)
|
|
24
|
+
- `SCM` — PR/CI/reviews (GitHub, GitLab)
|
|
25
|
+
- `Notifier` — push notifications (desktop, Slack, webhook)
|
|
26
|
+
- `Terminal` — human interaction UI (iTerm2, web)
|
|
27
|
+
- `Session` — running agent instance (state, metadata, handles)
|
|
28
|
+
- `OrchestratorEvent` — events emitted by lifecycle manager
|
|
29
|
+
- `PluginModule` — what every plugin exports
|
|
30
|
+
|
|
31
|
+
### `src/services/session-manager.ts` — Session CRUD
|
|
32
|
+
|
|
33
|
+
Handles session lifecycle:
|
|
34
|
+
|
|
35
|
+
- `spawn(config)` — create new session (workspace + runtime + agent)
|
|
36
|
+
- `list(projectId?)` — list all sessions
|
|
37
|
+
- `get(sessionId)` — get session details
|
|
38
|
+
- `kill(sessionId)` — terminate session
|
|
39
|
+
- `cleanup(projectId?)` — kill completed/merged sessions
|
|
40
|
+
- `send(sessionId, message)` — send message to agent
|
|
41
|
+
|
|
42
|
+
**Data flow in `spawn()`:**
|
|
43
|
+
|
|
44
|
+
1. Load project config
|
|
45
|
+
2. **Validate issue exists** via `Tracker.getIssue()` (if issueId provided, fails-fast if not found)
|
|
46
|
+
3. Reserve session ID
|
|
47
|
+
4. Determine branch name
|
|
48
|
+
5. Create workspace via `Workspace.create()`
|
|
49
|
+
6. Generate prompt via `Tracker.generatePrompt()`
|
|
50
|
+
7. Build layered worker prompt via `buildPrompt()` into `systemPrompt` + `taskPrompt`
|
|
51
|
+
8. Persist `systemPromptFile` for the session and, for OpenCode workers, write `OPENCODE_CONFIG`
|
|
52
|
+
9. Build launch command via `Agent.getLaunchCommand()`
|
|
53
|
+
10. Create runtime session via `Runtime.create()`
|
|
54
|
+
11. Run `Agent.postLaunchSetup()` (optional)
|
|
55
|
+
12. Write metadata file
|
|
56
|
+
13. Return Session object
|
|
57
|
+
|
|
58
|
+
**Note:** If issue validation fails (not found, auth error), spawn fails before creating any resources (no workspace, no runtime, no session ID). This prevents spawning sessions with broken issue references.
|
|
59
|
+
Worker sessions keep persistent instructions in the prompt file. OpenCode workers consume that file through `OPENCODE_CONFIG`, while OpenCode orchestrators continue to project their system prompt into workspace `AGENTS.md`.
|
|
60
|
+
|
|
61
|
+
### `src/services/lifecycle-manager.ts` — State Machine + Reactions
|
|
62
|
+
|
|
63
|
+
Polls sessions, detects state changes, triggers reactions:
|
|
64
|
+
|
|
65
|
+
**State machine:**
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
spawning → working → pr_open → ci_failed/review_pending/approved → mergeable → merged
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Reactions:**
|
|
72
|
+
|
|
73
|
+
- `ci-failed` → send fix prompt to agent
|
|
74
|
+
- `changes-requested` → send review comments to agent
|
|
75
|
+
- `approved-and-green` → notify human (or auto-merge)
|
|
76
|
+
- `agent-stuck` → notify human
|
|
77
|
+
|
|
78
|
+
**Polling loop:**
|
|
79
|
+
|
|
80
|
+
1. For each session: check agent activity state (`Agent.getActivityState()`)
|
|
81
|
+
2. If PR exists: check CI status (`SCM.getCISummary()`), review state (`SCM.getReviewDecision()`)
|
|
82
|
+
3. Update session status based on state
|
|
83
|
+
4. Trigger reactions if state changed
|
|
84
|
+
5. Emit events
|
|
85
|
+
|
|
86
|
+
### `src/services/plugin-registry.ts` — Plugin Discovery + Loading
|
|
87
|
+
|
|
88
|
+
Loads plugins and provides access to them:
|
|
89
|
+
|
|
90
|
+
- `register(plugin, config?)` — register a plugin instance
|
|
91
|
+
- `get<T>(slot, name)` — get plugin by slot + name
|
|
92
|
+
- `list(slot)` — list all plugins for a slot
|
|
93
|
+
- `loadBuiltins(config?)` — load built-in plugins (runtime-tmux, agent-claude-code, etc.)
|
|
94
|
+
- `loadFromConfig(config)` — load built-ins today; external plugin descriptors are the marketplace extension point
|
|
95
|
+
|
|
96
|
+
**Built-in plugins** (loaded by default):
|
|
97
|
+
|
|
98
|
+
- runtime-tmux, runtime-process
|
|
99
|
+
- agent-claude-code, agent-codex, agent-aider, agent-cursor, agent-kimicode, agent-opencode
|
|
100
|
+
- workspace-worktree, workspace-clone
|
|
101
|
+
- tracker-github, tracker-linear, tracker-gitlab
|
|
102
|
+
- scm-github, scm-gitlab
|
|
103
|
+
- notifier-desktop, notifier-discord, notifier-slack, notifier-composio, notifier-openclaw, notifier-webhook
|
|
104
|
+
- terminal-iterm2, terminal-web
|
|
105
|
+
|
|
106
|
+
### `src/config.ts` — Configuration Loading
|
|
107
|
+
|
|
108
|
+
Loads and validates `agent-orchestrator.yaml`:
|
|
109
|
+
|
|
110
|
+
**Main config sections:**
|
|
111
|
+
|
|
112
|
+
- Runtime data paths are auto-derived from the config location under `~/.agent-orchestrator/{hash}-{projectId}/`
|
|
113
|
+
- `port` — web dashboard port (default 3000, set different values for multiple projects)
|
|
114
|
+
- `terminalPort` — terminal WebSocket port (auto-detected if not set)
|
|
115
|
+
- `directTerminalPort` — direct terminal WebSocket port (auto-detected if not set)
|
|
116
|
+
- `defaults` — default plugins (runtime, agent, workspace, notifiers)
|
|
117
|
+
- `plugins` — installer-managed external plugin descriptors (registry, npm, or local)
|
|
118
|
+
- `projects` — per-project config (repo, path, branch, symlinks, reactions, agentRules)
|
|
119
|
+
- `notifiers` — notification channel config (Slack webhooks, etc.)
|
|
120
|
+
- `notificationRouting` — which notifiers get which priority events
|
|
121
|
+
- `reactions` — auto-response config (ci-failed, changes-requested, approved-and-green, etc.)
|
|
122
|
+
|
|
123
|
+
**Zod schemas** validate all config at load time.
|
|
124
|
+
|
|
125
|
+
## Common Tasks
|
|
126
|
+
|
|
127
|
+
### Adding a Field to Session
|
|
128
|
+
|
|
129
|
+
1. Edit `src/types.ts` → `Session` interface
|
|
130
|
+
2. Edit `src/services/session-manager.ts` → initialize field in `spawn()`
|
|
131
|
+
3. Rebuild: `pnpm --filter @made-by-moonlight/athene-core build`
|
|
132
|
+
|
|
133
|
+
### Adding an Event Type
|
|
134
|
+
|
|
135
|
+
1. Edit `src/types.ts` → `EventType` union
|
|
136
|
+
2. Emit the event: `eventEmitter.emit()` in relevant service
|
|
137
|
+
3. Add reaction handler (optional): `src/services/lifecycle-manager.ts`
|
|
138
|
+
|
|
139
|
+
### Adding a Reaction
|
|
140
|
+
|
|
141
|
+
1. Edit `src/services/lifecycle-manager.ts` → add handler function
|
|
142
|
+
2. Wire it up in the polling loop
|
|
143
|
+
3. Add config schema in `src/config.ts` if new reaction type
|
|
144
|
+
|
|
145
|
+
### Feedback Tools (v1)
|
|
146
|
+
|
|
147
|
+
`@made-by-moonlight/athene-core` exports two structured feedback tool contracts:
|
|
148
|
+
|
|
149
|
+
- `bug_report`
|
|
150
|
+
- `improvement_suggestion`
|
|
151
|
+
|
|
152
|
+
Both share the same required input fields:
|
|
153
|
+
|
|
154
|
+
- `title`
|
|
155
|
+
- `body`
|
|
156
|
+
- `evidence` (array of strings)
|
|
157
|
+
- `session`
|
|
158
|
+
- `source`
|
|
159
|
+
- `confidence` (0..1)
|
|
160
|
+
|
|
161
|
+
Example:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { FEEDBACK_TOOL_NAMES, FeedbackReportStore, getFeedbackReportsDir } from "@made-by-moonlight/athene-core";
|
|
165
|
+
|
|
166
|
+
const reportsDir = getFeedbackReportsDir(configPath, projectPath);
|
|
167
|
+
const store = new FeedbackReportStore(reportsDir);
|
|
168
|
+
|
|
169
|
+
const saved = store.persist(FEEDBACK_TOOL_NAMES.BUG_REPORT, {
|
|
170
|
+
title: "SSO login loop",
|
|
171
|
+
body: "Google SSO redirects back to /login repeatedly.",
|
|
172
|
+
evidence: ["trace_id=abc123", "screenshot: login-loop.png"],
|
|
173
|
+
session: "ao-22",
|
|
174
|
+
source: "agent",
|
|
175
|
+
confidence: 0.84,
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Storage format:
|
|
180
|
+
|
|
181
|
+
- Reports are persisted under `~/.agent-orchestrator/{hash}-{projectId}/feedback-reports`
|
|
182
|
+
- Each report is a typed key=value file (`report_<timestamp>_<id>.kv`) for easy inspection
|
|
183
|
+
- A deterministic dedupe key (`sha256`, 16 hex chars) is generated from normalized tool+content
|
|
184
|
+
|
|
185
|
+
Migration notes:
|
|
186
|
+
|
|
187
|
+
- No migration needed for existing AO installs
|
|
188
|
+
- The `feedback-reports` directory is created lazily on first persisted report
|
|
189
|
+
|
|
190
|
+
## Testing
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# Run all core tests
|
|
194
|
+
pnpm --filter @made-by-moonlight/athene-core test
|
|
195
|
+
|
|
196
|
+
# Run in watch mode
|
|
197
|
+
pnpm --filter @made-by-moonlight/athene-core test -- --watch
|
|
198
|
+
|
|
199
|
+
# Run specific test
|
|
200
|
+
pnpm --filter @made-by-moonlight/athene-core test -- session-manager.test.ts
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Tests are in `src/__tests__/`:
|
|
204
|
+
|
|
205
|
+
- `session-manager.test.ts` — session CRUD, spawn, cleanup
|
|
206
|
+
- `lifecycle-manager.test.ts` — state machine, reactions
|
|
207
|
+
- `plugin-registry.test.ts` — plugin loading, resolution
|
|
208
|
+
- `tmux.test.ts` — tmux utility functions (not a plugin test)
|
|
209
|
+
- `prompt-builder.test.ts` — prompt generation utilities
|
|
210
|
+
|
|
211
|
+
## Building
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Build core
|
|
215
|
+
pnpm --filter @made-by-moonlight/athene-core build
|
|
216
|
+
|
|
217
|
+
# Typecheck
|
|
218
|
+
pnpm --filter @made-by-moonlight/athene-core typecheck
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
This package is a dependency of all other packages. Build it first if working on the codebase.
|
|
222
|
+
|
|
223
|
+
## Architecture Notes
|
|
224
|
+
|
|
225
|
+
**Why flat metadata files?**
|
|
226
|
+
|
|
227
|
+
- Debuggability: `cat ~/.agent-orchestrator/<hash>-my-app/sessions/app-3` shows full state
|
|
228
|
+
- No database dependency (survives crashes, easy to inspect)
|
|
229
|
+
- Backwards-compatible with bash script orchestrator
|
|
230
|
+
|
|
231
|
+
**Why polling instead of webhooks?**
|
|
232
|
+
|
|
233
|
+
- Simpler (no webhook setup, no ngrok for local dev)
|
|
234
|
+
- Works offline (CI/review state is fetched, not pushed)
|
|
235
|
+
- Survives orchestrator restarts (no missed events)
|
|
236
|
+
|
|
237
|
+
**Why plugin slots?**
|
|
238
|
+
|
|
239
|
+
- Swappability: use tmux on Linux/macOS, `process` (ConPTY) on Windows, docker in CI, k8s in prod — same agent/workspace stack across all of them
|
|
240
|
+
- Testability: mock plugins for tests
|
|
241
|
+
- Extensibility: users can add custom plugins (e.g., company-specific notifier)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity event logging — write API.
|
|
3
|
+
*
|
|
4
|
+
* recordActivityEvent() is synchronous and best-effort: it never throws.
|
|
5
|
+
* If the DB is unavailable or a write fails, the event is dropped and
|
|
6
|
+
* droppedEventCount is incremented.
|
|
7
|
+
*
|
|
8
|
+
* droppedEventCount is process-local. Events dropped in other processes
|
|
9
|
+
* (web server, lifecycle manager) are not reflected here.
|
|
10
|
+
*/
|
|
11
|
+
export type ActivityEventSource = "lifecycle" | "session-manager" | "api" | "ui" | "scm" | "runtime" | "agent" | "tracker" | "workspace" | "notifier" | "reaction" | "report-watcher" | "cli" | "config" | "plugin-registry" | "migration" | "recovery";
|
|
12
|
+
export type ActivityEventKind = "session.spawn_started" | "session.spawned" | "session.spawn_failed" | "session.spawn_step_failed" | "session.killed" | "session.kill_started" | "session.send_failed" | "session.restore_failed" | "session.restore_fallback" | "session.rollback_started" | "session.rollback_step_failed" | "session.workspace_hooks_failed" | "session.cleanup_error" | "session.orchestrator_conflict" | "runtime.lost_detected" | "runtime.lost_persist_failed" | "runtime.destroy_failed" | "workspace.destroy_failed" | "agent.opencode_purge_failed" | "tracker.issue_fetch_failed" | "tracker.generate_prompt_failed" | "metadata.corrupt_detected" | "activity.transition" | "lifecycle.transition" | "ci.failing" | "review.pending" | "scm.batch_enrich_failed" | "scm.detect_pr_succeeded" | "scm.detect_pr_failed" | "scm.review_fetch_failed" | "scm.poll_pr_failed" | "runtime.probe_failed" | "agent.process_probe_failed" | "agent.activity_probe_failed" | "scm.gh_unavailable" | "scm.batch_enrich_pr_failed" | "scm.ci_summary_failclosed" | "workspace.post_create_failed" | "workspace.branch_collision" | "workspace.destroy_fell_back" | "workspace.corrupt_clone_skipped" | "tracker.dep_missing" | "tracker.api_timeout" | "notifier.auth_failed" | "notifier.unreachable" | "notifier.rate_limited" | "notifier.dep_missing" | "reaction.escalated" | "reaction.send_to_agent_failed" | "reaction.action_succeeded" | "session.auto_cleanup_deferred" | "session.auto_cleanup_completed" | "session.auto_cleanup_failed" | "lifecycle.poll_failed" | "detecting.escalated" | "notification.delivery_failed" | "notification.target_missing" | "report_watcher.triggered" | "config.project_resolve_failed" | "config.project_malformed" | "config.project_invalid" | "config.migrated" | "plugin-registry.load_failed" | "plugin-registry.validation_failed" | "plugin-registry.specifier_failed" | "migration.blocked" | "migration.project_failed" | "migration.rename_failed" | "migration.completed" | "migration.rollback_skipped" | "api.webhook_unverified" | "api.webhook_rejected" | "api.webhook_received" | "api.webhook_failed" | "ui.terminal_connected" | "ui.terminal_disconnected" | "ui.terminal_heartbeat_lost" | "ui.terminal_pty_lost" | "ui.terminal_protocol_error" | "ui.session_broadcast_failed" | "recovery.session_failed" | "recovery.action_failed" | "api.agent_report.session_not_found" | "api.agent_report.transition_rejected" | "api.agent_report.apply_failed";
|
|
13
|
+
export type ActivityEventLevel = "debug" | "info" | "warn" | "error";
|
|
14
|
+
export interface ActivityEventInput {
|
|
15
|
+
projectId?: string;
|
|
16
|
+
sessionId?: string;
|
|
17
|
+
source: ActivityEventSource | string;
|
|
18
|
+
kind: ActivityEventKind | string;
|
|
19
|
+
level?: ActivityEventLevel;
|
|
20
|
+
summary: string;
|
|
21
|
+
data?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
export interface ActivityEvent {
|
|
24
|
+
id: number;
|
|
25
|
+
tsEpoch: number;
|
|
26
|
+
ts: string;
|
|
27
|
+
projectId: string | null;
|
|
28
|
+
sessionId: string | null;
|
|
29
|
+
source: string;
|
|
30
|
+
kind: string;
|
|
31
|
+
level: string;
|
|
32
|
+
summary: string;
|
|
33
|
+
data: string | null;
|
|
34
|
+
rank?: number;
|
|
35
|
+
}
|
|
36
|
+
/** Number of events dropped due to DB errors in this process. */
|
|
37
|
+
export declare function droppedEventCount(): number;
|
|
38
|
+
/**
|
|
39
|
+
* Record an activity event. Synchronous, best-effort — never throws.
|
|
40
|
+
*/
|
|
41
|
+
export declare function recordActivityEvent(event: ActivityEventInput): void;
|
|
42
|
+
//# sourceMappingURL=activity-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-events.d.ts","sourceRoot":"","sources":["../src/activity-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,MAAM,mBAAmB,GAC3B,WAAW,GACX,iBAAiB,GACjB,KAAK,GACL,IAAI,GACJ,KAAK,GACL,SAAS,GACT,OAAO,GACP,SAAS,GACT,WAAW,GACX,UAAU,GACV,UAAU,GACV,gBAAgB,GAChB,KAAK,GACL,QAAQ,GACR,iBAAiB,GACjB,WAAW,GACX,UAAU,CAAC;AAEf,MAAM,MAAM,iBAAiB,GACzB,uBAAuB,GACvB,iBAAiB,GACjB,sBAAsB,GACtB,2BAA2B,GAC3B,gBAAgB,GAChB,sBAAsB,GACtB,qBAAqB,GACrB,wBAAwB,GACxB,0BAA0B,GAC1B,0BAA0B,GAC1B,8BAA8B,GAC9B,gCAAgC,GAChC,uBAAuB,GACvB,+BAA+B,GAC/B,uBAAuB,GACvB,6BAA6B,GAC7B,wBAAwB,GACxB,0BAA0B,GAC1B,6BAA6B,GAC7B,4BAA4B,GAC5B,gCAAgC,GAChC,2BAA2B,GAC3B,qBAAqB,GACrB,sBAAsB,GACtB,YAAY,GACZ,gBAAgB,GAEhB,yBAAyB,GACzB,yBAAyB,GACzB,sBAAsB,GACtB,yBAAyB,GACzB,oBAAoB,GACpB,sBAAsB,GACtB,4BAA4B,GAC5B,6BAA6B,GAE7B,oBAAoB,GACpB,4BAA4B,GAC5B,2BAA2B,GAC3B,8BAA8B,GAC9B,4BAA4B,GAC5B,6BAA6B,GAC7B,iCAAiC,GACjC,qBAAqB,GACrB,qBAAqB,GACrB,sBAAsB,GACtB,sBAAsB,GACtB,uBAAuB,GACvB,sBAAsB,GAEtB,oBAAoB,GACpB,+BAA+B,GAC/B,2BAA2B,GAE3B,+BAA+B,GAC/B,gCAAgC,GAChC,6BAA6B,GAC7B,uBAAuB,GACvB,qBAAqB,GAErB,8BAA8B,GAC9B,6BAA6B,GAE7B,0BAA0B,GAE1B,+BAA+B,GAC/B,0BAA0B,GAC1B,wBAAwB,GACxB,iBAAiB,GACjB,6BAA6B,GAC7B,mCAAmC,GACnC,kCAAkC,GAClC,mBAAmB,GACnB,0BAA0B,GAC1B,yBAAyB,GACzB,qBAAqB,GACrB,4BAA4B,GAE5B,wBAAwB,GACxB,sBAAsB,GACtB,sBAAsB,GACtB,oBAAoB,GAEpB,uBAAuB,GACvB,0BAA0B,GAC1B,4BAA4B,GAC5B,sBAAsB,GACtB,4BAA4B,GAC5B,6BAA6B,GAE7B,yBAAyB,GACzB,wBAAwB,GACxB,oCAAoC,GACpC,sCAAsC,GACtC,+BAA+B,CAAC;AAEpC,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAErE,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAAC;IACrC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAAC;IACjC,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAQD,iEAAiE;AACjE,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAyJD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAoCnE"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { getDb } from './events-db.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Activity event logging — write API.
|
|
5
|
+
*
|
|
6
|
+
* recordActivityEvent() is synchronous and best-effort: it never throws.
|
|
7
|
+
* If the DB is unavailable or a write fails, the event is dropped and
|
|
8
|
+
* droppedEventCount is incremented.
|
|
9
|
+
*
|
|
10
|
+
* droppedEventCount is process-local. Events dropped in other processes
|
|
11
|
+
* (web server, lifecycle manager) are not reflected here.
|
|
12
|
+
*/
|
|
13
|
+
let _droppedEventCount = 0;
|
|
14
|
+
let _lastPruneMs = 0;
|
|
15
|
+
const PRUNE_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
|
|
16
|
+
const RETENTION_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
17
|
+
const PRUNE_BATCH_SIZE = 1000;
|
|
18
|
+
/** Number of events dropped due to DB errors in this process. */
|
|
19
|
+
function droppedEventCount() {
|
|
20
|
+
return _droppedEventCount;
|
|
21
|
+
}
|
|
22
|
+
function pruneOldEvents(db, cutoff) {
|
|
23
|
+
db?.prepare(`DELETE FROM activity_events
|
|
24
|
+
WHERE rowid IN (
|
|
25
|
+
SELECT rowid FROM activity_events WHERE ts_epoch < ? LIMIT ?
|
|
26
|
+
)`).run(cutoff, PRUNE_BATCH_SIZE);
|
|
27
|
+
}
|
|
28
|
+
// Patterns that indicate sensitive field names
|
|
29
|
+
const SENSITIVE_KEY_RE = /token|password|secret|authorization|cookie|api[-_]?key/i;
|
|
30
|
+
// URL credentials: https://token@host or http://user:pass@host.
|
|
31
|
+
// Linear scan — find :// then scan forward for the next @ before a path
|
|
32
|
+
// separator or whitespace. O(n) worst case, no regex backtracking, no length
|
|
33
|
+
// limits. Replaces the previous CREDENTIAL_URL_RE which either ReDoS'd
|
|
34
|
+
// (unbounded quantifier) or missed >200-char userinfo (bounded quantifier).
|
|
35
|
+
function redactCredentialUrls(input) {
|
|
36
|
+
let result = input;
|
|
37
|
+
let offset = 0;
|
|
38
|
+
while (offset < result.length) {
|
|
39
|
+
const proto = result.indexOf("://", offset);
|
|
40
|
+
if (proto === -1)
|
|
41
|
+
break;
|
|
42
|
+
// Only match http:// or https:// (case-insensitive, matching old /gi flag)
|
|
43
|
+
if (proto < 4) {
|
|
44
|
+
offset = proto + 3;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const schemeEnd = result.slice(Math.max(0, proto - 5), proto).toLowerCase();
|
|
48
|
+
if (!schemeEnd.endsWith("http") && !schemeEnd.endsWith("https")) {
|
|
49
|
+
offset = proto + 3;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
let cursor = proto + 3;
|
|
53
|
+
while (cursor < result.length) {
|
|
54
|
+
const ch = result.charCodeAt(cursor);
|
|
55
|
+
// Space/control chars or '/' mean no '@' is coming in userinfo
|
|
56
|
+
if (ch <= 0x20 || ch === 0x2f)
|
|
57
|
+
break;
|
|
58
|
+
if (ch === 0x40) {
|
|
59
|
+
// '@' found — redact everything between :// and @
|
|
60
|
+
// Lowercase the scheme to match the old /gi regex behavior
|
|
61
|
+
const before = result.slice(0, proto + 3).toLowerCase();
|
|
62
|
+
const suffix = result.slice(cursor);
|
|
63
|
+
result = before + "[redacted]" + suffix;
|
|
64
|
+
offset = proto + 3 + "[redacted]".length + 1;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
cursor++;
|
|
68
|
+
}
|
|
69
|
+
// No '@' found — not a credential URL, move past this ://
|
|
70
|
+
if (cursor >= result.length ||
|
|
71
|
+
result.charCodeAt(cursor) <= 0x20 ||
|
|
72
|
+
result.charCodeAt(cursor) === 0x2f) {
|
|
73
|
+
offset = proto + 3;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
// Per-string-value cap. The whole-data 16 KB cap still applies on top of this;
|
|
79
|
+
// truncating individual strings limits blast radius if a pattern below misses a
|
|
80
|
+
// new token format and a long error message gets pasted in.
|
|
81
|
+
const STRING_VALUE_MAX_CHARS = 500;
|
|
82
|
+
// Token-shape patterns matched against ANY string value, not just keys.
|
|
83
|
+
// Order: more-specific first. Replacement strings preserve the prefix where
|
|
84
|
+
// the prefix itself is informative (e.g. "Bearer [redacted]" so RCA can still
|
|
85
|
+
// see this was a bearer-auth failure).
|
|
86
|
+
//
|
|
87
|
+
// SENSITIVE_KEY_RE above redacts entire values under sensitive *key* names;
|
|
88
|
+
// these patterns redact token-shaped *substrings* anywhere — including under
|
|
89
|
+
// keys like `message` and `errorMessage`, which are the leak vector flagged
|
|
90
|
+
// in PR #1620 review (data column is FTS5-indexed in events-db.ts).
|
|
91
|
+
const TOKEN_PATTERNS = [
|
|
92
|
+
// Bearer auth headers (also catches JWTs prefixed with Bearer)
|
|
93
|
+
[/\bBearer\s+[A-Za-z0-9._~+/=-]{12,}\b/gi, "Bearer [redacted]"],
|
|
94
|
+
// GitHub Personal Access Tokens — classic (ghp_/gho_/ghu_/ghs_/ghr_)
|
|
95
|
+
[/\bgh[pousr]_[A-Za-z0-9_]{20,}\b/g, "[redacted]"],
|
|
96
|
+
// GitHub fine-grained PATs
|
|
97
|
+
[/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g, "[redacted]"],
|
|
98
|
+
// OpenAI / Anthropic sk- keys (incl. sk-proj-, sk-svcacct-, sk-ant-)
|
|
99
|
+
[/\bsk-(?:ant-)?(?:proj-|svcacct-)?[A-Za-z0-9_-]{16,}\b/g, "[redacted]"],
|
|
100
|
+
// Slack tokens (xoxb-, xoxp-, xoxa-, xoxr-, xoxs-)
|
|
101
|
+
[/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, "[redacted]"],
|
|
102
|
+
// AWS access key IDs (16 trailing chars exactly per AWS spec)
|
|
103
|
+
[/\bAKIA[0-9A-Z]{16}\b/g, "[redacted]"],
|
|
104
|
+
// JWTs — three base64url segments, eyJ prefix on header
|
|
105
|
+
[/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g, "[redacted]"],
|
|
106
|
+
// ENV-style assignments: MY_API_TOKEN=value, GITHUB_SECRET=..., etc.
|
|
107
|
+
// Scoped to ALL_CAPS keys containing a sensitive word so prose like
|
|
108
|
+
// "the message=hello" doesn't redact.
|
|
109
|
+
[
|
|
110
|
+
/\b([A-Z][A-Z0-9_]*(?:TOKEN|PASSWORD|SECRET|AUTHORIZATION|COOKIE|API_KEY|APIKEY)[A-Z0-9_]*)=([^\s"'`]{6,})/g,
|
|
111
|
+
"$1=[redacted]",
|
|
112
|
+
],
|
|
113
|
+
];
|
|
114
|
+
function sanitizeString(value) {
|
|
115
|
+
let cleaned = redactCredentialUrls(value);
|
|
116
|
+
for (const [pattern, replacement] of TOKEN_PATTERNS) {
|
|
117
|
+
cleaned = cleaned.replace(pattern, replacement);
|
|
118
|
+
}
|
|
119
|
+
if (cleaned.length > STRING_VALUE_MAX_CHARS) {
|
|
120
|
+
cleaned = `${cleaned.slice(0, STRING_VALUE_MAX_CHARS - 3)}...`;
|
|
121
|
+
}
|
|
122
|
+
return cleaned;
|
|
123
|
+
}
|
|
124
|
+
function sanitizeValue(value, seen) {
|
|
125
|
+
if (typeof value === "bigint")
|
|
126
|
+
return value.toString();
|
|
127
|
+
if (typeof value === "string")
|
|
128
|
+
return sanitizeString(value);
|
|
129
|
+
if (value === null || typeof value !== "object")
|
|
130
|
+
return value;
|
|
131
|
+
if (seen.has(value))
|
|
132
|
+
return "[circular]";
|
|
133
|
+
seen.add(value);
|
|
134
|
+
if (Array.isArray(value)) {
|
|
135
|
+
return value.map((item) => sanitizeValue(item, seen));
|
|
136
|
+
}
|
|
137
|
+
const cleaned = {};
|
|
138
|
+
for (const [k, v] of Object.entries(value)) {
|
|
139
|
+
cleaned[k] = SENSITIVE_KEY_RE.test(k) ? "[redacted]" : sanitizeValue(v, seen);
|
|
140
|
+
}
|
|
141
|
+
return cleaned;
|
|
142
|
+
}
|
|
143
|
+
function sanitizeData(data) {
|
|
144
|
+
const cleaned = sanitizeValue(data, new WeakSet());
|
|
145
|
+
let json;
|
|
146
|
+
try {
|
|
147
|
+
json = JSON.stringify(cleaned);
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
// Reject if over 16 KB after sanitization (slicing would produce malformed JSON)
|
|
153
|
+
if (json.length > 16 * 1024) {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
return json;
|
|
157
|
+
}
|
|
158
|
+
function sanitizeSummary(summary) {
|
|
159
|
+
if (summary.length <= 500)
|
|
160
|
+
return summary;
|
|
161
|
+
return `${summary.slice(0, 497)}...`;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Record an activity event. Synchronous, best-effort — never throws.
|
|
165
|
+
*/
|
|
166
|
+
function recordActivityEvent(event) {
|
|
167
|
+
try {
|
|
168
|
+
const db = getDb();
|
|
169
|
+
if (!db) {
|
|
170
|
+
_droppedEventCount++;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const now = Date.now();
|
|
174
|
+
const ts = new Date(now).toISOString();
|
|
175
|
+
const summary = sanitizeSummary(event.summary);
|
|
176
|
+
const data = event.data ? sanitizeData(event.data) : undefined;
|
|
177
|
+
db.prepare(`INSERT INTO activity_events
|
|
178
|
+
(ts_epoch, ts, project_id, session_id, source, type, log_level, summary, data)
|
|
179
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(now, ts, event.projectId ?? null, event.sessionId ?? null, event.source, event.kind, event.level ?? "info", summary, data ?? null);
|
|
180
|
+
// Periodically purge old events so long-lived processes don't grow the DB indefinitely
|
|
181
|
+
if (now - _lastPruneMs >= PRUNE_INTERVAL_MS) {
|
|
182
|
+
_lastPruneMs = now;
|
|
183
|
+
pruneOldEvents(db, now - RETENTION_MS);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
_droppedEventCount++;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export { droppedEventCount, recordActivityEvent };
|
|
192
|
+
//# sourceMappingURL=activity-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-events.js","sources":["../src/activity-events.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ActivityState, ActivityLogEntry, ActivityDetection } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* @deprecated Actionable states no longer decay on wallclock. Retained until
|
|
4
|
+
* the activity-reducer cleanup removes the old activity-log module.
|
|
5
|
+
*/
|
|
6
|
+
export declare const ACTIVITY_INPUT_STALENESS_MS: number;
|
|
7
|
+
/**
|
|
8
|
+
* Get the path to the activity JSONL log for a session.
|
|
9
|
+
* Location: `{workspacePath}/.ao/activity.jsonl`
|
|
10
|
+
*/
|
|
11
|
+
export declare function getActivityLogPath(workspacePath: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Append an activity observation to the session's JSONL log.
|
|
14
|
+
* Creates the `.ao/` directory if it doesn't exist.
|
|
15
|
+
*/
|
|
16
|
+
export declare function appendActivityEntry(workspacePath: string, state: ActivityState, source: "terminal" | "native" | "hook", trigger?: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Read the last activity entry from the session's JSONL log.
|
|
19
|
+
* Returns the parsed entry with the file's modification time, or null if
|
|
20
|
+
* the file doesn't exist or is empty.
|
|
21
|
+
*/
|
|
22
|
+
export declare function readLastActivityEntry(workspacePath: string): Promise<{
|
|
23
|
+
entry: ActivityLogEntry;
|
|
24
|
+
modifiedAt: Date;
|
|
25
|
+
} | null>;
|
|
26
|
+
/**
|
|
27
|
+
* Check the AO activity JSONL for actionable states only.
|
|
28
|
+
*
|
|
29
|
+
* Only returns `waiting_input`/`blocked`.
|
|
30
|
+
* Non-critical states (`active`, `ready`, `idle`) always return `null` so
|
|
31
|
+
* callers fall through to their native signals (git commits, chat history,
|
|
32
|
+
* API queries, native JSONL). This prevents the lifecycle manager's
|
|
33
|
+
* `recordActivity` writes (which refresh `mtime` every poll cycle) from
|
|
34
|
+
* shadowing those richer detection methods and breaking stuck-detection.
|
|
35
|
+
*/
|
|
36
|
+
export declare function checkActivityLogState(activityResult: {
|
|
37
|
+
entry: ActivityLogEntry;
|
|
38
|
+
modifiedAt: Date;
|
|
39
|
+
} | null): ActivityDetection | null;
|
|
40
|
+
/**
|
|
41
|
+
* Derive an activity state from the JSONL entry with age-based decay.
|
|
42
|
+
*
|
|
43
|
+
* Unlike `checkActivityLogState` (which only returns actionable states),
|
|
44
|
+
* this returns any state — but reclassifies `active`/`ready` entries as
|
|
45
|
+
* `ready`/`idle` if they've aged past the active window / threshold.
|
|
46
|
+
* Use this as a last-resort fallback when native signals are unavailable.
|
|
47
|
+
*/
|
|
48
|
+
export declare function getActivityFallbackState(activityResult: {
|
|
49
|
+
entry: ActivityLogEntry;
|
|
50
|
+
modifiedAt: Date;
|
|
51
|
+
} | null, activeWindowMs: number, thresholdMs: number): ActivityDetection | null;
|
|
52
|
+
/**
|
|
53
|
+
* Build the arguments for `appendActivityEntry` from terminal output.
|
|
54
|
+
*
|
|
55
|
+
* Classifies terminal output via the provided `detectActivity` function and
|
|
56
|
+
* returns the state + trigger. Plugins call `appendActivityEntry` themselves
|
|
57
|
+
* (keeping it mockable in tests).
|
|
58
|
+
*/
|
|
59
|
+
export declare function classifyTerminalActivity(terminalOutput: string, detectActivity: (output: string) => ActivityState): {
|
|
60
|
+
state: ActivityState;
|
|
61
|
+
trigger: string | undefined;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Shared `recordActivity` implementation for all agents.
|
|
65
|
+
*
|
|
66
|
+
* Classifies terminal output, deduplicates writes (skips when the state
|
|
67
|
+
* hasn't changed and the last entry is recent), and appends to the JSONL.
|
|
68
|
+
* Actionable states (waiting_input/blocked) always write immediately.
|
|
69
|
+
*/
|
|
70
|
+
export declare function recordTerminalActivity(workspacePath: string, terminalOutput: string, detectActivity: (output: string) => ActivityState): Promise<void>;
|
|
71
|
+
//# sourceMappingURL=activity-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-log.d.ts","sourceRoot":"","sources":["../src/activity-log.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAErF;;;GAGG;AACH,eAAO,MAAM,2BAA2B,QAAgB,CAAC;AAEzD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,UAAU,GAAG,QAAQ,GAAG,MAAM,EACtC,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,UAAU,EAAE,IAAI,CAAA;CAAE,GAAG,IAAI,CAAC,CA+D/D;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,cAAc,EAAE;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,UAAU,EAAE,IAAI,CAAA;CAAE,GAAG,IAAI,GACnE,iBAAiB,GAAG,IAAI,CAa1B;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,cAAc,EAAE;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,UAAU,EAAE,IAAI,CAAA;CAAE,GAAG,IAAI,EACpE,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,GAClB,iBAAiB,GAAG,IAAI,CAyB1B;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,aAAa,GAChD;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAOvD;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,aAAa,GAChD,OAAO,CAAC,IAAI,CAAC,CAcf"}
|