agentic-orchestrator 0.1.25 → 0.1.26
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.
- package/agentic/orchestrator/defaults/policy.defaults.yaml +1 -0
- package/agentic/orchestrator/policy.yaml +1 -0
- package/agentic/orchestrator/schemas/policy.schema.json +4 -0
- package/agentic/orchestrator/schemas/policy.user.schema.json +4 -0
- package/apps/control-plane/src/application/services/activity-monitor-service.ts +42 -0
- package/apps/control-plane/src/application/services/collision-queue-service.ts +48 -0
- package/apps/control-plane/src/application/services/cost-tracking-service.ts +80 -0
- package/apps/control-plane/src/application/services/feature-deletion-service.ts +57 -0
- package/apps/control-plane/src/application/services/feature-lifecycle-service.ts +65 -0
- package/apps/control-plane/src/application/services/feature-state-service.ts +87 -0
- package/apps/control-plane/src/application/services/gate-selection-service.ts +28 -0
- package/apps/control-plane/src/application/services/gate-service.ts +148 -0
- package/apps/control-plane/src/application/services/issue-tracker-service.ts +28 -0
- package/apps/control-plane/src/application/services/lock-service.ts +88 -0
- package/apps/control-plane/src/application/services/merge-service.ts +63 -0
- package/apps/control-plane/src/application/services/notifier-service.ts +70 -0
- package/apps/control-plane/src/application/services/patch-service.ts +95 -0
- package/apps/control-plane/src/application/services/performance-analytics-service.ts +90 -0
- package/apps/control-plane/src/application/services/plan-service.ts +184 -0
- package/apps/control-plane/src/application/services/pr-monitor-service.ts +58 -0
- package/apps/control-plane/src/application/services/qa-index-service.ts +68 -0
- package/apps/control-plane/src/application/services/reactions-service.ts +77 -0
- package/apps/control-plane/src/application/services/reporting-service.ts +79 -0
- package/apps/control-plane/src/application/services/run-lease-service.ts +79 -0
- package/apps/control-plane/src/application/tools/tool-router.ts +76 -0
- package/apps/control-plane/src/mcp/tool-runtime.ts +4 -0
- package/apps/control-plane/src/providers/providers.ts +96 -0
- package/apps/control-plane/src/providers/worker-provider-factory.ts +4 -0
- package/apps/control-plane/src/supervisor/build-wave-executor.ts +4 -0
- package/apps/control-plane/src/supervisor/planning-wave-executor.ts +4 -0
- package/apps/control-plane/src/supervisor/qa-wave-executor.ts +4 -0
- package/apps/control-plane/src/supervisor/run-coordinator.ts +4 -0
- package/apps/control-plane/src/supervisor/runtime.ts +4 -0
- package/apps/control-plane/src/supervisor/session-orchestrator.ts +4 -0
- package/apps/control-plane/test/dashboard-api.integration.spec.ts +310 -1
- package/apps/control-plane/test/dashboard-client.spec.ts +136 -1
- package/apps/control-plane/test/dashboard-live-feed.spec.ts +153 -0
- package/apps/control-plane/test/dashboard-ui-utils.spec.ts +132 -0
- package/apps/control-plane/test/mcp.spec.ts +96 -0
- package/apps/control-plane/test/tool-runtime.spec.ts +68 -0
- package/config/agentic/orchestrator/policy.yaml +1 -0
- package/dist/apps/control-plane/application/services/activity-monitor-service.d.ts +42 -0
- package/dist/apps/control-plane/application/services/activity-monitor-service.js +42 -0
- package/dist/apps/control-plane/application/services/activity-monitor-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/collision-queue-service.d.ts +48 -0
- package/dist/apps/control-plane/application/services/collision-queue-service.js +48 -0
- package/dist/apps/control-plane/application/services/collision-queue-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/cost-tracking-service.d.ts +79 -0
- package/dist/apps/control-plane/application/services/cost-tracking-service.js +76 -0
- package/dist/apps/control-plane/application/services/cost-tracking-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/feature-deletion-service.d.ts +57 -0
- package/dist/apps/control-plane/application/services/feature-deletion-service.js +57 -0
- package/dist/apps/control-plane/application/services/feature-deletion-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/feature-lifecycle-service.d.ts +64 -0
- package/dist/apps/control-plane/application/services/feature-lifecycle-service.js +61 -0
- package/dist/apps/control-plane/application/services/feature-lifecycle-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/feature-state-service.d.ts +86 -0
- package/dist/apps/control-plane/application/services/feature-state-service.js +83 -0
- package/dist/apps/control-plane/application/services/feature-state-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/gate-selection-service.d.ts +28 -0
- package/dist/apps/control-plane/application/services/gate-selection-service.js +28 -0
- package/dist/apps/control-plane/application/services/gate-selection-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/gate-service.d.ts +148 -0
- package/dist/apps/control-plane/application/services/gate-service.js +120 -0
- package/dist/apps/control-plane/application/services/gate-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/issue-tracker-service.d.ts +28 -0
- package/dist/apps/control-plane/application/services/issue-tracker-service.js +28 -0
- package/dist/apps/control-plane/application/services/issue-tracker-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/lock-service.d.ts +88 -0
- package/dist/apps/control-plane/application/services/lock-service.js +64 -0
- package/dist/apps/control-plane/application/services/lock-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/merge-service.d.ts +62 -0
- package/dist/apps/control-plane/application/services/merge-service.js +59 -0
- package/dist/apps/control-plane/application/services/merge-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/notifier-service.d.ts +70 -0
- package/dist/apps/control-plane/application/services/notifier-service.js +70 -0
- package/dist/apps/control-plane/application/services/notifier-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/patch-service.d.ts +94 -0
- package/dist/apps/control-plane/application/services/patch-service.js +91 -0
- package/dist/apps/control-plane/application/services/patch-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/performance-analytics-service.d.ts +89 -0
- package/dist/apps/control-plane/application/services/performance-analytics-service.js +86 -0
- package/dist/apps/control-plane/application/services/performance-analytics-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/plan-service.d.ts +287 -0
- package/dist/apps/control-plane/application/services/plan-service.js +207 -0
- package/dist/apps/control-plane/application/services/plan-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/pr-monitor-service.d.ts +58 -0
- package/dist/apps/control-plane/application/services/pr-monitor-service.js +58 -0
- package/dist/apps/control-plane/application/services/pr-monitor-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/qa-index-service.d.ts +67 -0
- package/dist/apps/control-plane/application/services/qa-index-service.js +64 -0
- package/dist/apps/control-plane/application/services/qa-index-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/reactions-service.d.ts +76 -0
- package/dist/apps/control-plane/application/services/reactions-service.js +73 -0
- package/dist/apps/control-plane/application/services/reactions-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/reporting-service.d.ts +78 -0
- package/dist/apps/control-plane/application/services/reporting-service.js +75 -0
- package/dist/apps/control-plane/application/services/reporting-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/run-lease-service.d.ts +79 -0
- package/dist/apps/control-plane/application/services/run-lease-service.js +79 -0
- package/dist/apps/control-plane/application/services/run-lease-service.js.map +1 -1
- package/dist/apps/control-plane/application/tools/tool-router.d.ts +76 -0
- package/dist/apps/control-plane/application/tools/tool-router.js +62 -0
- package/dist/apps/control-plane/application/tools/tool-router.js.map +1 -1
- package/dist/apps/control-plane/mcp/tool-runtime.d.ts +3 -0
- package/dist/apps/control-plane/mcp/tool-runtime.js +3 -0
- package/dist/apps/control-plane/mcp/tool-runtime.js.map +1 -1
- package/dist/apps/control-plane/providers/providers.d.ts +88 -0
- package/dist/apps/control-plane/providers/providers.js.map +1 -1
- package/dist/apps/control-plane/providers/worker-provider-factory.d.ts +3 -0
- package/dist/apps/control-plane/providers/worker-provider-factory.js.map +1 -1
- package/dist/apps/control-plane/supervisor/build-wave-executor.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/build-wave-executor.js +3 -0
- package/dist/apps/control-plane/supervisor/build-wave-executor.js.map +1 -1
- package/dist/apps/control-plane/supervisor/planning-wave-executor.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/planning-wave-executor.js +3 -0
- package/dist/apps/control-plane/supervisor/planning-wave-executor.js.map +1 -1
- package/dist/apps/control-plane/supervisor/qa-wave-executor.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/qa-wave-executor.js +3 -0
- package/dist/apps/control-plane/supervisor/qa-wave-executor.js.map +1 -1
- package/dist/apps/control-plane/supervisor/run-coordinator.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/run-coordinator.js +3 -0
- package/dist/apps/control-plane/supervisor/run-coordinator.js.map +1 -1
- package/dist/apps/control-plane/supervisor/runtime.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/runtime.js +3 -0
- package/dist/apps/control-plane/supervisor/runtime.js.map +1 -1
- package/dist/apps/control-plane/supervisor/session-orchestrator.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/session-orchestrator.js +3 -0
- package/dist/apps/control-plane/supervisor/session-orchestrator.js.map +1 -1
- package/package.json +1 -1
- package/packages/web-dashboard/src/app/analytics/page.tsx +451 -0
- package/packages/web-dashboard/src/app/api/analytics/route.ts +62 -0
- package/packages/web-dashboard/src/app/api/collisions/route.ts +63 -0
- package/packages/web-dashboard/src/app/api/features/[id]/cost/route.ts +29 -0
- package/packages/web-dashboard/src/app/api/features/[id]/review-brief/route.ts +31 -0
- package/packages/web-dashboard/src/app/api/features/[id]/route.ts +5 -8
- package/packages/web-dashboard/src/app/api/features/[id]/test-index/route.ts +31 -0
- package/packages/web-dashboard/src/app/api/flaky/route.ts +20 -0
- package/packages/web-dashboard/src/app/api/policy/budget/route.ts +31 -0
- package/packages/web-dashboard/src/app/api/run/route.ts +162 -0
- package/packages/web-dashboard/src/app/api/status/route.ts +2 -5
- package/packages/web-dashboard/src/app/feature/[id]/page.tsx +112 -0
- package/packages/web-dashboard/src/app/page.tsx +379 -77
- package/packages/web-dashboard/src/components/agent-pipeline-stepper.tsx +100 -0
- package/packages/web-dashboard/src/components/dependency-chains.tsx +58 -0
- package/packages/web-dashboard/src/components/detail-panel.tsx +215 -5
- package/packages/web-dashboard/src/components/feature-card.tsx +5 -0
- package/packages/web-dashboard/src/components/feature-cost-panel.tsx +66 -0
- package/packages/web-dashboard/src/components/feature-list-view.tsx +336 -0
- package/packages/web-dashboard/src/components/gate-step-drilldown.tsx +92 -0
- package/packages/web-dashboard/src/components/kanban-board.tsx +3 -0
- package/packages/web-dashboard/src/components/live-event-feed.tsx +60 -0
- package/packages/web-dashboard/src/components/lock-resource-map.tsx +73 -0
- package/packages/web-dashboard/src/components/plan-revision-timeline.tsx +55 -0
- package/packages/web-dashboard/src/components/plan-risk-annotations.tsx +63 -0
- package/packages/web-dashboard/src/components/plan-scope-tree.tsx +285 -0
- package/packages/web-dashboard/src/components/qa-coverage-map.tsx +174 -0
- package/packages/web-dashboard/src/components/quick-launch-panel.tsx +70 -0
- package/packages/web-dashboard/src/components/review-brief-panel.tsx +75 -0
- package/packages/web-dashboard/src/components/run-health-panel.tsx +85 -0
- package/packages/web-dashboard/src/components/summary-bar.tsx +59 -2
- package/packages/web-dashboard/src/components/verification-signals.tsx +167 -0
- package/packages/web-dashboard/src/lib/analytics-utils.ts +3 -0
- package/packages/web-dashboard/src/lib/aop-client.ts +510 -11
- package/packages/web-dashboard/src/lib/api-envelope.ts +48 -0
- package/packages/web-dashboard/src/lib/authz-adapter.ts +33 -0
- package/packages/web-dashboard/src/lib/dashboard-utils.ts +304 -0
- package/packages/web-dashboard/src/lib/live-feed.ts +218 -0
- package/packages/web-dashboard/src/lib/policy-reader.ts +22 -0
- package/packages/web-dashboard/src/lib/types.ts +114 -0
- package/packages/web-dashboard/src/styles/dashboard.module.css +341 -1
- package/spec-files/{outstanding → completed}/agentic_orchestrator_dashboard_advanced_ux_spec.md +1 -1
- package/spec-files/outstanding/agentic_orchestrator_evidence_integrity_doctor_spec.md +561 -0
- package/spec-files/outstanding/agentic_orchestrator_kernel_simplification_spec.md +834 -0
- package/spec-files/outstanding/agentic_orchestrator_observability_integrity_diagnostics_spec.md +12 -5
- package/spec-files/outstanding/agentic_orchestrator_observability_replay_spec.md +442 -0
- package/spec-files/progress.md +174 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { okEnvelope } from '@/lib/api-envelope.js';
|
|
2
|
+
import { readDashboardStatus, resolveProjectRoot } from '@/lib/aop-client.js';
|
|
3
|
+
|
|
4
|
+
export const dynamic = 'force-dynamic';
|
|
5
|
+
|
|
6
|
+
export async function GET(req: Request): Promise<Response> {
|
|
7
|
+
const project = new URL(req.url).searchParams.get('project');
|
|
8
|
+
const repoRoot = await resolveProjectRoot(project);
|
|
9
|
+
const payload = await readDashboardStatus(repoRoot);
|
|
10
|
+
const suspects = Array.isArray(payload.flaky_suspects) ? payload.flaky_suspects : [];
|
|
11
|
+
return Response.json(
|
|
12
|
+
okEnvelope(
|
|
13
|
+
{
|
|
14
|
+
suspects,
|
|
15
|
+
available: suspects.length > 0,
|
|
16
|
+
},
|
|
17
|
+
{ source: 'derived' },
|
|
18
|
+
),
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { errorEnvelope, okEnvelope } from '@/lib/api-envelope.js';
|
|
2
|
+
import { resolveProjectRoot } from '@/lib/aop-client.js';
|
|
3
|
+
import { readPolicyYaml } from '@/lib/policy-reader.js';
|
|
4
|
+
|
|
5
|
+
export const dynamic = 'force-dynamic';
|
|
6
|
+
|
|
7
|
+
export async function GET(req: Request): Promise<Response> {
|
|
8
|
+
const project = new URL(req.url).searchParams.get('project');
|
|
9
|
+
const repoRoot = await resolveProjectRoot(project);
|
|
10
|
+
const policy = await readPolicyYaml(repoRoot);
|
|
11
|
+
const budget =
|
|
12
|
+
policy && typeof policy.budget === 'object' ? (policy.budget as Record<string, unknown>) : null;
|
|
13
|
+
|
|
14
|
+
if (!budget) {
|
|
15
|
+
return Response.json(
|
|
16
|
+
errorEnvelope('policy_not_configured', 'Budget policy is not configured for this project'),
|
|
17
|
+
{ status: 404 },
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return Response.json(
|
|
22
|
+
okEnvelope(
|
|
23
|
+
{
|
|
24
|
+
per_feature_limit_usd:
|
|
25
|
+
typeof budget.per_feature_limit_usd === 'number' ? budget.per_feature_limit_usd : null,
|
|
26
|
+
alert_threshold: typeof budget.alert_threshold === 'number' ? budget.alert_threshold : 0.8,
|
|
27
|
+
},
|
|
28
|
+
{ source: 'artifact' },
|
|
29
|
+
),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { authorizeDashboardAction } from '@/lib/authz-adapter.js';
|
|
2
|
+
import { errorEnvelope, okEnvelope } from '@/lib/api-envelope.js';
|
|
3
|
+
import { resolveProjectRoot } from '@/lib/aop-client.js';
|
|
4
|
+
import { callOrchestratorTool } from '@/lib/orchestrator-tools.js';
|
|
5
|
+
import { readPolicyYaml } from '@/lib/policy-reader.js';
|
|
6
|
+
|
|
7
|
+
export const dynamic = 'force-dynamic';
|
|
8
|
+
|
|
9
|
+
interface RunRequestBody {
|
|
10
|
+
feature_id?: string;
|
|
11
|
+
goal?: string;
|
|
12
|
+
confirm_launch?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface QuickLaunchAvailability {
|
|
16
|
+
available: boolean;
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
authorized: boolean;
|
|
19
|
+
reason: string | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const FEATURE_ID_PATTERN = /^[a-z0-9](?:[a-z0-9_-]{1,126}[a-z0-9])?$/;
|
|
23
|
+
|
|
24
|
+
function quickLaunchEnabled(policy: Record<string, unknown> | null): boolean {
|
|
25
|
+
if (process.env.AOP_DASHBOARD_QUICK_LAUNCH === 'true') {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
const dashboard =
|
|
29
|
+
policy && typeof policy.dashboard === 'object'
|
|
30
|
+
? (policy.dashboard as Record<string, unknown>)
|
|
31
|
+
: null;
|
|
32
|
+
return dashboard?.quick_launch === true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function getAvailability(
|
|
36
|
+
req: Request,
|
|
37
|
+
project: string | null,
|
|
38
|
+
policy: Record<string, unknown> | null,
|
|
39
|
+
): Promise<QuickLaunchAvailability> {
|
|
40
|
+
const enabled = quickLaunchEnabled(policy);
|
|
41
|
+
if (!enabled) {
|
|
42
|
+
return {
|
|
43
|
+
available: false,
|
|
44
|
+
enabled: false,
|
|
45
|
+
authorized: false,
|
|
46
|
+
reason:
|
|
47
|
+
'Quick launch is disabled by policy. Enable dashboard.quick_launch or AOP_DASHBOARD_QUICK_LAUNCH=true.',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const auth = await authorizeDashboardAction({
|
|
52
|
+
action: 'dashboard.quick_launch',
|
|
53
|
+
project,
|
|
54
|
+
request: req,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (!auth.allowed) {
|
|
58
|
+
return {
|
|
59
|
+
available: false,
|
|
60
|
+
enabled: true,
|
|
61
|
+
authorized: false,
|
|
62
|
+
reason: auth.message ?? 'Action not authorized',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
available: true,
|
|
68
|
+
enabled: true,
|
|
69
|
+
authorized: true,
|
|
70
|
+
reason: null,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function GET(req: Request): Promise<Response> {
|
|
75
|
+
const url = new URL(req.url);
|
|
76
|
+
const project = url.searchParams.get('project');
|
|
77
|
+
const repoRoot = await resolveProjectRoot(project);
|
|
78
|
+
const policy = await readPolicyYaml(repoRoot);
|
|
79
|
+
const availability = await getAvailability(req, project, policy);
|
|
80
|
+
return Response.json(okEnvelope(availability, { source: 'artifact' }));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function POST(req: Request): Promise<Response> {
|
|
84
|
+
const url = new URL(req.url);
|
|
85
|
+
const project = url.searchParams.get('project');
|
|
86
|
+
const repoRoot = await resolveProjectRoot(project);
|
|
87
|
+
const policy = await readPolicyYaml(repoRoot);
|
|
88
|
+
const availability = await getAvailability(req, project, policy);
|
|
89
|
+
if (!availability.available) {
|
|
90
|
+
return Response.json(
|
|
91
|
+
errorEnvelope('unauthorized_action', availability.reason ?? 'Action not authorized'),
|
|
92
|
+
{ status: 403 },
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const body = (await req.json()) as RunRequestBody;
|
|
97
|
+
const featureId = typeof body.feature_id === 'string' ? body.feature_id.trim() : '';
|
|
98
|
+
const goal = typeof body.goal === 'string' ? body.goal.trim() : '';
|
|
99
|
+
const confirmed = body.confirm_launch === true;
|
|
100
|
+
|
|
101
|
+
if (!confirmed) {
|
|
102
|
+
return Response.json(
|
|
103
|
+
errorEnvelope(
|
|
104
|
+
'invalid_input',
|
|
105
|
+
'confirm_launch=true is required to prevent accidental feature launches.',
|
|
106
|
+
),
|
|
107
|
+
{ status: 400 },
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!FEATURE_ID_PATTERN.test(featureId)) {
|
|
112
|
+
return Response.json(
|
|
113
|
+
errorEnvelope(
|
|
114
|
+
'invalid_input',
|
|
115
|
+
'feature_id must match /^[a-z0-9](?:[a-z0-9_-]{1,126}[a-z0-9])?$/',
|
|
116
|
+
),
|
|
117
|
+
{ status: 400 },
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (goal.length === 0) {
|
|
122
|
+
return Response.json(errorEnvelope('invalid_input', 'goal is required'), {
|
|
123
|
+
status: 400,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const result = await callOrchestratorTool(
|
|
128
|
+
'feature.init',
|
|
129
|
+
{
|
|
130
|
+
feature_id: featureId,
|
|
131
|
+
goal,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
mutating: true,
|
|
135
|
+
featureId,
|
|
136
|
+
repoRoot,
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
if (!result.ok) {
|
|
141
|
+
return Response.json(
|
|
142
|
+
errorEnvelope(
|
|
143
|
+
result.error?.code ?? 'tool_unavailable',
|
|
144
|
+
result.error?.message ?? 'Unable to launch feature',
|
|
145
|
+
{ retryable: result.error?.code === 'tool_timeout' },
|
|
146
|
+
),
|
|
147
|
+
{ status: 409 },
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return Response.json(
|
|
152
|
+
okEnvelope(
|
|
153
|
+
{
|
|
154
|
+
feature_id: featureId,
|
|
155
|
+
goal,
|
|
156
|
+
status: 'initialized',
|
|
157
|
+
},
|
|
158
|
+
{ source: 'tool' },
|
|
159
|
+
),
|
|
160
|
+
{ status: 201 },
|
|
161
|
+
);
|
|
162
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readDashboardStatus, resolveProjectRoot } from '@/lib/aop-client.js';
|
|
2
|
+
import { okEnvelope } from '@/lib/api-envelope.js';
|
|
2
3
|
|
|
3
4
|
export const dynamic = 'force-dynamic';
|
|
4
5
|
|
|
@@ -7,9 +8,5 @@ export async function GET(req: Request): Promise<Response> {
|
|
|
7
8
|
const project = url.searchParams.get('project');
|
|
8
9
|
const repoRoot = await resolveProjectRoot(project);
|
|
9
10
|
const payload = await readDashboardStatus(repoRoot);
|
|
10
|
-
return Response.json({
|
|
11
|
-
ok: true,
|
|
12
|
-
data: payload,
|
|
13
|
-
timestamp: new Date().toISOString(),
|
|
14
|
-
});
|
|
11
|
+
return Response.json(okEnvelope(payload, { source: 'derived' }));
|
|
15
12
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
4
|
+
import Link from 'next/link';
|
|
5
|
+
import { DetailPanel } from '@/components/detail-panel';
|
|
6
|
+
import { ToastStack, useToast } from '@/components/toast';
|
|
7
|
+
import type { DashboardStatusPayload, FeatureDetail } from '@/lib/types.js';
|
|
8
|
+
import styles from '@/styles/dashboard.module.css';
|
|
9
|
+
|
|
10
|
+
function emptyPayload(): DashboardStatusPayload {
|
|
11
|
+
return {
|
|
12
|
+
index: { active: [], blocked: [], merged: [], blocked_queue: [] },
|
|
13
|
+
features: [],
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function jsonFetch<T>(input: string, init?: RequestInit): Promise<T> {
|
|
18
|
+
const response = await fetch(input, init);
|
|
19
|
+
return (await response.json()) as T;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default function FeatureFocusPage({ params }: { params: Promise<{ id: string }> }) {
|
|
23
|
+
const [featureId, setFeatureId] = useState<string>('');
|
|
24
|
+
const [project, setProject] = useState<string>('');
|
|
25
|
+
const [detail, setDetail] = useState<FeatureDetail | null>(null);
|
|
26
|
+
const [payload, setPayload] = useState<DashboardStatusPayload>(emptyPayload());
|
|
27
|
+
const { notifications, addToast, dismissToast } = useToast();
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
void (async () => {
|
|
31
|
+
const resolved = await params;
|
|
32
|
+
setFeatureId(resolved.id);
|
|
33
|
+
})();
|
|
34
|
+
}, [params]);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const current = new URL(window.location.href).searchParams.get('project');
|
|
38
|
+
setProject(current ?? '');
|
|
39
|
+
}, []);
|
|
40
|
+
|
|
41
|
+
const query = useMemo(() => {
|
|
42
|
+
return project.length > 0 ? `?project=${encodeURIComponent(project)}` : '';
|
|
43
|
+
}, [project]);
|
|
44
|
+
|
|
45
|
+
const refreshStatus = useCallback(async () => {
|
|
46
|
+
const response = await jsonFetch<{ ok: boolean; data?: DashboardStatusPayload }>(
|
|
47
|
+
`/api/status${query}`,
|
|
48
|
+
);
|
|
49
|
+
if (response.ok && response.data) {
|
|
50
|
+
setPayload(response.data);
|
|
51
|
+
}
|
|
52
|
+
}, [query]);
|
|
53
|
+
|
|
54
|
+
const refreshDetail = useCallback(
|
|
55
|
+
async (targetFeatureId: string) => {
|
|
56
|
+
const response = await jsonFetch<{ ok: boolean; data?: FeatureDetail }>(
|
|
57
|
+
`/api/features/${targetFeatureId}${query}`,
|
|
58
|
+
);
|
|
59
|
+
if (response.ok && response.data) {
|
|
60
|
+
setDetail(response.data);
|
|
61
|
+
} else {
|
|
62
|
+
setDetail(null);
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
[query],
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (featureId.length === 0) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
void refreshDetail(featureId);
|
|
73
|
+
void refreshStatus();
|
|
74
|
+
}, [featureId, refreshDetail, refreshStatus]);
|
|
75
|
+
|
|
76
|
+
const statusLabel =
|
|
77
|
+
payload.features.find((feature) => feature.feature_id === featureId)?.status ??
|
|
78
|
+
detail?.feature.status ??
|
|
79
|
+
'unknown';
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div className={styles.dashboardRoot}>
|
|
83
|
+
<header className={styles.focusHeader}>
|
|
84
|
+
<div className={styles.titleBlock}>
|
|
85
|
+
<h1 className={styles.pageTitle}>{featureId}</h1>
|
|
86
|
+
<p className={styles.pageSubtitle}>Focus review view • Status: {statusLabel}</p>
|
|
87
|
+
</div>
|
|
88
|
+
<div className={styles.inlineActions}>
|
|
89
|
+
<Link href={`/${query.length > 0 ? query : ''}`} className={styles.linkButton}>
|
|
90
|
+
Back To Board
|
|
91
|
+
</Link>
|
|
92
|
+
</div>
|
|
93
|
+
</header>
|
|
94
|
+
|
|
95
|
+
<div className={styles.focusLayout}>
|
|
96
|
+
<DetailPanel
|
|
97
|
+
detail={detail}
|
|
98
|
+
project={project}
|
|
99
|
+
isFocusView
|
|
100
|
+
onClose={() => {
|
|
101
|
+
window.location.href = `/${query.length > 0 ? query : ''}`;
|
|
102
|
+
}}
|
|
103
|
+
refreshDetail={refreshDetail}
|
|
104
|
+
refreshStatus={refreshStatus}
|
|
105
|
+
addToast={addToast}
|
|
106
|
+
/>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<ToastStack notifications={notifications} onDismiss={dismissToast} />
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
}
|