plugin-agent-orchestrator 1.0.22 → 1.0.25

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 (103) hide show
  1. package/client-v2.d.ts +2 -0
  2. package/client-v2.js +1 -0
  3. package/dist/client/index.js +1 -1
  4. package/dist/client-v2/214.723affb37c13bf7a.js +10 -0
  5. package/dist/client-v2/264.0533912e6c5ea2d7.js +10 -0
  6. package/dist/client-v2/41.1805b2edfaa4afe2.js +10 -0
  7. package/dist/client-v2/418.5ae055abf141820e.js +10 -0
  8. package/dist/client-v2/619.d99d3c9e61c99064.js +10 -0
  9. package/dist/client-v2/70.a15d7fcec7c41768.js +10 -0
  10. package/dist/client-v2/892.72db4161511c8a16.js +10 -0
  11. package/dist/client-v2/926.87f660b670d85bcc.js +10 -0
  12. package/dist/client-v2/index.js +10 -0
  13. package/dist/externalVersion.js +8 -6
  14. package/dist/locale/en-US.json +7 -0
  15. package/dist/locale/vi-VN.json +7 -0
  16. package/dist/locale/zh-CN.json +27 -0
  17. package/dist/server/migrations/20260615000000-normalize-ai-employee-tool-bindings.js +63 -0
  18. package/dist/server/plugin.js +32 -1
  19. package/dist/server/services/AgentHarness.js +52 -27
  20. package/dist/server/services/AgentLoopController.js +8 -2
  21. package/dist/server/services/AgentLoopService.js +1 -1
  22. package/dist/server/services/AgentRegistryService.js +53 -42
  23. package/dist/server/services/CircuitBreaker.js +7 -2
  24. package/dist/server/services/CodeValidator.js +48 -14
  25. package/dist/server/services/SandboxRunner.js +18 -14
  26. package/dist/server/skill-hub/plugin.js +44 -17
  27. package/dist/server/tools/delegate-task.js +7 -2
  28. package/dist/server/tools/skill-execute.js +33 -2
  29. package/dist/server/utils/ai-manager.js +51 -0
  30. package/dist/server/utils/ctx-utils.js +11 -0
  31. package/dist/server/utils/skill-settings.js +122 -0
  32. package/package.json +49 -45
  33. package/src/client/AIEmployeesContext.tsx +60 -19
  34. package/src/client/AgentRunsTab.tsx +769 -764
  35. package/src/client/HarnessProfilesTab.tsx +257 -247
  36. package/src/client/RulesTab.tsx +787 -716
  37. package/src/client/TracingTab.tsx +9 -6
  38. package/src/client/plugin.tsx +34 -27
  39. package/src/client/skill-hub/components/ExecutionHistory.tsx +9 -8
  40. package/src/client/skill-hub/components/GitSkillImport.tsx +12 -5
  41. package/src/client/skill-hub/components/LoopSettings.tsx +2 -2
  42. package/src/client/skill-hub/components/SkillEditor.tsx +2 -2
  43. package/src/client/skill-hub/components/SkillManager.tsx +2 -2
  44. package/src/client/skill-hub/components/SkillMetrics.tsx +157 -124
  45. package/src/client/skill-hub/components/SkillTestPanel.tsx +14 -13
  46. package/src/client/skill-hub/index.tsx +58 -51
  47. package/src/client/skill-hub/locale.ts +1 -1
  48. package/src/client/skill-hub/tools/InteractionSchemasProvider.tsx +132 -99
  49. package/src/client/skill-hub/tools/registerSkillLoopCards.ts +71 -58
  50. package/src/client/tools/PlanApprovalCard.tsx +3 -2
  51. package/src/client/tools/registerOrchestratorCards.ts +17 -7
  52. package/src/client-v2/components/AIEmployeeSelect.tsx +47 -0
  53. package/src/client-v2/components/AIEmployeesContext.tsx +110 -0
  54. package/src/client-v2/components/AgentRunsTab.tsx +767 -0
  55. package/src/client-v2/components/HarnessProfilesTab.tsx +254 -0
  56. package/src/client-v2/components/RulesTab.tsx +782 -0
  57. package/src/client-v2/components/TracingTab.tsx +432 -0
  58. package/src/client-v2/hooks/useApiRequest.ts +114 -0
  59. package/src/client-v2/index.tsx +1 -0
  60. package/src/client-v2/pages/AgentRunsPage.tsx +13 -0
  61. package/src/client-v2/pages/ExecutionHistoryPage.tsx +10 -0
  62. package/src/client-v2/pages/HarnessProfilesPage.tsx +10 -0
  63. package/src/client-v2/pages/LoopSettingsPage.tsx +10 -0
  64. package/src/client-v2/pages/RulesPage.tsx +13 -0
  65. package/src/client-v2/pages/SkillDefinitionsPage.tsx +10 -0
  66. package/src/client-v2/pages/SkillMetricsPage.tsx +10 -0
  67. package/src/client-v2/pages/TracingPage.tsx +13 -0
  68. package/src/client-v2/plugin.tsx +70 -0
  69. package/src/client-v2/skill-hub/components/ExecutionHistory.tsx +196 -0
  70. package/src/client-v2/skill-hub/components/FileLinkList.tsx +37 -0
  71. package/src/client-v2/skill-hub/components/GitSkillImport.tsx +539 -0
  72. package/src/client-v2/skill-hub/components/LoopSettings.tsx +331 -0
  73. package/src/client-v2/skill-hub/components/SkillEditor.tsx +453 -0
  74. package/src/client-v2/skill-hub/components/SkillManager.tsx +174 -0
  75. package/src/client-v2/skill-hub/components/SkillMetrics.tsx +157 -0
  76. package/src/client-v2/skill-hub/components/SkillTestPanel.tsx +135 -0
  77. package/src/client-v2/skill-hub/locale.ts +13 -0
  78. package/src/client-v2/skill-hub/tools/loopTemplates.ts +52 -0
  79. package/src/client-v2/skill-hub/utils/jsonFields.ts +41 -0
  80. package/src/client-v2/utils/jsonFields.ts +41 -0
  81. package/src/locale/en-US.json +7 -0
  82. package/src/locale/vi-VN.json +7 -0
  83. package/src/locale/zh-CN.json +27 -0
  84. package/src/server/__tests__/agent-registry-service.test.ts +147 -0
  85. package/src/server/__tests__/code-validator.test.ts +63 -0
  86. package/src/server/__tests__/skill-execute.test.ts +33 -0
  87. package/src/server/__tests__/skill-settings.test.ts +63 -0
  88. package/src/server/migrations/20260615000000-normalize-ai-employee-tool-bindings.ts +39 -0
  89. package/src/server/plugin.ts +62 -21
  90. package/src/server/services/AgentHarness.ts +49 -22
  91. package/src/server/services/AgentLoopController.ts +17 -6
  92. package/src/server/services/AgentLoopService.ts +1 -1
  93. package/src/server/services/AgentPlannerService.ts +10 -0
  94. package/src/server/services/AgentRegistryService.ts +89 -47
  95. package/src/server/services/CircuitBreaker.ts +10 -0
  96. package/src/server/services/CodeValidator.ts +237 -159
  97. package/src/server/services/SandboxRunner.ts +203 -189
  98. package/src/server/skill-hub/plugin.ts +933 -898
  99. package/src/server/tools/delegate-task.ts +12 -9
  100. package/src/server/tools/skill-execute.ts +194 -160
  101. package/src/server/utils/ai-manager.ts +24 -0
  102. package/src/server/utils/ctx-utils.ts +14 -0
  103. package/src/server/utils/skill-settings.ts +116 -0
@@ -1,205 +1,219 @@
1
- import { exec, ChildProcess, ExecException } from 'child_process';
2
- import { writeFileSync } from 'fs';
3
- import { resolve } from 'path';
4
- import { FileManager, OutputFileInfo } from './FileManager';
5
- import { CodeValidator } from './CodeValidator';
6
-
7
- export interface ExecuteOptions {
8
- language: 'node' | 'python';
9
- code: string;
10
- execId: string;
11
- timeoutSeconds?: number;
12
- maxOutputSizeMb?: number;
13
- onProgress?: (progress: ProgressUpdate) => void;
14
- /** AbortSignal — when aborted, the child process is killed */
15
- signal?: { addEventListener(event: string, listener: () => void): void };
1
+ import { exec, ChildProcess, ExecException } from 'child_process';
2
+ import { writeFileSync } from 'fs';
3
+ import { resolve, delimiter } from 'path';
4
+ import { tmpdir } from 'os';
5
+ import { FileManager, OutputFileInfo } from './FileManager';
6
+ import { CodeValidator } from './CodeValidator';
7
+
8
+ const IS_WINDOWS = process.platform === 'win32';
9
+
10
+ /** Resolve the python executable name per platform (python3 on *nix, python on Windows). */
11
+ function pythonBinary() {
12
+ return process.env.SKILL_HUB_PYTHON_BIN || (IS_WINDOWS ? 'python' : 'python3');
13
+ }
14
+
15
+ export interface ExecuteOptions {
16
+ language: 'node' | 'python';
17
+ code: string;
18
+ execId: string;
19
+ timeoutSeconds?: number;
20
+ maxOutputSizeMb?: number;
21
+ onProgress?: (progress: ProgressUpdate) => void;
22
+ /** AbortSignal — when aborted, the child process is killed */
23
+ signal?: { addEventListener(event: string, listener: () => void): void };
16
24
  /** Package whitelist for import validation (from skillWorkerConfigs) */
17
25
  packageWhitelist?: string[];
18
26
  /** Optional installed/copied skill package root exposed to the runtime as SKILL_DIR. */
19
27
  skillDir?: string;
20
28
  }
21
-
22
- export interface ProgressUpdate {
23
- percent: number;
24
- log: string;
25
- }
26
-
27
- export interface ExecutionResult {
28
- success: boolean;
29
- stdout: string;
30
- stderr: string;
31
- files: OutputFileInfo[];
32
- durationMs: number;
33
- /** true if execution was canceled by user */
34
- canceled?: boolean;
35
- /** true if execution timed out */
36
- timedOut?: boolean;
37
- }
38
-
39
- export class SandboxRunner {
40
- private validator = new CodeValidator();
41
-
42
- private sandboxWorkspace: string;
43
-
44
- constructor(
45
- private fileManager: FileManager,
46
- private logger: any,
47
- private storagePath: string,
48
- ) {
49
- this.sandboxWorkspace = resolve(storagePath, 'sandbox-workspace');
50
- }
51
-
52
- async execute(options: ExecuteOptions): Promise<ExecutionResult> {
53
- const {
54
- language,
55
- code,
56
- execId,
57
- timeoutSeconds = 60,
58
- maxOutputSizeMb = 50,
59
- onProgress,
29
+
30
+ export interface ProgressUpdate {
31
+ percent: number;
32
+ log: string;
33
+ }
34
+
35
+ export interface ExecutionResult {
36
+ success: boolean;
37
+ stdout: string;
38
+ stderr: string;
39
+ files: OutputFileInfo[];
40
+ durationMs: number;
41
+ /** true if execution was canceled by user */
42
+ canceled?: boolean;
43
+ /** true if execution timed out */
44
+ timedOut?: boolean;
45
+ }
46
+
47
+ export class SandboxRunner {
48
+ private validator = new CodeValidator();
49
+
50
+ private sandboxWorkspace: string;
51
+
52
+ constructor(
53
+ private fileManager: FileManager,
54
+ private logger: any,
55
+ private storagePath: string,
56
+ ) {
57
+ this.sandboxWorkspace = resolve(storagePath, 'sandbox-workspace');
58
+ }
59
+
60
+ async execute(options: ExecuteOptions): Promise<ExecutionResult> {
61
+ const {
62
+ language,
63
+ code,
64
+ execId,
65
+ timeoutSeconds = 60,
66
+ maxOutputSizeMb = 50,
67
+ onProgress,
60
68
  signal,
61
69
  packageWhitelist,
62
70
  skillDir,
63
71
  } = options;
64
-
65
- // 1. Validate code against forbidden patterns
66
- this.validator.validate(code, language);
67
-
68
- // 1b. Validate imports against whitelist (if env initialized)
69
- if (packageWhitelist?.length) {
70
- this.validator.validateImports(code, language, packageWhitelist);
71
- }
72
-
73
- // 2. Prepare workspace
74
- const workDir = this.fileManager.createExecDir(execId);
75
- const outputDir = this.fileManager.getOutputDir(execId);
76
-
77
- const filename = language === 'node' ? 'script.js' : 'script.py';
78
- const scriptPath = resolve(workDir, filename);
79
- writeFileSync(scriptPath, code, 'utf-8');
80
-
81
- onProgress?.({ percent: 20, log: 'Code validated, executing...' });
82
-
83
- // 3. Build command
84
- const cmd = language === 'node'
85
- ? `node "${scriptPath}"`
86
- : `python3 "${scriptPath}"`;
87
-
88
- // 4. Resolve Node Modules Path using the local sandbox workspace
89
- const path = require('path');
90
- const nodePath = path.resolve(this.sandboxWorkspace, 'node_modules');
91
- const finalNodePath = process.env.NODE_PATH
92
- ? `${nodePath}${path.delimiter}${process.env.NODE_PATH}`
93
- : nodePath;
94
-
95
- // 5. Execute via child_process with sanitized env
96
- const startTime = Date.now();
97
- let childProc: ChildProcess | null = null;
98
- let wasCanceled = false;
99
-
100
- const processResult = await new Promise<{
101
- exitCode: number;
102
- stdout: string;
103
- stderr: string;
104
- }>((resolveResult) => {
105
- childProc = exec(
106
- cmd,
107
- {
108
- timeout: timeoutSeconds * 1000,
109
- maxBuffer: 10 * 1024 * 1024, // 10MB stdout/stderr buffer
110
- cwd: workDir,
111
- env: {
112
- // Sanitized environment — only expose what's necessary
113
- PATH: process.env.PATH,
114
- HOME: '/tmp',
115
- TMPDIR: '/tmp',
116
- OUTPUT_DIR: outputDir,
117
- LANG: 'en_US.UTF-8',
118
- // Node.js
119
- NODE_PATH: finalNodePath,
72
+
73
+ // 1. Validate code against forbidden patterns
74
+ this.validator.validate(code, language);
75
+
76
+ // 1b. Validate imports against the allowlist (builtins + whitelist).
77
+ // Always runs: an empty whitelist restricts code to built-in modules only,
78
+ // which blocks stdlib-based network exfiltration (urllib/socket/etc.).
79
+ this.validator.validateImports(code, language, packageWhitelist || []);
80
+
81
+ // 2. Prepare workspace
82
+ const workDir = this.fileManager.createExecDir(execId);
83
+ const outputDir = this.fileManager.getOutputDir(execId);
84
+
85
+ const filename = language === 'node' ? 'script.js' : 'script.py';
86
+ const scriptPath = resolve(workDir, filename);
87
+ writeFileSync(scriptPath, code, 'utf-8');
88
+
89
+ onProgress?.({ percent: 20, log: 'Code validated, executing...' });
90
+
91
+ // 3. Resolve Node Modules Path using the local sandbox workspace
92
+ const nodePath = resolve(this.sandboxWorkspace, 'node_modules');
93
+ const finalNodePath = process.env.NODE_PATH ? `${nodePath}${delimiter}${process.env.NODE_PATH}` : nodePath;
94
+
95
+ // 4. Build command.
96
+ // Memory cap (MB): node gets --max-old-space-size; on Linux/macOS the whole
97
+ // process tree is additionally bounded with `ulimit -v` so a python skill
98
+ // cannot exhaust host memory. Windows has no ulimit equivalent, so the cap
99
+ // there is best-effort (node heap only) — production runs on Linux.
100
+ const memLimitMb = Math.max(64, Number(process.env.SKILL_HUB_MEM_LIMIT_MB || 512));
101
+ const baseCmd =
102
+ language === 'node'
103
+ ? `node --max-old-space-size=${memLimitMb} "${scriptPath}"`
104
+ : `${pythonBinary()} "${scriptPath}"`;
105
+ const cmd = !IS_WINDOWS && memLimitMb > 0 ? `ulimit -v ${memLimitMb * 1024} 2>/dev/null; exec ${baseCmd}` : baseCmd;
106
+
107
+ // 5. Execute via child_process with sanitized env
108
+ const startTime = Date.now();
109
+ let childProc: ChildProcess | null = null;
110
+ let wasCanceled = false;
111
+
112
+ const processResult = await new Promise<{
113
+ exitCode: number;
114
+ stdout: string;
115
+ stderr: string;
116
+ }>((_resolve) => {
117
+ childProc = exec(
118
+ cmd,
119
+ {
120
+ timeout: timeoutSeconds * 1000,
121
+ maxBuffer: 10 * 1024 * 1024, // 10MB stdout/stderr buffer
122
+ cwd: workDir,
123
+ env: {
124
+ // Sanitized environment — only expose what's necessary
125
+ PATH: process.env.PATH,
126
+ HOME: tmpdir(),
127
+ TMPDIR: tmpdir(),
128
+ OUTPUT_DIR: outputDir,
129
+ LANG: 'en_US.UTF-8',
130
+ // Node.js
131
+ NODE_PATH: finalNodePath,
120
132
  // Python — include bundled packages (svg_to_pptx etc.)
121
133
  PYTHONPATH: [
122
- skillDir ? path.resolve(skillDir, 'scripts') : '',
123
- path.resolve(this.sandboxWorkspace, 'python_packages'),
134
+ skillDir ? resolve(skillDir, 'scripts') : '',
135
+ resolve(this.sandboxWorkspace, 'python_packages'),
124
136
  process.env.PYTHONPATH || '',
125
- ].filter(Boolean).join(path.delimiter),
137
+ ]
138
+ .filter(Boolean)
139
+ .join(delimiter),
126
140
  PYTHONIOENCODING: 'utf-8',
127
141
  SKILL_DIR: skillDir || '',
128
142
  // DO NOT pass: DB credentials, API keys, APP_KEY, etc.
129
143
  },
130
- },
131
- (error: ExecException | null, stdout: string, stderr: string) => {
132
- let exitCode = 0;
133
- if (error) {
134
- if (error.killed) {
135
- exitCode = -1; // Killed by timeout or abort
136
- } else {
137
- exitCode = error.code || 1;
138
- }
139
- }
140
-
141
- resolveResult({
142
- exitCode,
143
- stdout: (stdout || '').slice(0, 5000),
144
- stderr: (stderr || '').slice(0, 2000),
145
- });
146
- },
147
- );
148
-
149
- // 5. Wire abort signal → kill child process
150
- if (signal) {
151
- signal.addEventListener('abort', () => {
152
- wasCanceled = true;
153
- if (childProc && !childProc.killed) {
154
- this.logger.info(`[skill-hub] Killing child process for exec ${execId} (user abort)`);
155
- childProc.kill('SIGTERM');
156
- // Force kill after 3 seconds if SIGTERM didn't work
157
- setTimeout(() => {
158
- if (childProc && !childProc.killed) {
159
- childProc.kill('SIGKILL');
160
- }
161
- }, 3000);
162
- }
163
- });
164
- }
165
- });
166
-
167
- const durationMs = Date.now() - startTime;
168
-
169
- onProgress?.({ percent: 80, log: wasCanceled ? 'Canceled' : 'Execution completed, collecting files...' });
170
-
171
- // 6. Collect output files
172
- const files = this.fileManager.listOutputFiles(execId);
173
-
174
- // 7. Check output size limit
175
- const totalSizeBytes = this.fileManager.getTotalOutputSize(execId);
176
- const maxBytes = maxOutputSizeMb * 1024 * 1024;
177
- if (totalSizeBytes > maxBytes) {
178
- this.logger.warn(
179
- `[skill-hub] Execution ${execId}: output size ${totalSizeBytes} bytes exceeds limit ${maxBytes}`,
180
- );
181
- }
182
-
183
- onProgress?.({ percent: 100, log: 'Done' });
184
-
185
- // Detect timeout
186
- const timedOut = !wasCanceled && processResult.exitCode === -1;
187
- if (timedOut) {
188
- processResult.stderr = `Execution timed out after ${timeoutSeconds}s\n${processResult.stderr}`;
189
- }
190
-
191
- if (wasCanceled) {
192
- processResult.stderr = `Execution canceled by user\n${processResult.stderr}`;
193
- }
194
-
195
- return {
196
- success: !wasCanceled && !timedOut && processResult.exitCode === 0,
197
- stdout: processResult.stdout,
198
- stderr: processResult.stderr,
199
- files,
200
- durationMs,
201
- canceled: wasCanceled,
202
- timedOut,
203
- };
204
- }
205
- }
144
+ },
145
+ (error: ExecException | null, stdout: string, stderr: string) => {
146
+ let exitCode = 0;
147
+ if (error) {
148
+ if (error.killed) {
149
+ exitCode = -1; // Killed by timeout or abort
150
+ } else {
151
+ exitCode = error.code || 1;
152
+ }
153
+ }
154
+
155
+ _resolve({
156
+ exitCode,
157
+ stdout: (stdout || '').slice(0, 5000),
158
+ stderr: (stderr || '').slice(0, 2000),
159
+ });
160
+ },
161
+ );
162
+
163
+ // 5. Wire abort signal → kill child process
164
+ if (signal) {
165
+ signal.addEventListener('abort', () => {
166
+ wasCanceled = true;
167
+ if (childProc && !childProc.killed) {
168
+ this.logger.info(`[skill-hub] Killing child process for exec ${execId} (user abort)`);
169
+ childProc.kill('SIGTERM');
170
+ // Force kill after 3 seconds if SIGTERM didn't work
171
+ setTimeout(() => {
172
+ if (childProc && !childProc.killed) {
173
+ childProc.kill('SIGKILL');
174
+ }
175
+ }, 3000);
176
+ }
177
+ });
178
+ }
179
+ });
180
+
181
+ const durationMs = Date.now() - startTime;
182
+
183
+ onProgress?.({ percent: 80, log: wasCanceled ? 'Canceled' : 'Execution completed, collecting files...' });
184
+
185
+ // 6. Collect output files
186
+ const files = this.fileManager.listOutputFiles(execId);
187
+
188
+ // 7. Check output size limit
189
+ const totalSizeBytes = this.fileManager.getTotalOutputSize(execId);
190
+ const maxBytes = maxOutputSizeMb * 1024 * 1024;
191
+ if (totalSizeBytes > maxBytes) {
192
+ this.logger.warn(
193
+ `[skill-hub] Execution ${execId}: output size ${totalSizeBytes} bytes exceeds limit ${maxBytes}`,
194
+ );
195
+ }
196
+
197
+ onProgress?.({ percent: 100, log: 'Done' });
198
+
199
+ // Detect timeout
200
+ const timedOut = !wasCanceled && processResult.exitCode === -1;
201
+ if (timedOut) {
202
+ processResult.stderr = `Execution timed out after ${timeoutSeconds}s\n${processResult.stderr}`;
203
+ }
204
+
205
+ if (wasCanceled) {
206
+ processResult.stderr = `Execution canceled by user\n${processResult.stderr}`;
207
+ }
208
+
209
+ return {
210
+ success: !wasCanceled && !timedOut && processResult.exitCode === 0,
211
+ stdout: processResult.stdout,
212
+ stderr: processResult.stderr,
213
+ files,
214
+ durationMs,
215
+ canceled: wasCanceled,
216
+ timedOut,
217
+ };
218
+ }
219
+ }