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.
- package/dist/client/AgentRunsTab.d.ts +2 -0
- package/dist/client/HarnessProfilesTab.d.ts +2 -0
- package/dist/client/index.js +1 -1
- package/dist/client/skill-hub/components/LoopSettings.d.ts +2 -0
- package/dist/client/skill-hub/index.d.ts +2 -1
- package/dist/client/skill-hub/tools/InteractionSchemasProvider.d.ts +1 -14
- package/dist/client/skill-hub/tools/loopTemplates.d.ts +22 -0
- package/dist/client/skill-hub/tools/registerSkillLoopCards.d.ts +1 -0
- package/dist/client/tools/PlanApprovalCard.d.ts +3 -0
- package/dist/client/tools/registerOrchestratorCards.d.ts +1 -0
- package/dist/externalVersion.js +6 -6
- package/dist/server/collections/agent-harness-profiles.d.ts +2 -0
- package/dist/server/collections/agent-harness-profiles.js +89 -0
- package/dist/server/collections/agent-loop-events.d.ts +2 -0
- package/dist/server/collections/agent-loop-events.js +101 -0
- package/dist/server/collections/agent-loop-runs.d.ts +2 -0
- package/dist/server/collections/agent-loop-runs.js +188 -0
- package/dist/server/collections/agent-loop-steps.d.ts +2 -0
- package/dist/server/collections/agent-loop-steps.js +174 -0
- package/dist/server/collections/orchestrator-config.js +7 -0
- package/dist/server/collections/skill-executions.js +12 -0
- package/dist/server/collections/skill-loop-configs.d.ts +3 -0
- package/dist/server/collections/skill-loop-configs.js +94 -0
- package/dist/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.d.ts +7 -0
- package/dist/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.js +55 -0
- package/dist/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.d.ts +12 -0
- package/dist/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.js +162 -0
- package/dist/server/plugin.d.ts +2 -0
- package/dist/server/plugin.js +13 -0
- package/dist/server/resources/agent-loop.d.ts +3 -0
- package/dist/server/resources/agent-loop.js +205 -0
- package/dist/server/services/AgentHarness.d.ts +42 -0
- package/dist/server/services/AgentHarness.js +565 -0
- package/dist/server/services/AgentLoopController.d.ts +205 -0
- package/dist/server/services/AgentLoopController.js +940 -0
- package/dist/server/services/AgentLoopRepository.d.ts +20 -0
- package/dist/server/services/AgentLoopRepository.js +210 -0
- package/dist/server/services/AgentLoopService.d.ts +149 -0
- package/dist/server/services/AgentLoopService.js +133 -0
- package/dist/server/services/AgentPlanValidator.d.ts +4 -0
- package/dist/server/services/AgentPlanValidator.js +99 -0
- package/dist/server/services/AgentPlannerService.d.ts +8 -0
- package/dist/server/services/AgentPlannerService.js +119 -0
- package/dist/server/services/AgentRegistryService.d.ts +13 -0
- package/dist/server/services/AgentRegistryService.js +178 -0
- package/dist/server/services/ExecutionSpanService.d.ts +2 -0
- package/dist/server/skill-hub/plugin.d.ts +3 -0
- package/dist/server/skill-hub/plugin.js +137 -54
- package/dist/server/tools/agent-loop.d.ts +235 -0
- package/dist/server/tools/agent-loop.js +406 -0
- package/dist/server/tools/delegate-task.js +37 -350
- package/dist/server/tools/orchestrator-plan.d.ts +205 -0
- package/dist/server/tools/orchestrator-plan.js +291 -0
- package/dist/server/tools/skill-execute.js +2 -0
- package/package.json +2 -2
- package/src/client/AgentRunsTab.tsx +764 -0
- package/src/client/HarnessProfilesTab.tsx +247 -0
- package/src/client/OrchestratorSettings.tsx +40 -2
- package/src/client/RulesTab.tsx +103 -6
- package/src/client/plugin.tsx +27 -54
- package/src/client/skill-hub/components/LoopSettings.tsx +331 -0
- package/src/client/skill-hub/index.tsx +51 -75
- package/src/client/skill-hub/tools/InteractionSchemasProvider.tsx +56 -16
- package/src/client/skill-hub/tools/SkillHubCard.tsx +35 -4
- package/src/client/skill-hub/tools/loopTemplates.ts +52 -0
- package/src/client/skill-hub/tools/registerSkillLoopCards.ts +58 -0
- package/src/client/tools/PlanApprovalCard.tsx +175 -0
- package/src/client/tools/registerOrchestratorCards.ts +7 -0
- package/src/server/collections/agent-harness-profiles.ts +59 -0
- package/src/server/collections/agent-loop-events.ts +71 -0
- package/src/server/collections/agent-loop-runs.ts +158 -0
- package/src/server/collections/agent-loop-steps.ts +144 -0
- package/src/server/collections/orchestrator-config.ts +7 -0
- package/src/server/collections/skill-executions.ts +63 -51
- package/src/server/collections/skill-loop-configs.ts +65 -0
- package/src/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.ts +30 -0
- package/src/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.ts +142 -0
- package/src/server/plugin.ts +15 -0
- package/src/server/resources/agent-loop.ts +183 -0
- package/src/server/services/AgentHarness.ts +663 -0
- package/src/server/services/AgentLoopController.ts +1128 -0
- package/src/server/services/AgentLoopRepository.ts +194 -0
- package/src/server/services/AgentLoopService.ts +161 -0
- package/src/server/services/AgentPlanValidator.ts +73 -0
- package/src/server/services/AgentPlannerService.ts +93 -0
- package/src/server/services/AgentRegistryService.ts +169 -0
- package/src/server/services/ExecutionSpanService.ts +2 -0
- package/src/server/skill-hub/plugin.ts +881 -771
- package/src/server/tools/agent-loop.ts +399 -0
- package/src/server/tools/delegate-task.ts +48 -463
- package/src/server/tools/orchestrator-plan.ts +279 -0
- 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
|
+
});
|
|
@@ -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}`, {
|
|
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(
|
|
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((
|
|
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",
|
|
181
|
-
writeStream.on("error",
|
|
182
|
-
streamData.stream.on("error",
|
|
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(
|
|
189
|
+
const { metadata, instructions } = await this.skillRepoService.extractSkillPackage(
|
|
190
|
+
skillName,
|
|
191
|
+
tempZipPath
|
|
192
|
+
);
|
|
187
193
|
const code = this.skillRepoService.getSkillCode(skillName);
|
|
188
|
-
const updateValues = {
|
|
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)
|
|
194
|
-
|
|
195
|
-
if (metadata.
|
|
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 {
|
|
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
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
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
|
-
|
|
504
|
-
|
|
505
|
-
|
|
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(
|
|
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(
|
|
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:
|