@cleocode/core 2026.4.98 → 2026.4.99
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/gc/daemon-entry.d.ts +15 -0
- package/dist/gc/daemon-entry.d.ts.map +1 -0
- package/dist/gc/daemon.d.ts +71 -0
- package/dist/gc/daemon.d.ts.map +1 -0
- package/dist/gc/index.d.ts +14 -0
- package/dist/gc/index.d.ts.map +1 -0
- package/dist/gc/runner.d.ts +132 -0
- package/dist/gc/runner.d.ts.map +1 -0
- package/dist/gc/state.d.ts +94 -0
- package/dist/gc/state.d.ts.map +1 -0
- package/dist/gc/transcript.d.ts +130 -0
- package/dist/gc/transcript.d.ts.map +1 -0
- package/dist/sentient/daemon-entry.d.ts +11 -0
- package/dist/sentient/daemon-entry.d.ts.map +1 -0
- package/dist/sentient/daemon.d.ts +160 -0
- package/dist/sentient/daemon.d.ts.map +1 -0
- package/dist/sentient/index.d.ts +18 -0
- package/dist/sentient/index.d.ts.map +1 -0
- package/dist/sentient/ingesters/brain-ingester.d.ts +44 -0
- package/dist/sentient/ingesters/brain-ingester.d.ts.map +1 -0
- package/dist/sentient/ingesters/nexus-ingester.d.ts +45 -0
- package/dist/sentient/ingesters/nexus-ingester.d.ts.map +1 -0
- package/dist/sentient/ingesters/test-ingester.d.ts +43 -0
- package/dist/sentient/ingesters/test-ingester.d.ts.map +1 -0
- package/dist/sentient/proposal-rate-limiter.d.ts +93 -0
- package/dist/sentient/proposal-rate-limiter.d.ts.map +1 -0
- package/dist/sentient/propose-tick.d.ts +105 -0
- package/dist/sentient/propose-tick.d.ts.map +1 -0
- package/dist/sentient/state.d.ts +143 -0
- package/dist/sentient/state.d.ts.map +1 -0
- package/dist/sentient/tick.d.ts +193 -0
- package/dist/sentient/tick.d.ts.map +1 -0
- package/package.json +76 -8
- package/src/gc/__tests__/runner.test.ts +367 -0
- package/src/gc/__tests__/state.test.ts +169 -0
- package/src/gc/__tests__/transcript.test.ts +371 -0
- package/src/gc/daemon-entry.ts +26 -0
- package/src/gc/daemon.ts +251 -0
- package/src/gc/index.ts +14 -0
- package/src/gc/runner.ts +378 -0
- package/src/gc/state.ts +140 -0
- package/src/gc/transcript.ts +380 -0
- package/src/sentient/__tests__/brain-ingester.test.ts +154 -0
- package/src/sentient/__tests__/daemon.test.ts +472 -0
- package/src/sentient/__tests__/dream-tick.test.ts +200 -0
- package/src/sentient/__tests__/nexus-ingester.test.ts +138 -0
- package/src/sentient/__tests__/proposal-rate-limiter.test.ts +247 -0
- package/src/sentient/__tests__/propose-tick.test.ts +296 -0
- package/src/sentient/__tests__/test-ingester.test.ts +104 -0
- package/src/sentient/daemon-entry.ts +20 -0
- package/src/sentient/daemon.ts +471 -0
- package/src/sentient/index.ts +18 -0
- package/src/sentient/ingesters/brain-ingester.ts +122 -0
- package/src/sentient/ingesters/nexus-ingester.ts +171 -0
- package/src/sentient/ingesters/test-ingester.ts +205 -0
- package/src/sentient/proposal-rate-limiter.ts +172 -0
- package/src/sentient/propose-tick.ts +415 -0
- package/src/sentient/state.ts +229 -0
- package/src/sentient/tick.ts +688 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GC Daemon Entry Point — Standalone script executed by `spawnGCDaemon()`.
|
|
3
|
+
*
|
|
4
|
+
* This script is spawned as a detached background process by `cleo daemon start`.
|
|
5
|
+
* It must NOT import from the main CLI shim (no citty, no commander). It only
|
|
6
|
+
* imports from the gc/ module subtree.
|
|
7
|
+
*
|
|
8
|
+
* The cleoDir is passed as argv[2] by `spawnGCDaemon()`.
|
|
9
|
+
*
|
|
10
|
+
* @see gc/daemon.ts for the spawn logic
|
|
11
|
+
* @task T731
|
|
12
|
+
* @epic T726
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=daemon-entry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-entry.d.ts","sourceRoot":"","sources":["../../src/gc/daemon-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GC Daemon — Sidecar background process for autonomous transcript cleanup.
|
|
3
|
+
*
|
|
4
|
+
* Architecture (Pattern B from T751 §2.2):
|
|
5
|
+
* - Spawned via `cleo daemon start` as a detached Node.js process
|
|
6
|
+
* - All three required flags: `detached: true`, file stdio, `child.unref()`
|
|
7
|
+
* - Persists across CLI invocations
|
|
8
|
+
* - Crash recovery via `.cleo/gc-state.json` startup-check
|
|
9
|
+
* - node-cron v4 for scheduling (zero runtime deps, cross-platform)
|
|
10
|
+
*
|
|
11
|
+
* Startup algorithm (systemd `Persistent=true` semantics in pure Node.js):
|
|
12
|
+
* 1. Read gc-state.json
|
|
13
|
+
* 2. If pendingPrune non-empty → resume deletion (crash recovery)
|
|
14
|
+
* 3. If lastRunAt null OR elapsed > 24h → run GC immediately (missed-run recovery)
|
|
15
|
+
* 4. Schedule future runs via node-cron (daily at 03:00 UTC)
|
|
16
|
+
* 5. Write daemonPid to state
|
|
17
|
+
*
|
|
18
|
+
* @see ADR-047 — Autonomous GC and Disk Safety
|
|
19
|
+
* @see T751 §2.2 for sidecar daemon pattern rationale
|
|
20
|
+
* @task T731
|
|
21
|
+
* @epic T726
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Bootstrap the GC daemon process.
|
|
25
|
+
*
|
|
26
|
+
* Performs crash recovery, missed-run recovery, and schedules future GC runs.
|
|
27
|
+
* This function runs in the long-lived daemon process.
|
|
28
|
+
*
|
|
29
|
+
* @param cleoDir - Absolute path to the `.cleo/` directory
|
|
30
|
+
*/
|
|
31
|
+
export declare function bootstrapDaemon(cleoDir: string): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Spawn the GC daemon as a detached background process.
|
|
34
|
+
*
|
|
35
|
+
* All three requirements from T751 §2.2 are met:
|
|
36
|
+
* 1. `detached: true` — process group leader (survives parent exit)
|
|
37
|
+
* 2. File stdio — stdout/stderr redirected to gc.log (not inherited)
|
|
38
|
+
* 3. `child.unref()` — parent CLI exits immediately
|
|
39
|
+
*
|
|
40
|
+
* @param cleoDir - Absolute path to the `.cleo/` directory
|
|
41
|
+
* @returns PID of the spawned daemon process
|
|
42
|
+
*/
|
|
43
|
+
export declare function spawnGCDaemon(cleoDir: string): Promise<number>;
|
|
44
|
+
/**
|
|
45
|
+
* Stop the GC daemon by sending SIGTERM to its PID.
|
|
46
|
+
*
|
|
47
|
+
* Uses `process.kill(pid, 0)` as a no-throw liveness probe before signalling.
|
|
48
|
+
*
|
|
49
|
+
* @param cleoDir - Absolute path to the `.cleo/` directory
|
|
50
|
+
* @returns `{ stopped: boolean; pid: number | null; reason: string }`
|
|
51
|
+
*/
|
|
52
|
+
export declare function stopGCDaemon(cleoDir: string): Promise<{
|
|
53
|
+
stopped: boolean;
|
|
54
|
+
pid: number | null;
|
|
55
|
+
reason: string;
|
|
56
|
+
}>;
|
|
57
|
+
/**
|
|
58
|
+
* Check whether the GC daemon is currently running.
|
|
59
|
+
*
|
|
60
|
+
* @param cleoDir - Absolute path to the `.cleo/` directory
|
|
61
|
+
* @returns `{ running: boolean; pid: number | null; startedAt: string | null }`
|
|
62
|
+
*/
|
|
63
|
+
export declare function getGCDaemonStatus(cleoDir: string): Promise<{
|
|
64
|
+
running: boolean;
|
|
65
|
+
pid: number | null;
|
|
66
|
+
startedAt: string | null;
|
|
67
|
+
lastRunAt: string | null;
|
|
68
|
+
lastDiskUsedPct: number | null;
|
|
69
|
+
escalationNeeded: boolean;
|
|
70
|
+
}>;
|
|
71
|
+
//# sourceMappingURL=daemon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/gc/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAyBH;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0DpE;AAMD;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgCpE;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAgCnE;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAChE,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC,CAsBD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @cleocode/core/gc — Autonomous GC daemon public API.
|
|
3
|
+
*
|
|
4
|
+
* Provides transcript cleanup, disk-pressure monitoring, GC state
|
|
5
|
+
* management, and daemon lifecycle (spawn/stop/status).
|
|
6
|
+
*
|
|
7
|
+
* @see ADR-047 — Autonomous GC and Disk Safety
|
|
8
|
+
* @package @cleocode/core
|
|
9
|
+
*/
|
|
10
|
+
export * from './daemon.js';
|
|
11
|
+
export * from './runner.js';
|
|
12
|
+
export * from './state.js';
|
|
13
|
+
export * from './transcript.js';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gc/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GC Runner — Core garbage collection logic for autonomous transcript cleanup.
|
|
3
|
+
*
|
|
4
|
+
* Performs disk-pressure-aware pruning of ephemeral transcript and temp files
|
|
5
|
+
* under `~/.claude/projects/` using the five-tier threshold model from T751.
|
|
6
|
+
*
|
|
7
|
+
* Retention policy (per ADR-047 and docs/specs/memory-architecture-spec.md §8):
|
|
8
|
+
* - `.temp/` files: 24h normal, 1h emergency
|
|
9
|
+
* - Transcript directories (agent-*.jsonl, tool-results/): 7d normal, 1d emergency
|
|
10
|
+
* - `.cleo/logs/`: 30d normal, 7d emergency
|
|
11
|
+
* - `.cleo/agent-outputs/*.md` (committed artifacts): NEVER auto-pruned
|
|
12
|
+
*
|
|
13
|
+
* Circuit breaker: if `ANTHROPIC_API_KEY` is absent AND no local model configured,
|
|
14
|
+
* skip extraction and only delete transcripts older than 30 days.
|
|
15
|
+
*
|
|
16
|
+
* @see ADR-047 — Autonomous GC and Disk Safety
|
|
17
|
+
* @see docs/specs/memory-architecture-spec.md §8
|
|
18
|
+
* @task T731
|
|
19
|
+
* @epic T726
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Disk usage percentage thresholds.
|
|
23
|
+
*
|
|
24
|
+
* Values mirror the five-tier model recommended by T751 research §3.2:
|
|
25
|
+
* - OK: < 70% — routine cleanup by age policy only
|
|
26
|
+
* - WATCH: 70-85% — log + schedule next GC sooner
|
|
27
|
+
* - WARN: 85-90% — log + set escalation flag for next CLI invocation
|
|
28
|
+
* - URGENT: 90-95% — auto-prune oldest transcripts immediately
|
|
29
|
+
* - EMERGENCY: ≥ 95% — auto-prune all transcripts > 1d, pause new writes
|
|
30
|
+
*/
|
|
31
|
+
export declare const DISK_THRESHOLDS: {
|
|
32
|
+
readonly WATCH: 70;
|
|
33
|
+
readonly WARN: 85;
|
|
34
|
+
readonly URGENT: 90;
|
|
35
|
+
readonly EMERGENCY: 95;
|
|
36
|
+
};
|
|
37
|
+
/** Human-readable tier labels. */
|
|
38
|
+
export type DiskTier = 'ok' | 'watch' | 'warn' | 'urgent' | 'emergency';
|
|
39
|
+
/**
|
|
40
|
+
* Result of a single GC run.
|
|
41
|
+
*/
|
|
42
|
+
export interface GCResult {
|
|
43
|
+
/** Disk usage percentage at time of GC run (0–100). */
|
|
44
|
+
diskUsedPct: number;
|
|
45
|
+
/** Disk tier classification. */
|
|
46
|
+
threshold: DiskTier;
|
|
47
|
+
/** Files pruned during this run. */
|
|
48
|
+
pruned: Array<{
|
|
49
|
+
path: string;
|
|
50
|
+
bytes: number;
|
|
51
|
+
}>;
|
|
52
|
+
/** Total bytes freed. */
|
|
53
|
+
bytesFreed: number;
|
|
54
|
+
/** Whether escalation flag was set (disk ≥ WARN). */
|
|
55
|
+
escalationSet: boolean;
|
|
56
|
+
/** Human-readable escalation reason (set when escalationSet=true). */
|
|
57
|
+
escalationReason: string | null;
|
|
58
|
+
/** ISO-8601 timestamp of run completion. */
|
|
59
|
+
completedAt: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Options for a GC run.
|
|
63
|
+
*/
|
|
64
|
+
export interface GCRunOptions {
|
|
65
|
+
/**
|
|
66
|
+
* Absolute path to the `.cleo/` directory (used for state file and disk check).
|
|
67
|
+
* Defaults to `~/.cleo`.
|
|
68
|
+
*/
|
|
69
|
+
cleoDir?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Override the default `~/.claude/projects/` scan directory.
|
|
72
|
+
* Primarily used in tests to point at a temp directory.
|
|
73
|
+
*/
|
|
74
|
+
projectsDir?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Paths from a previous crashed run to resume deletion from.
|
|
77
|
+
* Written to `pendingPrune` in gc-state.json BEFORE starting deletion.
|
|
78
|
+
*/
|
|
79
|
+
resumeFrom?: string[];
|
|
80
|
+
/**
|
|
81
|
+
* Dry-run mode: compute what would be pruned, but make zero filesystem changes.
|
|
82
|
+
*/
|
|
83
|
+
dryRun?: boolean;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Classify a disk usage percentage into a tier.
|
|
87
|
+
*
|
|
88
|
+
* @param pct - Disk usage percentage (0–100)
|
|
89
|
+
* @returns DiskTier
|
|
90
|
+
*/
|
|
91
|
+
export declare function classifyDiskTier(pct: number): DiskTier;
|
|
92
|
+
/**
|
|
93
|
+
* Compute retention threshold in milliseconds based on disk tier.
|
|
94
|
+
*
|
|
95
|
+
* Higher disk pressure → shorter retention → more aggressive pruning.
|
|
96
|
+
*
|
|
97
|
+
* @param tier - Current disk tier
|
|
98
|
+
* @returns Maximum age in milliseconds for transcript retention
|
|
99
|
+
*/
|
|
100
|
+
export declare function retentionMs(tier: DiskTier): number;
|
|
101
|
+
/**
|
|
102
|
+
* Get the size of a path in bytes (file or directory recursively).
|
|
103
|
+
* Returns 0 if the path does not exist.
|
|
104
|
+
*
|
|
105
|
+
* @param targetPath - Path to measure
|
|
106
|
+
* @returns Size in bytes
|
|
107
|
+
*/
|
|
108
|
+
export declare function getPathBytes(targetPath: string): Promise<number>;
|
|
109
|
+
/**
|
|
110
|
+
* Idempotently delete a path (file or directory).
|
|
111
|
+
*
|
|
112
|
+
* Silently ignores ENOENT — safe to call if path was already deleted.
|
|
113
|
+
* Uses `force: true` to suppress errors on missing paths.
|
|
114
|
+
*
|
|
115
|
+
* @param targetPath - Path to delete
|
|
116
|
+
*/
|
|
117
|
+
export declare function idempotentRm(targetPath: string): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Execute a GC run: check disk pressure, determine retention threshold,
|
|
120
|
+
* prune eligible transcript files, update gc-state.json.
|
|
121
|
+
*
|
|
122
|
+
* This function is idempotent and safe to call multiple times. Crash recovery
|
|
123
|
+
* is implemented via the `pendingPrune` field in gc-state.json:
|
|
124
|
+
* 1. Write paths to `pendingPrune` BEFORE starting deletion
|
|
125
|
+
* 2. Remove each path from `pendingPrune` AFTER successful deletion
|
|
126
|
+
* 3. Clear `pendingPrune` when the job completes
|
|
127
|
+
*
|
|
128
|
+
* @param opts - GC run options
|
|
129
|
+
* @returns GC run results
|
|
130
|
+
*/
|
|
131
|
+
export declare function runGC(opts?: GCRunOptions): Promise<GCResult>;
|
|
132
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/gc/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA4BH;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe;;;;;CAKlB,CAAC;AAEX,kCAAkC;AAClC,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,SAAS,EAAE,QAAQ,CAAC;IACpB,oCAAoC;IACpC,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,yBAAyB;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,aAAa,EAAE,OAAO,CAAC;IACvB,sEAAsE;IACtE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAMtD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAWlD;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAetE;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQpE;AA6ED;;;;;;;;;;;;GAYG;AACH,wBAAsB,KAAK,CAAC,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgGtE"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GC State — Persistent crash-recovery state for the autonomous GC daemon.
|
|
3
|
+
*
|
|
4
|
+
* Stored in `.cleo/gc-state.json` (plain JSON, not SQLite) to avoid
|
|
5
|
+
* SQLite WAL conflicts between the long-running daemon process and the
|
|
6
|
+
* main CLEO CLI process. Human-readable for debugging.
|
|
7
|
+
*
|
|
8
|
+
* The file is gitignored (see .gitignore §.cleo/ section) and created empty
|
|
9
|
+
* by `cleo init`. It is NOT included in `cleo backup restore` scope because
|
|
10
|
+
* it is ephemeral operational state — only the `daemonPid` and `lastRunAt`
|
|
11
|
+
* fields survive between process restarts.
|
|
12
|
+
*
|
|
13
|
+
* @see ADR-047 — Autonomous GC and Disk Safety
|
|
14
|
+
* @task T731
|
|
15
|
+
* @epic T726
|
|
16
|
+
*/
|
|
17
|
+
/** Schema version for gc-state.json. Bump on breaking field changes. */
|
|
18
|
+
export declare const GC_STATE_SCHEMA_VERSION: "1.0";
|
|
19
|
+
/**
|
|
20
|
+
* Persistent GC daemon state written to `.cleo/gc-state.json`.
|
|
21
|
+
*
|
|
22
|
+
* Design principles:
|
|
23
|
+
* - `pendingPrune` enables idempotent crash recovery: populate BEFORE deletion,
|
|
24
|
+
* clear each entry AFTER successful deletion, clear entirely when job completes.
|
|
25
|
+
* - `diskThresholdBreached` is a sticky flag: cleared only when disk drops
|
|
26
|
+
* below the WATCH tier (70%).
|
|
27
|
+
* - `escalationNeeded` is set by the daemon when disk is in WARN/URGENT range;
|
|
28
|
+
* cleared by the CLI after displaying the escalation banner.
|
|
29
|
+
*/
|
|
30
|
+
export interface GCState {
|
|
31
|
+
/** JSON schema version for forward-compatibility checks. */
|
|
32
|
+
schemaVersion: typeof GC_STATE_SCHEMA_VERSION;
|
|
33
|
+
/** ISO-8601 timestamp of last COMPLETED GC run. null = never run. */
|
|
34
|
+
lastRunAt: string | null;
|
|
35
|
+
/** Outcome of the last GC run. */
|
|
36
|
+
lastRunResult: 'success' | 'partial' | 'failed' | null;
|
|
37
|
+
/** Bytes freed in the last completed GC run. */
|
|
38
|
+
lastRunBytesFreed: number;
|
|
39
|
+
/**
|
|
40
|
+
* Paths queued for deletion but not yet deleted.
|
|
41
|
+
* Written BEFORE starting deletion; cleared entry-by-entry on success.
|
|
42
|
+
* Enables idempotent crash recovery on daemon restart.
|
|
43
|
+
*/
|
|
44
|
+
pendingPrune: string[] | null;
|
|
45
|
+
/** Number of consecutive GC failures. Triggers escalation banner after 3. */
|
|
46
|
+
consecutiveFailures: number;
|
|
47
|
+
/** Sticky flag: true when disk is ≥ WATCH tier (70%). Cleared when disk < 70%. */
|
|
48
|
+
diskThresholdBreached: boolean;
|
|
49
|
+
/** Current disk usage percentage (0–100) from the last GC run. */
|
|
50
|
+
lastDiskUsedPct: number | null;
|
|
51
|
+
/**
|
|
52
|
+
* Escalation banner flag. Set by daemon when disk is in WARN+ range.
|
|
53
|
+
* Cleared by CLI after displaying the banner to the user.
|
|
54
|
+
*/
|
|
55
|
+
escalationNeeded: boolean;
|
|
56
|
+
/** Escalation reason shown in the CLI banner. */
|
|
57
|
+
escalationReason: string | null;
|
|
58
|
+
/** PID of the currently running daemon process. null = daemon not running. */
|
|
59
|
+
daemonPid: number | null;
|
|
60
|
+
/** ISO-8601 timestamp when the daemon was last started. */
|
|
61
|
+
daemonStartedAt: string | null;
|
|
62
|
+
}
|
|
63
|
+
/** Default (empty) GC state for fresh initialisation. */
|
|
64
|
+
export declare const DEFAULT_GC_STATE: GCState;
|
|
65
|
+
/**
|
|
66
|
+
* Read the GC state from disk.
|
|
67
|
+
*
|
|
68
|
+
* Returns the default state if the file does not exist or is malformed.
|
|
69
|
+
* Never throws — GC state file absence is not an error condition.
|
|
70
|
+
*
|
|
71
|
+
* @param statePath - Absolute path to gc-state.json
|
|
72
|
+
* @returns Parsed GC state, merged with defaults for any missing fields
|
|
73
|
+
*/
|
|
74
|
+
export declare function readGCState(statePath: string): Promise<GCState>;
|
|
75
|
+
/**
|
|
76
|
+
* Write the GC state to disk atomically via tmp-then-rename.
|
|
77
|
+
*
|
|
78
|
+
* Atomic write prevents partial reads if the daemon crashes mid-write.
|
|
79
|
+
* Idempotent: safe to call multiple times.
|
|
80
|
+
*
|
|
81
|
+
* @param statePath - Absolute path to gc-state.json
|
|
82
|
+
* @param state - GC state to persist
|
|
83
|
+
*/
|
|
84
|
+
export declare function writeGCState(statePath: string, state: GCState): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Patch a subset of fields in the GC state file.
|
|
87
|
+
*
|
|
88
|
+
* Convenience wrapper: reads current state, merges patch, writes back.
|
|
89
|
+
*
|
|
90
|
+
* @param statePath - Absolute path to gc-state.json
|
|
91
|
+
* @param patch - Partial state to merge over the existing state
|
|
92
|
+
*/
|
|
93
|
+
export declare function patchGCState(statePath: string, patch: Partial<GCState>): Promise<GCState>;
|
|
94
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/gc/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,wEAAwE;AACxE,eAAO,MAAM,uBAAuB,EAAG,KAAc,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,OAAO;IACtB,4DAA4D;IAC5D,aAAa,EAAE,OAAO,uBAAuB,CAAC;IAC9C,qEAAqE;IACrE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,kCAAkC;IAClC,aAAa,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,IAAI,CAAC;IACvD,gDAAgD;IAChD,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC9B,6EAA6E;IAC7E,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kFAAkF;IAClF,qBAAqB,EAAE,OAAO,CAAC;IAC/B,kEAAkE;IAClE,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;;OAGG;IACH,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iDAAiD;IACjD,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,8EAA8E;IAC9E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,2DAA2D;IAC3D,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,yDAAyD;AACzD,eAAO,MAAM,gBAAgB,EAAE,OAa9B,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWrE;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CASnF;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAK/F"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcript Scanner — Inventory and age-classification of Claude session transcripts.
|
|
3
|
+
*
|
|
4
|
+
* Implements the hot/warm/cold three-tier model from memory-architecture-spec.md §6:
|
|
5
|
+
* - HOT (0–24h): Full JSONL retained; agents can re-read
|
|
6
|
+
* - WARM (1–7d): Pending extraction; scheduled at session end
|
|
7
|
+
* - COLD (>7d): brain.db entries only; raw JSONL deleted (tombstone in brain_obs)
|
|
8
|
+
*
|
|
9
|
+
* Storage layout scanned (§6.2):
|
|
10
|
+
* ```
|
|
11
|
+
* ~/.claude/projects/
|
|
12
|
+
* <project-slug>/
|
|
13
|
+
* <session-uuid>.jsonl ← root-level main session transcript
|
|
14
|
+
* <session-uuid>/ ← session UUID directory
|
|
15
|
+
* subagents/
|
|
16
|
+
* agent-<agentId>.jsonl ← subagent transcript
|
|
17
|
+
* agent-<agentId>.meta.json
|
|
18
|
+
* tool-results/
|
|
19
|
+
* <toolUseId>.json
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @see docs/specs/memory-architecture-spec.md §6.1–6.2
|
|
23
|
+
* @task T728
|
|
24
|
+
* @epic T726
|
|
25
|
+
*/
|
|
26
|
+
/** Hot/warm/cold lifecycle tier for a transcript session. */
|
|
27
|
+
export type TranscriptTier = 'hot' | 'warm' | 'cold';
|
|
28
|
+
/**
|
|
29
|
+
* Metadata for a single session transcript discovered on disk.
|
|
30
|
+
*/
|
|
31
|
+
export interface SessionInfo {
|
|
32
|
+
/** Absolute path to the root session JSONL file. */
|
|
33
|
+
jsonlPath: string;
|
|
34
|
+
/** Project slug (directory name under `~/.claude/projects/`). */
|
|
35
|
+
projectSlug: string;
|
|
36
|
+
/** Session UUID extracted from the JSONL filename. */
|
|
37
|
+
sessionId: string;
|
|
38
|
+
/** Last modified time of the JSONL file (ms since epoch). */
|
|
39
|
+
mtimeMs: number;
|
|
40
|
+
/** Age of the session in milliseconds. */
|
|
41
|
+
ageMs: number;
|
|
42
|
+
/** Lifecycle tier. */
|
|
43
|
+
tier: TranscriptTier;
|
|
44
|
+
/** Size in bytes of the root JSONL file. */
|
|
45
|
+
bytes: number;
|
|
46
|
+
/**
|
|
47
|
+
* Absolute path to the session UUID directory (if it exists).
|
|
48
|
+
* Contains `subagents/` and `tool-results/` subdirs.
|
|
49
|
+
*/
|
|
50
|
+
sessionDir: string | null;
|
|
51
|
+
/** Size in bytes of the session UUID directory (including subagents). */
|
|
52
|
+
sessionDirBytes: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Aggregate scan result: session inventory with tier-based grouping.
|
|
56
|
+
*/
|
|
57
|
+
export interface TranscriptScanResult {
|
|
58
|
+
/** Total number of sessions found. */
|
|
59
|
+
totalSessions: number;
|
|
60
|
+
/** HOT tier sessions (< 24h). */
|
|
61
|
+
hot: SessionInfo[];
|
|
62
|
+
/** WARM tier sessions (24h–7d). */
|
|
63
|
+
warm: SessionInfo[];
|
|
64
|
+
/** Total size of all discovered transcripts in bytes. */
|
|
65
|
+
totalBytes: number;
|
|
66
|
+
/** Absolute path to `~/.claude/projects/`. */
|
|
67
|
+
projectsDir: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Result of a transcript prune operation.
|
|
71
|
+
*/
|
|
72
|
+
export interface TranscriptPruneResult {
|
|
73
|
+
/** Number of sessions pruned. */
|
|
74
|
+
pruned: number;
|
|
75
|
+
/** Bytes freed. */
|
|
76
|
+
bytesFreed: number;
|
|
77
|
+
/** Paths that were deleted (or would be deleted in dry-run). */
|
|
78
|
+
deletedPaths: string[];
|
|
79
|
+
/** Whether this was a dry-run (no filesystem mutations). */
|
|
80
|
+
dryRun: boolean;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Classify a session age into a transcript tier.
|
|
84
|
+
*
|
|
85
|
+
* @param ageMs - Session age in milliseconds
|
|
86
|
+
* @returns Lifecycle tier
|
|
87
|
+
*/
|
|
88
|
+
export declare function classifyTranscriptTier(ageMs: number): TranscriptTier;
|
|
89
|
+
/**
|
|
90
|
+
* Scan `~/.claude/projects/` and return a structured inventory of all
|
|
91
|
+
* session transcripts, classified by hot/warm/cold tier.
|
|
92
|
+
*
|
|
93
|
+
* Does not modify any files. Safe to call at any time.
|
|
94
|
+
*
|
|
95
|
+
* @param projectsDir - Override the default `~/.claude/projects/` path (for testing)
|
|
96
|
+
* @returns Transcript scan result with tier-classified session list
|
|
97
|
+
*/
|
|
98
|
+
export declare function scanTranscripts(projectsDir?: string): Promise<TranscriptScanResult>;
|
|
99
|
+
/**
|
|
100
|
+
* Prune session transcripts older than `olderThanMs` milliseconds.
|
|
101
|
+
*
|
|
102
|
+
* Dry-run by default: pass `confirm: true` to perform actual deletion.
|
|
103
|
+
*
|
|
104
|
+
* Circuit breakers (from memory-architecture-spec.md §6.4):
|
|
105
|
+
* - If `ANTHROPIC_API_KEY` is absent, only delete sessions older than 30d
|
|
106
|
+
* (raw preservation fallback — skip extraction).
|
|
107
|
+
*
|
|
108
|
+
* @param opts - Prune options
|
|
109
|
+
* @param opts.olderThanMs - Delete sessions older than this many milliseconds
|
|
110
|
+
* @param opts.confirm - If true, perform actual deletion; dry-run if false
|
|
111
|
+
* @param opts.projectsDir - Override `~/.claude/projects/` (for testing)
|
|
112
|
+
* @returns Prune result with count, bytes freed, and deleted paths
|
|
113
|
+
*/
|
|
114
|
+
export declare function pruneTranscripts(opts: {
|
|
115
|
+
olderThanMs: number;
|
|
116
|
+
confirm: boolean;
|
|
117
|
+
projectsDir?: string;
|
|
118
|
+
}): Promise<TranscriptPruneResult>;
|
|
119
|
+
/**
|
|
120
|
+
* Parse a human-readable duration string into milliseconds.
|
|
121
|
+
*
|
|
122
|
+
* Supported formats: `7d`, `24h`, `30m`, `1d`, `14d`, `168h`, etc.
|
|
123
|
+
* Used by `cleo transcript prune --older-than <duration>`.
|
|
124
|
+
*
|
|
125
|
+
* @param duration - Duration string (e.g. `"7d"`, `"24h"`, `"30m"`)
|
|
126
|
+
* @returns Duration in milliseconds
|
|
127
|
+
* @throws Error if the format is not recognized
|
|
128
|
+
*/
|
|
129
|
+
export declare function parseDurationMs(duration: string): number;
|
|
130
|
+
//# sourceMappingURL=transcript.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../../src/gc/transcript.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAqBH,6DAA6D;AAC7D,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,EAAE,cAAc,CAAC;IACrB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,yEAAyE;IACzE,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,GAAG,EAAE,WAAW,EAAE,CAAC;IACnB,mCAAmC;IACnC,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,4DAA4D;IAC5D,MAAM,EAAE,OAAO,CAAC;CACjB;AAMD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAIpE;AAmBD;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAsFzF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAmGjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAcxD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentient Daemon Entry Point — spawned by `spawnSentientDaemon()`.
|
|
3
|
+
*
|
|
4
|
+
* Runs as a detached background process. Receives the project root as
|
|
5
|
+
* argv[2]. Does NOT import the CLI shim — only the sentient/ subtree.
|
|
6
|
+
*
|
|
7
|
+
* @see sentient/daemon.ts for spawn logic
|
|
8
|
+
* @task T946
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=daemon-entry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-entry.d.ts","sourceRoot":"","sources":["../../src/sentient/daemon-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentient Daemon — Tier-1 autonomous loop sidecar.
|
|
3
|
+
*
|
|
4
|
+
* Runs as a detached Node.js process and executes `runTick()` every 5
|
|
5
|
+
* minutes. Mirrors the gc/daemon.ts sidecar pattern (ADR-047) and honours
|
|
6
|
+
* the worktree protocol — all state lives under the project's `.cleo/`.
|
|
7
|
+
*
|
|
8
|
+
* Scoped IN (this module):
|
|
9
|
+
* - Tier-1 execution of unblocked tasks via `cleo orchestrate spawn`
|
|
10
|
+
* - Kill-switch with re-check at every tick checkpoint
|
|
11
|
+
* - Advisory locking via an OS-level lockfile (two daemons cannot coexist)
|
|
12
|
+
* - Stuck detection + self-pause on stuck-rate threshold
|
|
13
|
+
* - fs.watch-based fast kill propagation
|
|
14
|
+
*
|
|
15
|
+
* Scoped OUT (separate epics):
|
|
16
|
+
* - Tier-2 proposal queue (`cleo propose` / status='proposed' generation)
|
|
17
|
+
* - Tier-3 sandbox auto-merge (requires agent-in-container infra)
|
|
18
|
+
* - Ed25519 signing of receipts (handled by Agent B2 llmtxt/identity wiring)
|
|
19
|
+
*
|
|
20
|
+
* @see ADR-054 — Sentient Loop Tier-1
|
|
21
|
+
* @task T946
|
|
22
|
+
*/
|
|
23
|
+
import { type FileHandle } from 'node:fs/promises';
|
|
24
|
+
import { type SentientState } from './state.js';
|
|
25
|
+
/** Relative subpath under a project root where sentient state lives. */
|
|
26
|
+
export declare const SENTIENT_STATE_FILE: ".cleo/sentient-state.json";
|
|
27
|
+
/** Relative subpath for the daemon lockfile. */
|
|
28
|
+
export declare const SENTIENT_LOCK_FILE: ".cleo/sentient.lock";
|
|
29
|
+
/** Cron expression: every 5 minutes (Tier-1 tick). */
|
|
30
|
+
export declare const SENTIENT_CRON_EXPR = "*/5 * * * *";
|
|
31
|
+
/**
|
|
32
|
+
* Cron expression: every 2 hours (Tier-2 propose tick).
|
|
33
|
+
*
|
|
34
|
+
* Separate from the Tier-1 cron to avoid proposal flooding.
|
|
35
|
+
* Only fires when `tier2Enabled = true` in sentient-state.json.
|
|
36
|
+
*
|
|
37
|
+
* @task T1008
|
|
38
|
+
*/
|
|
39
|
+
export declare const SENTIENT_PROPOSE_CRON_EXPR = "0 */2 * * *";
|
|
40
|
+
/** Subdirectory for daemon logs. */
|
|
41
|
+
export declare const SENTIENT_LOG_DIR: ".cleo/logs";
|
|
42
|
+
/** Log filename (stdout). */
|
|
43
|
+
export declare const SENTIENT_LOG: "sentient.log";
|
|
44
|
+
/** Log filename (stderr). */
|
|
45
|
+
export declare const SENTIENT_ERR: "sentient.err";
|
|
46
|
+
/** Handle to an active advisory lock. */
|
|
47
|
+
export interface LockHandle {
|
|
48
|
+
/** Absolute path to the lockfile. */
|
|
49
|
+
path: string;
|
|
50
|
+
/** Underlying file handle held exclusively by this process. */
|
|
51
|
+
handle: FileHandle;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Acquire an exclusive advisory lock on the sentient lockfile.
|
|
55
|
+
*
|
|
56
|
+
* Uses `fs.open` with `O_CREAT | O_EXCL` semantics — if the file already
|
|
57
|
+
* exists AND its recorded pid is alive, acquisition fails. Stale lockfiles
|
|
58
|
+
* (pid dead) are reclaimed automatically.
|
|
59
|
+
*
|
|
60
|
+
* @param lockPath - Absolute path to `.cleo/sentient.lock`
|
|
61
|
+
* @returns Lock handle, or null if lock is held by a live process
|
|
62
|
+
*/
|
|
63
|
+
export declare function acquireLock(lockPath: string): Promise<LockHandle | null>;
|
|
64
|
+
/**
|
|
65
|
+
* Release an advisory lock acquired via {@link acquireLock}.
|
|
66
|
+
* Does NOT remove the lockfile — the pid inside is a useful diagnostic.
|
|
67
|
+
*/
|
|
68
|
+
export declare function releaseLock(lock: LockHandle): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Bootstrap the sentient daemon process.
|
|
71
|
+
*
|
|
72
|
+
* Steps:
|
|
73
|
+
* 1. Acquire advisory lock (fail fast if another daemon is running)
|
|
74
|
+
* 2. Persist our pid + startedAt to state.json
|
|
75
|
+
* 3. Watch state.json for killSwitch changes (fast propagation)
|
|
76
|
+
* 4. Register a SIGTERM handler for graceful shutdown
|
|
77
|
+
* 5. Schedule cron with noOverlap so long ticks don't stack
|
|
78
|
+
*
|
|
79
|
+
* @param projectRoot - Absolute path to the project (contains `.cleo/`)
|
|
80
|
+
*/
|
|
81
|
+
export declare function bootstrapDaemon(projectRoot: string): Promise<void>;
|
|
82
|
+
/** Outcome of {@link spawnSentientDaemon}. */
|
|
83
|
+
export interface SpawnDaemonResult {
|
|
84
|
+
/** PID of the spawned daemon. */
|
|
85
|
+
pid: number;
|
|
86
|
+
/** Absolute path to the .cleo/sentient-state.json file. */
|
|
87
|
+
statePath: string;
|
|
88
|
+
/** Absolute path to the log file. */
|
|
89
|
+
logPath: string;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Spawn the sentient daemon as a detached background process.
|
|
93
|
+
*
|
|
94
|
+
* All three T751 §2.2 requirements:
|
|
95
|
+
* 1. `detached: true` — process-group leader survives parent exit
|
|
96
|
+
* 2. File-based stdio — no TTY inheritance
|
|
97
|
+
* 3. `child.unref()` — parent CLI returns immediately
|
|
98
|
+
*
|
|
99
|
+
* @param projectRoot - Absolute path to the project root (contains `.cleo/`)
|
|
100
|
+
* @returns PID + log paths
|
|
101
|
+
*/
|
|
102
|
+
export declare function spawnSentientDaemon(projectRoot: string): Promise<SpawnDaemonResult>;
|
|
103
|
+
/** Outcome of {@link stopSentientDaemon}. */
|
|
104
|
+
export interface StopDaemonResult {
|
|
105
|
+
/** Whether the stop signal was delivered. */
|
|
106
|
+
stopped: boolean;
|
|
107
|
+
/** Last known pid; null if no pid was recorded. */
|
|
108
|
+
pid: number | null;
|
|
109
|
+
/** Human-readable reason. */
|
|
110
|
+
reason: string;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Stop the sentient daemon.
|
|
114
|
+
*
|
|
115
|
+
* Flips killSwitch=true FIRST (so an in-flight tick notices on its next
|
|
116
|
+
* checkpoint re-read), then sends SIGTERM. This gives the daemon a fast,
|
|
117
|
+
* graceful shutdown path even during a long-running spawn.
|
|
118
|
+
*
|
|
119
|
+
* @param projectRoot - Absolute path to the project root
|
|
120
|
+
* @param reason - Optional reason stored on state file for diagnostics
|
|
121
|
+
* @returns Stop result
|
|
122
|
+
*/
|
|
123
|
+
export declare function stopSentientDaemon(projectRoot: string, reason?: string): Promise<StopDaemonResult>;
|
|
124
|
+
/**
|
|
125
|
+
* Clear the kill switch so the cron schedule resumes executing ticks.
|
|
126
|
+
*
|
|
127
|
+
* Does NOT restart the daemon process — that is the caller's responsibility
|
|
128
|
+
* via `cleo sentient start` if the process itself exited.
|
|
129
|
+
*
|
|
130
|
+
* @param projectRoot - Absolute path to the project root
|
|
131
|
+
*/
|
|
132
|
+
export declare function resumeSentientDaemon(projectRoot: string): Promise<SentientState>;
|
|
133
|
+
/** Status snapshot returned by {@link getSentientDaemonStatus}. */
|
|
134
|
+
export interface SentientStatus {
|
|
135
|
+
/** Whether the pid on file is currently alive. */
|
|
136
|
+
running: boolean;
|
|
137
|
+
/** Recorded pid (null when never started or cleared on stop). */
|
|
138
|
+
pid: number | null;
|
|
139
|
+
/** ISO-8601 timestamp of last start. */
|
|
140
|
+
startedAt: string | null;
|
|
141
|
+
/** ISO-8601 timestamp of the last completed tick. */
|
|
142
|
+
lastTickAt: string | null;
|
|
143
|
+
/** Kill-switch state. */
|
|
144
|
+
killSwitch: boolean;
|
|
145
|
+
/** Reason supplied with the last kill. */
|
|
146
|
+
killSwitchReason: string | null;
|
|
147
|
+
/** Rolling stats. */
|
|
148
|
+
stats: SentientState['stats'];
|
|
149
|
+
/** Number of currently-stuck tasks. */
|
|
150
|
+
stuckCount: number;
|
|
151
|
+
/** Currently active task id (set mid-tick). */
|
|
152
|
+
activeTaskId: string | null;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Return a diagnostic snapshot for `cleo sentient status`.
|
|
156
|
+
*
|
|
157
|
+
* @param projectRoot - Absolute path to the project root
|
|
158
|
+
*/
|
|
159
|
+
export declare function getSentientDaemonStatus(projectRoot: string): Promise<SentientStatus>;
|
|
160
|
+
//# sourceMappingURL=daemon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/sentient/daemon.ts"],"names":[],"mappings":"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"}
|