@renseiai/agentfactory 0.8.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 (246) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -0
  3. package/dist/src/config/index.d.ts +3 -0
  4. package/dist/src/config/index.d.ts.map +1 -0
  5. package/dist/src/config/index.js +1 -0
  6. package/dist/src/config/repository-config.d.ts +44 -0
  7. package/dist/src/config/repository-config.d.ts.map +1 -0
  8. package/dist/src/config/repository-config.js +88 -0
  9. package/dist/src/config/repository-config.test.d.ts +2 -0
  10. package/dist/src/config/repository-config.test.d.ts.map +1 -0
  11. package/dist/src/config/repository-config.test.js +249 -0
  12. package/dist/src/deployment/deployment-checker.d.ts +110 -0
  13. package/dist/src/deployment/deployment-checker.d.ts.map +1 -0
  14. package/dist/src/deployment/deployment-checker.js +242 -0
  15. package/dist/src/deployment/index.d.ts +3 -0
  16. package/dist/src/deployment/index.d.ts.map +1 -0
  17. package/dist/src/deployment/index.js +2 -0
  18. package/dist/src/frontend/index.d.ts +2 -0
  19. package/dist/src/frontend/index.d.ts.map +1 -0
  20. package/dist/src/frontend/index.js +1 -0
  21. package/dist/src/frontend/types.d.ts +106 -0
  22. package/dist/src/frontend/types.d.ts.map +1 -0
  23. package/dist/src/frontend/types.js +11 -0
  24. package/dist/src/governor/decision-engine.d.ts +52 -0
  25. package/dist/src/governor/decision-engine.d.ts.map +1 -0
  26. package/dist/src/governor/decision-engine.js +220 -0
  27. package/dist/src/governor/decision-engine.test.d.ts +2 -0
  28. package/dist/src/governor/decision-engine.test.d.ts.map +1 -0
  29. package/dist/src/governor/decision-engine.test.js +629 -0
  30. package/dist/src/governor/event-bus.d.ts +43 -0
  31. package/dist/src/governor/event-bus.d.ts.map +1 -0
  32. package/dist/src/governor/event-bus.js +8 -0
  33. package/dist/src/governor/event-deduplicator.d.ts +43 -0
  34. package/dist/src/governor/event-deduplicator.d.ts.map +1 -0
  35. package/dist/src/governor/event-deduplicator.js +53 -0
  36. package/dist/src/governor/event-driven-governor.d.ts +131 -0
  37. package/dist/src/governor/event-driven-governor.d.ts.map +1 -0
  38. package/dist/src/governor/event-driven-governor.js +379 -0
  39. package/dist/src/governor/event-driven-governor.test.d.ts +2 -0
  40. package/dist/src/governor/event-driven-governor.test.d.ts.map +1 -0
  41. package/dist/src/governor/event-driven-governor.test.js +673 -0
  42. package/dist/src/governor/event-types.d.ts +78 -0
  43. package/dist/src/governor/event-types.d.ts.map +1 -0
  44. package/dist/src/governor/event-types.js +32 -0
  45. package/dist/src/governor/governor-types.d.ts +82 -0
  46. package/dist/src/governor/governor-types.d.ts.map +1 -0
  47. package/dist/src/governor/governor-types.js +21 -0
  48. package/dist/src/governor/governor.d.ts +100 -0
  49. package/dist/src/governor/governor.d.ts.map +1 -0
  50. package/dist/src/governor/governor.js +262 -0
  51. package/dist/src/governor/governor.test.d.ts +2 -0
  52. package/dist/src/governor/governor.test.d.ts.map +1 -0
  53. package/dist/src/governor/governor.test.js +514 -0
  54. package/dist/src/governor/human-touchpoints.d.ts +131 -0
  55. package/dist/src/governor/human-touchpoints.d.ts.map +1 -0
  56. package/dist/src/governor/human-touchpoints.js +251 -0
  57. package/dist/src/governor/human-touchpoints.test.d.ts +2 -0
  58. package/dist/src/governor/human-touchpoints.test.d.ts.map +1 -0
  59. package/dist/src/governor/human-touchpoints.test.js +366 -0
  60. package/dist/src/governor/in-memory-event-bus.d.ts +29 -0
  61. package/dist/src/governor/in-memory-event-bus.d.ts.map +1 -0
  62. package/dist/src/governor/in-memory-event-bus.js +79 -0
  63. package/dist/src/governor/index.d.ts +14 -0
  64. package/dist/src/governor/index.d.ts.map +1 -0
  65. package/dist/src/governor/index.js +13 -0
  66. package/dist/src/governor/override-parser.d.ts +60 -0
  67. package/dist/src/governor/override-parser.d.ts.map +1 -0
  68. package/dist/src/governor/override-parser.js +98 -0
  69. package/dist/src/governor/override-parser.test.d.ts +2 -0
  70. package/dist/src/governor/override-parser.test.d.ts.map +1 -0
  71. package/dist/src/governor/override-parser.test.js +312 -0
  72. package/dist/src/governor/platform-adapter.d.ts +69 -0
  73. package/dist/src/governor/platform-adapter.d.ts.map +1 -0
  74. package/dist/src/governor/platform-adapter.js +11 -0
  75. package/dist/src/governor/processing-state.d.ts +66 -0
  76. package/dist/src/governor/processing-state.d.ts.map +1 -0
  77. package/dist/src/governor/processing-state.js +43 -0
  78. package/dist/src/governor/processing-state.test.d.ts +2 -0
  79. package/dist/src/governor/processing-state.test.d.ts.map +1 -0
  80. package/dist/src/governor/processing-state.test.js +96 -0
  81. package/dist/src/governor/top-of-funnel.d.ts +118 -0
  82. package/dist/src/governor/top-of-funnel.d.ts.map +1 -0
  83. package/dist/src/governor/top-of-funnel.js +168 -0
  84. package/dist/src/governor/top-of-funnel.test.d.ts +2 -0
  85. package/dist/src/governor/top-of-funnel.test.d.ts.map +1 -0
  86. package/dist/src/governor/top-of-funnel.test.js +331 -0
  87. package/dist/src/index.d.ts +11 -0
  88. package/dist/src/index.d.ts.map +1 -0
  89. package/dist/src/index.js +10 -0
  90. package/dist/src/linear-cli.d.ts +38 -0
  91. package/dist/src/linear-cli.d.ts.map +1 -0
  92. package/dist/src/linear-cli.js +674 -0
  93. package/dist/src/logger.d.ts +117 -0
  94. package/dist/src/logger.d.ts.map +1 -0
  95. package/dist/src/logger.js +430 -0
  96. package/dist/src/manifest/generate.d.ts +20 -0
  97. package/dist/src/manifest/generate.d.ts.map +1 -0
  98. package/dist/src/manifest/generate.js +65 -0
  99. package/dist/src/manifest/index.d.ts +4 -0
  100. package/dist/src/manifest/index.d.ts.map +1 -0
  101. package/dist/src/manifest/index.js +2 -0
  102. package/dist/src/manifest/route-manifest.d.ts +34 -0
  103. package/dist/src/manifest/route-manifest.d.ts.map +1 -0
  104. package/dist/src/manifest/route-manifest.js +148 -0
  105. package/dist/src/orchestrator/activity-emitter.d.ts +119 -0
  106. package/dist/src/orchestrator/activity-emitter.d.ts.map +1 -0
  107. package/dist/src/orchestrator/activity-emitter.js +306 -0
  108. package/dist/src/orchestrator/api-activity-emitter.d.ts +167 -0
  109. package/dist/src/orchestrator/api-activity-emitter.d.ts.map +1 -0
  110. package/dist/src/orchestrator/api-activity-emitter.js +417 -0
  111. package/dist/src/orchestrator/heartbeat-writer.d.ts +57 -0
  112. package/dist/src/orchestrator/heartbeat-writer.d.ts.map +1 -0
  113. package/dist/src/orchestrator/heartbeat-writer.js +137 -0
  114. package/dist/src/orchestrator/index.d.ts +20 -0
  115. package/dist/src/orchestrator/index.d.ts.map +1 -0
  116. package/dist/src/orchestrator/index.js +22 -0
  117. package/dist/src/orchestrator/log-analyzer.d.ts +160 -0
  118. package/dist/src/orchestrator/log-analyzer.d.ts.map +1 -0
  119. package/dist/src/orchestrator/log-analyzer.js +572 -0
  120. package/dist/src/orchestrator/log-config.d.ts +39 -0
  121. package/dist/src/orchestrator/log-config.d.ts.map +1 -0
  122. package/dist/src/orchestrator/log-config.js +45 -0
  123. package/dist/src/orchestrator/orchestrator.d.ts +316 -0
  124. package/dist/src/orchestrator/orchestrator.d.ts.map +1 -0
  125. package/dist/src/orchestrator/orchestrator.js +3290 -0
  126. package/dist/src/orchestrator/parse-work-result.d.ts +16 -0
  127. package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -0
  128. package/dist/src/orchestrator/parse-work-result.js +135 -0
  129. package/dist/src/orchestrator/parse-work-result.test.d.ts +2 -0
  130. package/dist/src/orchestrator/parse-work-result.test.d.ts.map +1 -0
  131. package/dist/src/orchestrator/parse-work-result.test.js +234 -0
  132. package/dist/src/orchestrator/progress-logger.d.ts +72 -0
  133. package/dist/src/orchestrator/progress-logger.d.ts.map +1 -0
  134. package/dist/src/orchestrator/progress-logger.js +135 -0
  135. package/dist/src/orchestrator/session-logger.d.ts +159 -0
  136. package/dist/src/orchestrator/session-logger.d.ts.map +1 -0
  137. package/dist/src/orchestrator/session-logger.js +275 -0
  138. package/dist/src/orchestrator/state-recovery.d.ts +96 -0
  139. package/dist/src/orchestrator/state-recovery.d.ts.map +1 -0
  140. package/dist/src/orchestrator/state-recovery.js +302 -0
  141. package/dist/src/orchestrator/state-types.d.ts +165 -0
  142. package/dist/src/orchestrator/state-types.d.ts.map +1 -0
  143. package/dist/src/orchestrator/state-types.js +7 -0
  144. package/dist/src/orchestrator/stream-parser.d.ts +151 -0
  145. package/dist/src/orchestrator/stream-parser.d.ts.map +1 -0
  146. package/dist/src/orchestrator/stream-parser.js +137 -0
  147. package/dist/src/orchestrator/types.d.ts +232 -0
  148. package/dist/src/orchestrator/types.d.ts.map +1 -0
  149. package/dist/src/orchestrator/types.js +4 -0
  150. package/dist/src/orchestrator/validate-git-remote.test.d.ts +2 -0
  151. package/dist/src/orchestrator/validate-git-remote.test.d.ts.map +1 -0
  152. package/dist/src/orchestrator/validate-git-remote.test.js +61 -0
  153. package/dist/src/providers/a2a-auth.d.ts +81 -0
  154. package/dist/src/providers/a2a-auth.d.ts.map +1 -0
  155. package/dist/src/providers/a2a-auth.js +188 -0
  156. package/dist/src/providers/a2a-auth.test.d.ts +2 -0
  157. package/dist/src/providers/a2a-auth.test.d.ts.map +1 -0
  158. package/dist/src/providers/a2a-auth.test.js +232 -0
  159. package/dist/src/providers/a2a-provider.d.ts +254 -0
  160. package/dist/src/providers/a2a-provider.d.ts.map +1 -0
  161. package/dist/src/providers/a2a-provider.integration.test.d.ts +9 -0
  162. package/dist/src/providers/a2a-provider.integration.test.d.ts.map +1 -0
  163. package/dist/src/providers/a2a-provider.integration.test.js +665 -0
  164. package/dist/src/providers/a2a-provider.js +811 -0
  165. package/dist/src/providers/a2a-provider.test.d.ts +2 -0
  166. package/dist/src/providers/a2a-provider.test.d.ts.map +1 -0
  167. package/dist/src/providers/a2a-provider.test.js +681 -0
  168. package/dist/src/providers/amp-provider.d.ts +20 -0
  169. package/dist/src/providers/amp-provider.d.ts.map +1 -0
  170. package/dist/src/providers/amp-provider.js +24 -0
  171. package/dist/src/providers/claude-provider.d.ts +18 -0
  172. package/dist/src/providers/claude-provider.d.ts.map +1 -0
  173. package/dist/src/providers/claude-provider.js +437 -0
  174. package/dist/src/providers/codex-provider.d.ts +133 -0
  175. package/dist/src/providers/codex-provider.d.ts.map +1 -0
  176. package/dist/src/providers/codex-provider.js +381 -0
  177. package/dist/src/providers/codex-provider.test.d.ts +2 -0
  178. package/dist/src/providers/codex-provider.test.d.ts.map +1 -0
  179. package/dist/src/providers/codex-provider.test.js +387 -0
  180. package/dist/src/providers/index.d.ts +44 -0
  181. package/dist/src/providers/index.d.ts.map +1 -0
  182. package/dist/src/providers/index.js +85 -0
  183. package/dist/src/providers/spring-ai-provider.d.ts +90 -0
  184. package/dist/src/providers/spring-ai-provider.d.ts.map +1 -0
  185. package/dist/src/providers/spring-ai-provider.integration.test.d.ts +13 -0
  186. package/dist/src/providers/spring-ai-provider.integration.test.d.ts.map +1 -0
  187. package/dist/src/providers/spring-ai-provider.integration.test.js +351 -0
  188. package/dist/src/providers/spring-ai-provider.js +317 -0
  189. package/dist/src/providers/spring-ai-provider.test.d.ts +2 -0
  190. package/dist/src/providers/spring-ai-provider.test.d.ts.map +1 -0
  191. package/dist/src/providers/spring-ai-provider.test.js +200 -0
  192. package/dist/src/providers/types.d.ts +165 -0
  193. package/dist/src/providers/types.d.ts.map +1 -0
  194. package/dist/src/providers/types.js +13 -0
  195. package/dist/src/templates/adapters.d.ts +51 -0
  196. package/dist/src/templates/adapters.d.ts.map +1 -0
  197. package/dist/src/templates/adapters.js +104 -0
  198. package/dist/src/templates/adapters.test.d.ts +2 -0
  199. package/dist/src/templates/adapters.test.d.ts.map +1 -0
  200. package/dist/src/templates/adapters.test.js +165 -0
  201. package/dist/src/templates/agent-definition.d.ts +85 -0
  202. package/dist/src/templates/agent-definition.d.ts.map +1 -0
  203. package/dist/src/templates/agent-definition.js +97 -0
  204. package/dist/src/templates/agent-definition.test.d.ts +2 -0
  205. package/dist/src/templates/agent-definition.test.d.ts.map +1 -0
  206. package/dist/src/templates/agent-definition.test.js +209 -0
  207. package/dist/src/templates/index.d.ts +14 -0
  208. package/dist/src/templates/index.d.ts.map +1 -0
  209. package/dist/src/templates/index.js +11 -0
  210. package/dist/src/templates/loader.d.ts +41 -0
  211. package/dist/src/templates/loader.d.ts.map +1 -0
  212. package/dist/src/templates/loader.js +114 -0
  213. package/dist/src/templates/registry.d.ts +80 -0
  214. package/dist/src/templates/registry.d.ts.map +1 -0
  215. package/dist/src/templates/registry.js +177 -0
  216. package/dist/src/templates/registry.test.d.ts +2 -0
  217. package/dist/src/templates/registry.test.d.ts.map +1 -0
  218. package/dist/src/templates/registry.test.js +198 -0
  219. package/dist/src/templates/renderer.d.ts +29 -0
  220. package/dist/src/templates/renderer.d.ts.map +1 -0
  221. package/dist/src/templates/renderer.js +35 -0
  222. package/dist/src/templates/strategy-templates.test.d.ts +2 -0
  223. package/dist/src/templates/strategy-templates.test.d.ts.map +1 -0
  224. package/dist/src/templates/strategy-templates.test.js +619 -0
  225. package/dist/src/templates/types.d.ts +233 -0
  226. package/dist/src/templates/types.d.ts.map +1 -0
  227. package/dist/src/templates/types.js +127 -0
  228. package/dist/src/templates/types.test.d.ts +2 -0
  229. package/dist/src/templates/types.test.d.ts.map +1 -0
  230. package/dist/src/templates/types.test.js +232 -0
  231. package/dist/src/tools/index.d.ts +6 -0
  232. package/dist/src/tools/index.d.ts.map +1 -0
  233. package/dist/src/tools/index.js +3 -0
  234. package/dist/src/tools/linear-runner.d.ts +34 -0
  235. package/dist/src/tools/linear-runner.d.ts.map +1 -0
  236. package/dist/src/tools/linear-runner.js +700 -0
  237. package/dist/src/tools/plugins/linear.d.ts +9 -0
  238. package/dist/src/tools/plugins/linear.d.ts.map +1 -0
  239. package/dist/src/tools/plugins/linear.js +138 -0
  240. package/dist/src/tools/registry.d.ts +9 -0
  241. package/dist/src/tools/registry.d.ts.map +1 -0
  242. package/dist/src/tools/registry.js +18 -0
  243. package/dist/src/tools/types.d.ts +18 -0
  244. package/dist/src/tools/types.d.ts.map +1 -0
  245. package/dist/src/tools/types.js +1 -0
  246. package/package.json +78 -0
@@ -0,0 +1,514 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { WorkflowGovernor } from './governor.js';
3
+ // ---------------------------------------------------------------------------
4
+ // Helpers
5
+ // ---------------------------------------------------------------------------
6
+ function makeIssue(overrides = {}) {
7
+ return {
8
+ id: 'issue-1',
9
+ identifier: 'SUP-100',
10
+ title: 'Test Issue',
11
+ description: undefined,
12
+ status: 'Backlog',
13
+ labels: [],
14
+ createdAt: Date.now() - 2 * 60 * 60 * 1000,
15
+ ...overrides,
16
+ };
17
+ }
18
+ /**
19
+ * Create mock GovernorDependencies with sensible defaults.
20
+ * All callbacks return "no blockers" so issues are actionable by default.
21
+ */
22
+ function makeMockDeps(overrides = {}) {
23
+ return {
24
+ listIssues: vi.fn().mockResolvedValue([]),
25
+ hasActiveSession: vi.fn().mockResolvedValue(false),
26
+ isWithinCooldown: vi.fn().mockResolvedValue(false),
27
+ isParentIssue: vi.fn().mockResolvedValue(false),
28
+ isHeld: vi.fn().mockResolvedValue(false),
29
+ getOverridePriority: vi.fn().mockResolvedValue(null),
30
+ getWorkflowStrategy: vi.fn().mockResolvedValue(undefined),
31
+ isResearchCompleted: vi.fn().mockResolvedValue(false),
32
+ isBacklogCreationCompleted: vi.fn().mockResolvedValue(false),
33
+ getCompletedSessionCount: vi.fn().mockResolvedValue(0),
34
+ dispatchWork: vi.fn().mockResolvedValue(undefined),
35
+ ...overrides,
36
+ };
37
+ }
38
+ function makeConfig(overrides = {}) {
39
+ return {
40
+ projects: ['TestProject'],
41
+ scanIntervalMs: 60_000,
42
+ maxConcurrentDispatches: 3,
43
+ ...overrides,
44
+ };
45
+ }
46
+ // ---------------------------------------------------------------------------
47
+ // scanOnce — basic behavior
48
+ // ---------------------------------------------------------------------------
49
+ describe('WorkflowGovernor.scanOnce', () => {
50
+ it('scans all configured projects', async () => {
51
+ const deps = makeMockDeps();
52
+ const governor = new WorkflowGovernor(makeConfig({ projects: ['Project-A', 'Project-B'] }), deps);
53
+ const results = await governor.scanOnce();
54
+ expect(results).toHaveLength(2);
55
+ expect(results[0].project).toBe('Project-A');
56
+ expect(results[1].project).toBe('Project-B');
57
+ expect(deps.listIssues).toHaveBeenCalledWith('Project-A');
58
+ expect(deps.listIssues).toHaveBeenCalledWith('Project-B');
59
+ });
60
+ it('returns empty results for project with no issues', async () => {
61
+ const deps = makeMockDeps({
62
+ listIssues: vi.fn().mockResolvedValue([]),
63
+ });
64
+ const governor = new WorkflowGovernor(makeConfig(), deps);
65
+ const results = await governor.scanOnce();
66
+ expect(results).toHaveLength(1);
67
+ expect(results[0].scannedIssues).toBe(0);
68
+ expect(results[0].actionsDispatched).toBe(0);
69
+ });
70
+ it('dispatches development for Backlog issues', async () => {
71
+ const issue = makeIssue({ status: 'Backlog' });
72
+ const deps = makeMockDeps({
73
+ listIssues: vi.fn().mockResolvedValue([issue]),
74
+ });
75
+ const governor = new WorkflowGovernor(makeConfig(), deps);
76
+ const results = await governor.scanOnce();
77
+ expect(results[0].actionsDispatched).toBe(1);
78
+ expect(deps.dispatchWork).toHaveBeenCalledWith(expect.objectContaining({ id: 'issue-1' }), 'trigger-development');
79
+ });
80
+ it('dispatches QA for Finished issues', async () => {
81
+ const issue = makeIssue({ status: 'Finished' });
82
+ const deps = makeMockDeps({
83
+ listIssues: vi.fn().mockResolvedValue([issue]),
84
+ });
85
+ const governor = new WorkflowGovernor(makeConfig(), deps);
86
+ const results = await governor.scanOnce();
87
+ expect(results[0].actionsDispatched).toBe(1);
88
+ expect(deps.dispatchWork).toHaveBeenCalledWith(expect.objectContaining({ id: 'issue-1' }), 'trigger-qa');
89
+ });
90
+ it('dispatches acceptance for Delivered issues', async () => {
91
+ const issue = makeIssue({ status: 'Delivered' });
92
+ const deps = makeMockDeps({
93
+ listIssues: vi.fn().mockResolvedValue([issue]),
94
+ });
95
+ const governor = new WorkflowGovernor(makeConfig(), deps);
96
+ const results = await governor.scanOnce();
97
+ expect(results[0].actionsDispatched).toBe(1);
98
+ expect(deps.dispatchWork).toHaveBeenCalledWith(expect.objectContaining({ id: 'issue-1' }), 'trigger-acceptance');
99
+ });
100
+ it('dispatches refinement for Rejected issues', async () => {
101
+ const issue = makeIssue({ status: 'Rejected' });
102
+ const deps = makeMockDeps({
103
+ listIssues: vi.fn().mockResolvedValue([issue]),
104
+ });
105
+ const governor = new WorkflowGovernor(makeConfig(), deps);
106
+ const results = await governor.scanOnce();
107
+ expect(results[0].actionsDispatched).toBe(1);
108
+ expect(deps.dispatchWork).toHaveBeenCalledWith(expect.objectContaining({ id: 'issue-1' }), 'trigger-refinement');
109
+ });
110
+ it('skips issues with active sessions', async () => {
111
+ const issue = makeIssue({ status: 'Backlog' });
112
+ const deps = makeMockDeps({
113
+ listIssues: vi.fn().mockResolvedValue([issue]),
114
+ hasActiveSession: vi.fn().mockResolvedValue(true),
115
+ });
116
+ const governor = new WorkflowGovernor(makeConfig(), deps);
117
+ const results = await governor.scanOnce();
118
+ expect(results[0].actionsDispatched).toBe(0);
119
+ expect(results[0].skippedReasons.size).toBe(1);
120
+ expect(deps.dispatchWork).not.toHaveBeenCalled();
121
+ });
122
+ it('skips held issues', async () => {
123
+ const issue = makeIssue({ status: 'Backlog' });
124
+ const deps = makeMockDeps({
125
+ listIssues: vi.fn().mockResolvedValue([issue]),
126
+ isHeld: vi.fn().mockResolvedValue(true),
127
+ });
128
+ const governor = new WorkflowGovernor(makeConfig(), deps);
129
+ const results = await governor.scanOnce();
130
+ expect(results[0].actionsDispatched).toBe(0);
131
+ expect(deps.dispatchWork).not.toHaveBeenCalled();
132
+ });
133
+ it('skips issues within cooldown', async () => {
134
+ const issue = makeIssue({ status: 'Backlog' });
135
+ const deps = makeMockDeps({
136
+ listIssues: vi.fn().mockResolvedValue([issue]),
137
+ isWithinCooldown: vi.fn().mockResolvedValue(true),
138
+ });
139
+ const governor = new WorkflowGovernor(makeConfig(), deps);
140
+ const results = await governor.scanOnce();
141
+ expect(results[0].actionsDispatched).toBe(0);
142
+ expect(deps.dispatchWork).not.toHaveBeenCalled();
143
+ });
144
+ });
145
+ // ---------------------------------------------------------------------------
146
+ // maxConcurrentDispatches limiting
147
+ // ---------------------------------------------------------------------------
148
+ describe('WorkflowGovernor.scanOnce — dispatch limit', () => {
149
+ it('respects maxConcurrentDispatches limit', async () => {
150
+ const issues = [
151
+ makeIssue({ id: 'issue-1', identifier: 'SUP-1', status: 'Backlog' }),
152
+ makeIssue({ id: 'issue-2', identifier: 'SUP-2', status: 'Backlog' }),
153
+ makeIssue({ id: 'issue-3', identifier: 'SUP-3', status: 'Backlog' }),
154
+ makeIssue({ id: 'issue-4', identifier: 'SUP-4', status: 'Backlog' }),
155
+ makeIssue({ id: 'issue-5', identifier: 'SUP-5', status: 'Backlog' }),
156
+ ];
157
+ const deps = makeMockDeps({
158
+ listIssues: vi.fn().mockResolvedValue(issues),
159
+ });
160
+ const governor = new WorkflowGovernor(makeConfig({ maxConcurrentDispatches: 2 }), deps);
161
+ const results = await governor.scanOnce();
162
+ expect(results[0].actionsDispatched).toBe(2);
163
+ expect(deps.dispatchWork).toHaveBeenCalledTimes(2);
164
+ });
165
+ it('dispatches all when under the limit', async () => {
166
+ const issues = [
167
+ makeIssue({ id: 'issue-1', identifier: 'SUP-1', status: 'Backlog' }),
168
+ makeIssue({ id: 'issue-2', identifier: 'SUP-2', status: 'Finished' }),
169
+ ];
170
+ const deps = makeMockDeps({
171
+ listIssues: vi.fn().mockResolvedValue(issues),
172
+ });
173
+ const governor = new WorkflowGovernor(makeConfig({ maxConcurrentDispatches: 5 }), deps);
174
+ const results = await governor.scanOnce();
175
+ expect(results[0].actionsDispatched).toBe(2);
176
+ expect(deps.dispatchWork).toHaveBeenCalledTimes(2);
177
+ });
178
+ it('counts skipped issues toward scanned but not dispatched', async () => {
179
+ const issues = [
180
+ makeIssue({ id: 'issue-1', identifier: 'SUP-1', status: 'Accepted' }), // terminal
181
+ makeIssue({ id: 'issue-2', identifier: 'SUP-2', status: 'Backlog' }), // actionable
182
+ ];
183
+ const deps = makeMockDeps({
184
+ listIssues: vi.fn().mockResolvedValue(issues),
185
+ });
186
+ const governor = new WorkflowGovernor(makeConfig(), deps);
187
+ const results = await governor.scanOnce();
188
+ expect(results[0].scannedIssues).toBe(2);
189
+ expect(results[0].actionsDispatched).toBe(1);
190
+ expect(results[0].skippedReasons.size).toBe(1);
191
+ });
192
+ });
193
+ // ---------------------------------------------------------------------------
194
+ // Error handling
195
+ // ---------------------------------------------------------------------------
196
+ describe('WorkflowGovernor.scanOnce — error handling', () => {
197
+ it('records error when listIssues fails', async () => {
198
+ const deps = makeMockDeps({
199
+ listIssues: vi.fn().mockRejectedValue(new Error('API timeout')),
200
+ });
201
+ const governor = new WorkflowGovernor(makeConfig(), deps);
202
+ const results = await governor.scanOnce();
203
+ expect(results[0].errors).toHaveLength(1);
204
+ expect(results[0].errors[0].error).toContain('API timeout');
205
+ });
206
+ it('single issue failure does not stop scan of remaining issues', async () => {
207
+ const issues = [
208
+ makeIssue({ id: 'issue-1', identifier: 'SUP-1', status: 'Backlog' }),
209
+ makeIssue({ id: 'issue-2', identifier: 'SUP-2', status: 'Backlog' }),
210
+ makeIssue({ id: 'issue-3', identifier: 'SUP-3', status: 'Backlog' }),
211
+ ];
212
+ const dispatchWork = vi.fn()
213
+ .mockResolvedValueOnce(undefined) // issue-1 succeeds
214
+ .mockRejectedValueOnce(new Error('Dispatch failed')) // issue-2 fails
215
+ .mockResolvedValueOnce(undefined); // issue-3 succeeds
216
+ const deps = makeMockDeps({
217
+ listIssues: vi.fn().mockResolvedValue(issues),
218
+ dispatchWork,
219
+ });
220
+ const governor = new WorkflowGovernor(makeConfig(), deps);
221
+ const results = await governor.scanOnce();
222
+ // issue-1 and issue-3 dispatched, issue-2 errored
223
+ expect(results[0].actionsDispatched).toBe(2);
224
+ expect(results[0].errors).toHaveLength(1);
225
+ expect(results[0].errors[0].issueId).toBe('SUP-2');
226
+ expect(dispatchWork).toHaveBeenCalledTimes(3);
227
+ });
228
+ it('context gathering failure is caught and recorded', async () => {
229
+ const issue = makeIssue({ status: 'Backlog' });
230
+ const deps = makeMockDeps({
231
+ listIssues: vi.fn().mockResolvedValue([issue]),
232
+ hasActiveSession: vi.fn().mockRejectedValue(new Error('Redis down')),
233
+ });
234
+ const governor = new WorkflowGovernor(makeConfig(), deps);
235
+ const results = await governor.scanOnce();
236
+ expect(results[0].errors).toHaveLength(1);
237
+ expect(results[0].errors[0].error).toContain('Redis down');
238
+ expect(results[0].actionsDispatched).toBe(0);
239
+ });
240
+ });
241
+ // ---------------------------------------------------------------------------
242
+ // Gathers context in parallel
243
+ // ---------------------------------------------------------------------------
244
+ describe('WorkflowGovernor.scanOnce — context gathering', () => {
245
+ it('calls all dependency checks for each issue', async () => {
246
+ const issue = makeIssue({ id: 'issue-1', status: 'Backlog' });
247
+ const deps = makeMockDeps({
248
+ listIssues: vi.fn().mockResolvedValue([issue]),
249
+ });
250
+ const governor = new WorkflowGovernor(makeConfig(), deps);
251
+ await governor.scanOnce();
252
+ expect(deps.hasActiveSession).toHaveBeenCalledWith('issue-1');
253
+ expect(deps.isWithinCooldown).toHaveBeenCalledWith('issue-1');
254
+ expect(deps.isParentIssue).toHaveBeenCalledWith('issue-1');
255
+ expect(deps.isHeld).toHaveBeenCalledWith('issue-1');
256
+ expect(deps.getWorkflowStrategy).toHaveBeenCalledWith('issue-1');
257
+ expect(deps.isResearchCompleted).toHaveBeenCalledWith('issue-1');
258
+ expect(deps.isBacklogCreationCompleted).toHaveBeenCalledWith('issue-1');
259
+ });
260
+ });
261
+ // ---------------------------------------------------------------------------
262
+ // start / stop lifecycle
263
+ // ---------------------------------------------------------------------------
264
+ describe('WorkflowGovernor — start/stop lifecycle', () => {
265
+ beforeEach(() => {
266
+ vi.useFakeTimers();
267
+ });
268
+ afterEach(() => {
269
+ vi.useRealTimers();
270
+ });
271
+ it('isRunning returns false before start', () => {
272
+ const deps = makeMockDeps();
273
+ const governor = new WorkflowGovernor(makeConfig(), deps);
274
+ expect(governor.isRunning()).toBe(false);
275
+ });
276
+ it('isRunning returns true after start', () => {
277
+ const deps = makeMockDeps();
278
+ const governor = new WorkflowGovernor(makeConfig(), deps);
279
+ governor.start();
280
+ expect(governor.isRunning()).toBe(true);
281
+ governor.stop();
282
+ });
283
+ it('isRunning returns false after stop', () => {
284
+ const deps = makeMockDeps();
285
+ const governor = new WorkflowGovernor(makeConfig(), deps);
286
+ governor.start();
287
+ governor.stop();
288
+ expect(governor.isRunning()).toBe(false);
289
+ });
290
+ it('start triggers an immediate scan', async () => {
291
+ const deps = makeMockDeps();
292
+ const governor = new WorkflowGovernor(makeConfig(), deps);
293
+ governor.start();
294
+ // Flush the fire-and-forget scanOnce by allowing the promise microtask to resolve
295
+ await vi.advanceTimersByTimeAsync(1);
296
+ expect(deps.listIssues).toHaveBeenCalledTimes(1);
297
+ governor.stop();
298
+ });
299
+ it('subsequent scans run on the configured interval', async () => {
300
+ const deps = makeMockDeps();
301
+ const governor = new WorkflowGovernor(makeConfig({ scanIntervalMs: 5000 }), deps);
302
+ governor.start();
303
+ // First immediate scan
304
+ await vi.advanceTimersByTimeAsync(1);
305
+ expect(deps.listIssues).toHaveBeenCalledTimes(1);
306
+ // Advance by one interval
307
+ await vi.advanceTimersByTimeAsync(5000);
308
+ expect(deps.listIssues).toHaveBeenCalledTimes(2);
309
+ // Advance by another interval
310
+ await vi.advanceTimersByTimeAsync(5000);
311
+ expect(deps.listIssues).toHaveBeenCalledTimes(3);
312
+ governor.stop();
313
+ });
314
+ it('stop prevents further scans', async () => {
315
+ const deps = makeMockDeps();
316
+ const governor = new WorkflowGovernor(makeConfig({ scanIntervalMs: 5000 }), deps);
317
+ governor.start();
318
+ await vi.advanceTimersByTimeAsync(1);
319
+ expect(deps.listIssues).toHaveBeenCalledTimes(1);
320
+ governor.stop();
321
+ await vi.advanceTimersByTimeAsync(10000);
322
+ // No additional calls after stop
323
+ expect(deps.listIssues).toHaveBeenCalledTimes(1);
324
+ });
325
+ it('calling start twice does not create duplicate intervals', async () => {
326
+ const deps = makeMockDeps();
327
+ const governor = new WorkflowGovernor(makeConfig({ scanIntervalMs: 5000 }), deps);
328
+ governor.start();
329
+ governor.start(); // second call should be a no-op
330
+ await vi.advanceTimersByTimeAsync(1);
331
+ expect(deps.listIssues).toHaveBeenCalledTimes(1);
332
+ await vi.advanceTimersByTimeAsync(5000);
333
+ // Only 1 interval fires, not 2
334
+ expect(deps.listIssues).toHaveBeenCalledTimes(2);
335
+ governor.stop();
336
+ });
337
+ it('calling stop when not running is safe', () => {
338
+ const deps = makeMockDeps();
339
+ const governor = new WorkflowGovernor(makeConfig(), deps);
340
+ // Should not throw
341
+ expect(() => governor.stop()).not.toThrow();
342
+ });
343
+ });
344
+ // ---------------------------------------------------------------------------
345
+ // Multiple projects
346
+ // ---------------------------------------------------------------------------
347
+ describe('WorkflowGovernor.scanOnce — multiple projects', () => {
348
+ it('scans each project independently', async () => {
349
+ const projectAIssues = [
350
+ makeIssue({ id: 'a-1', identifier: 'A-1', status: 'Backlog', project: 'Project-A' }),
351
+ ];
352
+ const projectBIssues = [
353
+ makeIssue({ id: 'b-1', identifier: 'B-1', status: 'Finished', project: 'Project-B' }),
354
+ makeIssue({ id: 'b-2', identifier: 'B-2', status: 'Delivered', project: 'Project-B' }),
355
+ ];
356
+ const listIssues = vi.fn()
357
+ .mockImplementation((project) => {
358
+ if (project === 'Project-A')
359
+ return Promise.resolve(projectAIssues);
360
+ if (project === 'Project-B')
361
+ return Promise.resolve(projectBIssues);
362
+ return Promise.resolve([]);
363
+ });
364
+ const deps = makeMockDeps({ listIssues });
365
+ const governor = new WorkflowGovernor(makeConfig({ projects: ['Project-A', 'Project-B'] }), deps);
366
+ const results = await governor.scanOnce();
367
+ expect(results).toHaveLength(2);
368
+ expect(results[0].project).toBe('Project-A');
369
+ expect(results[0].actionsDispatched).toBe(1);
370
+ expect(results[1].project).toBe('Project-B');
371
+ expect(results[1].actionsDispatched).toBe(2);
372
+ });
373
+ it('dispatch limit is per-project', async () => {
374
+ const issues = [
375
+ makeIssue({ id: 'issue-1', identifier: 'SUP-1', status: 'Backlog' }),
376
+ makeIssue({ id: 'issue-2', identifier: 'SUP-2', status: 'Backlog' }),
377
+ makeIssue({ id: 'issue-3', identifier: 'SUP-3', status: 'Backlog' }),
378
+ ];
379
+ const deps = makeMockDeps({
380
+ listIssues: vi.fn().mockResolvedValue(issues),
381
+ });
382
+ const governor = new WorkflowGovernor(makeConfig({
383
+ projects: ['Project-A', 'Project-B'],
384
+ maxConcurrentDispatches: 1,
385
+ }), deps);
386
+ const results = await governor.scanOnce();
387
+ // Each project dispatches 1 (the limit), total 2
388
+ expect(results[0].actionsDispatched).toBe(1);
389
+ expect(results[1].actionsDispatched).toBe(1);
390
+ expect(deps.dispatchWork).toHaveBeenCalledTimes(2);
391
+ });
392
+ });
393
+ // ---------------------------------------------------------------------------
394
+ // Mixed statuses in a single scan
395
+ // ---------------------------------------------------------------------------
396
+ describe('WorkflowGovernor.scanOnce — mixed statuses', () => {
397
+ it('handles a mix of actionable and non-actionable issues', async () => {
398
+ const issues = [
399
+ makeIssue({ id: 'i-1', identifier: 'SUP-1', status: 'Accepted' }), // terminal
400
+ makeIssue({ id: 'i-2', identifier: 'SUP-2', status: 'Backlog' }), // actionable
401
+ makeIssue({ id: 'i-3', identifier: 'SUP-3', status: 'Started' }), // not actionable (already working)
402
+ makeIssue({ id: 'i-4', identifier: 'SUP-4', status: 'Finished' }), // actionable
403
+ makeIssue({ id: 'i-5', identifier: 'SUP-5', status: 'Canceled' }), // terminal
404
+ ];
405
+ const deps = makeMockDeps({
406
+ listIssues: vi.fn().mockResolvedValue(issues),
407
+ });
408
+ const governor = new WorkflowGovernor(makeConfig(), deps);
409
+ const results = await governor.scanOnce();
410
+ expect(results[0].scannedIssues).toBe(5);
411
+ expect(results[0].actionsDispatched).toBe(2); // Backlog + Finished
412
+ expect(results[0].skippedReasons.size).toBe(3); // Accepted + Started + Canceled
413
+ });
414
+ });
415
+ // ---------------------------------------------------------------------------
416
+ // PRIORITY override affects processing order
417
+ // ---------------------------------------------------------------------------
418
+ describe('WorkflowGovernor.scanOnce — PRIORITY override sorting', () => {
419
+ it('dispatches high-priority issues before low-priority ones', async () => {
420
+ const issues = [
421
+ makeIssue({ id: 'low-1', identifier: 'SUP-1', status: 'Backlog' }),
422
+ makeIssue({ id: 'high-1', identifier: 'SUP-2', status: 'Backlog' }),
423
+ makeIssue({ id: 'med-1', identifier: 'SUP-3', status: 'Backlog' }),
424
+ ];
425
+ const getOverridePriority = vi.fn().mockImplementation((issueId) => {
426
+ if (issueId === 'high-1')
427
+ return Promise.resolve('high');
428
+ if (issueId === 'med-1')
429
+ return Promise.resolve('medium');
430
+ if (issueId === 'low-1')
431
+ return Promise.resolve('low');
432
+ return Promise.resolve(null);
433
+ });
434
+ const dispatchWork = vi.fn().mockResolvedValue(undefined);
435
+ const deps = makeMockDeps({
436
+ listIssues: vi.fn().mockResolvedValue(issues),
437
+ getOverridePriority,
438
+ dispatchWork,
439
+ });
440
+ const governor = new WorkflowGovernor(makeConfig(), deps);
441
+ await governor.scanOnce();
442
+ expect(dispatchWork).toHaveBeenCalledTimes(3);
443
+ // high-1 dispatched first, then med-1, then low-1
444
+ expect(dispatchWork.mock.calls[0][0]).toEqual(expect.objectContaining({ id: 'high-1' }));
445
+ expect(dispatchWork.mock.calls[1][0]).toEqual(expect.objectContaining({ id: 'med-1' }));
446
+ expect(dispatchWork.mock.calls[2][0]).toEqual(expect.objectContaining({ id: 'low-1' }));
447
+ });
448
+ it('dispatches priority-overridden issues before non-overridden ones', async () => {
449
+ const issues = [
450
+ makeIssue({ id: 'none-1', identifier: 'SUP-1', status: 'Backlog' }),
451
+ makeIssue({ id: 'none-2', identifier: 'SUP-2', status: 'Backlog' }),
452
+ makeIssue({ id: 'high-1', identifier: 'SUP-3', status: 'Backlog' }),
453
+ ];
454
+ const getOverridePriority = vi.fn().mockImplementation((issueId) => {
455
+ if (issueId === 'high-1')
456
+ return Promise.resolve('high');
457
+ return Promise.resolve(null);
458
+ });
459
+ const dispatchWork = vi.fn().mockResolvedValue(undefined);
460
+ const deps = makeMockDeps({
461
+ listIssues: vi.fn().mockResolvedValue(issues),
462
+ getOverridePriority,
463
+ dispatchWork,
464
+ });
465
+ const governor = new WorkflowGovernor(makeConfig(), deps);
466
+ await governor.scanOnce();
467
+ expect(dispatchWork).toHaveBeenCalledTimes(3);
468
+ // high-1 dispatched first, regardless of list order
469
+ expect(dispatchWork.mock.calls[0][0]).toEqual(expect.objectContaining({ id: 'high-1' }));
470
+ });
471
+ it('priority sorting affects which issues get dispatched under the limit', async () => {
472
+ const issues = [
473
+ makeIssue({ id: 'none-1', identifier: 'SUP-1', status: 'Backlog' }),
474
+ makeIssue({ id: 'none-2', identifier: 'SUP-2', status: 'Backlog' }),
475
+ makeIssue({ id: 'high-1', identifier: 'SUP-3', status: 'Backlog' }),
476
+ ];
477
+ const getOverridePriority = vi.fn().mockImplementation((issueId) => {
478
+ if (issueId === 'high-1')
479
+ return Promise.resolve('high');
480
+ return Promise.resolve(null);
481
+ });
482
+ const dispatchWork = vi.fn().mockResolvedValue(undefined);
483
+ const deps = makeMockDeps({
484
+ listIssues: vi.fn().mockResolvedValue(issues),
485
+ getOverridePriority,
486
+ dispatchWork,
487
+ });
488
+ // Only dispatch 1 — the high-priority issue should win
489
+ const governor = new WorkflowGovernor(makeConfig({ maxConcurrentDispatches: 1 }), deps);
490
+ await governor.scanOnce();
491
+ expect(dispatchWork).toHaveBeenCalledTimes(1);
492
+ expect(dispatchWork.mock.calls[0][0]).toEqual(expect.objectContaining({ id: 'high-1' }));
493
+ });
494
+ it('issues with equal priority maintain stable order', async () => {
495
+ const issues = [
496
+ makeIssue({ id: 'a', identifier: 'SUP-1', status: 'Backlog' }),
497
+ makeIssue({ id: 'b', identifier: 'SUP-2', status: 'Backlog' }),
498
+ makeIssue({ id: 'c', identifier: 'SUP-3', status: 'Backlog' }),
499
+ ];
500
+ // All have no priority override
501
+ const dispatchWork = vi.fn().mockResolvedValue(undefined);
502
+ const deps = makeMockDeps({
503
+ listIssues: vi.fn().mockResolvedValue(issues),
504
+ dispatchWork,
505
+ });
506
+ const governor = new WorkflowGovernor(makeConfig(), deps);
507
+ await governor.scanOnce();
508
+ expect(dispatchWork).toHaveBeenCalledTimes(3);
509
+ // Original order preserved for equal priority
510
+ expect(dispatchWork.mock.calls[0][0]).toEqual(expect.objectContaining({ id: 'a' }));
511
+ expect(dispatchWork.mock.calls[1][0]).toEqual(expect.objectContaining({ id: 'b' }));
512
+ expect(dispatchWork.mock.calls[2][0]).toEqual(expect.objectContaining({ id: 'c' }));
513
+ });
514
+ });
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Human Touchpoint Manager
3
+ *
4
+ * Manages human override state and generates review request notifications.
5
+ * Uses a storage adapter pattern so that packages/core does not depend on
6
+ * packages/server (Redis) directly.
7
+ */
8
+ import type { OverrideDirective, OverridePriority } from './override-parser.js';
9
+ /**
10
+ * Persisted override state for an issue
11
+ */
12
+ export interface OverrideState {
13
+ issueId: string;
14
+ directive: OverrideDirective;
15
+ isActive: boolean;
16
+ expiresAt?: number;
17
+ }
18
+ /**
19
+ * Configuration for touchpoint timeouts
20
+ */
21
+ export interface TouchpointConfig {
22
+ /** Timeout for review requests (default: 4 hours) */
23
+ reviewRequestTimeoutMs: number;
24
+ /** Timeout for decomposition proposals (default: 2 hours) */
25
+ decompositionProposalTimeoutMs: number;
26
+ /** Timeout for escalation alerts (default: Infinity — requires human response) */
27
+ escalationTimeoutMs: number;
28
+ }
29
+ /**
30
+ * Default touchpoint configuration
31
+ */
32
+ export declare const DEFAULT_TOUCHPOINT_CONFIG: TouchpointConfig;
33
+ /**
34
+ * Types of human touchpoint notifications
35
+ */
36
+ export type TouchpointType = 'review-request' | 'decomposition-proposal' | 'escalation-alert';
37
+ /**
38
+ * A notification posted to an issue requesting human attention
39
+ */
40
+ export interface TouchpointNotification {
41
+ type: TouchpointType;
42
+ issueId: string;
43
+ body: string;
44
+ postedAt: number;
45
+ timeoutMs: number;
46
+ respondedAt?: number;
47
+ }
48
+ /**
49
+ * Storage adapter for override state persistence.
50
+ * Implementations can back this with Redis, in-memory maps, etc.
51
+ */
52
+ export interface OverrideStorage {
53
+ get(issueId: string): Promise<OverrideState | null>;
54
+ set(issueId: string, state: OverrideState): Promise<void>;
55
+ clear(issueId: string): Promise<void>;
56
+ }
57
+ /**
58
+ * In-memory override storage for testing and local development
59
+ */
60
+ export declare class InMemoryOverrideStorage implements OverrideStorage {
61
+ private store;
62
+ get(issueId: string): Promise<OverrideState | null>;
63
+ set(issueId: string, state: OverrideState): Promise<void>;
64
+ clear(issueId: string): Promise<void>;
65
+ }
66
+ /**
67
+ * Initialize the touchpoint manager with a storage adapter.
68
+ * Must be called before using state management functions.
69
+ */
70
+ export declare function initTouchpointStorage(storage: OverrideStorage): void;
71
+ /**
72
+ * Get the current override state for an issue
73
+ */
74
+ export declare function getOverrideState(issueId: string): Promise<OverrideState | null>;
75
+ /**
76
+ * Set an override directive for an issue
77
+ */
78
+ export declare function setOverrideState(issueId: string, directive: OverrideDirective): Promise<void>;
79
+ /**
80
+ * Clear the override state for an issue (e.g., on RESUME)
81
+ */
82
+ export declare function clearOverrideState(issueId: string): Promise<void>;
83
+ /**
84
+ * Check if an issue is currently held (HOLD directive active)
85
+ */
86
+ export declare function isHeld(issueId: string): Promise<boolean>;
87
+ /**
88
+ * Get the PRIORITY override for an issue, if one is active.
89
+ * Returns the priority level ('high' | 'medium' | 'low') or null if no priority override.
90
+ */
91
+ export declare function getOverridePriority(issueId: string): Promise<OverridePriority | null>;
92
+ /**
93
+ * Generate a review request notification.
94
+ * Typically posted at cycle 2 when the context-enriched strategy kicks in.
95
+ */
96
+ export declare function generateReviewRequest(context: {
97
+ issueIdentifier: string;
98
+ cycleCount: number;
99
+ failureSummary: string;
100
+ strategy: string;
101
+ totalCostUsd?: number;
102
+ }, config?: TouchpointConfig): TouchpointNotification;
103
+ /**
104
+ * Generate a decomposition proposal notification.
105
+ * Typically posted at cycle 3 when the decompose strategy kicks in.
106
+ */
107
+ export declare function generateDecompositionProposal(context: {
108
+ issueIdentifier: string;
109
+ cycleCount: number;
110
+ failureSummary: string;
111
+ totalCostUsd?: number;
112
+ }, config?: TouchpointConfig): TouchpointNotification;
113
+ /**
114
+ * Generate an escalation alert notification.
115
+ * Posted at cycle 4+ when human intervention is required.
116
+ */
117
+ export declare function generateEscalationAlert(context: {
118
+ issueIdentifier: string;
119
+ cycleCount: number;
120
+ failureSummary: string;
121
+ totalCostUsd?: number;
122
+ blockerIdentifier?: string;
123
+ }, config?: TouchpointConfig): TouchpointNotification;
124
+ /**
125
+ * Check if a touchpoint notification has timed out (human did not respond in time).
126
+ *
127
+ * A touchpoint with Infinity timeout never times out (always returns false).
128
+ * A touchpoint that has been responded to (respondedAt is set) never times out.
129
+ */
130
+ export declare function hasTouchpointTimedOut(notification: TouchpointNotification): boolean;
131
+ //# sourceMappingURL=human-touchpoints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"human-touchpoints.d.ts","sourceRoot":"","sources":["../../../src/governor/human-touchpoints.ts"],"names":[],"mappings":"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"}