@opengoat/core 2026.2.14 → 2026.2.17
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/core/agents/application/agent.service.d.ts +19 -3
- package/dist/core/agents/application/agent.service.js +170 -45
- package/dist/core/agents/application/agent.service.js.map +1 -1
- package/dist/core/boards/application/board.service.d.ts +2 -0
- package/dist/core/boards/application/board.service.js +36 -2
- package/dist/core/boards/application/board.service.js.map +1 -1
- package/dist/core/bootstrap/application/bootstrap.service.js +17 -1
- package/dist/core/bootstrap/application/bootstrap.service.js.map +1 -1
- package/dist/core/opengoat/application/opengoat.service.d.ts +14 -3
- package/dist/core/opengoat/application/opengoat.service.helpers.d.ts +62 -0
- package/dist/core/opengoat/application/opengoat.service.helpers.js +403 -0
- package/dist/core/opengoat/application/opengoat.service.helpers.js.map +1 -0
- package/dist/core/opengoat/application/opengoat.service.js +653 -371
- package/dist/core/opengoat/application/opengoat.service.js.map +1 -1
- package/dist/core/orchestration/application/orchestration.service.d.ts +2 -0
- package/dist/core/orchestration/application/orchestration.service.js +150 -52
- package/dist/core/orchestration/application/orchestration.service.js.map +1 -1
- package/dist/core/providers/application/provider.service.d.ts +35 -4
- package/dist/core/providers/application/provider.service.js +823 -96
- package/dist/core/providers/application/provider.service.js.map +1 -1
- package/dist/core/providers/index.d.ts +1 -1
- package/dist/core/providers/index.js.map +1 -1
- package/dist/core/providers/loader.js +4 -2
- package/dist/core/providers/loader.js.map +1 -1
- package/dist/core/providers/openclaw-gateway-rpc.d.ts +20 -0
- package/dist/core/providers/openclaw-gateway-rpc.js +629 -0
- package/dist/core/providers/openclaw-gateway-rpc.js.map +1 -0
- package/dist/core/providers/provider-module.d.ts +11 -0
- package/dist/core/providers/provider-module.js +8 -1
- package/dist/core/providers/provider-module.js.map +1 -1
- package/dist/core/providers/providers/claude-code/index.d.ts +4 -0
- package/dist/core/providers/providers/claude-code/index.js +32 -0
- package/dist/core/providers/providers/claude-code/index.js.map +1 -0
- package/dist/core/providers/providers/claude-code/provider.d.ts +13 -0
- package/dist/core/providers/providers/claude-code/provider.js +152 -0
- package/dist/core/providers/providers/claude-code/provider.js.map +1 -0
- package/dist/core/providers/providers/codex/index.d.ts +4 -0
- package/dist/core/providers/providers/codex/index.js +32 -0
- package/dist/core/providers/providers/codex/index.js.map +1 -0
- package/dist/core/providers/providers/codex/provider.d.ts +12 -0
- package/dist/core/providers/providers/codex/provider.js +156 -0
- package/dist/core/providers/providers/codex/provider.js.map +1 -0
- package/dist/core/providers/providers/cursor/index.d.ts +4 -0
- package/dist/core/providers/providers/cursor/index.js +32 -0
- package/dist/core/providers/providers/cursor/index.js.map +1 -0
- package/dist/core/providers/providers/cursor/provider.d.ts +12 -0
- package/dist/core/providers/providers/cursor/provider.js +161 -0
- package/dist/core/providers/providers/cursor/provider.js.map +1 -0
- package/dist/core/providers/providers/gemini-cli/index.d.ts +4 -0
- package/dist/core/providers/providers/gemini-cli/index.js +36 -0
- package/dist/core/providers/providers/gemini-cli/index.js.map +1 -0
- package/dist/core/providers/providers/gemini-cli/provider.d.ts +11 -0
- package/dist/core/providers/providers/gemini-cli/provider.js +122 -0
- package/dist/core/providers/providers/gemini-cli/provider.js.map +1 -0
- package/dist/core/providers/providers/openclaw/index.js +8 -0
- package/dist/core/providers/providers/openclaw/index.js.map +1 -1
- package/dist/core/providers/providers/openclaw/provider.js +1 -0
- package/dist/core/providers/providers/openclaw/provider.js.map +1 -1
- package/dist/core/providers/providers/opencode/index.d.ts +4 -0
- package/dist/core/providers/providers/opencode/index.js +27 -0
- package/dist/core/providers/providers/opencode/index.js.map +1 -0
- package/dist/core/providers/providers/opencode/provider.d.ts +12 -0
- package/dist/core/providers/providers/opencode/provider.js +130 -0
- package/dist/core/providers/providers/opencode/provider.js.map +1 -0
- package/dist/core/providers/providers/registry.d.ts +2 -0
- package/dist/core/providers/providers/registry.js +17 -0
- package/dist/core/providers/providers/registry.js.map +1 -0
- package/dist/core/providers/registry.d.ts +2 -1
- package/dist/core/providers/registry.js +40 -0
- package/dist/core/providers/registry.js.map +1 -1
- package/dist/core/providers/types.d.ts +1 -0
- package/dist/core/scenarios/application/scenario-runner.service.js +2 -1
- package/dist/core/scenarios/application/scenario-runner.service.js.map +1 -1
- package/dist/core/sessions/application/session.service.d.ts +0 -2
- package/dist/core/sessions/application/session.service.js +10 -73
- package/dist/core/sessions/application/session.service.js.map +1 -1
- package/dist/core/sessions/domain/session.d.ts +0 -3
- package/dist/core/sessions/domain/session.js.map +1 -1
- package/dist/core/skills/application/skill.service.js +4 -1
- package/dist/core/skills/application/skill.service.js.map +1 -1
- package/dist/core/templates/assets/ceo/BOOTSTRAP.md +3 -3
- package/dist/core/templates/assets/ceo/ROLE.md +5 -3
- package/dist/core/templates/assets/skills/og-board-individual/SKILL.md +66 -89
- package/dist/core/templates/assets/skills/og-board-manager/SKILL.md +64 -45
- package/dist/core/templates/assets/skills/og-boards/SKILL.md +65 -0
- package/dist/core/templates/default-templates.d.ts +1 -2
- package/dist/core/templates/default-templates.js +22 -10
- package/dist/core/templates/default-templates.js.map +1 -1
- package/package.json +3 -2
|
@@ -1,18 +1,24 @@
|
|
|
1
|
-
import { homedir } from "node:os";
|
|
2
|
-
import path from "node:path";
|
|
3
1
|
import { AgentManifestService } from "../../agents/application/agent-manifest.service.js";
|
|
4
2
|
import { AgentService } from "../../agents/application/agent.service.js";
|
|
5
3
|
import { BoardService, } from "../../boards/index.js";
|
|
6
4
|
import { BootstrapService } from "../../bootstrap/application/bootstrap.service.js";
|
|
7
|
-
import { DEFAULT_AGENT_ID, normalizeAgentId } from "../../domain/agent-id.js";
|
|
5
|
+
import { DEFAULT_AGENT_ID, isDefaultAgentId, normalizeAgentId, } from "../../domain/agent-id.js";
|
|
8
6
|
import { createNoopLogger } from "../../logging/index.js";
|
|
9
7
|
import { OrchestrationService, } from "../../orchestration/index.js";
|
|
10
8
|
import { ProviderService } from "../../providers/application/provider.service.js";
|
|
11
9
|
import { ProviderCommandNotFoundError, createDefaultProviderRegistry, } from "../../providers/index.js";
|
|
10
|
+
import { dirname, resolve as resolvePath } from "node:path";
|
|
12
11
|
import { SessionService, } from "../../sessions/index.js";
|
|
13
12
|
import { SkillService, } from "../../skills/index.js";
|
|
13
|
+
import { assertAgentExists, buildBlockedTaskMessage, buildInactiveAgentMessage, buildNotificationSessionRef, buildPendingTaskMessage, buildReporteeStats, buildTodoTaskMessage, collectAllReportees, containsAgentNotFoundMessage, containsAlreadyExistsMessage, extractManagedSkillsDir, extractOpenClawAgents, isSpawnPermissionOrMissing, pathIsWithin, pathMatches, prepareOpenClawCommandEnv, resolveInactiveAgentNotificationTarget, resolveInactiveMinutes, resolveMaxParallelFlows, toErrorMessage, } from "./opengoat.service.helpers.js";
|
|
14
14
|
const OPENCLAW_PROVIDER_ID = "openclaw";
|
|
15
15
|
const OPENCLAW_DEFAULT_AGENT_ID = "main";
|
|
16
|
+
const OPENCLAW_AGENT_SANDBOX_MODE = "off";
|
|
17
|
+
const OPENCLAW_AGENT_TOOLS_ALLOW_ALL_JSON = "[\"*\"]";
|
|
18
|
+
const OPENCLAW_OPENGOAT_PLUGIN_ID = "openclaw-plugin";
|
|
19
|
+
const OPENCLAW_OPENGOAT_PLUGIN_ROOT_ID = "opengoat-plugin";
|
|
20
|
+
const OPENCLAW_OPENGOAT_PLUGIN_LEGACY_PACK_ID = "openclaw-plugin-pack";
|
|
21
|
+
const OPENCLAW_OPENGOAT_PLUGIN_FALLBACK_ID = "workspace";
|
|
16
22
|
export class OpenGoatService {
|
|
17
23
|
fileSystem;
|
|
18
24
|
pathPort;
|
|
@@ -178,9 +184,10 @@ export class OpenGoatService {
|
|
|
178
184
|
const warnings = [];
|
|
179
185
|
let ceoSynced = false;
|
|
180
186
|
let ceoSyncCode;
|
|
181
|
-
let
|
|
182
|
-
|
|
183
|
-
|
|
187
|
+
let localAgents = [];
|
|
188
|
+
const ceoExists = (await this.agentService.listAgents(paths)).some((agent) => agent.id === DEFAULT_AGENT_ID);
|
|
189
|
+
if (!ceoExists) {
|
|
190
|
+
await this.agentService.ensureAgent(paths, {
|
|
184
191
|
id: DEFAULT_AGENT_ID,
|
|
185
192
|
displayName: "CEO",
|
|
186
193
|
}, {
|
|
@@ -188,7 +195,6 @@ export class OpenGoatService {
|
|
|
188
195
|
reportsTo: null,
|
|
189
196
|
role: "CEO",
|
|
190
197
|
});
|
|
191
|
-
ceoDescriptor = created.agent;
|
|
192
198
|
}
|
|
193
199
|
try {
|
|
194
200
|
await this.syncOpenClawRoleSkills(paths, DEFAULT_AGENT_ID);
|
|
@@ -196,42 +202,50 @@ export class OpenGoatService {
|
|
|
196
202
|
catch (error) {
|
|
197
203
|
warnings.push(`OpenClaw role skill assignment sync for "ceo" failed: ${toErrorMessage(error)}`);
|
|
198
204
|
}
|
|
205
|
+
let openClawAgentEntriesById;
|
|
199
206
|
try {
|
|
200
|
-
|
|
201
|
-
providerId: OPENCLAW_PROVIDER_ID,
|
|
202
|
-
displayName: ceoDescriptor.displayName,
|
|
203
|
-
workspaceDir: ceoDescriptor.workspaceDir,
|
|
204
|
-
internalConfigDir: ceoDescriptor.internalConfigDir,
|
|
205
|
-
});
|
|
206
|
-
ceoSyncCode = ceoSync.code;
|
|
207
|
-
ceoSynced =
|
|
208
|
-
ceoSync.code === 0 ||
|
|
209
|
-
containsAlreadyExistsMessage(ceoSync.stdout, ceoSync.stderr);
|
|
210
|
-
if (!ceoSynced) {
|
|
211
|
-
warnings.push(`OpenClaw sync for "ceo" failed (code ${ceoSync.code}). ${(ceoSync.stderr || ceoSync.stdout).trim()}`);
|
|
212
|
-
}
|
|
207
|
+
openClawAgentEntriesById = new Map((await this.listOpenClawAgents(paths)).map((entry) => [entry.id, entry]));
|
|
213
208
|
}
|
|
214
209
|
catch (error) {
|
|
215
|
-
warnings.push(`OpenClaw
|
|
210
|
+
warnings.push(`OpenClaw startup inventory check failed: ${toErrorMessage(error)}`);
|
|
216
211
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
internalConfigDir: ceoDescriptor.internalConfigDir,
|
|
212
|
+
try {
|
|
213
|
+
localAgents = await this.agentService.listAgents(paths);
|
|
214
|
+
for (const agent of localAgents) {
|
|
215
|
+
const sync = await this.syncOpenClawAgentRegistration(paths, {
|
|
216
|
+
descriptor: agent,
|
|
217
|
+
existingEntry: openClawAgentEntriesById?.get(agent.id),
|
|
224
218
|
});
|
|
219
|
+
warnings.push(...sync.warnings);
|
|
220
|
+
if (agent.id === DEFAULT_AGENT_ID) {
|
|
221
|
+
ceoSynced = sync.synced;
|
|
222
|
+
ceoSyncCode = sync.code;
|
|
223
|
+
}
|
|
225
224
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
warnings.push(`OpenClaw startup sync failed: ${toErrorMessage(error)}`);
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
warnings.push(...(await this.syncOpenClawAgentExecutionPolicies(paths, localAgents.map((agent) => agent.id))));
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
warnings.push(`OpenClaw agent policy sync failed: ${toErrorMessage(error)}`);
|
|
229
234
|
}
|
|
230
235
|
try {
|
|
231
|
-
await this.
|
|
236
|
+
warnings.push(...(await this.ensureOpenGoatPluginToolsRegistered(paths)));
|
|
232
237
|
}
|
|
233
238
|
catch (error) {
|
|
234
|
-
warnings.push(`
|
|
239
|
+
warnings.push(`OpenClaw plugin tool sync failed: ${toErrorMessage(error)}`);
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
const agents = await this.agentService.listAgents(paths);
|
|
243
|
+
for (const agent of agents) {
|
|
244
|
+
await this.agentService.ensureAgentWorkspaceCommandShim(paths, agent.id);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
warnings.push(`OpenGoat workspace command shim sync failed: ${toErrorMessage(error)}`);
|
|
235
249
|
}
|
|
236
250
|
return {
|
|
237
251
|
ceoSyncCode,
|
|
@@ -241,6 +255,10 @@ export class OpenGoatService {
|
|
|
241
255
|
}
|
|
242
256
|
async createAgent(rawName, options = {}) {
|
|
243
257
|
const identity = this.agentService.normalizeAgentName(rawName);
|
|
258
|
+
const managerAgentId = resolveCreateAgentManagerId(identity.id, options.reportsTo);
|
|
259
|
+
if (managerAgentId) {
|
|
260
|
+
await this.assertManagerSupportsReportees(managerAgentId);
|
|
261
|
+
}
|
|
244
262
|
const paths = this.pathsProvider.getPaths();
|
|
245
263
|
const created = await this.agentService.ensureAgent(paths, identity, {
|
|
246
264
|
type: options.type,
|
|
@@ -287,12 +305,15 @@ export class OpenGoatService {
|
|
|
287
305
|
}
|
|
288
306
|
throw new Error(`OpenClaw agent location sync failed for "${created.agent.id}". ${toErrorMessage(error)}`);
|
|
289
307
|
}
|
|
308
|
+
await this.syncOpenClawAgentExecutionPolicies(paths, [created.agent.id]);
|
|
290
309
|
try {
|
|
291
310
|
const workspaceBootstrap = await this.agentService.ensureAgentWorkspaceBootstrap(paths, {
|
|
292
311
|
agentId: created.agent.id,
|
|
293
312
|
displayName: created.agent.displayName,
|
|
294
313
|
role: options.role?.trim() ??
|
|
295
314
|
(created.alreadyExisted ? created.agent.role : ""),
|
|
315
|
+
}, {
|
|
316
|
+
syncBootstrapMarkdown: false,
|
|
296
317
|
});
|
|
297
318
|
created.createdPaths.push(...workspaceBootstrap.createdPaths);
|
|
298
319
|
created.skippedPaths.push(...workspaceBootstrap.skippedPaths);
|
|
@@ -390,12 +411,13 @@ export class OpenGoatService {
|
|
|
390
411
|
this.agentService.listAgents(paths),
|
|
391
412
|
this.agentManifestService.listManifests(paths),
|
|
392
413
|
]);
|
|
414
|
+
const reporteeStats = buildReporteeStats(manifests);
|
|
393
415
|
const descriptorsById = new Map(agents.map((agent) => [agent.id, agent]));
|
|
394
416
|
const agent = descriptorsById.get(agentId);
|
|
395
417
|
if (!agent) {
|
|
396
418
|
throw new Error(`Agent "${agentId}" does not exist.`);
|
|
397
419
|
}
|
|
398
|
-
const totalReportees =
|
|
420
|
+
const totalReportees = reporteeStats.totalByManager.get(agentId) ?? 0;
|
|
399
421
|
const directReportees = manifests
|
|
400
422
|
.filter((manifest) => manifest.metadata.reportsTo === agentId)
|
|
401
423
|
.map((manifest) => {
|
|
@@ -408,8 +430,7 @@ export class OpenGoatService {
|
|
|
408
430
|
id: manifest.agentId,
|
|
409
431
|
name,
|
|
410
432
|
role,
|
|
411
|
-
totalReportees:
|
|
412
|
-
.length,
|
|
433
|
+
totalReportees: reporteeStats.totalByManager.get(manifest.agentId) ?? 0,
|
|
413
434
|
};
|
|
414
435
|
})
|
|
415
436
|
.sort((left, right) => left.id.localeCompare(right.id));
|
|
@@ -445,7 +466,9 @@ export class OpenGoatService {
|
|
|
445
466
|
}
|
|
446
467
|
async setAgentProvider(agentId, providerId) {
|
|
447
468
|
const paths = this.pathsProvider.getPaths();
|
|
448
|
-
|
|
469
|
+
const binding = await this.providerService.setAgentProvider(paths, agentId, providerId);
|
|
470
|
+
await this.ensureAgentProviderRoleSkills(paths, binding.agentId);
|
|
471
|
+
return binding;
|
|
449
472
|
}
|
|
450
473
|
async getOpenClawGatewayConfig() {
|
|
451
474
|
const paths = this.pathsProvider.getPaths();
|
|
@@ -503,94 +526,161 @@ export class OpenGoatService {
|
|
|
503
526
|
const paths = this.pathsProvider.getPaths();
|
|
504
527
|
return this.boardService.addTaskWorklog(paths, actorId, taskId, content);
|
|
505
528
|
}
|
|
529
|
+
async assertManagerSupportsReportees(managerAgentId) {
|
|
530
|
+
const paths = this.pathsProvider.getPaths();
|
|
531
|
+
const agents = await this.agentService.listAgents(paths);
|
|
532
|
+
if (!agents.some((agent) => agent.id === managerAgentId)) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
const managerBinding = await this.providerService.getAgentProvider(paths, managerAgentId);
|
|
536
|
+
const providers = await this.providerService.listProviders();
|
|
537
|
+
const provider = providers.find((candidate) => candidate.id === managerBinding.providerId);
|
|
538
|
+
if (!provider) {
|
|
539
|
+
throw new Error(`Provider "${managerBinding.providerId}" is not available for manager "${managerAgentId}".`);
|
|
540
|
+
}
|
|
541
|
+
if (!provider.capabilities.reportees) {
|
|
542
|
+
throw new Error(`Cannot assign "${managerAgentId}" as manager because provider "${provider.displayName}" does not support reportees.`);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
506
545
|
async runTaskCronCycle(options = {}) {
|
|
507
546
|
const paths = this.pathsProvider.getPaths();
|
|
508
547
|
const ranAt = this.resolveNowIso();
|
|
509
548
|
const manifests = await this.agentManifestService.listManifests(paths);
|
|
510
|
-
const manifestsById = new Map(manifests.map((manifest) => [manifest.agentId, manifest]));
|
|
511
549
|
const inactiveMinutes = resolveInactiveMinutes(options.inactiveMinutes);
|
|
512
550
|
const notificationTarget = resolveInactiveAgentNotificationTarget(options.notificationTarget);
|
|
513
551
|
const notifyInactiveAgents = options.notifyInactiveAgents ?? true;
|
|
552
|
+
const maxParallelFlows = resolveMaxParallelFlows(options.maxParallelFlows);
|
|
514
553
|
const inactiveCandidates = notifyInactiveAgents
|
|
515
554
|
? await this.collectInactiveAgents(paths, manifests, inactiveMinutes, notificationTarget)
|
|
516
555
|
: [];
|
|
517
|
-
const latestCeoProjectPath = notifyInactiveAgents
|
|
518
|
-
? await this.resolveLatestProjectPathForAgent(paths, DEFAULT_AGENT_ID)
|
|
519
|
-
: undefined;
|
|
520
556
|
const tasks = await this.boardService.listTasks(paths, { limit: 10_000 });
|
|
521
|
-
const
|
|
522
|
-
|
|
557
|
+
const pendingTaskIds = new Set(await this.boardService.listPendingTaskIdsOlderThan(paths, inactiveMinutes));
|
|
558
|
+
const taskStatusDispatch = await this.dispatchTaskStatusAutomations(paths, tasks, manifests, pendingTaskIds, inactiveMinutes, ranAt, maxParallelFlows);
|
|
559
|
+
const inactiveDispatches = await this.dispatchInactiveAgentAutomations(paths, inactiveCandidates, inactiveMinutes, ranAt, maxParallelFlows);
|
|
560
|
+
const dispatches = [
|
|
561
|
+
...taskStatusDispatch.dispatches,
|
|
562
|
+
...inactiveDispatches,
|
|
563
|
+
];
|
|
564
|
+
const failed = dispatches.filter((entry) => !entry.ok).length;
|
|
565
|
+
return {
|
|
566
|
+
ranAt,
|
|
567
|
+
scannedTasks: tasks.length,
|
|
568
|
+
todoTasks: taskStatusDispatch.todoTasks,
|
|
569
|
+
blockedTasks: taskStatusDispatch.blockedTasks,
|
|
570
|
+
inactiveAgents: inactiveCandidates.length,
|
|
571
|
+
sent: dispatches.length - failed,
|
|
572
|
+
failed,
|
|
573
|
+
dispatches,
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
async dispatchTaskStatusAutomations(paths, tasks, manifests, pendingTaskIds, pendingMinutes, notificationTimestamp, maxParallelFlows) {
|
|
577
|
+
const manifestsById = new Map(manifests.map((manifest) => [manifest.agentId, manifest]));
|
|
578
|
+
const requests = [];
|
|
523
579
|
let todoTasks = 0;
|
|
580
|
+
let pendingTasks = 0;
|
|
524
581
|
let blockedTasks = 0;
|
|
525
582
|
for (const task of tasks) {
|
|
526
|
-
if (task.status !== "todo" && task.status !== "blocked") {
|
|
527
|
-
continue;
|
|
528
|
-
}
|
|
529
583
|
if (task.status === "todo") {
|
|
530
584
|
todoTasks += 1;
|
|
531
585
|
const targetAgentId = task.assignedTo;
|
|
532
|
-
const sessionRef =
|
|
533
|
-
const message = buildTodoTaskMessage({
|
|
534
|
-
|
|
535
|
-
|
|
586
|
+
const sessionRef = buildNotificationSessionRef(targetAgentId);
|
|
587
|
+
const message = buildTodoTaskMessage({
|
|
588
|
+
task,
|
|
589
|
+
notificationTimestamp,
|
|
590
|
+
});
|
|
591
|
+
requests.push({
|
|
536
592
|
kind: "todo",
|
|
537
593
|
targetAgentId,
|
|
538
594
|
sessionRef,
|
|
539
595
|
taskId: task.taskId,
|
|
540
|
-
|
|
541
|
-
error: result.error,
|
|
596
|
+
message,
|
|
542
597
|
});
|
|
543
598
|
continue;
|
|
544
599
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
600
|
+
if (task.status === "pending") {
|
|
601
|
+
if (!pendingTaskIds.has(task.taskId)) {
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
pendingTasks += 1;
|
|
605
|
+
const targetAgentId = task.assignedTo;
|
|
606
|
+
const sessionRef = buildNotificationSessionRef(targetAgentId);
|
|
607
|
+
const message = buildPendingTaskMessage({
|
|
608
|
+
task,
|
|
609
|
+
pendingMinutes,
|
|
610
|
+
notificationTimestamp,
|
|
611
|
+
});
|
|
612
|
+
requests.push({
|
|
613
|
+
kind: "pending",
|
|
614
|
+
targetAgentId,
|
|
615
|
+
sessionRef,
|
|
616
|
+
taskId: task.taskId,
|
|
617
|
+
message,
|
|
618
|
+
});
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
if (task.status === "blocked") {
|
|
622
|
+
blockedTasks += 1;
|
|
623
|
+
const assigneeManifest = manifestsById.get(task.assignedTo);
|
|
624
|
+
const managerAgentId = normalizeAgentId(assigneeManifest?.metadata.reportsTo ?? "") ||
|
|
625
|
+
DEFAULT_AGENT_ID;
|
|
626
|
+
const sessionRef = buildNotificationSessionRef(managerAgentId);
|
|
627
|
+
const message = buildBlockedTaskMessage({
|
|
628
|
+
task,
|
|
629
|
+
notificationTimestamp,
|
|
630
|
+
});
|
|
631
|
+
requests.push({
|
|
632
|
+
kind: "blocked",
|
|
633
|
+
targetAgentId: managerAgentId,
|
|
634
|
+
sessionRef,
|
|
635
|
+
taskId: task.taskId,
|
|
636
|
+
message,
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
const dispatches = await runWithConcurrencyByKey(requests, maxParallelFlows, (request) => normalizeAgentId(request.targetAgentId) || DEFAULT_AGENT_ID, async (request) => {
|
|
641
|
+
const result = await this.dispatchAutomationMessage(paths, request.targetAgentId, request.sessionRef, request.message);
|
|
642
|
+
return {
|
|
643
|
+
kind: request.kind,
|
|
644
|
+
targetAgentId: request.targetAgentId,
|
|
645
|
+
sessionRef: request.sessionRef,
|
|
646
|
+
taskId: request.taskId,
|
|
647
|
+
message: request.message,
|
|
557
648
|
ok: result.ok,
|
|
558
649
|
error: result.error,
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
|
|
650
|
+
};
|
|
651
|
+
});
|
|
652
|
+
return {
|
|
653
|
+
dispatches,
|
|
654
|
+
todoTasks,
|
|
655
|
+
pendingTasks,
|
|
656
|
+
blockedTasks,
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
async dispatchInactiveAgentAutomations(paths, inactiveCandidates, inactiveMinutes, notificationTimestamp, maxParallelFlows = 1) {
|
|
660
|
+
return runWithConcurrencyByKey(inactiveCandidates, maxParallelFlows, (candidate) => normalizeAgentId(candidate.managerAgentId) || DEFAULT_AGENT_ID, async (candidate) => {
|
|
661
|
+
const sessionRef = buildNotificationSessionRef(candidate.managerAgentId);
|
|
563
662
|
const message = buildInactiveAgentMessage({
|
|
564
663
|
managerAgentId: candidate.managerAgentId,
|
|
565
664
|
subjectAgentId: candidate.subjectAgentId,
|
|
566
665
|
subjectName: candidate.subjectName,
|
|
567
666
|
role: candidate.role,
|
|
667
|
+
directReporteesCount: candidate.directReporteesCount,
|
|
668
|
+
indirectReporteesCount: candidate.indirectReporteesCount,
|
|
568
669
|
inactiveMinutes,
|
|
670
|
+
notificationTimestamp,
|
|
569
671
|
lastActionTimestamp: candidate.lastActionTimestamp,
|
|
570
672
|
});
|
|
571
|
-
const result = await this.dispatchAutomationMessage(paths, candidate.managerAgentId, sessionRef, message
|
|
572
|
-
|
|
573
|
-
});
|
|
574
|
-
dispatches.push({
|
|
673
|
+
const result = await this.dispatchAutomationMessage(paths, candidate.managerAgentId, sessionRef, message);
|
|
674
|
+
return {
|
|
575
675
|
kind: "inactive",
|
|
576
676
|
targetAgentId: candidate.managerAgentId,
|
|
577
677
|
sessionRef,
|
|
578
678
|
subjectAgentId: candidate.subjectAgentId,
|
|
679
|
+
message,
|
|
579
680
|
ok: result.ok,
|
|
580
681
|
error: result.error,
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
const failed = dispatches.filter((entry) => !entry.ok).length;
|
|
584
|
-
return {
|
|
585
|
-
ranAt,
|
|
586
|
-
scannedTasks,
|
|
587
|
-
todoTasks,
|
|
588
|
-
blockedTasks,
|
|
589
|
-
inactiveAgents: inactiveCandidates.length,
|
|
590
|
-
sent: dispatches.length - failed,
|
|
591
|
-
failed,
|
|
592
|
-
dispatches,
|
|
593
|
-
};
|
|
682
|
+
};
|
|
683
|
+
});
|
|
594
684
|
}
|
|
595
685
|
async listSkills(agentId = DEFAULT_AGENT_ID) {
|
|
596
686
|
const paths = this.pathsProvider.getPaths();
|
|
@@ -645,7 +735,6 @@ export class OpenGoatService {
|
|
|
645
735
|
const paths = this.pathsProvider.getPaths();
|
|
646
736
|
const prepared = await this.sessionService.prepareRunSession(paths, agentId, {
|
|
647
737
|
sessionRef: options.sessionRef,
|
|
648
|
-
projectPath: options.projectPath,
|
|
649
738
|
forceNew: options.forceNew,
|
|
650
739
|
userMessage: "",
|
|
651
740
|
});
|
|
@@ -689,6 +778,7 @@ export class OpenGoatService {
|
|
|
689
778
|
const result = await this.orchestrationService.runAgent(paths, agentId, {
|
|
690
779
|
message,
|
|
691
780
|
sessionRef,
|
|
781
|
+
disableSession: options.disableSession ?? false,
|
|
692
782
|
cwd: options.cwd,
|
|
693
783
|
env: process.env,
|
|
694
784
|
});
|
|
@@ -743,7 +833,7 @@ export class OpenGoatService {
|
|
|
743
833
|
return;
|
|
744
834
|
}
|
|
745
835
|
syncedAgents.add(targetAgentId);
|
|
746
|
-
const sync = await this.
|
|
836
|
+
const sync = await this.ensureAgentProviderRoleSkills(paths, targetAgentId);
|
|
747
837
|
createdPaths.push(...sync.createdPaths);
|
|
748
838
|
skippedPaths.push(...sync.skippedPaths);
|
|
749
839
|
removedPaths.push(...sync.removedPaths);
|
|
@@ -781,6 +871,7 @@ export class OpenGoatService {
|
|
|
781
871
|
for (const legacySkillId of [
|
|
782
872
|
"board-manager",
|
|
783
873
|
"board-individual",
|
|
874
|
+
"og-boards",
|
|
784
875
|
"og-board-manager",
|
|
785
876
|
"og-board-individual",
|
|
786
877
|
"manager",
|
|
@@ -800,6 +891,18 @@ export class OpenGoatService {
|
|
|
800
891
|
removedPaths,
|
|
801
892
|
};
|
|
802
893
|
}
|
|
894
|
+
async ensureAgentProviderRoleSkills(paths, rawAgentId) {
|
|
895
|
+
const agentId = normalizeAgentId(rawAgentId);
|
|
896
|
+
if (!agentId) {
|
|
897
|
+
throw new Error("Agent id cannot be empty.");
|
|
898
|
+
}
|
|
899
|
+
const runtimeProfile = await this.providerService.getAgentRuntimeProfile(paths, agentId);
|
|
900
|
+
const managedRoleSkillDirectories = await this.providerService.listProviderRoleSkillDirectories();
|
|
901
|
+
return this.agentService.ensureAgentWorkspaceRoleSkills(paths, agentId, {
|
|
902
|
+
requiredSkillDirectories: runtimeProfile.roleSkillDirectories,
|
|
903
|
+
managedSkillDirectories: managedRoleSkillDirectories,
|
|
904
|
+
});
|
|
905
|
+
}
|
|
803
906
|
async resolveOpenClawManagedSkillsDir(paths) {
|
|
804
907
|
if (this.openClawManagedSkillsDirCache !== undefined) {
|
|
805
908
|
return this.openClawManagedSkillsDirCache;
|
|
@@ -809,20 +912,28 @@ export class OpenGoatService {
|
|
|
809
912
|
...process.env,
|
|
810
913
|
...(providerConfig?.env ?? {}),
|
|
811
914
|
};
|
|
812
|
-
const skillsList = await this.runOpenClaw(["skills", "list", "--json"], {
|
|
813
|
-
env,
|
|
814
|
-
});
|
|
815
|
-
if (skillsList.code !== 0) {
|
|
816
|
-
throw new Error(`OpenClaw skills list failed (exit ${skillsList.code}). ${skillsList.stderr.trim() || skillsList.stdout.trim() || ""}`.trim());
|
|
817
|
-
}
|
|
818
|
-
let parsed;
|
|
819
915
|
try {
|
|
820
|
-
|
|
916
|
+
const skillsList = await this.runOpenClaw(["skills", "list", "--json"], {
|
|
917
|
+
env,
|
|
918
|
+
});
|
|
919
|
+
if (skillsList.code !== 0) {
|
|
920
|
+
throw new Error(`OpenClaw skills list failed (exit ${skillsList.code}). ${skillsList.stderr.trim() || skillsList.stdout.trim() || ""}`.trim());
|
|
921
|
+
}
|
|
922
|
+
const parsed = parseLooseJson(skillsList.stdout);
|
|
923
|
+
if (parsed === undefined) {
|
|
924
|
+
throw new Error("OpenClaw skills list returned non-JSON output; cannot resolve managed skills directory.");
|
|
925
|
+
}
|
|
926
|
+
const managedSkillsDir = extractManagedSkillsDir(parsed);
|
|
927
|
+
this.openClawManagedSkillsDirCache = managedSkillsDir;
|
|
928
|
+
return managedSkillsDir;
|
|
821
929
|
}
|
|
822
|
-
catch {
|
|
823
|
-
|
|
930
|
+
catch (error) {
|
|
931
|
+
if (!(error instanceof ProviderCommandNotFoundError)) {
|
|
932
|
+
throw error;
|
|
933
|
+
}
|
|
824
934
|
}
|
|
825
|
-
const
|
|
935
|
+
const skillsStatus = await this.providerService.getOpenClawSkillsStatusViaGateway(paths, env);
|
|
936
|
+
const managedSkillsDir = extractManagedSkillsDir(skillsStatus);
|
|
826
937
|
this.openClawManagedSkillsDirCache = managedSkillsDir;
|
|
827
938
|
return managedSkillsDir;
|
|
828
939
|
}
|
|
@@ -831,20 +942,30 @@ export class OpenGoatService {
|
|
|
831
942
|
return [];
|
|
832
943
|
}
|
|
833
944
|
const env = await this.resolveOpenClawEnv(paths);
|
|
834
|
-
const listed = await this.runOpenClaw(["agents", "list", "--json"], {
|
|
835
|
-
env,
|
|
836
|
-
});
|
|
837
|
-
if (listed.code !== 0) {
|
|
838
|
-
throw new Error(`OpenClaw agents list failed (exit ${listed.code}). ${listed.stderr.trim() || listed.stdout.trim() || ""}`.trim());
|
|
839
|
-
}
|
|
840
|
-
let parsed;
|
|
841
945
|
try {
|
|
842
|
-
|
|
946
|
+
const listed = await this.runOpenClaw(["agents", "list", "--json"], {
|
|
947
|
+
env,
|
|
948
|
+
});
|
|
949
|
+
if (listed.code !== 0) {
|
|
950
|
+
throw new Error(`OpenClaw agents list failed (exit ${listed.code}). ${listed.stderr.trim() || listed.stdout.trim() || ""}`.trim());
|
|
951
|
+
}
|
|
952
|
+
const parsed = parseLooseJson(listed.stdout);
|
|
953
|
+
if (parsed === undefined) {
|
|
954
|
+
throw new Error("OpenClaw agents list returned non-JSON output; cannot inspect agents.");
|
|
955
|
+
}
|
|
956
|
+
return extractOpenClawAgents(parsed);
|
|
843
957
|
}
|
|
844
|
-
catch {
|
|
845
|
-
|
|
958
|
+
catch (error) {
|
|
959
|
+
if (!(error instanceof ProviderCommandNotFoundError)) {
|
|
960
|
+
throw error;
|
|
961
|
+
}
|
|
846
962
|
}
|
|
847
|
-
|
|
963
|
+
const entries = await this.providerService.listOpenClawAgentsViaGateway(paths, env);
|
|
964
|
+
return entries.map((entry) => ({
|
|
965
|
+
id: entry.id,
|
|
966
|
+
workspace: entry.workspace,
|
|
967
|
+
agentDir: entry.agentDir,
|
|
968
|
+
}));
|
|
848
969
|
}
|
|
849
970
|
async addWorkspaceAgentCandidates(paths, candidates, warnings) {
|
|
850
971
|
try {
|
|
@@ -861,25 +982,66 @@ export class OpenGoatService {
|
|
|
861
982
|
warnings.push(`Workspace fallback discovery failed: ${toErrorMessage(error)}`);
|
|
862
983
|
}
|
|
863
984
|
}
|
|
864
|
-
async
|
|
865
|
-
|
|
866
|
-
|
|
985
|
+
async syncOpenClawAgentRegistration(paths, params) {
|
|
986
|
+
const warnings = [];
|
|
987
|
+
if (params.existingEntry &&
|
|
988
|
+
pathMatches(params.existingEntry.workspace, params.descriptor.workspaceDir) &&
|
|
989
|
+
pathMatches(params.existingEntry.agentDir, params.descriptor.internalConfigDir)) {
|
|
990
|
+
return {
|
|
991
|
+
synced: true,
|
|
992
|
+
code: 0,
|
|
993
|
+
warnings,
|
|
994
|
+
};
|
|
867
995
|
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
996
|
+
let runtimeSync;
|
|
997
|
+
try {
|
|
998
|
+
runtimeSync = await this.providerService.createProviderAgent(paths, params.descriptor.id, {
|
|
999
|
+
providerId: OPENCLAW_PROVIDER_ID,
|
|
1000
|
+
displayName: params.descriptor.displayName,
|
|
1001
|
+
workspaceDir: params.descriptor.workspaceDir,
|
|
1002
|
+
internalConfigDir: params.descriptor.internalConfigDir,
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
catch (error) {
|
|
1006
|
+
warnings.push(`OpenClaw sync for "${params.descriptor.id}" failed: ${toErrorMessage(error)}`);
|
|
1007
|
+
return {
|
|
1008
|
+
synced: false,
|
|
1009
|
+
warnings,
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
const synced = runtimeSync.code === 0 ||
|
|
1013
|
+
containsAlreadyExistsMessage(runtimeSync.stdout, runtimeSync.stderr);
|
|
1014
|
+
if (!synced) {
|
|
1015
|
+
warnings.push(`OpenClaw sync for "${params.descriptor.id}" failed (code ${runtimeSync.code}). ${(runtimeSync.stderr || runtimeSync.stdout).trim()}`);
|
|
1016
|
+
return {
|
|
1017
|
+
synced,
|
|
1018
|
+
code: runtimeSync.code,
|
|
1019
|
+
warnings,
|
|
1020
|
+
};
|
|
874
1021
|
}
|
|
875
|
-
let parsed;
|
|
876
1022
|
try {
|
|
877
|
-
|
|
1023
|
+
await this.ensureOpenClawAgentLocation(paths, {
|
|
1024
|
+
agentId: params.descriptor.id,
|
|
1025
|
+
displayName: params.descriptor.displayName,
|
|
1026
|
+
workspaceDir: params.descriptor.workspaceDir,
|
|
1027
|
+
internalConfigDir: params.descriptor.internalConfigDir,
|
|
1028
|
+
}, params.existingEntry);
|
|
878
1029
|
}
|
|
879
|
-
catch {
|
|
880
|
-
|
|
1030
|
+
catch (error) {
|
|
1031
|
+
warnings.push(`OpenClaw location sync for "${params.descriptor.id}" failed: ${toErrorMessage(error)}`);
|
|
881
1032
|
}
|
|
882
|
-
|
|
1033
|
+
return {
|
|
1034
|
+
synced,
|
|
1035
|
+
code: runtimeSync.code,
|
|
1036
|
+
warnings,
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
async ensureOpenClawAgentLocation(paths, params, existingEntry) {
|
|
1040
|
+
if (!this.commandRunner) {
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
const entry = existingEntry ??
|
|
1044
|
+
(await this.listOpenClawAgents(paths)).find((candidate) => candidate.id === normalizeAgentId(params.agentId));
|
|
883
1045
|
if (!entry) {
|
|
884
1046
|
return;
|
|
885
1047
|
}
|
|
@@ -902,6 +1064,219 @@ export class OpenGoatService {
|
|
|
902
1064
|
throw new Error(`OpenClaw agent location repair failed creating "${params.agentId}" (exit ${recreated.code}). ${recreated.stderr.trim() || recreated.stdout.trim() || ""}`.trim());
|
|
903
1065
|
}
|
|
904
1066
|
}
|
|
1067
|
+
async syncOpenClawAgentExecutionPolicies(paths, rawAgentIds) {
|
|
1068
|
+
if (!this.commandRunner) {
|
|
1069
|
+
return [];
|
|
1070
|
+
}
|
|
1071
|
+
const agentIds = [
|
|
1072
|
+
...new Set(rawAgentIds
|
|
1073
|
+
.map((agentId) => normalizeAgentId(agentId))
|
|
1074
|
+
.filter((agentId) => Boolean(agentId))),
|
|
1075
|
+
];
|
|
1076
|
+
if (agentIds.length === 0) {
|
|
1077
|
+
return [];
|
|
1078
|
+
}
|
|
1079
|
+
const warnings = [];
|
|
1080
|
+
const env = await this.resolveOpenClawEnv(paths);
|
|
1081
|
+
let entries;
|
|
1082
|
+
try {
|
|
1083
|
+
const listResult = await this.runOpenClaw(["config", "get", "agents.list"], {
|
|
1084
|
+
env,
|
|
1085
|
+
});
|
|
1086
|
+
if (listResult.code !== 0) {
|
|
1087
|
+
warnings.push(`OpenClaw config read failed (agents.list, code ${listResult.code}). ${listResult.stderr.trim() || listResult.stdout.trim() || ""}`.trim());
|
|
1088
|
+
return warnings;
|
|
1089
|
+
}
|
|
1090
|
+
const parsed = parseLooseJson(listResult.stdout);
|
|
1091
|
+
if (parsed === undefined) {
|
|
1092
|
+
warnings.push("OpenClaw config read returned non-JSON for agents.list; skipping sandbox/tools policy sync.");
|
|
1093
|
+
return warnings;
|
|
1094
|
+
}
|
|
1095
|
+
if (!Array.isArray(parsed)) {
|
|
1096
|
+
warnings.push("OpenClaw config agents.list is not an array; skipping sandbox/tools policy sync.");
|
|
1097
|
+
return warnings;
|
|
1098
|
+
}
|
|
1099
|
+
entries = parsed;
|
|
1100
|
+
}
|
|
1101
|
+
catch (error) {
|
|
1102
|
+
if (!(error instanceof ProviderCommandNotFoundError)) {
|
|
1103
|
+
throw error;
|
|
1104
|
+
}
|
|
1105
|
+
return this.providerService.syncOpenClawAgentExecutionPoliciesViaGateway(paths, agentIds, env);
|
|
1106
|
+
}
|
|
1107
|
+
const indexById = new Map();
|
|
1108
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
1109
|
+
const entry = entries[index];
|
|
1110
|
+
if (!entry) {
|
|
1111
|
+
continue;
|
|
1112
|
+
}
|
|
1113
|
+
const id = normalizeAgentConfigEntryId(entry);
|
|
1114
|
+
if (!id || indexById.has(id)) {
|
|
1115
|
+
continue;
|
|
1116
|
+
}
|
|
1117
|
+
indexById.set(id, index);
|
|
1118
|
+
}
|
|
1119
|
+
for (const agentId of agentIds) {
|
|
1120
|
+
const index = indexById.get(agentId);
|
|
1121
|
+
if (index === undefined) {
|
|
1122
|
+
warnings.push(`OpenClaw config policy sync skipped for "${agentId}" because no agents.list entry was found.`);
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
const entry = asRecord(entries[index]);
|
|
1126
|
+
if (readAgentSandboxMode(entry) !== OPENCLAW_AGENT_SANDBOX_MODE) {
|
|
1127
|
+
const sandboxSet = await this.runOpenClaw(["config", "set", `agents.list[${index}].sandbox.mode`, OPENCLAW_AGENT_SANDBOX_MODE], { env });
|
|
1128
|
+
if (sandboxSet.code !== 0) {
|
|
1129
|
+
warnings.push(`OpenClaw sandbox policy sync failed for "${agentId}" (code ${sandboxSet.code}). ${sandboxSet.stderr.trim() || sandboxSet.stdout.trim() || ""}`.trim());
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
if (!hasAgentToolsAllowAll(entry)) {
|
|
1133
|
+
const toolsSet = await this.runOpenClaw(["config", "set", `agents.list[${index}].tools.allow`, OPENCLAW_AGENT_TOOLS_ALLOW_ALL_JSON], { env });
|
|
1134
|
+
if (toolsSet.code !== 0) {
|
|
1135
|
+
warnings.push(`OpenClaw tools policy sync failed for "${agentId}" (code ${toolsSet.code}). ${toolsSet.stderr.trim() || toolsSet.stdout.trim() || ""}`.trim());
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return warnings;
|
|
1140
|
+
}
|
|
1141
|
+
async ensureOpenGoatPluginToolsRegistered(paths) {
|
|
1142
|
+
if (!this.commandRunner) {
|
|
1143
|
+
return [];
|
|
1144
|
+
}
|
|
1145
|
+
const warnings = [];
|
|
1146
|
+
const env = await this.resolveOpenClawEnv(paths);
|
|
1147
|
+
const pluginSourcePath = await this.resolveOpenGoatPluginSourcePath();
|
|
1148
|
+
if (!pluginSourcePath) {
|
|
1149
|
+
warnings.push("OpenClaw OpenGoat plugin source path was not found; OpenGoat tools may be unavailable to agents.");
|
|
1150
|
+
return warnings;
|
|
1151
|
+
}
|
|
1152
|
+
warnings.push(...(await this.configureOpenClawPluginSourcePath(env, pluginSourcePath)));
|
|
1153
|
+
return warnings;
|
|
1154
|
+
}
|
|
1155
|
+
async resolveOpenGoatPluginSourcePath() {
|
|
1156
|
+
const explicit = process.env.OPENGOAT_OPENCLAW_PLUGIN_PATH?.trim();
|
|
1157
|
+
const argvEntry = process.argv[1]?.trim();
|
|
1158
|
+
const argvDir = argvEntry ? dirname(resolvePath(argvEntry)) : undefined;
|
|
1159
|
+
const argvPathCandidates = argvDir
|
|
1160
|
+
? collectPluginPathCandidatesFromArgvDir(argvDir)
|
|
1161
|
+
: [];
|
|
1162
|
+
const candidates = dedupeStrings([
|
|
1163
|
+
explicit,
|
|
1164
|
+
...argvPathCandidates,
|
|
1165
|
+
resolvePath(process.cwd(), "packages", "openclaw-plugin"),
|
|
1166
|
+
resolvePath(process.cwd(), "dist", "openclaw-plugin"),
|
|
1167
|
+
resolvePath(process.cwd(), "node_modules", "@opengoat", "openclaw-plugin"),
|
|
1168
|
+
argvDir ? resolvePath(argvDir, "..", "dist", "openclaw-plugin") : undefined,
|
|
1169
|
+
argvDir
|
|
1170
|
+
? resolvePath(argvDir, "..", "node_modules", "@opengoat", "openclaw-plugin")
|
|
1171
|
+
: undefined,
|
|
1172
|
+
argvDir
|
|
1173
|
+
? resolvePath(argvDir, "..", "..", "@opengoat", "openclaw-plugin")
|
|
1174
|
+
: undefined,
|
|
1175
|
+
]);
|
|
1176
|
+
for (const candidate of candidates) {
|
|
1177
|
+
const pluginManifestPath = this.pathPort.join(candidate, "openclaw.plugin.json");
|
|
1178
|
+
if (await this.fileSystem.exists(pluginManifestPath)) {
|
|
1179
|
+
return candidate;
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
return undefined;
|
|
1183
|
+
}
|
|
1184
|
+
async configureOpenClawPluginSourcePath(env, pluginSourcePath) {
|
|
1185
|
+
const warnings = [];
|
|
1186
|
+
const currentPathsResult = await this.runOpenClaw(["config", "get", "plugins.load.paths"], { env });
|
|
1187
|
+
const currentPaths = currentPathsResult.code === 0
|
|
1188
|
+
? readStringArray(parseLooseJson(currentPathsResult.stdout))
|
|
1189
|
+
: [];
|
|
1190
|
+
const mergedPaths = await this.mergePluginLoadPaths(pluginSourcePath, currentPaths);
|
|
1191
|
+
if (!samePathList(currentPaths, mergedPaths)) {
|
|
1192
|
+
const setPaths = await this.runOpenClaw(["config", "set", "plugins.load.paths", JSON.stringify(mergedPaths)], { env });
|
|
1193
|
+
if (setPaths.code !== 0) {
|
|
1194
|
+
warnings.push(`OpenClaw plugin source path update failed (code ${setPaths.code}). ${setPaths.stderr.trim() || setPaths.stdout.trim() || ""}`.trim());
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
const pluginIds = [
|
|
1198
|
+
OPENCLAW_OPENGOAT_PLUGIN_ID,
|
|
1199
|
+
OPENCLAW_OPENGOAT_PLUGIN_ROOT_ID,
|
|
1200
|
+
OPENCLAW_OPENGOAT_PLUGIN_LEGACY_PACK_ID,
|
|
1201
|
+
OPENCLAW_OPENGOAT_PLUGIN_FALLBACK_ID,
|
|
1202
|
+
];
|
|
1203
|
+
const enableFailures = [];
|
|
1204
|
+
let pluginEnabled = false;
|
|
1205
|
+
let enabledPluginId;
|
|
1206
|
+
for (const pluginId of pluginIds) {
|
|
1207
|
+
const enablePlugin = await this.runOpenClaw(["config", "set", `plugins.entries.${pluginId}.enabled`, "true"], { env });
|
|
1208
|
+
if (enablePlugin.code === 0) {
|
|
1209
|
+
pluginEnabled = true;
|
|
1210
|
+
enabledPluginId = pluginId;
|
|
1211
|
+
break;
|
|
1212
|
+
}
|
|
1213
|
+
const message = enablePlugin.stderr.trim() || enablePlugin.stdout.trim() || "";
|
|
1214
|
+
if (isPluginNotFoundMessage(message)) {
|
|
1215
|
+
continue;
|
|
1216
|
+
}
|
|
1217
|
+
enableFailures.push(`OpenClaw plugin enable failed for "${pluginId}" (code ${enablePlugin.code}). ${message}`.trim());
|
|
1218
|
+
}
|
|
1219
|
+
if (!pluginEnabled) {
|
|
1220
|
+
if (enableFailures.length === 0) {
|
|
1221
|
+
warnings.push(`OpenClaw plugin enable failed: no matching plugin id was found (${pluginIds.join(", ")}).`);
|
|
1222
|
+
}
|
|
1223
|
+
else {
|
|
1224
|
+
warnings.push(...enableFailures);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
if (enabledPluginId) {
|
|
1228
|
+
const idsToDisable = pluginIds.filter((pluginId) => pluginId !== enabledPluginId);
|
|
1229
|
+
for (const pluginId of idsToDisable) {
|
|
1230
|
+
const disablePlugin = await this.runOpenClaw(["config", "set", `plugins.entries.${pluginId}.enabled`, "false"], { env });
|
|
1231
|
+
if (disablePlugin.code === 0) {
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
1234
|
+
const message = disablePlugin.stderr.trim() || disablePlugin.stdout.trim() || "";
|
|
1235
|
+
if (isPluginNotFoundMessage(message)) {
|
|
1236
|
+
continue;
|
|
1237
|
+
}
|
|
1238
|
+
warnings.push(`OpenClaw plugin cleanup failed for "${pluginId}" (code ${disablePlugin.code}). ${message}`.trim());
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
return warnings;
|
|
1242
|
+
}
|
|
1243
|
+
async mergePluginLoadPaths(pluginSourcePath, currentPaths) {
|
|
1244
|
+
const merged = dedupeStrings([pluginSourcePath, ...currentPaths]);
|
|
1245
|
+
const normalizedPluginSource = resolvePath(pluginSourcePath);
|
|
1246
|
+
const filtered = [];
|
|
1247
|
+
for (const candidate of merged) {
|
|
1248
|
+
const normalizedCandidate = resolvePath(candidate);
|
|
1249
|
+
if (pathMatches(normalizedCandidate, normalizedPluginSource)) {
|
|
1250
|
+
filtered.push(candidate);
|
|
1251
|
+
continue;
|
|
1252
|
+
}
|
|
1253
|
+
const manifestId = await this.readPluginManifestId(candidate);
|
|
1254
|
+
if (manifestId &&
|
|
1255
|
+
isOpenGoatPluginId(manifestId)) {
|
|
1256
|
+
continue;
|
|
1257
|
+
}
|
|
1258
|
+
filtered.push(candidate);
|
|
1259
|
+
}
|
|
1260
|
+
return dedupeStrings(filtered);
|
|
1261
|
+
}
|
|
1262
|
+
async readPluginManifestId(path) {
|
|
1263
|
+
const manifestPath = this.pathPort.join(path, "openclaw.plugin.json");
|
|
1264
|
+
if (!(await this.fileSystem.exists(manifestPath))) {
|
|
1265
|
+
return undefined;
|
|
1266
|
+
}
|
|
1267
|
+
try {
|
|
1268
|
+
const raw = await this.fileSystem.readFile(manifestPath);
|
|
1269
|
+
const parsed = parseLooseJson(raw);
|
|
1270
|
+
const id = asRecord(parsed).id;
|
|
1271
|
+
if (typeof id === "string" && id.trim().length > 0) {
|
|
1272
|
+
return id.trim();
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
catch {
|
|
1276
|
+
// Ignore malformed manifests when cleaning up load paths.
|
|
1277
|
+
}
|
|
1278
|
+
return undefined;
|
|
1279
|
+
}
|
|
905
1280
|
async resolveOpenClawEnv(paths) {
|
|
906
1281
|
const providerConfig = await this.providerService.getProviderConfig(paths, OPENCLAW_PROVIDER_ID);
|
|
907
1282
|
return {
|
|
@@ -912,6 +1287,7 @@ export class OpenGoatService {
|
|
|
912
1287
|
async collectInactiveAgents(paths, manifests, inactiveMinutes, notificationTarget) {
|
|
913
1288
|
const nowMs = this.resolveNowMs();
|
|
914
1289
|
const inactiveCutoffMs = nowMs - inactiveMinutes * 60_000;
|
|
1290
|
+
const reporteeStats = buildReporteeStats(manifests);
|
|
915
1291
|
const inactive = [];
|
|
916
1292
|
for (const manifest of manifests) {
|
|
917
1293
|
const managerAgentId = normalizeAgentId(manifest.metadata.reportsTo ?? "");
|
|
@@ -926,295 +1302,201 @@ export class OpenGoatService {
|
|
|
926
1302
|
if (lastAction && lastAction.timestamp >= inactiveCutoffMs) {
|
|
927
1303
|
continue;
|
|
928
1304
|
}
|
|
1305
|
+
const directReporteesCount = reporteeStats.directByManager.get(manifest.agentId) ?? 0;
|
|
1306
|
+
const totalReporteesCount = reporteeStats.totalByManager.get(manifest.agentId) ?? 0;
|
|
1307
|
+
const indirectReporteesCount = Math.max(0, totalReporteesCount - directReporteesCount);
|
|
929
1308
|
inactive.push({
|
|
930
1309
|
managerAgentId,
|
|
931
1310
|
subjectAgentId: manifest.agentId,
|
|
932
1311
|
subjectName: manifest.metadata.name,
|
|
933
1312
|
role: manifest.metadata.description,
|
|
1313
|
+
directReporteesCount,
|
|
1314
|
+
indirectReporteesCount,
|
|
934
1315
|
lastActionTimestamp: lastAction?.timestamp,
|
|
935
1316
|
});
|
|
936
1317
|
}
|
|
937
1318
|
return inactive;
|
|
938
1319
|
}
|
|
939
|
-
async resolveLatestProjectPathForAgent(paths, rawAgentId) {
|
|
940
|
-
const agentId = normalizeAgentId(rawAgentId);
|
|
941
|
-
if (!agentId) {
|
|
942
|
-
return undefined;
|
|
943
|
-
}
|
|
944
|
-
const sessions = await this.sessionService.listSessions(paths, agentId);
|
|
945
|
-
const latestProjectPath = sessions[0]?.projectPath?.trim();
|
|
946
|
-
return latestProjectPath || undefined;
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
function containsAlreadyExistsMessage(stdout, stderr) {
|
|
950
|
-
const text = `${stdout}\n${stderr}`.toLowerCase();
|
|
951
|
-
return /\balready exists?\b/.test(text);
|
|
952
1320
|
}
|
|
953
|
-
function
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
}
|
|
957
|
-
function toErrorMessage(error) {
|
|
958
|
-
if (error instanceof Error) {
|
|
959
|
-
return error.message;
|
|
1321
|
+
function asRecord(value) {
|
|
1322
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1323
|
+
return {};
|
|
960
1324
|
}
|
|
961
|
-
return
|
|
1325
|
+
return value;
|
|
962
1326
|
}
|
|
963
|
-
function
|
|
964
|
-
|
|
965
|
-
|
|
1327
|
+
function normalizeAgentConfigEntryId(value) {
|
|
1328
|
+
const entry = asRecord(value);
|
|
1329
|
+
const id = entry.id;
|
|
1330
|
+
if (typeof id !== "string") {
|
|
1331
|
+
return undefined;
|
|
966
1332
|
}
|
|
967
|
-
return
|
|
1333
|
+
return normalizeAgentId(id);
|
|
968
1334
|
}
|
|
969
|
-
function
|
|
970
|
-
|
|
1335
|
+
function readAgentSandboxMode(entry) {
|
|
1336
|
+
const sandbox = asRecord(entry.sandbox);
|
|
1337
|
+
const mode = sandbox.mode;
|
|
1338
|
+
if (typeof mode !== "string") {
|
|
1339
|
+
return undefined;
|
|
1340
|
+
}
|
|
1341
|
+
const trimmed = mode.trim();
|
|
1342
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
971
1343
|
}
|
|
972
|
-
function
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
if (typeof record.managedSkillsDir !== "string") {
|
|
978
|
-
return null;
|
|
1344
|
+
function hasAgentToolsAllowAll(entry) {
|
|
1345
|
+
const tools = asRecord(entry.tools);
|
|
1346
|
+
const allow = tools.allow;
|
|
1347
|
+
if (!Array.isArray(allow)) {
|
|
1348
|
+
return false;
|
|
979
1349
|
}
|
|
980
|
-
|
|
981
|
-
return managedSkillsDir || null;
|
|
1350
|
+
return allow.some((value) => typeof value === "string" && value.trim() === "*");
|
|
982
1351
|
}
|
|
983
|
-
function
|
|
984
|
-
if (!Array.isArray(
|
|
1352
|
+
function readStringArray(value) {
|
|
1353
|
+
if (!Array.isArray(value)) {
|
|
985
1354
|
return [];
|
|
986
1355
|
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
continue;
|
|
991
|
-
}
|
|
992
|
-
const record = entry;
|
|
993
|
-
const id = normalizeAgentId(String(record.id ?? ""));
|
|
994
|
-
if (!id) {
|
|
995
|
-
continue;
|
|
996
|
-
}
|
|
997
|
-
entries.push({
|
|
998
|
-
id,
|
|
999
|
-
workspace: typeof record.workspace === "string" ? record.workspace : "",
|
|
1000
|
-
agentDir: typeof record.agentDir === "string" ? record.agentDir : "",
|
|
1001
|
-
});
|
|
1002
|
-
}
|
|
1003
|
-
return entries;
|
|
1356
|
+
return value
|
|
1357
|
+
.map((entry) => (typeof entry === "string" ? entry.trim() : ""))
|
|
1358
|
+
.filter((entry) => entry.length > 0);
|
|
1004
1359
|
}
|
|
1005
|
-
function
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
return null;
|
|
1360
|
+
async function runWithConcurrency(items, rawConcurrency, worker) {
|
|
1361
|
+
if (items.length === 0) {
|
|
1362
|
+
return [];
|
|
1009
1363
|
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1364
|
+
const concurrency = Math.max(1, Math.floor(rawConcurrency));
|
|
1365
|
+
const results = new Array(items.length);
|
|
1366
|
+
let nextIndex = 0;
|
|
1367
|
+
const runWorker = async () => {
|
|
1368
|
+
while (true) {
|
|
1369
|
+
const currentIndex = nextIndex;
|
|
1370
|
+
nextIndex += 1;
|
|
1371
|
+
if (currentIndex >= items.length) {
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
results[currentIndex] = await worker(items[currentIndex], currentIndex);
|
|
1013
1375
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
return null;
|
|
1020
|
-
}
|
|
1021
|
-
function pathMatches(left, right) {
|
|
1022
|
-
const leftNormalized = normalizePathForCompare(left);
|
|
1023
|
-
const rightNormalized = normalizePathForCompare(right);
|
|
1024
|
-
if (!leftNormalized || !rightNormalized) {
|
|
1025
|
-
return false;
|
|
1026
|
-
}
|
|
1027
|
-
return leftNormalized === rightNormalized;
|
|
1376
|
+
};
|
|
1377
|
+
const workerCount = Math.min(concurrency, items.length);
|
|
1378
|
+
const workers = Array.from({ length: workerCount }, () => runWorker());
|
|
1379
|
+
await Promise.all(workers);
|
|
1380
|
+
return results;
|
|
1028
1381
|
}
|
|
1029
|
-
function
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
if (!normalizedContainer || !normalizedCandidate) {
|
|
1033
|
-
return false;
|
|
1034
|
-
}
|
|
1035
|
-
const relative = path.relative(normalizedContainer, normalizedCandidate);
|
|
1036
|
-
if (!relative) {
|
|
1037
|
-
return true;
|
|
1382
|
+
async function runWithConcurrencyByKey(items, rawConcurrency, resolveKey, worker) {
|
|
1383
|
+
if (items.length === 0) {
|
|
1384
|
+
return [];
|
|
1038
1385
|
}
|
|
1039
|
-
|
|
1386
|
+
const grouped = new Map();
|
|
1387
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
1388
|
+
const item = items[index];
|
|
1389
|
+
const key = resolveKey(item, index).trim().toLowerCase() || "default";
|
|
1390
|
+
const bucket = grouped.get(key) ?? [];
|
|
1391
|
+
bucket.push({ item, index });
|
|
1392
|
+
grouped.set(key, bucket);
|
|
1393
|
+
}
|
|
1394
|
+
const results = new Array(items.length);
|
|
1395
|
+
await runWithConcurrency([...grouped.values()], rawConcurrency, async (entries) => {
|
|
1396
|
+
for (const entry of entries) {
|
|
1397
|
+
results[entry.index] = await worker(entry.item, entry.index);
|
|
1398
|
+
}
|
|
1399
|
+
});
|
|
1400
|
+
return results;
|
|
1040
1401
|
}
|
|
1041
|
-
function
|
|
1042
|
-
const trimmed =
|
|
1402
|
+
function parseLooseJson(raw) {
|
|
1403
|
+
const trimmed = raw.trim();
|
|
1043
1404
|
if (!trimmed) {
|
|
1044
|
-
return
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1405
|
+
return undefined;
|
|
1406
|
+
}
|
|
1407
|
+
try {
|
|
1408
|
+
return JSON.parse(trimmed);
|
|
1409
|
+
}
|
|
1410
|
+
catch {
|
|
1411
|
+
// continue
|
|
1412
|
+
}
|
|
1413
|
+
const starts = dedupeNumbers([
|
|
1414
|
+
trimmed.indexOf("{"),
|
|
1415
|
+
trimmed.indexOf("["),
|
|
1416
|
+
trimmed.lastIndexOf("{"),
|
|
1417
|
+
trimmed.lastIndexOf("["),
|
|
1418
|
+
]).filter((index) => index >= 0);
|
|
1419
|
+
for (const startIndex of starts) {
|
|
1420
|
+
const candidate = trimmed.slice(startIndex).trim();
|
|
1421
|
+
if (!candidate) {
|
|
1422
|
+
continue;
|
|
1423
|
+
}
|
|
1424
|
+
try {
|
|
1425
|
+
return JSON.parse(candidate);
|
|
1426
|
+
}
|
|
1427
|
+
catch {
|
|
1428
|
+
// keep trying candidates
|
|
1429
|
+
}
|
|
1049
1430
|
}
|
|
1050
|
-
return
|
|
1051
|
-
}
|
|
1052
|
-
function buildTaskSessionRef(agentId, taskId) {
|
|
1053
|
-
const normalizedAgentId = normalizeAgentId(agentId) || DEFAULT_AGENT_ID;
|
|
1054
|
-
const normalizedTaskId = normalizeAgentId(taskId) || "task";
|
|
1055
|
-
return `agent:${normalizedAgentId}:agent_${normalizedAgentId}_task_${normalizedTaskId}`;
|
|
1056
|
-
}
|
|
1057
|
-
function buildInactiveSessionRef(managerAgentId, subjectAgentId) {
|
|
1058
|
-
const manager = normalizeAgentId(managerAgentId) || DEFAULT_AGENT_ID;
|
|
1059
|
-
const subject = normalizeAgentId(subjectAgentId) || "agent";
|
|
1060
|
-
return `agent:${manager}:agent_${manager}_inactive_${subject}`;
|
|
1061
|
-
}
|
|
1062
|
-
function buildTodoTaskMessage(params) {
|
|
1063
|
-
const blockers = params.task.blockers.length > 0 ? params.task.blockers.join("; ") : "None";
|
|
1064
|
-
const artifacts = params.task.artifacts.length > 0
|
|
1065
|
-
? params.task.artifacts
|
|
1066
|
-
.map((entry) => `- ${entry.createdAt} @${entry.createdBy}: ${entry.content}`)
|
|
1067
|
-
.join("\n")
|
|
1068
|
-
: "- None";
|
|
1069
|
-
const worklog = params.task.worklog.length > 0
|
|
1070
|
-
? params.task.worklog
|
|
1071
|
-
.map((entry) => `- ${entry.createdAt} @${entry.createdBy}: ${entry.content}`)
|
|
1072
|
-
.join("\n")
|
|
1073
|
-
: "- None";
|
|
1074
|
-
return [
|
|
1075
|
-
`Task #${params.task.taskId} is assigned to you and currently in TODO. Please work on it now.`,
|
|
1076
|
-
"",
|
|
1077
|
-
`Task ID: ${params.task.taskId}`,
|
|
1078
|
-
`Title: ${params.task.title}`,
|
|
1079
|
-
`Description: ${params.task.description}`,
|
|
1080
|
-
`Project: ${params.task.project}`,
|
|
1081
|
-
`Status: ${params.task.status}`,
|
|
1082
|
-
`Owner: @${params.task.owner}`,
|
|
1083
|
-
`Assigned to: @${params.task.assignedTo}`,
|
|
1084
|
-
`Created at: ${params.task.createdAt}`,
|
|
1085
|
-
`Blockers: ${blockers}`,
|
|
1086
|
-
"Artifacts:",
|
|
1087
|
-
artifacts,
|
|
1088
|
-
"Worklog:",
|
|
1089
|
-
worklog,
|
|
1090
|
-
].join("\n");
|
|
1091
|
-
}
|
|
1092
|
-
function buildBlockedTaskMessage(params) {
|
|
1093
|
-
const blockerReason = params.task.blockers.length > 0
|
|
1094
|
-
? params.task.blockers.join("; ")
|
|
1095
|
-
: params.task.statusReason?.trim() || "no blocker details were provided";
|
|
1096
|
-
const artifacts = params.task.artifacts.length > 0
|
|
1097
|
-
? params.task.artifacts
|
|
1098
|
-
.map((entry) => `- ${entry.createdAt} @${entry.createdBy}: ${entry.content}`)
|
|
1099
|
-
.join("\n")
|
|
1100
|
-
: "- None";
|
|
1101
|
-
const worklog = params.task.worklog.length > 0
|
|
1102
|
-
? params.task.worklog
|
|
1103
|
-
.map((entry) => `- ${entry.createdAt} @${entry.createdBy}: ${entry.content}`)
|
|
1104
|
-
.join("\n")
|
|
1105
|
-
: "- None";
|
|
1106
|
-
return [
|
|
1107
|
-
`Task #${params.task.taskId}, assigned to your reportee "@${params.task.assignedTo}" is blocked because of ${blockerReason}. Help unblocking it.`,
|
|
1108
|
-
"",
|
|
1109
|
-
`Task ID: ${params.task.taskId}`,
|
|
1110
|
-
`Title: ${params.task.title}`,
|
|
1111
|
-
`Description: ${params.task.description}`,
|
|
1112
|
-
`Project: ${params.task.project}`,
|
|
1113
|
-
`Status: ${params.task.status}`,
|
|
1114
|
-
`Owner: @${params.task.owner}`,
|
|
1115
|
-
`Assigned to: @${params.task.assignedTo}`,
|
|
1116
|
-
`Created at: ${params.task.createdAt}`,
|
|
1117
|
-
"Artifacts:",
|
|
1118
|
-
artifacts,
|
|
1119
|
-
"Worklog:",
|
|
1120
|
-
worklog,
|
|
1121
|
-
].join("\n");
|
|
1431
|
+
return undefined;
|
|
1122
1432
|
}
|
|
1123
|
-
function
|
|
1124
|
-
|
|
1125
|
-
Number.isFinite(params.lastActionTimestamp)
|
|
1126
|
-
? new Date(params.lastActionTimestamp).toISOString()
|
|
1127
|
-
: null;
|
|
1128
|
-
return [
|
|
1129
|
-
`Your reportee "@${params.subjectAgentId}" (${params.subjectName}) has no activity in the last ${params.inactiveMinutes} minutes.`,
|
|
1130
|
-
...(params.role ? [`Role: ${params.role}`] : []),
|
|
1131
|
-
...(lastAction ? [`Last action: ${lastAction}`] : []),
|
|
1132
|
-
"Please check in and unblock progress.",
|
|
1133
|
-
].join("\n");
|
|
1433
|
+
function dedupeNumbers(values) {
|
|
1434
|
+
return [...new Set(values)];
|
|
1134
1435
|
}
|
|
1135
|
-
function
|
|
1136
|
-
|
|
1137
|
-
|
|
1436
|
+
function resolveCreateAgentManagerId(agentId, reportsTo) {
|
|
1437
|
+
const normalizedAgentId = normalizeAgentId(agentId);
|
|
1438
|
+
if (isDefaultAgentId(normalizedAgentId)) {
|
|
1439
|
+
return null;
|
|
1138
1440
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
function collectAllReportees(manifests, managerAgentId) {
|
|
1142
|
-
const byManager = new Map();
|
|
1143
|
-
for (const manifest of manifests) {
|
|
1144
|
-
const reportsTo = manifest.metadata.reportsTo;
|
|
1145
|
-
if (!reportsTo) {
|
|
1146
|
-
continue;
|
|
1147
|
-
}
|
|
1148
|
-
const reportees = byManager.get(reportsTo) ?? [];
|
|
1149
|
-
reportees.push(manifest.agentId);
|
|
1150
|
-
byManager.set(reportsTo, reportees);
|
|
1441
|
+
if (reportsTo === null || reportsTo === undefined) {
|
|
1442
|
+
return DEFAULT_AGENT_ID;
|
|
1151
1443
|
}
|
|
1152
|
-
const
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
const current = queue.shift();
|
|
1156
|
-
if (!current || current === managerAgentId || visited.has(current)) {
|
|
1157
|
-
continue;
|
|
1158
|
-
}
|
|
1159
|
-
visited.add(current);
|
|
1160
|
-
queue.push(...(byManager.get(current) ?? []));
|
|
1444
|
+
const normalizedManagerId = normalizeAgentId(reportsTo);
|
|
1445
|
+
if (!normalizedManagerId || normalizedManagerId === normalizedAgentId) {
|
|
1446
|
+
return DEFAULT_AGENT_ID;
|
|
1161
1447
|
}
|
|
1162
|
-
return
|
|
1448
|
+
return normalizedManagerId;
|
|
1163
1449
|
}
|
|
1164
|
-
function
|
|
1165
|
-
const
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1450
|
+
function collectPluginPathCandidatesFromArgvDir(argvDir) {
|
|
1451
|
+
const maxDepth = 8;
|
|
1452
|
+
const candidates = [];
|
|
1453
|
+
let currentDir = argvDir;
|
|
1454
|
+
for (let depth = 0; depth < maxDepth; depth += 1) {
|
|
1455
|
+
candidates.push(resolvePath(currentDir, "packages", "openclaw-plugin"));
|
|
1456
|
+
candidates.push(resolvePath(currentDir, "openclaw-plugin"));
|
|
1457
|
+
const parentDir = dirname(currentDir);
|
|
1458
|
+
if (parentDir === currentDir) {
|
|
1459
|
+
break;
|
|
1460
|
+
}
|
|
1461
|
+
currentDir = parentDir;
|
|
1462
|
+
}
|
|
1463
|
+
return dedupeStrings(candidates);
|
|
1173
1464
|
}
|
|
1174
|
-
function
|
|
1175
|
-
const homeDir = homedir();
|
|
1176
|
-
const preferredPaths = [
|
|
1177
|
-
path.dirname(process.execPath),
|
|
1178
|
-
path.join(homeDir, ".npm-global", "bin"),
|
|
1179
|
-
path.join(homeDir, ".npm", "bin"),
|
|
1180
|
-
path.join(homeDir, ".local", "bin"),
|
|
1181
|
-
path.join(homeDir, ".volta", "bin"),
|
|
1182
|
-
path.join(homeDir, ".fnm", "current", "bin"),
|
|
1183
|
-
path.join(homeDir, ".asdf", "shims"),
|
|
1184
|
-
path.join(homeDir, "bin"),
|
|
1185
|
-
];
|
|
1186
|
-
const npmPrefixCandidates = dedupePathEntries([
|
|
1187
|
-
env.npm_config_prefix ?? "",
|
|
1188
|
-
env.NPM_CONFIG_PREFIX ?? "",
|
|
1189
|
-
process.env.npm_config_prefix ?? "",
|
|
1190
|
-
process.env.NPM_CONFIG_PREFIX ?? "",
|
|
1191
|
-
]);
|
|
1192
|
-
for (const prefix of npmPrefixCandidates) {
|
|
1193
|
-
preferredPaths.push(path.join(prefix, "bin"));
|
|
1194
|
-
}
|
|
1195
|
-
if (process.platform === "darwin") {
|
|
1196
|
-
preferredPaths.push("/opt/homebrew/bin", "/opt/homebrew/opt/node@22/bin", "/usr/local/opt/node@22/bin");
|
|
1197
|
-
}
|
|
1198
|
-
return preferredPaths;
|
|
1199
|
-
}
|
|
1200
|
-
function dedupePathEntries(entries) {
|
|
1465
|
+
function dedupeStrings(values) {
|
|
1201
1466
|
const seen = new Set();
|
|
1202
1467
|
const deduped = [];
|
|
1203
|
-
for (const
|
|
1204
|
-
const
|
|
1205
|
-
if (!
|
|
1468
|
+
for (const value of values) {
|
|
1469
|
+
const trimmed = value?.trim();
|
|
1470
|
+
if (!trimmed || seen.has(trimmed)) {
|
|
1206
1471
|
continue;
|
|
1207
1472
|
}
|
|
1208
|
-
seen.add(
|
|
1209
|
-
deduped.push(
|
|
1473
|
+
seen.add(trimmed);
|
|
1474
|
+
deduped.push(trimmed);
|
|
1210
1475
|
}
|
|
1211
1476
|
return deduped;
|
|
1212
1477
|
}
|
|
1213
|
-
function
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1478
|
+
function samePathList(left, right) {
|
|
1479
|
+
if (left.length !== right.length) {
|
|
1480
|
+
return false;
|
|
1481
|
+
}
|
|
1482
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
1483
|
+
const leftValue = left[index];
|
|
1484
|
+
const rightValue = right[index];
|
|
1485
|
+
if (!leftValue || !rightValue || !pathMatches(leftValue, rightValue)) {
|
|
1486
|
+
return false;
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
return true;
|
|
1490
|
+
}
|
|
1491
|
+
function isOpenGoatPluginId(pluginId) {
|
|
1492
|
+
return [
|
|
1493
|
+
OPENCLAW_OPENGOAT_PLUGIN_ID,
|
|
1494
|
+
OPENCLAW_OPENGOAT_PLUGIN_ROOT_ID,
|
|
1495
|
+
OPENCLAW_OPENGOAT_PLUGIN_LEGACY_PACK_ID,
|
|
1496
|
+
OPENCLAW_OPENGOAT_PLUGIN_FALLBACK_ID,
|
|
1497
|
+
].includes(pluginId.trim().toLowerCase());
|
|
1498
|
+
}
|
|
1499
|
+
function isPluginNotFoundMessage(message) {
|
|
1500
|
+
return message.toLowerCase().includes("plugin not found");
|
|
1219
1501
|
}
|
|
1220
1502
|
//# sourceMappingURL=opengoat.service.js.map
|