@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
package/src/daemon.ts ADDED
@@ -0,0 +1,534 @@
1
+ /**
2
+ * GLM Daemon - Core Daemon
3
+ *
4
+ * Main orchestrator for autonomous GLM 4.7 agents.
5
+ * Implements SLAM pattern: State → Loop → Action → Memory
6
+ *
7
+ * Now skill-based - uses composable skills instead of hardcoded phases.
8
+ */
9
+
10
+ import { promises as fsp } from "fs";
11
+ import { join } from "path";
12
+ import type {
13
+ GLMDaemonConfig,
14
+ GLMDaemonState,
15
+ GLMDaemonStatus,
16
+ } from "./types.js";
17
+ import { GLMAgent } from "./agent.js";
18
+ import { HookSystem } from "./hooks.js";
19
+ import { ToolBridge } from "./tools.js";
20
+ import { StateManager } from "./state.js";
21
+ import { Team } from "@ebowwa/teammates";
22
+ import { GLMClient } from "@ebowwa/ai";
23
+ import {
24
+ type SkillWorkflowConfig,
25
+ type SkillContext,
26
+ codingWorkflow,
27
+ workflowConfigRegistry,
28
+ executeWorkflowSkill,
29
+ isWorkflowComplete,
30
+ skillRegistry,
31
+ } from "./skills/index.js";
32
+
33
+ // Re-export types for external use
34
+ export type { GLMDaemonConfig, GLMDaemonState, GLMDaemonStatus } from "./types.js";
35
+
36
+ const STATE_FILE = ".glm-daemon.json";
37
+ const SETTINGS_FILE = ".claude/settings.local.json";
38
+
39
+ /**
40
+ * Extended daemon config with skill-based workflow support
41
+ */
42
+ export interface GLMDaemonConfigWithWorkflow extends GLMDaemonConfig {
43
+ /** Workflow ID to use (defaults to "coding") */
44
+ workflowId?: string;
45
+ }
46
+
47
+ export class GLMDaemon {
48
+ private config: GLMDaemonConfigWithWorkflow;
49
+ private state: GLMDaemonState;
50
+ private statePath: string;
51
+ private agent: GLMAgent;
52
+ private hooks: HookSystem;
53
+ private tools: ToolBridge;
54
+ private stateManager: StateManager;
55
+ private team: Team | null = null;
56
+ private subagents: Map<string, GLMAgent> = new Map();
57
+ private running = false;
58
+ private client: GLMClient;
59
+ private workflow: SkillWorkflowConfig;
60
+ private customContext: Record<string, unknown> = {};
61
+
62
+ constructor(config: GLMDaemonConfigWithWorkflow) {
63
+ this.config = config;
64
+ this.statePath = join(config.cwd, STATE_FILE);
65
+ this.client = new GLMClient();
66
+
67
+ // Initialize workflow from registry
68
+ const workflowId = config.workflowId || "coding";
69
+ const foundWorkflow = workflowConfigRegistry.get(workflowId);
70
+ if (!foundWorkflow) {
71
+ throw new Error(`Workflow not found: ${workflowId}. Available: ${workflowConfigRegistry.list().map(w => w.id).join(", ")}`);
72
+ }
73
+ this.workflow = foundWorkflow;
74
+
75
+ console.log(`[GLMDaemon] Using workflow: ${this.workflow.name}`);
76
+
77
+ // Initialize state
78
+ this.state = this.initializeState();
79
+
80
+ // Initialize components
81
+ this.hooks = new HookSystem(config.hooks || {});
82
+ this.tools = new ToolBridge(config.tools || {});
83
+ this.stateManager = new StateManager(this.statePath);
84
+
85
+ // Initialize main agent
86
+ this.agent = new GLMAgent({
87
+ agentId: "glm-daemon",
88
+ name: "GLM Daemon",
89
+ prompt: this.buildSystemPrompt(),
90
+ model: config.model,
91
+ tools: this.tools.getAvailableTools(),
92
+ });
93
+
94
+ // Log available skills
95
+ console.log(`[GLMDaemon] Available skills: ${skillRegistry.listIds().length}`);
96
+ }
97
+
98
+ /**
99
+ * Initialize daemon state
100
+ */
101
+ private initializeState(): GLMDaemonState {
102
+ const now = new Date().toISOString();
103
+ const initialSkill = this.workflow.initialSkill;
104
+
105
+ return {
106
+ prompt: "", // Will be set when starting
107
+ promise: this.config.completionPromise || "TASK_COMPLETE",
108
+ iteration: 0,
109
+ maxIterations: this.config.maxIterations || 0,
110
+ startTime: now,
111
+ lastUpdate: now,
112
+ tokens: {
113
+ totalInput: 0,
114
+ totalOutput: 0,
115
+ byIteration: [],
116
+ },
117
+ filesChanged: [],
118
+ workMemory: {
119
+ completedFiles: [],
120
+ fileChecksums: {},
121
+ },
122
+ slam: {
123
+ enabled: true,
124
+ phase: initialSkill, // Now uses skill ID as phase
125
+ state: {
126
+ currentTask: "",
127
+ beliefs: {},
128
+ goals: [this.config.completionPromise || "TASK_COMPLETE"],
129
+ },
130
+ subtasks: [],
131
+ currentSubtask: null,
132
+ completedSubtasks: [],
133
+ memory: {
134
+ actionsTaken: [],
135
+ outcomes: {},
136
+ patterns: {},
137
+ },
138
+ },
139
+ git: {
140
+ enabled: this.config.autoCommit || this.config.autoPR || false,
141
+ autoCommit: this.config.autoCommit || false,
142
+ autoPR: this.config.autoPR || false,
143
+ baseBranch: this.config.baseBranch || "main",
144
+ branchName: "",
145
+ branchCreated: false,
146
+ currentCommit: "",
147
+ },
148
+ reflection: {
149
+ toolCount: 0,
150
+ totalToolCount: 0,
151
+ checkpointCount: 0,
152
+ lastReflection: null,
153
+ summaries: [],
154
+ },
155
+ };
156
+ }
157
+
158
+ /**
159
+ * Build system prompt using workflow config
160
+ */
161
+ private buildSystemPrompt(): string {
162
+ const currentSkillId = this.state.slam.phase;
163
+ const currentSkill = skillRegistry.get(currentSkillId);
164
+
165
+ let prompt = `You are an autonomous AI agent.
166
+
167
+ Your task: ${this.state.prompt}
168
+
169
+ Workflow: ${this.workflow.name}
170
+ Available skills in this workflow:
171
+ ${this.workflow.skills.map(s => `- ${s}`).join("\n")}
172
+
173
+ Current skill: ${currentSkill?.name || currentSkillId}
174
+ ${currentSkill?.description ? `Description: ${currentSkill.description}` : ""}
175
+
176
+ Iteration: ${this.state.iteration}
177
+ `;
178
+
179
+ if (this.workflow.systemPromptExtensions) {
180
+ prompt += `\n${this.workflow.systemPromptExtensions}`;
181
+ }
182
+
183
+ return prompt;
184
+ }
185
+
186
+ /**
187
+ * Create skill execution context
188
+ */
189
+ private createSkillContext(): SkillContext {
190
+ return {
191
+ state: this.state,
192
+ agent: this.agent,
193
+ custom: this.customContext,
194
+ incrementToolCount: () => this.incrementToolCount(),
195
+ executeHook: (event: string, ...args: unknown[]) => this.hooks.execute(event, ...args),
196
+ callSkill: async (id: string, params?: unknown) => {
197
+ return skillRegistry.execute(id, this.createSkillContext(), params);
198
+ },
199
+ workflowConfig: this.workflow.skillParams,
200
+ };
201
+ }
202
+
203
+ /**
204
+ * Start the daemon with a prompt
205
+ */
206
+ async start(prompt: string): Promise<string> {
207
+ if (this.running) {
208
+ throw new Error("Daemon already running");
209
+ }
210
+
211
+ this.state.prompt = prompt;
212
+ this.state.slam.state.currentTask = prompt;
213
+ this.running = true;
214
+
215
+ // Update agent prompt
216
+ this.agent = new GLMAgent({
217
+ agentId: "glm-daemon",
218
+ name: "GLM Daemon",
219
+ prompt: this.buildSystemPrompt(),
220
+ model: this.config.model,
221
+ tools: this.tools.getAvailableTools(),
222
+ });
223
+
224
+ // Initialize team for multi-agent coordination
225
+ this.team = await Team.create({
226
+ name: this.config.teamName,
227
+ description: `GLM daemon for: ${prompt.substring(0, 50)}...`,
228
+ });
229
+
230
+ // Run session start hook
231
+ await this.hooks.execute("onSessionStart", this.state);
232
+
233
+ // Save initial state
234
+ await this.stateManager.save(this.state);
235
+
236
+ console.log(`[GLMDaemon] Starting with skill: ${this.state.slam.phase}`);
237
+
238
+ // Start main loop
239
+ this.runLoop().catch((error) => {
240
+ console.error("[GLMDaemon] Loop error:", error);
241
+ this.running = false;
242
+ });
243
+
244
+ return this.config.teamName;
245
+ }
246
+
247
+ /**
248
+ * Main daemon loop (SLAM pattern with skill execution)
249
+ */
250
+ private async runLoop(): Promise<void> {
251
+ while (this.running) {
252
+ // Check completion condition
253
+ if (await this.checkCompletion()) {
254
+ await this.complete();
255
+ break;
256
+ }
257
+
258
+ // Check iteration limit
259
+ if (
260
+ this.state.maxIterations > 0 &&
261
+ this.state.iteration >= this.state.maxIterations
262
+ ) {
263
+ console.log(`[GLMDaemon] Max iterations reached: ${this.state.maxIterations}`);
264
+ await this.stop();
265
+ break;
266
+ }
267
+
268
+ // Run iteration start hook
269
+ await this.hooks.execute("onIterationStart", this.state.iteration);
270
+
271
+ // Execute current skill
272
+ await this.executeSkill();
273
+
274
+ // Update state
275
+ this.state.iteration++;
276
+ this.state.lastUpdate = new Date().toISOString();
277
+
278
+ // Save state
279
+ await this.stateManager.save(this.state);
280
+
281
+ // Run iteration end hook
282
+ await this.hooks.execute("onIterationEnd", this.state.iteration, null);
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Execute current skill via skill registry
288
+ */
289
+ private async executeSkill(): Promise<void> {
290
+ const currentSkillId = this.state.slam.phase;
291
+ console.log(`[GLMDaemon] Executing skill: ${currentSkillId}`);
292
+
293
+ // Create skill context
294
+ const context = this.createSkillContext();
295
+
296
+ try {
297
+ // Execute via skill-based workflow
298
+ const result = await executeWorkflowSkill(this.workflow, context);
299
+
300
+ // Update custom context
301
+ if (result.contextUpdates) {
302
+ this.customContext = { ...this.customContext, ...result.contextUpdates };
303
+ }
304
+
305
+ // Apply state updates from result
306
+ if (result.stateUpdates) {
307
+ this.applyStateUpdates(result.stateUpdates);
308
+ }
309
+
310
+ // Update phase from result (skill transition)
311
+ if (result.stateUpdates?.slam?.phase) {
312
+ const newPhase = result.stateUpdates.slam.phase;
313
+ if (newPhase !== currentSkillId) {
314
+ console.log(`[GLMDaemon] Skill transition: ${currentSkillId} -> ${newPhase}`);
315
+ this.state.slam.phase = newPhase;
316
+ }
317
+ }
318
+
319
+ // Stop loop if requested
320
+ if (!result.success && result.error) {
321
+ console.error(`[GLMDaemon] Skill error:`, result.error);
322
+ }
323
+ } catch (error) {
324
+ console.error(`[GLMDaemon] Skill execution failed:`, error);
325
+ throw error;
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Apply state updates from skill result
331
+ */
332
+ private applyStateUpdates(updates: Partial<GLMDaemonState>): void {
333
+ // Deep merge slam updates
334
+ if (updates.slam) {
335
+ this.state.slam = {
336
+ ...this.state.slam,
337
+ ...updates.slam,
338
+ state: {
339
+ ...this.state.slam.state,
340
+ ...(updates.slam.state || {}),
341
+ },
342
+ memory: {
343
+ ...this.state.slam.memory,
344
+ ...(updates.slam.memory || {}),
345
+ },
346
+ };
347
+ }
348
+
349
+ // Apply other updates
350
+ if (updates.filesChanged) {
351
+ this.state.filesChanged = updates.filesChanged;
352
+ }
353
+ if (updates.workMemory) {
354
+ this.state.workMemory = updates.workMemory;
355
+ }
356
+ if (updates.git) {
357
+ this.state.git = { ...this.state.git, ...updates.git };
358
+ }
359
+ if (updates.reflection) {
360
+ this.state.reflection = { ...this.state.reflection, ...updates.reflection };
361
+ }
362
+ }
363
+
364
+ /**
365
+ * Increment tool count and check for reflection trigger
366
+ */
367
+ incrementToolCount(): boolean {
368
+ const reflectionConfig = this.config.reflection || {
369
+ enabled: false,
370
+ toolLimit: 0,
371
+ autoContinue: true,
372
+ };
373
+
374
+ this.state.reflection.toolCount++;
375
+ this.state.reflection.totalToolCount++;
376
+
377
+ // Check if we hit the limit
378
+ if (reflectionConfig.enabled && reflectionConfig.toolLimit > 0 &&
379
+ this.state.reflection.toolCount >= reflectionConfig.toolLimit) {
380
+ // Store current phase to return after reflection
381
+ this.state.slam.previousPhase = this.state.slam.phase;
382
+ this.state.slam.phase = "/reflect"; // Use skill ID
383
+ return true; // Reflection triggered
384
+ }
385
+
386
+ return false;
387
+ }
388
+
389
+ /**
390
+ * Complete the daemon run
391
+ */
392
+ private async complete(): Promise<void> {
393
+ console.log("[GLMDaemon] Task complete!");
394
+ this.state.slam.phase = "/complete";
395
+ this.running = false;
396
+
397
+ // Run session end hook
398
+ await this.hooks.execute("onSessionEnd", this.state);
399
+
400
+ // Save final state
401
+ await this.stateManager.save(this.state);
402
+
403
+ // Create PR if enabled
404
+ if (this.state.git.autoPR) {
405
+ // TODO: Use MCP git tools for PR creation
406
+ console.log("[GLMDaemon] Auto-PR would happen here via MCP git tools");
407
+ }
408
+ }
409
+
410
+ /**
411
+ * Check if workflow is complete
412
+ */
413
+ private async checkCompletion(): Promise<boolean> {
414
+ const context = this.createSkillContext();
415
+ return isWorkflowComplete(this.workflow, context);
416
+ }
417
+
418
+ /**
419
+ * Stop the daemon
420
+ */
421
+ async stop(): Promise<void> {
422
+ console.log("[GLMDaemon] Stopping...");
423
+ this.running = false;
424
+
425
+ // Cleanup
426
+ if (this.team) {
427
+ await this.team.broadcast("team-lead", "Daemon stopping");
428
+ }
429
+
430
+ // Save final state
431
+ await this.stateManager.save(this.state);
432
+ }
433
+
434
+ /**
435
+ * Get daemon status
436
+ */
437
+ getStatus(): GLMDaemonStatus {
438
+ return {
439
+ id: this.config.teamName,
440
+ status: this.running ? "running" : "stopped",
441
+ phase: this.state.slam.phase,
442
+ iteration: this.state.iteration,
443
+ prompt: this.state.prompt,
444
+ currentTask: this.state.slam.state.currentTask,
445
+ totalSubtasks: this.state.slam.subtasks.length,
446
+ completedSubtasks: this.state.slam.completedSubtasks.length,
447
+ startedAt: this.state.startTime,
448
+ lastUpdate: this.state.lastUpdate,
449
+ };
450
+ }
451
+
452
+ /**
453
+ * Get reflection status and summaries
454
+ */
455
+ getReflectionStatus(): {
456
+ enabled: boolean;
457
+ toolLimit: number;
458
+ toolCount: number;
459
+ totalToolCount: number;
460
+ checkpointCount: number;
461
+ lastReflection: string | null;
462
+ summaries: import("./types.js").GLMReflectionSummary[];
463
+ } {
464
+ const config = this.config.reflection || {
465
+ enabled: false,
466
+ toolLimit: 0,
467
+ };
468
+
469
+ return {
470
+ enabled: config.enabled,
471
+ toolLimit: config.toolLimit,
472
+ toolCount: this.state.reflection.toolCount,
473
+ totalToolCount: this.state.reflection.totalToolCount,
474
+ checkpointCount: this.state.reflection.checkpointCount,
475
+ lastReflection: this.state.reflection.lastReflection,
476
+ summaries: this.state.reflection.summaries,
477
+ };
478
+ }
479
+
480
+ /**
481
+ * Force a reflection checkpoint now
482
+ */
483
+ async forceReflection(): Promise<import("./types.js").GLMReflectionSummary | null> {
484
+ if (!this.running) {
485
+ return null;
486
+ }
487
+
488
+ // Store current phase
489
+ this.state.slam.previousPhase = this.state.slam.phase;
490
+ this.state.slam.phase = "/reflect";
491
+
492
+ // Execute reflection skill
493
+ const context = this.createSkillContext();
494
+ const result = await skillRegistry.execute("/reflect", context);
495
+
496
+ // Return to previous phase
497
+ if (result.stateUpdates?.slam?.phase) {
498
+ this.state.slam.phase = result.stateUpdates.slam.phase;
499
+ } else {
500
+ this.state.slam.phase = this.state.slam.previousPhase || "/execute-subtask";
501
+ }
502
+
503
+ // Return the summary from result data
504
+ return result.data as any || null;
505
+ }
506
+
507
+ /**
508
+ * Get the current workflow config
509
+ */
510
+ getWorkflow(): SkillWorkflowConfig {
511
+ return this.workflow;
512
+ }
513
+
514
+ /**
515
+ * Get workflow configuration
516
+ */
517
+ getWorkflowConfig() {
518
+ return this.workflow;
519
+ }
520
+
521
+ /**
522
+ * Get custom context (for debugging)
523
+ */
524
+ getContext(): Record<string, unknown> {
525
+ return { ...this.customContext };
526
+ }
527
+
528
+ /**
529
+ * List available skills
530
+ */
531
+ listSkills(): Array<{ id: string; name: string; description: string }> {
532
+ return skillRegistry.getInfo();
533
+ }
534
+ }
package/src/hooks.ts ADDED
@@ -0,0 +1,97 @@
1
+ /**
2
+ * GLM Daemon - Hook System
3
+ *
4
+ * Implements hook lifecycle for daemon events.
5
+ * Based on Claude Code's hook system.
6
+ */
7
+
8
+ import type { GLMHooksConfig, GLMDaemonState } from "./types.js";
9
+
10
+ type GLMHookHandler = (
11
+ ...args: unknown[]
12
+ ) => Promise<unknown> | unknown;
13
+
14
+ export class HookSystem {
15
+ private hooks: Map<string, GLMHookHandler[]> = new Map();
16
+
17
+ constructor(config: GLMHooksConfig) {
18
+ // Register hooks from config
19
+ if (config.onSessionStart) {
20
+ this.register("onSessionStart", config.onSessionStart as GLMHookHandler);
21
+ }
22
+ if (config.onSessionEnd) {
23
+ this.register("onSessionEnd", config.onSessionEnd as GLMHookHandler);
24
+ }
25
+ if (config.onPreToolUse) {
26
+ this.register("onPreToolUse", config.onPreToolUse as GLMHookHandler);
27
+ }
28
+ if (config.onPostToolUse) {
29
+ this.register("onPostToolUse", config.onPostToolUse as GLMHookHandler);
30
+ }
31
+ if (config.onSubagentStart) {
32
+ this.register("onSubagentStart", config.onSubagentStart as GLMHookHandler);
33
+ }
34
+ if (config.onSubagentStop) {
35
+ this.register("onSubagentStop", config.onSubagentStop as GLMHookHandler);
36
+ }
37
+ if (config.onIterationStart) {
38
+ this.register("onIterationStart", config.onIterationStart as GLMHookHandler);
39
+ }
40
+ if (config.onIterationEnd) {
41
+ this.register("onIterationEnd", config.onIterationEnd as GLMHookHandler);
42
+ }
43
+ if (config.onReflection) {
44
+ this.register("onReflection", config.onReflection as GLMHookHandler);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Register a hook handler
50
+ */
51
+ register(event: string, handler: GLMHookHandler): void {
52
+ if (!this.hooks.has(event)) {
53
+ this.hooks.set(event, []);
54
+ }
55
+ this.hooks.get(event)!.push(handler);
56
+ }
57
+
58
+ /**
59
+ * Execute all handlers for an event
60
+ */
61
+ async execute(event: string, ...args: unknown[]): Promise<void> {
62
+ const handlers = this.hooks.get(event);
63
+ if (!handlers) {
64
+ return;
65
+ }
66
+
67
+ for (const handler of handlers) {
68
+ try {
69
+ await handler(...args);
70
+ } catch (error) {
71
+ console.error(`[HookSystem] Error in ${event}:`, error);
72
+ // Continue executing other hooks even if one fails
73
+ }
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Remove all handlers for an event
79
+ */
80
+ clear(event: string): void {
81
+ this.hooks.delete(event);
82
+ }
83
+
84
+ /**
85
+ * Remove all hooks
86
+ */
87
+ clearAll(): void {
88
+ this.hooks.clear();
89
+ }
90
+
91
+ /**
92
+ * Get registered events
93
+ */
94
+ getEvents(): string[] {
95
+ return Array.from(this.hooks.keys());
96
+ }
97
+ }