@cleocode/core 2026.3.69 → 2026.3.71
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/dist/agents/retry.d.ts.map +1 -1
- package/dist/agents/retry.js +23 -42
- package/dist/agents/retry.js.map +1 -1
- package/dist/cleo.d.ts +2 -300
- package/dist/cleo.d.ts.map +1 -1
- package/dist/cleo.js +2 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +30 -0
- package/dist/config.js.map +1 -1
- package/dist/hooks/handlers/file-hooks.d.ts +5 -2
- package/dist/hooks/handlers/file-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/index.d.ts +2 -0
- package/dist/hooks/handlers/index.d.ts.map +1 -1
- package/dist/hooks/handlers/mcp-hooks.d.ts +11 -7
- package/dist/hooks/handlers/mcp-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/memory-bridge-refresh.d.ts +20 -0
- package/dist/hooks/handlers/memory-bridge-refresh.d.ts.map +1 -0
- package/dist/hooks/handlers/memory-bridge-refresh.js +42 -0
- package/dist/hooks/handlers/memory-bridge-refresh.js.map +1 -0
- package/dist/hooks/handlers/session-hooks.d.ts +10 -0
- package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/session-hooks.js +36 -0
- package/dist/hooks/handlers/session-hooks.js.map +1 -1
- package/dist/hooks/handlers/task-hooks.d.ts +4 -0
- package/dist/hooks/handlers/task-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/task-hooks.js +7 -0
- package/dist/hooks/handlers/task-hooks.js.map +1 -1
- package/dist/hooks/handlers/work-capture-hooks.d.ts +40 -0
- package/dist/hooks/handlers/work-capture-hooks.d.ts.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5069 -4678
- package/dist/index.js.map +4 -4
- package/dist/internal.d.ts +10 -2
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +10 -3
- package/dist/internal.js.map +1 -1
- package/dist/memory/auto-extract.d.ts +13 -0
- package/dist/memory/auto-extract.d.ts.map +1 -1
- package/dist/memory/auto-extract.js +34 -0
- package/dist/memory/auto-extract.js.map +1 -1
- package/dist/memory/brain-embedding.d.ts +13 -0
- package/dist/memory/brain-embedding.d.ts.map +1 -1
- package/dist/memory/brain-embedding.js +17 -0
- package/dist/memory/brain-embedding.js.map +1 -1
- package/dist/memory/brain-maintenance.d.ts +110 -0
- package/dist/memory/brain-maintenance.d.ts.map +1 -0
- package/dist/memory/brain-maintenance.js +98 -0
- package/dist/memory/brain-maintenance.js.map +1 -0
- package/dist/memory/brain-retrieval.d.ts +31 -5
- package/dist/memory/brain-retrieval.d.ts.map +1 -1
- package/dist/memory/brain-retrieval.js +53 -6
- package/dist/memory/brain-retrieval.js.map +1 -1
- package/dist/memory/embedding-local.d.ts +55 -0
- package/dist/memory/embedding-local.d.ts.map +1 -0
- package/dist/memory/embedding-local.js +97 -0
- package/dist/memory/embedding-local.js.map +1 -0
- package/dist/memory/embedding-queue.d.ts +90 -0
- package/dist/memory/embedding-queue.d.ts.map +1 -0
- package/dist/memory/embedding-queue.js +271 -0
- package/dist/memory/embedding-queue.js.map +1 -0
- package/dist/memory/embedding-worker.d.ts +19 -0
- package/dist/memory/embedding-worker.d.ts.map +1 -0
- package/dist/memory/embedding-worker.js +58 -0
- package/dist/memory/embedding-worker.js.map +1 -0
- package/dist/memory/memory-bridge.d.ts +21 -1
- package/dist/memory/memory-bridge.d.ts.map +1 -1
- package/dist/memory/memory-bridge.js +83 -2
- package/dist/memory/memory-bridge.js.map +1 -1
- package/dist/memory/session-memory.d.ts +26 -0
- package/dist/memory/session-memory.d.ts.map +1 -1
- package/dist/memory/session-memory.js +105 -0
- package/dist/memory/session-memory.js.map +1 -1
- package/dist/pagination.js +3 -0
- package/dist/pagination.js.map +1 -1
- package/dist/sessions/index.d.ts.map +1 -1
- package/dist/sessions/index.js +2 -6
- package/dist/sessions/index.js.map +1 -1
- package/dist/store/brain-sqlite.js +13 -62
- package/dist/store/brain-sqlite.js.map +1 -1
- package/dist/store/migration-manager.js +151 -0
- package/dist/store/migration-manager.js.map +1 -0
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +16 -134
- package/dist/store/sqlite.js.map +1 -1
- package/dist/tasks/add.js +27 -22
- package/dist/tasks/add.js.map +1 -1
- package/dist/tasks/complete.d.ts.map +1 -1
- package/dist/tasks/complete.js +13 -40
- package/dist/tasks/complete.js.map +1 -1
- package/dist/tasks/enforcement.js +12 -15
- package/dist/tasks/enforcement.js.map +1 -1
- package/dist/upgrade.js +246 -3
- package/dist/upgrade.js.map +1 -1
- package/migrations/drizzle-tasks/20260320013731_wave0-schema-hardening/migration.sql +17 -17
- package/package.json +6 -5
- package/src/agents/retry.ts +30 -24
- package/src/cleo.ts +30 -251
- package/src/config.ts +18 -0
- package/src/hooks/handlers/file-hooks.ts +29 -3
- package/src/hooks/handlers/index.ts +2 -0
- package/src/hooks/handlers/mcp-hooks.ts +32 -13
- package/src/hooks/handlers/memory-bridge-refresh.ts +47 -0
- package/src/hooks/handlers/session-hooks.ts +38 -0
- package/src/hooks/handlers/task-hooks.ts +8 -0
- package/src/hooks/handlers/work-capture-hooks.ts +184 -0
- package/src/index.ts +5 -0
- package/src/internal.ts +28 -2
- package/src/memory/__tests__/brain-automation.test.ts +941 -0
- package/src/memory/auto-extract.ts +40 -0
- package/src/memory/brain-embedding.ts +18 -0
- package/src/memory/brain-maintenance.ts +183 -0
- package/src/memory/brain-retrieval.ts +85 -7
- package/src/memory/embedding-local.ts +107 -0
- package/src/memory/embedding-queue.ts +304 -0
- package/src/memory/embedding-worker.ts +79 -0
- package/src/memory/memory-bridge.ts +101 -2
- package/src/memory/session-memory.ts +123 -0
- package/src/sessions/index.ts +2 -6
- package/src/store/__tests__/test-db-helper.js +14 -2
- package/src/store/__tests__/test-db-helper.ts +4 -1
- package/src/store/sqlite.ts +28 -0
- package/src/tasks/__tests__/complete-unblocks.test.ts +4 -1
- package/src/tasks/__tests__/complete.test.ts +18 -6
- package/src/tasks/__tests__/epic-enforcement.test.ts +4 -1
- package/src/tasks/__tests__/update.test.ts +4 -1
- package/src/tasks/complete.ts +8 -8
- package/templates/config.template.json +19 -0
- package/templates/global-config.template.json +19 -0
package/src/cleo.ts
CHANGED
|
@@ -16,26 +16,26 @@
|
|
|
16
16
|
|
|
17
17
|
import path from 'node:path';
|
|
18
18
|
import type {
|
|
19
|
+
AdminAPI,
|
|
20
|
+
AgentsAPI,
|
|
21
|
+
CleoInitOptions,
|
|
19
22
|
DataAccessor,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
IntelligenceAPI,
|
|
24
|
+
LifecycleAPI,
|
|
25
|
+
MemoryAPI,
|
|
26
|
+
NexusAPI,
|
|
27
|
+
OrchestrationAPI,
|
|
28
|
+
ReleaseAPI,
|
|
29
|
+
SessionsAPI,
|
|
30
|
+
StickyAPI,
|
|
31
|
+
SyncAPI,
|
|
32
|
+
TasksAPI,
|
|
29
33
|
} from '@cleocode/contracts';
|
|
30
34
|
// Admin
|
|
31
35
|
import { exportTasks } from './admin/export.js';
|
|
32
|
-
import type { ImportParams } from './admin/import.js';
|
|
33
36
|
import { importTasks } from './admin/import.js';
|
|
34
37
|
// Agents
|
|
35
38
|
import {
|
|
36
|
-
type AgentCapacity,
|
|
37
|
-
type AgentHealthStatus,
|
|
38
|
-
type AgentInstanceRow,
|
|
39
39
|
checkAgentHealth,
|
|
40
40
|
deregisterAgent,
|
|
41
41
|
detectCrashedAgents,
|
|
@@ -43,16 +43,10 @@ import {
|
|
|
43
43
|
heartbeat,
|
|
44
44
|
isOverloaded,
|
|
45
45
|
listAgentInstances,
|
|
46
|
-
type RegisterAgentOptions,
|
|
47
46
|
registerAgent,
|
|
48
47
|
} from './agents/index.js';
|
|
49
48
|
// Intelligence
|
|
50
|
-
import {
|
|
51
|
-
type BlastRadius,
|
|
52
|
-
calculateBlastRadius,
|
|
53
|
-
type ImpactReport,
|
|
54
|
-
predictImpact,
|
|
55
|
-
} from './intelligence/index.js';
|
|
49
|
+
import { calculateBlastRadius, predictImpact } from './intelligence/index.js';
|
|
56
50
|
// Lifecycle
|
|
57
51
|
import {
|
|
58
52
|
checkGate,
|
|
@@ -66,7 +60,6 @@ import {
|
|
|
66
60
|
skipStage,
|
|
67
61
|
startStage,
|
|
68
62
|
} from './lifecycle/index.js';
|
|
69
|
-
import type { BrainObservationType } from './memory/brain-retrieval.js';
|
|
70
63
|
// Memory
|
|
71
64
|
import {
|
|
72
65
|
fetchBrainEntries,
|
|
@@ -74,7 +67,6 @@ import {
|
|
|
74
67
|
searchBrainCompact,
|
|
75
68
|
timelineBrain,
|
|
76
69
|
} from './memory/brain-retrieval.js';
|
|
77
|
-
import type { HybridSearchOptions } from './memory/brain-search.js';
|
|
78
70
|
import { hybridSearch, searchBrain } from './memory/brain-search.js';
|
|
79
71
|
// Nexus
|
|
80
72
|
import { discoverRelated, searchAcrossProjects } from './nexus/discover.js';
|
|
@@ -162,237 +154,24 @@ import { showTask } from './tasks/show.js';
|
|
|
162
154
|
import { updateTask } from './tasks/update.js';
|
|
163
155
|
|
|
164
156
|
// ============================================================================
|
|
165
|
-
// Domain API interfaces
|
|
166
|
-
// ============================================================================
|
|
167
|
-
|
|
168
|
-
export interface TasksAPI {
|
|
169
|
-
add(params: {
|
|
170
|
-
title: string;
|
|
171
|
-
description: string;
|
|
172
|
-
parent?: string;
|
|
173
|
-
priority?: TaskPriority;
|
|
174
|
-
type?: TaskType;
|
|
175
|
-
size?: TaskSize;
|
|
176
|
-
phase?: string;
|
|
177
|
-
labels?: string[];
|
|
178
|
-
depends?: string[];
|
|
179
|
-
notes?: string;
|
|
180
|
-
}): Promise<unknown>;
|
|
181
|
-
find(params: {
|
|
182
|
-
query?: string;
|
|
183
|
-
id?: string;
|
|
184
|
-
status?: TaskStatus;
|
|
185
|
-
limit?: number;
|
|
186
|
-
}): Promise<unknown>;
|
|
187
|
-
show(taskId: string): Promise<unknown>;
|
|
188
|
-
list(params?: {
|
|
189
|
-
status?: TaskStatus;
|
|
190
|
-
priority?: TaskPriority;
|
|
191
|
-
parentId?: string;
|
|
192
|
-
phase?: string;
|
|
193
|
-
limit?: number;
|
|
194
|
-
}): Promise<unknown>;
|
|
195
|
-
update(params: {
|
|
196
|
-
taskId: string;
|
|
197
|
-
title?: string;
|
|
198
|
-
status?: TaskStatus;
|
|
199
|
-
priority?: TaskPriority;
|
|
200
|
-
description?: string;
|
|
201
|
-
notes?: string;
|
|
202
|
-
}): Promise<unknown>;
|
|
203
|
-
complete(params: { taskId: string; notes?: string }): Promise<unknown>;
|
|
204
|
-
delete(params: { taskId: string; force?: boolean }): Promise<unknown>;
|
|
205
|
-
archive(params?: { before?: string; taskIds?: string[]; dryRun?: boolean }): Promise<unknown>;
|
|
206
|
-
/** Start working on a specific task (sets focus). */
|
|
207
|
-
start(taskId: string): Promise<unknown>;
|
|
208
|
-
/** Stop working on the current task (clears focus). */
|
|
209
|
-
stop(): Promise<{ previousTask: string | null }>;
|
|
210
|
-
/** Get the current task work state. */
|
|
211
|
-
current(): Promise<unknown>;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
export interface SessionsAPI {
|
|
215
|
-
start(params: {
|
|
216
|
-
name: string;
|
|
217
|
-
scope: string;
|
|
218
|
-
agent?: string;
|
|
219
|
-
startTask?: string;
|
|
220
|
-
}): Promise<unknown>;
|
|
221
|
-
end(params?: { note?: string }): Promise<unknown>;
|
|
222
|
-
status(): Promise<unknown>;
|
|
223
|
-
resume(sessionId: string): Promise<unknown>;
|
|
224
|
-
list(params?: { status?: string; limit?: number }): Promise<unknown>;
|
|
225
|
-
find(params?: {
|
|
226
|
-
status?: string;
|
|
227
|
-
scope?: string;
|
|
228
|
-
query?: string;
|
|
229
|
-
limit?: number;
|
|
230
|
-
}): Promise<unknown>;
|
|
231
|
-
show(sessionId: string): Promise<unknown>;
|
|
232
|
-
suspend(sessionId: string, reason?: string): Promise<unknown>;
|
|
233
|
-
briefing(params?: { maxNextTasks?: number; scope?: string }): Promise<unknown>;
|
|
234
|
-
handoff(sessionId: string, options?: { note?: string; nextAction?: string }): Promise<unknown>;
|
|
235
|
-
gc(maxAgeHours?: number): Promise<unknown>;
|
|
236
|
-
recordDecision(params: {
|
|
237
|
-
sessionId: string;
|
|
238
|
-
taskId: string;
|
|
239
|
-
decision: string;
|
|
240
|
-
rationale: string;
|
|
241
|
-
alternatives?: string[];
|
|
242
|
-
}): Promise<unknown>;
|
|
243
|
-
recordAssumption(params: {
|
|
244
|
-
assumption: string;
|
|
245
|
-
confidence: 'high' | 'medium' | 'low';
|
|
246
|
-
sessionId?: string;
|
|
247
|
-
taskId?: string;
|
|
248
|
-
}): Promise<unknown>;
|
|
249
|
-
contextDrift(params?: { sessionId?: string }): Promise<unknown>;
|
|
250
|
-
decisionLog(params?: { sessionId?: string; taskId?: string }): Promise<unknown>;
|
|
251
|
-
lastHandoff(scope?: { type: string; epicId?: string }): Promise<unknown>;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export interface MemoryAPI {
|
|
255
|
-
observe(params: { text: string; title?: string; type?: BrainObservationType }): Promise<unknown>;
|
|
256
|
-
find(params: {
|
|
257
|
-
query: string;
|
|
258
|
-
limit?: number;
|
|
259
|
-
tables?: Array<'decisions' | 'patterns' | 'learnings' | 'observations'>;
|
|
260
|
-
}): Promise<unknown>;
|
|
261
|
-
fetch(params: { ids: string[] }): Promise<unknown>;
|
|
262
|
-
timeline(params: { anchor: string; depthBefore?: number; depthAfter?: number }): Promise<unknown>;
|
|
263
|
-
search(query: string, options?: { limit?: number }): Promise<unknown>;
|
|
264
|
-
hybridSearch(query: string, options?: HybridSearchOptions): Promise<unknown>;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
export interface OrchestrationAPI {
|
|
268
|
-
start(epicId: string): Promise<unknown>;
|
|
269
|
-
analyze(epicId: string): Promise<unknown>;
|
|
270
|
-
readyTasks(epicId: string): Promise<unknown>;
|
|
271
|
-
nextTask(epicId: string): Promise<unknown>;
|
|
272
|
-
context(epicId: string): Promise<unknown>;
|
|
273
|
-
dependencyGraph(tasks: Task[]): unknown;
|
|
274
|
-
epicStatus(epicId: string, title: string, children: Task[]): unknown;
|
|
275
|
-
progress(tasks: Task[]): unknown;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
export interface LifecycleAPI {
|
|
279
|
-
status(epicId: string): Promise<unknown>;
|
|
280
|
-
startStage(epicId: string, stage: string): Promise<unknown>;
|
|
281
|
-
completeStage(epicId: string, stage: string, artifacts?: string[]): Promise<unknown>;
|
|
282
|
-
skipStage(epicId: string, stage: string, reason: string): Promise<unknown>;
|
|
283
|
-
checkGate(epicId: string, targetStage: string): Promise<unknown>;
|
|
284
|
-
history(epicId: string): Promise<unknown>;
|
|
285
|
-
resetStage(epicId: string, stage: string, reason: string): Promise<unknown>;
|
|
286
|
-
passGate(epicId: string, gateName: string, agent?: string): Promise<unknown>;
|
|
287
|
-
failGate(epicId: string, gateName: string, reason?: string): Promise<unknown>;
|
|
288
|
-
stages: readonly string[];
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
export interface ReleaseAPI {
|
|
292
|
-
prepare(params: { version: string; tasks?: string[]; notes?: string }): Promise<unknown>;
|
|
293
|
-
commit(params: { version: string }): Promise<unknown>;
|
|
294
|
-
tag(params: { version: string }): Promise<unknown>;
|
|
295
|
-
push(params: { version: string; remote?: string; explicitPush?: boolean }): Promise<unknown>;
|
|
296
|
-
rollback(params: { version: string; reason?: string }): Promise<unknown>;
|
|
297
|
-
calculateVersion(current: string, bumpType: string): string;
|
|
298
|
-
bumpVersion(): Promise<unknown>;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
export interface AdminAPI {
|
|
302
|
-
export(params?: Record<string, unknown>): Promise<unknown>;
|
|
303
|
-
import(params: Omit<ImportParams, 'cwd'>): Promise<unknown>;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
export interface StickyAPI {
|
|
307
|
-
add(params: {
|
|
308
|
-
content: string;
|
|
309
|
-
tags?: string[];
|
|
310
|
-
priority?: string;
|
|
311
|
-
color?: string;
|
|
312
|
-
}): Promise<unknown>;
|
|
313
|
-
show(stickyId: string): Promise<unknown>;
|
|
314
|
-
list(params?: {
|
|
315
|
-
status?: string;
|
|
316
|
-
color?: string;
|
|
317
|
-
priority?: string;
|
|
318
|
-
limit?: number;
|
|
319
|
-
}): Promise<unknown>;
|
|
320
|
-
archive(stickyId: string): Promise<unknown>;
|
|
321
|
-
purge(stickyId: string): Promise<unknown>;
|
|
322
|
-
convert(params: {
|
|
323
|
-
stickyId: string;
|
|
324
|
-
targetType: 'task' | 'memory' | 'task_note' | 'session_note';
|
|
325
|
-
title?: string;
|
|
326
|
-
memoryType?: string;
|
|
327
|
-
taskId?: string;
|
|
328
|
-
}): Promise<unknown>;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
export interface NexusAPI {
|
|
332
|
-
init(): Promise<unknown>;
|
|
333
|
-
register(params: { path: string; name?: string; permissions?: string }): Promise<unknown>;
|
|
334
|
-
unregister(params: { name: string }): Promise<unknown>;
|
|
335
|
-
list(): Promise<unknown>;
|
|
336
|
-
show(params: { name: string }): Promise<unknown>;
|
|
337
|
-
sync(params?: { name?: string }): Promise<unknown>;
|
|
338
|
-
discover(params: { query: string; method?: string; limit?: number }): Promise<unknown>;
|
|
339
|
-
search(params: { pattern: string; project?: string; limit?: number }): Promise<unknown>;
|
|
340
|
-
setPermission(params: { name: string; level: 'read' | 'write' | 'execute' }): Promise<unknown>;
|
|
341
|
-
sharingStatus(): Promise<unknown>;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
export interface SyncAPI {
|
|
345
|
-
/** Reconcile external tasks with CLEO as SSoT. */
|
|
346
|
-
reconcile(params: {
|
|
347
|
-
externalTasks: ExternalTask[];
|
|
348
|
-
providerId: string;
|
|
349
|
-
dryRun?: boolean;
|
|
350
|
-
conflictPolicy?: ReconcileOptions['conflictPolicy'];
|
|
351
|
-
defaultPhase?: string;
|
|
352
|
-
defaultLabels?: string[];
|
|
353
|
-
}): Promise<ReconcileResult>;
|
|
354
|
-
/** Get all external task links for a provider. */
|
|
355
|
-
getLinks(providerId: string): Promise<ExternalTaskLink[]>;
|
|
356
|
-
/** Get all external task links for a CLEO task. */
|
|
357
|
-
getTaskLinks(taskId: string): Promise<ExternalTaskLink[]>;
|
|
358
|
-
/** Remove all external task links for a provider. */
|
|
359
|
-
removeProviderLinks(providerId: string): Promise<number>;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
export interface AgentsAPI {
|
|
363
|
-
/** Register a new agent instance. */
|
|
364
|
-
register(options: RegisterAgentOptions): Promise<AgentInstanceRow>;
|
|
365
|
-
/** Deregister an agent instance. */
|
|
366
|
-
deregister(agentId: string): Promise<AgentInstanceRow | null>;
|
|
367
|
-
/** Get health status for a specific agent. */
|
|
368
|
-
health(agentId: string): Promise<AgentHealthStatus | null>;
|
|
369
|
-
/** Detect agents that have crashed (missed heartbeats). */
|
|
370
|
-
detectCrashed(thresholdMs?: number): Promise<AgentInstanceRow[]>;
|
|
371
|
-
/** Record a heartbeat for an agent. */
|
|
372
|
-
recordHeartbeat(agentId: string): Promise<unknown>;
|
|
373
|
-
/** Get capacity info for an agent. */
|
|
374
|
-
capacity(agentId: string): Promise<AgentCapacity | null>;
|
|
375
|
-
/** Check if system is overloaded (available capacity below threshold). */
|
|
376
|
-
isOverloaded(threshold?: number): Promise<boolean>;
|
|
377
|
-
/** List all agent instances with optional filters. */
|
|
378
|
-
list(params?: { status?: string; agentType?: string }): Promise<AgentInstanceRow[]>;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
export interface IntelligenceAPI {
|
|
382
|
-
/** Predict impact of a change description on related tasks. */
|
|
383
|
-
predictImpact(change: string): Promise<ImpactReport>;
|
|
384
|
-
/** Calculate blast radius for a task change. */
|
|
385
|
-
blastRadius(taskId: string): Promise<BlastRadius>;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// ============================================================================
|
|
389
|
-
// Init options
|
|
157
|
+
// Domain API interfaces — re-exported from @cleocode/contracts/facade
|
|
390
158
|
// ============================================================================
|
|
391
159
|
|
|
392
|
-
export
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
160
|
+
export type {
|
|
161
|
+
AdminAPI,
|
|
162
|
+
AgentsAPI,
|
|
163
|
+
CleoInitOptions,
|
|
164
|
+
IntelligenceAPI,
|
|
165
|
+
LifecycleAPI,
|
|
166
|
+
MemoryAPI,
|
|
167
|
+
NexusAPI,
|
|
168
|
+
OrchestrationAPI,
|
|
169
|
+
ReleaseAPI,
|
|
170
|
+
SessionsAPI,
|
|
171
|
+
StickyAPI,
|
|
172
|
+
SyncAPI,
|
|
173
|
+
TasksAPI,
|
|
174
|
+
} from '@cleocode/contracts';
|
|
396
175
|
|
|
397
176
|
// ============================================================================
|
|
398
177
|
// Cleo facade class
|
package/src/config.ts
CHANGED
|
@@ -79,6 +79,24 @@ const DEFAULTS: CleoConfig = {
|
|
|
79
79
|
agentPrefix: 'cleo-',
|
|
80
80
|
privacyTier: 'private',
|
|
81
81
|
},
|
|
82
|
+
brain: {
|
|
83
|
+
autoCapture: true,
|
|
84
|
+
captureFiles: false,
|
|
85
|
+
captureMcp: false,
|
|
86
|
+
captureWork: false,
|
|
87
|
+
embedding: {
|
|
88
|
+
enabled: true,
|
|
89
|
+
provider: 'local' as const,
|
|
90
|
+
},
|
|
91
|
+
memoryBridge: {
|
|
92
|
+
autoRefresh: true,
|
|
93
|
+
contextAware: true,
|
|
94
|
+
maxTokens: 2000,
|
|
95
|
+
},
|
|
96
|
+
summarization: {
|
|
97
|
+
enabled: true,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
82
100
|
};
|
|
83
101
|
|
|
84
102
|
/** Environment variable to config path mapping. */
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* Includes 5-second dedup and path-based filtering to avoid noisy writes.
|
|
6
6
|
* Auto-registers on module load.
|
|
7
7
|
*
|
|
8
|
-
* Disabled by default.
|
|
8
|
+
* Disabled by default. Enable via:
|
|
9
|
+
* - Config: brain.captureFiles = true (checked first)
|
|
10
|
+
* - Env: CLEO_BRAIN_CAPTURE_FILES=true (overrides config)
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
import { isAbsolute, relative } from 'node:path';
|
|
@@ -39,10 +41,34 @@ function shouldSkipPath(relativePath: string): boolean {
|
|
|
39
41
|
return SKIP_PATTERNS.some((pattern) => pattern.test(relativePath));
|
|
40
42
|
}
|
|
41
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Check whether file-change capture is enabled.
|
|
46
|
+
*
|
|
47
|
+
* Resolution order (first truthy wins):
|
|
48
|
+
* 1. CLEO_BRAIN_CAPTURE_FILES env var (explicit override)
|
|
49
|
+
* 2. brain.captureFiles project config value
|
|
50
|
+
*
|
|
51
|
+
* Defaults to false when neither is set.
|
|
52
|
+
*/
|
|
53
|
+
async function isFileCaptureEnabled(projectRoot: string): Promise<boolean> {
|
|
54
|
+
const envOverride = process.env['CLEO_BRAIN_CAPTURE_FILES'];
|
|
55
|
+
if (envOverride !== undefined) {
|
|
56
|
+
return envOverride === 'true';
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const { loadConfig } = await import('../../config.js');
|
|
60
|
+
const config = await loadConfig(projectRoot);
|
|
61
|
+
return config.brain?.captureFiles ?? false;
|
|
62
|
+
} catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
42
67
|
/**
|
|
43
68
|
* Handle onFileChange - capture file changes to BRAIN
|
|
44
69
|
*
|
|
45
|
-
* Gated behind CLEO_BRAIN_CAPTURE_FILES
|
|
70
|
+
* Gated behind brain.captureFiles config or CLEO_BRAIN_CAPTURE_FILES env var.
|
|
71
|
+
* Env var takes precedence over config for backward compatibility.
|
|
46
72
|
* Deduplicates rapid writes to the same file within a 5-second window.
|
|
47
73
|
* Filters out .cleo/ internal files and test temp directories.
|
|
48
74
|
* Converts absolute paths to project-relative paths.
|
|
@@ -52,7 +78,7 @@ export async function handleFileChange(
|
|
|
52
78
|
payload: OnFileChangePayload,
|
|
53
79
|
): Promise<void> {
|
|
54
80
|
// Opt-in gate: disabled by default to prevent observation noise
|
|
55
|
-
if (
|
|
81
|
+
if (!(await isFileCaptureEnabled(projectRoot))) return;
|
|
56
82
|
|
|
57
83
|
// 5-second dedup
|
|
58
84
|
const now = Date.now();
|
|
@@ -11,6 +11,7 @@ import './task-hooks.js';
|
|
|
11
11
|
import './error-hooks.js';
|
|
12
12
|
import './file-hooks.js';
|
|
13
13
|
import './mcp-hooks.js';
|
|
14
|
+
import './work-capture-hooks.js';
|
|
14
15
|
|
|
15
16
|
export { handleError } from './error-hooks.js';
|
|
16
17
|
export { handleFileChange } from './file-hooks.js';
|
|
@@ -18,3 +19,4 @@ export { handlePromptSubmit, handleResponseComplete } from './mcp-hooks.js';
|
|
|
18
19
|
// Re-export handler functions for explicit use
|
|
19
20
|
export { handleSessionEnd, handleSessionStart } from './session-hooks.js';
|
|
20
21
|
export { handleToolComplete, handleToolStart } from './task-hooks.js';
|
|
22
|
+
export { handleWorkPromptSubmit, handleWorkResponseComplete } from './work-capture-hooks.js';
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP Prompt/Response Hook Handlers - Wave 2 of T5237
|
|
3
3
|
*
|
|
4
|
-
* Handlers for onPromptSubmit and onResponseComplete events
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Handlers for onPromptSubmit and onResponseComplete events that capture
|
|
5
|
+
* ALL gateway operations (read and write) to BRAIN.
|
|
6
|
+
* By default, NO brain capture (too noisy). Enable via:
|
|
7
|
+
* - Config: brain.captureMcp = true (checked first)
|
|
8
|
+
* - Env: CLEO_BRAIN_CAPTURE_MCP=true (overrides config)
|
|
7
9
|
* Auto-registers on module load.
|
|
8
10
|
*/
|
|
9
11
|
|
|
@@ -17,23 +19,39 @@ function isMissingBrainSchemaError(err: unknown): boolean {
|
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
|
-
* Check
|
|
21
|
-
*
|
|
22
|
+
* Check whether MCP-level brain capture is enabled.
|
|
23
|
+
*
|
|
24
|
+
* Resolution order (first truthy wins):
|
|
25
|
+
* 1. CLEO_BRAIN_CAPTURE_MCP env var (explicit override)
|
|
26
|
+
* 2. brain.captureMcp project config value
|
|
27
|
+
*
|
|
28
|
+
* Defaults to false when neither is set (too noisy for normal operation).
|
|
22
29
|
*/
|
|
23
|
-
function isBrainCaptureEnabled(): boolean {
|
|
24
|
-
|
|
30
|
+
async function isBrainCaptureEnabled(projectRoot: string): Promise<boolean> {
|
|
31
|
+
const envOverride = process.env['CLEO_BRAIN_CAPTURE_MCP'];
|
|
32
|
+
if (envOverride !== undefined) {
|
|
33
|
+
return envOverride === 'true';
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const { loadConfig } = await import('../../config.js');
|
|
37
|
+
const config = await loadConfig(projectRoot);
|
|
38
|
+
return config.brain?.captureMcp ?? false;
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
25
42
|
}
|
|
26
43
|
|
|
27
44
|
/**
|
|
28
|
-
* Handle onPromptSubmit - optionally capture prompt events to BRAIN
|
|
45
|
+
* Handle onPromptSubmit - optionally capture ALL gateway prompt events to BRAIN.
|
|
29
46
|
*
|
|
30
|
-
* No-op by default.
|
|
47
|
+
* No-op by default. Enable via brain.captureMcp config or CLEO_BRAIN_CAPTURE_MCP env.
|
|
48
|
+
* For selective mutation-only capture, use work-capture-hooks.ts instead.
|
|
31
49
|
*/
|
|
32
50
|
export async function handlePromptSubmit(
|
|
33
51
|
projectRoot: string,
|
|
34
52
|
payload: OnPromptSubmitPayload,
|
|
35
53
|
): Promise<void> {
|
|
36
|
-
if (!isBrainCaptureEnabled()) return;
|
|
54
|
+
if (!(await isBrainCaptureEnabled(projectRoot))) return;
|
|
37
55
|
|
|
38
56
|
const { observeBrain } = await import('../../memory/brain-retrieval.js');
|
|
39
57
|
|
|
@@ -50,15 +68,16 @@ export async function handlePromptSubmit(
|
|
|
50
68
|
}
|
|
51
69
|
|
|
52
70
|
/**
|
|
53
|
-
* Handle onResponseComplete - optionally capture response events to BRAIN
|
|
71
|
+
* Handle onResponseComplete - optionally capture ALL gateway response events to BRAIN.
|
|
54
72
|
*
|
|
55
|
-
* No-op by default.
|
|
73
|
+
* No-op by default. Enable via brain.captureMcp config or CLEO_BRAIN_CAPTURE_MCP env.
|
|
74
|
+
* For selective mutation-only capture, use work-capture-hooks.ts instead.
|
|
56
75
|
*/
|
|
57
76
|
export async function handleResponseComplete(
|
|
58
77
|
projectRoot: string,
|
|
59
78
|
payload: OnResponseCompletePayload,
|
|
60
79
|
): Promise<void> {
|
|
61
|
-
if (!isBrainCaptureEnabled()) return;
|
|
80
|
+
if (!(await isBrainCaptureEnabled(projectRoot))) return;
|
|
62
81
|
|
|
63
82
|
const { observeBrain } = await import('../../memory/brain-retrieval.js');
|
|
64
83
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared memory bridge refresh helper for hook handlers.
|
|
3
|
+
*
|
|
4
|
+
* Provides a config-gated, debounced wrapper around refreshMemoryBridge().
|
|
5
|
+
* Prevents rapid regeneration of .cleo/memory-bridge.md when multiple
|
|
6
|
+
* lifecycle events fire in quick succession (e.g. session end + task complete).
|
|
7
|
+
*
|
|
8
|
+
* Debounce: max one refresh per 30 seconds across all callers in the process.
|
|
9
|
+
*
|
|
10
|
+
* @task T138
|
|
11
|
+
* @epic T134
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Debounce window in milliseconds. */
|
|
15
|
+
const DEBOUNCE_MS = 30_000;
|
|
16
|
+
|
|
17
|
+
/** Timestamp of the last successful refresh call (module-level singleton). */
|
|
18
|
+
let lastRefreshTime = 0;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Refresh the memory bridge if autoRefresh is enabled and the debounce window
|
|
22
|
+
* has elapsed. Reads config via loadConfig() (cascaded). Never throws.
|
|
23
|
+
*
|
|
24
|
+
* @param projectRoot - Absolute path to the project root directory.
|
|
25
|
+
*/
|
|
26
|
+
export async function maybeRefreshMemoryBridge(projectRoot: string): Promise<void> {
|
|
27
|
+
try {
|
|
28
|
+
const { loadConfig } = await import('../../config.js');
|
|
29
|
+
const config = await loadConfig(projectRoot);
|
|
30
|
+
|
|
31
|
+
if (!config.brain?.memoryBridge?.autoRefresh) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
if (now - lastRefreshTime < DEBOUNCE_MS) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
lastRefreshTime = now;
|
|
41
|
+
|
|
42
|
+
const { refreshMemoryBridge } = await import('../../memory/memory-bridge.js');
|
|
43
|
+
await refreshMemoryBridge(projectRoot);
|
|
44
|
+
} catch {
|
|
45
|
+
// Best-effort: never block lifecycle events
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handlers that capture session lifecycle events to BRAIN via memory.observe.
|
|
5
5
|
* Auto-registers on module load.
|
|
6
|
+
*
|
|
7
|
+
* T138: Triggers memory bridge refresh on session start and end.
|
|
8
|
+
* T139: Regenerates bridge with session scope on start.
|
|
9
|
+
* T144: Extracts transcript observations on session end.
|
|
6
10
|
*/
|
|
7
11
|
|
|
8
12
|
import { hooks } from '../registry.js';
|
|
9
13
|
import type { OnSessionEndPayload, OnSessionStartPayload } from '../types.js';
|
|
14
|
+
import { maybeRefreshMemoryBridge } from './memory-bridge-refresh.js';
|
|
10
15
|
|
|
11
16
|
function isMissingBrainSchemaError(err: unknown): boolean {
|
|
12
17
|
if (!(err instanceof Error)) return false;
|
|
@@ -16,6 +21,9 @@ function isMissingBrainSchemaError(err: unknown): boolean {
|
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
23
|
* Handle onSessionStart - capture initial session context
|
|
24
|
+
*
|
|
25
|
+
* T138: Refresh memory bridge on session start.
|
|
26
|
+
* T139: Regenerate bridge with session scope context.
|
|
19
27
|
*/
|
|
20
28
|
export async function handleSessionStart(
|
|
21
29
|
projectRoot: string,
|
|
@@ -34,10 +42,16 @@ export async function handleSessionStart(
|
|
|
34
42
|
} catch (err) {
|
|
35
43
|
if (!isMissingBrainSchemaError(err)) throw err;
|
|
36
44
|
}
|
|
45
|
+
|
|
46
|
+
// T138/T139: Refresh memory bridge after session starts (best-effort)
|
|
47
|
+
await maybeRefreshMemoryBridge(projectRoot);
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
/**
|
|
40
51
|
* Handle onSessionEnd - capture session summary
|
|
52
|
+
*
|
|
53
|
+
* T138: Refresh memory bridge after session ends.
|
|
54
|
+
* T144: Extract transcript observations via cross-provider adapter.
|
|
41
55
|
*/
|
|
42
56
|
export async function handleSessionEnd(
|
|
43
57
|
projectRoot: string,
|
|
@@ -64,6 +78,30 @@ export async function handleSessionEnd(
|
|
|
64
78
|
} catch {
|
|
65
79
|
// Grading must never block session end
|
|
66
80
|
}
|
|
81
|
+
|
|
82
|
+
// T144: Cross-provider transcript extraction (best-effort)
|
|
83
|
+
try {
|
|
84
|
+
const { loadConfig } = await import('../../config.js');
|
|
85
|
+
const config = await loadConfig(projectRoot);
|
|
86
|
+
if (config.brain?.autoCapture) {
|
|
87
|
+
const { AdapterManager } = await import('../../adapters/index.js');
|
|
88
|
+
const manager = AdapterManager.getInstance(projectRoot);
|
|
89
|
+
const activeAdapter = manager.getActive();
|
|
90
|
+
const hookProvider = activeAdapter?.hooks;
|
|
91
|
+
if (hookProvider && typeof hookProvider.getTranscript === 'function') {
|
|
92
|
+
const transcript = await hookProvider.getTranscript(payload.sessionId, projectRoot);
|
|
93
|
+
if (transcript) {
|
|
94
|
+
const { extractFromTranscript } = await import('../../memory/auto-extract.js');
|
|
95
|
+
await extractFromTranscript(projectRoot, payload.sessionId, transcript);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
// Graceful no-op: transcript extraction must never block session end
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// T138: Refresh memory bridge after session ends (best-effort)
|
|
104
|
+
await maybeRefreshMemoryBridge(projectRoot);
|
|
67
105
|
}
|
|
68
106
|
|
|
69
107
|
// Register handlers on module load
|
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handlers that capture task lifecycle events to BRAIN via memory.observe.
|
|
5
5
|
* Auto-registers on module load.
|
|
6
|
+
*
|
|
7
|
+
* T138: Triggers memory bridge refresh after task completion.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
10
|
import { hooks } from '../registry.js';
|
|
9
11
|
import type { OnToolCompletePayload, OnToolStartPayload } from '../types.js';
|
|
12
|
+
import { maybeRefreshMemoryBridge } from './memory-bridge-refresh.js';
|
|
10
13
|
|
|
11
14
|
function isMissingBrainSchemaError(err: unknown): boolean {
|
|
12
15
|
if (!(err instanceof Error)) return false;
|
|
@@ -37,6 +40,8 @@ export async function handleToolStart(
|
|
|
37
40
|
|
|
38
41
|
/**
|
|
39
42
|
* Handle onToolComplete (maps to task.complete in CLEO)
|
|
43
|
+
*
|
|
44
|
+
* T138: Refresh memory bridge after task completion.
|
|
40
45
|
*/
|
|
41
46
|
export async function handleToolComplete(
|
|
42
47
|
projectRoot: string,
|
|
@@ -54,6 +59,9 @@ export async function handleToolComplete(
|
|
|
54
59
|
} catch (err) {
|
|
55
60
|
if (!isMissingBrainSchemaError(err)) throw err;
|
|
56
61
|
}
|
|
62
|
+
|
|
63
|
+
// T138: Refresh memory bridge after task completes (best-effort)
|
|
64
|
+
await maybeRefreshMemoryBridge(projectRoot);
|
|
57
65
|
}
|
|
58
66
|
|
|
59
67
|
// Register handlers
|