plugin-agent-orchestrator 1.0.28 → 1.0.32

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/README.md +9 -7
  2. package/dist/client/index.js +1 -1
  3. package/dist/client-v2/{214.723affb37c13bf7a.js → 214.79650a549273f163.js} +1 -1
  4. package/dist/client-v2/264.718a107e43fc163c.js +10 -0
  5. package/dist/client-v2/373.f5d5292e53c4e832.js +10 -0
  6. package/dist/client-v2/{41.1805b2edfaa4afe2.js → 41.ba6e080cc0488143.js} +1 -1
  7. package/dist/client-v2/418.29e713f79131eece.js +10 -0
  8. package/dist/client-v2/619.bd3c5698b40705c3.js +10 -0
  9. package/dist/client-v2/677.a991ce0250ff5c77.js +10 -0
  10. package/dist/client-v2/{70.a15d7fcec7c41768.js → 70.bda9518881c05360.js} +1 -1
  11. package/dist/client-v2/925.f5370de8f6632d65.js +10 -0
  12. package/dist/client-v2/index.js +1 -1
  13. package/dist/externalVersion.js +7 -10
  14. package/dist/locale/en-US.json +94 -25
  15. package/dist/locale/vi-VN.json +94 -25
  16. package/dist/locale/zh-CN.json +94 -25
  17. package/dist/server/collections/agent-execution-spans.js +37 -0
  18. package/dist/server/collections/agent-harness-profiles.js +2 -2
  19. package/dist/server/collections/agent-memory-contexts.js +125 -0
  20. package/dist/server/collections/orchestrator-logs.js +2 -2
  21. package/dist/server/migrations/20260425000000-add-interaction-schema.js +3 -1
  22. package/dist/server/migrations/20260427000000-change-packages-to-text.js +3 -1
  23. package/dist/server/migrations/20260427000001-change-other-json-to-text.js +6 -2
  24. package/dist/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.js +21 -19
  25. package/dist/server/migrations/20260621000000-native-policy-profile-defaults.js +193 -0
  26. package/dist/server/plugin.js +128 -74
  27. package/dist/server/resources/agent-monitor.js +454 -0
  28. package/dist/server/services/AgentHarness.js +24 -499
  29. package/dist/server/services/AgentMemoryContextService.js +216 -0
  30. package/dist/server/services/ExecutionSpanService.js +2 -2
  31. package/dist/server/services/NativeSubAgentObserver.js +413 -0
  32. package/dist/server/skill-hub/plugin.js +81 -5
  33. package/dist/server/skill-hub/tasks/SkillExecutionTask.js +9 -3
  34. package/dist/server/tools/delegate-task.js +11 -589
  35. package/dist/server/utils/skill-settings.js +18 -1
  36. package/package.json +47 -49
  37. package/src/client/AIEmployeesContext.tsx +5 -18
  38. package/src/client/AgentRunsTab.tsx +2 -771
  39. package/src/client/HarnessProfilesTab.tsx +2 -257
  40. package/src/client/OrchestratorSettings.tsx +97 -106
  41. package/src/client/RulesTab.tsx +2 -788
  42. package/src/client/plugin.tsx +0 -2
  43. package/src/client/skill-hub/components/ExecutionHistory.tsx +200 -202
  44. package/src/client/skill-hub/components/ExecutionProgress.tsx +51 -55
  45. package/src/client/skill-hub/components/LoopSettings.tsx +331 -331
  46. package/src/client/skill-hub/components/SkillEditor.tsx +43 -39
  47. package/src/client/skill-hub/components/SkillManager.tsx +194 -181
  48. package/src/client/skill-hub/components/SkillTestPanel.tsx +141 -145
  49. package/src/client/skill-hub/locale.ts +16 -16
  50. package/src/client/skill-hub/tools/SkillHubCard.tsx +104 -109
  51. package/src/client/skill-hub/tools/loopTemplates.ts +52 -52
  52. package/src/client/skill-hub/utils/jsonFields.ts +7 -3
  53. package/src/client-v2/components/AIEmployeesContext.tsx +3 -16
  54. package/src/client-v2/components/AgentRunsTab.tsx +182 -455
  55. package/src/client-v2/components/HarnessProfilesTab.tsx +34 -31
  56. package/src/client-v2/components/RulesTab.tsx +2 -782
  57. package/src/client-v2/components/TracingTab.tsx +1 -1
  58. package/src/client-v2/hooks/useApiRequest.ts +8 -1
  59. package/src/client-v2/pages/RulesPage.tsx +2 -2
  60. package/src/client-v2/plugin.tsx +3 -3
  61. package/src/locale/en-US.json +94 -25
  62. package/src/locale/vi-VN.json +94 -25
  63. package/src/locale/zh-CN.json +94 -25
  64. package/src/server/__tests__/native-sub-agent-observer.test.ts +246 -0
  65. package/src/server/__tests__/skill-settings.test.ts +6 -6
  66. package/src/server/__tests__/smoke.test.ts +1 -0
  67. package/src/server/collections/agent-execution-spans.ts +37 -0
  68. package/src/server/collections/agent-harness-profiles.ts +59 -59
  69. package/src/server/collections/agent-loop-events.ts +71 -71
  70. package/src/server/collections/agent-loop-steps.ts +144 -144
  71. package/src/server/collections/agent-memory-contexts.ts +95 -0
  72. package/src/server/collections/orchestrator-logs.ts +4 -4
  73. package/src/server/collections/skill-definitions.ts +111 -111
  74. package/src/server/collections/skill-executions.ts +106 -106
  75. package/src/server/collections/skill-loop-configs.ts +65 -65
  76. package/src/server/migrations/20260423000000-add-progress-fields.ts +14 -14
  77. package/src/server/migrations/20260425000000-add-interaction-schema.ts +3 -1
  78. package/src/server/migrations/20260427000000-change-packages-to-text.ts +4 -2
  79. package/src/server/migrations/20260427000001-change-other-json-to-text.ts +9 -5
  80. package/src/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.ts +30 -30
  81. package/src/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.ts +145 -142
  82. package/src/server/migrations/20260615000000-normalize-ai-employee-tool-bindings.ts +2 -2
  83. package/src/server/migrations/20260621000000-native-policy-profile-defaults.ts +193 -0
  84. package/src/server/plugin.ts +151 -94
  85. package/src/server/resources/agent-monitor.ts +482 -0
  86. package/src/server/services/AgentHarness.ts +38 -623
  87. package/src/server/services/AgentMemoryContextService.ts +256 -0
  88. package/src/server/services/AgentPlanValidator.ts +73 -73
  89. package/src/server/services/ExecutionSpanService.ts +6 -2
  90. package/src/server/services/FileManager.ts +144 -144
  91. package/src/server/services/NativeSubAgentObserver.ts +507 -0
  92. package/src/server/services/SkillManager.ts +583 -583
  93. package/src/server/services/SkillRepositoryService.ts +5 -7
  94. package/src/server/services/TokenTracker.ts +3 -3
  95. package/src/server/services/WorkerEnvManager.ts +1 -2
  96. package/src/server/skill-hub/actions/git-import.ts +5 -7
  97. package/src/server/skill-hub/plugin.ts +89 -6
  98. package/src/server/skill-hub/tasks/SkillExecutionTask.ts +470 -460
  99. package/src/server/skill-hub/utils/json-fields.ts +1 -1
  100. package/src/server/tools/delegate-task.ts +13 -847
  101. package/src/server/utils/skill-settings.ts +24 -6
  102. package/dist/client-v2/264.0533912e6c5ea2d7.js +0 -10
  103. package/dist/client-v2/418.5ae055abf141820e.js +0 -10
  104. package/dist/client-v2/619.d99d3c9e61c99064.js +0 -10
  105. package/dist/client-v2/892.72db4161511c8a16.js +0 -10
  106. package/dist/client-v2/926.87f660b670d85bcc.js +0 -10
  107. package/src/client/tools/PlanApprovalCard.tsx +0 -176
  108. package/src/client/tools/registerOrchestratorCards.ts +0 -17
@@ -25,7 +25,7 @@ export class SkillRepositoryService {
25
25
  */
26
26
  async extractSkillPackage(skillName: string, zipFilePath: string) {
27
27
  const targetDir = resolve(this.baseDir, skillName);
28
-
28
+
29
29
  // Clean target dir if exists
30
30
  if (existsSync(targetDir)) {
31
31
  rmSync(targetDir, { recursive: true, force: true });
@@ -100,8 +100,6 @@ export class SkillRepositoryService {
100
100
  });
101
101
  }
102
102
 
103
-
104
-
105
103
  private getSkillCodeFromDir(dir: string): string | null {
106
104
  if (existsSync(resolve(dir, 'index.py'))) {
107
105
  return readFileSync(resolve(dir, 'index.py'), 'utf8');
@@ -118,14 +116,14 @@ export class SkillRepositoryService {
118
116
 
119
117
  private aggregateOtherMarkdownFiles(dir: string, baseDir = dir): string {
120
118
  let combined = '';
121
-
119
+
122
120
  if (!existsSync(dir)) return combined;
123
-
121
+
124
122
  const items = readdirSync(dir);
125
123
  for (const item of items) {
126
124
  const fullPath = resolve(dir, item);
127
125
  const stat = statSync(fullPath);
128
-
126
+
129
127
  if (stat.isDirectory()) {
130
128
  if (item !== 'node_modules' && item !== '.git') {
131
129
  combined += this.aggregateOtherMarkdownFiles(fullPath, baseDir);
@@ -136,7 +134,7 @@ export class SkillRepositoryService {
136
134
  combined += `\n\n--- Content from ${relPath} ---\n\n${content}`;
137
135
  }
138
136
  }
139
-
137
+
140
138
  return combined;
141
139
  }
142
140
  }
@@ -32,14 +32,14 @@ function estimateCost(inputTokens: number, outputTokens: number): number {
32
32
  }
33
33
 
34
34
  /**
35
- * Extract usage_metadata from the final state returned by createReactAgent.invoke().
35
+ * Extract usage_metadata from a legacy agent final state.
36
36
  *
37
- * LangChain's createReactAgent returns a final state object with a `messages` array.
37
+ * Legacy agent execution returned a final state object with a `messages` array.
38
38
  * The last AIMessage in that array carries `usage_metadata` populated by the
39
39
  * LLM provider after the final generation step. This is the standard LangChain
40
40
  * approach — no private API access needed.
41
41
  *
42
- * Expected shape (from @langchain/core/messages/ai):
42
+ * Expected message shape:
43
43
  * AIMessage.usage_metadata = {
44
44
  * input_tokens: number,
45
45
  * output_tokens: number,
@@ -101,8 +101,7 @@ export class WorkerEnvManager {
101
101
 
102
102
  await (this as any).app.pubSubManager.publish('skill-hub.init-env.done', {
103
103
  status: 'succeeded',
104
- log:
105
- 'Sandbox environment is ready on this worker. Package installation is managed by the worker image/runtime; whitelist was refreshed.',
104
+ log: 'Sandbox environment is ready on this worker. Package installation is managed by the worker image/runtime; whitelist was refreshed.',
106
105
  whitelist,
107
106
  });
108
107
  }
@@ -162,7 +162,7 @@ export async function gitSyncSkills(ctx: Context, next: () => Promise<void>) {
162
162
  const parsed = parseSkillMarkdown(skillMd);
163
163
  frontmatter = parsed.metadata;
164
164
  instructions = parsed.body;
165
-
165
+
166
166
  const otherMds = await fetchAllMarkdownInFolder(git, ref, skillBaseDir);
167
167
  if (otherMds) instructions += otherMds;
168
168
  } catch {
@@ -422,8 +422,6 @@ function parseBoolean(value: any, fallback: boolean) {
422
422
  return Boolean(value);
423
423
  }
424
424
 
425
-
426
-
427
425
  function extractStringProperty(source: string, property: string) {
428
426
  const match = source.match(new RegExp(`${property}\\s*:\\s*(['"\`])([\\s\\S]*?)\\1`));
429
427
  return match ? match[2] : null;
@@ -470,7 +468,7 @@ async function fetchAllMarkdownInFolder(git: any, ref: string, folderPath: strin
470
468
  try {
471
469
  const filesOut = await git.raw(['ls-tree', '-r', '--name-only', `${ref}:${folderPath}/`]);
472
470
  const files = filesOut.trim().split('\n').filter(Boolean);
473
-
471
+
474
472
  for (const file of files) {
475
473
  if (file.toLowerCase().endsWith('.md') && file.toUpperCase() !== 'SKILL.md') {
476
474
  const content = await readOptionalGitFile(git, ref, joinGitPath(folderPath, file));
@@ -479,8 +477,8 @@ async function fetchAllMarkdownInFolder(git: any, ref: string, folderPath: strin
479
477
  }
480
478
  }
481
479
  }
482
- } catch (err) {}
480
+ } catch (err) {
481
+ // Optional folder expansion can fail for missing paths or refs; keep import best-effort.
482
+ }
483
483
  return combined;
484
484
  }
485
-
486
-
@@ -16,6 +16,8 @@ import { tryGetAIToolsManager } from '../utils/ai-manager';
16
16
  import type { ToolsRuntime } from '@nocobase/ai';
17
17
 
18
18
  type ToolRuntimeInput = string | ToolsRuntime | undefined;
19
+ const SKILL_TASK_POLL_INTERVAL_MS = Number.parseInt(process.env.SKILL_HUB_TASK_POLL_INTERVAL_MS || '5000', 10);
20
+ const SKILL_TASK_POLL_LIMIT = Number.parseInt(process.env.SKILL_HUB_TASK_POLL_LIMIT || '3', 10);
19
21
 
20
22
  function normalizeToolRuntime(runtime: ToolRuntimeInput): ToolsRuntime | undefined {
21
23
  if (!runtime) return undefined;
@@ -100,6 +102,10 @@ export class SkillHubSubFeature {
100
102
  private cleanupInterval: ReturnType<typeof setInterval> | null = null;
101
103
  private initEnvDoneCallback: any = null;
102
104
  private initEnvProgressCallback: any = null;
105
+ private skillTaskCallback: any = null;
106
+ private initEnvTaskCallback: any = null;
107
+ private skillTaskPoller: ReturnType<typeof setInterval> | null = null;
108
+ private skillTaskPolling = false;
103
109
  private mcpController: McpController;
104
110
  private skillRepoService: SkillRepositoryService;
105
111
  private rateLimiter = new RateLimiter(
@@ -255,21 +261,24 @@ export class SkillHubSubFeature {
255
261
  });
256
262
 
257
263
  // 5. Subscribe PubSub — worker processes skill execution tasks
258
- (this as any).app.pubSubManager.subscribe('skill-hub.task', async (payload: any) => {
264
+ this.skillTaskCallback = async (payload: any) => {
259
265
  if (process.env.SKILL_HUB_SANDBOX === 'false') return;
260
266
  await this.onQueueTask(payload);
261
- });
267
+ };
268
+ (this as any).app.pubSubManager.subscribe('skill-hub.task', this.skillTaskCallback);
262
269
 
263
270
  // 5b. Subscribe PubSub — worker processes init-env tasks
264
- (this as any).app.pubSubManager.subscribe('skill-hub.init-env', async (payload: any) => {
271
+ this.initEnvTaskCallback = async (payload: any) => {
265
272
  if (process.env.SKILL_HUB_SANDBOX === 'false') return;
266
273
  await this.workerEnvManager.executeInit(payload);
267
- });
274
+ };
275
+ (this as any).app.pubSubManager.subscribe('skill-hub.init-env', this.initEnvTaskCallback);
268
276
 
269
277
  // 6. Register AI tools + subscriptions (deferred — after all plugins loaded)
270
278
  (this as any).app.on('afterStart', async () => {
271
279
  this.registerAITools();
272
280
  this.startCleanupInterval();
281
+ this.startSkillTaskPoller();
273
282
  await this.subscribeInitEnvDone();
274
283
  // Ensure any newly added built-in skills are seeded automatically on upgrade/restart
275
284
  await this.skillManager.seedDefaults().catch((e) => {
@@ -280,6 +289,15 @@ export class SkillHubSubFeature {
280
289
 
281
290
  private async onQueueTask(message: { id: string }) {
282
291
  (this as any).app.logger.info(`[skill-hub] Worker received queue task: ${message.id}`);
292
+ if (process.env.SKILL_HUB_SANDBOX === 'false') {
293
+ return;
294
+ }
295
+ const claimed = await this.claimSkillExecution(message.id);
296
+ if (!claimed) {
297
+ (this as any).app.logger.debug(`[skill-hub] Task ${message.id} ignored: already claimed or not pending.`);
298
+ return;
299
+ }
300
+
283
301
  const execution = await (this as any).db.getRepository('skillExecutions').findOne({
284
302
  filter: { id: message.id },
285
303
  appends: ['skill'],
@@ -299,9 +317,56 @@ export class SkillHubSubFeature {
299
317
  await task.run();
300
318
  }
301
319
 
320
+ private async claimSkillExecution(id: string): Promise<boolean> {
321
+ const model = (this as any).db.getModel('skillExecutions');
322
+ const [affected] = await model.update(
323
+ { status: 'running' },
324
+ {
325
+ where: {
326
+ id,
327
+ status: 'pending',
328
+ },
329
+ },
330
+ );
331
+ return affected > 0;
332
+ }
333
+
334
+ private startSkillTaskPoller() {
335
+ if (process.env.SKILL_HUB_SANDBOX === 'false' || this.skillTaskPoller) {
336
+ return;
337
+ }
338
+
339
+ const tick = () => {
340
+ this.processPendingSkillExecutions().catch((error) => {
341
+ (this as any).app.logger.warn(`[skill-hub] Pending task poll failed: ${error?.message || error}`);
342
+ });
343
+ };
344
+
345
+ this.skillTaskPoller = setInterval(tick, SKILL_TASK_POLL_INTERVAL_MS);
346
+ (this.skillTaskPoller as any).unref?.();
347
+ setTimeout(tick, 1000);
348
+ }
349
+
350
+ private async processPendingSkillExecutions() {
351
+ if (this.skillTaskPolling) return;
352
+ this.skillTaskPolling = true;
353
+ try {
354
+ const records = await (this as any).db.getRepository('skillExecutions').find({
355
+ filter: { status: 'pending' },
356
+ sort: ['createdAt'],
357
+ limit: SKILL_TASK_POLL_LIMIT,
358
+ });
359
+ for (const record of records) {
360
+ await this.onQueueTask({ id: String(record.get('id')) });
361
+ }
362
+ } finally {
363
+ this.skillTaskPolling = false;
364
+ }
365
+ }
366
+
302
367
  /**
303
368
  * Execute skill — called by both AI tool and REST test endpoint.
304
- * Dispatches to worker via EventQueue, waits for result via PubSub.
369
+ * Dispatches to sandbox workers via PubSub, waits for result via PubSub.
305
370
  * Pushes progress to SSE via runtime.writer (if within AI tool context).
306
371
  * Includes rate limiting and graceful abort propagation.
307
372
  */
@@ -408,7 +473,7 @@ export class SkillHubSubFeature {
408
473
  });
409
474
  }
410
475
 
411
- // NOW Dispatch to worker via EventQueue
476
+ // NOW dispatch to sandbox workers.
412
477
  await (this as any).app.pubSubManager.publish('skill-hub.task', { id: execId });
413
478
 
414
479
  // Wait for completion
@@ -759,6 +824,20 @@ export class SkillHubSubFeature {
759
824
 
760
825
  async beforeStop() {
761
826
  // Unsubscribe PubSub
827
+ if (this.skillTaskCallback) {
828
+ try {
829
+ await (this as any).app.pubSubManager.unsubscribe('skill-hub.task', this.skillTaskCallback);
830
+ } catch {
831
+ /* ignore */
832
+ }
833
+ }
834
+ if (this.initEnvTaskCallback) {
835
+ try {
836
+ await (this as any).app.pubSubManager.unsubscribe('skill-hub.init-env', this.initEnvTaskCallback);
837
+ } catch {
838
+ /* ignore */
839
+ }
840
+ }
762
841
  if (this.initEnvDoneCallback) {
763
842
  try {
764
843
  await (this as any).app.pubSubManager.unsubscribe('skill-hub.init-env.done', this.initEnvDoneCallback);
@@ -779,6 +858,10 @@ export class SkillHubSubFeature {
779
858
  clearInterval(this.cleanupInterval);
780
859
  this.cleanupInterval = null;
781
860
  }
861
+ if (this.skillTaskPoller) {
862
+ clearInterval(this.skillTaskPoller);
863
+ this.skillTaskPoller = null;
864
+ }
782
865
  }
783
866
 
784
867
  // --- Handlers ---