@ebowwa/daemons 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +264 -0
  2. package/dist/bin/discord-cli.js +124118 -0
  3. package/dist/bin/manager.js +143 -0
  4. package/dist/bin/telegram-cli.js +124114 -0
  5. package/dist/index.js +125340 -0
  6. package/package.json +94 -0
  7. package/src/agent.ts +111 -0
  8. package/src/channels/base.ts +573 -0
  9. package/src/channels/discord.ts +306 -0
  10. package/src/channels/index.ts +169 -0
  11. package/src/channels/telegram.ts +315 -0
  12. package/src/daemon.ts +534 -0
  13. package/src/hooks.ts +97 -0
  14. package/src/index.ts +111 -0
  15. package/src/memory.ts +369 -0
  16. package/src/skills/coding/commit.ts +202 -0
  17. package/src/skills/coding/execute-subtask.ts +136 -0
  18. package/src/skills/coding/fix-issues.ts +126 -0
  19. package/src/skills/coding/index.ts +26 -0
  20. package/src/skills/coding/plan-task.ts +158 -0
  21. package/src/skills/coding/quality-check.ts +155 -0
  22. package/src/skills/index.ts +65 -0
  23. package/src/skills/registry.ts +380 -0
  24. package/src/skills/shared/index.ts +21 -0
  25. package/src/skills/shared/reflect.ts +156 -0
  26. package/src/skills/shared/review.ts +201 -0
  27. package/src/skills/shared/trajectory.ts +319 -0
  28. package/src/skills/trading/analyze-market.ts +144 -0
  29. package/src/skills/trading/check-risk.ts +176 -0
  30. package/src/skills/trading/execute-trade.ts +185 -0
  31. package/src/skills/trading/generate-signal.ts +160 -0
  32. package/src/skills/trading/index.ts +26 -0
  33. package/src/skills/trading/monitor-position.ts +179 -0
  34. package/src/skills/types.ts +235 -0
  35. package/src/skills/workflows.ts +340 -0
  36. package/src/state.ts +77 -0
  37. package/src/tools.ts +134 -0
  38. package/src/types.ts +314 -0
  39. package/src/workflow.ts +341 -0
  40. package/src/workflows/coding.ts +580 -0
  41. package/src/workflows/index.ts +61 -0
  42. package/src/workflows/trading.ts +608 -0
@@ -0,0 +1,380 @@
1
+ /**
2
+ * GLM Daemon - Skill Registry
3
+ *
4
+ * Central registry for all available skills.
5
+ * Skills register themselves on import, or can be registered manually.
6
+ */
7
+
8
+ import type {
9
+ Skill,
10
+ SkillConfig,
11
+ SkillContext,
12
+ SkillResult,
13
+ SkillExecutionOptions,
14
+ SkillExecutionMeta,
15
+ } from "./types.js";
16
+
17
+ /**
18
+ * Skill Registry - manages all available skills
19
+ */
20
+ export class SkillRegistry {
21
+ private skills: Map<string, Skill> = new Map();
22
+ private executionHistory: SkillExecutionMeta[] = [];
23
+ private maxHistorySize = 1000;
24
+
25
+ /**
26
+ * Register a skill
27
+ */
28
+ register(skill: Skill): void {
29
+ if (this.skills.has(skill.id)) {
30
+ console.warn(`[SkillRegistry] Overwriting existing skill: ${skill.id}`);
31
+ }
32
+ this.skills.set(skill.id, skill);
33
+ console.log(`[SkillRegistry] Registered skill: ${skill.id}`);
34
+ }
35
+
36
+ /**
37
+ * Register multiple skills
38
+ */
39
+ registerAll(skills: Skill[]): void {
40
+ for (const skill of skills) {
41
+ this.register(skill);
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Get a skill by ID
47
+ */
48
+ get(id: string): Skill | undefined {
49
+ return this.skills.get(id);
50
+ }
51
+
52
+ /**
53
+ * Check if a skill exists
54
+ */
55
+ has(id: string): boolean {
56
+ return this.skills.has(id);
57
+ }
58
+
59
+ /**
60
+ * List all registered skills
61
+ */
62
+ list(): Skill[] {
63
+ return Array.from(this.skills.values());
64
+ }
65
+
66
+ /**
67
+ * List skill IDs only
68
+ */
69
+ listIds(): string[] {
70
+ return Array.from(this.skills.keys());
71
+ }
72
+
73
+ /**
74
+ * List skills by tag
75
+ */
76
+ listByTag(tag: string): Skill[] {
77
+ return this.list().filter((s) => s.tags?.includes(tag));
78
+ }
79
+
80
+ /**
81
+ * Get skills info for AI/discovery
82
+ */
83
+ getInfo(): Array<{
84
+ id: string;
85
+ name: string;
86
+ description: string;
87
+ tags?: string[];
88
+ parallelizable?: boolean;
89
+ }> {
90
+ return this.list().map((s) => ({
91
+ id: s.id,
92
+ name: s.name,
93
+ description: s.description,
94
+ tags: s.tags,
95
+ parallelizable: s.parallelizable,
96
+ }));
97
+ }
98
+
99
+ /**
100
+ * Execute a skill with retry logic
101
+ */
102
+ async execute(
103
+ skillId: string,
104
+ context: SkillContext,
105
+ params?: unknown,
106
+ options: SkillExecutionOptions = {}
107
+ ): Promise<SkillResult> {
108
+ const skill = this.skills.get(skillId);
109
+ if (!skill) {
110
+ return {
111
+ success: false,
112
+ error: `Skill not found: ${skillId}`,
113
+ };
114
+ }
115
+
116
+ // Validate parameters if schema provided
117
+ if (skill.paramsSchema && !options.skipValidation) {
118
+ const parsed = skill.paramsSchema.safeParse(params);
119
+ if (!parsed.success) {
120
+ return {
121
+ success: false,
122
+ error: `Invalid params for ${skillId}: ${parsed.error.message}`,
123
+ };
124
+ }
125
+ }
126
+
127
+ // Check required context keys
128
+ if (skill.requires) {
129
+ const missing = skill.requires.filter((key) => !(key in context.custom));
130
+ if (missing.length > 0) {
131
+ return {
132
+ success: false,
133
+ error: `Missing required context for ${skillId}: ${missing.join(", ")}`,
134
+ };
135
+ }
136
+ }
137
+
138
+ // Execute with retry
139
+ const maxRetries = options.maxRetries ?? skill.maxRetries ?? 3;
140
+ const baseDelay = skill.retryBaseDelay ?? 1000;
141
+ let lastError: string | undefined;
142
+
143
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
144
+ const meta: SkillExecutionMeta = {
145
+ skillId,
146
+ startTime: new Date().toISOString(),
147
+ attempt,
148
+ success: false,
149
+ };
150
+
151
+ try {
152
+ // Merge default params with provided params
153
+ const mergedParams = skill.defaultParams
154
+ ? { ...skill.defaultParams, ...(params as Record<string, unknown>) }
155
+ : params;
156
+
157
+ const result = await this.executeWithTimeout(
158
+ skill,
159
+ mergedParams,
160
+ context,
161
+ options.timeout
162
+ );
163
+
164
+ meta.endTime = new Date().toISOString();
165
+ meta.duration =
166
+ new Date(meta.endTime).getTime() - new Date(meta.startTime).getTime();
167
+ meta.success = result.success;
168
+ meta.error = result.error;
169
+
170
+ this.recordExecution(meta);
171
+
172
+ if (result.success) {
173
+ return result;
174
+ }
175
+
176
+ lastError = result.error;
177
+
178
+ // Don't retry on certain errors
179
+ if (
180
+ result.error?.includes("not allowed") ||
181
+ result.error?.includes("not found")
182
+ ) {
183
+ break;
184
+ }
185
+
186
+ // Exponential backoff before retry
187
+ if (attempt < maxRetries) {
188
+ const delay = baseDelay * Math.pow(2, attempt - 1);
189
+ console.log(
190
+ `[SkillRegistry] Retrying ${skillId} in ${delay}ms (attempt ${attempt}/${maxRetries})`
191
+ );
192
+ await new Promise((resolve) => setTimeout(resolve, delay));
193
+ }
194
+ } catch (error) {
195
+ meta.endTime = new Date().toISOString();
196
+ meta.duration =
197
+ new Date(meta.endTime).getTime() - new Date(meta.startTime).getTime();
198
+ meta.success = false;
199
+ meta.error = error instanceof Error ? error.message : String(error);
200
+ lastError = meta.error;
201
+
202
+ this.recordExecution(meta);
203
+
204
+ if (attempt < maxRetries) {
205
+ const delay = baseDelay * Math.pow(2, attempt - 1);
206
+ await new Promise((resolve) => setTimeout(resolve, delay));
207
+ }
208
+ }
209
+ }
210
+
211
+ return {
212
+ success: false,
213
+ error: lastError || `Skill failed after ${maxRetries} attempts`,
214
+ };
215
+ }
216
+
217
+ /**
218
+ * Execute multiple skills in parallel
219
+ */
220
+ async executeParallel(
221
+ skillIds: string[],
222
+ context: SkillContext,
223
+ params?: Record<string, unknown>
224
+ ): Promise<Map<string, SkillResult>> {
225
+ const results = new Map<string, SkillResult>();
226
+
227
+ // Validate all skills exist and are parallelizable
228
+ for (const id of skillIds) {
229
+ const skill = this.skills.get(id);
230
+ if (!skill) {
231
+ results.set(id, { success: false, error: `Skill not found: ${id}` });
232
+ continue;
233
+ }
234
+ if (!skill.parallelizable) {
235
+ results.set(id, {
236
+ success: false,
237
+ error: `Skill ${id} is not parallelizable`,
238
+ });
239
+ }
240
+ }
241
+
242
+ // Filter out already-failed skills
243
+ const toExecute = skillIds.filter((id) => !results.has(id));
244
+
245
+ // Execute in parallel
246
+ const promises = toExecute.map(async (id) => {
247
+ const result = await this.execute(id, context, params?.[id]);
248
+ return { id, result };
249
+ });
250
+
251
+ const settled = await Promise.allSettled(promises);
252
+
253
+ for (const item of settled) {
254
+ if (item.status === "fulfilled") {
255
+ results.set(item.value.id, item.value.result);
256
+ } else {
257
+ // This shouldn't happen as errors are caught in execute()
258
+ const id = toExecute[settled.indexOf(item)];
259
+ results.set(id, {
260
+ success: false,
261
+ error: item.reason?.message || "Unknown error",
262
+ });
263
+ }
264
+ }
265
+
266
+ return results;
267
+ }
268
+
269
+ /**
270
+ * Execute with optional timeout
271
+ */
272
+ private async executeWithTimeout(
273
+ skill: Skill,
274
+ params: unknown,
275
+ context: SkillContext,
276
+ timeout?: number
277
+ ): Promise<SkillResult> {
278
+ if (!timeout) {
279
+ return skill.execute(params, context);
280
+ }
281
+
282
+ return new Promise((resolve) => {
283
+ const timer = setTimeout(() => {
284
+ resolve({
285
+ success: false,
286
+ error: `Skill ${skill.id} timed out after ${timeout}ms`,
287
+ });
288
+ }, timeout);
289
+
290
+ skill
291
+ .execute(params, context)
292
+ .then((result) => {
293
+ clearTimeout(timer);
294
+ resolve(result);
295
+ })
296
+ .catch((error) => {
297
+ clearTimeout(timer);
298
+ resolve({
299
+ success: false,
300
+ error: error instanceof Error ? error.message : String(error),
301
+ });
302
+ });
303
+ });
304
+ }
305
+
306
+ /**
307
+ * Record execution in history
308
+ */
309
+ private recordExecution(meta: SkillExecutionMeta): void {
310
+ this.executionHistory.push(meta);
311
+
312
+ // Trim old entries
313
+ if (this.executionHistory.length > this.maxHistorySize) {
314
+ this.executionHistory = this.executionHistory.slice(-this.maxHistorySize);
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Get execution history
320
+ */
321
+ getHistory(limit = 100): SkillExecutionMeta[] {
322
+ return this.executionHistory.slice(-limit);
323
+ }
324
+
325
+ /**
326
+ * Get execution stats for a skill
327
+ */
328
+ getStats(skillId: string): {
329
+ total: number;
330
+ success: number;
331
+ failed: number;
332
+ avgDuration: number;
333
+ } {
334
+ const executions = this.executionHistory.filter((m) => m.skillId === skillId);
335
+ const total = executions.length;
336
+ const success = executions.filter((m) => m.success).length;
337
+ const durations = executions
338
+ .filter((m) => m.duration !== undefined)
339
+ .map((m) => m.duration!);
340
+
341
+ return {
342
+ total,
343
+ success,
344
+ failed: total - success,
345
+ avgDuration:
346
+ durations.length > 0
347
+ ? durations.reduce((a, b) => a + b, 0) / durations.length
348
+ : 0,
349
+ };
350
+ }
351
+
352
+ /**
353
+ * Clear registry (useful for testing)
354
+ */
355
+ clear(): void {
356
+ this.skills.clear();
357
+ this.executionHistory = [];
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Global skill registry instance
363
+ */
364
+ export const skillRegistry = new SkillRegistry();
365
+
366
+ /**
367
+ * Helper to create and register a skill
368
+ */
369
+ export function defineSkill<P = unknown, D extends Record<string, unknown> = Record<string, unknown>>(
370
+ config: SkillConfig<P>,
371
+ execute: (params: P | undefined, context: SkillContext) => Promise<SkillResult<D>>
372
+ ): Skill<P, D> {
373
+ const skill: Skill<P, D> = {
374
+ ...config,
375
+ execute,
376
+ };
377
+
378
+ skillRegistry.register(skill);
379
+ return skill;
380
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * GLM Daemon - Shared Skills
3
+ *
4
+ * Skills shared across all workflows.
5
+ */
6
+
7
+ // Import to auto-register
8
+ import "./reflect.js";
9
+ import "./review.js";
10
+ import "./trajectory.js";
11
+
12
+ // Re-export for explicit imports
13
+ export { reflectSkill } from "./reflect.js";
14
+ export { reviewSkill } from "./review.js";
15
+ export { trajectorySkill } from "./trajectory.js";
16
+
17
+ // Re-export types
18
+ export type { ReflectData } from "./reflect.js";
19
+ export type { ReviewData } from "./review.js";
20
+ export type { TrajectoryData, LearnedPattern } from "./trajectory.js";
21
+ export { TrajectoryParams } from "./trajectory.js";
@@ -0,0 +1,156 @@
1
+ /**
2
+ * GLM Daemon - Reflect Skill
3
+ *
4
+ * TL;DR checkpoint for summarizing progress after N tool calls.
5
+ * Used by all workflows for periodic reflection.
6
+ */
7
+
8
+ import { z } from "zod";
9
+ import type { Skill, SkillContext, SkillResult } from "../types.js";
10
+ import { skillRegistry } from "../registry.js";
11
+
12
+ /**
13
+ * Reflect skill parameters
14
+ */
15
+ const ReflectParams = z.object({
16
+ /** Force reflection even if not at checkpoint */
17
+ force: z.boolean().optional(),
18
+ });
19
+
20
+ /**
21
+ * Reflection result data (matches GLMReflectionSummary)
22
+ */
23
+ interface ReflectData {
24
+ /** Checkpoint number */
25
+ checkpoint: number;
26
+ /** Summary text */
27
+ summary: string;
28
+ /** What was accomplished */
29
+ accomplishments: string[];
30
+ /** What to do next */
31
+ nextSteps: string[];
32
+ /** Any issues/blockers */
33
+ issues: string[];
34
+ /** Timestamp */
35
+ timestamp: string;
36
+ /** Tools used in this chunk */
37
+ toolsUsed: number;
38
+ /** Phase when reflection occurred */
39
+ phase: string;
40
+ }
41
+
42
+ /**
43
+ * Reflect skill definition
44
+ */
45
+ export const reflectSkill: Skill<void, ReflectData> = {
46
+ id: "/reflect",
47
+ name: "Reflect",
48
+ description:
49
+ "TL;DR checkpoint for summarizing progress. Generates a brief summary of what was done and what's next.",
50
+ tags: ["shared", "checkpoint", "meta"],
51
+ parallelizable: false,
52
+ maxRetries: 1,
53
+
54
+ async execute(_params, context): Promise<SkillResult<ReflectData>> {
55
+ const checkpoint = context.state.reflection.checkpointCount + 1;
56
+ const previousPhase = context.state.slam.previousPhase || "unknown";
57
+
58
+ console.log(`[/reflect] Checkpoint #${checkpoint}`);
59
+ console.log(`[/reflect] Tools used this chunk: ${context.state.reflection.toolCount}`);
60
+
61
+ const prompt = `REFLECTION CHECKPOINT #${checkpoint}
62
+
63
+ You have used ${context.state.reflection.toolCount} tools. Pause and reflect.
64
+
65
+ Task: ${context.state.prompt}
66
+ Current Phase: ${previousPhase}
67
+ Iteration: ${context.state.iteration}
68
+
69
+ Progress so far:
70
+ - Subtasks completed: ${context.state.slam.completedSubtasks.length}/${context.state.slam.subtasks.length}
71
+ - Files changed: ${context.state.filesChanged.length > 0 ? context.state.filesChanged.join(", ") : "none"}
72
+
73
+ Generate a TL;DR summary in this JSON format:
74
+ {
75
+ "summary": "1-2 sentence summary of progress",
76
+ "accomplishments": ["what was done"],
77
+ "nextSteps": ["what needs to be done next"],
78
+ "issues": ["any blockers or problems"]
79
+ }
80
+
81
+ Be concise. Focus on what matters for continuing the task.`;
82
+
83
+ try {
84
+ const response = await context.agent.execute(prompt);
85
+
86
+ // Parse JSON from response
87
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
88
+ let data: ReflectData;
89
+
90
+ if (jsonMatch) {
91
+ const parsed = JSON.parse(jsonMatch[0]);
92
+ data = {
93
+ checkpoint,
94
+ summary: parsed.summary || "Progress made",
95
+ accomplishments: parsed.accomplishments || [],
96
+ nextSteps: parsed.nextSteps || [],
97
+ issues: parsed.issues || [],
98
+ timestamp: new Date().toISOString(),
99
+ toolsUsed: context.state.reflection.toolCount,
100
+ phase: previousPhase,
101
+ };
102
+ } else {
103
+ // Fallback if no JSON found
104
+ data = {
105
+ checkpoint,
106
+ summary: response.substring(0, 200),
107
+ accomplishments: [],
108
+ nextSteps: [],
109
+ issues: [],
110
+ timestamp: new Date().toISOString(),
111
+ toolsUsed: context.state.reflection.toolCount,
112
+ phase: previousPhase,
113
+ };
114
+ }
115
+
116
+ console.log(`[/reflect] Summary: ${data.summary}`);
117
+ if (data.accomplishments.length > 0) {
118
+ console.log(`[/reflect] Accomplishments: ${data.accomplishments.join(", ")}`);
119
+ }
120
+ if (data.nextSteps.length > 0) {
121
+ console.log(`[/reflect] Next: ${data.nextSteps.join(", ")}`);
122
+ }
123
+ if (data.issues.length > 0) {
124
+ console.log(`[/reflect] Issues: ${data.issues.join(", ")}`);
125
+ }
126
+
127
+ // Execute reflection hook
128
+ await context.executeHook("onReflection", context.state, data);
129
+
130
+ return {
131
+ success: true,
132
+ data,
133
+ stateUpdates: {
134
+ reflection: {
135
+ ...context.state.reflection,
136
+ toolCount: 0, // Reset counter
137
+ checkpointCount: checkpoint,
138
+ lastReflection: data.timestamp,
139
+ summaries: [...context.state.reflection.summaries, data],
140
+ },
141
+ },
142
+ };
143
+ } catch (error) {
144
+ return {
145
+ success: false,
146
+ error: error instanceof Error ? error.message : String(error),
147
+ };
148
+ }
149
+ },
150
+ };
151
+
152
+ // Auto-register
153
+ skillRegistry.register(reflectSkill);
154
+
155
+ export { ReflectParams };
156
+ export type { ReflectData };