plugin-agent-orchestrator 1.0.16 → 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 (92) hide show
  1. package/dist/client/AgentRunsTab.d.ts +2 -0
  2. package/dist/client/HarnessProfilesTab.d.ts +2 -0
  3. package/dist/client/index.js +1 -1
  4. package/dist/client/skill-hub/components/LoopSettings.d.ts +2 -0
  5. package/dist/client/skill-hub/index.d.ts +2 -1
  6. package/dist/client/skill-hub/tools/InteractionSchemasProvider.d.ts +1 -14
  7. package/dist/client/skill-hub/tools/loopTemplates.d.ts +22 -0
  8. package/dist/client/skill-hub/tools/registerSkillLoopCards.d.ts +1 -0
  9. package/dist/client/tools/PlanApprovalCard.d.ts +3 -0
  10. package/dist/client/tools/registerOrchestratorCards.d.ts +1 -0
  11. package/dist/externalVersion.js +6 -6
  12. package/dist/server/collections/agent-harness-profiles.d.ts +2 -0
  13. package/dist/server/collections/agent-harness-profiles.js +89 -0
  14. package/dist/server/collections/agent-loop-events.d.ts +2 -0
  15. package/dist/server/collections/agent-loop-events.js +101 -0
  16. package/dist/server/collections/agent-loop-runs.d.ts +2 -0
  17. package/dist/server/collections/agent-loop-runs.js +188 -0
  18. package/dist/server/collections/agent-loop-steps.d.ts +2 -0
  19. package/dist/server/collections/agent-loop-steps.js +174 -0
  20. package/dist/server/collections/orchestrator-config.js +7 -0
  21. package/dist/server/collections/skill-executions.js +12 -0
  22. package/dist/server/collections/skill-loop-configs.d.ts +3 -0
  23. package/dist/server/collections/skill-loop-configs.js +94 -0
  24. package/dist/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.d.ts +7 -0
  25. package/dist/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.js +55 -0
  26. package/dist/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.d.ts +12 -0
  27. package/dist/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.js +162 -0
  28. package/dist/server/plugin.d.ts +2 -0
  29. package/dist/server/plugin.js +13 -0
  30. package/dist/server/resources/agent-loop.d.ts +3 -0
  31. package/dist/server/resources/agent-loop.js +205 -0
  32. package/dist/server/services/AgentHarness.d.ts +42 -0
  33. package/dist/server/services/AgentHarness.js +565 -0
  34. package/dist/server/services/AgentLoopController.d.ts +205 -0
  35. package/dist/server/services/AgentLoopController.js +940 -0
  36. package/dist/server/services/AgentLoopRepository.d.ts +20 -0
  37. package/dist/server/services/AgentLoopRepository.js +210 -0
  38. package/dist/server/services/AgentLoopService.d.ts +149 -0
  39. package/dist/server/services/AgentLoopService.js +133 -0
  40. package/dist/server/services/AgentPlanValidator.d.ts +4 -0
  41. package/dist/server/services/AgentPlanValidator.js +99 -0
  42. package/dist/server/services/AgentPlannerService.d.ts +8 -0
  43. package/dist/server/services/AgentPlannerService.js +119 -0
  44. package/dist/server/services/AgentRegistryService.d.ts +13 -0
  45. package/dist/server/services/AgentRegistryService.js +178 -0
  46. package/dist/server/services/ExecutionSpanService.d.ts +2 -0
  47. package/dist/server/skill-hub/plugin.d.ts +3 -0
  48. package/dist/server/skill-hub/plugin.js +137 -54
  49. package/dist/server/tools/agent-loop.d.ts +235 -0
  50. package/dist/server/tools/agent-loop.js +406 -0
  51. package/dist/server/tools/delegate-task.js +37 -350
  52. package/dist/server/tools/orchestrator-plan.d.ts +205 -0
  53. package/dist/server/tools/orchestrator-plan.js +291 -0
  54. package/dist/server/tools/skill-execute.js +2 -0
  55. package/package.json +2 -2
  56. package/src/client/AgentRunsTab.tsx +764 -0
  57. package/src/client/HarnessProfilesTab.tsx +247 -0
  58. package/src/client/OrchestratorSettings.tsx +40 -2
  59. package/src/client/RulesTab.tsx +103 -6
  60. package/src/client/plugin.tsx +27 -54
  61. package/src/client/skill-hub/components/LoopSettings.tsx +331 -0
  62. package/src/client/skill-hub/index.tsx +51 -75
  63. package/src/client/skill-hub/tools/InteractionSchemasProvider.tsx +56 -16
  64. package/src/client/skill-hub/tools/SkillHubCard.tsx +35 -4
  65. package/src/client/skill-hub/tools/loopTemplates.ts +52 -0
  66. package/src/client/skill-hub/tools/registerSkillLoopCards.ts +58 -0
  67. package/src/client/tools/PlanApprovalCard.tsx +175 -0
  68. package/src/client/tools/registerOrchestratorCards.ts +7 -0
  69. package/src/server/collections/agent-harness-profiles.ts +59 -0
  70. package/src/server/collections/agent-loop-events.ts +71 -0
  71. package/src/server/collections/agent-loop-runs.ts +158 -0
  72. package/src/server/collections/agent-loop-steps.ts +144 -0
  73. package/src/server/collections/orchestrator-config.ts +7 -0
  74. package/src/server/collections/skill-executions.ts +63 -51
  75. package/src/server/collections/skill-loop-configs.ts +65 -0
  76. package/src/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.ts +30 -0
  77. package/src/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.ts +142 -0
  78. package/src/server/plugin.ts +15 -0
  79. package/src/server/resources/agent-loop.ts +183 -0
  80. package/src/server/services/AgentHarness.ts +663 -0
  81. package/src/server/services/AgentLoopController.ts +1128 -0
  82. package/src/server/services/AgentLoopRepository.ts +194 -0
  83. package/src/server/services/AgentLoopService.ts +161 -0
  84. package/src/server/services/AgentPlanValidator.ts +73 -0
  85. package/src/server/services/AgentPlannerService.ts +93 -0
  86. package/src/server/services/AgentRegistryService.ts +169 -0
  87. package/src/server/services/ExecutionSpanService.ts +2 -0
  88. package/src/server/skill-hub/plugin.ts +881 -771
  89. package/src/server/tools/agent-loop.ts +399 -0
  90. package/src/server/tools/delegate-task.ts +48 -463
  91. package/src/server/tools/orchestrator-plan.ts +279 -0
  92. package/src/server/tools/skill-execute.ts +68 -64
@@ -0,0 +1,119 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var AgentPlannerService_exports = {};
28
+ __export(AgentPlannerService_exports, {
29
+ AgentPlannerService: () => AgentPlannerService
30
+ });
31
+ module.exports = __toCommonJS(AgentPlannerService_exports);
32
+ function normalizeStepType(value) {
33
+ return ["reasoning", "skill", "tool", "sub_agent", "verification"].includes(value) ? value : "tool";
34
+ }
35
+ function normalizePlanKey(step, index) {
36
+ return String(step.planKey || step.key || step.id || `step_${index + 1}`);
37
+ }
38
+ function asArray(value) {
39
+ return Array.isArray(value) ? value : [];
40
+ }
41
+ function asObject(value) {
42
+ if (value && typeof value === "object" && !Array.isArray(value)) return value;
43
+ if (typeof value === "string" && value.trim()) {
44
+ try {
45
+ const parsed = JSON.parse(value);
46
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
47
+ } catch {
48
+ return {};
49
+ }
50
+ }
51
+ return {};
52
+ }
53
+ class AgentPlannerService {
54
+ buildPlan(goal, plan, options) {
55
+ var _a;
56
+ if (Array.isArray(plan) && plan.length > 0) {
57
+ return plan.map((step, index) => {
58
+ var _a2;
59
+ return {
60
+ ...step,
61
+ planKey: normalizePlanKey(step, index),
62
+ type: normalizeStepType(step.type),
63
+ dependsOn: asArray(step.dependsOn).map(String),
64
+ metadata: {
65
+ ...asObject(step.metadata),
66
+ harnessTag: options.harnessTag || ((_a2 = options.metadata) == null ? void 0 : _a2.harnessTag) || "default"
67
+ }
68
+ };
69
+ });
70
+ }
71
+ const harnessTag = options.harnessTag || ((_a = options.metadata) == null ? void 0 : _a.harnessTag) || "default";
72
+ const steps = [
73
+ {
74
+ planKey: "prepare_context",
75
+ title: "Prepare execution context",
76
+ description: "Review the user goal, available context, and constraints before execution.",
77
+ type: "reasoning",
78
+ input: { goal },
79
+ metadata: { harnessTag }
80
+ }
81
+ ];
82
+ if (options.targetAgent) {
83
+ steps.push({
84
+ planKey: "delegate_execution",
85
+ title: `Delegate execution to ${options.targetAgent}`,
86
+ description: goal,
87
+ type: "sub_agent",
88
+ target: options.targetAgent,
89
+ input: { goal },
90
+ dependsOn: ["prepare_context"],
91
+ metadata: { harnessTag }
92
+ });
93
+ } else {
94
+ steps.push({
95
+ planKey: "execute_goal",
96
+ title: "Execute approved goal",
97
+ description: "Execute the user goal in the leader harness using the available approved tools.",
98
+ type: "reasoning",
99
+ input: { goal },
100
+ dependsOn: ["prepare_context"],
101
+ metadata: { harnessTag, controllerOnly: true }
102
+ });
103
+ }
104
+ steps.push({
105
+ planKey: "verify_result",
106
+ title: "Verify result",
107
+ description: "Check that the completed work matches the approved goal and report evidence.",
108
+ type: "verification",
109
+ input: { goal },
110
+ dependsOn: [steps[steps.length - 1].planKey || "execute_goal"],
111
+ metadata: { harnessTag }
112
+ });
113
+ return steps;
114
+ }
115
+ }
116
+ // Annotate the CommonJS export names for ESM import in node:
117
+ 0 && (module.exports = {
118
+ AgentPlannerService
119
+ });
@@ -0,0 +1,13 @@
1
+ export declare class AgentRegistryService {
2
+ private readonly plugin;
3
+ constructor(plugin: any);
4
+ get db(): any;
5
+ getHarnessProfile(tag: string): Promise<any>;
6
+ getOrchestratorConfig(leaderUsername: string, subAgentUsername: string): Promise<any>;
7
+ getAIEmployee(username: string): Promise<any>;
8
+ resolveModelSettings(subAgentUsername: string, leaderUsername?: string, dynamicValues?: any): Promise<{
9
+ llmService: string;
10
+ model: string;
11
+ }>;
12
+ isRegisteredDelegationTool(toolName: string): Promise<boolean>;
13
+ }
@@ -0,0 +1,178 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var AgentRegistryService_exports = {};
28
+ __export(AgentRegistryService_exports, {
29
+ AgentRegistryService: () => AgentRegistryService
30
+ });
31
+ module.exports = __toCommonJS(AgentRegistryService_exports);
32
+ function toPlain(record) {
33
+ var _a;
34
+ return ((_a = record == null ? void 0 : record.toJSON) == null ? void 0 : _a.call(record)) || record;
35
+ }
36
+ function asObject(value) {
37
+ if (value && typeof value === "object" && !Array.isArray(value)) return value;
38
+ if (typeof value === "string" && value.trim()) {
39
+ try {
40
+ const parsed = JSON.parse(value);
41
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
42
+ } catch {
43
+ return {};
44
+ }
45
+ }
46
+ return {};
47
+ }
48
+ function normalizeEmployeeUsername(raw) {
49
+ if (!raw) return null;
50
+ if (typeof raw === "string") return raw;
51
+ return raw.username || raw.aiEmployeeUsername || raw.name || null;
52
+ }
53
+ class AgentRegistryService {
54
+ constructor(plugin) {
55
+ this.plugin = plugin;
56
+ }
57
+ get db() {
58
+ return this.plugin.db;
59
+ }
60
+ async getHarnessProfile(tag) {
61
+ try {
62
+ const repo = this.db.getRepository("agentHarnessProfiles");
63
+ if (!repo) return null;
64
+ const profile = await repo.findOne({
65
+ filter: {
66
+ tag: tag || "default",
67
+ enabled: true
68
+ }
69
+ });
70
+ return profile ? toPlain(profile) : null;
71
+ } catch {
72
+ return null;
73
+ }
74
+ }
75
+ async getOrchestratorConfig(leaderUsername, subAgentUsername) {
76
+ try {
77
+ const repo = this.db.getRepository("orchestratorConfig");
78
+ if (!repo) return null;
79
+ const config = await repo.findOne({
80
+ filter: {
81
+ leaderUsername,
82
+ subAgentUsername,
83
+ enabled: true
84
+ }
85
+ });
86
+ return config ? toPlain(config) : null;
87
+ } catch {
88
+ return null;
89
+ }
90
+ }
91
+ async getAIEmployee(username) {
92
+ try {
93
+ const repo = this.db.getRepository("aiEmployees");
94
+ if (!repo) return null;
95
+ const employee = await repo.findOne({
96
+ filter: { username }
97
+ });
98
+ return employee ? toPlain(employee) : null;
99
+ } catch {
100
+ return null;
101
+ }
102
+ }
103
+ async resolveModelSettings(subAgentUsername, leaderUsername, dynamicValues) {
104
+ const subAgent = await this.getAIEmployee(subAgentUsername);
105
+ if (!subAgent) {
106
+ throw new Error(`Sub-agent "${subAgentUsername}" was not found.`);
107
+ }
108
+ const hasModelSettings = (val) => {
109
+ return Boolean((val == null ? void 0 : val.llmService) && (val == null ? void 0 : val.model));
110
+ };
111
+ let modelSettings = hasModelSettings(dynamicValues) ? dynamicValues : void 0;
112
+ if (!modelSettings) {
113
+ if (hasModelSettings(subAgent.modelSettings)) {
114
+ modelSettings = subAgent.modelSettings;
115
+ }
116
+ }
117
+ if (!modelSettings && leaderUsername) {
118
+ const leader = await this.getAIEmployee(leaderUsername);
119
+ if (leader && hasModelSettings(leader.modelSettings)) {
120
+ modelSettings = leader.modelSettings;
121
+ }
122
+ }
123
+ return modelSettings;
124
+ }
125
+ async isRegisteredDelegationTool(toolName) {
126
+ if (!toolName || typeof toolName !== "string") return false;
127
+ if (!toolName.startsWith("delegate_") && !toolName.startsWith("dispatch_subagents_")) {
128
+ return false;
129
+ }
130
+ try {
131
+ const configRepo = this.db.getRepository("orchestratorConfig");
132
+ if (!configRepo) return false;
133
+ if (toolName.startsWith("dispatch_subagents_")) {
134
+ const leader = toolName.substring("dispatch_subagents_".length);
135
+ const count = await configRepo.count({
136
+ filter: {
137
+ leaderUsername: leader,
138
+ enabled: true
139
+ }
140
+ });
141
+ return count > 0;
142
+ }
143
+ if (toolName.startsWith("delegate_") && toolName.includes("_to_")) {
144
+ const parts = toolName.substring("delegate_".length).split("_to_");
145
+ if (parts.length === 2) {
146
+ const [leader, subAgent] = parts;
147
+ const count = await configRepo.count({
148
+ filter: {
149
+ leaderUsername: leader,
150
+ subAgentUsername: subAgent,
151
+ enabled: true
152
+ }
153
+ });
154
+ if (count > 0) return true;
155
+ }
156
+ }
157
+ if (toolName.startsWith("delegate_to_")) {
158
+ const subAgent = toolName.substring("delegate_to_".length);
159
+ const configs = await configRepo.find({
160
+ filter: {
161
+ subAgentUsername: subAgent,
162
+ enabled: true
163
+ }
164
+ });
165
+ if ((configs == null ? void 0 : configs.length) === 1) {
166
+ return true;
167
+ }
168
+ }
169
+ return false;
170
+ } catch {
171
+ return false;
172
+ }
173
+ }
174
+ }
175
+ // Annotate the CommonJS export names for ESM import in node:
176
+ 0 && (module.exports = {
177
+ AgentRegistryService
178
+ });
@@ -7,6 +7,8 @@ export type OrchestratorTraceContext = {
7
7
  leaderUsername?: string;
8
8
  employeeUsername?: string;
9
9
  toolName?: string;
10
+ agentLoopRunId?: string;
11
+ agentLoopStepId?: string;
10
12
  };
11
13
  type SpanValues = {
12
14
  rootRunId: string;
@@ -40,6 +40,9 @@ export declare class SkillHubSubFeature {
40
40
  * When a worker finishes init, auto-update the DB with status.
41
41
  */
42
42
  private subscribeInitEnvDone;
43
+ private getSkillRecordId;
44
+ private resolveLoopInteractionSchema;
45
+ private getLoopConfigsBySkillId;
43
46
  private registerAITools;
44
47
  private startCleanupInterval;
45
48
  beforeStop(): Promise<void>;
@@ -53,6 +53,7 @@ var import_McpController = require("./mcp/McpController");
53
53
  var import_SkillRepositoryService = require("../services/SkillRepositoryService");
54
54
  var import_git_import = require("./actions/git-import");
55
55
  var import_json_fields = require("./utils/json-fields");
56
+ var import_ExecutionSpanService = require("../services/ExecutionSpanService");
56
57
  class RateLimiter {
57
58
  constructor(maxExecutions = 10, windowMs = 60 * 1e3) {
58
59
  this.maxExecutions = maxExecutions;
@@ -77,9 +78,7 @@ class RateLimiter {
77
78
  /** Get remaining executions for a user */
78
79
  remaining(userId) {
79
80
  const now = Date.now();
80
- const executions = (this.userExecutions.get(userId) || []).filter(
81
- (t) => now - t < this.windowMs
82
- );
81
+ const executions = (this.userExecutions.get(userId) || []).filter((t) => now - t < this.windowMs);
83
82
  return Math.max(0, this.maxExecutions - executions.length);
84
83
  }
85
84
  /** Periodically clean up expired entries (call from interval) */
@@ -152,7 +151,9 @@ class SkillHubSubFeature {
152
151
  require("fs").rmSync(dir, { recursive: true, force: true });
153
152
  }
154
153
  } catch (err) {
155
- this.app.logger.error(`[skill-hub] Failed to cleanup physical storage for execId ${execId}`, { error: err });
154
+ this.app.logger.error(`[skill-hub] Failed to cleanup physical storage for execId ${execId}`, {
155
+ error: err
156
+ });
156
157
  }
157
158
  });
158
159
  this.db.on("skillDefinitions.afterSave", async (model, options) => {
@@ -170,29 +171,39 @@ class SkillHubSubFeature {
170
171
  }
171
172
  const streamData = await fileManager.getFileStream(attachment);
172
173
  if (!streamData || !streamData.stream) {
173
- this.app.logger.warn(`[skill-hub] Could not get file stream for attachment ${attachment.get("id")}`);
174
+ this.app.logger.warn(
175
+ `[skill-hub] Could not get file stream for attachment ${attachment.get("id")}`
176
+ );
174
177
  return;
175
178
  }
176
179
  const tempZipPath = (0, import_path.resolve)(os.tmpdir(), `skill_${Date.now()}_${model.get("id")}.zip`);
177
- await new Promise((resolvePipe, rejectPipe) => {
180
+ await new Promise((resolve2, reject) => {
178
181
  const writeStream = (0, import_fs.createWriteStream)(tempZipPath);
179
182
  streamData.stream.pipe(writeStream);
180
- writeStream.on("finish", resolvePipe);
181
- writeStream.on("error", rejectPipe);
182
- streamData.stream.on("error", rejectPipe);
183
+ writeStream.on("finish", resolve2);
184
+ writeStream.on("error", reject);
185
+ streamData.stream.on("error", reject);
183
186
  });
184
187
  if (require("fs").existsSync(tempZipPath)) {
185
188
  const skillName = model.get("name");
186
- const { metadata, instructions } = await this.skillRepoService.extractSkillPackage(skillName, tempZipPath);
189
+ const { metadata, instructions } = await this.skillRepoService.extractSkillPackage(
190
+ skillName,
191
+ tempZipPath
192
+ );
187
193
  const code = this.skillRepoService.getSkillCode(skillName);
188
- const updateValues = { storageType: attachment.get("storageId") ? `storage-${attachment.get("storageId")}` : "local" };
194
+ const updateValues = {
195
+ storageType: attachment.get("storageId") ? `storage-${attachment.get("storageId")}` : "local"
196
+ };
189
197
  if (code) updateValues.codeTemplate = code;
190
198
  if (metadata.description) updateValues.description = metadata.description;
191
199
  if (metadata.title) updateValues.title = metadata.title;
192
200
  if (metadata.language) updateValues.language = metadata.language;
193
- if (metadata.inputSchema) updateValues.inputSchema = (0, import_json_fields.stringifyJsonText)((0, import_json_fields.parseJsonLike)(metadata.inputSchema, null));
194
- if (metadata.interactionSchema) updateValues.interactionSchema = (0, import_json_fields.stringifyJsonText)((0, import_json_fields.parseJsonLike)(metadata.interactionSchema, null));
195
- if (metadata.packages) updateValues.packages = (0, import_json_fields.stringifyJsonText)((0, import_json_fields.parseJsonLike)(metadata.packages, []), []);
201
+ if (metadata.inputSchema)
202
+ updateValues.inputSchema = (0, import_json_fields.stringifyJsonText)((0, import_json_fields.parseJsonLike)(metadata.inputSchema, null));
203
+ if (metadata.interactionSchema)
204
+ updateValues.interactionSchema = (0, import_json_fields.stringifyJsonText)((0, import_json_fields.parseJsonLike)(metadata.interactionSchema, null));
205
+ if (metadata.packages)
206
+ updateValues.packages = (0, import_json_fields.stringifyJsonText)((0, import_json_fields.parseJsonLike)(metadata.packages, []), []);
196
207
  if (metadata.timeoutSeconds) updateValues.timeoutSeconds = metadata.timeoutSeconds;
197
208
  if (instructions) updateValues.instructions = instructions;
198
209
  await this.db.getRepository("skillDefinitions").update({
@@ -262,12 +273,19 @@ class SkillHubSubFeature {
262
273
  );
263
274
  }
264
275
  }
276
+ const traceContext = (0, import_ExecutionSpanService.getOrchestratorTraceContext)(ctx);
265
277
  const execution = await this.db.getRepository("skillExecutions").create({
266
278
  values: {
267
279
  skillId: skill.id,
268
280
  status: "pending",
269
281
  inputArgs: (0, import_json_fields.stringifyJsonText)(inputArgs, {}),
270
282
  sessionId: (_c = ctx == null ? void 0 : ctx.state) == null ? void 0 : _c.sessionId,
283
+ orchestratorRootRunId: traceContext == null ? void 0 : traceContext.rootRunId,
284
+ orchestratorSpanId: traceContext == null ? void 0 : traceContext.spanId,
285
+ orchestratorParentSpanId: traceContext == null ? void 0 : traceContext.parentSpanId,
286
+ orchestratorToolCallId: traceContext == null ? void 0 : traceContext.toolCallId,
287
+ agentLoopRunId: traceContext == null ? void 0 : traceContext.agentLoopRunId,
288
+ agentLoopStepId: traceContext == null ? void 0 : traceContext.agentLoopStepId,
271
289
  triggeredById: (_e = (_d = ctx == null ? void 0 : ctx.state) == null ? void 0 : _d.currentUser) == null ? void 0 : _e.id
272
290
  }
273
291
  });
@@ -340,7 +358,13 @@ class SkillHubSubFeature {
340
358
  downloadUrl: `/api/skillHub:download?execId=${execId}&f=${b64name}`
341
359
  };
342
360
  });
343
- return { ...result, files: filesWithUrls, execId };
361
+ return {
362
+ ...result,
363
+ files: filesWithUrls,
364
+ execId,
365
+ agentLoopRunId: traceContext == null ? void 0 : traceContext.agentLoopRunId,
366
+ agentLoopStepId: traceContext == null ? void 0 : traceContext.agentLoopStepId
367
+ };
344
368
  }
345
369
  async handleDownload(ctx, next) {
346
370
  var _a, _b;
@@ -455,6 +479,49 @@ class SkillHubSubFeature {
455
479
  await this.app.pubSubManager.subscribe("skill-hub.init-env.done", this.initEnvDoneCallback);
456
480
  await this.app.pubSubManager.subscribe("skill-hub.init-env.progress", this.initEnvProgressCallback);
457
481
  }
482
+ getSkillRecordId(skill) {
483
+ return String(skill.get ? skill.get("id") : skill.id);
484
+ }
485
+ resolveLoopInteractionSchema(loopConfig) {
486
+ if (!loopConfig) return null;
487
+ const schema = (0, import_json_fields.parseJsonText)(loopConfig.get ? loopConfig.get("schema") : loopConfig.schema, null);
488
+ const prompt = loopConfig.get ? loopConfig.get("prompt") : loopConfig.prompt;
489
+ if (schema && typeof schema === "object") {
490
+ return prompt && !schema.prompt ? { ...schema, prompt } : schema;
491
+ }
492
+ if (prompt) {
493
+ return {
494
+ type: "confirm",
495
+ prompt
496
+ };
497
+ }
498
+ return null;
499
+ }
500
+ async getLoopConfigsBySkillId(skillIds) {
501
+ const ids = Array.from(new Set(skillIds.map((id) => String(id)).filter(Boolean)));
502
+ const configsBySkillId = /* @__PURE__ */ new Map();
503
+ if (!ids.length) return configsBySkillId;
504
+ try {
505
+ const configs = await this.db.getRepository("skillLoopConfigs").find({
506
+ filter: {
507
+ enabled: true,
508
+ skillId: {
509
+ $in: ids
510
+ }
511
+ },
512
+ sort: ["-updatedAt"]
513
+ });
514
+ for (const config of configs || []) {
515
+ const skillId = String(config.get ? config.get("skillId") : config.skillId);
516
+ if (!configsBySkillId.has(skillId)) {
517
+ configsBySkillId.set(skillId, config);
518
+ }
519
+ }
520
+ } catch (err) {
521
+ this.app.logger.warn("[skill-hub] Failed to load loop configs", err);
522
+ }
523
+ return configsBySkillId;
524
+ }
458
525
  registerAITools() {
459
526
  var _a;
460
527
  try {
@@ -470,45 +537,56 @@ class SkillHubSubFeature {
470
537
  filter: { enabled: true }
471
538
  });
472
539
  if (!skills || skills.length === 0) return;
473
- const tools = await Promise.all(skills.map(async (skill) => {
474
- const sanitizedToolName = skill.get("name").toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
475
- const autoCall = !!skill.get("autoCall");
476
- const interactionSchema = (0, import_json_fields.parseJsonText)(skill.get("interactionSchema"), null);
477
- const fullDescription = await this.getSkillDescriptionForAI(skill);
478
- const baseDescription = `${fullDescription || skill.get("description")}
479
- Language: ${skill.get("language")}`;
480
- const description = !autoCall && interactionSchema ? `${baseDescription}
540
+ const loopConfigsBySkillId = await this.getLoopConfigsBySkillId(
541
+ skills.map((skill) => this.getSkillRecordId(skill))
542
+ );
543
+ const tools = await Promise.all(
544
+ skills.map(async (skill) => {
545
+ const sanitizedToolName = skill.get("name").toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
546
+ const autoCall = !!skill.get("autoCall");
547
+ const loopConfig = loopConfigsBySkillId.get(this.getSkillRecordId(skill));
548
+ const loopInteractionSchema = this.resolveLoopInteractionSchema(loopConfig);
549
+ const skillInteractionSchema = (0, import_json_fields.parseJsonText)(skill.get("interactionSchema"), null);
550
+ const interactionSchema = loopInteractionSchema || skillInteractionSchema;
551
+ const requiresHumanReview = !!interactionSchema;
552
+ const fullDescription = await this.getSkillDescriptionForAI(skill);
553
+ const baseDescription = `${fullDescription || skill.get("description")}
554
+ Language: ${skill.get(
555
+ "language"
556
+ )}`;
557
+ const description = requiresHumanReview ? `${baseDescription}
481
558
 
482
- IMPORTANT: This skill requires human confirmation. Pass best-effort args; the user will adjust them in UI before execution.` : baseDescription;
483
- return {
484
- scope: "CUSTOM",
485
- execution: "backend",
486
- defaultPermission: autoCall ? "ALLOW" : "ASK",
487
- introduction: {
488
- title: `Skill Hub: ${skill.get("title")}`,
489
- about: skill.get("description") || `Th\u1EF1c thi k\u1EF9 n\u0103ng ${skill.get("title")}`
490
- },
491
- definition: {
492
- name: `skill_hub_${sanitizedToolName}`,
493
- description,
494
- schema: (0, import_json_fields.parseJsonText)(skill.get("inputSchema"), { type: "object", properties: {} })
495
- },
496
- invoke: async (toolCtx, args) => {
497
- const latestSkill = await this.db.getRepository("skillDefinitions").findOne({
498
- filter: { id: skill.get("id"), enabled: true }
499
- });
500
- if (!latestSkill) {
501
- return { status: "error", content: `Skill "${skill.get("name")}" is no longer available` };
559
+ IMPORTANT: This skill is bound to a Skill Hub human-in-the-loop review template. Pass best-effort args; the user can approve, edit, or reject them before execution.` : baseDescription;
560
+ return {
561
+ scope: "CUSTOM",
562
+ execution: "backend",
563
+ defaultPermission: requiresHumanReview ? "ASK" : autoCall ? "ALLOW" : "ASK",
564
+ introduction: {
565
+ title: `Skill Hub: ${skill.get("title")}`,
566
+ about: skill.get("description") || `Th\u1EF1c thi k\u1EF9 n\u0103ng ${skill.get("title")}`
567
+ },
568
+ definition: {
569
+ name: `skill_hub_${sanitizedToolName}`,
570
+ description,
571
+ schema: (0, import_json_fields.parseJsonText)(skill.get("inputSchema"), { type: "object", properties: {} })
572
+ },
573
+ invoke: async (toolCtx, args) => {
574
+ const latestSkill = await this.db.getRepository("skillDefinitions").findOne({
575
+ filter: { id: skill.get("id"), enabled: true }
576
+ });
577
+ if (!latestSkill) {
578
+ return { status: "error", content: `Skill "${skill.get("name")}" is no longer available` };
579
+ }
580
+ const result = await this.executeSkill(latestSkill, args, toolCtx);
581
+ return {
582
+ status: result.status === "succeeded" ? "success" : "error",
583
+ result
584
+ // Attach raw result
585
+ };
502
586
  }
503
- const result = await this.executeSkill(latestSkill, args, toolCtx);
504
- return {
505
- status: result.status === "succeeded" ? "success" : "error",
506
- result
507
- // Attach raw result
508
- };
509
- }
510
- };
511
- }));
587
+ };
588
+ })
589
+ );
512
590
  register.registerTools(tools);
513
591
  } catch (err) {
514
592
  this.app.logger.warn("[skill-hub] Failed to provide dynamic tools", err);
@@ -616,7 +694,9 @@ IMPORTANT: This skill requires human confirmation. Pass best-effort args; the us
616
694
  */
617
695
  registerSkillTemplate(pluginName, skillDef) {
618
696
  this.skillTemplates.set(skillDef.name, this.hydrateSkillTemplate(pluginName, skillDef));
619
- this.app.logger.info(`[skill-hub] Registered skill template "${skillDef.name}" from plugin "${pluginName}"`);
697
+ this.app.logger.info(
698
+ `[skill-hub] Registered skill template "${skillDef.name}" from plugin "${pluginName}"`
699
+ );
620
700
  }
621
701
  resolveSkillTemplate(templateName) {
622
702
  if (!templateName) return null;
@@ -644,7 +724,10 @@ IMPORTANT: This skill requires human confirmation. Pass best-effort args; the us
644
724
  const description = skill.get ? skill.get("description") : skill.description;
645
725
  const instructions = await this.getSkillInstructions(skill);
646
726
  const maxInlineInstructionChars = 24e3;
647
- const inlineInstructions = instructions && instructions.length > maxInlineInstructionChars ? `${instructions.slice(0, maxInlineInstructionChars)}
727
+ const inlineInstructions = instructions && instructions.length > maxInlineInstructionChars ? `${instructions.slice(
728
+ 0,
729
+ maxInlineInstructionChars
730
+ )}
648
731
 
649
732
  [Instructions truncated in tool description. Call skill_hub_execute with action="describe" and this skillName to load the complete workflow.]` : instructions;
650
733
  return [description, inlineInstructions ? `Instructions: