plugin-agent-orchestrator 1.0.17 → 1.0.18

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 (139) hide show
  1. package/dist/client/AIEmployeeSelect.d.ts +11 -0
  2. package/dist/client/AIEmployeesContext.d.ts +30 -0
  3. package/dist/client/AgentRunsTab.d.ts +2 -0
  4. package/dist/client/HarnessProfilesTab.d.ts +2 -0
  5. package/dist/client/OrchestratorSettings.d.ts +3 -0
  6. package/dist/client/RulesTab.d.ts +2 -0
  7. package/dist/client/TracingTab.d.ts +2 -0
  8. package/dist/client/index.d.ts +1 -0
  9. package/dist/client/index.js +1 -1
  10. package/dist/client/plugin.d.ts +6 -0
  11. package/dist/client/skill-hub/components/ExecutionHistory.d.ts +2 -0
  12. package/dist/client/skill-hub/components/ExecutionProgress.d.ts +20 -0
  13. package/dist/client/skill-hub/components/GitSkillImport.d.ts +7 -0
  14. package/dist/client/skill-hub/components/LoopSettings.d.ts +2 -0
  15. package/dist/client/skill-hub/components/SkillEditor.d.ts +7 -0
  16. package/dist/client/skill-hub/components/SkillManager.d.ts +2 -0
  17. package/dist/client/skill-hub/components/SkillMetrics.d.ts +2 -0
  18. package/dist/client/skill-hub/components/SkillTestPanel.d.ts +7 -0
  19. package/dist/client/skill-hub/index.d.ts +11 -0
  20. package/dist/client/skill-hub/locale.d.ts +3 -0
  21. package/dist/client/skill-hub/tools/InteractionSchemasProvider.d.ts +6 -0
  22. package/dist/client/skill-hub/tools/SkillHubCard.d.ts +3 -0
  23. package/dist/client/skill-hub/tools/loopTemplates.d.ts +22 -0
  24. package/dist/client/skill-hub/tools/registerSkillLoopCards.d.ts +1 -0
  25. package/dist/client/skill-hub/utils/jsonFields.d.ts +3 -0
  26. package/dist/client/tools/PlanApprovalCard.d.ts +3 -0
  27. package/dist/client/tools/registerOrchestratorCards.d.ts +1 -0
  28. package/dist/externalVersion.js +6 -6
  29. package/dist/index.d.ts +2 -0
  30. package/dist/server/collections/agent-execution-spans.d.ts +9 -0
  31. package/dist/server/collections/agent-harness-profiles.d.ts +2 -0
  32. package/dist/server/collections/agent-harness-profiles.js +89 -0
  33. package/dist/server/collections/agent-loop-events.d.ts +2 -0
  34. package/dist/server/collections/agent-loop-events.js +101 -0
  35. package/dist/server/collections/agent-loop-runs.d.ts +2 -0
  36. package/dist/server/collections/agent-loop-runs.js +188 -0
  37. package/dist/server/collections/agent-loop-steps.d.ts +2 -0
  38. package/dist/server/collections/agent-loop-steps.js +174 -0
  39. package/dist/server/collections/orchestrator-config.d.ts +2 -0
  40. package/dist/server/collections/orchestrator-config.js +7 -0
  41. package/dist/server/collections/orchestrator-logs.d.ts +8 -0
  42. package/dist/server/collections/skill-definitions.d.ts +3 -0
  43. package/dist/server/collections/skill-executions.d.ts +3 -0
  44. package/dist/server/collections/skill-executions.js +12 -0
  45. package/dist/server/collections/skill-loop-configs.d.ts +3 -0
  46. package/dist/server/collections/skill-loop-configs.js +94 -0
  47. package/dist/server/collections/skill-worker-configs.d.ts +3 -0
  48. package/dist/server/index.d.ts +1 -0
  49. package/dist/server/migrations/20260423000000-add-progress-fields.d.ts +4 -0
  50. package/dist/server/migrations/20260425000000-add-interaction-schema.d.ts +4 -0
  51. package/dist/server/migrations/20260427000000-add-tracing-detail-fields.d.ts +7 -0
  52. package/dist/server/migrations/20260427000000-change-packages-to-text.d.ts +4 -0
  53. package/dist/server/migrations/20260427000001-change-other-json-to-text.d.ts +4 -0
  54. package/dist/server/migrations/20260429000000-add-llm-fields.d.ts +7 -0
  55. package/dist/server/migrations/20260429000000-fix-inputargs-json-to-text.d.ts +16 -0
  56. package/dist/server/migrations/20260503000000-add-orchestrator-trace-fields.d.ts +7 -0
  57. package/dist/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.d.ts +7 -0
  58. package/dist/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.js +55 -0
  59. package/dist/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.d.ts +12 -0
  60. package/dist/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.js +162 -0
  61. package/dist/server/plugin.d.ts +16 -0
  62. package/dist/server/plugin.js +13 -0
  63. package/dist/server/resources/agent-loop.d.ts +3 -0
  64. package/dist/server/resources/agent-loop.js +205 -0
  65. package/dist/server/resources/tracing.d.ts +7 -0
  66. package/dist/server/services/AgentHarness.d.ts +42 -0
  67. package/dist/server/services/AgentHarness.js +565 -0
  68. package/dist/server/services/AgentLoopController.d.ts +205 -0
  69. package/dist/server/services/AgentLoopController.js +940 -0
  70. package/dist/server/services/AgentLoopRepository.d.ts +20 -0
  71. package/dist/server/services/AgentLoopRepository.js +210 -0
  72. package/dist/server/services/AgentLoopService.d.ts +149 -0
  73. package/dist/server/services/AgentLoopService.js +133 -0
  74. package/dist/server/services/AgentPlanValidator.d.ts +4 -0
  75. package/dist/server/services/AgentPlanValidator.js +99 -0
  76. package/dist/server/services/AgentPlannerService.d.ts +8 -0
  77. package/dist/server/services/AgentPlannerService.js +119 -0
  78. package/dist/server/services/AgentRegistryService.d.ts +13 -0
  79. package/dist/server/services/AgentRegistryService.js +178 -0
  80. package/dist/server/services/CodeValidator.d.ts +32 -0
  81. package/dist/server/services/ExecutionSpanService.d.ts +46 -0
  82. package/dist/server/services/FileManager.d.ts +28 -0
  83. package/dist/server/services/SandboxRunner.d.ts +41 -0
  84. package/dist/server/services/SkillManager.d.ts +6 -0
  85. package/dist/server/services/SkillRepositoryService.d.ts +22 -0
  86. package/dist/server/services/WorkerEnvManager.d.ts +26 -0
  87. package/dist/server/skill-hub/actions/git-import.d.ts +21 -0
  88. package/dist/server/skill-hub/mcp/McpController.d.ts +15 -0
  89. package/dist/server/skill-hub/plugin.d.ts +61 -0
  90. package/dist/server/skill-hub/plugin.js +137 -54
  91. package/dist/server/skill-hub/tasks/SkillExecutionTask.d.ts +16 -0
  92. package/dist/server/skill-hub/utils/json-fields.d.ts +7 -0
  93. package/dist/server/tools/agent-loop.d.ts +235 -0
  94. package/dist/server/tools/agent-loop.js +406 -0
  95. package/dist/server/tools/delegate-task.d.ts +19 -0
  96. package/dist/server/tools/delegate-task.js +19 -368
  97. package/dist/server/tools/external-rag-search.d.ts +42 -0
  98. package/dist/server/tools/orchestrator-plan.d.ts +205 -0
  99. package/dist/server/tools/orchestrator-plan.js +291 -0
  100. package/dist/server/tools/skill-execute.d.ts +36 -0
  101. package/dist/server/tools/skill-execute.js +2 -0
  102. package/package.json +1 -1
  103. package/src/client/AgentRunsTab.tsx +764 -0
  104. package/src/client/HarnessProfilesTab.tsx +247 -0
  105. package/src/client/OrchestratorSettings.tsx +40 -2
  106. package/src/client/RulesTab.tsx +103 -6
  107. package/src/client/plugin.tsx +27 -54
  108. package/src/client/skill-hub/components/LoopSettings.tsx +331 -0
  109. package/src/client/skill-hub/index.tsx +51 -75
  110. package/src/client/skill-hub/tools/InteractionSchemasProvider.tsx +56 -16
  111. package/src/client/skill-hub/tools/SkillHubCard.tsx +35 -4
  112. package/src/client/skill-hub/tools/loopTemplates.ts +52 -0
  113. package/src/client/skill-hub/tools/registerSkillLoopCards.ts +58 -0
  114. package/src/client/tools/PlanApprovalCard.tsx +175 -0
  115. package/src/client/tools/registerOrchestratorCards.ts +7 -0
  116. package/src/server/collections/agent-harness-profiles.ts +59 -0
  117. package/src/server/collections/agent-loop-events.ts +71 -0
  118. package/src/server/collections/agent-loop-runs.ts +158 -0
  119. package/src/server/collections/agent-loop-steps.ts +144 -0
  120. package/src/server/collections/orchestrator-config.ts +7 -0
  121. package/src/server/collections/skill-executions.ts +63 -51
  122. package/src/server/collections/skill-loop-configs.ts +65 -0
  123. package/src/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.ts +30 -0
  124. package/src/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.ts +142 -0
  125. package/src/server/plugin.ts +15 -0
  126. package/src/server/resources/agent-loop.ts +183 -0
  127. package/src/server/services/AgentHarness.ts +663 -0
  128. package/src/server/services/AgentLoopController.ts +1128 -0
  129. package/src/server/services/AgentLoopRepository.ts +194 -0
  130. package/src/server/services/AgentLoopService.ts +161 -0
  131. package/src/server/services/AgentPlanValidator.ts +73 -0
  132. package/src/server/services/AgentPlannerService.ts +93 -0
  133. package/src/server/services/AgentRegistryService.ts +169 -0
  134. package/src/server/services/ExecutionSpanService.ts +2 -0
  135. package/src/server/skill-hub/plugin.ts +881 -771
  136. package/src/server/tools/agent-loop.ts +399 -0
  137. package/src/server/tools/delegate-task.ts +23 -485
  138. package/src/server/tools/orchestrator-plan.ts +279 -0
  139. package/src/server/tools/skill-execute.ts +68 -64
@@ -1,55 +1,55 @@
1
- import { CollectionOptions } from '@nocobase/database';
2
-
3
- export default {
4
- name: 'skillExecutions',
5
- title: 'Skill Executions',
6
- fields: [
7
- {
8
- name: 'id',
9
- type: 'bigInt',
10
- autoIncrement: true,
11
- primaryKey: true,
12
- },
13
- {
14
- name: 'skill',
15
- type: 'belongsTo',
16
- target: 'skillDefinitions',
17
- foreignKey: 'skillId',
18
- },
19
- {
20
- // 'pending' | 'running' | 'succeeded' | 'failed' | 'canceled' | 'timeout'
21
- name: 'status',
22
- type: 'string',
23
- length: 20,
24
- defaultValue: 'pending',
25
- },
1
+ import { CollectionOptions } from '@nocobase/database';
2
+
3
+ export default {
4
+ name: 'skillExecutions',
5
+ title: 'Skill Executions',
6
+ fields: [
7
+ {
8
+ name: 'id',
9
+ type: 'bigInt',
10
+ autoIncrement: true,
11
+ primaryKey: true,
12
+ },
13
+ {
14
+ name: 'skill',
15
+ type: 'belongsTo',
16
+ target: 'skillDefinitions',
17
+ foreignKey: 'skillId',
18
+ },
19
+ {
20
+ // 'pending' | 'running' | 'succeeded' | 'failed' | 'canceled' | 'timeout'
21
+ name: 'status',
22
+ type: 'string',
23
+ length: 20,
24
+ defaultValue: 'pending',
25
+ },
26
26
  {
27
27
  name: 'inputArgs',
28
28
  type: 'text',
29
29
  },
30
- {
31
- // Final code after template rendering
32
- name: 'executedCode',
33
- type: 'text',
34
- },
35
- {
36
- name: 'stdout',
37
- type: 'text',
38
- },
39
- {
40
- name: 'stderr',
41
- type: 'text',
42
- },
30
+ {
31
+ // Final code after template rendering
32
+ name: 'executedCode',
33
+ type: 'text',
34
+ },
35
+ {
36
+ name: 'stdout',
37
+ type: 'text',
38
+ },
39
+ {
40
+ name: 'stderr',
41
+ type: 'text',
42
+ },
43
43
  {
44
44
  // [{ name, size, mimeType }]
45
45
  name: 'outputFiles',
46
46
  type: 'text',
47
47
  defaultValue: null,
48
48
  },
49
- {
50
- name: 'durationMs',
51
- type: 'integer',
52
- },
49
+ {
50
+ name: 'durationMs',
51
+ type: 'integer',
52
+ },
53
53
  {
54
54
  name: 'sessionId',
55
55
  type: 'string',
@@ -80,15 +80,27 @@ export default {
80
80
  length: 100,
81
81
  allowNull: true,
82
82
  },
83
+ {
84
+ name: 'agentLoopRunId',
85
+ type: 'string',
86
+ length: 100,
87
+ allowNull: true,
88
+ },
89
+ {
90
+ name: 'agentLoopStepId',
91
+ type: 'string',
92
+ length: 100,
93
+ allowNull: true,
94
+ },
83
95
  {
84
96
  name: 'triggeredBy',
85
97
  type: 'belongsTo',
86
- target: 'users',
87
- foreignKey: 'triggeredById',
88
- },
89
- {
90
- name: 'createdAt',
91
- type: 'date',
92
- },
93
- ],
94
- } as CollectionOptions;
98
+ target: 'users',
99
+ foreignKey: 'triggeredById',
100
+ },
101
+ {
102
+ name: 'createdAt',
103
+ type: 'date',
104
+ },
105
+ ],
106
+ } as CollectionOptions;
@@ -0,0 +1,65 @@
1
+ import { CollectionOptions } from '@nocobase/database';
2
+
3
+ export default {
4
+ name: 'skillLoopConfigs',
5
+ title: 'Skill Loop Configs',
6
+ fields: [
7
+ {
8
+ name: 'id',
9
+ type: 'bigInt',
10
+ autoIncrement: true,
11
+ primaryKey: true,
12
+ },
13
+ {
14
+ name: 'skill',
15
+ type: 'belongsTo',
16
+ target: 'skillDefinitions',
17
+ foreignKey: 'skillId',
18
+ },
19
+ {
20
+ name: 'enabled',
21
+ type: 'boolean',
22
+ defaultValue: true,
23
+ },
24
+ {
25
+ name: 'title',
26
+ type: 'string',
27
+ length: 200,
28
+ },
29
+ {
30
+ name: 'templateKey',
31
+ type: 'string',
32
+ length: 80,
33
+ defaultValue: 'confirm',
34
+ },
35
+ {
36
+ name: 'prompt',
37
+ type: 'text',
38
+ },
39
+ {
40
+ // Resolved interaction schema consumed by the generic Skill Hub UI card.
41
+ name: 'schema',
42
+ type: 'text',
43
+ },
44
+ {
45
+ // Optional template-specific state for future loop renderers.
46
+ name: 'config',
47
+ type: 'text',
48
+ defaultValue: null,
49
+ },
50
+ {
51
+ name: 'createdAt',
52
+ type: 'date',
53
+ },
54
+ {
55
+ name: 'updatedAt',
56
+ type: 'date',
57
+ },
58
+ {
59
+ name: 'createdBy',
60
+ type: 'belongsTo',
61
+ target: 'users',
62
+ foreignKey: 'createdById',
63
+ },
64
+ ],
65
+ } as CollectionOptions;
@@ -0,0 +1,30 @@
1
+ import { Migration } from '@nocobase/server';
2
+
3
+ export default class AddAgentLoopFieldsToSkillExecutions extends Migration {
4
+ on = 'afterLoad';
5
+ appVersion = '>=0.1.0';
6
+
7
+ async up() {
8
+ const queryInterface = (this as any).db.sequelize.getQueryInterface();
9
+ const tablePrefix = (this as any).db.options.tablePrefix || '';
10
+ const tableName = `${tablePrefix}skillExecutions`;
11
+ const tableExists = await queryInterface.tableExists(tableName).catch(() => false);
12
+ if (!tableExists) return;
13
+
14
+ const tableDesc = await queryInterface.describeTable(tableName);
15
+ const addIfMissing = async (name: string) => {
16
+ if (tableDesc[name]) return;
17
+ await queryInterface.addColumn(tableName, name, {
18
+ type: 'VARCHAR(100)',
19
+ allowNull: true,
20
+ });
21
+ };
22
+
23
+ await addIfMissing('agentLoopRunId');
24
+ await addIfMissing('agentLoopStepId');
25
+ }
26
+
27
+ async down() {
28
+ // No rollback: these nullable trace-link columns are backward compatible.
29
+ }
30
+ }
@@ -0,0 +1,142 @@
1
+ import { DataTypes } from '@nocobase/database';
2
+ import { Migration } from '@nocobase/server';
3
+
4
+ export default class AddPlanApprovalAndHarnessProfiles extends Migration {
5
+ on = 'afterLoad';
6
+ appVersion = '>=0.1.0';
7
+
8
+ async up() {
9
+ const db = (this as any).db;
10
+ const queryInterface = db.sequelize.getQueryInterface();
11
+ const tablePrefix = db.options.tablePrefix || '';
12
+
13
+ await this.addRunColumns(queryInterface, `${tablePrefix}agentLoopRuns`);
14
+ await this.addStepColumns(queryInterface, `${tablePrefix}agentLoopSteps`);
15
+ await this.addConfigColumns(queryInterface, `${tablePrefix}orchestratorConfig`);
16
+ await this.ensureHarnessProfiles(queryInterface, `${tablePrefix}agentHarnessProfiles`);
17
+ await this.seedDefaultProfiles();
18
+ }
19
+
20
+ async addRunColumns(queryInterface: any, tableName: string) {
21
+ const tableExists = await queryInterface.tableExists(tableName).catch(() => false);
22
+ if (!tableExists) return;
23
+ const tableDesc = await queryInterface.describeTable(tableName);
24
+ const addIfMissing = async (name: string, spec: any) => {
25
+ if (tableDesc[name]) return;
26
+ await queryInterface.addColumn(tableName, name, spec);
27
+ };
28
+
29
+ await addIfMissing('approvalStatus', { type: DataTypes.STRING(30), allowNull: true, defaultValue: 'none' });
30
+ await addIfMissing('approvedById', { type: DataTypes.BIGINT, allowNull: true });
31
+ await addIfMissing('approvedAt', { type: DataTypes.DATE, allowNull: true });
32
+ await addIfMissing('rejectionReason', { type: DataTypes.TEXT, allowNull: true });
33
+ await addIfMissing('changeRequest', { type: DataTypes.TEXT, allowNull: true });
34
+ await addIfMissing('planVersion', { type: DataTypes.INTEGER, allowNull: true, defaultValue: 1 });
35
+ await addIfMissing('planSource', { type: DataTypes.STRING(50), allowNull: true });
36
+ await addIfMissing('plannerModel', { type: DataTypes.STRING(100), allowNull: true });
37
+ await addIfMissing('lockedBy', { type: DataTypes.STRING(100), allowNull: true });
38
+ await addIfMissing('lockedUntil', { type: DataTypes.DATE, allowNull: true });
39
+ }
40
+
41
+ async addStepColumns(queryInterface: any, tableName: string) {
42
+ const tableExists = await queryInterface.tableExists(tableName).catch(() => false);
43
+ if (!tableExists) return;
44
+ const tableDesc = await queryInterface.describeTable(tableName);
45
+ if (!tableDesc.dependencyPolicy) {
46
+ await queryInterface.addColumn(tableName, 'dependencyPolicy', {
47
+ type: DataTypes.STRING(30),
48
+ allowNull: true,
49
+ defaultValue: 'require_success',
50
+ });
51
+ }
52
+ }
53
+
54
+ async addConfigColumns(queryInterface: any, tableName: string) {
55
+ const tableExists = await queryInterface.tableExists(tableName).catch(() => false);
56
+ if (!tableExists) return;
57
+ const tableDesc = await queryInterface.describeTable(tableName);
58
+ if (!tableDesc.harnessTag) {
59
+ await queryInterface.addColumn(tableName, 'harnessTag', {
60
+ type: DataTypes.STRING(100),
61
+ allowNull: true,
62
+ defaultValue: 'default',
63
+ });
64
+ }
65
+ }
66
+
67
+ async ensureHarnessProfiles(queryInterface: any, tableName: string) {
68
+ const tableExists = await queryInterface.tableExists(tableName).catch(() => false);
69
+ if (tableExists) return;
70
+ await queryInterface.createTable(tableName, {
71
+ id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true },
72
+ tag: { type: DataTypes.STRING(100), allowNull: false, unique: true },
73
+ title: { type: DataTypes.STRING(200), allowNull: true },
74
+ description: { type: DataTypes.TEXT, allowNull: true },
75
+ enabled: { type: DataTypes.BOOLEAN, allowNull: true, defaultValue: true },
76
+ settings: { type: DataTypes.JSON, allowNull: true, defaultValue: {} },
77
+ createdAt: { type: DataTypes.DATE, allowNull: true },
78
+ updatedAt: { type: DataTypes.DATE, allowNull: true },
79
+ });
80
+ }
81
+
82
+ async seedDefaultProfiles() {
83
+ const repo = (this as any).db.getRepository('agentHarnessProfiles');
84
+ if (!repo) return;
85
+ const profiles = [
86
+ {
87
+ tag: 'default',
88
+ title: 'Default',
89
+ description: 'Balanced profile for normal multi-agent work.',
90
+ settings: {
91
+ requirePlanApproval: true,
92
+ allowSubAgents: true,
93
+ allowToolCalls: true,
94
+ maxParallelSubAgents: 3,
95
+ maxControllerSteps: 100,
96
+ },
97
+ },
98
+ {
99
+ tag: 'safe',
100
+ title: 'Safe',
101
+ description: 'Strict approval-first profile for higher-risk work.',
102
+ settings: {
103
+ requirePlanApproval: true,
104
+ allowSubAgents: true,
105
+ allowToolCalls: true,
106
+ maxParallelSubAgents: 1,
107
+ maxControllerSteps: 50,
108
+ requireVerification: true,
109
+ },
110
+ },
111
+ {
112
+ tag: 'file-heavy',
113
+ title: 'File Heavy',
114
+ description: 'Profile for tasks that inspect or transform many attachments/files.',
115
+ settings: {
116
+ requirePlanApproval: true,
117
+ allowSubAgents: true,
118
+ allowToolCalls: true,
119
+ maxParallelSubAgents: 2,
120
+ maxControllerSteps: 120,
121
+ preferFileTools: true,
122
+ },
123
+ },
124
+ ];
125
+ for (const profile of profiles) {
126
+ const existing = await repo.findOne({ filter: { tag: profile.tag } });
127
+ if (existing) continue;
128
+ await repo.create({
129
+ values: {
130
+ ...profile,
131
+ enabled: true,
132
+ createdAt: new Date(),
133
+ updatedAt: new Date(),
134
+ },
135
+ });
136
+ }
137
+ }
138
+
139
+ async down() {
140
+ // No rollback: new nullable columns and the profile table are backward compatible.
141
+ }
142
+ }
@@ -2,14 +2,19 @@ import { Plugin } from '@nocobase/server';
2
2
  import path from 'path';
3
3
  import { createDelegateToolsProvider } from './tools/delegate-task';
4
4
  import { createExternalRagSearchTool } from './tools/external-rag-search';
5
+ import { createOrchestratorPlanTools } from './tools/orchestrator-plan';
5
6
  import { registerTracingResource } from './resources/tracing';
7
+ import { registerAgentLoopResource } from './resources/agent-loop';
6
8
  import SkillHubSubFeature from './skill-hub/plugin';
9
+ import { AgentLoopService } from './services/AgentLoopService';
7
10
 
8
11
  export class PluginAgentOrchestratorServer extends Plugin {
9
12
  skillHub: SkillHubSubFeature;
13
+ agentLoopService: AgentLoopService;
10
14
 
11
15
  async afterAdd() {
12
16
  this.skillHub = new SkillHubSubFeature(this);
17
+ this.agentLoopService = new AgentLoopService(this);
13
18
  }
14
19
 
15
20
  async beforeLoad() {
@@ -32,9 +37,15 @@ export class PluginAgentOrchestratorServer extends Plugin {
32
37
  actions: [
33
38
  'orchestratorConfig:*',
34
39
  'orchestratorTracing:*',
40
+ 'agentLoops:*',
41
+ 'agentLoopRuns:*',
42
+ 'agentLoopSteps:*',
43
+ 'agentLoopEvents:*',
44
+ 'agentHarnessProfiles:*',
35
45
  'agentExecutionSpans:*',
36
46
  'skillDefinitions:*',
37
47
  'skillExecutions:*',
48
+ 'skillLoopConfigs:*',
38
49
  'skillHub:*',
39
50
  'skillWorkerConfigs:*',
40
51
  ],
@@ -45,9 +56,13 @@ export class PluginAgentOrchestratorServer extends Plugin {
45
56
  // Uses createReactAgent (LangGraph public API) instead of private AIEmployee class.
46
57
  // Tools are registered via app.aiManager.toolsManager (public API from @nocobase/ai core).
47
58
  const toolsManager = (this as any).app.aiManager.toolsManager;
59
+ toolsManager.registerTools(createOrchestratorPlanTools(this, this.agentLoopService));
48
60
  toolsManager.registerTools(createExternalRagSearchTool(this));
49
61
  toolsManager.registerDynamicTools(createDelegateToolsProvider(this));
50
62
 
63
+ // --- Register Agent Loop Resource ---
64
+ registerAgentLoopResource(this, this.agentLoopService);
65
+
51
66
  // --- Register Tracing Resource (Phase 5) ---
52
67
  // Custom read-only resource for the Swarm Tracing admin page.
53
68
  registerTracingResource(this);
@@ -0,0 +1,183 @@
1
+ import { Plugin } from '@nocobase/server';
2
+ import { AgentLoopService } from '../services/AgentLoopService';
3
+
4
+ function toPlain(record: any) {
5
+ return record?.toJSON?.() || record;
6
+ }
7
+
8
+ function currentUserId(ctx: any) {
9
+ return ctx?.state?.currentUser?.id || ctx?.auth?.user?.id;
10
+ }
11
+
12
+ function values(ctx: any) {
13
+ return ctx.request?.body || ctx.action?.params?.values || {};
14
+ }
15
+
16
+ function formatRunRow(raw: any) {
17
+ const row = toPlain(raw);
18
+ return {
19
+ id: row.id,
20
+ rootRunId: row.rootRunId,
21
+ sessionId: row.sessionId,
22
+ messageId: row.messageId,
23
+ leaderUsername: row.leaderUsername,
24
+ goal: row.goal,
25
+ status: row.status,
26
+ approvalStatus: row.approvalStatus,
27
+ planVersion: row.planVersion,
28
+ planSource: row.planSource,
29
+ plannerModel: row.plannerModel,
30
+ approvedById: row.approvedById,
31
+ approvedAt: row.approvedAt,
32
+ rejectionReason: row.rejectionReason,
33
+ changeRequest: row.changeRequest,
34
+ currentStepId: row.currentStepId,
35
+ iterationCount: row.iterationCount || 0,
36
+ finalAnswer: row.finalAnswer,
37
+ summary: row.summary,
38
+ userId: row.userId,
39
+ startedAt: row.startedAt,
40
+ endedAt: row.endedAt,
41
+ createdAt: row.createdAt,
42
+ updatedAt: row.updatedAt,
43
+ };
44
+ }
45
+
46
+ export function registerAgentLoopResource(plugin: Plugin, service: AgentLoopService) {
47
+ const app = plugin.app;
48
+
49
+ app.resource({
50
+ name: 'agentLoops',
51
+ actions: {
52
+ async list(ctx, next) {
53
+ const { page = 1, pageSize = 20, sort = ['-createdAt'], filter = {} } = ctx.action.params;
54
+ const repo = ctx.db.getRepository('agentLoopRuns');
55
+ const [rows, count] = await repo.findAndCount({
56
+ filter,
57
+ sort,
58
+ offset: (Number(page) - 1) * Number(pageSize),
59
+ limit: Number(pageSize),
60
+ });
61
+
62
+ ctx.body = {
63
+ data: rows.map(formatRunRow),
64
+ meta: {
65
+ count,
66
+ page: Number(page),
67
+ pageSize: Number(pageSize),
68
+ totalPage: Math.ceil(count / Number(pageSize)),
69
+ },
70
+ };
71
+ await next();
72
+ },
73
+
74
+ async get(ctx, next) {
75
+ const { filterByTk } = ctx.action.params;
76
+ if (!filterByTk) {
77
+ ctx.throw(400, 'run id is required');
78
+ return;
79
+ }
80
+ ctx.body = { data: await service.getRunDetail(filterByTk) };
81
+ await next();
82
+ },
83
+
84
+ async resume(ctx, next) {
85
+ const body = values(ctx);
86
+ const runId = body.runId || ctx.action.params.filterByTk;
87
+ if (!runId) {
88
+ ctx.throw(400, 'runId is required');
89
+ return;
90
+ }
91
+ ctx.body = {
92
+ data: await service.resumeRun(runId, {
93
+ stepId: body.stepId,
94
+ approved: body.approved !== false,
95
+ editedInput: body.editedInput,
96
+ userId: currentUserId(ctx),
97
+ ctx,
98
+ }),
99
+ };
100
+ await next();
101
+ },
102
+
103
+ async approvePlan(ctx, next) {
104
+ const body = values(ctx);
105
+ const runId = body.runId || ctx.action.params.filterByTk;
106
+ if (!runId) {
107
+ ctx.throw(400, 'runId is required');
108
+ return;
109
+ }
110
+ ctx.body = {
111
+ data: await service.approvePlanAndExecute(runId, {
112
+ userId: currentUserId(ctx),
113
+ ctx,
114
+ reason: body.reason,
115
+ }),
116
+ };
117
+ await next();
118
+ },
119
+
120
+ async rejectPlan(ctx, next) {
121
+ const body = values(ctx);
122
+ const runId = body.runId || ctx.action.params.filterByTk;
123
+ if (!runId) {
124
+ ctx.throw(400, 'runId is required');
125
+ return;
126
+ }
127
+ ctx.body = {
128
+ data: await service.rejectPlan(runId, {
129
+ reason: body.reason,
130
+ userId: currentUserId(ctx),
131
+ }),
132
+ };
133
+ await next();
134
+ },
135
+
136
+ async requestPlanChanges(ctx, next) {
137
+ const body = values(ctx);
138
+ const runId = body.runId || ctx.action.params.filterByTk;
139
+ if (!runId) {
140
+ ctx.throw(400, 'runId is required');
141
+ return;
142
+ }
143
+ ctx.body = {
144
+ data: await service.requestPlanChanges(runId, {
145
+ feedback: body.feedback,
146
+ userId: currentUserId(ctx),
147
+ }),
148
+ };
149
+ await next();
150
+ },
151
+
152
+ async cancel(ctx, next) {
153
+ const body = values(ctx);
154
+ const runId = body.runId || ctx.action.params.filterByTk;
155
+ if (!runId) {
156
+ ctx.throw(400, 'runId is required');
157
+ return;
158
+ }
159
+ ctx.body = {
160
+ data: await service.cancelRun(runId, {
161
+ reason: body.reason,
162
+ userId: currentUserId(ctx),
163
+ }),
164
+ };
165
+ await next();
166
+ },
167
+
168
+ async retryStep(ctx, next) {
169
+ const body = values(ctx);
170
+ if (!body.stepId) {
171
+ ctx.throw(400, 'stepId is required');
172
+ return;
173
+ }
174
+ ctx.body = {
175
+ data: await service.retryStep(body.stepId, {
176
+ userId: currentUserId(ctx),
177
+ }),
178
+ };
179
+ await next();
180
+ },
181
+ },
182
+ });
183
+ }