clementine-agent 1.0.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 (190) hide show
  1. package/.env.example +44 -0
  2. package/LICENSE +21 -0
  3. package/README.md +795 -0
  4. package/dist/agent/agent-manager.d.ts +69 -0
  5. package/dist/agent/agent-manager.js +441 -0
  6. package/dist/agent/assistant.d.ts +225 -0
  7. package/dist/agent/assistant.js +3888 -0
  8. package/dist/agent/auto-update.d.ts +32 -0
  9. package/dist/agent/auto-update.js +186 -0
  10. package/dist/agent/daily-planner.d.ts +24 -0
  11. package/dist/agent/daily-planner.js +379 -0
  12. package/dist/agent/execution-advisor.d.ts +10 -0
  13. package/dist/agent/execution-advisor.js +272 -0
  14. package/dist/agent/hooks.d.ts +45 -0
  15. package/dist/agent/hooks.js +564 -0
  16. package/dist/agent/insight-engine.d.ts +66 -0
  17. package/dist/agent/insight-engine.js +225 -0
  18. package/dist/agent/intent-classifier.d.ts +48 -0
  19. package/dist/agent/intent-classifier.js +214 -0
  20. package/dist/agent/link-extractor.d.ts +19 -0
  21. package/dist/agent/link-extractor.js +90 -0
  22. package/dist/agent/mcp-bridge.d.ts +62 -0
  23. package/dist/agent/mcp-bridge.js +435 -0
  24. package/dist/agent/metacognition.d.ts +66 -0
  25. package/dist/agent/metacognition.js +221 -0
  26. package/dist/agent/orchestrator.d.ts +81 -0
  27. package/dist/agent/orchestrator.js +790 -0
  28. package/dist/agent/profiles.d.ts +22 -0
  29. package/dist/agent/profiles.js +91 -0
  30. package/dist/agent/prompt-cache.d.ts +24 -0
  31. package/dist/agent/prompt-cache.js +68 -0
  32. package/dist/agent/prompt-evolver.d.ts +28 -0
  33. package/dist/agent/prompt-evolver.js +279 -0
  34. package/dist/agent/role-scaffolds.d.ts +28 -0
  35. package/dist/agent/role-scaffolds.js +433 -0
  36. package/dist/agent/safe-restart.d.ts +41 -0
  37. package/dist/agent/safe-restart.js +150 -0
  38. package/dist/agent/self-improve.d.ts +66 -0
  39. package/dist/agent/self-improve.js +1706 -0
  40. package/dist/agent/session-event-log.d.ts +114 -0
  41. package/dist/agent/session-event-log.js +233 -0
  42. package/dist/agent/skill-extractor.d.ts +72 -0
  43. package/dist/agent/skill-extractor.js +435 -0
  44. package/dist/agent/source-mods.d.ts +61 -0
  45. package/dist/agent/source-mods.js +230 -0
  46. package/dist/agent/source-preflight.d.ts +25 -0
  47. package/dist/agent/source-preflight.js +100 -0
  48. package/dist/agent/stall-guard.d.ts +62 -0
  49. package/dist/agent/stall-guard.js +109 -0
  50. package/dist/agent/strategic-planner.d.ts +60 -0
  51. package/dist/agent/strategic-planner.js +352 -0
  52. package/dist/agent/team-bus.d.ts +89 -0
  53. package/dist/agent/team-bus.js +556 -0
  54. package/dist/agent/team-router.d.ts +26 -0
  55. package/dist/agent/team-router.js +37 -0
  56. package/dist/agent/tool-loop-detector.d.ts +59 -0
  57. package/dist/agent/tool-loop-detector.js +242 -0
  58. package/dist/agent/workflow-runner.d.ts +36 -0
  59. package/dist/agent/workflow-runner.js +317 -0
  60. package/dist/agent/workflow-variables.d.ts +16 -0
  61. package/dist/agent/workflow-variables.js +62 -0
  62. package/dist/channels/discord-agent-bot.d.ts +101 -0
  63. package/dist/channels/discord-agent-bot.js +881 -0
  64. package/dist/channels/discord-bot-manager.d.ts +80 -0
  65. package/dist/channels/discord-bot-manager.js +262 -0
  66. package/dist/channels/discord-utils.d.ts +51 -0
  67. package/dist/channels/discord-utils.js +293 -0
  68. package/dist/channels/discord.d.ts +12 -0
  69. package/dist/channels/discord.js +1832 -0
  70. package/dist/channels/slack-agent-bot.d.ts +73 -0
  71. package/dist/channels/slack-agent-bot.js +320 -0
  72. package/dist/channels/slack-bot-manager.d.ts +66 -0
  73. package/dist/channels/slack-bot-manager.js +236 -0
  74. package/dist/channels/slack-utils.d.ts +39 -0
  75. package/dist/channels/slack-utils.js +189 -0
  76. package/dist/channels/slack.d.ts +11 -0
  77. package/dist/channels/slack.js +196 -0
  78. package/dist/channels/telegram.d.ts +10 -0
  79. package/dist/channels/telegram.js +235 -0
  80. package/dist/channels/webhook.d.ts +9 -0
  81. package/dist/channels/webhook.js +78 -0
  82. package/dist/channels/whatsapp.d.ts +11 -0
  83. package/dist/channels/whatsapp.js +181 -0
  84. package/dist/cli/chat.d.ts +14 -0
  85. package/dist/cli/chat.js +220 -0
  86. package/dist/cli/cron.d.ts +17 -0
  87. package/dist/cli/cron.js +552 -0
  88. package/dist/cli/dashboard.d.ts +15 -0
  89. package/dist/cli/dashboard.js +17677 -0
  90. package/dist/cli/index.d.ts +3 -0
  91. package/dist/cli/index.js +2474 -0
  92. package/dist/cli/routes/delegations.d.ts +19 -0
  93. package/dist/cli/routes/delegations.js +154 -0
  94. package/dist/cli/routes/digest.d.ts +17 -0
  95. package/dist/cli/routes/digest.js +375 -0
  96. package/dist/cli/routes/goals.d.ts +14 -0
  97. package/dist/cli/routes/goals.js +258 -0
  98. package/dist/cli/routes/workflows.d.ts +18 -0
  99. package/dist/cli/routes/workflows.js +97 -0
  100. package/dist/cli/setup.d.ts +8 -0
  101. package/dist/cli/setup.js +619 -0
  102. package/dist/cli/tunnel.d.ts +35 -0
  103. package/dist/cli/tunnel.js +141 -0
  104. package/dist/config.d.ts +145 -0
  105. package/dist/config.js +278 -0
  106. package/dist/events/bus.d.ts +43 -0
  107. package/dist/events/bus.js +136 -0
  108. package/dist/gateway/cron-scheduler.d.ts +166 -0
  109. package/dist/gateway/cron-scheduler.js +1767 -0
  110. package/dist/gateway/delivery-queue.d.ts +30 -0
  111. package/dist/gateway/delivery-queue.js +110 -0
  112. package/dist/gateway/heartbeat-scheduler.d.ts +99 -0
  113. package/dist/gateway/heartbeat-scheduler.js +1298 -0
  114. package/dist/gateway/heartbeat.d.ts +3 -0
  115. package/dist/gateway/heartbeat.js +3 -0
  116. package/dist/gateway/lanes.d.ts +24 -0
  117. package/dist/gateway/lanes.js +76 -0
  118. package/dist/gateway/notifications.d.ts +29 -0
  119. package/dist/gateway/notifications.js +75 -0
  120. package/dist/gateway/router.d.ts +210 -0
  121. package/dist/gateway/router.js +1330 -0
  122. package/dist/index.d.ts +12 -0
  123. package/dist/index.js +1015 -0
  124. package/dist/memory/chunker.d.ts +28 -0
  125. package/dist/memory/chunker.js +226 -0
  126. package/dist/memory/consolidation.d.ts +44 -0
  127. package/dist/memory/consolidation.js +171 -0
  128. package/dist/memory/context-assembler.d.ts +50 -0
  129. package/dist/memory/context-assembler.js +149 -0
  130. package/dist/memory/embeddings.d.ts +38 -0
  131. package/dist/memory/embeddings.js +180 -0
  132. package/dist/memory/graph-store.d.ts +66 -0
  133. package/dist/memory/graph-store.js +613 -0
  134. package/dist/memory/mmr.d.ts +21 -0
  135. package/dist/memory/mmr.js +75 -0
  136. package/dist/memory/search.d.ts +26 -0
  137. package/dist/memory/search.js +67 -0
  138. package/dist/memory/store.d.ts +530 -0
  139. package/dist/memory/store.js +2022 -0
  140. package/dist/security/integrity.d.ts +24 -0
  141. package/dist/security/integrity.js +58 -0
  142. package/dist/security/patterns.d.ts +34 -0
  143. package/dist/security/patterns.js +110 -0
  144. package/dist/security/scanner.d.ts +32 -0
  145. package/dist/security/scanner.js +263 -0
  146. package/dist/tools/admin-tools.d.ts +12 -0
  147. package/dist/tools/admin-tools.js +1278 -0
  148. package/dist/tools/external-tools.d.ts +11 -0
  149. package/dist/tools/external-tools.js +1327 -0
  150. package/dist/tools/goal-tools.d.ts +9 -0
  151. package/dist/tools/goal-tools.js +159 -0
  152. package/dist/tools/mcp-server.d.ts +13 -0
  153. package/dist/tools/mcp-server.js +141 -0
  154. package/dist/tools/memory-tools.d.ts +10 -0
  155. package/dist/tools/memory-tools.js +568 -0
  156. package/dist/tools/session-tools.d.ts +6 -0
  157. package/dist/tools/session-tools.js +146 -0
  158. package/dist/tools/shared.d.ts +216 -0
  159. package/dist/tools/shared.js +340 -0
  160. package/dist/tools/team-tools.d.ts +6 -0
  161. package/dist/tools/team-tools.js +447 -0
  162. package/dist/tools/tool-meta.d.ts +34 -0
  163. package/dist/tools/tool-meta.js +133 -0
  164. package/dist/tools/vault-tools.d.ts +8 -0
  165. package/dist/tools/vault-tools.js +457 -0
  166. package/dist/types.d.ts +716 -0
  167. package/dist/types.js +16 -0
  168. package/dist/vault-migrations/0001-add-execution-framework.d.ts +10 -0
  169. package/dist/vault-migrations/0001-add-execution-framework.js +47 -0
  170. package/dist/vault-migrations/0002-add-agentic-communication.d.ts +12 -0
  171. package/dist/vault-migrations/0002-add-agentic-communication.js +79 -0
  172. package/dist/vault-migrations/0003-update-execution-pipeline-narration.d.ts +11 -0
  173. package/dist/vault-migrations/0003-update-execution-pipeline-narration.js +73 -0
  174. package/dist/vault-migrations/helpers.d.ts +14 -0
  175. package/dist/vault-migrations/helpers.js +44 -0
  176. package/dist/vault-migrations/runner.d.ts +14 -0
  177. package/dist/vault-migrations/runner.js +139 -0
  178. package/dist/vault-migrations/types.d.ts +42 -0
  179. package/dist/vault-migrations/types.js +9 -0
  180. package/install.sh +320 -0
  181. package/package.json +84 -0
  182. package/scripts/postinstall.js +125 -0
  183. package/vault/00-System/AGENTS.md +66 -0
  184. package/vault/00-System/CRON.md +71 -0
  185. package/vault/00-System/HEARTBEAT.md +58 -0
  186. package/vault/00-System/MEMORY.md +16 -0
  187. package/vault/00-System/SOUL.md +96 -0
  188. package/vault/05-Tasks/TASKS.md +19 -0
  189. package/vault/06-Templates/_Daily-Template.md +28 -0
  190. package/vault/06-Templates/_People-Template.md +22 -0
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Structured Session Event Log — Append-only JSONL event log for agent sessions.
3
+ *
4
+ * Inspired by Anthropic's managed agents architecture: durable state stored
5
+ * outside the context window, enabling crash recovery and session replay.
6
+ *
7
+ * Events: tool_call, tool_result, checkpoint, phase_start, phase_end,
8
+ * error, decision, compaction, query_start, query_end.
9
+ */
10
+ export type SessionEventType = 'query_start' | 'query_end' | 'tool_call' | 'tool_result' | 'checkpoint' | 'phase_start' | 'phase_end' | 'error' | 'decision' | 'compaction';
11
+ export interface SessionEvent {
12
+ type: SessionEventType;
13
+ timestamp: string;
14
+ sessionKey: string;
15
+ data: Record<string, unknown>;
16
+ }
17
+ export interface QueryStartEvent extends SessionEvent {
18
+ type: 'query_start';
19
+ data: {
20
+ prompt: string;
21
+ model?: string;
22
+ source?: string;
23
+ };
24
+ }
25
+ export interface QueryEndEvent extends SessionEvent {
26
+ type: 'query_end';
27
+ data: {
28
+ responseLength: number;
29
+ sessionId?: string;
30
+ terminalReason?: string;
31
+ durationMs: number;
32
+ };
33
+ }
34
+ export interface ToolCallEvent extends SessionEvent {
35
+ type: 'tool_call';
36
+ data: {
37
+ tool: string;
38
+ input: Record<string, unknown>;
39
+ toolUseId?: string;
40
+ };
41
+ }
42
+ export interface CheckpointEvent extends SessionEvent {
43
+ type: 'checkpoint';
44
+ data: {
45
+ summary: string;
46
+ completed?: string[];
47
+ remaining?: string[];
48
+ artifacts?: string[];
49
+ };
50
+ }
51
+ export interface PhaseEvent extends SessionEvent {
52
+ type: 'phase_start' | 'phase_end';
53
+ data: {
54
+ phase: number;
55
+ jobName?: string;
56
+ [key: string]: unknown;
57
+ };
58
+ }
59
+ export interface ErrorEvent extends SessionEvent {
60
+ type: 'error';
61
+ data: {
62
+ message: string;
63
+ code?: string;
64
+ recoverable?: boolean;
65
+ };
66
+ }
67
+ export declare class EventLog {
68
+ private dir;
69
+ constructor(dir?: string);
70
+ /** Sanitize session key for use as a filename. */
71
+ private keyToFile;
72
+ /** Append an event to the session log. */
73
+ emit(sessionKey: string, type: SessionEventType, data: Record<string, unknown>): void;
74
+ emitQueryStart(sessionKey: string, prompt: string, opts?: {
75
+ model?: string;
76
+ source?: string;
77
+ }): void;
78
+ emitQueryEnd(sessionKey: string, opts: {
79
+ responseLength: number;
80
+ sessionId?: string;
81
+ terminalReason?: string;
82
+ durationMs: number;
83
+ }): void;
84
+ emitToolCall(sessionKey: string, tool: string, input: Record<string, unknown>, toolUseId?: string): void;
85
+ emitCheckpoint(sessionKey: string, summary: string, opts?: {
86
+ completed?: string[];
87
+ remaining?: string[];
88
+ artifacts?: string[];
89
+ }): void;
90
+ emitPhaseStart(sessionKey: string, phase: number, jobName?: string): void;
91
+ emitPhaseEnd(sessionKey: string, phase: number, opts?: Record<string, unknown>): void;
92
+ emitError(sessionKey: string, message: string, opts?: {
93
+ code?: string;
94
+ recoverable?: boolean;
95
+ }): void;
96
+ /** Get all events for a session. */
97
+ getEvents(sessionKey: string, opts?: {
98
+ type?: SessionEventType;
99
+ limit?: number;
100
+ since?: string;
101
+ }): SessionEvent[];
102
+ /** Get the last checkpoint for a session. */
103
+ getLastCheckpoint(sessionKey: string): CheckpointEvent | null;
104
+ /** Get recovery context for a crashed session — summarize what happened. */
105
+ getRecoveryContext(sessionKey: string): string | null;
106
+ /** Check if a session has an unfinished query (for crash recovery). */
107
+ hasOrphanedQuery(sessionKey: string): boolean;
108
+ /** Get tool usage stats for a session. */
109
+ getToolStats(sessionKey: string): Record<string, number>;
110
+ /** Clean up old session logs (older than maxAge days). */
111
+ cleanup(maxAgeDays?: number): number;
112
+ }
113
+ export declare function getEventLog(): EventLog;
114
+ //# sourceMappingURL=session-event-log.d.ts.map
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Structured Session Event Log — Append-only JSONL event log for agent sessions.
3
+ *
4
+ * Inspired by Anthropic's managed agents architecture: durable state stored
5
+ * outside the context window, enabling crash recovery and session replay.
6
+ *
7
+ * Events: tool_call, tool_result, checkpoint, phase_start, phase_end,
8
+ * error, decision, compaction, query_start, query_end.
9
+ */
10
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, unlinkSync } from 'node:fs';
11
+ import path from 'node:path';
12
+ import pino from 'pino';
13
+ import { BASE_DIR } from '../config.js';
14
+ const logger = pino({ name: 'clementine.event-log' });
15
+ const SESSIONS_DIR = path.join(BASE_DIR, 'sessions');
16
+ const MAX_LOG_SIZE = 10 * 1024 * 1024; // 10MB per session log
17
+ // ── EventLog Class ─────────────────────────────────────────────────
18
+ export class EventLog {
19
+ dir;
20
+ constructor(dir) {
21
+ this.dir = dir ?? SESSIONS_DIR;
22
+ if (!existsSync(this.dir)) {
23
+ mkdirSync(this.dir, { recursive: true });
24
+ }
25
+ }
26
+ /** Sanitize session key for use as a filename. */
27
+ keyToFile(sessionKey) {
28
+ const safe = sessionKey.replace(/[^a-zA-Z0-9._:-]/g, '_').slice(0, 120);
29
+ return path.join(this.dir, `${safe}.jsonl`);
30
+ }
31
+ /** Append an event to the session log. */
32
+ emit(sessionKey, type, data) {
33
+ try {
34
+ const event = {
35
+ type,
36
+ timestamp: new Date().toISOString(),
37
+ sessionKey,
38
+ data,
39
+ };
40
+ const filePath = this.keyToFile(sessionKey);
41
+ // Size guard — rotate if too large
42
+ if (existsSync(filePath)) {
43
+ try {
44
+ const { statSync } = require('node:fs');
45
+ const stat = statSync(filePath);
46
+ if (stat.size > MAX_LOG_SIZE) {
47
+ const bakPath = filePath + '.bak';
48
+ if (existsSync(bakPath))
49
+ unlinkSync(bakPath);
50
+ require('node:fs').renameSync(filePath, bakPath);
51
+ }
52
+ }
53
+ catch { /* non-fatal */ }
54
+ }
55
+ appendFileSync(filePath, JSON.stringify(event) + '\n');
56
+ }
57
+ catch (err) {
58
+ logger.debug({ err, sessionKey, type }, 'Failed to emit event');
59
+ }
60
+ }
61
+ // ── Convenience emitters ─────────────────────────────────────────
62
+ emitQueryStart(sessionKey, prompt, opts) {
63
+ this.emit(sessionKey, 'query_start', {
64
+ prompt: prompt.slice(0, 500),
65
+ model: opts?.model,
66
+ source: opts?.source,
67
+ });
68
+ }
69
+ emitQueryEnd(sessionKey, opts) {
70
+ this.emit(sessionKey, 'query_end', opts);
71
+ }
72
+ emitToolCall(sessionKey, tool, input, toolUseId) {
73
+ // Truncate large inputs to keep log manageable
74
+ const truncatedInput = {};
75
+ for (const [k, v] of Object.entries(input)) {
76
+ const val = typeof v === 'string' && v.length > 500 ? v.slice(0, 500) + '...' : v;
77
+ truncatedInput[k] = val;
78
+ }
79
+ this.emit(sessionKey, 'tool_call', { tool, input: truncatedInput, toolUseId });
80
+ }
81
+ emitCheckpoint(sessionKey, summary, opts) {
82
+ this.emit(sessionKey, 'checkpoint', { summary, ...opts });
83
+ }
84
+ emitPhaseStart(sessionKey, phase, jobName) {
85
+ this.emit(sessionKey, 'phase_start', { phase, jobName });
86
+ }
87
+ emitPhaseEnd(sessionKey, phase, opts) {
88
+ this.emit(sessionKey, 'phase_end', { phase, ...opts });
89
+ }
90
+ emitError(sessionKey, message, opts) {
91
+ this.emit(sessionKey, 'error', { message: message.slice(0, 1000), ...opts });
92
+ }
93
+ // ── Readers ──────────────────────────────────────────────────────
94
+ /** Get all events for a session. */
95
+ getEvents(sessionKey, opts) {
96
+ const filePath = this.keyToFile(sessionKey);
97
+ if (!existsSync(filePath))
98
+ return [];
99
+ try {
100
+ const lines = readFileSync(filePath, 'utf-8').split('\n').filter(Boolean);
101
+ let events = [];
102
+ for (const line of lines) {
103
+ try {
104
+ const event = JSON.parse(line);
105
+ if (opts?.type && event.type !== opts.type)
106
+ continue;
107
+ if (opts?.since && event.timestamp < opts.since)
108
+ continue;
109
+ events.push(event);
110
+ }
111
+ catch { /* skip malformed lines */ }
112
+ }
113
+ if (opts?.limit) {
114
+ events = events.slice(-opts.limit);
115
+ }
116
+ return events;
117
+ }
118
+ catch {
119
+ return [];
120
+ }
121
+ }
122
+ /** Get the last checkpoint for a session. */
123
+ getLastCheckpoint(sessionKey) {
124
+ const events = this.getEvents(sessionKey, { type: 'checkpoint' });
125
+ return events.length > 0 ? events[events.length - 1] : null;
126
+ }
127
+ /** Get recovery context for a crashed session — summarize what happened. */
128
+ getRecoveryContext(sessionKey) {
129
+ const events = this.getEvents(sessionKey);
130
+ if (events.length === 0)
131
+ return null;
132
+ // Find the last query_start without a matching query_end
133
+ let lastQueryStart = null;
134
+ let lastQueryEnd = null;
135
+ for (const evt of events) {
136
+ if (evt.type === 'query_start')
137
+ lastQueryStart = evt;
138
+ if (evt.type === 'query_end')
139
+ lastQueryEnd = evt;
140
+ }
141
+ // If the last query completed, no recovery needed
142
+ if (lastQueryEnd && lastQueryStart && lastQueryEnd.timestamp >= lastQueryStart.timestamp) {
143
+ return null;
144
+ }
145
+ if (!lastQueryStart)
146
+ return null;
147
+ // Build recovery summary from events after the last query_start
148
+ const afterStart = events.filter(e => e.timestamp >= lastQueryStart.timestamp);
149
+ const toolCalls = afterStart.filter(e => e.type === 'tool_call');
150
+ const checkpoints = afterStart.filter(e => e.type === 'checkpoint');
151
+ const errors = afterStart.filter(e => e.type === 'error');
152
+ const parts = [];
153
+ parts.push(`[Session Recovery: Your previous query was interrupted]`);
154
+ parts.push(`Original prompt: ${(lastQueryStart.data.prompt || '').slice(0, 300)}`);
155
+ if (toolCalls.length > 0) {
156
+ const toolNames = toolCalls.map(t => t.data.tool).filter(Boolean);
157
+ parts.push(`Tools used before interruption: ${toolNames.join(', ')}`);
158
+ }
159
+ if (checkpoints.length > 0) {
160
+ const lastCp = checkpoints[checkpoints.length - 1];
161
+ parts.push(`Last checkpoint: ${lastCp.data.summary || 'unknown'}`);
162
+ if (Array.isArray(lastCp.data.completed) && lastCp.data.completed.length > 0) {
163
+ parts.push(`Completed: ${lastCp.data.completed.join(', ')}`);
164
+ }
165
+ if (Array.isArray(lastCp.data.remaining) && lastCp.data.remaining.length > 0) {
166
+ parts.push(`Remaining: ${lastCp.data.remaining.join(', ')}`);
167
+ }
168
+ }
169
+ if (errors.length > 0) {
170
+ parts.push(`Errors: ${errors.map(e => e.data.message).join('; ')}`);
171
+ }
172
+ parts.push(`Please pick up where you left off.`);
173
+ return parts.join('\n');
174
+ }
175
+ /** Check if a session has an unfinished query (for crash recovery). */
176
+ hasOrphanedQuery(sessionKey) {
177
+ const events = this.getEvents(sessionKey);
178
+ let lastStart = null;
179
+ let lastEnd = null;
180
+ for (const evt of events) {
181
+ if (evt.type === 'query_start')
182
+ lastStart = evt.timestamp;
183
+ if (evt.type === 'query_end')
184
+ lastEnd = evt.timestamp;
185
+ }
186
+ return lastStart !== null && (lastEnd === null || lastEnd < lastStart);
187
+ }
188
+ /** Get tool usage stats for a session. */
189
+ getToolStats(sessionKey) {
190
+ const events = this.getEvents(sessionKey, { type: 'tool_call' });
191
+ const stats = {};
192
+ for (const evt of events) {
193
+ const tool = evt.data.tool;
194
+ if (tool)
195
+ stats[tool] = (stats[tool] ?? 0) + 1;
196
+ }
197
+ return stats;
198
+ }
199
+ /** Clean up old session logs (older than maxAge days). */
200
+ cleanup(maxAgeDays = 30) {
201
+ if (!existsSync(this.dir))
202
+ return 0;
203
+ const { readdirSync, statSync } = require('node:fs');
204
+ const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000;
205
+ let cleaned = 0;
206
+ try {
207
+ for (const file of readdirSync(this.dir)) {
208
+ if (!file.endsWith('.jsonl'))
209
+ continue;
210
+ const filePath = path.join(this.dir, file);
211
+ try {
212
+ if (statSync(filePath).mtimeMs < cutoff) {
213
+ unlinkSync(filePath);
214
+ cleaned++;
215
+ }
216
+ }
217
+ catch { /* skip */ }
218
+ }
219
+ }
220
+ catch { /* non-fatal */ }
221
+ if (cleaned > 0)
222
+ logger.info({ cleaned }, 'Cleaned up old session logs');
223
+ return cleaned;
224
+ }
225
+ }
226
+ // ── Singleton ──────────────────────────────────────────────────────
227
+ let _instance = null;
228
+ export function getEventLog() {
229
+ if (!_instance)
230
+ _instance = new EventLog();
231
+ return _instance;
232
+ }
233
+ //# sourceMappingURL=session-event-log.js.map
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Clementine TypeScript — Procedural Memory (Skill Extraction + Retrieval).
3
+ *
4
+ * Extracts reusable skill documents from successful multi-step executions
5
+ * (unleashed jobs, cron runs, complex chat interactions) and stores them
6
+ * as markdown files in vault/00-System/skills/ (global) or
7
+ * vault/00-System/agents/{slug}/skills/ (agent-scoped).
8
+ *
9
+ * New skills land in a pending queue first. The owner approves or rejects
10
+ * them via chat or dashboard before they become active.
11
+ *
12
+ * Skills are automatically indexed by the memory store FTS5 and retrieved
13
+ * during context search to avoid re-deriving procedures from scratch.
14
+ */
15
+ import type { SkillDocument } from '../types.js';
16
+ import type { PersonalAssistant } from './assistant.js';
17
+ /**
18
+ * Extract a reusable skill from a successful execution.
19
+ * New skills go to the pending queue — owner must approve before they activate.
20
+ * Merges into existing approved skills directly (no re-approval needed).
21
+ */
22
+ export declare function extractSkill(assistant: PersonalAssistant, context: {
23
+ source: SkillDocument['source'];
24
+ sourceJob?: string;
25
+ agentSlug?: string;
26
+ prompt: string;
27
+ output: string;
28
+ toolsUsed: string[];
29
+ durationMs: number;
30
+ }): Promise<SkillDocument | null>;
31
+ /** Move a pending skill to the active skills directory. */
32
+ export declare function approvePendingSkill(name: string): {
33
+ ok: boolean;
34
+ message: string;
35
+ };
36
+ /** Delete a pending skill (reject it). */
37
+ export declare function rejectPendingSkill(name: string): {
38
+ ok: boolean;
39
+ message: string;
40
+ };
41
+ /** List all skills waiting for approval. */
42
+ export declare function listPendingSkills(): Array<{
43
+ name: string;
44
+ title: string;
45
+ description: string;
46
+ source: string;
47
+ agentSlug?: string;
48
+ createdAt: string;
49
+ }>;
50
+ /** Search skills by query text and return matching skill content for injection. Agent-scoped skills get a priority boost. */
51
+ export interface SkillMatch {
52
+ name: string;
53
+ title: string;
54
+ content: string;
55
+ score: number;
56
+ toolsUsed: string[];
57
+ attachments: string[];
58
+ skillDir: string;
59
+ }
60
+ export declare function searchSkills(query: string, limit?: number, agentSlug?: string): SkillMatch[];
61
+ /** Record that a skill was used (bump use count). */
62
+ export declare function recordSkillUse(skillName: string, agentSlug?: string): void;
63
+ /** List all active skills (global + all agent-scoped). */
64
+ export declare function listSkills(agentSlug?: string): Array<{
65
+ name: string;
66
+ title: string;
67
+ source: string;
68
+ useCount: number;
69
+ updatedAt: string;
70
+ agentSlug?: string;
71
+ }>;
72
+ //# sourceMappingURL=skill-extractor.d.ts.map