speexor 0.1.1 → 0.2.1

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 (54) hide show
  1. package/API-REFERENCE.md +96 -1
  2. package/ARCHITECTURE.md +83 -32
  3. package/BENCHMARKS.md +73 -0
  4. package/CHANGELOG.md +59 -4
  5. package/CODE-OF-CONDUCT.md +83 -83
  6. package/CONTRIBUTING.md +92 -97
  7. package/FAQ.md +132 -105
  8. package/GLOSSARY.md +34 -0
  9. package/LICENSE.md +21 -21
  10. package/PUBLISH.md +82 -77
  11. package/README.md +220 -6
  12. package/REFACTOR-LOG.md +40 -40
  13. package/ROADMAP.md +31 -42
  14. package/SECURITY-DEFAULTS.md +118 -0
  15. package/SECURITY.md +80 -79
  16. package/SUMMARY.md +31 -8
  17. package/TESTING.md +140 -140
  18. package/dist/{agent-5D3BVWNK.js → agent-C64T66XT.js} +4 -4
  19. package/dist/agent-C64T66XT.js.map +1 -0
  20. package/dist/{chunk-B7WLHC4W.js → chunk-5OD5UWB5.js} +322 -121
  21. package/dist/chunk-5OD5UWB5.js.map +1 -0
  22. package/dist/chunk-GOGI3JQD.js +1637 -0
  23. package/dist/chunk-GOGI3JQD.js.map +1 -0
  24. package/dist/{chunk-2F66BZYJ.js → chunk-VEZQT5SX.js} +80 -8
  25. package/dist/chunk-VEZQT5SX.js.map +1 -0
  26. package/dist/cli/index.js +2058 -18
  27. package/dist/cli/index.js.map +1 -1
  28. package/dist/core/index.d.ts +682 -3
  29. package/dist/core/index.js +1 -1
  30. package/dist/index.d.ts +102 -14
  31. package/dist/index.js +55 -29
  32. package/dist/index.js.map +1 -1
  33. package/dist/plugins/index.d.ts +1 -1
  34. package/dist/plugins/index.js +1 -1
  35. package/dist/types-BOMap-tI.d.ts +389 -0
  36. package/docs/PRD03.md +119 -0
  37. package/docs/PRD06.md +125 -0
  38. package/docs/SETUP.md +94 -94
  39. package/docs/TROUBLESHOOTING.md +113 -113
  40. package/docs/adr/0001-record-architecture-decisions.md +44 -0
  41. package/docs/adr/0002-plugin-architecture.md +53 -0
  42. package/docs/adr/0003-recursive-task-decomposition.md +57 -0
  43. package/docs/adr/0004-local-first-security.md +58 -0
  44. package/docs/adr/0005-data-directory-layout.md +69 -0
  45. package/examples/basic.yaml +61 -61
  46. package/package.json +103 -102
  47. package/schema/config.schema.json +119 -119
  48. package/speexor.config.yaml.example +30 -30
  49. package/dist/agent-5D3BVWNK.js.map +0 -1
  50. package/dist/chunk-2F66BZYJ.js.map +0 -1
  51. package/dist/chunk-B7WLHC4W.js.map +0 -1
  52. package/dist/chunk-SXALZEOJ.js +0 -345
  53. package/dist/chunk-SXALZEOJ.js.map +0 -1
  54. package/dist/types-0q_okI2g.d.ts +0 -205
@@ -1,4 +1,4 @@
1
1
  import '../chunk-5NA2TFPG.js';
2
- export { DEFAULT_REACTION_RULES, SpeexorLifecycle, createEventBus, generateDefaultConfig, loadConfig, validateConfig } from '../chunk-2F66BZYJ.js';
2
+ export { DEFAULT_REACTION_RULES, SpeexorLifecycle, configSchema, costGuardSchema, createEventBus, decompositionSchema, extensionsSchema, generateDefaultConfig, governanceSchema, loadConfig, loggingSchema, performanceSchema, riskPolicySchema, schedulerSchema, securitySchema, validateConfig, worktreeHierarchySchema } from '../chunk-VEZQT5SX.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
package/dist/index.d.ts CHANGED
@@ -1,11 +1,45 @@
1
- import { S as SpeexorState, a as ProjectConfig, A as AgentSession, W as WorktreeSession, R as RuntimeSession, T as TrackerEvent } from './types-0q_okI2g.js';
2
- export { b as AgentPlugin, c as AgentProvider, d as AgentStatus, e as AgentTask, C as CIRun, E as EventBus, N as NotifierPlugin, f as PRComment, g as PRInfo, h as PRStatus, i as PluginContext, P as PluginModule, j as PluginSlot, k as ProviderRouting, l as ReactionConfig, m as ReactionRule, n as RuntimePlugin, o as RuntimeType, p as SCMPlugin, q as SessionEventType, r as SessionStatus, s as SpeexorConfig, t as TerminalPlugin, u as TrackerEventType, v as TrackerFilter, w as TrackerIssue, x as TrackerPlugin, y as WorkspacePlugin } from './types-0q_okI2g.js';
1
+ import { a as ProviderCostEntry, S as SpeexorState, b as ProjectConfig, A as AgentSession, W as WorktreeSession, R as RuntimeSession, T as TaskGraph, c as AgentEvent, d as ApprovalItem, D as DecisionLogEntry, e as TrackerEvent } from './types-BOMap-tI.js';
2
+ export { f as AgentEventType, g as AgentPlugin, h as AgentProvider, i as AgentStatus, j as AgentTask, k as ApprovalAxis, l as ApprovalStatus, C as CIRun, m as CommandExecution, n as CostGuardConfig, o as DecompositionConfig, E as EventBus, p as ExtensionManifest, q as ExtensionPermissions, r as ExtensionType, s as ExtensionsConfig, G as GovernanceConfig, L as LoggingConfig, N as NotifierPlugin, t as PRComment, u as PRInfo, v as PRStatus, w as PerformanceConfig, x as PermissionAxis, y as PermissionLevel, z as PluginContext, P as PluginModule, B as PluginSlot, F as ProviderRouting, H as ReactionConfig, I as ReactionRule, J as RiskPolicyConfig, K as RollbackRecord, M as RuntimePlugin, O as RuntimeType, Q as SCMPlugin, U as SchedulerConfig, V as SecurityConfig, X as SessionEventType, Y as SessionStatus, Z as SpeexorConfig, _ as TaskNode, $ as TaskNodeStatus, a0 as TaskOrigin, a1 as TaskResult, a2 as TerminalPlugin, a3 as TrackerEventType, a4 as TrackerFilter, a5 as TrackerIssue, a6 as TrackerPlugin, a7 as WorkspacePlugin, a8 as WorkspaceSession, a9 as WorktreeHierarchyConfig } from './types-BOMap-tI.js';
3
3
  import { SpeexorLifecycle } from './core/index.js';
4
- export { DEFAULT_REACTION_RULES, createEventBus, generateDefaultConfig, loadConfig, validateConfig } from './core/index.js';
4
+ export { DEFAULT_REACTION_RULES, configSchema, costGuardSchema, createEventBus, decompositionSchema, extensionsSchema, generateDefaultConfig, governanceSchema, loadConfig, loggingSchema, performanceSchema, riskPolicySchema, schedulerSchema, securitySchema, validateConfig, worktreeHierarchySchema } from './core/index.js';
5
5
  export { loadAllPlugins, loadPluginByType } from './plugins/index.js';
6
+ import 'zod';
7
+
8
+ interface CostPanelData {
9
+ totalCost: number;
10
+ byProvider: Array<{
11
+ name: string;
12
+ cost: number;
13
+ }>;
14
+ byProject: Array<{
15
+ name: string;
16
+ cost: number;
17
+ }>;
18
+ byTaskNode: Array<{
19
+ taskId: string;
20
+ title: string;
21
+ cost: number;
22
+ }>;
23
+ recentEntries: ProviderCostEntry[];
24
+ budgetExceeded: boolean;
25
+ }
26
+
27
+ interface ActivityEntry {
28
+ timestamp: Date;
29
+ agentId: string;
30
+ type: string;
31
+ summary: string;
32
+ details?: Record<string, unknown>;
33
+ }
6
34
 
7
35
  declare class DashboardState {
8
36
  private state;
37
+ private taskGraphs;
38
+ private agentEvents;
39
+ private approvals;
40
+ private activityFeed;
41
+ private costData;
42
+ private decisionLogEntries;
9
43
  private listeners;
10
44
  getState(): SpeexorState;
11
45
  setProjects(projects: ProjectConfig[]): void;
@@ -16,9 +50,26 @@ declare class DashboardState {
16
50
  removeWorktree(sessionId: string): void;
17
51
  addRuntime(runtime: RuntimeSession): void;
18
52
  removeRuntime(sessionId: string): void;
53
+ updateTaskGraph(graph: TaskGraph): void;
54
+ getTaskGraph(id: string): TaskGraph | undefined;
55
+ getTaskGraphs(): TaskGraph[];
56
+ addAgentEvent(event: AgentEvent): void;
57
+ getAgentEvents(agentId?: string): AgentEvent[];
58
+ getAgentEventsByTask(taskId: string): AgentEvent[];
59
+ addApproval(item: ApprovalItem): void;
60
+ resolveApproval(id: string, status: 'approved' | 'rejected' | 'pending'): void;
61
+ getApprovals(): ApprovalItem[];
62
+ getPendingApprovals(): ApprovalItem[];
63
+ updateActivityFeed(entry: ActivityEntry): void;
64
+ getActivityFeed(limit?: number): ActivityEntry[];
65
+ updateCostData(data: CostPanelData): void;
66
+ getCostData(): CostPanelData | null;
67
+ addDecisionLogEntry(entry: DecisionLogEntry): void;
68
+ getDecisionLogEntries(): DecisionLogEntry[];
69
+ getDecisionLog(): DecisionLogEntry[];
19
70
  onUpdate(listener: () => void): () => void;
20
71
  private notify;
21
- toJSON(): SpeexorState;
72
+ toJSON(): Record<string, unknown>;
22
73
  }
23
74
 
24
75
  declare class DashboardServer {
@@ -27,15 +78,30 @@ declare class DashboardServer {
27
78
  private state;
28
79
  private port;
29
80
  private routes;
81
+ private sseClients;
82
+ private heartbeatTimer;
30
83
  constructor(lifecycle: SpeexorLifecycle, port?: number);
31
84
  private setupRoutes;
32
85
  private handleRequest;
33
86
  private matchPath;
34
87
  private sendJSON;
88
+ private broadcastSSE;
35
89
  private handleStatus;
36
90
  private handleProjects;
37
91
  private handleSessions;
38
92
  private handleHealth;
93
+ private handleSSE;
94
+ private handleTaskGraphs;
95
+ private handleTaskGraphById;
96
+ private handleApprovals;
97
+ private handleApproveApproval;
98
+ private handleRejectApproval;
99
+ private handleActivityFeed;
100
+ private handleCost;
101
+ private handleTaskGraphNode;
102
+ private handleDecisionLog;
103
+ private handleRollbackApproval;
104
+ private handleEvalCalibration;
39
105
  private serveDashboardHTML;
40
106
  start(): void;
41
107
  stop(): void;
@@ -56,20 +122,42 @@ declare class ReactionEngine {
56
122
  resetRetryCount(key: string): void;
57
123
  }
58
124
 
125
+ /**
126
+ * SessionStore — manages persistent state for Speexor sessions.
127
+ *
128
+ * MIGRATED from sync I/O (readFileSync/writeFileSync) to async I/O per:
129
+ * - PRD06 Track 1.4: "Session store migrasi dari sync I/O ke async"
130
+ * - REFACTOR-LOG: technical debt since v0.1.0
131
+ *
132
+ * This ensures non-blocking I/O for ≥10 concurrent agents (PRD01 NFR).
133
+ *
134
+ * Design: Constructor still uses readFileSync once (necessary sync startup),
135
+ * but ALL subsequent writes use async writeFile + serialized write queue
136
+ * to prevent concurrent write conflicts while keeping event loop responsive.
137
+ */
59
138
  declare class SessionStore {
60
139
  private statePath;
61
140
  private state;
141
+ private writeQueue;
62
142
  constructor(baseDir?: string);
63
- private load;
64
- private save;
143
+ /**
144
+ * Synchronous initial load (called once in constructor).
145
+ * This is the only sync I/O call — acceptable because it runs once at startup.
146
+ */
147
+ private loadInitialState;
148
+ /**
149
+ * Async save — enqueues writes to prevent concurrent write conflicts
150
+ * while keeping the event loop responsive.
151
+ */
152
+ private saveAsync;
65
153
  getState(): SpeexorState;
66
- addSession(session: AgentSession): void;
67
- updateSession(sessionId: string, updates: Partial<AgentSession>): void;
68
- removeSession(sessionId: string): void;
69
- addWorktree(worktree: WorktreeSession): void;
70
- removeWorktree(sessionId: string): void;
71
- setProjects(projects: ProjectConfig[]): void;
72
- clear(): void;
154
+ addSession(session: AgentSession): Promise<void>;
155
+ updateSession(sessionId: string, updates: Partial<AgentSession>): Promise<void>;
156
+ removeSession(sessionId: string): Promise<void>;
157
+ addWorktree(worktree: WorktreeSession): Promise<void>;
158
+ removeWorktree(sessionId: string): Promise<void>;
159
+ setProjects(projects: ProjectConfig[]): Promise<void>;
160
+ clear(): Promise<void>;
73
161
  }
74
162
 
75
- export { AgentSession, DashboardServer, DashboardState, ProjectConfig, ReactionEngine, RuntimeSession, SessionStore, SpeexorLifecycle, SpeexorState, TrackerEvent, WorktreeSession, createDashboardServer };
163
+ export { AgentEvent, AgentSession, ApprovalItem, DashboardServer, DashboardState, DecisionLogEntry, ProjectConfig, ProviderCostEntry, ReactionEngine, RuntimeSession, SessionStore, SpeexorLifecycle, SpeexorState, TaskGraph, TrackerEvent, WorktreeSession, createDashboardServer };
package/dist/index.js CHANGED
@@ -1,10 +1,13 @@
1
1
  import './chunk-5NA2TFPG.js';
2
- export { DashboardServer, DashboardState, createDashboardServer } from './chunk-SXALZEOJ.js';
3
- export { DEFAULT_REACTION_RULES, SpeexorLifecycle, createEventBus, generateDefaultConfig, loadConfig, validateConfig } from './chunk-2F66BZYJ.js';
4
- export { loadAllPlugins, loadPluginByType } from './chunk-B7WLHC4W.js';
2
+ export { DashboardServer, DashboardState, createDashboardServer } from './chunk-GOGI3JQD.js';
3
+ export { DEFAULT_REACTION_RULES, SpeexorLifecycle, configSchema, costGuardSchema, createEventBus, decompositionSchema, extensionsSchema, generateDefaultConfig, governanceSchema, loadConfig, loggingSchema, performanceSchema, riskPolicySchema, schedulerSchema, securitySchema, validateConfig, worktreeHierarchySchema } from './chunk-VEZQT5SX.js';
4
+ export { loadAllPlugins, loadPluginByType } from './chunk-5OD5UWB5.js';
5
5
  import Debug from 'debug';
6
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
6
+ import { existsSync, mkdirSync, readFileSync } from 'fs';
7
+ import { writeFile } from 'fs/promises';
7
8
  import { join } from 'path';
9
+ import { homedir } from 'os';
10
+ import 'crypto';
8
11
 
9
12
  var debug = Debug("speexor:reaction");
10
13
  var ReactionEngine = class {
@@ -120,19 +123,35 @@ Data: ${JSON.stringify(event.data)}`,
120
123
  this.retryCounts.delete(key);
121
124
  }
122
125
  };
123
- var debug2 = Debug("speexor:store");
126
+ var debug2 = Debug("speexor:data-dir");
127
+ function resolveBaseDir(cwdDir) {
128
+ const homeDir = join(homedir(), ".speexor");
129
+ if (existsSync(homeDir)) {
130
+ debug2(`Using canonical data dir: ${homeDir}`);
131
+ return homeDir;
132
+ }
133
+ const fallback = cwdDir ?? join(process.cwd(), ".speexor");
134
+ debug2(`Falling back to CWD data dir: ${fallback}`);
135
+ return fallback;
136
+ }
137
+ var debug3 = Debug("speexor:store");
124
138
  var SessionStore = class {
125
139
  statePath;
126
140
  state;
141
+ writeQueue = Promise.resolve();
127
142
  constructor(baseDir) {
128
- const dir = baseDir ?? join(process.cwd(), ".speexor");
143
+ const dir = resolveBaseDir(baseDir);
129
144
  if (!existsSync(dir)) {
130
145
  mkdirSync(dir, { recursive: true });
131
146
  }
132
147
  this.statePath = join(dir, "state.json");
133
- this.state = this.load();
148
+ this.state = this.loadInitialState();
134
149
  }
135
- load() {
150
+ /**
151
+ * Synchronous initial load (called once in constructor).
152
+ * This is the only sync I/O call — acceptable because it runs once at startup.
153
+ */
154
+ loadInitialState() {
136
155
  try {
137
156
  if (existsSync(this.statePath)) {
138
157
  const raw = readFileSync(this.statePath, "utf-8");
@@ -145,25 +164,32 @@ var SessionStore = class {
145
164
  };
146
165
  }
147
166
  } catch (error) {
148
- debug2(`Failed to load state: ${error}`);
167
+ debug3(`Failed to load state: ${error}`);
149
168
  }
150
169
  return { sessions: [], worktrees: [], runtimes: [], projects: [] };
151
170
  }
152
- save() {
153
- try {
154
- writeFileSync(this.statePath, JSON.stringify(this.state, null, 2), "utf-8");
155
- } catch (error) {
156
- debug2(`Failed to save state: ${error}`);
157
- }
171
+ /**
172
+ * Async save — enqueues writes to prevent concurrent write conflicts
173
+ * while keeping the event loop responsive.
174
+ */
175
+ async saveAsync() {
176
+ this.writeQueue = this.writeQueue.then(async () => {
177
+ try {
178
+ await writeFile(this.statePath, JSON.stringify(this.state, null, 2), "utf-8");
179
+ } catch (error) {
180
+ debug3(`Failed to save state: ${error}`);
181
+ }
182
+ });
183
+ return this.writeQueue;
158
184
  }
159
185
  getState() {
160
186
  return { ...this.state };
161
187
  }
162
- addSession(session) {
188
+ async addSession(session) {
163
189
  this.state.sessions.push(session);
164
- this.save();
190
+ await this.saveAsync();
165
191
  }
166
- updateSession(sessionId, updates) {
192
+ async updateSession(sessionId, updates) {
167
193
  const idx = this.state.sessions.findIndex((s) => s.id === sessionId);
168
194
  if (idx !== -1) {
169
195
  const existing = this.state.sessions[idx];
@@ -174,29 +200,29 @@ var SessionStore = class {
174
200
  if (updates.status !== void 0) existing.status = updates.status;
175
201
  if (updates.startedAt !== void 0) existing.startedAt = updates.startedAt;
176
202
  if (updates.runtimeSessionId !== void 0) existing.runtimeSessionId = updates.runtimeSessionId;
177
- this.save();
203
+ await this.saveAsync();
178
204
  }
179
205
  }
180
206
  }
181
- removeSession(sessionId) {
207
+ async removeSession(sessionId) {
182
208
  this.state.sessions = this.state.sessions.filter((s) => s.id !== sessionId);
183
- this.save();
209
+ await this.saveAsync();
184
210
  }
185
- addWorktree(worktree) {
211
+ async addWorktree(worktree) {
186
212
  this.state.worktrees.push(worktree);
187
- this.save();
213
+ await this.saveAsync();
188
214
  }
189
- removeWorktree(sessionId) {
215
+ async removeWorktree(sessionId) {
190
216
  this.state.worktrees = this.state.worktrees.filter((w) => w.id !== sessionId);
191
- this.save();
217
+ await this.saveAsync();
192
218
  }
193
- setProjects(projects) {
219
+ async setProjects(projects) {
194
220
  this.state.projects = projects;
195
- this.save();
221
+ await this.saveAsync();
196
222
  }
197
- clear() {
223
+ async clear() {
198
224
  this.state = { sessions: [], worktrees: [], runtimes: [], projects: [] };
199
- this.save();
225
+ await this.saveAsync();
200
226
  }
201
227
  };
202
228
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/reaction/engine.ts","../src/store/session.ts"],"names":["debug","Debug"],"mappings":";;;;;;;;AAIA,IAAM,KAAA,GAAQ,MAAM,kBAAkB,CAAA;AAS/B,IAAM,iBAAN,MAAqB;AAAA,EAClB,SAAA;AAAA,EACA,WAA8B,EAAC;AAAA,EAC/B,WAAA,uBAAkB,GAAA,EAAoB;AAAA,EAE9C,YAAY,SAAA,EAA6B;AACvC,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AAGjB,IAAA,SAAA,CAAU,QAAA,CAAS,EAAA,CAAG,oBAAA,EAAsB,CAAC,IAAA,KAAkB;AAC7D,MAAA,MAAM,EAAE,SAAA,EAAW,MAAA,EAAO,GAAI,IAAA;AAC9B,MAAA,KAAA,CAAM,CAAA,oBAAA,EAAuB,SAAS,CAAA,UAAA,EAAa,MAAM,CAAA,CAAE,CAAA;AAAA,IAC7D,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,UAAU,QAAA,EAAiC;AACzC,IAAA,IAAA,CAAK,WAAW,EAAC;AAEjB,IAAA,KAAA,MAAW,WAAW,QAAA,EAAU;AAC9B,MAAA,IAAI,CAAC,QAAQ,SAAA,EAAW;AAExB,MAAA,KAAA,MAAW,CAAC,WAAW,IAAI,CAAA,IAAK,OAAO,OAAA,CAAQ,OAAA,CAAQ,SAAS,CAAA,EAAG;AACjE,QAAA,IAAI,CAAC,IAAA,EAAM;AAEX,QAAA,IAAA,CAAK,SAAS,IAAA,CAAK;AAAA,UACjB,SAAA;AAAA,UACA,IAAA;AAAA,UACA,OAAA;AAAA,UACA,MAAA,EAAQ,OAAO,KAAA,KAAwB;AACrC,YAAA,MAAM,IAAA,CAAK,eAAA,CAAgB,KAAA,EAAO,IAAA,EAAM,OAAO,CAAA;AAAA,UACjD;AAAA,SACD,CAAA;AAAA,MACH;AAAA,IACF;AAEA,IAAA,KAAA,CAAM,CAAA,WAAA,EAAc,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA,oBAAA,CAAsB,CAAA;AAAA,EAChE;AAAA,EAEA,MAAM,aAAa,KAAA,EAAoC;AACrD,IAAA,KAAA,CAAM,qBAAqB,KAAA,CAAM,IAAI,CAAA,WAAA,EAAc,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAElE,IAAA,MAAM,gBAAA,GAAmB,KAAK,QAAA,CAAS,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,SAAA,KAAc,KAAA,CAAM,IAAI,CAAA;AAE/E,IAAA,KAAA,MAAW,WAAW,gBAAA,EAAkB;AACtC,MAAA,MAAM,MAAM,CAAA,EAAG,KAAA,CAAM,OAAO,CAAA,CAAA,EAAI,QAAQ,SAAS,CAAA,CAAA;AACjD,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,GAAG,CAAA,IAAK,CAAA;AAE7C,MAAA,IAAI,OAAA,IAAW,OAAA,CAAQ,IAAA,CAAK,OAAA,EAAS;AACnC,QAAA,KAAA,CAAM,CAAA,wBAAA,EAA2B,GAAG,CAAA,EAAA,EAAK,OAAO,IAAI,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAA,CAAA,CAAG,CAAA;AAC3E,QAAA,IAAI,OAAA,CAAQ,IAAA,CAAK,MAAA,KAAW,UAAA,EAAY;AACtC,UAAA,IAAA,CAAK,QAAA,CAAS,KAAA,EAAO,OAAA,CAAQ,OAAO,CAAA;AAAA,QACtC;AACA,QAAA;AAAA,MACF;AAEA,MAAA,IAAI;AACF,QAAA,MAAM,OAAA,CAAQ,OAAO,KAAK,CAAA;AAC1B,QAAA,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,GAAA,EAAK,OAAA,GAAU,CAAC,CAAA;AAAA,MACvC,SAAS,KAAA,EAAO;AACd,QAAA,KAAA,CAAM,CAAA,oBAAA,EAAuB,GAAG,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,eAAA,CAAgB,KAAA,EAAqB,IAAA,EAAoB,OAAA,EAAuC;AAC5G,IAAA,IAAI,CAAC,KAAK,IAAA,EAAM;AACd,MAAA,KAAA,CAAM,CAAA,SAAA,EAAY,KAAA,CAAM,IAAI,CAAA,qCAAA,CAAkC,CAAA;AAC9D,MAAA,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,IAAA,CAAK,oBAAA,EAAsB;AAAA,QACjD,WAAW,KAAA,CAAM,IAAA;AAAA,QACjB,QAAQ,KAAA,CAAM,OAAA;AAAA,QACd,MAAA,EAAQ;AAAA,OACT,CAAA;AACD,MAAA;AAAA,IACF;AAEA,IAAA,QAAQ,KAAK,MAAA;AAAQ,MACnB,KAAK,KAAA;AACH,QAAA,MAAM,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,OAAO,CAAA;AACjC,QAAA;AAAA,MACF,KAAK,QAAA;AACH,QAAA,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,IAAA,CAAK,oBAAA,EAAsB;AAAA,UACjD,WAAW,KAAA,CAAM,IAAA;AAAA,UACjB,QAAQ,KAAA,CAAM,OAAA;AAAA,UACd,MAAA,EAAQ;AAAA,SACT,CAAA;AACD,QAAA;AAAA,MACF,KAAK,UAAA;AACH,QAAA,IAAA,CAAK,QAAA,CAAS,OAAO,OAAO,CAAA;AAC5B,QAAA;AAAA,MACF,KAAK,MAAA;AACH,QAAA,KAAA,CAAM,CAAA,sBAAA,EAAyB,KAAA,CAAM,IAAI,CAAA,CAAE,CAAA;AAC3C,QAAA;AAAA;AACJ,EACF;AAAA,EAEA,MAAc,OAAA,CAAQ,KAAA,EAAqB,OAAA,EAAuC;AAChF,IAAA,MAAM,IAAA,GAAkB;AAAA,MACtB,IAAI,CAAA,IAAA,EAAO,KAAA,CAAM,OAAO,CAAA,CAAA,EAAI,IAAA,CAAK,KAAK,CAAA,CAAA;AAAA,MACtC,OAAO,CAAA,UAAA,EAAa,KAAA,CAAM,IAAI,CAAA,WAAA,EAAc,MAAM,OAAO,CAAA,CAAA;AAAA,MACzD,WAAA,EAAa,CAAA,kCAAA,EAAqC,KAAA,CAAM,IAAI;AAAA,OAAA,EAAY,MAAM,OAAO;AAAA,MAAA,EAAW,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,IAAI,CAAC,CAAA,CAAA;AAAA,MAC1H,YAAY,OAAA,CAAQ,UAAA;AAAA,MACpB,MAAA,EAAQ,CAAA,YAAA,EAAe,KAAA,CAAM,OAAO,CAAA,CAAA;AAAA,MACpC,QAAA,EAAU,QAAQ,QAAA,CAAS;AAAA,KAC7B;AAEA,IAAA,KAAA,CAAM,CAAA,iCAAA,EAAoC,IAAA,CAAK,EAAE,CAAA,CAAE,CAAA;AAEnD,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,SAAA,CAAU,UAAA,CAAW,IAAI,CAAA;AAAA,IACtC,SAAS,KAAA,EAAO;AACd,MAAA,KAAA,CAAM,CAAA,uBAAA,EAA0B,KAAK,CAAA,CAAE,CAAA;AAAA,IACzC;AAAA,EACF;AAAA,EAEQ,QAAA,CAAS,OAAqB,QAAA,EAA+B;AACnE,IAAA,KAAA,CAAM,eAAe,KAAA,CAAM,IAAI,CAAA,WAAA,EAAc,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAE5D,IAAA,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,IAAA,CAAK,oBAAA,EAAsB;AAAA,MACjD,WAAW,KAAA,CAAM,IAAA;AAAA,MACjB,QAAQ,KAAA,CAAM,OAAA;AAAA,MACd,MAAA,EAAQ,UAAA;AAAA,MACR,SAAS,CAAA,yBAAA,EAA4B,KAAA,CAAM,IAAI,CAAA,UAAA,EAAa,MAAM,OAAO,CAAA;AAAA,KAC1E,CAAA;AAAA,EACH;AAAA,EAEA,cAAc,GAAA,EAAqB;AACjC,IAAA,OAAO,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,GAAG,CAAA,IAAK,CAAA;AAAA,EACtC;AAAA,EAEA,gBAAgB,GAAA,EAAmB;AACjC,IAAA,IAAA,CAAK,WAAA,CAAY,OAAO,GAAG,CAAA;AAAA,EAC7B;AACF;AC5IA,IAAMA,MAAAA,GAAQC,MAAM,eAAe,CAAA;AAE5B,IAAM,eAAN,MAAmB;AAAA,EAChB,SAAA;AAAA,EACA,KAAA;AAAA,EAER,YAAY,OAAA,EAAkB;AAC5B,IAAA,MAAM,MAAM,OAAA,IAAW,IAAA,CAAK,OAAA,CAAQ,GAAA,IAAO,UAAU,CAAA;AAErD,IAAA,IAAI,CAAC,UAAA,CAAW,GAAG,CAAA,EAAG;AACpB,MAAA,SAAA,CAAU,GAAA,EAAK,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AAAA,IACpC;AAEA,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA,CAAK,GAAA,EAAK,YAAY,CAAA;AACvC,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAK,IAAA,EAAK;AAAA,EACzB;AAAA,EAEQ,IAAA,GAAqB;AAC3B,IAAA,IAAI;AACF,MAAA,IAAI,UAAA,CAAW,IAAA,CAAK,SAAS,CAAA,EAAG;AAC9B,QAAA,MAAM,GAAA,GAAM,YAAA,CAAa,IAAA,CAAK,SAAA,EAAW,OAAO,CAAA;AAChD,QAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC3B,QAAA,OAAO;AAAA,UACL,QAAA,EAAU,IAAA,CAAK,QAAA,IAAY,EAAC;AAAA,UAC5B,SAAA,EAAW,IAAA,CAAK,SAAA,IAAa,EAAC;AAAA,UAC9B,QAAA,EAAU,IAAA,CAAK,QAAA,IAAY,EAAC;AAAA,UAC5B,QAAA,EAAU,IAAA,CAAK,QAAA,IAAY;AAAC,SAC9B;AAAA,MACF;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAAD,MAAAA,CAAM,CAAA,sBAAA,EAAyB,KAAK,CAAA,CAAE,CAAA;AAAA,IACxC;AAEA,IAAA,OAAO,EAAE,QAAA,EAAU,EAAC,EAAG,SAAA,EAAW,EAAC,EAAG,QAAA,EAAU,EAAC,EAAG,QAAA,EAAU,EAAC,EAAE;AAAA,EACnE;AAAA,EAEQ,IAAA,GAAa;AACnB,IAAA,IAAI;AACF,MAAA,aAAA,CAAc,IAAA,CAAK,WAAW,IAAA,CAAK,SAAA,CAAU,KAAK,KAAA,EAAO,IAAA,EAAM,CAAC,CAAA,EAAG,OAAO,CAAA;AAAA,IAC5E,SAAS,KAAA,EAAO;AACd,MAAAA,MAAAA,CAAM,CAAA,sBAAA,EAAyB,KAAK,CAAA,CAAE,CAAA;AAAA,IACxC;AAAA,EACF;AAAA,EAEA,QAAA,GAAyB;AACvB,IAAA,OAAO,EAAE,GAAG,IAAA,CAAK,KAAA,EAAM;AAAA,EACzB;AAAA,EAEA,WAAW,OAAA,EAA6B;AACtC,IAAA,IAAA,CAAK,KAAA,CAAM,QAAA,CAAS,IAAA,CAAK,OAAO,CAAA;AAChC,IAAA,IAAA,CAAK,IAAA,EAAK;AAAA,EACZ;AAAA,EAEA,aAAA,CAAc,WAAmB,OAAA,EAAsC;AACrE,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,CAAM,QAAA,CAAS,UAAU,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,SAAS,CAAA;AACnE,IAAA,IAAI,QAAQ,EAAA,EAAI;AACd,MAAA,MAAM,QAAA,GAAW,IAAA,CAAK,KAAA,CAAM,QAAA,CAAS,GAAG,CAAA;AACxC,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,IAAI,OAAA,CAAQ,EAAA,KAAO,MAAA,EAAW,QAAA,CAAS,KAAK,OAAA,CAAQ,EAAA;AACpD,QAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,MAAA,EAAW,QAAA,CAAS,SAAS,OAAA,CAAQ,MAAA;AAC5D,QAAA,IAAI,OAAA,CAAQ,QAAA,KAAa,MAAA,EAAW,QAAA,CAAS,WAAW,OAAA,CAAQ,QAAA;AAChE,QAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,MAAA,EAAW,QAAA,CAAS,SAAS,OAAA,CAAQ,MAAA;AAC5D,QAAA,IAAI,OAAA,CAAQ,SAAA,KAAc,MAAA,EAAW,QAAA,CAAS,YAAY,OAAA,CAAQ,SAAA;AAClE,QAAA,IAAI,OAAA,CAAQ,gBAAA,KAAqB,MAAA,EAAW,QAAA,CAAS,mBAAmB,OAAA,CAAQ,gBAAA;AAChF,QAAA,IAAA,CAAK,IAAA,EAAK;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA,EAEA,cAAc,SAAA,EAAyB;AACrC,IAAA,IAAA,CAAK,KAAA,CAAM,QAAA,GAAW,IAAA,CAAK,KAAA,CAAM,QAAA,CAAS,OAAO,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,SAAS,CAAA;AAC1E,IAAA,IAAA,CAAK,IAAA,EAAK;AAAA,EACZ;AAAA,EAEA,YAAY,QAAA,EAAiC;AAC3C,IAAA,IAAA,CAAK,KAAA,CAAM,SAAA,CAAU,IAAA,CAAK,QAAQ,CAAA;AAClC,IAAA,IAAA,CAAK,IAAA,EAAK;AAAA,EACZ;AAAA,EAEA,eAAe,SAAA,EAAyB;AACtC,IAAA,IAAA,CAAK,KAAA,CAAM,SAAA,GAAY,IAAA,CAAK,KAAA,CAAM,SAAA,CAAU,OAAO,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,SAAS,CAAA;AAC5E,IAAA,IAAA,CAAK,IAAA,EAAK;AAAA,EACZ;AAAA,EAEA,YAAY,QAAA,EAAiC;AAC3C,IAAA,IAAA,CAAK,MAAM,QAAA,GAAW,QAAA;AACtB,IAAA,IAAA,CAAK,IAAA,EAAK;AAAA,EACZ;AAAA,EAEA,KAAA,GAAc;AACZ,IAAA,IAAA,CAAK,KAAA,GAAQ,EAAE,QAAA,EAAU,EAAC,EAAG,SAAA,EAAW,EAAC,EAAG,QAAA,EAAU,EAAC,EAAG,QAAA,EAAU,EAAC,EAAE;AACvE,IAAA,IAAA,CAAK,IAAA,EAAK;AAAA,EACZ;AACF","file":"index.js","sourcesContent":["import type { SpeexorLifecycle } from '../core/lifecycle.js'\nimport type { ReactionRule, TrackerEvent, ProjectConfig, AgentTask } from '../core/types.js'\nimport Debug from 'debug'\n\nconst debug = Debug('speexor:reaction')\n\ninterface ReactionHandler {\n eventType: string\n rule: ReactionRule\n project: ProjectConfig\n handle(event: TrackerEvent): Promise<void>\n}\n\nexport class ReactionEngine {\n private lifecycle: SpeexorLifecycle\n private handlers: ReactionHandler[] = []\n private retryCounts = new Map<string, number>()\n\n constructor(lifecycle: SpeexorLifecycle) {\n this.lifecycle = lifecycle\n\n // Listen to lifecycle events\n lifecycle.eventBus.on('reaction:triggered', (data: unknown) => {\n const { eventType, taskId } = data as { eventType: string; taskId: string }\n debug(`Reaction triggered: ${eventType} for task ${taskId}`)\n })\n }\n\n configure(projects: ProjectConfig[]): void {\n this.handlers = []\n\n for (const project of projects) {\n if (!project.reactions) continue\n\n for (const [eventType, rule] of Object.entries(project.reactions)) {\n if (!rule) continue\n\n this.handlers.push({\n eventType,\n rule,\n project,\n handle: async (event: TrackerEvent) => {\n await this.executeReaction(event, rule, project)\n },\n })\n }\n }\n\n debug(`Configured ${this.handlers.length} reaction handler(s)`)\n }\n\n async processEvent(event: TrackerEvent): Promise<void> {\n debug(`Processing event: ${event.type} for issue ${event.issueId}`)\n\n const matchingHandlers = this.handlers.filter((h) => h.eventType === event.type)\n\n for (const handler of matchingHandlers) {\n const key = `${event.issueId}:${handler.eventType}`\n const retries = this.retryCounts.get(key) ?? 0\n\n if (retries >= handler.rule.retries) {\n debug(`Max retries reached for ${key} (${retries}/${handler.rule.retries})`)\n if (handler.rule.action === 'escalate') {\n this.escalate(event, handler.project)\n }\n continue\n }\n\n try {\n await handler.handle(event)\n this.retryCounts.set(key, retries + 1)\n } catch (error) {\n debug(`Reaction failed for ${key}: ${error}`)\n }\n }\n }\n\n private async executeReaction(event: TrackerEvent, rule: ReactionRule, project: ProjectConfig): Promise<void> {\n if (!rule.auto) {\n debug(`Rule for ${event.type} is not auto — notifying instead`)\n this.lifecycle.eventBus.emit('reaction:triggered', {\n eventType: event.type,\n taskId: event.issueId,\n action: 'notify',\n })\n return\n }\n\n switch (rule.action) {\n case 'fix':\n await this.autoFix(event, project)\n break\n case 'notify':\n this.lifecycle.eventBus.emit('reaction:triggered', {\n eventType: event.type,\n taskId: event.issueId,\n action: 'notify',\n })\n break\n case 'escalate':\n this.escalate(event, project)\n break\n case 'skip':\n debug(`Skipping reaction for ${event.type}`)\n break\n }\n }\n\n private async autoFix(event: TrackerEvent, project: ProjectConfig): Promise<void> {\n const task: AgentTask = {\n id: `fix-${event.issueId}-${Date.now()}`,\n title: `Auto-fix: ${event.type} for issue ${event.issueId}`,\n description: `Automated fix triggered by event: ${event.type}\\nIssue: ${event.issueId}\\nData: ${JSON.stringify(event.data)}`,\n repository: project.repository,\n branch: `speexor/fix-${event.issueId}`,\n provider: project.provider.primary,\n }\n\n debug(`Auto-fix spawning agent for task ${task.id}`)\n\n try {\n await this.lifecycle.spawnAgent(task)\n } catch (error) {\n debug(`Auto-fix spawn failed: ${error}`)\n }\n }\n\n private escalate(event: TrackerEvent, _project: ProjectConfig): void {\n debug(`Escalating: ${event.type} for issue ${event.issueId}`)\n\n this.lifecycle.eventBus.emit('reaction:triggered', {\n eventType: event.type,\n taskId: event.issueId,\n action: 'escalate',\n message: `Max retries exceeded for ${event.type} on issue ${event.issueId}`,\n })\n }\n\n getRetryCount(key: string): number {\n return this.retryCounts.get(key) ?? 0\n }\n\n resetRetryCount(key: string): void {\n this.retryCounts.delete(key)\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { SpeexorState, AgentSession, WorktreeSession, ProjectConfig } from '../core/types.js'\nimport Debug from 'debug'\n\nconst debug = Debug('speexor:store')\n\nexport class SessionStore {\n private statePath: string\n private state: SpeexorState\n\n constructor(baseDir?: string) {\n const dir = baseDir ?? join(process.cwd(), '.speexor')\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n\n this.statePath = join(dir, 'state.json')\n this.state = this.load()\n }\n\n private load(): SpeexorState {\n try {\n if (existsSync(this.statePath)) {\n const raw = readFileSync(this.statePath, 'utf-8')\n const data = JSON.parse(raw)\n return {\n sessions: data.sessions ?? [],\n worktrees: data.worktrees ?? [],\n runtimes: data.runtimes ?? [],\n projects: data.projects ?? [],\n }\n }\n } catch (error) {\n debug(`Failed to load state: ${error}`)\n }\n\n return { sessions: [], worktrees: [], runtimes: [], projects: [] }\n }\n\n private save(): void {\n try {\n writeFileSync(this.statePath, JSON.stringify(this.state, null, 2), 'utf-8')\n } catch (error) {\n debug(`Failed to save state: ${error}`)\n }\n }\n\n getState(): SpeexorState {\n return { ...this.state }\n }\n\n addSession(session: AgentSession): void {\n this.state.sessions.push(session)\n this.save()\n }\n\n updateSession(sessionId: string, updates: Partial<AgentSession>): void {\n const idx = this.state.sessions.findIndex((s) => s.id === sessionId)\n if (idx !== -1) {\n const existing = this.state.sessions[idx]\n if (existing) {\n if (updates.id !== undefined) existing.id = updates.id\n if (updates.taskId !== undefined) existing.taskId = updates.taskId\n if (updates.provider !== undefined) existing.provider = updates.provider\n if (updates.status !== undefined) existing.status = updates.status\n if (updates.startedAt !== undefined) existing.startedAt = updates.startedAt\n if (updates.runtimeSessionId !== undefined) existing.runtimeSessionId = updates.runtimeSessionId\n this.save()\n }\n }\n }\n\n removeSession(sessionId: string): void {\n this.state.sessions = this.state.sessions.filter((s) => s.id !== sessionId)\n this.save()\n }\n\n addWorktree(worktree: WorktreeSession): void {\n this.state.worktrees.push(worktree)\n this.save()\n }\n\n removeWorktree(sessionId: string): void {\n this.state.worktrees = this.state.worktrees.filter((w) => w.id !== sessionId)\n this.save()\n }\n\n setProjects(projects: ProjectConfig[]): void {\n this.state.projects = projects\n this.save()\n }\n\n clear(): void {\n this.state = { sessions: [], worktrees: [], runtimes: [], projects: [] }\n this.save()\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/reaction/engine.ts","../src/core/data-dir.ts","../src/store/session.ts"],"names":["debug","Debug","existsSync","join"],"mappings":"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","file":"index.js","sourcesContent":["import type { SpeexorLifecycle } from '../core/lifecycle.js'\r\nimport type { ReactionRule, TrackerEvent, ProjectConfig, AgentTask } from '../core/types.js'\r\nimport Debug from 'debug'\r\n\r\nconst debug = Debug('speexor:reaction')\r\n\r\ninterface ReactionHandler {\r\n eventType: string\r\n rule: ReactionRule\r\n project: ProjectConfig\r\n handle(event: TrackerEvent): Promise<void>\r\n}\r\n\r\nexport class ReactionEngine {\r\n private lifecycle: SpeexorLifecycle\r\n private handlers: ReactionHandler[] = []\r\n private retryCounts = new Map<string, number>()\r\n\r\n constructor(lifecycle: SpeexorLifecycle) {\r\n this.lifecycle = lifecycle\r\n\r\n // Listen to lifecycle events\r\n lifecycle.eventBus.on('reaction:triggered', (data: unknown) => {\r\n const { eventType, taskId } = data as { eventType: string; taskId: string }\r\n debug(`Reaction triggered: ${eventType} for task ${taskId}`)\r\n })\r\n }\r\n\r\n configure(projects: ProjectConfig[]): void {\r\n this.handlers = []\r\n\r\n for (const project of projects) {\r\n if (!project.reactions) continue\r\n\r\n for (const [eventType, rule] of Object.entries(project.reactions)) {\r\n if (!rule) continue\r\n\r\n this.handlers.push({\r\n eventType,\r\n rule,\r\n project,\r\n handle: async (event: TrackerEvent) => {\r\n await this.executeReaction(event, rule, project)\r\n },\r\n })\r\n }\r\n }\r\n\r\n debug(`Configured ${this.handlers.length} reaction handler(s)`)\r\n }\r\n\r\n async processEvent(event: TrackerEvent): Promise<void> {\r\n debug(`Processing event: ${event.type} for issue ${event.issueId}`)\r\n\r\n const matchingHandlers = this.handlers.filter((h) => h.eventType === event.type)\r\n\r\n for (const handler of matchingHandlers) {\r\n const key = `${event.issueId}:${handler.eventType}`\r\n const retries = this.retryCounts.get(key) ?? 0\r\n\r\n if (retries >= handler.rule.retries) {\r\n debug(`Max retries reached for ${key} (${retries}/${handler.rule.retries})`)\r\n if (handler.rule.action === 'escalate') {\r\n this.escalate(event, handler.project)\r\n }\r\n continue\r\n }\r\n\r\n try {\r\n await handler.handle(event)\r\n this.retryCounts.set(key, retries + 1)\r\n } catch (error) {\r\n debug(`Reaction failed for ${key}: ${error}`)\r\n }\r\n }\r\n }\r\n\r\n private async executeReaction(event: TrackerEvent, rule: ReactionRule, project: ProjectConfig): Promise<void> {\r\n if (!rule.auto) {\r\n debug(`Rule for ${event.type} is not auto — notifying instead`)\r\n this.lifecycle.eventBus.emit('reaction:triggered', {\r\n eventType: event.type,\r\n taskId: event.issueId,\r\n action: 'notify',\r\n })\r\n return\r\n }\r\n\r\n switch (rule.action) {\r\n case 'fix':\r\n await this.autoFix(event, project)\r\n break\r\n case 'notify':\r\n this.lifecycle.eventBus.emit('reaction:triggered', {\r\n eventType: event.type,\r\n taskId: event.issueId,\r\n action: 'notify',\r\n })\r\n break\r\n case 'escalate':\r\n this.escalate(event, project)\r\n break\r\n case 'skip':\r\n debug(`Skipping reaction for ${event.type}`)\r\n break\r\n }\r\n }\r\n\r\n private async autoFix(event: TrackerEvent, project: ProjectConfig): Promise<void> {\r\n const task: AgentTask = {\r\n id: `fix-${event.issueId}-${Date.now()}`,\r\n title: `Auto-fix: ${event.type} for issue ${event.issueId}`,\r\n description: `Automated fix triggered by event: ${event.type}\\nIssue: ${event.issueId}\\nData: ${JSON.stringify(event.data)}`,\r\n repository: project.repository,\r\n branch: `speexor/fix-${event.issueId}`,\r\n provider: project.provider.primary,\r\n }\r\n\r\n debug(`Auto-fix spawning agent for task ${task.id}`)\r\n\r\n try {\r\n await this.lifecycle.spawnAgent(task)\r\n } catch (error) {\r\n debug(`Auto-fix spawn failed: ${error}`)\r\n }\r\n }\r\n\r\n private escalate(event: TrackerEvent, _project: ProjectConfig): void {\r\n debug(`Escalating: ${event.type} for issue ${event.issueId}`)\r\n\r\n this.lifecycle.eventBus.emit('reaction:triggered', {\r\n eventType: event.type,\r\n taskId: event.issueId,\r\n action: 'escalate',\r\n message: `Max retries exceeded for ${event.type} on issue ${event.issueId}`,\r\n })\r\n }\r\n\r\n getRetryCount(key: string): number {\r\n return this.retryCounts.get(key) ?? 0\r\n }\r\n\r\n resetRetryCount(key: string): void {\r\n this.retryCounts.delete(key)\r\n }\r\n}\r\n","import { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { existsSync } from 'node:fs'\nimport { createHash } from 'node:crypto'\nimport Debug from 'debug'\n\nconst debug = Debug('speexor:data-dir')\n\nfunction hashProjectId(projectId: string): string {\n return createHash('sha256').update(projectId).digest('hex').substring(0, 8)\n}\n\nexport function getSpeexorDataDir(projectId?: string): string {\n const home = homedir()\n const base = join(home, '.speexor')\n\n if (projectId) {\n const h = hashProjectId(projectId)\n return join(base, `${h}-${projectId}`)\n }\n\n return base\n}\n\nexport function resolveBaseDir(cwdDir?: string): string {\n const homeDir = join(homedir(), '.speexor')\n if (existsSync(homeDir)) {\n debug(`Using canonical data dir: ${homeDir}`)\n return homeDir\n }\n const fallback = cwdDir ?? join(process.cwd(), '.speexor')\n debug(`Falling back to CWD data dir: ${fallback}`)\n return fallback\n}\n","import { existsSync, mkdirSync, readFileSync } from 'node:fs'\nimport { writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { SpeexorState, AgentSession, WorktreeSession, ProjectConfig } from '../core/types.js'\nimport { resolveBaseDir } from '../core/data-dir.js'\nimport Debug from 'debug'\n\nconst debug = Debug('speexor:store')\n\n/**\n * SessionStore — manages persistent state for Speexor sessions.\n *\n * MIGRATED from sync I/O (readFileSync/writeFileSync) to async I/O per:\n * - PRD06 Track 1.4: \"Session store migrasi dari sync I/O ke async\"\n * - REFACTOR-LOG: technical debt since v0.1.0\n *\n * This ensures non-blocking I/O for ≥10 concurrent agents (PRD01 NFR).\n *\n * Design: Constructor still uses readFileSync once (necessary sync startup),\n * but ALL subsequent writes use async writeFile + serialized write queue\n * to prevent concurrent write conflicts while keeping event loop responsive.\n */\nexport class SessionStore {\n private statePath: string\n private state: SpeexorState\n private writeQueue: Promise<void> = Promise.resolve()\n\n constructor(baseDir?: string) {\n const dir = resolveBaseDir(baseDir)\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n\n this.statePath = join(dir, 'state.json')\n this.state = this.loadInitialState()\n }\n\n /**\n * Synchronous initial load (called once in constructor).\n * This is the only sync I/O call — acceptable because it runs once at startup.\n */\n private loadInitialState(): SpeexorState {\n try {\n if (existsSync(this.statePath)) {\n const raw = readFileSync(this.statePath, 'utf-8')\n const data = JSON.parse(raw)\n return {\n sessions: data.sessions ?? [],\n worktrees: data.worktrees ?? [],\n runtimes: data.runtimes ?? [],\n projects: data.projects ?? [],\n }\n }\n } catch (error) {\n debug(`Failed to load state: ${error}`)\n }\n\n return { sessions: [], worktrees: [], runtimes: [], projects: [] }\n }\n\n /**\n * Async save — enqueues writes to prevent concurrent write conflicts\n * while keeping the event loop responsive.\n */\n private async saveAsync(): Promise<void> {\n this.writeQueue = this.writeQueue.then(async () => {\n try {\n await writeFile(this.statePath, JSON.stringify(this.state, null, 2), 'utf-8')\n } catch (error) {\n debug(`Failed to save state: ${error}`)\n }\n })\n return this.writeQueue\n }\n\n getState(): SpeexorState {\n return { ...this.state }\n }\n\n async addSession(session: AgentSession): Promise<void> {\n this.state.sessions.push(session)\n await this.saveAsync()\n }\n\n async updateSession(sessionId: string, updates: Partial<AgentSession>): Promise<void> {\n const idx = this.state.sessions.findIndex((s) => s.id === sessionId)\n if (idx !== -1) {\n const existing = this.state.sessions[idx]\n if (existing) {\n if (updates.id !== undefined) existing.id = updates.id\n if (updates.taskId !== undefined) existing.taskId = updates.taskId\n if (updates.provider !== undefined) existing.provider = updates.provider\n if (updates.status !== undefined) existing.status = updates.status\n if (updates.startedAt !== undefined) existing.startedAt = updates.startedAt\n if (updates.runtimeSessionId !== undefined) existing.runtimeSessionId = updates.runtimeSessionId\n await this.saveAsync()\n }\n }\n }\n\n async removeSession(sessionId: string): Promise<void> {\n this.state.sessions = this.state.sessions.filter((s) => s.id !== sessionId)\n await this.saveAsync()\n }\n\n async addWorktree(worktree: WorktreeSession): Promise<void> {\n this.state.worktrees.push(worktree)\n await this.saveAsync()\n }\n\n async removeWorktree(sessionId: string): Promise<void> {\n this.state.worktrees = this.state.worktrees.filter((w) => w.id !== sessionId)\n await this.saveAsync()\n }\n\n async setProjects(projects: ProjectConfig[]): Promise<void> {\n this.state.projects = projects\n await this.saveAsync()\n }\n\n async clear(): Promise<void> {\n this.state = { sessions: [], worktrees: [], runtimes: [], projects: [] }\n await this.saveAsync()\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { P as PluginModule } from '../types-0q_okI2g.js';
1
+ import { P as PluginModule } from '../types-BOMap-tI.js';
2
2
 
3
3
  declare function loadAllPlugins(): PluginModule[];
4
4
  declare function loadPluginByType(type: string): PluginModule | undefined;
@@ -1,3 +1,3 @@
1
- export { loadAllPlugins, loadPluginByType } from '../chunk-B7WLHC4W.js';
1
+ export { loadAllPlugins, loadPluginByType } from '../chunk-5OD5UWB5.js';
2
2
  //# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map