codekin 0.4.1 → 0.5.0

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 (81) hide show
  1. package/README.md +12 -15
  2. package/bin/codekin.mjs +52 -32
  3. package/dist/assets/index-BwKZeT4V.css +1 -0
  4. package/dist/assets/index-CfBnNU24.js +186 -0
  5. package/dist/index.html +2 -2
  6. package/package.json +2 -7
  7. package/server/dist/approval-manager.d.ts +7 -2
  8. package/server/dist/approval-manager.js +44 -78
  9. package/server/dist/approval-manager.js.map +1 -1
  10. package/server/dist/claude-process.d.ts +23 -3
  11. package/server/dist/claude-process.js +120 -27
  12. package/server/dist/claude-process.js.map +1 -1
  13. package/server/dist/commit-event-handler.js.map +1 -1
  14. package/server/dist/config.d.ts +4 -0
  15. package/server/dist/config.js +17 -0
  16. package/server/dist/config.js.map +1 -1
  17. package/server/dist/diff-manager.d.ts +41 -0
  18. package/server/dist/diff-manager.js +303 -0
  19. package/server/dist/diff-manager.js.map +1 -0
  20. package/server/dist/error-page.d.ts +5 -0
  21. package/server/dist/error-page.js +144 -0
  22. package/server/dist/error-page.js.map +1 -0
  23. package/server/dist/native-permissions.d.ts +44 -0
  24. package/server/dist/native-permissions.js +163 -0
  25. package/server/dist/native-permissions.js.map +1 -0
  26. package/server/dist/orchestrator-children.d.ts +74 -0
  27. package/server/dist/orchestrator-children.js +281 -0
  28. package/server/dist/orchestrator-children.js.map +1 -0
  29. package/server/dist/orchestrator-learning.d.ts +134 -0
  30. package/server/dist/orchestrator-learning.js +567 -0
  31. package/server/dist/orchestrator-learning.js.map +1 -0
  32. package/server/dist/orchestrator-manager.d.ts +25 -0
  33. package/server/dist/orchestrator-manager.js +353 -0
  34. package/server/dist/orchestrator-manager.js.map +1 -0
  35. package/server/dist/orchestrator-memory.d.ts +77 -0
  36. package/server/dist/orchestrator-memory.js +288 -0
  37. package/server/dist/orchestrator-memory.js.map +1 -0
  38. package/server/dist/orchestrator-monitor.d.ts +59 -0
  39. package/server/dist/orchestrator-monitor.js +238 -0
  40. package/server/dist/orchestrator-monitor.js.map +1 -0
  41. package/server/dist/orchestrator-reports.d.ts +45 -0
  42. package/server/dist/orchestrator-reports.js +124 -0
  43. package/server/dist/orchestrator-reports.js.map +1 -0
  44. package/server/dist/orchestrator-routes.d.ts +17 -0
  45. package/server/dist/orchestrator-routes.js +526 -0
  46. package/server/dist/orchestrator-routes.js.map +1 -0
  47. package/server/dist/session-archive.js +9 -2
  48. package/server/dist/session-archive.js.map +1 -1
  49. package/server/dist/session-manager.d.ts +99 -39
  50. package/server/dist/session-manager.js +565 -394
  51. package/server/dist/session-manager.js.map +1 -1
  52. package/server/dist/session-naming.d.ts +6 -10
  53. package/server/dist/session-naming.js +60 -62
  54. package/server/dist/session-naming.js.map +1 -1
  55. package/server/dist/session-persistence.d.ts +6 -1
  56. package/server/dist/session-persistence.js +6 -0
  57. package/server/dist/session-persistence.js.map +1 -1
  58. package/server/dist/session-restart-scheduler.d.ts +30 -0
  59. package/server/dist/session-restart-scheduler.js +41 -0
  60. package/server/dist/session-restart-scheduler.js.map +1 -0
  61. package/server/dist/session-routes.js +122 -61
  62. package/server/dist/session-routes.js.map +1 -1
  63. package/server/dist/stepflow-types.d.ts +1 -1
  64. package/server/dist/tsconfig.tsbuildinfo +1 -1
  65. package/server/dist/types.d.ts +34 -2
  66. package/server/dist/types.js +8 -1
  67. package/server/dist/types.js.map +1 -1
  68. package/server/dist/upload-routes.js +7 -1
  69. package/server/dist/upload-routes.js.map +1 -1
  70. package/server/dist/version-check.d.ts +17 -0
  71. package/server/dist/version-check.js +89 -0
  72. package/server/dist/version-check.js.map +1 -0
  73. package/server/dist/workflow-engine.d.ts +74 -1
  74. package/server/dist/workflow-engine.js +20 -1
  75. package/server/dist/workflow-engine.js.map +1 -1
  76. package/server/dist/ws-message-handler.js +115 -9
  77. package/server/dist/ws-message-handler.js.map +1 -1
  78. package/server/dist/ws-server.js +90 -15
  79. package/server/dist/ws-server.js.map +1 -1
  80. package/dist/assets/index-BAdQqYEY.js +0 -182
  81. package/dist/assets/index-CeZYNLWt.css +0 -1
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Orchestrator memory store — SQLite + FTS5 for structured, searchable memory.
3
+ *
4
+ * Provides durable memory across restarts with full-text search retrieval,
5
+ * trust record tracking, and automatic expiry/aging of stale items.
6
+ */
7
+ import Database from 'better-sqlite3';
8
+ import { existsSync, mkdirSync } from 'fs';
9
+ import { join } from 'path';
10
+ import { randomUUID } from 'crypto';
11
+ import { ORCHESTRATOR_DIR } from './orchestrator-manager.js';
12
+ // ---------------------------------------------------------------------------
13
+ // Store
14
+ // ---------------------------------------------------------------------------
15
+ export class OrchestratorMemory {
16
+ db;
17
+ constructor(dbPath) {
18
+ if (!existsSync(ORCHESTRATOR_DIR))
19
+ mkdirSync(ORCHESTRATOR_DIR, { recursive: true });
20
+ const resolvedPath = dbPath ?? join(ORCHESTRATOR_DIR, 'memory.sqlite');
21
+ this.db = new Database(resolvedPath, { fileMustExist: false });
22
+ this.db.pragma('journal_mode = WAL');
23
+ this.createTables();
24
+ }
25
+ createTables() {
26
+ this.db.exec(`
27
+ CREATE TABLE IF NOT EXISTS memory_items (
28
+ id TEXT PRIMARY KEY,
29
+ memory_type TEXT NOT NULL,
30
+ scope TEXT,
31
+ title TEXT,
32
+ content TEXT NOT NULL,
33
+ source_ref TEXT,
34
+ confidence REAL DEFAULT 0.8,
35
+ created_at TEXT NOT NULL,
36
+ updated_at TEXT NOT NULL,
37
+ expires_at TEXT,
38
+ is_pinned INTEGER DEFAULT 0,
39
+ tags TEXT DEFAULT '[]'
40
+ );
41
+
42
+ CREATE TABLE IF NOT EXISTS trust_records (
43
+ id TEXT PRIMARY KEY,
44
+ action TEXT NOT NULL,
45
+ category TEXT NOT NULL,
46
+ severity TEXT NOT NULL DEFAULT 'medium',
47
+ repo TEXT,
48
+ approval_count INTEGER DEFAULT 0,
49
+ rejection_count INTEGER DEFAULT 0,
50
+ last_approved_at TEXT,
51
+ last_rejected_at TEXT,
52
+ pinned_level TEXT
53
+ );
54
+
55
+ CREATE INDEX IF NOT EXISTS idx_memory_type ON memory_items(memory_type);
56
+ CREATE INDEX IF NOT EXISTS idx_memory_scope ON memory_items(scope);
57
+ CREATE INDEX IF NOT EXISTS idx_memory_expires ON memory_items(expires_at);
58
+ CREATE INDEX IF NOT EXISTS idx_trust_action ON trust_records(action, category, repo);
59
+ `);
60
+ // FTS5 virtual table for full-text search
61
+ // Use a separate try-catch since FTS5 tables can't use IF NOT EXISTS
62
+ try {
63
+ this.db.exec(`
64
+ CREATE VIRTUAL TABLE memory_fts USING fts5(
65
+ title, content, tags,
66
+ content='memory_items',
67
+ content_rowid='rowid',
68
+ tokenize='unicode61'
69
+ );
70
+ `);
71
+ }
72
+ catch {
73
+ // Table already exists — that's fine
74
+ }
75
+ }
76
+ // -------------------------------------------------------------------------
77
+ // Memory CRUD
78
+ // -------------------------------------------------------------------------
79
+ /** Insert or update a memory item. */
80
+ upsert(item) {
81
+ const id = item.id ?? randomUUID();
82
+ const now = new Date().toISOString();
83
+ const tagsJson = JSON.stringify(item.tags);
84
+ const existing = this.db.prepare('SELECT id FROM memory_items WHERE id = ?').get(id);
85
+ if (existing) {
86
+ this.db.prepare(`
87
+ UPDATE memory_items SET
88
+ memory_type = ?, scope = ?, title = ?, content = ?,
89
+ source_ref = ?, confidence = ?, updated_at = ?,
90
+ expires_at = ?, is_pinned = ?, tags = ?
91
+ WHERE id = ?
92
+ `).run(item.memoryType, item.scope, item.title, item.content, item.sourceRef, item.confidence, now, item.expiresAt, item.isPinned ? 1 : 0, tagsJson, id);
93
+ }
94
+ else {
95
+ this.db.prepare(`
96
+ INSERT INTO memory_items (id, memory_type, scope, title, content,
97
+ source_ref, confidence, created_at, updated_at,
98
+ expires_at, is_pinned, tags)
99
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
100
+ `).run(id, item.memoryType, item.scope, item.title, item.content, item.sourceRef, item.confidence, now, now, item.expiresAt, item.isPinned ? 1 : 0, tagsJson);
101
+ }
102
+ // Update FTS index
103
+ this.db.prepare('INSERT INTO memory_fts(rowid, title, content, tags) VALUES ((SELECT rowid FROM memory_items WHERE id = ?), ?, ?, ?)').run(id, item.title ?? '', item.content, tagsJson);
104
+ return id;
105
+ }
106
+ /** Delete a memory item by ID. */
107
+ delete(id) {
108
+ const result = this.db.prepare('DELETE FROM memory_items WHERE id = ?').run(id);
109
+ return result.changes > 0;
110
+ }
111
+ /** Get a memory item by ID. */
112
+ get(id) {
113
+ const row = this.db.prepare('SELECT * FROM memory_items WHERE id = ?').get(id);
114
+ return row ? this.rowToItem(row) : null;
115
+ }
116
+ /** List memory items with optional filters. */
117
+ list(filters) {
118
+ let sql = 'SELECT * FROM memory_items WHERE 1=1';
119
+ const params = [];
120
+ if (filters?.memoryType) {
121
+ sql += ' AND memory_type = ?';
122
+ params.push(filters.memoryType);
123
+ }
124
+ if (filters?.scope !== undefined) {
125
+ if (filters.scope === null) {
126
+ sql += ' AND scope IS NULL';
127
+ }
128
+ else {
129
+ sql += ' AND scope = ?';
130
+ params.push(filters.scope);
131
+ }
132
+ }
133
+ if (filters?.pinnedOnly) {
134
+ sql += ' AND is_pinned = 1';
135
+ }
136
+ sql += ' ORDER BY updated_at DESC';
137
+ if (filters?.limit) {
138
+ sql += ' LIMIT ?';
139
+ params.push(filters.limit);
140
+ }
141
+ const rows = this.db.prepare(sql).all(...params);
142
+ return rows.map(r => this.rowToItem(r));
143
+ }
144
+ /** Full-text search across memory items. */
145
+ search(query, limit = 10) {
146
+ const rows = this.db.prepare(`
147
+ SELECT m.* FROM memory_items m
148
+ JOIN memory_fts f ON m.rowid = f.rowid
149
+ WHERE memory_fts MATCH ?
150
+ ORDER BY rank
151
+ LIMIT ?
152
+ `).all(query, limit);
153
+ return rows.map(r => this.rowToItem(r));
154
+ }
155
+ /** Remove expired items. */
156
+ expireStale() {
157
+ const now = new Date().toISOString();
158
+ const result = this.db.prepare('DELETE FROM memory_items WHERE expires_at IS NOT NULL AND expires_at < ?').run(now);
159
+ return result.changes;
160
+ }
161
+ // -------------------------------------------------------------------------
162
+ // Trust Records
163
+ // -------------------------------------------------------------------------
164
+ /** Get or create a trust record for an action pattern. */
165
+ getTrust(action, category, repo) {
166
+ const row = this.db.prepare(repo
167
+ ? 'SELECT * FROM trust_records WHERE action = ? AND category = ? AND repo = ?'
168
+ : 'SELECT * FROM trust_records WHERE action = ? AND category = ? AND repo IS NULL').get(...(repo ? [action, category, repo] : [action, category]));
169
+ if (row)
170
+ return this.rowToTrust(row);
171
+ // Check for global override
172
+ if (repo) {
173
+ const globalRow = this.db.prepare('SELECT * FROM trust_records WHERE action = ? AND category = ? AND repo IS NULL').get(action, category);
174
+ if (globalRow?.pinned_level)
175
+ return this.rowToTrust(globalRow);
176
+ }
177
+ // Create new record
178
+ const id = randomUUID();
179
+ this.db.prepare('INSERT INTO trust_records (id, action, category, repo) VALUES (?, ?, ?, ?)').run(id, action, category, repo);
180
+ return {
181
+ id, action, category, severity: 'medium', repo,
182
+ approvalCount: 0, rejectionCount: 0,
183
+ lastApprovedAt: null, lastRejectedAt: null, pinnedLevel: null,
184
+ };
185
+ }
186
+ /** Record a user approval for an action pattern. */
187
+ recordApproval(action, category, repo) {
188
+ const trust = this.getTrust(action, category, repo);
189
+ const now = new Date().toISOString();
190
+ this.db.prepare(`
191
+ UPDATE trust_records SET
192
+ approval_count = approval_count + 1,
193
+ last_approved_at = ?
194
+ WHERE id = ?
195
+ `).run(now, trust.id);
196
+ return { ...trust, approvalCount: trust.approvalCount + 1, lastApprovedAt: now };
197
+ }
198
+ /** Record a user rejection — resets trust to ASK level. */
199
+ recordRejection(action, category, repo) {
200
+ const trust = this.getTrust(action, category, repo);
201
+ const now = new Date().toISOString();
202
+ this.db.prepare(`
203
+ UPDATE trust_records SET
204
+ approval_count = 0,
205
+ rejection_count = rejection_count + 1,
206
+ last_rejected_at = ?,
207
+ pinned_level = NULL
208
+ WHERE id = ?
209
+ `).run(now, trust.id);
210
+ return { ...trust, approvalCount: 0, rejectionCount: trust.rejectionCount + 1, lastRejectedAt: now, pinnedLevel: null };
211
+ }
212
+ /** Pin a trust record to a specific level (user override). */
213
+ pinTrust(action, category, repo, level) {
214
+ const trust = this.getTrust(action, category, repo);
215
+ this.db.prepare('UPDATE trust_records SET pinned_level = ? WHERE id = ?').run(level, trust.id);
216
+ }
217
+ /** Reset all trust records back to ASK. */
218
+ resetAllTrust() {
219
+ this.db.prepare('UPDATE trust_records SET approval_count = 0, pinned_level = NULL').run();
220
+ }
221
+ /** Compute the effective trust level for an action. */
222
+ computeTrustLevel(action, category, severity, repo) {
223
+ const trust = this.getTrust(action, category, repo);
224
+ // User-pinned override takes precedence
225
+ if (trust.pinnedLevel)
226
+ return trust.pinnedLevel;
227
+ // High-severity actions have higher thresholds
228
+ const isHighSeverity = severity === 'high' || severity === 'critical';
229
+ const notifyThreshold = isHighSeverity ? 4 : 2;
230
+ const silentThreshold = isHighSeverity ? Infinity : 5; // high-severity never reaches silent
231
+ if (trust.approvalCount >= silentThreshold)
232
+ return 'silent';
233
+ if (trust.approvalCount >= notifyThreshold)
234
+ return 'notify_do';
235
+ return 'ask';
236
+ }
237
+ /** List all trust records with their effective levels. */
238
+ listTrustRecords() {
239
+ const rows = this.db.prepare('SELECT * FROM trust_records ORDER BY category, action').all();
240
+ return rows.map(r => {
241
+ const trust = this.rowToTrust(r);
242
+ return {
243
+ ...trust,
244
+ effectiveLevel: this.computeTrustLevel(trust.action, trust.category, trust.severity, trust.repo),
245
+ };
246
+ });
247
+ }
248
+ // -------------------------------------------------------------------------
249
+ // Cleanup
250
+ // -------------------------------------------------------------------------
251
+ close() {
252
+ this.db.close();
253
+ }
254
+ // -------------------------------------------------------------------------
255
+ // Helpers
256
+ // -------------------------------------------------------------------------
257
+ rowToItem(row) {
258
+ return {
259
+ id: row.id,
260
+ memoryType: row.memory_type,
261
+ scope: row.scope,
262
+ title: row.title,
263
+ content: row.content,
264
+ sourceRef: row.source_ref,
265
+ confidence: row.confidence,
266
+ createdAt: row.created_at,
267
+ updatedAt: row.updated_at,
268
+ expiresAt: row.expires_at,
269
+ isPinned: row.is_pinned === 1,
270
+ tags: JSON.parse(row.tags || '[]'),
271
+ };
272
+ }
273
+ rowToTrust(row) {
274
+ return {
275
+ id: row.id,
276
+ action: row.action,
277
+ category: row.category,
278
+ severity: row.severity,
279
+ repo: row.repo,
280
+ approvalCount: row.approval_count,
281
+ rejectionCount: row.rejection_count,
282
+ lastApprovedAt: row.last_approved_at,
283
+ lastRejectedAt: row.last_rejected_at,
284
+ pinnedLevel: row.pinned_level,
285
+ };
286
+ }
287
+ }
288
+ //# sourceMappingURL=orchestrator-memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator-memory.js","sourceRoot":"","sources":["../orchestrator-memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Orchestrator proactive monitor — watches for new reports, idle repos,
3
+ * and workflow events, then queues notifications for the orchestrator session.
4
+ *
5
+ * Runs a periodic poll and subscribes to workflow engine events.
6
+ * Notifications are delivered in-chat via the orchestrator session.
7
+ */
8
+ import type { SessionManager } from './session-manager.js';
9
+ import type { WorkflowEngine } from './workflow-engine.js';
10
+ import { OrchestratorMemory } from './orchestrator-memory.js';
11
+ export interface OrchestratorNotification {
12
+ id: string;
13
+ severity: 'info' | 'action' | 'alert';
14
+ title: string;
15
+ body: string;
16
+ timestamp: string;
17
+ delivered: boolean;
18
+ }
19
+ export declare class OrchestratorMonitor {
20
+ private sessions;
21
+ private pollTimer;
22
+ private agingTimer;
23
+ private notifications;
24
+ private seenReports;
25
+ private memory;
26
+ constructor(sessions: SessionManager);
27
+ /** Connect to the workflow engine for event-driven notifications. */
28
+ setEngine(engine: WorkflowEngine): void;
29
+ /** Set the memory store for aging and decision tracking. */
30
+ setMemory(memory: OrchestratorMemory): void;
31
+ /** Start the periodic poll. */
32
+ start(): void;
33
+ /** Stop the monitor. */
34
+ stop(): void;
35
+ /** Get pending (undelivered) notifications. */
36
+ getPending(): OrchestratorNotification[];
37
+ /** Mark notifications as delivered. */
38
+ markDelivered(ids: string[]): void;
39
+ /** Get all notifications (including delivered). */
40
+ getAll(): OrchestratorNotification[];
41
+ /** Run aging cycle and check for pending decision assessments. */
42
+ private runAgingAndAssessments;
43
+ /** Initial scan — populate the set of already-seen reports. */
44
+ private initialScan;
45
+ /** Periodic poll — check for new reports and idle repos. */
46
+ private poll;
47
+ /** Check for reports we haven't seen before. */
48
+ private checkNewReports;
49
+ /** Check for repos that haven't had recent commits. */
50
+ private checkPassiveRepos;
51
+ /** Handle workflow engine events. */
52
+ private handleWorkflowEvent;
53
+ /** Add a notification. */
54
+ private addNotification;
55
+ /** Deliver a notification to the orchestrator chat session. */
56
+ private deliverToOrchestrator;
57
+ /** Discover repo paths from REPOS_ROOT. */
58
+ private discoverRepoPaths;
59
+ }
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Orchestrator proactive monitor — watches for new reports, idle repos,
3
+ * and workflow events, then queues notifications for the orchestrator session.
4
+ *
5
+ * Runs a periodic poll and subscribes to workflow engine events.
6
+ * Notifications are delivered in-chat via the orchestrator session.
7
+ */
8
+ import { readdirSync, statSync, existsSync } from 'fs';
9
+ import { join } from 'path';
10
+ import { scanRepoReports } from './orchestrator-reports.js';
11
+ import { runAgingCycle, getPendingOutcomeAssessments } from './orchestrator-learning.js';
12
+ import { getOrchestratorSessionId } from './orchestrator-manager.js';
13
+ import { REPOS_ROOT, getAgentDisplayName } from './config.js';
14
+ // ---------------------------------------------------------------------------
15
+ // Monitor
16
+ // ---------------------------------------------------------------------------
17
+ const POLL_INTERVAL_MS = 15 * 60 * 1000; // 15 minutes
18
+ const PASSIVE_THRESHOLD_DAYS = 30;
19
+ export class OrchestratorMonitor {
20
+ sessions;
21
+ pollTimer = null;
22
+ agingTimer = null;
23
+ notifications = [];
24
+ seenReports = new Set();
25
+ memory = null;
26
+ constructor(sessions) {
27
+ this.sessions = sessions;
28
+ }
29
+ /** Connect to the workflow engine for event-driven notifications. */
30
+ setEngine(engine) {
31
+ engine.on('workflow_event', (event) => {
32
+ this.handleWorkflowEvent(event);
33
+ });
34
+ }
35
+ /** Set the memory store for aging and decision tracking. */
36
+ setMemory(memory) {
37
+ this.memory = memory;
38
+ }
39
+ /** Start the periodic poll. */
40
+ start() {
41
+ if (this.pollTimer)
42
+ return;
43
+ console.log('[orchestrator-monitor] Starting proactive monitor (poll every 15m)');
44
+ // Initial scan to populate seen reports
45
+ void this.initialScan();
46
+ this.pollTimer = setInterval(() => {
47
+ void this.poll();
48
+ }, POLL_INTERVAL_MS);
49
+ // Run aging cycle daily (check every 6 hours)
50
+ this.agingTimer = setInterval(() => {
51
+ this.runAgingAndAssessments();
52
+ }, 6 * 60 * 60 * 1000);
53
+ }
54
+ /** Stop the monitor. */
55
+ stop() {
56
+ if (this.pollTimer) {
57
+ clearInterval(this.pollTimer);
58
+ this.pollTimer = null;
59
+ }
60
+ if (this.agingTimer) {
61
+ clearInterval(this.agingTimer);
62
+ this.agingTimer = null;
63
+ }
64
+ }
65
+ /** Get pending (undelivered) notifications. */
66
+ getPending() {
67
+ return this.notifications.filter(n => !n.delivered);
68
+ }
69
+ /** Mark notifications as delivered. */
70
+ markDelivered(ids) {
71
+ for (const n of this.notifications) {
72
+ if (ids.includes(n.id))
73
+ n.delivered = true;
74
+ }
75
+ }
76
+ /** Get all notifications (including delivered). */
77
+ getAll() {
78
+ return [...this.notifications].sort((a, b) => b.timestamp.localeCompare(a.timestamp));
79
+ }
80
+ // -------------------------------------------------------------------------
81
+ // Internal
82
+ // -------------------------------------------------------------------------
83
+ /** Run aging cycle and check for pending decision assessments. */
84
+ runAgingAndAssessments() {
85
+ if (!this.memory)
86
+ return;
87
+ try {
88
+ const agingResult = runAgingCycle(this.memory);
89
+ if (agingResult.expired > 0 || agingResult.compacted > 0) {
90
+ console.log(`[orchestrator-monitor] Aging cycle: ${agingResult.expired} expired, ${agingResult.compacted} compacted, ${agingResult.decayed} decayed`);
91
+ }
92
+ // Check for decisions that need outcome assessment
93
+ const pending = getPendingOutcomeAssessments(this.memory);
94
+ if (pending.length > 0) {
95
+ this.addNotification({
96
+ severity: 'info',
97
+ title: 'Decisions need review',
98
+ body: `${pending.length} decision(s) from over a week ago need outcome assessment. Ask me to review them.`,
99
+ });
100
+ }
101
+ }
102
+ catch (err) {
103
+ console.error('[orchestrator-monitor] Aging cycle error:', err);
104
+ }
105
+ }
106
+ /** Initial scan — populate the set of already-seen reports. */
107
+ async initialScan() {
108
+ const repoPaths = this.discoverRepoPaths();
109
+ for (const repoPath of repoPaths) {
110
+ const reports = scanRepoReports(repoPath);
111
+ for (const r of reports) {
112
+ this.seenReports.add(r.filePath);
113
+ }
114
+ }
115
+ // initial scan complete
116
+ }
117
+ /** Periodic poll — check for new reports and idle repos. */
118
+ async poll() {
119
+ const repoPaths = this.discoverRepoPaths();
120
+ // Check for new reports
121
+ this.checkNewReports(repoPaths);
122
+ // Check for passive repos
123
+ this.checkPassiveRepos(repoPaths);
124
+ // initial scan complete
125
+ }
126
+ /** Check for reports we haven't seen before. */
127
+ checkNewReports(repoPaths) {
128
+ for (const repoPath of repoPaths) {
129
+ const reports = scanRepoReports(repoPath);
130
+ const newReports = reports.filter(r => !this.seenReports.has(r.filePath));
131
+ if (newReports.length > 0) {
132
+ for (const r of newReports) {
133
+ this.seenReports.add(r.filePath);
134
+ }
135
+ const repoName = repoPath.split('/').pop() ?? repoPath;
136
+ const categories = [...new Set(newReports.map(r => r.category))];
137
+ this.addNotification({
138
+ severity: 'action',
139
+ title: `New reports for ${repoName}`,
140
+ body: `${newReports.length} new report(s) landed: ${categories.join(', ')}. Want a summary?`,
141
+ });
142
+ }
143
+ }
144
+ }
145
+ /** Check for repos that haven't had recent commits. */
146
+ checkPassiveRepos(repoPaths) {
147
+ const now = Date.now();
148
+ for (const repoPath of repoPaths) {
149
+ try {
150
+ const gitDir = join(repoPath, '.git');
151
+ if (!existsSync(gitDir))
152
+ continue;
153
+ // Use HEAD ref's mtime as a proxy for last commit time
154
+ const headFile = join(gitDir, 'HEAD');
155
+ if (!existsSync(headFile))
156
+ continue;
157
+ const headStat = statSync(headFile);
158
+ const daysSinceActivity = Math.floor((now - headStat.mtime.getTime()) / (24 * 60 * 60 * 1000));
159
+ if (daysSinceActivity >= PASSIVE_THRESHOLD_DAYS) {
160
+ const repoName = repoPath.split('/').pop() ?? repoPath;
161
+ this.addNotification({
162
+ severity: 'info',
163
+ title: `${repoName} looks passive`,
164
+ body: `No activity in ${daysSinceActivity} days. Consider de-scheduling workflows to save resources.`,
165
+ });
166
+ }
167
+ }
168
+ catch {
169
+ // Skip repos we can't stat
170
+ }
171
+ }
172
+ }
173
+ /** Handle workflow engine events. */
174
+ handleWorkflowEvent(event) {
175
+ // Notify on workflow failures
176
+ if (event.eventType === 'run_failed') {
177
+ this.addNotification({
178
+ severity: 'alert',
179
+ title: `Workflow failed: ${event.kind}`,
180
+ body: `Run ${event.runId} failed. Check the workflow logs for details.`,
181
+ });
182
+ }
183
+ // Notify on successful runs that produce reports
184
+ if (event.eventType === 'run_succeeded') {
185
+ // Report was likely written — next poll will pick it up as a new report
186
+ }
187
+ }
188
+ /** Add a notification. */
189
+ addNotification(opts) {
190
+ const notification = {
191
+ id: `notif-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
192
+ timestamp: new Date().toISOString(),
193
+ delivered: false,
194
+ ...opts,
195
+ };
196
+ this.notifications.push(notification);
197
+ // Keep last 100 notifications
198
+ if (this.notifications.length > 100) {
199
+ this.notifications = this.notifications.slice(-100);
200
+ }
201
+ // Deliver to orchestrator session if active
202
+ this.deliverToOrchestrator(notification);
203
+ }
204
+ /** Deliver a notification to the orchestrator chat session. */
205
+ deliverToOrchestrator(notification) {
206
+ const orchestratorId = getOrchestratorSessionId(this.sessions);
207
+ if (!orchestratorId)
208
+ return;
209
+ const session = this.sessions.get(orchestratorId);
210
+ if (!session?.claudeProcess?.isAlive())
211
+ return;
212
+ // Send as a system message that the orchestrator will see and respond to
213
+ const message = `[Agent ${getAgentDisplayName()} Notification — ${notification.severity.toUpperCase()}]\n${notification.title}\n${notification.body}`;
214
+ this.sessions.sendInput(orchestratorId, message);
215
+ notification.delivered = true;
216
+ }
217
+ /** Discover repo paths from REPOS_ROOT. */
218
+ discoverRepoPaths() {
219
+ if (!existsSync(REPOS_ROOT))
220
+ return [];
221
+ try {
222
+ return readdirSync(REPOS_ROOT)
223
+ .map(name => join(REPOS_ROOT, name))
224
+ .filter(p => {
225
+ try {
226
+ return statSync(p).isDirectory() && existsSync(join(p, '.git'));
227
+ }
228
+ catch {
229
+ return false;
230
+ }
231
+ });
232
+ }
233
+ catch {
234
+ return [];
235
+ }
236
+ }
237
+ }
238
+ //# sourceMappingURL=orchestrator-monitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator-monitor.js","sourceRoot":"","sources":["../orchestrator-monitor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Orchestrator report reader — scans .codekin/reports/ across managed repos.
3
+ *
4
+ * Discovers audit reports, parses their metadata, and provides them
5
+ * to the orchestrator session for triage and action.
6
+ */
7
+ export interface ReportMeta {
8
+ /** Full path to the report file. */
9
+ filePath: string;
10
+ /** Report category (directory name): repo-health, security, code-review, etc. */
11
+ category: string;
12
+ /** Date extracted from filename (YYYY-MM-DD). */
13
+ date: string;
14
+ /** Repository path this report belongs to. */
15
+ repoPath: string;
16
+ /** File size in bytes. */
17
+ size: number;
18
+ /** Last modified time. */
19
+ mtime: string;
20
+ }
21
+ export interface ReportContent extends ReportMeta {
22
+ /** Full markdown content of the report. */
23
+ content: string;
24
+ }
25
+ /**
26
+ * Scan a single repo for all available reports.
27
+ * Returns metadata only (no content) for efficiency.
28
+ */
29
+ export declare function scanRepoReports(repoPath: string): ReportMeta[];
30
+ /**
31
+ * Scan multiple repos for reports.
32
+ */
33
+ export declare function scanAllReports(repoPaths: string[]): ReportMeta[];
34
+ /**
35
+ * Read a report's full content.
36
+ */
37
+ export declare function readReport(filePath: string): ReportContent | null;
38
+ /**
39
+ * Get the latest report for a given repo and category.
40
+ */
41
+ export declare function getLatestReport(repoPath: string, category: string): ReportMeta | null;
42
+ /**
43
+ * Get reports that are newer than a given date.
44
+ */
45
+ export declare function getReportsSince(repoPaths: string[], sinceDate: string): ReportMeta[];