agentic-orchestrator 0.1.13 → 0.1.15

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 (108) hide show
  1. package/AGENTS.md +139 -0
  2. package/CLAUDE.md +12 -0
  3. package/agentic/orchestrator/agents.yaml +3 -0
  4. package/agentic/orchestrator/defaults/policy.defaults.yaml +3 -0
  5. package/agentic/orchestrator/policy.yaml +3 -0
  6. package/agentic/orchestrator/schemas/agents.schema.json +15 -0
  7. package/agentic/orchestrator/schemas/policy.schema.json +14 -0
  8. package/apps/control-plane/src/cli/cli-argument-parser.ts +7 -0
  9. package/apps/control-plane/src/cli/help-command-handler.ts +8 -0
  10. package/apps/control-plane/src/cli/init-command-handler.ts +6 -0
  11. package/apps/control-plane/src/cli/resume-command-handler.ts +31 -2
  12. package/apps/control-plane/src/cli/run-command-handler.ts +31 -3
  13. package/apps/control-plane/src/cli/types.ts +1 -0
  14. package/apps/control-plane/src/core/error-codes.ts +4 -0
  15. package/apps/control-plane/src/core/kernel.ts +3 -0
  16. package/apps/control-plane/src/index.ts +14 -0
  17. package/apps/control-plane/src/interfaces/cli/bootstrap.ts +25 -3
  18. package/apps/control-plane/src/providers/api-worker-provider.ts +115 -0
  19. package/apps/control-plane/src/providers/cli-worker-provider.ts +385 -0
  20. package/apps/control-plane/src/providers/output-parsers/generic-output-parser.ts +100 -0
  21. package/apps/control-plane/src/providers/output-parsers/index.ts +11 -0
  22. package/apps/control-plane/src/providers/output-parsers/types.ts +23 -0
  23. package/apps/control-plane/src/providers/providers.ts +19 -0
  24. package/apps/control-plane/src/providers/worker-provider-factory.ts +198 -0
  25. package/apps/control-plane/src/supervisor/build-wave-executor.ts +140 -3
  26. package/apps/control-plane/src/supervisor/planning-wave-executor.ts +125 -5
  27. package/apps/control-plane/src/supervisor/qa-wave-executor.ts +144 -2
  28. package/apps/control-plane/src/supervisor/runtime.ts +24 -0
  29. package/apps/control-plane/src/supervisor/worker-decision-loop.ts +134 -12
  30. package/apps/control-plane/test/cli.unit.spec.ts +36 -0
  31. package/apps/control-plane/test/dashboard-api.integration.spec.ts +2 -2
  32. package/apps/control-plane/test/resume-command.spec.ts +31 -1
  33. package/apps/control-plane/test/worker-decision-loop.spec.ts +3 -0
  34. package/apps/control-plane/test/worker-execution-policy.spec.ts +284 -0
  35. package/apps/control-plane/test/worker-provider-adapters.spec.ts +440 -0
  36. package/apps/control-plane/test/worker-provider-factory.spec.ts +151 -0
  37. package/config/agentic/orchestrator/agents.yaml +3 -0
  38. package/dist/apps/control-plane/cli/cli-argument-parser.js +7 -0
  39. package/dist/apps/control-plane/cli/cli-argument-parser.js.map +1 -1
  40. package/dist/apps/control-plane/cli/help-command-handler.js +8 -0
  41. package/dist/apps/control-plane/cli/help-command-handler.js.map +1 -1
  42. package/dist/apps/control-plane/cli/init-command-handler.js +6 -0
  43. package/dist/apps/control-plane/cli/init-command-handler.js.map +1 -1
  44. package/dist/apps/control-plane/cli/resume-command-handler.d.ts +3 -0
  45. package/dist/apps/control-plane/cli/resume-command-handler.js +18 -2
  46. package/dist/apps/control-plane/cli/resume-command-handler.js.map +1 -1
  47. package/dist/apps/control-plane/cli/run-command-handler.d.ts +3 -1
  48. package/dist/apps/control-plane/cli/run-command-handler.js +17 -3
  49. package/dist/apps/control-plane/cli/run-command-handler.js.map +1 -1
  50. package/dist/apps/control-plane/cli/types.d.ts +1 -0
  51. package/dist/apps/control-plane/core/error-codes.d.ts +4 -0
  52. package/dist/apps/control-plane/core/error-codes.js +4 -0
  53. package/dist/apps/control-plane/core/error-codes.js.map +1 -1
  54. package/dist/apps/control-plane/core/kernel.d.ts +3 -0
  55. package/dist/apps/control-plane/core/kernel.js.map +1 -1
  56. package/dist/apps/control-plane/index.d.ts +2 -0
  57. package/dist/apps/control-plane/index.js +1 -0
  58. package/dist/apps/control-plane/index.js.map +1 -1
  59. package/dist/apps/control-plane/interfaces/cli/bootstrap.js +14 -2
  60. package/dist/apps/control-plane/interfaces/cli/bootstrap.js.map +1 -1
  61. package/dist/apps/control-plane/providers/api-worker-provider.d.ts +31 -0
  62. package/dist/apps/control-plane/providers/api-worker-provider.js +73 -0
  63. package/dist/apps/control-plane/providers/api-worker-provider.js.map +1 -0
  64. package/dist/apps/control-plane/providers/cli-worker-provider.d.ts +46 -0
  65. package/dist/apps/control-plane/providers/cli-worker-provider.js +274 -0
  66. package/dist/apps/control-plane/providers/cli-worker-provider.js.map +1 -0
  67. package/dist/apps/control-plane/providers/output-parsers/generic-output-parser.d.ts +10 -0
  68. package/dist/apps/control-plane/providers/output-parsers/generic-output-parser.js +79 -0
  69. package/dist/apps/control-plane/providers/output-parsers/generic-output-parser.js.map +1 -0
  70. package/dist/apps/control-plane/providers/output-parsers/index.d.ts +2 -0
  71. package/dist/apps/control-plane/providers/output-parsers/index.js +2 -0
  72. package/dist/apps/control-plane/providers/output-parsers/index.js.map +1 -0
  73. package/dist/apps/control-plane/providers/output-parsers/types.d.ts +21 -0
  74. package/dist/apps/control-plane/providers/output-parsers/types.js +2 -0
  75. package/dist/apps/control-plane/providers/output-parsers/types.js.map +1 -0
  76. package/dist/apps/control-plane/providers/providers.d.ts +4 -0
  77. package/dist/apps/control-plane/providers/providers.js +15 -0
  78. package/dist/apps/control-plane/providers/providers.js.map +1 -1
  79. package/dist/apps/control-plane/providers/worker-provider-factory.d.ts +41 -0
  80. package/dist/apps/control-plane/providers/worker-provider-factory.js +104 -0
  81. package/dist/apps/control-plane/providers/worker-provider-factory.js.map +1 -0
  82. package/dist/apps/control-plane/supervisor/build-wave-executor.d.ts +13 -0
  83. package/dist/apps/control-plane/supervisor/build-wave-executor.js +92 -3
  84. package/dist/apps/control-plane/supervisor/build-wave-executor.js.map +1 -1
  85. package/dist/apps/control-plane/supervisor/planning-wave-executor.d.ts +12 -0
  86. package/dist/apps/control-plane/supervisor/planning-wave-executor.js +83 -5
  87. package/dist/apps/control-plane/supervisor/planning-wave-executor.js.map +1 -1
  88. package/dist/apps/control-plane/supervisor/qa-wave-executor.d.ts +13 -0
  89. package/dist/apps/control-plane/supervisor/qa-wave-executor.js +91 -2
  90. package/dist/apps/control-plane/supervisor/qa-wave-executor.js.map +1 -1
  91. package/dist/apps/control-plane/supervisor/runtime.js +19 -0
  92. package/dist/apps/control-plane/supervisor/runtime.js.map +1 -1
  93. package/dist/apps/control-plane/supervisor/worker-decision-loop.d.ts +10 -0
  94. package/dist/apps/control-plane/supervisor/worker-decision-loop.js +113 -12
  95. package/dist/apps/control-plane/supervisor/worker-decision-loop.js.map +1 -1
  96. package/package.json +2 -2
  97. package/packages/web-dashboard/next-env.d.ts +2 -1
  98. package/packages/web-dashboard/src/app/api/features/[id]/checkout/route.ts +4 -3
  99. package/packages/web-dashboard/src/app/api/features/[id]/diff/route.ts +6 -2
  100. package/packages/web-dashboard/src/app/api/features/[id]/evidence/[artifact]/route.ts +6 -5
  101. package/packages/web-dashboard/src/app/api/features/[id]/review/route.ts +5 -4
  102. package/packages/web-dashboard/src/app/api/features/[id]/route.ts +7 -3
  103. package/packages/web-dashboard/src/lib/aop-client.ts +2 -2
  104. package/packages/web-dashboard/src/lib/orchestrator-tools.ts +1 -1
  105. package/packages/web-dashboard/tsconfig.json +1 -0
  106. package/spec-files/outstanding/agentic_orchestrator_human_input_interaction_protocol_spec.md +590 -0
  107. package/spec-files/outstanding/agentic_orchestrator_real_worker_provider_execution_spec.md +616 -0
  108. package/spec-files/progress.md +91 -0
@@ -0,0 +1,284 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { ERROR_CODES } from '../src/core/error-codes.js';
3
+ import { STATUS, TOOLS } from '../src/core/constants.js';
4
+ import { BuildWaveExecutor } from '../src/supervisor/build-wave-executor.js';
5
+ import { QaWaveExecutor } from '../src/supervisor/qa-wave-executor.js';
6
+ import type { WorkerDecisionResult } from '../src/supervisor/worker-decision-loop.js';
7
+
8
+ function decision(overrides: Partial<WorkerDecisionResult>): WorkerDecisionResult {
9
+ return {
10
+ planSubmission: false,
11
+ patchApplied: false,
12
+ noteLogged: false,
13
+ requestHandled: false,
14
+ invalidOutput: false,
15
+ noProgress: false,
16
+ outputTypes: [],
17
+ priorityOrder: [],
18
+ toolResults: [],
19
+ ...overrides,
20
+ };
21
+ }
22
+
23
+ function buildToolCaller() {
24
+ const callTool = vi.fn(
25
+ async (role: string, toolName: string, _args?: Record<string, unknown>) => {
26
+ if (toolName === TOOLS.FEATURE_STATE_GET && role === 'builder') {
27
+ return {
28
+ ok: true,
29
+ data: {
30
+ front_matter: {
31
+ status: STATUS.BUILDING,
32
+ gate_retry_count: 0,
33
+ version: 3,
34
+ },
35
+ },
36
+ };
37
+ }
38
+ if (toolName === TOOLS.FEATURE_STATE_GET && role === 'orchestrator') {
39
+ return {
40
+ ok: true,
41
+ data: {
42
+ front_matter: {
43
+ version: 7,
44
+ },
45
+ },
46
+ };
47
+ }
48
+ if (toolName === TOOLS.FEATURE_GET_CONTEXT) {
49
+ return { ok: true, data: { state: 'ctx' } };
50
+ }
51
+ if (toolName === TOOLS.GATES_RUN) {
52
+ return { ok: true, data: { overall: 'pass', evidence_path: 'evidence.json' } };
53
+ }
54
+ return { ok: true, data: {} };
55
+ },
56
+ );
57
+ return { callTool };
58
+ }
59
+
60
+ function qaToolCaller() {
61
+ const callTool = vi.fn(
62
+ async (role: string, toolName: string, _args?: Record<string, unknown>) => {
63
+ if (toolName === TOOLS.FEATURE_STATE_GET && role === 'qa') {
64
+ return {
65
+ ok: true,
66
+ data: {
67
+ front_matter: {
68
+ status: STATUS.QA,
69
+ gate_retry_count: 0,
70
+ version: 5,
71
+ },
72
+ },
73
+ };
74
+ }
75
+ if (toolName === TOOLS.FEATURE_STATE_GET && role === 'orchestrator') {
76
+ return {
77
+ ok: true,
78
+ data: {
79
+ front_matter: {
80
+ version: 9,
81
+ },
82
+ },
83
+ };
84
+ }
85
+ if (toolName === TOOLS.FEATURE_GET_CONTEXT) {
86
+ return { ok: true, data: { state: 'ctx' } };
87
+ }
88
+ if (toolName === TOOLS.GATES_RUN) {
89
+ return { ok: true, data: { overall: 'pass', evidence_path: 'qa-evidence.json' } };
90
+ }
91
+ return { ok: true, data: {} };
92
+ },
93
+ );
94
+ return { callTool };
95
+ }
96
+
97
+ describe('BuildWaveExecutor live policy branches', () => {
98
+ it('GIVEN_invalid_output_and_block_policy_WHEN_run_THEN_blocks_feature_and_skips_gates', async () => {
99
+ const toolCaller = buildToolCaller();
100
+ const executor = new BuildWaveExecutor({
101
+ toolCaller: toolCaller as never,
102
+ providerMode: 'live',
103
+ malformedOutputAction: 'block_feature',
104
+ workerDecisionRunner: {
105
+ execute: vi.fn(async () => decision({ invalidOutput: true })),
106
+ },
107
+ });
108
+
109
+ await executor.run(['feature-a'], 1);
110
+
111
+ const gateRuns = toolCaller.callTool.mock.calls.filter((call) => call[1] === TOOLS.GATES_RUN);
112
+ expect(gateRuns).toHaveLength(0);
113
+ const statePatches = toolCaller.callTool.mock.calls.filter(
114
+ (call) => call[1] === TOOLS.FEATURE_STATE_PATCH,
115
+ );
116
+ expect(statePatches).toHaveLength(1);
117
+ expect(statePatches[0][2]).toMatchObject({
118
+ feature_id: 'feature-a',
119
+ patch: {
120
+ front_matter: {
121
+ status: STATUS.BLOCKED,
122
+ },
123
+ },
124
+ });
125
+ });
126
+
127
+ it('GIVEN_invalid_output_and_fail_run_policy_WHEN_run_THEN_throws_provider_output_invalid', async () => {
128
+ const toolCaller = buildToolCaller();
129
+ const executor = new BuildWaveExecutor({
130
+ toolCaller: toolCaller as never,
131
+ providerMode: 'live',
132
+ malformedOutputAction: 'fail_run',
133
+ workerDecisionRunner: {
134
+ execute: vi.fn(async () => decision({ invalidOutput: true })),
135
+ },
136
+ });
137
+
138
+ await expect(executor.run(['feature-a'], 1)).rejects.toMatchObject({
139
+ code: ERROR_CODES.PROVIDER_OUTPUT_INVALID,
140
+ });
141
+ });
142
+
143
+ it('GIVEN_no_progress_below_limit_WHEN_run_THEN_skips_current_cycle_without_blocking', async () => {
144
+ const toolCaller = buildToolCaller();
145
+ const executor = new BuildWaveExecutor({
146
+ toolCaller: toolCaller as never,
147
+ providerMode: 'live',
148
+ noProgressLimit: 2,
149
+ workerDecisionRunner: {
150
+ execute: vi.fn(async () => decision({ noProgress: true })),
151
+ },
152
+ });
153
+
154
+ await executor.run(['feature-a'], 1);
155
+
156
+ const gateRuns = toolCaller.callTool.mock.calls.filter((call) => call[1] === TOOLS.GATES_RUN);
157
+ expect(gateRuns).toHaveLength(0);
158
+ const statePatches = toolCaller.callTool.mock.calls.filter(
159
+ (call) => call[1] === TOOLS.FEATURE_STATE_PATCH,
160
+ );
161
+ expect(statePatches).toHaveLength(0);
162
+ });
163
+
164
+ it('GIVEN_no_progress_at_limit_and_fail_run_policy_WHEN_run_THEN_throws_provider_no_progress', async () => {
165
+ const toolCaller = buildToolCaller();
166
+ const executor = new BuildWaveExecutor({
167
+ toolCaller: toolCaller as never,
168
+ providerMode: 'live',
169
+ noProgressLimit: 1,
170
+ noProgressAction: 'fail_run',
171
+ workerDecisionRunner: {
172
+ execute: vi.fn(async () => decision({ noProgress: true })),
173
+ },
174
+ });
175
+
176
+ await expect(executor.run(['feature-a'], 1)).rejects.toMatchObject({
177
+ code: ERROR_CODES.PROVIDER_NO_PROGRESS,
178
+ });
179
+ });
180
+ });
181
+
182
+ describe('QaWaveExecutor live policy branches', () => {
183
+ it('GIVEN_invalid_output_and_block_policy_WHEN_run_THEN_blocks_feature_and_skips_gate_and_session_rotation', async () => {
184
+ const toolCaller = qaToolCaller();
185
+ const kernel = {
186
+ updateFeatureSessionAssignment: vi.fn(async () => ({ ok: true })),
187
+ };
188
+ const provider = {
189
+ closeSession: vi.fn(async () => ({ closed: true })),
190
+ createSession: vi.fn(async () => ({ session_id: 'new-qa-session' })),
191
+ };
192
+ const promptProvider = {
193
+ loadRolePrompts: vi.fn(async () => ({ planner: null, builder: null, qa: null })),
194
+ };
195
+ const featureClusterPatcher = {
196
+ patchFeatureCluster: vi.fn(async () => ({ ok: true })),
197
+ };
198
+ const state = {
199
+ runId: 'run:test',
200
+ ownerInstanceId: 'owner:test',
201
+ orchestratorSessionId: 'orch-session',
202
+ sessionsByFeature: new Map([['feature-a', { planner: 'p-1', builder: 'b-1', qa: 'q-1' }]]),
203
+ };
204
+ const executor = new QaWaveExecutor({
205
+ kernel: kernel as never,
206
+ provider: provider as never,
207
+ toolCaller: toolCaller as never,
208
+ promptProvider: promptProvider as never,
209
+ featureClusterPatcher: featureClusterPatcher as never,
210
+ state: state as never,
211
+ providerMode: 'live',
212
+ malformedOutputAction: 'block_feature',
213
+ workerDecisionRunner: {
214
+ execute: vi.fn(async () => decision({ invalidOutput: true })),
215
+ },
216
+ });
217
+
218
+ await executor.run(['feature-a'], 1);
219
+
220
+ const gateRuns = toolCaller.callTool.mock.calls.filter((call) => call[1] === TOOLS.GATES_RUN);
221
+ expect(gateRuns).toHaveLength(0);
222
+ expect(provider.closeSession).not.toHaveBeenCalled();
223
+ expect(kernel.updateFeatureSessionAssignment).not.toHaveBeenCalled();
224
+ });
225
+
226
+ it('GIVEN_no_progress_below_limit_WHEN_run_THEN_skips_current_cycle', async () => {
227
+ const toolCaller = qaToolCaller();
228
+ const executor = new QaWaveExecutor({
229
+ kernel: { updateFeatureSessionAssignment: vi.fn() } as never,
230
+ provider: {
231
+ closeSession: vi.fn(),
232
+ createSession: vi.fn(),
233
+ } as never,
234
+ toolCaller: toolCaller as never,
235
+ promptProvider: { loadRolePrompts: vi.fn() } as never,
236
+ featureClusterPatcher: { patchFeatureCluster: vi.fn() } as never,
237
+ state: {
238
+ runId: 'run:test',
239
+ ownerInstanceId: 'owner:test',
240
+ orchestratorSessionId: 'orch-session',
241
+ sessionsByFeature: new Map(),
242
+ } as never,
243
+ providerMode: 'live',
244
+ noProgressLimit: 3,
245
+ workerDecisionRunner: {
246
+ execute: vi.fn(async () => decision({ noProgress: true })),
247
+ },
248
+ });
249
+
250
+ await executor.run(['feature-a'], 1);
251
+ const gateRuns = toolCaller.callTool.mock.calls.filter((call) => call[1] === TOOLS.GATES_RUN);
252
+ expect(gateRuns).toHaveLength(0);
253
+ });
254
+
255
+ it('GIVEN_no_progress_at_limit_and_fail_run_policy_WHEN_run_THEN_throws_provider_no_progress', async () => {
256
+ const toolCaller = qaToolCaller();
257
+ const executor = new QaWaveExecutor({
258
+ kernel: { updateFeatureSessionAssignment: vi.fn() } as never,
259
+ provider: {
260
+ closeSession: vi.fn(),
261
+ createSession: vi.fn(),
262
+ } as never,
263
+ toolCaller: toolCaller as never,
264
+ promptProvider: { loadRolePrompts: vi.fn() } as never,
265
+ featureClusterPatcher: { patchFeatureCluster: vi.fn() } as never,
266
+ state: {
267
+ runId: 'run:test',
268
+ ownerInstanceId: 'owner:test',
269
+ orchestratorSessionId: 'orch-session',
270
+ sessionsByFeature: new Map(),
271
+ } as never,
272
+ providerMode: 'live',
273
+ noProgressLimit: 1,
274
+ noProgressAction: 'fail_run',
275
+ workerDecisionRunner: {
276
+ execute: vi.fn(async () => decision({ noProgress: true })),
277
+ },
278
+ });
279
+
280
+ await expect(executor.run(['feature-a'], 1)).rejects.toMatchObject({
281
+ code: ERROR_CODES.PROVIDER_NO_PROGRESS,
282
+ });
283
+ });
284
+ });