plugin-agent-orchestrator 1.0.20 → 1.0.22

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 (158) hide show
  1. package/dist/client/index.js +1 -1
  2. package/dist/externalVersion.js +6 -6
  3. package/dist/server/collections/agent-execution-spans.js +24 -0
  4. package/dist/server/collections/agent-loop-runs.js +36 -0
  5. package/dist/server/collections/orchestrator-config.js +14 -0
  6. package/dist/server/migrations/20260601000000-add-token-fields.js +101 -0
  7. package/dist/server/plugin.js +56 -0
  8. package/dist/server/resources/agent-loop.js +33 -25
  9. package/dist/server/resources/tracing.js +5 -8
  10. package/dist/server/services/AgentHarness.js +56 -90
  11. package/dist/server/services/AgentLoopController.js +164 -125
  12. package/dist/server/services/AgentLoopRepository.js +16 -34
  13. package/dist/server/services/AgentLoopService.js +7 -1
  14. package/dist/server/services/AgentPlannerService.js +5 -25
  15. package/dist/server/services/AgentRegistryService.js +34 -24
  16. package/dist/server/services/CircuitBreaker.js +120 -0
  17. package/dist/server/services/ContextAggregator.js +201 -0
  18. package/dist/server/services/ExecutionSpanService.js +2 -5
  19. package/dist/server/services/RunEventBus.js +73 -0
  20. package/dist/server/services/TokenTracker.js +173 -0
  21. package/dist/server/tools/agent-loop.js +30 -63
  22. package/dist/server/tools/delegate-task.js +14 -72
  23. package/dist/server/tools/orchestrator-plan.js +10 -47
  24. package/dist/server/types.js +24 -0
  25. package/dist/server/utils/ctx-utils.js +152 -0
  26. package/dist/server/utils/logging.js +86 -0
  27. package/package.json +44 -44
  28. package/src/client/AgentRunsTab.tsx +764 -764
  29. package/src/client/HarnessProfilesTab.tsx +247 -247
  30. package/src/client/OrchestratorSettings.tsx +106 -106
  31. package/src/client/RulesTab.tsx +716 -716
  32. package/src/client/hooks/useRunEventStream.ts +76 -0
  33. package/src/client/index.tsx +2 -1
  34. package/src/client/plugin.tsx +27 -27
  35. package/src/client/skill-hub/components/LoopSettings.tsx +331 -331
  36. package/src/client/skill-hub/index.tsx +51 -51
  37. package/src/client/skill-hub/tools/InteractionSchemasProvider.tsx +99 -99
  38. package/src/client/skill-hub/tools/SkillHubCard.tsx +109 -109
  39. package/src/client/skill-hub/tools/loopTemplates.ts +52 -52
  40. package/src/client/skill-hub/tools/registerSkillLoopCards.ts +58 -58
  41. package/src/client/tools/PlanApprovalCard.tsx +175 -175
  42. package/src/client/tools/registerOrchestratorCards.ts +7 -7
  43. package/src/server/__tests__/agent-loop-controller.test.ts +375 -0
  44. package/src/server/__tests__/circuit-breaker.test.ts +169 -0
  45. package/src/server/__tests__/context-aggregator.test.ts +222 -0
  46. package/src/server/__tests__/parallel-execution.test.ts +318 -0
  47. package/src/server/__tests__/smoke.test.ts +120 -0
  48. package/src/server/collections/agent-execution-spans.ts +24 -0
  49. package/src/server/collections/agent-harness-profiles.ts +59 -59
  50. package/src/server/collections/agent-loop-events.ts +71 -71
  51. package/src/server/collections/agent-loop-runs.ts +38 -1
  52. package/src/server/collections/agent-loop-steps.ts +144 -144
  53. package/src/server/collections/orchestrator-config.ts +14 -0
  54. package/src/server/collections/skill-executions.ts +106 -106
  55. package/src/server/collections/skill-loop-configs.ts +65 -65
  56. package/src/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.ts +30 -30
  57. package/src/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.ts +142 -142
  58. package/src/server/migrations/20260601000000-add-token-fields.ts +89 -0
  59. package/src/server/plugin.ts +68 -0
  60. package/src/server/resources/agent-loop.ts +21 -12
  61. package/src/server/resources/tracing.ts +3 -7
  62. package/src/server/services/AgentHarness.ts +78 -116
  63. package/src/server/services/AgentLoopController.ts +197 -122
  64. package/src/server/services/AgentLoopRepository.ts +9 -25
  65. package/src/server/services/AgentLoopService.ts +13 -1
  66. package/src/server/services/AgentPlanValidator.ts +73 -73
  67. package/src/server/services/AgentPlannerService.ts +2 -25
  68. package/src/server/services/AgentRegistryService.ts +40 -31
  69. package/src/server/services/CircuitBreaker.ts +116 -0
  70. package/src/server/services/ContextAggregator.ts +239 -0
  71. package/src/server/services/ExecutionSpanService.ts +2 -4
  72. package/src/server/services/RunEventBus.ts +45 -0
  73. package/src/server/services/TokenTracker.ts +209 -0
  74. package/src/server/skill-hub/plugin.ts +898 -898
  75. package/src/server/skill-hub/tasks/SkillExecutionTask.ts +460 -460
  76. package/src/server/tools/agent-loop.ts +18 -57
  77. package/src/server/tools/delegate-task.ts +11 -93
  78. package/src/server/tools/orchestrator-plan.ts +26 -50
  79. package/src/server/tools/skill-execute.ts +160 -160
  80. package/src/server/types.ts +55 -0
  81. package/src/server/utils/ctx-utils.ts +118 -0
  82. package/src/server/utils/logging.ts +63 -0
  83. package/dist/client/AIEmployeeSelect.d.ts +0 -11
  84. package/dist/client/AIEmployeesContext.d.ts +0 -30
  85. package/dist/client/AgentRunsTab.d.ts +0 -2
  86. package/dist/client/HarnessProfilesTab.d.ts +0 -2
  87. package/dist/client/OrchestratorSettings.d.ts +0 -3
  88. package/dist/client/RulesTab.d.ts +0 -2
  89. package/dist/client/TracingTab.d.ts +0 -2
  90. package/dist/client/index.d.ts +0 -1
  91. package/dist/client/plugin.d.ts +0 -6
  92. package/dist/client/skill-hub/components/ExecutionHistory.d.ts +0 -2
  93. package/dist/client/skill-hub/components/ExecutionProgress.d.ts +0 -20
  94. package/dist/client/skill-hub/components/GitSkillImport.d.ts +0 -7
  95. package/dist/client/skill-hub/components/LoopSettings.d.ts +0 -2
  96. package/dist/client/skill-hub/components/SkillEditor.d.ts +0 -7
  97. package/dist/client/skill-hub/components/SkillManager.d.ts +0 -2
  98. package/dist/client/skill-hub/components/SkillMetrics.d.ts +0 -2
  99. package/dist/client/skill-hub/components/SkillTestPanel.d.ts +0 -7
  100. package/dist/client/skill-hub/index.d.ts +0 -11
  101. package/dist/client/skill-hub/locale.d.ts +0 -3
  102. package/dist/client/skill-hub/tools/InteractionSchemasProvider.d.ts +0 -6
  103. package/dist/client/skill-hub/tools/SkillHubCard.d.ts +0 -3
  104. package/dist/client/skill-hub/tools/loopTemplates.d.ts +0 -22
  105. package/dist/client/skill-hub/tools/registerSkillLoopCards.d.ts +0 -1
  106. package/dist/client/skill-hub/utils/jsonFields.d.ts +0 -3
  107. package/dist/client/tools/PlanApprovalCard.d.ts +0 -3
  108. package/dist/client/tools/registerOrchestratorCards.d.ts +0 -1
  109. package/dist/index.d.ts +0 -2
  110. package/dist/server/collections/agent-execution-spans.d.ts +0 -9
  111. package/dist/server/collections/agent-harness-profiles.d.ts +0 -2
  112. package/dist/server/collections/agent-loop-events.d.ts +0 -2
  113. package/dist/server/collections/agent-loop-runs.d.ts +0 -2
  114. package/dist/server/collections/agent-loop-steps.d.ts +0 -2
  115. package/dist/server/collections/orchestrator-config.d.ts +0 -2
  116. package/dist/server/collections/orchestrator-logs.d.ts +0 -8
  117. package/dist/server/collections/skill-definitions.d.ts +0 -3
  118. package/dist/server/collections/skill-executions.d.ts +0 -3
  119. package/dist/server/collections/skill-loop-configs.d.ts +0 -3
  120. package/dist/server/collections/skill-worker-configs.d.ts +0 -3
  121. package/dist/server/index.d.ts +0 -1
  122. package/dist/server/migrations/20260423000000-add-progress-fields.d.ts +0 -4
  123. package/dist/server/migrations/20260425000000-add-interaction-schema.d.ts +0 -4
  124. package/dist/server/migrations/20260427000000-add-tracing-detail-fields.d.ts +0 -7
  125. package/dist/server/migrations/20260427000000-change-packages-to-text.d.ts +0 -4
  126. package/dist/server/migrations/20260427000001-change-other-json-to-text.d.ts +0 -4
  127. package/dist/server/migrations/20260429000000-add-llm-fields.d.ts +0 -7
  128. package/dist/server/migrations/20260429000000-fix-inputargs-json-to-text.d.ts +0 -16
  129. package/dist/server/migrations/20260503000000-add-orchestrator-trace-fields.d.ts +0 -7
  130. package/dist/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.d.ts +0 -7
  131. package/dist/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.d.ts +0 -12
  132. package/dist/server/plugin.d.ts +0 -16
  133. package/dist/server/resources/agent-loop.d.ts +0 -3
  134. package/dist/server/resources/tracing.d.ts +0 -7
  135. package/dist/server/services/AgentHarness.d.ts +0 -42
  136. package/dist/server/services/AgentLoopController.d.ts +0 -205
  137. package/dist/server/services/AgentLoopRepository.d.ts +0 -20
  138. package/dist/server/services/AgentLoopService.d.ts +0 -149
  139. package/dist/server/services/AgentPlanValidator.d.ts +0 -4
  140. package/dist/server/services/AgentPlannerService.d.ts +0 -8
  141. package/dist/server/services/AgentRegistryService.d.ts +0 -13
  142. package/dist/server/services/CodeValidator.d.ts +0 -32
  143. package/dist/server/services/ExecutionSpanService.d.ts +0 -46
  144. package/dist/server/services/FileManager.d.ts +0 -28
  145. package/dist/server/services/SandboxRunner.d.ts +0 -41
  146. package/dist/server/services/SkillManager.d.ts +0 -6
  147. package/dist/server/services/SkillRepositoryService.d.ts +0 -22
  148. package/dist/server/services/WorkerEnvManager.d.ts +0 -26
  149. package/dist/server/skill-hub/actions/git-import.d.ts +0 -21
  150. package/dist/server/skill-hub/mcp/McpController.d.ts +0 -15
  151. package/dist/server/skill-hub/plugin.d.ts +0 -61
  152. package/dist/server/skill-hub/tasks/SkillExecutionTask.d.ts +0 -16
  153. package/dist/server/skill-hub/utils/json-fields.d.ts +0 -7
  154. package/dist/server/tools/agent-loop.d.ts +0 -235
  155. package/dist/server/tools/delegate-task.d.ts +0 -19
  156. package/dist/server/tools/external-rag-search.d.ts +0 -42
  157. package/dist/server/tools/orchestrator-plan.d.ts +0 -205
  158. package/dist/server/tools/skill-execute.d.ts +0 -36
@@ -1,58 +1,58 @@
1
- import { SkillHubCard } from './SkillHubCard';
2
- import { parseJsonText } from '../utils/jsonFields';
3
-
4
- const sanitize = (name: string) =>
5
- name
6
- .toLowerCase()
7
- .replace(/[^a-z0-9_]/g, '_')
8
- .replace(/_+/g, '_')
9
- .replace(/^_|_$/g, '');
10
-
11
- const extractList = (data: any) => {
12
- const value = data?.data?.data ?? data?.data ?? data ?? [];
13
- return Array.isArray(value) ? value : [];
14
- };
15
-
16
- export async function registerSkillLoopCards(app: any) {
17
- const toolsManager = app.aiManager?.toolsManager;
18
- if (!toolsManager) return;
19
-
20
- try {
21
- const skillsResponse = await app.apiClient.request({
22
- url: 'skillDefinitions:list',
23
- params: {
24
- filter: { enabled: true },
25
- fields: ['id', 'name', 'autoCall', 'interactionSchema'],
26
- pageSize: 500,
27
- },
28
- });
29
-
30
- let loopSkillIds = new Set<string>();
31
- try {
32
- const loopConfigsResponse = await app.apiClient.request({
33
- url: 'skillLoopConfigs:list',
34
- params: {
35
- filter: { enabled: true },
36
- fields: ['skillId'],
37
- pageSize: 500,
38
- },
39
- });
40
- loopSkillIds = new Set(extractList(loopConfigsResponse.data).map((config: any) => String(config.skillId)));
41
- if (loopSkillIds.size > 0) {
42
- toolsManager.registerTools('skill_hub_execute', { ui: { card: SkillHubCard } });
43
- }
44
- } catch {
45
- // Older deployments may not have the collection before migration/sync.
46
- }
47
-
48
- const skills = extractList(skillsResponse.data);
49
- for (const skill of skills) {
50
- const hasLoopConfig = loopSkillIds.has(String(skill.id));
51
- const hasLegacySchema = !skill.autoCall && !!parseJsonText(skill.interactionSchema, null);
52
- if (!hasLoopConfig && !hasLegacySchema) continue;
53
- toolsManager.registerTools(`skill_hub_${sanitize(skill.name)}`, { ui: { card: SkillHubCard } });
54
- }
55
- } catch {
56
- // user without ACL or backend unavailable - skip silently
57
- }
58
- }
1
+ import { SkillHubCard } from './SkillHubCard';
2
+ import { parseJsonText } from '../utils/jsonFields';
3
+
4
+ const sanitize = (name: string) =>
5
+ name
6
+ .toLowerCase()
7
+ .replace(/[^a-z0-9_]/g, '_')
8
+ .replace(/_+/g, '_')
9
+ .replace(/^_|_$/g, '');
10
+
11
+ const extractList = (data: any) => {
12
+ const value = data?.data?.data ?? data?.data ?? data ?? [];
13
+ return Array.isArray(value) ? value : [];
14
+ };
15
+
16
+ export async function registerSkillLoopCards(app: any) {
17
+ const toolsManager = app.aiManager?.toolsManager;
18
+ if (!toolsManager) return;
19
+
20
+ try {
21
+ const skillsResponse = await app.apiClient.request({
22
+ url: 'skillDefinitions:list',
23
+ params: {
24
+ filter: { enabled: true },
25
+ fields: ['id', 'name', 'autoCall', 'interactionSchema'],
26
+ pageSize: 500,
27
+ },
28
+ });
29
+
30
+ let loopSkillIds = new Set<string>();
31
+ try {
32
+ const loopConfigsResponse = await app.apiClient.request({
33
+ url: 'skillLoopConfigs:list',
34
+ params: {
35
+ filter: { enabled: true },
36
+ fields: ['skillId'],
37
+ pageSize: 500,
38
+ },
39
+ });
40
+ loopSkillIds = new Set(extractList(loopConfigsResponse.data).map((config: any) => String(config.skillId)));
41
+ if (loopSkillIds.size > 0) {
42
+ toolsManager.registerTools('skill_hub_execute', { ui: { card: SkillHubCard } });
43
+ }
44
+ } catch {
45
+ // Older deployments may not have the collection before migration/sync.
46
+ }
47
+
48
+ const skills = extractList(skillsResponse.data);
49
+ for (const skill of skills) {
50
+ const hasLoopConfig = loopSkillIds.has(String(skill.id));
51
+ const hasLegacySchema = !skill.autoCall && !!parseJsonText(skill.interactionSchema, null);
52
+ if (!hasLoopConfig && !hasLegacySchema) continue;
53
+ toolsManager.registerTools(`skill_hub_${sanitize(skill.name)}`, { ui: { card: SkillHubCard } });
54
+ }
55
+ } catch {
56
+ // user without ACL or backend unavailable - skip silently
57
+ }
58
+ }
@@ -1,175 +1,175 @@
1
- import React from 'react';
2
- import { Alert, Button, Card, Input, List, Space, Tag, Typography, message } from 'antd';
3
- import { ToolsUIProperties, useAPIClient } from '@nocobase/client';
4
-
5
- const { Paragraph, Text } = Typography;
6
-
7
- const extractData = (response: any) => response?.data?.data ?? response?.data ?? response;
8
-
9
- const summarizeArgsPlan = (plan: any[]) =>
10
- (Array.isArray(plan) ? plan : []).map((step, index) => ({
11
- id: step.id || step.planKey || index,
12
- planKey: step.planKey || step.key || step.id || `step_${index + 1}`,
13
- title: step.title || `Step ${index + 1}`,
14
- description: step.description || '',
15
- type: step.type || 'tool',
16
- target: step.target || '',
17
- dependsOn: step.dependsOn || [],
18
- }));
19
-
20
- export const PlanApprovalCard: React.FC<ToolsUIProperties> = ({ toolCall, decisions }) => {
21
- const api = useAPIClient();
22
- const rawArgs = (toolCall.args as Record<string, any>) || {};
23
- const runId = rawArgs.runId;
24
- const [detail, setDetail] = React.useState<any>(null);
25
- const [loading, setLoading] = React.useState(false);
26
- const [submitting, setSubmitting] = React.useState(false);
27
- const [feedback, setFeedback] = React.useState('');
28
-
29
- const interrupted = toolCall.invokeStatus === 'init' || toolCall.invokeStatus === 'interrupted';
30
-
31
- React.useEffect(() => {
32
- if (!interrupted || !runId) return;
33
- let mounted = true;
34
- setLoading(true);
35
- api
36
- .request({
37
- url: 'agentLoops:get',
38
- params: { filterByTk: runId },
39
- })
40
- .then((response) => {
41
- if (mounted) setDetail(extractData(response));
42
- })
43
- .catch(() => {
44
- if (mounted) setDetail(null);
45
- })
46
- .finally(() => {
47
- if (mounted) setLoading(false);
48
- });
49
- return () => {
50
- mounted = false;
51
- };
52
- }, [api, interrupted, runId]);
53
-
54
- if (!interrupted) {
55
- return null;
56
- }
57
-
58
- const run = detail?.run || {};
59
- const steps = summarizeArgsPlan(detail?.steps || rawArgs.plan || []);
60
- const goal = run.goal || rawArgs.goal || '';
61
-
62
- const rejectPlan = async () => {
63
- if (!runId) {
64
- await decisions.reject('missing_run_id');
65
- return;
66
- }
67
- setSubmitting(true);
68
- try {
69
- await api.request({
70
- url: 'agentLoops:rejectPlan',
71
- method: 'post',
72
- data: { runId, reason: feedback || 'Plan rejected by user.' },
73
- });
74
- await decisions.reject(JSON.stringify({ reason: 'plan_rejected', runId, feedback }));
75
- } catch (error: any) {
76
- message.error(error?.message || 'Failed to reject plan');
77
- } finally {
78
- setSubmitting(false);
79
- }
80
- };
81
-
82
- const requestChanges = async () => {
83
- if (!feedback.trim()) {
84
- message.warning('Add feedback before requesting changes.');
85
- return;
86
- }
87
- setSubmitting(true);
88
- try {
89
- await api.request({
90
- url: 'agentLoops:requestPlanChanges',
91
- method: 'post',
92
- data: { runId, feedback },
93
- });
94
- await decisions.reject(JSON.stringify({ reason: 'changes_requested', runId, feedback }));
95
- } catch (error: any) {
96
- message.error(error?.message || 'Failed to request plan changes');
97
- } finally {
98
- setSubmitting(false);
99
- }
100
- };
101
-
102
- return (
103
- <Card size="small" style={{ marginTop: 8 }}>
104
- <Space direction="vertical" size={12} style={{ width: '100%' }}>
105
- <Alert
106
- type="info"
107
- showIcon
108
- message="Review orchestrator plan"
109
- description="Execution starts only after you approve this plan."
110
- />
111
-
112
- <Space size={8} wrap>
113
- {runId && <Tag color="blue">Run #{runId}</Tag>}
114
- <Tag color="purple">Plan v{run.planVersion || rawArgs.planVersion || 1}</Tag>
115
- <Tag color={run.approvalStatus === 'pending' ? 'gold' : 'default'}>
116
- {run.approvalStatus || 'pending'}
117
- </Tag>
118
- {run.metadata?.harnessTag && <Tag>{run.metadata.harnessTag}</Tag>}
119
- </Space>
120
-
121
- {goal && (
122
- <Paragraph style={{ marginBottom: 0 }}>
123
- <Text strong>Goal: </Text>
124
- {goal}
125
- </Paragraph>
126
- )}
127
-
128
- <List
129
- size="small"
130
- loading={loading}
131
- dataSource={steps}
132
- locale={{ emptyText: 'No plan steps found.' }}
133
- renderItem={(step: any, index) => (
134
- <List.Item>
135
- <Space direction="vertical" size={2} style={{ width: '100%' }}>
136
- <Space size={8} wrap>
137
- <Text strong>
138
- {index + 1}. {step.title}
139
- </Text>
140
- <Tag>{step.type}</Tag>
141
- {step.target && <Tag color="green">{step.target}</Tag>}
142
- </Space>
143
- {step.description && <Text type="secondary">{step.description}</Text>}
144
- {step.dependsOn?.length > 0 && (
145
- <Text type="secondary" style={{ fontSize: 12 }}>
146
- Depends on: {step.dependsOn.join(', ')}
147
- </Text>
148
- )}
149
- </Space>
150
- </List.Item>
151
- )}
152
- />
153
-
154
- <Input.TextArea
155
- rows={3}
156
- value={feedback}
157
- onChange={(event) => setFeedback(event.target.value)}
158
- placeholder="Optional rejection reason or requested changes"
159
- />
160
-
161
- <Space wrap>
162
- <Button type="primary" loading={submitting} onClick={() => decisions.approve()}>
163
- Accept & Run
164
- </Button>
165
- <Button loading={submitting} onClick={requestChanges}>
166
- Request changes
167
- </Button>
168
- <Button danger loading={submitting} onClick={rejectPlan}>
169
- Reject
170
- </Button>
171
- </Space>
172
- </Space>
173
- </Card>
174
- );
175
- };
1
+ import React from 'react';
2
+ import { Alert, Button, Card, Input, List, Space, Tag, Typography, message } from 'antd';
3
+ import { ToolsUIProperties, useAPIClient } from '@nocobase/client';
4
+
5
+ const { Paragraph, Text } = Typography;
6
+
7
+ const extractData = (response: any) => response?.data?.data ?? response?.data ?? response;
8
+
9
+ const summarizeArgsPlan = (plan: any[]) =>
10
+ (Array.isArray(plan) ? plan : []).map((step, index) => ({
11
+ id: step.id || step.planKey || index,
12
+ planKey: step.planKey || step.key || step.id || `step_${index + 1}`,
13
+ title: step.title || `Step ${index + 1}`,
14
+ description: step.description || '',
15
+ type: step.type || 'tool',
16
+ target: step.target || '',
17
+ dependsOn: step.dependsOn || [],
18
+ }));
19
+
20
+ export const PlanApprovalCard: React.FC<ToolsUIProperties> = ({ toolCall, decisions }) => {
21
+ const api = useAPIClient();
22
+ const rawArgs = (toolCall.args as Record<string, any>) || {};
23
+ const runId = rawArgs.runId;
24
+ const [detail, setDetail] = React.useState<any>(null);
25
+ const [loading, setLoading] = React.useState(false);
26
+ const [submitting, setSubmitting] = React.useState(false);
27
+ const [feedback, setFeedback] = React.useState('');
28
+
29
+ const interrupted = toolCall.invokeStatus === 'init' || toolCall.invokeStatus === 'interrupted';
30
+
31
+ React.useEffect(() => {
32
+ if (!interrupted || !runId) return;
33
+ let mounted = true;
34
+ setLoading(true);
35
+ api
36
+ .request({
37
+ url: 'agentLoops:get',
38
+ params: { filterByTk: runId },
39
+ })
40
+ .then((response) => {
41
+ if (mounted) setDetail(extractData(response));
42
+ })
43
+ .catch(() => {
44
+ if (mounted) setDetail(null);
45
+ })
46
+ .finally(() => {
47
+ if (mounted) setLoading(false);
48
+ });
49
+ return () => {
50
+ mounted = false;
51
+ };
52
+ }, [api, interrupted, runId]);
53
+
54
+ if (!interrupted) {
55
+ return null;
56
+ }
57
+
58
+ const run = detail?.run || {};
59
+ const steps = summarizeArgsPlan(detail?.steps || rawArgs.plan || []);
60
+ const goal = run.goal || rawArgs.goal || '';
61
+
62
+ const rejectPlan = async () => {
63
+ if (!runId) {
64
+ await decisions.reject('missing_run_id');
65
+ return;
66
+ }
67
+ setSubmitting(true);
68
+ try {
69
+ await api.request({
70
+ url: 'agentLoops:rejectPlan',
71
+ method: 'post',
72
+ data: { runId, reason: feedback || 'Plan rejected by user.' },
73
+ });
74
+ await decisions.reject(JSON.stringify({ reason: 'plan_rejected', runId, feedback }));
75
+ } catch (error: any) {
76
+ message.error(error?.message || 'Failed to reject plan');
77
+ } finally {
78
+ setSubmitting(false);
79
+ }
80
+ };
81
+
82
+ const requestChanges = async () => {
83
+ if (!feedback.trim()) {
84
+ message.warning('Add feedback before requesting changes.');
85
+ return;
86
+ }
87
+ setSubmitting(true);
88
+ try {
89
+ await api.request({
90
+ url: 'agentLoops:requestPlanChanges',
91
+ method: 'post',
92
+ data: { runId, feedback },
93
+ });
94
+ await decisions.reject(JSON.stringify({ reason: 'changes_requested', runId, feedback }));
95
+ } catch (error: any) {
96
+ message.error(error?.message || 'Failed to request plan changes');
97
+ } finally {
98
+ setSubmitting(false);
99
+ }
100
+ };
101
+
102
+ return (
103
+ <Card size="small" style={{ marginTop: 8 }}>
104
+ <Space direction="vertical" size={12} style={{ width: '100%' }}>
105
+ <Alert
106
+ type="info"
107
+ showIcon
108
+ message="Review orchestrator plan"
109
+ description="Execution starts only after you approve this plan."
110
+ />
111
+
112
+ <Space size={8} wrap>
113
+ {runId && <Tag color="blue">Run #{runId}</Tag>}
114
+ <Tag color="purple">Plan v{run.planVersion || rawArgs.planVersion || 1}</Tag>
115
+ <Tag color={run.approvalStatus === 'pending' ? 'gold' : 'default'}>
116
+ {run.approvalStatus || 'pending'}
117
+ </Tag>
118
+ {run.metadata?.harnessTag && <Tag>{run.metadata.harnessTag}</Tag>}
119
+ </Space>
120
+
121
+ {goal && (
122
+ <Paragraph style={{ marginBottom: 0 }}>
123
+ <Text strong>Goal: </Text>
124
+ {goal}
125
+ </Paragraph>
126
+ )}
127
+
128
+ <List
129
+ size="small"
130
+ loading={loading}
131
+ dataSource={steps}
132
+ locale={{ emptyText: 'No plan steps found.' }}
133
+ renderItem={(step: any, index) => (
134
+ <List.Item>
135
+ <Space direction="vertical" size={2} style={{ width: '100%' }}>
136
+ <Space size={8} wrap>
137
+ <Text strong>
138
+ {index + 1}. {step.title}
139
+ </Text>
140
+ <Tag>{step.type}</Tag>
141
+ {step.target && <Tag color="green">{step.target}</Tag>}
142
+ </Space>
143
+ {step.description && <Text type="secondary">{step.description}</Text>}
144
+ {step.dependsOn?.length > 0 && (
145
+ <Text type="secondary" style={{ fontSize: 12 }}>
146
+ Depends on: {step.dependsOn.join(', ')}
147
+ </Text>
148
+ )}
149
+ </Space>
150
+ </List.Item>
151
+ )}
152
+ />
153
+
154
+ <Input.TextArea
155
+ rows={3}
156
+ value={feedback}
157
+ onChange={(event) => setFeedback(event.target.value)}
158
+ placeholder="Optional rejection reason or requested changes"
159
+ />
160
+
161
+ <Space wrap>
162
+ <Button type="primary" loading={submitting} onClick={() => decisions.approve()}>
163
+ Accept & Run
164
+ </Button>
165
+ <Button loading={submitting} onClick={requestChanges}>
166
+ Request changes
167
+ </Button>
168
+ <Button danger loading={submitting} onClick={rejectPlan}>
169
+ Reject
170
+ </Button>
171
+ </Space>
172
+ </Space>
173
+ </Card>
174
+ );
175
+ };
@@ -1,7 +1,7 @@
1
- import { PlanApprovalCard } from './PlanApprovalCard';
2
-
3
- export async function registerOrchestratorCards(app: any) {
4
- const toolsManager = app.aiManager?.toolsManager;
5
- if (!toolsManager) return;
6
- toolsManager.registerTools('orchestrator_execute_plan', { ui: { card: PlanApprovalCard } });
7
- }
1
+ import { PlanApprovalCard } from './PlanApprovalCard';
2
+
3
+ export async function registerOrchestratorCards(app: any) {
4
+ const toolsManager = app.aiManager?.toolsManager;
5
+ if (!toolsManager) return;
6
+ toolsManager.registerTools('orchestrator_execute_plan', { ui: { card: PlanApprovalCard } });
7
+ }