@opengoat/core 2026.2.14 → 2026.2.15
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 +7 -0
- package/dist/core/agents/application/agent.service.js +83 -8
- package/dist/core/agents/application/agent.service.js.map +1 -1
- package/dist/core/bootstrap/application/bootstrap.service.js +14 -1
- package/dist/core/bootstrap/application/bootstrap.service.js.map +1 -1
- package/dist/core/opengoat/application/opengoat.service.d.ts +9 -0
- package/dist/core/opengoat/application/opengoat.service.helpers.d.ts +52 -0
- package/dist/core/opengoat/application/opengoat.service.helpers.js +325 -0
- package/dist/core/opengoat/application/opengoat.service.helpers.js.map +1 -0
- package/dist/core/opengoat/application/opengoat.service.js +450 -317
- package/dist/core/opengoat/application/opengoat.service.js.map +1 -1
- package/dist/core/orchestration/application/orchestration.service.d.ts +1 -0
- package/dist/core/orchestration/application/orchestration.service.js +109 -24
- package/dist/core/orchestration/application/orchestration.service.js.map +1 -1
- package/dist/core/providers/application/provider.service.d.ts +1 -0
- package/dist/core/providers/application/provider.service.js +30 -20
- package/dist/core/providers/application/provider.service.js.map +1 -1
- package/dist/core/templates/assets/ceo/BOOTSTRAP.md +3 -3
- package/dist/core/templates/assets/ceo/ROLE.md +2 -1
- 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/default-templates.js +15 -3
- package/dist/core/templates/default-templates.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,3 @@
|
|
|
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";
|
|
@@ -9,10 +7,18 @@ 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, buildInactiveSessionRef, buildReporteeStats, buildTaskSessionRef, buildTodoTaskMessage, collectAllReportees, containsAgentNotFoundMessage, containsAlreadyExistsMessage, extractManagedSkillsDir, extractOpenClawAgents, isSpawnPermissionOrMissing, pathIsWithin, pathMatches, prepareOpenClawCommandEnv, resolveInactiveAgentNotificationTarget, resolveInactiveMinutes, 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)}`);
|
|
229
228
|
}
|
|
230
229
|
try {
|
|
231
|
-
await this.
|
|
230
|
+
warnings.push(...(await this.syncOpenClawAgentExecutionPolicies(paths, localAgents.map((agent) => agent.id))));
|
|
232
231
|
}
|
|
233
232
|
catch (error) {
|
|
234
|
-
warnings.push(`
|
|
233
|
+
warnings.push(`OpenClaw agent policy sync failed: ${toErrorMessage(error)}`);
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
warnings.push(...(await this.ensureOpenGoatPluginToolsRegistered(paths)));
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
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,
|
|
@@ -287,6 +301,7 @@ export class OpenGoatService {
|
|
|
287
301
|
}
|
|
288
302
|
throw new Error(`OpenClaw agent location sync failed for "${created.agent.id}". ${toErrorMessage(error)}`);
|
|
289
303
|
}
|
|
304
|
+
await this.syncOpenClawAgentExecutionPolicies(paths, [created.agent.id]);
|
|
290
305
|
try {
|
|
291
306
|
const workspaceBootstrap = await this.agentService.ensureAgentWorkspaceBootstrap(paths, {
|
|
292
307
|
agentId: created.agent.id,
|
|
@@ -390,12 +405,13 @@ export class OpenGoatService {
|
|
|
390
405
|
this.agentService.listAgents(paths),
|
|
391
406
|
this.agentManifestService.listManifests(paths),
|
|
392
407
|
]);
|
|
408
|
+
const reporteeStats = buildReporteeStats(manifests);
|
|
393
409
|
const descriptorsById = new Map(agents.map((agent) => [agent.id, agent]));
|
|
394
410
|
const agent = descriptorsById.get(agentId);
|
|
395
411
|
if (!agent) {
|
|
396
412
|
throw new Error(`Agent "${agentId}" does not exist.`);
|
|
397
413
|
}
|
|
398
|
-
const totalReportees =
|
|
414
|
+
const totalReportees = reporteeStats.totalByManager.get(agentId) ?? 0;
|
|
399
415
|
const directReportees = manifests
|
|
400
416
|
.filter((manifest) => manifest.metadata.reportsTo === agentId)
|
|
401
417
|
.map((manifest) => {
|
|
@@ -408,8 +424,7 @@ export class OpenGoatService {
|
|
|
408
424
|
id: manifest.agentId,
|
|
409
425
|
name,
|
|
410
426
|
role,
|
|
411
|
-
totalReportees:
|
|
412
|
-
.length,
|
|
427
|
+
totalReportees: reporteeStats.totalByManager.get(manifest.agentId) ?? 0,
|
|
413
428
|
};
|
|
414
429
|
})
|
|
415
430
|
.sort((left, right) => left.id.localeCompare(right.id));
|
|
@@ -507,7 +522,6 @@ export class OpenGoatService {
|
|
|
507
522
|
const paths = this.pathsProvider.getPaths();
|
|
508
523
|
const ranAt = this.resolveNowIso();
|
|
509
524
|
const manifests = await this.agentManifestService.listManifests(paths);
|
|
510
|
-
const manifestsById = new Map(manifests.map((manifest) => [manifest.agentId, manifest]));
|
|
511
525
|
const inactiveMinutes = resolveInactiveMinutes(options.inactiveMinutes);
|
|
512
526
|
const notificationTarget = resolveInactiveAgentNotificationTarget(options.notificationTarget);
|
|
513
527
|
const notifyInactiveAgents = options.notifyInactiveAgents ?? true;
|
|
@@ -518,8 +532,27 @@ export class OpenGoatService {
|
|
|
518
532
|
? await this.resolveLatestProjectPathForAgent(paths, DEFAULT_AGENT_ID)
|
|
519
533
|
: undefined;
|
|
520
534
|
const tasks = await this.boardService.listTasks(paths, { limit: 10_000 });
|
|
535
|
+
const taskStatusDispatch = await this.dispatchTaskStatusAutomations(paths, tasks, manifests);
|
|
536
|
+
const inactiveDispatches = await this.dispatchInactiveAgentAutomations(paths, inactiveCandidates, inactiveMinutes, latestCeoProjectPath);
|
|
537
|
+
const dispatches = [
|
|
538
|
+
...taskStatusDispatch.dispatches,
|
|
539
|
+
...inactiveDispatches,
|
|
540
|
+
];
|
|
541
|
+
const failed = dispatches.filter((entry) => !entry.ok).length;
|
|
542
|
+
return {
|
|
543
|
+
ranAt,
|
|
544
|
+
scannedTasks: tasks.length,
|
|
545
|
+
todoTasks: taskStatusDispatch.todoTasks,
|
|
546
|
+
blockedTasks: taskStatusDispatch.blockedTasks,
|
|
547
|
+
inactiveAgents: inactiveCandidates.length,
|
|
548
|
+
sent: dispatches.length - failed,
|
|
549
|
+
failed,
|
|
550
|
+
dispatches,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
async dispatchTaskStatusAutomations(paths, tasks, manifests) {
|
|
554
|
+
const manifestsById = new Map(manifests.map((manifest) => [manifest.agentId, manifest]));
|
|
521
555
|
const dispatches = [];
|
|
522
|
-
let scannedTasks = tasks.length;
|
|
523
556
|
let todoTasks = 0;
|
|
524
557
|
let blockedTasks = 0;
|
|
525
558
|
for (const task of tasks) {
|
|
@@ -558,6 +591,14 @@ export class OpenGoatService {
|
|
|
558
591
|
error: result.error,
|
|
559
592
|
});
|
|
560
593
|
}
|
|
594
|
+
return {
|
|
595
|
+
dispatches,
|
|
596
|
+
todoTasks,
|
|
597
|
+
blockedTasks,
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
async dispatchInactiveAgentAutomations(paths, inactiveCandidates, inactiveMinutes, latestCeoProjectPath) {
|
|
601
|
+
const dispatches = [];
|
|
561
602
|
for (const candidate of inactiveCandidates) {
|
|
562
603
|
const sessionRef = buildInactiveSessionRef(candidate.managerAgentId, candidate.subjectAgentId);
|
|
563
604
|
const message = buildInactiveAgentMessage({
|
|
@@ -565,6 +606,8 @@ export class OpenGoatService {
|
|
|
565
606
|
subjectAgentId: candidate.subjectAgentId,
|
|
566
607
|
subjectName: candidate.subjectName,
|
|
567
608
|
role: candidate.role,
|
|
609
|
+
directReporteesCount: candidate.directReporteesCount,
|
|
610
|
+
indirectReporteesCount: candidate.indirectReporteesCount,
|
|
568
611
|
inactiveMinutes,
|
|
569
612
|
lastActionTimestamp: candidate.lastActionTimestamp,
|
|
570
613
|
});
|
|
@@ -580,17 +623,7 @@ export class OpenGoatService {
|
|
|
580
623
|
error: result.error,
|
|
581
624
|
});
|
|
582
625
|
}
|
|
583
|
-
|
|
584
|
-
return {
|
|
585
|
-
ranAt,
|
|
586
|
-
scannedTasks,
|
|
587
|
-
todoTasks,
|
|
588
|
-
blockedTasks,
|
|
589
|
-
inactiveAgents: inactiveCandidates.length,
|
|
590
|
-
sent: dispatches.length - failed,
|
|
591
|
-
failed,
|
|
592
|
-
dispatches,
|
|
593
|
-
};
|
|
626
|
+
return dispatches;
|
|
594
627
|
}
|
|
595
628
|
async listSkills(agentId = DEFAULT_AGENT_ID) {
|
|
596
629
|
const paths = this.pathsProvider.getPaths();
|
|
@@ -689,6 +722,7 @@ export class OpenGoatService {
|
|
|
689
722
|
const result = await this.orchestrationService.runAgent(paths, agentId, {
|
|
690
723
|
message,
|
|
691
724
|
sessionRef,
|
|
725
|
+
disableSession: options.disableSession ?? true,
|
|
692
726
|
cwd: options.cwd,
|
|
693
727
|
env: process.env,
|
|
694
728
|
});
|
|
@@ -815,11 +849,8 @@ export class OpenGoatService {
|
|
|
815
849
|
if (skillsList.code !== 0) {
|
|
816
850
|
throw new Error(`OpenClaw skills list failed (exit ${skillsList.code}). ${skillsList.stderr.trim() || skillsList.stdout.trim() || ""}`.trim());
|
|
817
851
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
parsed = JSON.parse(skillsList.stdout);
|
|
821
|
-
}
|
|
822
|
-
catch {
|
|
852
|
+
const parsed = parseLooseJson(skillsList.stdout);
|
|
853
|
+
if (parsed === undefined) {
|
|
823
854
|
throw new Error("OpenClaw skills list returned non-JSON output; cannot resolve managed skills directory.");
|
|
824
855
|
}
|
|
825
856
|
const managedSkillsDir = extractManagedSkillsDir(parsed);
|
|
@@ -837,11 +868,8 @@ export class OpenGoatService {
|
|
|
837
868
|
if (listed.code !== 0) {
|
|
838
869
|
throw new Error(`OpenClaw agents list failed (exit ${listed.code}). ${listed.stderr.trim() || listed.stdout.trim() || ""}`.trim());
|
|
839
870
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
parsed = JSON.parse(listed.stdout);
|
|
843
|
-
}
|
|
844
|
-
catch {
|
|
871
|
+
const parsed = parseLooseJson(listed.stdout);
|
|
872
|
+
if (parsed === undefined) {
|
|
845
873
|
throw new Error("OpenClaw agents list returned non-JSON output; cannot inspect agents.");
|
|
846
874
|
}
|
|
847
875
|
return extractOpenClawAgents(parsed);
|
|
@@ -861,25 +889,66 @@ export class OpenGoatService {
|
|
|
861
889
|
warnings.push(`Workspace fallback discovery failed: ${toErrorMessage(error)}`);
|
|
862
890
|
}
|
|
863
891
|
}
|
|
864
|
-
async
|
|
865
|
-
|
|
866
|
-
|
|
892
|
+
async syncOpenClawAgentRegistration(paths, params) {
|
|
893
|
+
const warnings = [];
|
|
894
|
+
if (params.existingEntry &&
|
|
895
|
+
pathMatches(params.existingEntry.workspace, params.descriptor.workspaceDir) &&
|
|
896
|
+
pathMatches(params.existingEntry.agentDir, params.descriptor.internalConfigDir)) {
|
|
897
|
+
return {
|
|
898
|
+
synced: true,
|
|
899
|
+
code: 0,
|
|
900
|
+
warnings,
|
|
901
|
+
};
|
|
867
902
|
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
903
|
+
let runtimeSync;
|
|
904
|
+
try {
|
|
905
|
+
runtimeSync = await this.providerService.createProviderAgent(paths, params.descriptor.id, {
|
|
906
|
+
providerId: OPENCLAW_PROVIDER_ID,
|
|
907
|
+
displayName: params.descriptor.displayName,
|
|
908
|
+
workspaceDir: params.descriptor.workspaceDir,
|
|
909
|
+
internalConfigDir: params.descriptor.internalConfigDir,
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
catch (error) {
|
|
913
|
+
warnings.push(`OpenClaw sync for "${params.descriptor.id}" failed: ${toErrorMessage(error)}`);
|
|
914
|
+
return {
|
|
915
|
+
synced: false,
|
|
916
|
+
warnings,
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
const synced = runtimeSync.code === 0 ||
|
|
920
|
+
containsAlreadyExistsMessage(runtimeSync.stdout, runtimeSync.stderr);
|
|
921
|
+
if (!synced) {
|
|
922
|
+
warnings.push(`OpenClaw sync for "${params.descriptor.id}" failed (code ${runtimeSync.code}). ${(runtimeSync.stderr || runtimeSync.stdout).trim()}`);
|
|
923
|
+
return {
|
|
924
|
+
synced,
|
|
925
|
+
code: runtimeSync.code,
|
|
926
|
+
warnings,
|
|
927
|
+
};
|
|
874
928
|
}
|
|
875
|
-
let parsed;
|
|
876
929
|
try {
|
|
877
|
-
|
|
930
|
+
await this.ensureOpenClawAgentLocation(paths, {
|
|
931
|
+
agentId: params.descriptor.id,
|
|
932
|
+
displayName: params.descriptor.displayName,
|
|
933
|
+
workspaceDir: params.descriptor.workspaceDir,
|
|
934
|
+
internalConfigDir: params.descriptor.internalConfigDir,
|
|
935
|
+
}, params.existingEntry);
|
|
878
936
|
}
|
|
879
|
-
catch {
|
|
880
|
-
|
|
937
|
+
catch (error) {
|
|
938
|
+
warnings.push(`OpenClaw location sync for "${params.descriptor.id}" failed: ${toErrorMessage(error)}`);
|
|
939
|
+
}
|
|
940
|
+
return {
|
|
941
|
+
synced,
|
|
942
|
+
code: runtimeSync.code,
|
|
943
|
+
warnings,
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
async ensureOpenClawAgentLocation(paths, params, existingEntry) {
|
|
947
|
+
if (!this.commandRunner) {
|
|
948
|
+
return;
|
|
881
949
|
}
|
|
882
|
-
const entry =
|
|
950
|
+
const entry = existingEntry ??
|
|
951
|
+
(await this.listOpenClawAgents(paths)).find((candidate) => candidate.id === normalizeAgentId(params.agentId));
|
|
883
952
|
if (!entry) {
|
|
884
953
|
return;
|
|
885
954
|
}
|
|
@@ -902,6 +971,210 @@ export class OpenGoatService {
|
|
|
902
971
|
throw new Error(`OpenClaw agent location repair failed creating "${params.agentId}" (exit ${recreated.code}). ${recreated.stderr.trim() || recreated.stdout.trim() || ""}`.trim());
|
|
903
972
|
}
|
|
904
973
|
}
|
|
974
|
+
async syncOpenClawAgentExecutionPolicies(paths, rawAgentIds) {
|
|
975
|
+
if (!this.commandRunner) {
|
|
976
|
+
return [];
|
|
977
|
+
}
|
|
978
|
+
const agentIds = [
|
|
979
|
+
...new Set(rawAgentIds
|
|
980
|
+
.map((agentId) => normalizeAgentId(agentId))
|
|
981
|
+
.filter((agentId) => Boolean(agentId))),
|
|
982
|
+
];
|
|
983
|
+
if (agentIds.length === 0) {
|
|
984
|
+
return [];
|
|
985
|
+
}
|
|
986
|
+
const warnings = [];
|
|
987
|
+
const env = await this.resolveOpenClawEnv(paths);
|
|
988
|
+
const listResult = await this.runOpenClaw(["config", "get", "agents.list"], {
|
|
989
|
+
env,
|
|
990
|
+
});
|
|
991
|
+
if (listResult.code !== 0) {
|
|
992
|
+
warnings.push(`OpenClaw config read failed (agents.list, code ${listResult.code}). ${listResult.stderr.trim() || listResult.stdout.trim() || ""}`.trim());
|
|
993
|
+
return warnings;
|
|
994
|
+
}
|
|
995
|
+
const parsed = parseLooseJson(listResult.stdout);
|
|
996
|
+
if (parsed === undefined) {
|
|
997
|
+
warnings.push("OpenClaw config read returned non-JSON for agents.list; skipping sandbox/tools policy sync.");
|
|
998
|
+
return warnings;
|
|
999
|
+
}
|
|
1000
|
+
if (!Array.isArray(parsed)) {
|
|
1001
|
+
warnings.push("OpenClaw config agents.list is not an array; skipping sandbox/tools policy sync.");
|
|
1002
|
+
return warnings;
|
|
1003
|
+
}
|
|
1004
|
+
const entries = parsed;
|
|
1005
|
+
const indexById = new Map();
|
|
1006
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
1007
|
+
const entry = entries[index];
|
|
1008
|
+
if (!entry) {
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
const id = normalizeAgentConfigEntryId(entry);
|
|
1012
|
+
if (!id || indexById.has(id)) {
|
|
1013
|
+
continue;
|
|
1014
|
+
}
|
|
1015
|
+
indexById.set(id, index);
|
|
1016
|
+
}
|
|
1017
|
+
for (const agentId of agentIds) {
|
|
1018
|
+
const index = indexById.get(agentId);
|
|
1019
|
+
if (index === undefined) {
|
|
1020
|
+
warnings.push(`OpenClaw config policy sync skipped for "${agentId}" because no agents.list entry was found.`);
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
1023
|
+
const entry = asRecord(entries[index]);
|
|
1024
|
+
if (readAgentSandboxMode(entry) !== OPENCLAW_AGENT_SANDBOX_MODE) {
|
|
1025
|
+
const sandboxSet = await this.runOpenClaw(["config", "set", `agents.list[${index}].sandbox.mode`, OPENCLAW_AGENT_SANDBOX_MODE], { env });
|
|
1026
|
+
if (sandboxSet.code !== 0) {
|
|
1027
|
+
warnings.push(`OpenClaw sandbox policy sync failed for "${agentId}" (code ${sandboxSet.code}). ${sandboxSet.stderr.trim() || sandboxSet.stdout.trim() || ""}`.trim());
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
if (!hasAgentToolsAllowAll(entry)) {
|
|
1031
|
+
const toolsSet = await this.runOpenClaw(["config", "set", `agents.list[${index}].tools.allow`, OPENCLAW_AGENT_TOOLS_ALLOW_ALL_JSON], { env });
|
|
1032
|
+
if (toolsSet.code !== 0) {
|
|
1033
|
+
warnings.push(`OpenClaw tools policy sync failed for "${agentId}" (code ${toolsSet.code}). ${toolsSet.stderr.trim() || toolsSet.stdout.trim() || ""}`.trim());
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
return warnings;
|
|
1038
|
+
}
|
|
1039
|
+
async ensureOpenGoatPluginToolsRegistered(paths) {
|
|
1040
|
+
if (!this.commandRunner) {
|
|
1041
|
+
return [];
|
|
1042
|
+
}
|
|
1043
|
+
const warnings = [];
|
|
1044
|
+
const env = await this.resolveOpenClawEnv(paths);
|
|
1045
|
+
const pluginSourcePath = await this.resolveOpenGoatPluginSourcePath();
|
|
1046
|
+
if (!pluginSourcePath) {
|
|
1047
|
+
warnings.push("OpenClaw OpenGoat plugin source path was not found; OpenGoat tools may be unavailable to agents.");
|
|
1048
|
+
return warnings;
|
|
1049
|
+
}
|
|
1050
|
+
warnings.push(...(await this.configureOpenClawPluginSourcePath(env, pluginSourcePath)));
|
|
1051
|
+
return warnings;
|
|
1052
|
+
}
|
|
1053
|
+
async resolveOpenGoatPluginSourcePath() {
|
|
1054
|
+
const explicit = process.env.OPENGOAT_OPENCLAW_PLUGIN_PATH?.trim();
|
|
1055
|
+
const argvEntry = process.argv[1]?.trim();
|
|
1056
|
+
const argvDir = argvEntry ? dirname(resolvePath(argvEntry)) : undefined;
|
|
1057
|
+
const argvPathCandidates = argvDir
|
|
1058
|
+
? collectPluginPathCandidatesFromArgvDir(argvDir)
|
|
1059
|
+
: [];
|
|
1060
|
+
const candidates = dedupeStrings([
|
|
1061
|
+
explicit,
|
|
1062
|
+
...argvPathCandidates,
|
|
1063
|
+
resolvePath(process.cwd(), "packages", "openclaw-plugin"),
|
|
1064
|
+
resolvePath(process.cwd(), "dist", "openclaw-plugin"),
|
|
1065
|
+
resolvePath(process.cwd(), "node_modules", "@opengoat", "openclaw-plugin"),
|
|
1066
|
+
argvDir ? resolvePath(argvDir, "..", "dist", "openclaw-plugin") : undefined,
|
|
1067
|
+
argvDir
|
|
1068
|
+
? resolvePath(argvDir, "..", "node_modules", "@opengoat", "openclaw-plugin")
|
|
1069
|
+
: undefined,
|
|
1070
|
+
argvDir
|
|
1071
|
+
? resolvePath(argvDir, "..", "..", "@opengoat", "openclaw-plugin")
|
|
1072
|
+
: undefined,
|
|
1073
|
+
]);
|
|
1074
|
+
for (const candidate of candidates) {
|
|
1075
|
+
const pluginManifestPath = this.pathPort.join(candidate, "openclaw.plugin.json");
|
|
1076
|
+
if (await this.fileSystem.exists(pluginManifestPath)) {
|
|
1077
|
+
return candidate;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
return undefined;
|
|
1081
|
+
}
|
|
1082
|
+
async configureOpenClawPluginSourcePath(env, pluginSourcePath) {
|
|
1083
|
+
const warnings = [];
|
|
1084
|
+
const currentPathsResult = await this.runOpenClaw(["config", "get", "plugins.load.paths"], { env });
|
|
1085
|
+
const currentPaths = currentPathsResult.code === 0
|
|
1086
|
+
? readStringArray(parseLooseJson(currentPathsResult.stdout))
|
|
1087
|
+
: [];
|
|
1088
|
+
const mergedPaths = await this.mergePluginLoadPaths(pluginSourcePath, currentPaths);
|
|
1089
|
+
if (!samePathList(currentPaths, mergedPaths)) {
|
|
1090
|
+
const setPaths = await this.runOpenClaw(["config", "set", "plugins.load.paths", JSON.stringify(mergedPaths)], { env });
|
|
1091
|
+
if (setPaths.code !== 0) {
|
|
1092
|
+
warnings.push(`OpenClaw plugin source path update failed (code ${setPaths.code}). ${setPaths.stderr.trim() || setPaths.stdout.trim() || ""}`.trim());
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
const pluginIds = [
|
|
1096
|
+
OPENCLAW_OPENGOAT_PLUGIN_ID,
|
|
1097
|
+
OPENCLAW_OPENGOAT_PLUGIN_ROOT_ID,
|
|
1098
|
+
OPENCLAW_OPENGOAT_PLUGIN_LEGACY_PACK_ID,
|
|
1099
|
+
OPENCLAW_OPENGOAT_PLUGIN_FALLBACK_ID,
|
|
1100
|
+
];
|
|
1101
|
+
const enableFailures = [];
|
|
1102
|
+
let pluginEnabled = false;
|
|
1103
|
+
let enabledPluginId;
|
|
1104
|
+
for (const pluginId of pluginIds) {
|
|
1105
|
+
const enablePlugin = await this.runOpenClaw(["config", "set", `plugins.entries.${pluginId}.enabled`, "true"], { env });
|
|
1106
|
+
if (enablePlugin.code === 0) {
|
|
1107
|
+
pluginEnabled = true;
|
|
1108
|
+
enabledPluginId = pluginId;
|
|
1109
|
+
break;
|
|
1110
|
+
}
|
|
1111
|
+
const message = enablePlugin.stderr.trim() || enablePlugin.stdout.trim() || "";
|
|
1112
|
+
if (isPluginNotFoundMessage(message)) {
|
|
1113
|
+
continue;
|
|
1114
|
+
}
|
|
1115
|
+
enableFailures.push(`OpenClaw plugin enable failed for "${pluginId}" (code ${enablePlugin.code}). ${message}`.trim());
|
|
1116
|
+
}
|
|
1117
|
+
if (!pluginEnabled) {
|
|
1118
|
+
if (enableFailures.length === 0) {
|
|
1119
|
+
warnings.push(`OpenClaw plugin enable failed: no matching plugin id was found (${pluginIds.join(", ")}).`);
|
|
1120
|
+
}
|
|
1121
|
+
else {
|
|
1122
|
+
warnings.push(...enableFailures);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
if (enabledPluginId) {
|
|
1126
|
+
const idsToDisable = pluginIds.filter((pluginId) => pluginId !== enabledPluginId);
|
|
1127
|
+
for (const pluginId of idsToDisable) {
|
|
1128
|
+
const disablePlugin = await this.runOpenClaw(["config", "set", `plugins.entries.${pluginId}.enabled`, "false"], { env });
|
|
1129
|
+
if (disablePlugin.code === 0) {
|
|
1130
|
+
continue;
|
|
1131
|
+
}
|
|
1132
|
+
const message = disablePlugin.stderr.trim() || disablePlugin.stdout.trim() || "";
|
|
1133
|
+
if (isPluginNotFoundMessage(message)) {
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
warnings.push(`OpenClaw plugin cleanup failed for "${pluginId}" (code ${disablePlugin.code}). ${message}`.trim());
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return warnings;
|
|
1140
|
+
}
|
|
1141
|
+
async mergePluginLoadPaths(pluginSourcePath, currentPaths) {
|
|
1142
|
+
const merged = dedupeStrings([pluginSourcePath, ...currentPaths]);
|
|
1143
|
+
const normalizedPluginSource = resolvePath(pluginSourcePath);
|
|
1144
|
+
const filtered = [];
|
|
1145
|
+
for (const candidate of merged) {
|
|
1146
|
+
const normalizedCandidate = resolvePath(candidate);
|
|
1147
|
+
if (pathMatches(normalizedCandidate, normalizedPluginSource)) {
|
|
1148
|
+
filtered.push(candidate);
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
const manifestId = await this.readPluginManifestId(candidate);
|
|
1152
|
+
if (manifestId &&
|
|
1153
|
+
isOpenGoatPluginId(manifestId)) {
|
|
1154
|
+
continue;
|
|
1155
|
+
}
|
|
1156
|
+
filtered.push(candidate);
|
|
1157
|
+
}
|
|
1158
|
+
return dedupeStrings(filtered);
|
|
1159
|
+
}
|
|
1160
|
+
async readPluginManifestId(path) {
|
|
1161
|
+
const manifestPath = this.pathPort.join(path, "openclaw.plugin.json");
|
|
1162
|
+
if (!(await this.fileSystem.exists(manifestPath))) {
|
|
1163
|
+
return undefined;
|
|
1164
|
+
}
|
|
1165
|
+
try {
|
|
1166
|
+
const raw = await this.fileSystem.readFile(manifestPath);
|
|
1167
|
+
const parsed = parseLooseJson(raw);
|
|
1168
|
+
const id = asRecord(parsed).id;
|
|
1169
|
+
if (typeof id === "string" && id.trim().length > 0) {
|
|
1170
|
+
return id.trim();
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
catch {
|
|
1174
|
+
// Ignore malformed manifests when cleaning up load paths.
|
|
1175
|
+
}
|
|
1176
|
+
return undefined;
|
|
1177
|
+
}
|
|
905
1178
|
async resolveOpenClawEnv(paths) {
|
|
906
1179
|
const providerConfig = await this.providerService.getProviderConfig(paths, OPENCLAW_PROVIDER_ID);
|
|
907
1180
|
return {
|
|
@@ -912,6 +1185,7 @@ export class OpenGoatService {
|
|
|
912
1185
|
async collectInactiveAgents(paths, manifests, inactiveMinutes, notificationTarget) {
|
|
913
1186
|
const nowMs = this.resolveNowMs();
|
|
914
1187
|
const inactiveCutoffMs = nowMs - inactiveMinutes * 60_000;
|
|
1188
|
+
const reporteeStats = buildReporteeStats(manifests);
|
|
915
1189
|
const inactive = [];
|
|
916
1190
|
for (const manifest of manifests) {
|
|
917
1191
|
const managerAgentId = normalizeAgentId(manifest.metadata.reportsTo ?? "");
|
|
@@ -926,11 +1200,16 @@ export class OpenGoatService {
|
|
|
926
1200
|
if (lastAction && lastAction.timestamp >= inactiveCutoffMs) {
|
|
927
1201
|
continue;
|
|
928
1202
|
}
|
|
1203
|
+
const directReporteesCount = reporteeStats.directByManager.get(manifest.agentId) ?? 0;
|
|
1204
|
+
const totalReporteesCount = reporteeStats.totalByManager.get(manifest.agentId) ?? 0;
|
|
1205
|
+
const indirectReporteesCount = Math.max(0, totalReporteesCount - directReporteesCount);
|
|
929
1206
|
inactive.push({
|
|
930
1207
|
managerAgentId,
|
|
931
1208
|
subjectAgentId: manifest.agentId,
|
|
932
1209
|
subjectName: manifest.metadata.name,
|
|
933
1210
|
role: manifest.metadata.description,
|
|
1211
|
+
directReporteesCount,
|
|
1212
|
+
indirectReporteesCount,
|
|
934
1213
|
lastActionTimestamp: lastAction?.timestamp,
|
|
935
1214
|
});
|
|
936
1215
|
}
|
|
@@ -946,275 +1225,129 @@ export class OpenGoatService {
|
|
|
946
1225
|
return latestProjectPath || undefined;
|
|
947
1226
|
}
|
|
948
1227
|
}
|
|
949
|
-
function
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
}
|
|
953
|
-
function containsAgentNotFoundMessage(stdout, stderr) {
|
|
954
|
-
const text = `${stdout}\n${stderr}`.toLowerCase();
|
|
955
|
-
return /\b(not found|does not exist|no such agent|unknown agent|could not find|no agent found|not exist)\b/.test(text);
|
|
956
|
-
}
|
|
957
|
-
function toErrorMessage(error) {
|
|
958
|
-
if (error instanceof Error) {
|
|
959
|
-
return error.message;
|
|
1228
|
+
function asRecord(value) {
|
|
1229
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1230
|
+
return {};
|
|
960
1231
|
}
|
|
961
|
-
return
|
|
1232
|
+
return value;
|
|
962
1233
|
}
|
|
963
|
-
function
|
|
964
|
-
|
|
965
|
-
|
|
1234
|
+
function normalizeAgentConfigEntryId(value) {
|
|
1235
|
+
const entry = asRecord(value);
|
|
1236
|
+
const id = entry.id;
|
|
1237
|
+
if (typeof id !== "string") {
|
|
1238
|
+
return undefined;
|
|
966
1239
|
}
|
|
967
|
-
return
|
|
1240
|
+
return normalizeAgentId(id);
|
|
968
1241
|
}
|
|
969
|
-
function
|
|
970
|
-
|
|
1242
|
+
function readAgentSandboxMode(entry) {
|
|
1243
|
+
const sandbox = asRecord(entry.sandbox);
|
|
1244
|
+
const mode = sandbox.mode;
|
|
1245
|
+
if (typeof mode !== "string") {
|
|
1246
|
+
return undefined;
|
|
1247
|
+
}
|
|
1248
|
+
const trimmed = mode.trim();
|
|
1249
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
971
1250
|
}
|
|
972
|
-
function
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
const record = payload;
|
|
977
|
-
if (typeof record.managedSkillsDir !== "string") {
|
|
978
|
-
return null;
|
|
979
|
-
}
|
|
980
|
-
const managedSkillsDir = record.managedSkillsDir.trim();
|
|
981
|
-
return managedSkillsDir || null;
|
|
982
|
-
}
|
|
983
|
-
function extractOpenClawAgents(payload) {
|
|
984
|
-
if (!Array.isArray(payload)) {
|
|
985
|
-
return [];
|
|
986
|
-
}
|
|
987
|
-
const entries = [];
|
|
988
|
-
for (const entry of payload) {
|
|
989
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
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;
|
|
1004
|
-
}
|
|
1005
|
-
function extractOpenClawAgentEntry(payload, agentId) {
|
|
1006
|
-
const normalizedAgentId = normalizeAgentId(agentId);
|
|
1007
|
-
if (!normalizedAgentId) {
|
|
1008
|
-
return null;
|
|
1009
|
-
}
|
|
1010
|
-
for (const entry of extractOpenClawAgents(payload)) {
|
|
1011
|
-
if (entry.id !== normalizedAgentId) {
|
|
1012
|
-
continue;
|
|
1013
|
-
}
|
|
1014
|
-
return {
|
|
1015
|
-
workspace: entry.workspace,
|
|
1016
|
-
agentDir: entry.agentDir,
|
|
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) {
|
|
1251
|
+
function hasAgentToolsAllowAll(entry) {
|
|
1252
|
+
const tools = asRecord(entry.tools);
|
|
1253
|
+
const allow = tools.allow;
|
|
1254
|
+
if (!Array.isArray(allow)) {
|
|
1025
1255
|
return false;
|
|
1026
1256
|
}
|
|
1027
|
-
return
|
|
1257
|
+
return allow.some((value) => typeof value === "string" && value.trim() === "*");
|
|
1028
1258
|
}
|
|
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;
|
|
1259
|
+
function readStringArray(value) {
|
|
1260
|
+
if (!Array.isArray(value)) {
|
|
1261
|
+
return [];
|
|
1038
1262
|
}
|
|
1039
|
-
return
|
|
1263
|
+
return value
|
|
1264
|
+
.map((entry) => (typeof entry === "string" ? entry.trim() : ""))
|
|
1265
|
+
.filter((entry) => entry.length > 0);
|
|
1040
1266
|
}
|
|
1041
|
-
function
|
|
1042
|
-
const trimmed =
|
|
1267
|
+
function parseLooseJson(raw) {
|
|
1268
|
+
const trimmed = raw.trim();
|
|
1043
1269
|
if (!trimmed) {
|
|
1044
|
-
return
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
const
|
|
1059
|
-
|
|
1060
|
-
|
|
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");
|
|
1122
|
-
}
|
|
1123
|
-
function buildInactiveAgentMessage(params) {
|
|
1124
|
-
const lastAction = typeof params.lastActionTimestamp === "number" &&
|
|
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");
|
|
1134
|
-
}
|
|
1135
|
-
function assertAgentExists(manifests, agentId) {
|
|
1136
|
-
if (manifests.some((manifest) => manifest.agentId === agentId)) {
|
|
1137
|
-
return;
|
|
1138
|
-
}
|
|
1139
|
-
throw new Error(`Agent "${agentId}" does not exist.`);
|
|
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) {
|
|
1270
|
+
return undefined;
|
|
1271
|
+
}
|
|
1272
|
+
try {
|
|
1273
|
+
return JSON.parse(trimmed);
|
|
1274
|
+
}
|
|
1275
|
+
catch {
|
|
1276
|
+
// continue
|
|
1277
|
+
}
|
|
1278
|
+
const starts = dedupeNumbers([
|
|
1279
|
+
trimmed.indexOf("{"),
|
|
1280
|
+
trimmed.indexOf("["),
|
|
1281
|
+
trimmed.lastIndexOf("{"),
|
|
1282
|
+
trimmed.lastIndexOf("["),
|
|
1283
|
+
]).filter((index) => index >= 0);
|
|
1284
|
+
for (const startIndex of starts) {
|
|
1285
|
+
const candidate = trimmed.slice(startIndex).trim();
|
|
1286
|
+
if (!candidate) {
|
|
1146
1287
|
continue;
|
|
1147
1288
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
const queue = [...(byManager.get(managerAgentId) ?? [])];
|
|
1154
|
-
while (queue.length > 0) {
|
|
1155
|
-
const current = queue.shift();
|
|
1156
|
-
if (!current || current === managerAgentId || visited.has(current)) {
|
|
1157
|
-
continue;
|
|
1289
|
+
try {
|
|
1290
|
+
return JSON.parse(candidate);
|
|
1291
|
+
}
|
|
1292
|
+
catch {
|
|
1293
|
+
// keep trying candidates
|
|
1158
1294
|
}
|
|
1159
|
-
visited.add(current);
|
|
1160
|
-
queue.push(...(byManager.get(current) ?? []));
|
|
1161
1295
|
}
|
|
1162
|
-
return
|
|
1296
|
+
return undefined;
|
|
1163
1297
|
}
|
|
1164
|
-
function
|
|
1165
|
-
|
|
1166
|
-
...resolvePreferredOpenClawCommandPaths(env),
|
|
1167
|
-
...(env.PATH?.split(path.delimiter) ?? []),
|
|
1168
|
-
]);
|
|
1169
|
-
return {
|
|
1170
|
-
...env,
|
|
1171
|
-
PATH: mergedPath.join(path.delimiter),
|
|
1172
|
-
};
|
|
1298
|
+
function dedupeNumbers(values) {
|
|
1299
|
+
return [...new Set(values)];
|
|
1173
1300
|
}
|
|
1174
|
-
function
|
|
1175
|
-
const
|
|
1176
|
-
const
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
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;
|
|
1301
|
+
function collectPluginPathCandidatesFromArgvDir(argvDir) {
|
|
1302
|
+
const maxDepth = 8;
|
|
1303
|
+
const candidates = [];
|
|
1304
|
+
let currentDir = argvDir;
|
|
1305
|
+
for (let depth = 0; depth < maxDepth; depth += 1) {
|
|
1306
|
+
candidates.push(resolvePath(currentDir, "packages", "openclaw-plugin"));
|
|
1307
|
+
candidates.push(resolvePath(currentDir, "openclaw-plugin"));
|
|
1308
|
+
const parentDir = dirname(currentDir);
|
|
1309
|
+
if (parentDir === currentDir) {
|
|
1310
|
+
break;
|
|
1311
|
+
}
|
|
1312
|
+
currentDir = parentDir;
|
|
1313
|
+
}
|
|
1314
|
+
return dedupeStrings(candidates);
|
|
1199
1315
|
}
|
|
1200
|
-
function
|
|
1316
|
+
function dedupeStrings(values) {
|
|
1201
1317
|
const seen = new Set();
|
|
1202
1318
|
const deduped = [];
|
|
1203
|
-
for (const
|
|
1204
|
-
const
|
|
1205
|
-
if (!
|
|
1319
|
+
for (const value of values) {
|
|
1320
|
+
const trimmed = value?.trim();
|
|
1321
|
+
if (!trimmed || seen.has(trimmed)) {
|
|
1206
1322
|
continue;
|
|
1207
1323
|
}
|
|
1208
|
-
seen.add(
|
|
1209
|
-
deduped.push(
|
|
1324
|
+
seen.add(trimmed);
|
|
1325
|
+
deduped.push(trimmed);
|
|
1210
1326
|
}
|
|
1211
1327
|
return deduped;
|
|
1212
1328
|
}
|
|
1213
|
-
function
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1329
|
+
function samePathList(left, right) {
|
|
1330
|
+
if (left.length !== right.length) {
|
|
1331
|
+
return false;
|
|
1332
|
+
}
|
|
1333
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
1334
|
+
const leftValue = left[index];
|
|
1335
|
+
const rightValue = right[index];
|
|
1336
|
+
if (!leftValue || !rightValue || !pathMatches(leftValue, rightValue)) {
|
|
1337
|
+
return false;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
return true;
|
|
1341
|
+
}
|
|
1342
|
+
function isOpenGoatPluginId(pluginId) {
|
|
1343
|
+
return [
|
|
1344
|
+
OPENCLAW_OPENGOAT_PLUGIN_ID,
|
|
1345
|
+
OPENCLAW_OPENGOAT_PLUGIN_ROOT_ID,
|
|
1346
|
+
OPENCLAW_OPENGOAT_PLUGIN_LEGACY_PACK_ID,
|
|
1347
|
+
OPENCLAW_OPENGOAT_PLUGIN_FALLBACK_ID,
|
|
1348
|
+
].includes(pluginId.trim().toLowerCase());
|
|
1349
|
+
}
|
|
1350
|
+
function isPluginNotFoundMessage(message) {
|
|
1351
|
+
return message.toLowerCase().includes("plugin not found");
|
|
1219
1352
|
}
|
|
1220
1353
|
//# sourceMappingURL=opengoat.service.js.map
|