agentic-qe 2.7.1 → 2.7.3

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 (157) hide show
  1. package/CHANGELOG.md +179 -0
  2. package/README.md +2 -2
  3. package/dist/agents/CodeIntelligenceAgent.d.ts.map +1 -1
  4. package/dist/agents/CodeIntelligenceAgent.js +7 -5
  5. package/dist/agents/CodeIntelligenceAgent.js.map +1 -1
  6. package/dist/cli/commands/knowledge-graph.d.ts.map +1 -1
  7. package/dist/cli/commands/knowledge-graph.js +4 -2
  8. package/dist/cli/commands/knowledge-graph.js.map +1 -1
  9. package/dist/cli/commands/migrate/index.d.ts +14 -0
  10. package/dist/cli/commands/migrate/index.d.ts.map +1 -0
  11. package/dist/cli/commands/migrate/index.js +283 -0
  12. package/dist/cli/commands/migrate/index.js.map +1 -0
  13. package/dist/cli/formatters/KGOutputFormatter.d.ts.map +1 -1
  14. package/dist/cli/formatters/KGOutputFormatter.js +15 -6
  15. package/dist/cli/formatters/KGOutputFormatter.js.map +1 -1
  16. package/dist/cli/index.js +3 -0
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/cli/init/database-init.d.ts.map +1 -1
  19. package/dist/cli/init/database-init.js +105 -0
  20. package/dist/cli/init/database-init.js.map +1 -1
  21. package/dist/code-intelligence/orchestrator/CodeIntelligenceOrchestrator.d.ts +9 -3
  22. package/dist/code-intelligence/orchestrator/CodeIntelligenceOrchestrator.d.ts.map +1 -1
  23. package/dist/code-intelligence/orchestrator/CodeIntelligenceOrchestrator.js +41 -3
  24. package/dist/code-intelligence/orchestrator/CodeIntelligenceOrchestrator.js.map +1 -1
  25. package/dist/code-intelligence/service/CodeIntelligenceService.js +1 -1
  26. package/dist/code-intelligence/service/CodeIntelligenceService.js.map +1 -1
  27. package/dist/core/memory/HNSWVectorMemory.js +1 -1
  28. package/dist/learning/QLearning.d.ts +110 -2
  29. package/dist/learning/QLearning.d.ts.map +1 -1
  30. package/dist/learning/QLearning.js +218 -1
  31. package/dist/learning/QLearning.js.map +1 -1
  32. package/dist/learning/metrics/LearningMetrics.d.ts +37 -0
  33. package/dist/learning/metrics/LearningMetrics.d.ts.map +1 -1
  34. package/dist/learning/metrics/LearningMetrics.js +73 -0
  35. package/dist/learning/metrics/LearningMetrics.js.map +1 -1
  36. package/dist/mcp/handlers/fleet-init.d.ts +10 -0
  37. package/dist/mcp/handlers/fleet-init.d.ts.map +1 -1
  38. package/dist/mcp/handlers/fleet-init.js +61 -0
  39. package/dist/mcp/handlers/fleet-init.js.map +1 -1
  40. package/dist/mcp/handlers/learning/learning-store-pattern.d.ts +13 -0
  41. package/dist/mcp/handlers/learning/learning-store-pattern.d.ts.map +1 -1
  42. package/dist/mcp/handlers/learning/learning-store-pattern.js +38 -0
  43. package/dist/mcp/handlers/learning/learning-store-pattern.js.map +1 -1
  44. package/dist/mcp/handlers/phase3/Phase3DomainTools.d.ts +89 -0
  45. package/dist/mcp/handlers/phase3/Phase3DomainTools.d.ts.map +1 -1
  46. package/dist/mcp/handlers/phase3/Phase3DomainTools.js +110 -1
  47. package/dist/mcp/handlers/phase3/Phase3DomainTools.js.map +1 -1
  48. package/dist/mcp/handlers/task-orchestrate.d.ts +27 -1
  49. package/dist/mcp/handlers/task-orchestrate.d.ts.map +1 -1
  50. package/dist/mcp/handlers/task-orchestrate.js +188 -8
  51. package/dist/mcp/handlers/task-orchestrate.js.map +1 -1
  52. package/dist/mcp/server-instructions.d.ts +1 -1
  53. package/dist/mcp/server-instructions.js +1 -1
  54. package/dist/mcp/server.d.ts +6 -0
  55. package/dist/mcp/server.d.ts.map +1 -1
  56. package/dist/mcp/server.js +92 -2
  57. package/dist/mcp/server.js.map +1 -1
  58. package/dist/mcp/services/AgentRegistry.d.ts +13 -0
  59. package/dist/mcp/services/AgentRegistry.d.ts.map +1 -1
  60. package/dist/mcp/services/AgentRegistry.js +66 -0
  61. package/dist/mcp/services/AgentRegistry.js.map +1 -1
  62. package/dist/mcp/tools/qe/quality-gates/evaluate-quality-gate.d.ts +55 -0
  63. package/dist/mcp/tools/qe/quality-gates/evaluate-quality-gate.d.ts.map +1 -1
  64. package/dist/mcp/tools/qe/quality-gates/evaluate-quality-gate.js +233 -0
  65. package/dist/mcp/tools/qe/quality-gates/evaluate-quality-gate.js.map +1 -1
  66. package/dist/mcp/tools/qe/quality-gates/index.d.ts +5 -2
  67. package/dist/mcp/tools/qe/quality-gates/index.d.ts.map +1 -1
  68. package/dist/mcp/tools/qe/quality-gates/index.js +10 -1
  69. package/dist/mcp/tools/qe/quality-gates/index.js.map +1 -1
  70. package/dist/mcp/tools.d.ts +1 -0
  71. package/dist/mcp/tools.d.ts.map +1 -1
  72. package/dist/mcp/tools.js +156 -1
  73. package/dist/mcp/tools.js.map +1 -1
  74. package/dist/persistence/migrations/all-migrations.d.ts +18 -0
  75. package/dist/persistence/migrations/all-migrations.d.ts.map +1 -0
  76. package/dist/persistence/migrations/all-migrations.js +624 -0
  77. package/dist/persistence/migrations/all-migrations.js.map +1 -0
  78. package/dist/persistence/migrations/index.d.ts +110 -0
  79. package/dist/persistence/migrations/index.d.ts.map +1 -0
  80. package/dist/persistence/migrations/index.js +303 -0
  81. package/dist/persistence/migrations/index.js.map +1 -0
  82. package/dist/planning/GOAPPlanner.d.ts +170 -0
  83. package/dist/planning/GOAPPlanner.d.ts.map +1 -0
  84. package/dist/planning/GOAPPlanner.js +781 -0
  85. package/dist/planning/GOAPPlanner.js.map +1 -0
  86. package/dist/planning/PlanLearning.d.ts +184 -0
  87. package/dist/planning/PlanLearning.d.ts.map +1 -0
  88. package/dist/planning/PlanLearning.js +526 -0
  89. package/dist/planning/PlanLearning.js.map +1 -0
  90. package/dist/planning/PlanSimilarity.d.ts +148 -0
  91. package/dist/planning/PlanSimilarity.d.ts.map +1 -0
  92. package/dist/planning/PlanSimilarity.js +463 -0
  93. package/dist/planning/PlanSimilarity.js.map +1 -0
  94. package/dist/planning/WorldStateBuilder.d.ts +150 -0
  95. package/dist/planning/WorldStateBuilder.d.ts.map +1 -0
  96. package/dist/planning/WorldStateBuilder.js +267 -0
  97. package/dist/planning/WorldStateBuilder.js.map +1 -0
  98. package/dist/planning/actions/fleet-actions.d.ts +78 -0
  99. package/dist/planning/actions/fleet-actions.d.ts.map +1 -0
  100. package/dist/planning/actions/fleet-actions.js +329 -0
  101. package/dist/planning/actions/fleet-actions.js.map +1 -0
  102. package/dist/planning/actions/index.d.ts +61 -0
  103. package/dist/planning/actions/index.d.ts.map +1 -0
  104. package/dist/planning/actions/index.js +159 -0
  105. package/dist/planning/actions/index.js.map +1 -0
  106. package/dist/planning/actions/orchestration-actions.d.ts +61 -0
  107. package/dist/planning/actions/orchestration-actions.d.ts.map +1 -0
  108. package/dist/planning/actions/orchestration-actions.js +395 -0
  109. package/dist/planning/actions/orchestration-actions.js.map +1 -0
  110. package/dist/planning/actions/quality-gate-actions.d.ts +160 -0
  111. package/dist/planning/actions/quality-gate-actions.d.ts.map +1 -0
  112. package/dist/planning/actions/quality-gate-actions.js +639 -0
  113. package/dist/planning/actions/quality-gate-actions.js.map +1 -0
  114. package/dist/planning/actions/test-strategy-actions.d.ts +70 -0
  115. package/dist/planning/actions/test-strategy-actions.d.ts.map +1 -0
  116. package/dist/planning/actions/test-strategy-actions.js +278 -0
  117. package/dist/planning/actions/test-strategy-actions.js.map +1 -0
  118. package/dist/planning/execution/PlanExecutor.d.ts +223 -0
  119. package/dist/planning/execution/PlanExecutor.d.ts.map +1 -0
  120. package/dist/planning/execution/PlanExecutor.js +978 -0
  121. package/dist/planning/execution/PlanExecutor.js.map +1 -0
  122. package/dist/planning/execution/index.d.ts +12 -0
  123. package/dist/planning/execution/index.d.ts.map +1 -0
  124. package/dist/planning/execution/index.js +18 -0
  125. package/dist/planning/execution/index.js.map +1 -0
  126. package/dist/planning/goals/TaskWorkflowGoals.d.ts +67 -0
  127. package/dist/planning/goals/TaskWorkflowGoals.d.ts.map +1 -0
  128. package/dist/planning/goals/TaskWorkflowGoals.js +208 -0
  129. package/dist/planning/goals/TaskWorkflowGoals.js.map +1 -0
  130. package/dist/planning/goals/index.d.ts +10 -0
  131. package/dist/planning/goals/index.d.ts.map +1 -0
  132. package/dist/planning/goals/index.js +19 -0
  133. package/dist/planning/goals/index.js.map +1 -0
  134. package/dist/planning/index.d.ts +36 -0
  135. package/dist/planning/index.d.ts.map +1 -0
  136. package/dist/planning/index.js +187 -0
  137. package/dist/planning/index.js.map +1 -0
  138. package/dist/planning/integration/GOAPQualityGateIntegration.d.ts +235 -0
  139. package/dist/planning/integration/GOAPQualityGateIntegration.d.ts.map +1 -0
  140. package/dist/planning/integration/GOAPQualityGateIntegration.js +589 -0
  141. package/dist/planning/integration/GOAPQualityGateIntegration.js.map +1 -0
  142. package/dist/planning/integration/GOAPTaskOrchestration.d.ts +165 -0
  143. package/dist/planning/integration/GOAPTaskOrchestration.d.ts.map +1 -0
  144. package/dist/planning/integration/GOAPTaskOrchestration.js +490 -0
  145. package/dist/planning/integration/GOAPTaskOrchestration.js.map +1 -0
  146. package/dist/planning/integration/index.d.ts +14 -0
  147. package/dist/planning/integration/index.d.ts.map +1 -0
  148. package/dist/planning/integration/index.js +23 -0
  149. package/dist/planning/integration/index.js.map +1 -0
  150. package/dist/planning/types.d.ts +266 -0
  151. package/dist/planning/types.d.ts.map +1 -0
  152. package/dist/planning/types.js +63 -0
  153. package/dist/planning/types.js.map +1 -0
  154. package/dist/utils/Database.d.ts.map +1 -1
  155. package/dist/utils/Database.js +81 -2
  156. package/dist/utils/Database.js.map +1 -1
  157. package/package.json +6 -3
@@ -0,0 +1,781 @@
1
+ "use strict";
2
+ /**
3
+ * GOAP Planner - A* Search Algorithm for Goal-Oriented Action Planning
4
+ *
5
+ * Implements optimal plan finding using A* search with:
6
+ * - Precondition checking
7
+ * - Effect application
8
+ * - Heuristic calculation
9
+ * - Plan reconstruction
10
+ *
11
+ * @module planning/GOAPPlanner
12
+ * @version 1.0.0
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.GOAPPlanner = void 0;
16
+ exports.getSharedGOAPPlanner = getSharedGOAPPlanner;
17
+ exports.resetSharedGOAPPlanner = resetSharedGOAPPlanner;
18
+ const Logger_1 = require("../utils/Logger");
19
+ const PlanSimilarity_1 = require("./PlanSimilarity");
20
+ /**
21
+ * GOAP Planner using A* search algorithm
22
+ */
23
+ class GOAPPlanner {
24
+ constructor(db) {
25
+ this.actionLibrary = [];
26
+ this.actionsLoaded = false;
27
+ this.enablePlanReuse = true; // Can be disabled for benchmarking A* directly
28
+ this.db = db;
29
+ this.logger = Logger_1.Logger.getInstance();
30
+ this.planSimilarity = new PlanSimilarity_1.PlanSimilarity(db);
31
+ }
32
+ /**
33
+ * Get PlanSimilarity instance for direct access
34
+ */
35
+ getPlanSimilarity() {
36
+ return this.planSimilarity;
37
+ }
38
+ /**
39
+ * Enable or disable plan reuse (useful for benchmarking)
40
+ */
41
+ setPlanReuseEnabled(enabled) {
42
+ this.enablePlanReuse = enabled;
43
+ }
44
+ /**
45
+ * Check if plan reuse is enabled
46
+ */
47
+ isPlanReuseEnabled() {
48
+ return this.enablePlanReuse;
49
+ }
50
+ /**
51
+ * Load action library from database (merges with programmatic actions)
52
+ */
53
+ async loadActionsFromDatabase() {
54
+ if (this.actionsLoaded)
55
+ return;
56
+ try {
57
+ const rows = this.db.prepare(`
58
+ SELECT * FROM goap_actions ORDER BY category, cost ASC
59
+ `).all();
60
+ // Merge DB actions with existing programmatic actions (avoid duplicates)
61
+ const existingIds = new Set(this.actionLibrary.map(a => a.id));
62
+ const dbActions = rows
63
+ .filter(row => !existingIds.has(row.id))
64
+ .map(row => ({
65
+ id: row.id,
66
+ name: row.name,
67
+ description: row.description || undefined,
68
+ agentType: row.agent_type,
69
+ preconditions: JSON.parse(row.preconditions),
70
+ effects: JSON.parse(row.effects),
71
+ cost: row.cost,
72
+ durationEstimate: row.duration_estimate || undefined,
73
+ successRate: row.success_rate,
74
+ executionCount: row.execution_count,
75
+ category: row.category
76
+ }));
77
+ this.actionLibrary.push(...dbActions);
78
+ this.actionsLoaded = true;
79
+ this.logger.debug(`[GOAPPlanner] Loaded ${dbActions.length} actions from database (total: ${this.actionLibrary.length})`);
80
+ }
81
+ catch (error) {
82
+ this.logger.warn('[GOAPPlanner] Failed to load actions from database, keeping existing library', { error });
83
+ // Keep existing actions instead of clearing
84
+ }
85
+ }
86
+ /**
87
+ * Add actions programmatically (for testing or bootstrap)
88
+ */
89
+ addActions(actions) {
90
+ this.actionLibrary.push(...actions);
91
+ }
92
+ /**
93
+ * Clear action library
94
+ */
95
+ clearActions() {
96
+ this.actionLibrary = [];
97
+ this.actionsLoaded = false;
98
+ }
99
+ /**
100
+ * Get current action library
101
+ */
102
+ getActionLibrary() {
103
+ return [...this.actionLibrary];
104
+ }
105
+ /**
106
+ * Ensure GOAP schema exists (creates missing tables)
107
+ */
108
+ ensureSchema() {
109
+ // Create goap_execution_steps if missing
110
+ this.db.exec(`
111
+ CREATE TABLE IF NOT EXISTS goap_execution_steps (
112
+ id TEXT PRIMARY KEY,
113
+ plan_id TEXT NOT NULL,
114
+ action_id TEXT NOT NULL,
115
+ step_order INTEGER NOT NULL,
116
+ world_state_before TEXT,
117
+ world_state_after TEXT,
118
+ status TEXT DEFAULT 'pending',
119
+ started_at DATETIME,
120
+ completed_at DATETIME,
121
+ error_message TEXT,
122
+ agent_id TEXT,
123
+ FOREIGN KEY (plan_id) REFERENCES goap_plans (id),
124
+ FOREIGN KEY (action_id) REFERENCES goap_actions (id)
125
+ )
126
+ `);
127
+ // Create indexes if missing
128
+ try {
129
+ this.db.exec('CREATE INDEX IF NOT EXISTS idx_goap_execution_plan ON goap_execution_steps (plan_id)');
130
+ this.db.exec('CREATE INDEX IF NOT EXISTS idx_goap_execution_action ON goap_execution_steps (action_id)');
131
+ }
132
+ catch {
133
+ // Indexes may already exist
134
+ }
135
+ this.logger.debug('[GOAPPlanner] Schema ensured');
136
+ }
137
+ /**
138
+ * Seed actions to database (upsert - won't duplicate)
139
+ * Call this with allActions to persist the action library
140
+ */
141
+ seedActions(actions) {
142
+ this.ensureSchema();
143
+ const upsert = this.db.prepare(`
144
+ INSERT INTO goap_actions (
145
+ id, name, description, agent_type, preconditions, effects,
146
+ cost, duration_estimate, success_rate, execution_count, category, created_at
147
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
148
+ ON CONFLICT(id) DO UPDATE SET
149
+ name = excluded.name,
150
+ description = excluded.description,
151
+ agent_type = excluded.agent_type,
152
+ preconditions = excluded.preconditions,
153
+ effects = excluded.effects,
154
+ cost = excluded.cost,
155
+ duration_estimate = excluded.duration_estimate,
156
+ category = excluded.category,
157
+ updated_at = CURRENT_TIMESTAMP
158
+ `);
159
+ let seeded = 0;
160
+ const insertMany = this.db.transaction((actionsToSeed) => {
161
+ for (const action of actionsToSeed) {
162
+ upsert.run(action.id, action.name, action.description || null, action.agentType, JSON.stringify(action.preconditions), JSON.stringify(action.effects), action.cost, action.durationEstimate || null, action.successRate ?? 1.0, action.executionCount ?? 0, action.category);
163
+ seeded++;
164
+ }
165
+ });
166
+ insertMany(actions);
167
+ this.logger.info(`[GOAPPlanner] Seeded ${seeded} actions to database`);
168
+ return seeded;
169
+ }
170
+ /**
171
+ * Get action count from database
172
+ */
173
+ getActionCountFromDatabase() {
174
+ const result = this.db.prepare('SELECT COUNT(*) as count FROM goap_actions').get();
175
+ return result.count;
176
+ }
177
+ /**
178
+ * A* search to find optimal plan from current to goal state
179
+ * Phase 5: First checks for reusable similar plans before running A*
180
+ */
181
+ async findPlan(currentState, goalConditions, constraints) {
182
+ await this.loadActionsFromDatabase();
183
+ const startTime = Date.now();
184
+ // Phase 5: Check for reusable similar plans first (O(log n) vs O(n) A* search)
185
+ if (this.enablePlanReuse) {
186
+ const reusedPlan = await this.tryReuseSimilarPlan(currentState, goalConditions, constraints);
187
+ if (reusedPlan) {
188
+ this.logger.info('[GOAPPlanner] Reused similar plan', {
189
+ planId: reusedPlan.id,
190
+ actions: reusedPlan.actions.length,
191
+ totalCost: reusedPlan.totalCost,
192
+ elapsedMs: Date.now() - startTime
193
+ });
194
+ return reusedPlan;
195
+ }
196
+ }
197
+ const openSet = [];
198
+ const closedSet = new Set();
199
+ // Start node
200
+ const startNode = {
201
+ state: this.cloneState(currentState),
202
+ gCost: 0,
203
+ hCost: this.calculateHeuristic(currentState, goalConditions),
204
+ fCost: 0,
205
+ action: null,
206
+ parent: null,
207
+ depth: 0
208
+ };
209
+ startNode.fCost = startNode.gCost + startNode.hCost;
210
+ openSet.push(startNode);
211
+ let iterations = 0;
212
+ const maxIterations = constraints?.maxIterations ?? 10000;
213
+ const timeoutMs = constraints?.timeoutMs ?? 5000;
214
+ const maxPlanLength = constraints?.maxPlanLength ?? 20;
215
+ while (openSet.length > 0 && iterations < maxIterations) {
216
+ iterations++;
217
+ // Check timeout
218
+ if (Date.now() - startTime > timeoutMs) {
219
+ this.logger.warn('[GOAPPlanner] Planning timeout exceeded', {
220
+ timeoutMs,
221
+ iterations,
222
+ elapsed: Date.now() - startTime
223
+ });
224
+ break;
225
+ }
226
+ // Get node with lowest fCost
227
+ openSet.sort((a, b) => a.fCost - b.fCost);
228
+ const current = openSet.shift();
229
+ // Check if goal reached
230
+ if (this.goalMet(current.state, goalConditions)) {
231
+ const plan = this.reconstructPlan(current, goalConditions);
232
+ this.logger.info('[GOAPPlanner] Plan found', {
233
+ iterations,
234
+ actions: plan.actions.length,
235
+ totalCost: plan.totalCost,
236
+ elapsedMs: Date.now() - startTime
237
+ });
238
+ return plan;
239
+ }
240
+ // Check max depth
241
+ if (current.depth >= maxPlanLength) {
242
+ continue;
243
+ }
244
+ const stateKey = this.stateHash(current.state);
245
+ if (closedSet.has(stateKey)) {
246
+ continue;
247
+ }
248
+ closedSet.add(stateKey);
249
+ // Expand neighbors (applicable actions)
250
+ const applicableActions = this.getApplicableActions(current.state, constraints);
251
+ for (const action of applicableActions) {
252
+ const nextState = this.applyAction(current.state, action);
253
+ const nextStateKey = this.stateHash(nextState);
254
+ if (closedSet.has(nextStateKey))
255
+ continue;
256
+ const gCost = current.gCost + this.getActionCost(action, current.state);
257
+ const hCost = this.calculateHeuristic(nextState, goalConditions);
258
+ const existingNode = openSet.find(n => this.stateHash(n.state) === nextStateKey);
259
+ if (!existingNode || gCost < existingNode.gCost) {
260
+ const newNode = {
261
+ state: nextState,
262
+ gCost,
263
+ hCost,
264
+ fCost: gCost + hCost,
265
+ action,
266
+ parent: current,
267
+ depth: current.depth + 1
268
+ };
269
+ if (existingNode) {
270
+ Object.assign(existingNode, newNode);
271
+ }
272
+ else {
273
+ openSet.push(newNode);
274
+ }
275
+ }
276
+ }
277
+ }
278
+ this.logger.warn('[GOAPPlanner] No plan found', {
279
+ iterations,
280
+ closedSetSize: closedSet.size,
281
+ elapsedMs: Date.now() - startTime
282
+ });
283
+ return null;
284
+ }
285
+ /**
286
+ * Phase 5: Try to reuse a similar plan from the signature cache
287
+ * Returns null if no suitable plan found, otherwise returns reconstructed plan
288
+ */
289
+ async tryReuseSimilarPlan(currentState, goalConditions, constraints) {
290
+ try {
291
+ // Find similar plans (target: <100ms)
292
+ const similarPlans = await this.planSimilarity.findSimilarPlans(goalConditions, currentState, { minSimilarity: 0.75, maxCandidates: 3 });
293
+ if (similarPlans.length === 0) {
294
+ return null;
295
+ }
296
+ // Try best match first (sorted by goal match, then similarity)
297
+ for (const similar of similarPlans) {
298
+ // Validate the action sequence is still valid
299
+ const actions = this.reconstructActionsFromSequence(similar.signature.actionSequence, constraints);
300
+ if (actions.length === 0) {
301
+ this.logger.debug('[GOAPPlanner] Similar plan has no valid actions', {
302
+ planId: similar.planId
303
+ });
304
+ continue;
305
+ }
306
+ // Verify preconditions can be met from current state
307
+ if (!this.validateActionSequence(currentState, actions)) {
308
+ this.logger.debug('[GOAPPlanner] Similar plan action sequence invalid for current state', {
309
+ planId: similar.planId
310
+ });
311
+ continue;
312
+ }
313
+ // Create reused plan with new ID
314
+ const reusedPlanId = `plan-reuse-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
315
+ const plan = {
316
+ id: reusedPlanId,
317
+ actions,
318
+ totalCost: similar.signature.totalCost,
319
+ estimatedDuration: actions.reduce((sum, a) => sum + (a.durationEstimate ?? 0), 0),
320
+ goalConditions,
321
+ reusedFromPlanId: similar.planId, // Track provenance
322
+ similarityScore: similar.similarityScore
323
+ };
324
+ this.logger.info('[GOAPPlanner] Found reusable plan', {
325
+ originalPlanId: similar.planId,
326
+ reusedPlanId,
327
+ similarity: similar.similarityScore.toFixed(3),
328
+ goalMatch: similar.goalMatch,
329
+ actions: actions.length
330
+ });
331
+ return plan;
332
+ }
333
+ return null;
334
+ }
335
+ catch (error) {
336
+ this.logger.warn('[GOAPPlanner] Error in plan reuse check, falling back to A*', { error });
337
+ return null;
338
+ }
339
+ }
340
+ /**
341
+ * Reconstruct action objects from action ID sequence
342
+ */
343
+ reconstructActionsFromSequence(actionSequence, constraints) {
344
+ const actions = [];
345
+ const excludedActions = new Set(constraints?.excludedActions || []);
346
+ const allowedCategories = constraints?.allowedCategories
347
+ ? new Set(constraints.allowedCategories)
348
+ : null;
349
+ for (const actionId of actionSequence) {
350
+ if (excludedActions.has(actionId))
351
+ continue;
352
+ const action = this.actionLibrary.find(a => a.id === actionId);
353
+ if (!action)
354
+ continue;
355
+ if (allowedCategories && !allowedCategories.has(action.category))
356
+ continue;
357
+ actions.push(action);
358
+ }
359
+ return actions;
360
+ }
361
+ /**
362
+ * Validate that action sequence can be executed from current state
363
+ */
364
+ validateActionSequence(initialState, actions) {
365
+ let currentState = this.cloneState(initialState);
366
+ for (const action of actions) {
367
+ if (!this.preconditionsMet(currentState, action.preconditions)) {
368
+ return false;
369
+ }
370
+ currentState = this.applyAction(currentState, action);
371
+ }
372
+ return true;
373
+ }
374
+ /**
375
+ * Store plan signature for future reuse
376
+ * Call this after successful plan execution
377
+ */
378
+ storePlanSignature(plan, initialState) {
379
+ try {
380
+ this.planSimilarity.storePlanSignature(plan.id, plan.goalConditions, initialState, plan.actions, plan.totalCost);
381
+ }
382
+ catch (error) {
383
+ this.logger.warn('[GOAPPlanner] Failed to store plan signature', {
384
+ planId: plan.id,
385
+ error
386
+ });
387
+ }
388
+ }
389
+ /**
390
+ * Record plan reuse outcome (for learning)
391
+ */
392
+ recordPlanReuseOutcome(planId, success) {
393
+ this.planSimilarity.recordPlanReuse(planId, success);
394
+ }
395
+ /**
396
+ * Get plan reuse statistics
397
+ */
398
+ getPlanReuseStats() {
399
+ return this.planSimilarity.getReuseStats();
400
+ }
401
+ /**
402
+ * Calculate heuristic distance to goal (admissible)
403
+ */
404
+ calculateHeuristic(state, goal) {
405
+ let distance = 0;
406
+ for (const [key, condition] of Object.entries(goal)) {
407
+ const currentValue = this.getStateValue(state, key);
408
+ const condObj = condition;
409
+ if (condObj.gte !== undefined) {
410
+ if (typeof currentValue === 'number' && currentValue < condObj.gte) {
411
+ distance += (condObj.gte - currentValue) / 100; // Normalize
412
+ }
413
+ }
414
+ if (condObj.gt !== undefined) {
415
+ if (typeof currentValue === 'number' && currentValue <= condObj.gt) {
416
+ distance += (condObj.gt - currentValue + 1) / 100;
417
+ }
418
+ }
419
+ if (condObj.lte !== undefined) {
420
+ if (typeof currentValue === 'number' && currentValue > condObj.lte) {
421
+ distance += (currentValue - condObj.lte) / 100;
422
+ }
423
+ }
424
+ if (condObj.lt !== undefined) {
425
+ if (typeof currentValue === 'number' && currentValue >= condObj.lt) {
426
+ distance += (currentValue - condObj.lt + 1) / 100;
427
+ }
428
+ }
429
+ if (condObj.eq !== undefined) {
430
+ if (currentValue !== condObj.eq) {
431
+ distance += 1;
432
+ }
433
+ }
434
+ if (condObj.ne !== undefined) {
435
+ if (currentValue === condObj.ne) {
436
+ distance += 1;
437
+ }
438
+ }
439
+ if (condObj.contains !== undefined) {
440
+ if (Array.isArray(currentValue) && !currentValue.includes(condObj.contains)) {
441
+ distance += 1;
442
+ }
443
+ }
444
+ if (condObj.exists !== undefined) {
445
+ const exists = currentValue !== undefined && currentValue !== null;
446
+ if (condObj.exists !== exists) {
447
+ distance += 1;
448
+ }
449
+ }
450
+ }
451
+ return distance;
452
+ }
453
+ /**
454
+ * Check if goal conditions are met
455
+ */
456
+ goalMet(state, goal) {
457
+ return this.conditionsMet(state, goal);
458
+ }
459
+ /**
460
+ * Check if all conditions are satisfied
461
+ */
462
+ conditionsMet(state, conditions) {
463
+ for (const [key, condition] of Object.entries(conditions)) {
464
+ if (!this.checkCondition(state, key, condition)) {
465
+ return false;
466
+ }
467
+ }
468
+ return true;
469
+ }
470
+ /**
471
+ * Check a single condition
472
+ */
473
+ checkCondition(state, key, condition) {
474
+ const value = this.getStateValue(state, key);
475
+ if (condition.gte !== undefined && (typeof value !== 'number' || value < condition.gte)) {
476
+ return false;
477
+ }
478
+ if (condition.gt !== undefined && (typeof value !== 'number' || value <= condition.gt)) {
479
+ return false;
480
+ }
481
+ if (condition.lte !== undefined && (typeof value !== 'number' || value > condition.lte)) {
482
+ return false;
483
+ }
484
+ if (condition.lt !== undefined && (typeof value !== 'number' || value >= condition.lt)) {
485
+ return false;
486
+ }
487
+ if (condition.eq !== undefined && value !== condition.eq) {
488
+ return false;
489
+ }
490
+ if (condition.ne !== undefined && value === condition.ne) {
491
+ return false;
492
+ }
493
+ if (condition.contains !== undefined) {
494
+ if (!Array.isArray(value) || !value.includes(condition.contains)) {
495
+ return false;
496
+ }
497
+ }
498
+ if (condition.exists !== undefined) {
499
+ const exists = value !== undefined && value !== null;
500
+ if (condition.exists !== exists) {
501
+ return false;
502
+ }
503
+ }
504
+ if (condition.in !== undefined) {
505
+ if (!condition.in.includes(value)) {
506
+ return false;
507
+ }
508
+ }
509
+ return true;
510
+ }
511
+ /**
512
+ * Check if action preconditions are met
513
+ */
514
+ preconditionsMet(state, preconditions) {
515
+ return this.conditionsMet(state, preconditions);
516
+ }
517
+ /**
518
+ * Get actions whose preconditions are satisfied
519
+ */
520
+ getApplicableActions(state, constraints) {
521
+ return this.actionLibrary.filter(action => {
522
+ // Check preconditions
523
+ if (!this.preconditionsMet(state, action.preconditions)) {
524
+ return false;
525
+ }
526
+ // Check category constraints
527
+ if (constraints?.allowedCategories) {
528
+ if (!constraints.allowedCategories.includes(action.category)) {
529
+ return false;
530
+ }
531
+ }
532
+ // Check excluded actions
533
+ if (constraints?.excludedActions) {
534
+ if (constraints.excludedActions.includes(action.id)) {
535
+ return false;
536
+ }
537
+ }
538
+ // Check preferred agent types (boost, don't exclude)
539
+ // This is handled in cost calculation instead
540
+ return true;
541
+ });
542
+ }
543
+ /**
544
+ * Calculate effective action cost
545
+ */
546
+ getActionCost(action, state) {
547
+ let cost = action.cost;
548
+ // Adjust based on success rate (prefer reliable actions)
549
+ if (action.successRate !== undefined && action.successRate < 1) {
550
+ cost = cost / action.successRate;
551
+ }
552
+ // Adjust for critical risk level (increase cost of risky actions)
553
+ if (state.context.riskLevel === 'critical') {
554
+ if (action.category === 'process') {
555
+ cost *= 2; // Discourage process shortcuts in critical situations
556
+ }
557
+ }
558
+ return cost;
559
+ }
560
+ /**
561
+ * Apply action effects to state
562
+ */
563
+ applyAction(state, action) {
564
+ const newState = this.cloneState(state);
565
+ for (const [key, effect] of Object.entries(action.effects)) {
566
+ this.applyEffect(newState, key, effect);
567
+ }
568
+ return newState;
569
+ }
570
+ /**
571
+ * Apply a single effect to state
572
+ */
573
+ applyEffect(state, key, effect) {
574
+ if (effect.set !== undefined) {
575
+ this.setStateValue(state, key, effect.set);
576
+ }
577
+ if (effect.increase !== undefined) {
578
+ const current = this.getStateValue(state, key) ?? 0;
579
+ if (typeof current === 'number') {
580
+ this.setStateValue(state, key, Math.min(100, current + effect.increase));
581
+ }
582
+ }
583
+ if (effect.decrease !== undefined) {
584
+ const current = this.getStateValue(state, key) ?? 0;
585
+ if (typeof current === 'number') {
586
+ this.setStateValue(state, key, Math.max(0, current - effect.decrease));
587
+ }
588
+ }
589
+ if (effect.increment !== undefined) {
590
+ const current = this.getStateValue(state, key) ?? 0;
591
+ if (typeof current === 'number') {
592
+ this.setStateValue(state, key, current + effect.increment);
593
+ }
594
+ }
595
+ if (effect.decrement !== undefined) {
596
+ const current = this.getStateValue(state, key) ?? 0;
597
+ if (typeof current === 'number') {
598
+ this.setStateValue(state, key, Math.max(0, current - effect.decrement));
599
+ }
600
+ }
601
+ if (effect.add !== undefined) {
602
+ const current = this.getStateValue(state, key) ?? [];
603
+ if (Array.isArray(current) && !current.includes(effect.add)) {
604
+ this.setStateValue(state, key, [...current, effect.add]);
605
+ }
606
+ }
607
+ if (effect.remove !== undefined) {
608
+ const current = this.getStateValue(state, key) ?? [];
609
+ if (Array.isArray(current)) {
610
+ this.setStateValue(state, key, current.filter(v => v !== effect.remove));
611
+ }
612
+ }
613
+ // NOTE: { update: 'measured' } was removed as it was a semantic hack
614
+ // that allowed the same action to repeat infinitely. We now use proper
615
+ // flag-based effects where measurement actions set boolean flags that
616
+ // enable improvement actions. This ensures each measurement action
617
+ // can only run once (precondition: flag == false, effect: flag = true).
618
+ }
619
+ /**
620
+ * Get value from nested state using dot notation
621
+ */
622
+ getStateValue(state, key) {
623
+ const parts = key.split('.');
624
+ let current = state;
625
+ for (const part of parts) {
626
+ if (current === undefined || current === null) {
627
+ return undefined;
628
+ }
629
+ current = current[part];
630
+ }
631
+ return current;
632
+ }
633
+ /**
634
+ * Set value in nested state using dot notation
635
+ */
636
+ setStateValue(state, key, value) {
637
+ const parts = key.split('.');
638
+ let current = state;
639
+ for (let i = 0; i < parts.length - 1; i++) {
640
+ const part = parts[i];
641
+ if (current[part] === undefined) {
642
+ current[part] = {};
643
+ }
644
+ current = current[part];
645
+ }
646
+ current[parts[parts.length - 1]] = value;
647
+ }
648
+ /**
649
+ * Create hash of state for deduplication
650
+ */
651
+ stateHash(state) {
652
+ // Hash all metrics that affect planning, including measurement flags
653
+ const key = {
654
+ coverage: state.coverage.line,
655
+ coverageMeasured: state.coverage.measured ?? false,
656
+ tests: state.quality.testsPassing,
657
+ testsMeasured: state.quality.testsMeasured ?? false,
658
+ integrationTested: state.quality.integrationTested ?? false,
659
+ security: state.quality.securityScore,
660
+ securityMeasured: state.quality.securityMeasured ?? false,
661
+ performance: state.quality.performanceScore,
662
+ performanceMeasured: state.quality.performanceMeasured ?? false,
663
+ complexityMeasured: state.quality.complexityMeasured ?? false,
664
+ gateEvaluated: state.quality.gateEvaluated ?? false,
665
+ gateStatus: state.quality.gateStatus,
666
+ agents: state.fleet.activeAgents,
667
+ availableTypes: state.fleet.availableAgents.sort().join(','),
668
+ time: Math.floor(state.resources.timeRemaining / 60) // Round to minutes
669
+ };
670
+ return JSON.stringify(key);
671
+ }
672
+ /**
673
+ * Deep clone world state
674
+ */
675
+ cloneState(state) {
676
+ return JSON.parse(JSON.stringify(state));
677
+ }
678
+ /**
679
+ * Reconstruct plan from goal node
680
+ */
681
+ reconstructPlan(goalNode, goalConditions) {
682
+ const actions = [];
683
+ let current = goalNode;
684
+ while (current && current.action) {
685
+ actions.unshift(current.action);
686
+ current = current.parent;
687
+ }
688
+ const planId = `plan-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
689
+ return {
690
+ id: planId,
691
+ actions,
692
+ totalCost: goalNode.gCost,
693
+ estimatedDuration: actions.reduce((sum, a) => sum + (a.durationEstimate ?? 0), 0),
694
+ goalConditions
695
+ };
696
+ }
697
+ /**
698
+ * Persist plan to database
699
+ */
700
+ async persistPlan(plan, initialState, goalId) {
701
+ try {
702
+ this.db.prepare(`
703
+ INSERT INTO goap_plans (
704
+ id, goal_id, initial_state, goal_state, action_sequence,
705
+ total_cost, estimated_duration, status, created_at
706
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', CURRENT_TIMESTAMP)
707
+ `).run(plan.id, goalId || null, JSON.stringify(initialState), JSON.stringify(plan.goalConditions), JSON.stringify(plan.actions.map(a => a.id)), plan.totalCost, plan.estimatedDuration);
708
+ this.logger.info('[GOAPPlanner] Plan persisted', { planId: plan.id });
709
+ }
710
+ catch (error) {
711
+ this.logger.error('[GOAPPlanner] Failed to persist plan', { planId: plan.id, error });
712
+ }
713
+ }
714
+ /**
715
+ * Update action success rate based on execution outcome
716
+ */
717
+ async updateActionSuccessRate(actionId, success) {
718
+ try {
719
+ const action = this.db.prepare(`
720
+ SELECT success_rate, execution_count FROM goap_actions WHERE id = ?
721
+ `).get(actionId);
722
+ if (!action)
723
+ return;
724
+ const newCount = action.execution_count + 1;
725
+ const newRate = (action.success_rate * action.execution_count + (success ? 1 : 0)) / newCount;
726
+ this.db.prepare(`
727
+ UPDATE goap_actions
728
+ SET success_rate = ?, execution_count = ?, updated_at = CURRENT_TIMESTAMP
729
+ WHERE id = ?
730
+ `).run(newRate, newCount, actionId);
731
+ // Update in-memory library
732
+ const memAction = this.actionLibrary.find(a => a.id === actionId);
733
+ if (memAction) {
734
+ memAction.successRate = newRate;
735
+ memAction.executionCount = newCount;
736
+ }
737
+ }
738
+ catch (error) {
739
+ this.logger.warn('[GOAPPlanner] Failed to update action success rate', { actionId, error });
740
+ }
741
+ }
742
+ /**
743
+ * Find alternative plans (for presenting options)
744
+ */
745
+ async findAlternativePlans(currentState, goalConditions, constraints, maxAlternatives = 3) {
746
+ const plans = [];
747
+ const usedActions = new Set();
748
+ for (let i = 0; i < maxAlternatives + 1; i++) {
749
+ const plan = await this.findPlan(currentState, goalConditions, {
750
+ ...constraints,
751
+ excludedActions: [...(constraints?.excludedActions || []), ...Array.from(usedActions)]
752
+ });
753
+ if (plan) {
754
+ plans.push(plan);
755
+ // Exclude first action of this plan for next iteration
756
+ if (plan.actions.length > 0) {
757
+ usedActions.add(plan.actions[0].id);
758
+ }
759
+ }
760
+ else {
761
+ break;
762
+ }
763
+ }
764
+ return plans;
765
+ }
766
+ }
767
+ exports.GOAPPlanner = GOAPPlanner;
768
+ /**
769
+ * Get shared GOAPPlanner instance
770
+ */
771
+ let sharedPlanner = null;
772
+ function getSharedGOAPPlanner(db) {
773
+ if (!sharedPlanner) {
774
+ sharedPlanner = new GOAPPlanner(db);
775
+ }
776
+ return sharedPlanner;
777
+ }
778
+ function resetSharedGOAPPlanner() {
779
+ sharedPlanner = null;
780
+ }
781
+ //# sourceMappingURL=GOAPPlanner.js.map