@opengoat/core 2026.2.9 → 2026.2.14
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/README.md +3 -3
- package/dist/core/acp/application/acp-agent.js +4 -8
- package/dist/core/acp/application/acp-agent.js.map +1 -1
- package/dist/core/agents/application/agent-manifest.service.d.ts +1 -4
- package/dist/core/agents/application/agent-manifest.service.js +39 -52
- package/dist/core/agents/application/agent-manifest.service.js.map +1 -1
- package/dist/core/agents/application/agent.service.d.ts +43 -4
- package/dist/core/agents/application/agent.service.js +476 -55
- package/dist/core/agents/application/agent.service.js.map +1 -1
- package/dist/core/agents/domain/agent-manifest.d.ts +10 -4
- package/dist/core/agents/domain/agent-manifest.js +118 -25
- package/dist/core/agents/domain/agent-manifest.js.map +1 -1
- package/dist/core/agents/index.d.ts +1 -3
- package/dist/core/agents/index.js +1 -3
- package/dist/core/agents/index.js.map +1 -1
- package/dist/core/boards/application/board.service.d.ts +64 -0
- package/dist/core/boards/application/board.service.js +590 -0
- package/dist/core/boards/application/board.service.js.map +1 -0
- package/dist/core/boards/domain/board.d.ts +30 -0
- package/dist/core/boards/domain/board.js +2 -0
- package/dist/core/boards/domain/board.js.map +1 -0
- package/dist/core/boards/index.d.ts +2 -0
- package/dist/core/boards/index.js +2 -0
- package/dist/core/boards/index.js.map +1 -0
- package/dist/core/bootstrap/application/bootstrap.service.d.ts +5 -2
- package/dist/core/bootstrap/application/bootstrap.service.js +32 -17
- package/dist/core/bootstrap/application/bootstrap.service.js.map +1 -1
- package/dist/core/domain/agent-id.d.ts +1 -1
- package/dist/core/domain/agent-id.js +1 -1
- package/dist/core/domain/agent-id.js.map +1 -1
- package/dist/core/domain/agent.d.ts +31 -8
- package/dist/core/domain/opengoat-paths.d.ts +1 -0
- package/dist/core/opengoat/application/opengoat.service.d.ts +111 -27
- package/dist/core/opengoat/application/opengoat.service.js +1048 -136
- package/dist/core/opengoat/application/opengoat.service.js.map +1 -1
- package/dist/core/opengoat/index.d.ts +1 -0
- package/dist/core/orchestration/application/orchestration.service.d.ts +8 -21
- package/dist/core/orchestration/application/orchestration.service.js +59 -871
- package/dist/core/orchestration/application/orchestration.service.js.map +1 -1
- package/dist/core/orchestration/application/routing.service.js +10 -8
- package/dist/core/orchestration/application/routing.service.js.map +1 -1
- package/dist/core/orchestration/domain/routing.d.ts +0 -17
- package/dist/core/orchestration/domain/run-events.d.ts +1 -1
- package/dist/core/orchestration/index.d.ts +0 -2
- package/dist/core/orchestration/index.js +0 -1
- package/dist/core/orchestration/index.js.map +1 -1
- package/dist/core/providers/application/provider.service.d.ts +16 -32
- package/dist/core/providers/application/provider.service.js +178 -202
- package/dist/core/providers/application/provider.service.js.map +1 -1
- package/dist/core/providers/cli-provider.d.ts +1 -0
- package/dist/core/providers/cli-provider.js +66 -1
- package/dist/core/providers/cli-provider.js.map +1 -1
- package/dist/core/providers/image-input.d.ts +16 -0
- package/dist/core/providers/image-input.js +158 -0
- package/dist/core/providers/image-input.js.map +1 -0
- package/dist/core/providers/index.d.ts +1 -7
- package/dist/core/providers/index.js +0 -13
- package/dist/core/providers/index.js.map +1 -1
- package/dist/core/providers/loader.js +1 -95
- package/dist/core/providers/loader.js.map +1 -1
- package/dist/core/providers/providers/openclaw/provider.js +213 -32
- package/dist/core/providers/providers/openclaw/provider.js.map +1 -1
- package/dist/core/providers/types.d.ts +8 -3
- package/dist/core/scenarios/application/scenario-runner.service.d.ts +0 -1
- package/dist/core/scenarios/application/scenario-runner.service.js +30 -87
- package/dist/core/scenarios/application/scenario-runner.service.js.map +1 -1
- package/dist/core/scenarios/domain/scenario.d.ts +0 -15
- package/dist/core/sessions/application/session.service.d.ts +10 -4
- package/dist/core/sessions/application/session.service.js +87 -89
- package/dist/core/sessions/application/session.service.js.map +1 -1
- package/dist/core/sessions/domain/session.d.ts +3 -3
- package/dist/core/sessions/domain/transcript.d.ts +1 -1
- package/dist/core/sessions/index.d.ts +1 -1
- package/dist/core/sessions/index.js.map +1 -1
- package/dist/core/skills/application/skill.service.d.ts +2 -3
- package/dist/core/skills/application/skill.service.js +153 -56
- package/dist/core/skills/application/skill.service.js.map +1 -1
- package/dist/core/skills/domain/skill.d.ts +2 -2
- package/dist/core/skills/domain/skill.js +4 -2
- package/dist/core/skills/domain/skill.js.map +1 -1
- package/dist/core/templates/assets/ceo/BOOTSTRAP.md +41 -0
- package/dist/core/templates/assets/ceo/ROLE.md +15 -0
- package/dist/core/templates/assets/organization/MISSION.md +73 -0
- package/dist/core/templates/assets/organization/STRATEGY.md +107 -0
- package/dist/core/templates/assets/organization/VISION.md +80 -0
- package/dist/core/templates/assets/organization/wiki/index.md +15 -0
- package/dist/core/templates/assets/skills/og-board-individual/SKILL.md +148 -0
- package/dist/core/templates/assets/skills/og-board-manager/SKILL.md +140 -0
- package/dist/core/templates/default-templates.d.ts +17 -14
- package/dist/core/templates/default-templates.js +115 -212
- package/dist/core/templates/default-templates.js.map +1 -1
- package/dist/index.d.ts +1 -4
- package/dist/index.js +1 -4
- package/dist/index.js.map +1 -1
- package/dist/platform/node/node-path.port.js +1 -0
- package/dist/platform/node/node-path.port.js.map +1 -1
- package/package.json +16 -13
- package/dist/core/agents/application/workspace-context.service.d.ts +0 -28
- package/dist/core/agents/application/workspace-context.service.js +0 -157
- package/dist/core/agents/application/workspace-context.service.js.map +0 -1
- package/dist/core/agents/domain/workspace-context.d.ts +0 -13
- package/dist/core/agents/domain/workspace-context.js +0 -14
- package/dist/core/agents/domain/workspace-context.js.map +0 -1
- package/dist/core/gateway/domain/protocol.d.ts +0 -113
- package/dist/core/gateway/domain/protocol.js +0 -394
- package/dist/core/gateway/domain/protocol.js.map +0 -1
- package/dist/core/gateway/index.d.ts +0 -2
- package/dist/core/gateway/index.js +0 -2
- package/dist/core/gateway/index.js.map +0 -1
- package/dist/core/llm/application/vercel-ai-text-runtime.d.ts +0 -26
- package/dist/core/llm/application/vercel-ai-text-runtime.js +0 -223
- package/dist/core/llm/application/vercel-ai-text-runtime.js.map +0 -1
- package/dist/core/llm/domain/text-runtime.d.ts +0 -22
- package/dist/core/llm/domain/text-runtime.js +0 -2
- package/dist/core/llm/domain/text-runtime.js.map +0 -1
- package/dist/core/llm/index.d.ts +0 -2
- package/dist/core/llm/index.js +0 -2
- package/dist/core/llm/index.js.map +0 -1
- package/dist/core/orchestration/application/orchestration-planner.service.d.ts +0 -28
- package/dist/core/orchestration/application/orchestration-planner.service.js +0 -279
- package/dist/core/orchestration/application/orchestration-planner.service.js.map +0 -1
- package/dist/core/orchestration/domain/loop.d.ts +0 -119
- package/dist/core/orchestration/domain/loop.js +0 -2
- package/dist/core/orchestration/domain/loop.js.map +0 -1
- package/dist/core/plugins/application/plugin.service.d.ts +0 -32
- package/dist/core/plugins/application/plugin.service.js +0 -236
- package/dist/core/plugins/application/plugin.service.js.map +0 -1
- package/dist/core/plugins/domain/openclaw-compat.d.ts +0 -60
- package/dist/core/plugins/domain/openclaw-compat.js +0 -9
- package/dist/core/plugins/domain/openclaw-compat.js.map +0 -1
- package/dist/core/plugins/index.d.ts +0 -3
- package/dist/core/plugins/index.js +0 -3
- package/dist/core/plugins/index.js.map +0 -1
- package/dist/core/providers/onboarding.d.ts +0 -13
- package/dist/core/providers/onboarding.js +0 -149
- package/dist/core/providers/onboarding.js.map +0 -1
- package/dist/core/providers/providers/claude/index.d.ts +0 -4
- package/dist/core/providers/providers/claude/index.js +0 -19
- package/dist/core/providers/providers/claude/index.js.map +0 -1
- package/dist/core/providers/providers/claude/provider.d.ts +0 -8
- package/dist/core/providers/providers/claude/provider.js +0 -106
- package/dist/core/providers/providers/claude/provider.js.map +0 -1
- package/dist/core/providers/providers/codex/index.d.ts +0 -4
- package/dist/core/providers/providers/codex/index.js +0 -19
- package/dist/core/providers/providers/codex/index.js.map +0 -1
- package/dist/core/providers/providers/codex/provider.d.ts +0 -7
- package/dist/core/providers/providers/codex/provider.js +0 -31
- package/dist/core/providers/providers/codex/provider.js.map +0 -1
- package/dist/core/providers/providers/cursor/index.d.ts +0 -4
- package/dist/core/providers/providers/cursor/index.js +0 -19
- package/dist/core/providers/providers/cursor/index.js.map +0 -1
- package/dist/core/providers/providers/cursor/provider.d.ts +0 -11
- package/dist/core/providers/providers/cursor/provider.js +0 -90
- package/dist/core/providers/providers/cursor/provider.js.map +0 -1
- package/dist/core/providers/providers/extended-http/catalog.d.ts +0 -40
- package/dist/core/providers/providers/extended-http/catalog.js +0 -728
- package/dist/core/providers/providers/extended-http/catalog.js.map +0 -1
- package/dist/core/providers/providers/extended-http/index.d.ts +0 -5
- package/dist/core/providers/providers/extended-http/index.js +0 -13
- package/dist/core/providers/providers/extended-http/index.js.map +0 -1
- package/dist/core/providers/providers/extended-http/provider.d.ts +0 -31
- package/dist/core/providers/providers/extended-http/provider.js +0 -580
- package/dist/core/providers/providers/extended-http/provider.js.map +0 -1
- package/dist/core/providers/providers/gemini/index.d.ts +0 -4
- package/dist/core/providers/providers/gemini/index.js +0 -37
- package/dist/core/providers/providers/gemini/index.js.map +0 -1
- package/dist/core/providers/providers/gemini/provider.d.ts +0 -7
- package/dist/core/providers/providers/gemini/provider.js +0 -59
- package/dist/core/providers/providers/gemini/provider.js.map +0 -1
- package/dist/core/providers/providers/grok/index.d.ts +0 -4
- package/dist/core/providers/providers/grok/index.js +0 -41
- package/dist/core/providers/providers/grok/index.js.map +0 -1
- package/dist/core/providers/providers/grok/provider.d.ts +0 -13
- package/dist/core/providers/providers/grok/provider.js +0 -95
- package/dist/core/providers/providers/grok/provider.js.map +0 -1
- package/dist/core/providers/providers/openai/index.d.ts +0 -4
- package/dist/core/providers/providers/openai/index.js +0 -45
- package/dist/core/providers/providers/openai/index.js.map +0 -1
- package/dist/core/providers/providers/openai/provider.d.ts +0 -14
- package/dist/core/providers/providers/openai/provider.js +0 -203
- package/dist/core/providers/providers/openai/provider.js.map +0 -1
- package/dist/core/providers/providers/opencode/index.d.ts +0 -4
- package/dist/core/providers/providers/opencode/index.js +0 -23
- package/dist/core/providers/providers/opencode/index.js.map +0 -1
- package/dist/core/providers/providers/opencode/provider.d.ts +0 -11
- package/dist/core/providers/providers/opencode/provider.js +0 -215
- package/dist/core/providers/providers/opencode/provider.js.map +0 -1
- package/dist/core/providers/providers/openrouter/index.d.ts +0 -4
- package/dist/core/providers/providers/openrouter/index.js +0 -37
- package/dist/core/providers/providers/openrouter/index.js.map +0 -1
- package/dist/core/providers/providers/openrouter/provider.d.ts +0 -13
- package/dist/core/providers/providers/openrouter/provider.js +0 -80
- package/dist/core/providers/providers/openrouter/provider.js.map +0 -1
- package/dist/platform/node/opengoat-gateway-client.d.ts +0 -19
- package/dist/platform/node/opengoat-gateway-client.js +0 -194
- package/dist/platform/node/opengoat-gateway-client.js.map +0 -1
- package/dist/platform/node/opengoat-gateway-server.d.ts +0 -53
- package/dist/platform/node/opengoat-gateway-server.js +0 -906
- package/dist/platform/node/opengoat-gateway-server.js.map +0 -1
|
@@ -1,207 +1,426 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import { createDefaultProviderRegistry } from "../../providers/index.js";
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
4
3
|
import { AgentManifestService } from "../../agents/application/agent-manifest.service.js";
|
|
5
4
|
import { AgentService } from "../../agents/application/agent.service.js";
|
|
6
|
-
import {
|
|
5
|
+
import { BoardService, } from "../../boards/index.js";
|
|
7
6
|
import { BootstrapService } from "../../bootstrap/application/bootstrap.service.js";
|
|
8
|
-
import {
|
|
7
|
+
import { DEFAULT_AGENT_ID, normalizeAgentId } from "../../domain/agent-id.js";
|
|
8
|
+
import { createNoopLogger } from "../../logging/index.js";
|
|
9
|
+
import { OrchestrationService, } from "../../orchestration/index.js";
|
|
9
10
|
import { ProviderService } from "../../providers/application/provider.service.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { ProviderCommandNotFoundError, createDefaultProviderRegistry, } from "../../providers/index.js";
|
|
12
|
+
import { SessionService, } from "../../sessions/index.js";
|
|
13
|
+
import { SkillService, } from "../../skills/index.js";
|
|
14
|
+
const OPENCLAW_PROVIDER_ID = "openclaw";
|
|
15
|
+
const OPENCLAW_DEFAULT_AGENT_ID = "main";
|
|
13
16
|
export class OpenGoatService {
|
|
17
|
+
fileSystem;
|
|
18
|
+
pathPort;
|
|
14
19
|
pathsProvider;
|
|
15
20
|
agentService;
|
|
16
21
|
agentManifestService;
|
|
17
22
|
bootstrapService;
|
|
18
23
|
providerService;
|
|
19
|
-
pluginService;
|
|
20
24
|
skillService;
|
|
21
25
|
sessionService;
|
|
22
26
|
orchestrationService;
|
|
27
|
+
boardService;
|
|
28
|
+
commandRunner;
|
|
29
|
+
nowIso;
|
|
30
|
+
openClawManagedSkillsDirCache;
|
|
23
31
|
constructor(deps) {
|
|
24
32
|
const nowIso = deps.nowIso ?? (() => new Date().toISOString());
|
|
25
|
-
const rootLogger = (deps.logger ?? createNoopLogger()).child({
|
|
33
|
+
const rootLogger = (deps.logger ?? createNoopLogger()).child({
|
|
34
|
+
scope: "opengoat-service",
|
|
35
|
+
});
|
|
26
36
|
const providerRegistryFactory = deps.providerRegistry
|
|
27
37
|
? () => deps.providerRegistry
|
|
28
38
|
: () => createDefaultProviderRegistry();
|
|
39
|
+
this.fileSystem = deps.fileSystem;
|
|
40
|
+
this.pathPort = deps.pathPort;
|
|
29
41
|
this.pathsProvider = deps.pathsProvider;
|
|
42
|
+
this.nowIso = nowIso;
|
|
30
43
|
this.agentService = new AgentService({
|
|
31
44
|
fileSystem: deps.fileSystem,
|
|
32
45
|
pathPort: deps.pathPort,
|
|
33
|
-
nowIso
|
|
46
|
+
nowIso,
|
|
34
47
|
});
|
|
35
48
|
this.agentManifestService = new AgentManifestService({
|
|
36
49
|
fileSystem: deps.fileSystem,
|
|
37
|
-
pathPort: deps.pathPort
|
|
50
|
+
pathPort: deps.pathPort,
|
|
38
51
|
});
|
|
39
52
|
this.bootstrapService = new BootstrapService({
|
|
40
53
|
fileSystem: deps.fileSystem,
|
|
54
|
+
pathPort: deps.pathPort,
|
|
41
55
|
pathsProvider: deps.pathsProvider,
|
|
42
56
|
agentService: this.agentService,
|
|
43
|
-
nowIso
|
|
44
|
-
});
|
|
45
|
-
const workspaceContextService = new WorkspaceContextService({
|
|
46
|
-
fileSystem: deps.fileSystem,
|
|
47
|
-
pathPort: deps.pathPort
|
|
57
|
+
nowIso,
|
|
48
58
|
});
|
|
49
|
-
this.pluginService =
|
|
50
|
-
deps.pluginService ??
|
|
51
|
-
new PluginService({
|
|
52
|
-
fileSystem: deps.fileSystem,
|
|
53
|
-
pathPort: deps.pathPort,
|
|
54
|
-
commandRunner: deps.commandRunner
|
|
55
|
-
});
|
|
56
59
|
this.skillService = new SkillService({
|
|
57
60
|
fileSystem: deps.fileSystem,
|
|
58
61
|
pathPort: deps.pathPort,
|
|
59
|
-
pluginSkillDirsProvider: (paths) => this.pluginService.resolvePluginSkillDirectories(paths)
|
|
60
62
|
});
|
|
61
63
|
this.providerService = new ProviderService({
|
|
62
64
|
fileSystem: deps.fileSystem,
|
|
63
65
|
pathPort: deps.pathPort,
|
|
64
66
|
providerRegistry: providerRegistryFactory,
|
|
65
|
-
workspaceContextService,
|
|
66
|
-
skillService: this.skillService,
|
|
67
67
|
nowIso,
|
|
68
|
-
logger: rootLogger.child({ scope: "provider" })
|
|
68
|
+
logger: rootLogger.child({ scope: "provider" }),
|
|
69
69
|
});
|
|
70
70
|
this.sessionService = new SessionService({
|
|
71
71
|
fileSystem: deps.fileSystem,
|
|
72
72
|
pathPort: deps.pathPort,
|
|
73
73
|
commandRunner: deps.commandRunner,
|
|
74
74
|
nowIso,
|
|
75
|
-
nowMs: () => Date.now()
|
|
75
|
+
nowMs: () => Date.now(),
|
|
76
76
|
});
|
|
77
|
+
this.commandRunner = deps.commandRunner;
|
|
77
78
|
this.orchestrationService = new OrchestrationService({
|
|
78
79
|
providerService: this.providerService,
|
|
79
|
-
skillService: this.skillService,
|
|
80
80
|
agentManifestService: this.agentManifestService,
|
|
81
81
|
sessionService: this.sessionService,
|
|
82
|
-
commandRunner: deps.commandRunner,
|
|
83
82
|
fileSystem: deps.fileSystem,
|
|
84
83
|
pathPort: deps.pathPort,
|
|
85
84
|
nowIso,
|
|
86
|
-
logger: rootLogger.child({ scope: "orchestration" })
|
|
85
|
+
logger: rootLogger.child({ scope: "orchestration" }),
|
|
86
|
+
});
|
|
87
|
+
this.boardService = new BoardService({
|
|
88
|
+
fileSystem: deps.fileSystem,
|
|
89
|
+
pathPort: deps.pathPort,
|
|
90
|
+
nowIso,
|
|
91
|
+
agentManifestService: this.agentManifestService,
|
|
87
92
|
});
|
|
88
93
|
}
|
|
89
94
|
initialize() {
|
|
90
|
-
return this.
|
|
95
|
+
return this.initializeRuntimeDefaults();
|
|
91
96
|
}
|
|
92
|
-
async
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
async hardReset() {
|
|
98
|
+
const paths = this.pathsProvider.getPaths();
|
|
99
|
+
const warnings = [];
|
|
100
|
+
const deletedOpenClawAgents = [];
|
|
101
|
+
const failedOpenClawAgents = [];
|
|
102
|
+
const removedOpenClawManagedSkillDirs = [];
|
|
103
|
+
const candidateOpenClawAgentIds = new Set();
|
|
104
|
+
const localAgents = await this.agentService.listAgents(paths);
|
|
105
|
+
for (const agent of localAgents) {
|
|
106
|
+
if (agent.id === OPENCLAW_DEFAULT_AGENT_ID) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
candidateOpenClawAgentIds.add(agent.id);
|
|
110
|
+
}
|
|
111
|
+
await this.addWorkspaceAgentCandidates(paths, candidateOpenClawAgentIds, warnings);
|
|
112
|
+
try {
|
|
113
|
+
const openClawAgents = await this.listOpenClawAgents(paths);
|
|
114
|
+
for (const entry of openClawAgents) {
|
|
115
|
+
if (entry.id !== OPENCLAW_DEFAULT_AGENT_ID &&
|
|
116
|
+
(pathIsWithin(paths.homeDir, entry.workspace) ||
|
|
117
|
+
pathIsWithin(paths.homeDir, entry.agentDir))) {
|
|
118
|
+
candidateOpenClawAgentIds.add(entry.id);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
warnings.push(`OpenClaw agent discovery failed: ${toErrorMessage(error)}`);
|
|
124
|
+
}
|
|
125
|
+
for (const agentId of [...candidateOpenClawAgentIds].sort((left, right) => left.localeCompare(right))) {
|
|
126
|
+
if (agentId === OPENCLAW_DEFAULT_AGENT_ID) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const deleted = await this.providerService.deleteProviderAgent(paths, agentId, { providerId: OPENCLAW_PROVIDER_ID });
|
|
131
|
+
if (deleted.code === 0 ||
|
|
132
|
+
containsAgentNotFoundMessage(deleted.stdout, deleted.stderr)) {
|
|
133
|
+
deletedOpenClawAgents.push(agentId);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const failureReason = deleted.stderr.trim() || deleted.stdout.trim();
|
|
137
|
+
if (!failureReason) {
|
|
138
|
+
warnings.push(`OpenClaw delete for "${agentId}" failed with code ${deleted.code}.`);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
failedOpenClawAgents.push({
|
|
142
|
+
agentId,
|
|
143
|
+
reason: failureReason,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (error instanceof ProviderCommandNotFoundError) {
|
|
148
|
+
deletedOpenClawAgents.push(agentId);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
failedOpenClawAgents.push({
|
|
152
|
+
agentId,
|
|
153
|
+
reason: toErrorMessage(error),
|
|
154
|
+
});
|
|
98
155
|
}
|
|
99
156
|
}
|
|
157
|
+
try {
|
|
158
|
+
const managedSkillsCleanup = await this.removeOpenClawManagedRoleSkills(paths);
|
|
159
|
+
removedOpenClawManagedSkillDirs.push(...managedSkillsCleanup.removedPaths);
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
warnings.push(`OpenClaw managed skills cleanup failed: ${toErrorMessage(error)}`);
|
|
163
|
+
}
|
|
164
|
+
await this.fileSystem.removeDir(paths.homeDir);
|
|
165
|
+
this.openClawManagedSkillsDirCache = undefined;
|
|
166
|
+
const homeRemoved = !(await this.fileSystem.exists(paths.homeDir));
|
|
167
|
+
return {
|
|
168
|
+
homeDir: paths.homeDir,
|
|
169
|
+
homeRemoved,
|
|
170
|
+
deletedOpenClawAgents,
|
|
171
|
+
failedOpenClawAgents,
|
|
172
|
+
removedOpenClawManagedSkillDirs,
|
|
173
|
+
warnings,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
async syncRuntimeDefaults() {
|
|
177
|
+
const paths = this.pathsProvider.getPaths();
|
|
178
|
+
const warnings = [];
|
|
179
|
+
let ceoSynced = false;
|
|
180
|
+
let ceoSyncCode;
|
|
181
|
+
let ceoDescriptor = (await this.agentService.listAgents(paths)).find((agent) => agent.id === DEFAULT_AGENT_ID);
|
|
182
|
+
if (!ceoDescriptor) {
|
|
183
|
+
const created = await this.agentService.ensureAgent(paths, {
|
|
184
|
+
id: DEFAULT_AGENT_ID,
|
|
185
|
+
displayName: "CEO",
|
|
186
|
+
}, {
|
|
187
|
+
type: "manager",
|
|
188
|
+
reportsTo: null,
|
|
189
|
+
role: "CEO",
|
|
190
|
+
});
|
|
191
|
+
ceoDescriptor = created.agent;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
await this.syncOpenClawRoleSkills(paths, DEFAULT_AGENT_ID);
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
warnings.push(`OpenClaw role skill assignment sync for "ceo" failed: ${toErrorMessage(error)}`);
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
const ceoSync = await this.providerService.createProviderAgent(paths, DEFAULT_AGENT_ID, {
|
|
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
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
warnings.push(`OpenClaw sync for "ceo" failed: ${toErrorMessage(error)}`);
|
|
216
|
+
}
|
|
217
|
+
if (ceoSynced) {
|
|
218
|
+
try {
|
|
219
|
+
await this.ensureOpenClawAgentLocation(paths, {
|
|
220
|
+
agentId: DEFAULT_AGENT_ID,
|
|
221
|
+
displayName: ceoDescriptor.displayName,
|
|
222
|
+
workspaceDir: ceoDescriptor.workspaceDir,
|
|
223
|
+
internalConfigDir: ceoDescriptor.internalConfigDir,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
warnings.push(`OpenClaw ceo location sync failed: ${toErrorMessage(error)}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
await this.agentService.ensureCeoWorkspaceBootstrap(paths);
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
warnings.push(`OpenGoat workspace bootstrap for "ceo" failed: ${toErrorMessage(error)}`);
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
ceoSyncCode,
|
|
238
|
+
ceoSynced,
|
|
239
|
+
warnings,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
async createAgent(rawName, options = {}) {
|
|
100
243
|
const identity = this.agentService.normalizeAgentName(rawName);
|
|
101
244
|
const paths = this.pathsProvider.getPaths();
|
|
102
|
-
const created = await this.agentService.ensureAgent(paths, identity
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
throw new Error(`
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
const externalAgentCreation = await this.providerService.createProviderAgent(paths, created.agent.id, {
|
|
124
|
-
providerId: resolvedBinding.providerId,
|
|
245
|
+
const created = await this.agentService.ensureAgent(paths, identity, {
|
|
246
|
+
type: options.type,
|
|
247
|
+
reportsTo: options.reportsTo,
|
|
248
|
+
skills: options.skills,
|
|
249
|
+
role: options.role,
|
|
250
|
+
});
|
|
251
|
+
try {
|
|
252
|
+
const workspaceSkillSync = await this.syncOpenClawRoleSkills(paths, created.agent.id);
|
|
253
|
+
created.createdPaths.push(...workspaceSkillSync.createdPaths);
|
|
254
|
+
created.skippedPaths.push(...workspaceSkillSync.skippedPaths);
|
|
255
|
+
created.skippedPaths.push(...workspaceSkillSync.removedPaths);
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
if (!created.alreadyExisted) {
|
|
259
|
+
await this.agentService.removeAgent(paths, created.agent.id);
|
|
260
|
+
}
|
|
261
|
+
throw new Error(`Failed to sync OpenClaw role skills for "${created.agent.id}". ${toErrorMessage(error)}`);
|
|
262
|
+
}
|
|
263
|
+
const runtimeSync = await this.providerService.createProviderAgent(paths, created.agent.id, {
|
|
264
|
+
providerId: OPENCLAW_PROVIDER_ID,
|
|
125
265
|
displayName: created.agent.displayName,
|
|
126
266
|
workspaceDir: created.agent.workspaceDir,
|
|
127
|
-
internalConfigDir: created.agent.internalConfigDir
|
|
267
|
+
internalConfigDir: created.agent.internalConfigDir,
|
|
128
268
|
});
|
|
269
|
+
if (runtimeSync.code !== 0 &&
|
|
270
|
+
!containsAlreadyExistsMessage(runtimeSync.stdout, runtimeSync.stderr)) {
|
|
271
|
+
if (!created.alreadyExisted) {
|
|
272
|
+
await this.agentService.removeAgent(paths, created.agent.id);
|
|
273
|
+
}
|
|
274
|
+
throw new Error(`OpenClaw agent creation failed for "${created.agent.id}" (exit ${runtimeSync.code}). ${runtimeSync.stderr.trim() || runtimeSync.stdout.trim() || ""}`.trim());
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
await this.ensureOpenClawAgentLocation(paths, {
|
|
278
|
+
agentId: created.agent.id,
|
|
279
|
+
displayName: created.agent.displayName,
|
|
280
|
+
workspaceDir: created.agent.workspaceDir,
|
|
281
|
+
internalConfigDir: created.agent.internalConfigDir,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
if (!created.alreadyExisted) {
|
|
286
|
+
await this.agentService.removeAgent(paths, created.agent.id);
|
|
287
|
+
}
|
|
288
|
+
throw new Error(`OpenClaw agent location sync failed for "${created.agent.id}". ${toErrorMessage(error)}`);
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
const workspaceBootstrap = await this.agentService.ensureAgentWorkspaceBootstrap(paths, {
|
|
292
|
+
agentId: created.agent.id,
|
|
293
|
+
displayName: created.agent.displayName,
|
|
294
|
+
role: options.role?.trim() ??
|
|
295
|
+
(created.alreadyExisted ? created.agent.role : ""),
|
|
296
|
+
});
|
|
297
|
+
created.createdPaths.push(...workspaceBootstrap.createdPaths);
|
|
298
|
+
created.skippedPaths.push(...workspaceBootstrap.skippedPaths);
|
|
299
|
+
created.skippedPaths.push(...workspaceBootstrap.removedPaths);
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
if (!created.alreadyExisted) {
|
|
303
|
+
await this.agentService.removeAgent(paths, created.agent.id);
|
|
304
|
+
}
|
|
305
|
+
throw new Error(`Failed to update workspace bootstrap for "${created.agent.id}". ${toErrorMessage(error)}`);
|
|
306
|
+
}
|
|
129
307
|
return {
|
|
130
308
|
...created,
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
code:
|
|
134
|
-
stdout:
|
|
135
|
-
stderr:
|
|
136
|
-
}
|
|
309
|
+
runtimeSync: {
|
|
310
|
+
runtimeId: runtimeSync.providerId,
|
|
311
|
+
code: runtimeSync.code,
|
|
312
|
+
stdout: runtimeSync.stdout,
|
|
313
|
+
stderr: runtimeSync.stderr,
|
|
314
|
+
},
|
|
137
315
|
};
|
|
138
316
|
}
|
|
139
|
-
async
|
|
317
|
+
async deleteAgent(rawAgentId, options = {}) {
|
|
140
318
|
const paths = this.pathsProvider.getPaths();
|
|
141
319
|
const agentId = normalizeAgentId(rawAgentId);
|
|
142
320
|
if (!agentId) {
|
|
143
321
|
throw new Error("Agent id cannot be empty.");
|
|
144
322
|
}
|
|
145
|
-
const
|
|
146
|
-
if (!
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
const supportsExternalAgentCreation = await this.providerSupportsExternalAgentCreation(resolvedProviderId);
|
|
152
|
-
if (!supportsExternalAgentCreation) {
|
|
153
|
-
throw new Error(`Provider "${resolvedProviderId}" does not support external agent creation.`);
|
|
154
|
-
}
|
|
155
|
-
const externalAgentCreation = await this.providerService.createProviderAgent(paths, agentId, {
|
|
156
|
-
providerId: resolvedProviderId,
|
|
157
|
-
displayName: agent.displayName,
|
|
158
|
-
workspaceDir: agent.workspaceDir,
|
|
159
|
-
internalConfigDir: agent.internalConfigDir
|
|
323
|
+
const existing = (await this.agentService.listAgents(paths)).find((entry) => entry.id === agentId);
|
|
324
|
+
if (!existing) {
|
|
325
|
+
return this.agentService.removeAgent(paths, agentId);
|
|
326
|
+
}
|
|
327
|
+
const runtimeSync = await this.providerService.deleteProviderAgent(paths, agentId, {
|
|
328
|
+
providerId: OPENCLAW_PROVIDER_ID,
|
|
160
329
|
});
|
|
330
|
+
if (runtimeSync.code !== 0 && !options.force) {
|
|
331
|
+
throw new Error(`OpenClaw agent deletion failed for "${agentId}" (exit ${runtimeSync.code}). ${runtimeSync.stderr.trim() || runtimeSync.stdout.trim() || ""}`.trim());
|
|
332
|
+
}
|
|
333
|
+
const removed = await this.agentService.removeAgent(paths, agentId);
|
|
161
334
|
return {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
335
|
+
...removed,
|
|
336
|
+
runtimeSync: {
|
|
337
|
+
runtimeId: runtimeSync.providerId,
|
|
338
|
+
code: runtimeSync.code,
|
|
339
|
+
stdout: runtimeSync.stdout,
|
|
340
|
+
stderr: runtimeSync.stderr,
|
|
341
|
+
},
|
|
166
342
|
};
|
|
167
343
|
}
|
|
168
|
-
async
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
|
|
344
|
+
async setAgentManager(rawAgentId, rawReportsTo) {
|
|
345
|
+
const paths = this.pathsProvider.getPaths();
|
|
346
|
+
const updated = await this.agentService.setAgentManager(paths, rawAgentId, rawReportsTo);
|
|
347
|
+
await this.syncOpenClawRoleSkills(paths, updated.agentId);
|
|
348
|
+
if (updated.previousReportsTo) {
|
|
349
|
+
await this.syncOpenClawRoleSkills(paths, updated.previousReportsTo);
|
|
350
|
+
}
|
|
351
|
+
if (updated.reportsTo) {
|
|
352
|
+
await this.syncOpenClawRoleSkills(paths, updated.reportsTo);
|
|
353
|
+
}
|
|
354
|
+
return updated;
|
|
355
|
+
}
|
|
356
|
+
async listAgents() {
|
|
357
|
+
const paths = this.pathsProvider.getPaths();
|
|
358
|
+
return this.agentService.listAgents(paths);
|
|
172
359
|
}
|
|
173
|
-
async
|
|
360
|
+
async listDirectReportees(rawAgentId) {
|
|
361
|
+
const agentId = normalizeAgentId(rawAgentId);
|
|
362
|
+
if (!agentId) {
|
|
363
|
+
throw new Error("Agent id cannot be empty.");
|
|
364
|
+
}
|
|
174
365
|
const paths = this.pathsProvider.getPaths();
|
|
366
|
+
const manifests = await this.agentManifestService.listManifests(paths);
|
|
367
|
+
assertAgentExists(manifests, agentId);
|
|
368
|
+
return manifests
|
|
369
|
+
.filter((manifest) => manifest.metadata.reportsTo === agentId)
|
|
370
|
+
.map((manifest) => manifest.agentId)
|
|
371
|
+
.sort((left, right) => left.localeCompare(right));
|
|
372
|
+
}
|
|
373
|
+
async listAllReportees(rawAgentId) {
|
|
175
374
|
const agentId = normalizeAgentId(rawAgentId);
|
|
176
375
|
if (!agentId) {
|
|
177
376
|
throw new Error("Agent id cannot be empty.");
|
|
178
377
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
378
|
+
const paths = this.pathsProvider.getPaths();
|
|
379
|
+
const manifests = await this.agentManifestService.listManifests(paths);
|
|
380
|
+
assertAgentExists(manifests, agentId);
|
|
381
|
+
return collectAllReportees(manifests, agentId);
|
|
382
|
+
}
|
|
383
|
+
async getAgentInfo(rawAgentId) {
|
|
384
|
+
const agentId = normalizeAgentId(rawAgentId);
|
|
385
|
+
if (!agentId) {
|
|
386
|
+
throw new Error("Agent id cannot be empty.");
|
|
183
387
|
}
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
388
|
+
const paths = this.pathsProvider.getPaths();
|
|
389
|
+
const [agents, manifests] = await Promise.all([
|
|
390
|
+
this.agentService.listAgents(paths),
|
|
391
|
+
this.agentManifestService.listManifests(paths),
|
|
392
|
+
]);
|
|
393
|
+
const descriptorsById = new Map(agents.map((agent) => [agent.id, agent]));
|
|
394
|
+
const agent = descriptorsById.get(agentId);
|
|
395
|
+
if (!agent) {
|
|
396
|
+
throw new Error(`Agent "${agentId}" does not exist.`);
|
|
187
397
|
}
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
398
|
+
const totalReportees = collectAllReportees(manifests, agentId).length;
|
|
399
|
+
const directReportees = manifests
|
|
400
|
+
.filter((manifest) => manifest.metadata.reportsTo === agentId)
|
|
401
|
+
.map((manifest) => {
|
|
402
|
+
const descriptor = descriptorsById.get(manifest.agentId);
|
|
403
|
+
const name = descriptor?.displayName?.trim() ||
|
|
404
|
+
manifest.metadata.name ||
|
|
405
|
+
manifest.agentId;
|
|
406
|
+
const role = descriptor?.role?.trim() || manifest.metadata.description || "Agent";
|
|
407
|
+
return {
|
|
408
|
+
id: manifest.agentId,
|
|
409
|
+
name,
|
|
410
|
+
role,
|
|
411
|
+
totalReportees: collectAllReportees(manifests, manifest.agentId)
|
|
412
|
+
.length,
|
|
413
|
+
};
|
|
414
|
+
})
|
|
415
|
+
.sort((left, right) => left.id.localeCompare(right.id));
|
|
191
416
|
return {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
stderr: externalAgentDeletion.stderr
|
|
198
|
-
}
|
|
417
|
+
id: agent.id,
|
|
418
|
+
name: agent.displayName,
|
|
419
|
+
role: agent.role,
|
|
420
|
+
totalReportees,
|
|
421
|
+
directReportees,
|
|
199
422
|
};
|
|
200
423
|
}
|
|
201
|
-
async listAgents() {
|
|
202
|
-
const paths = this.pathsProvider.getPaths();
|
|
203
|
-
return this.agentService.listAgents(paths);
|
|
204
|
-
}
|
|
205
424
|
listProviders() {
|
|
206
425
|
return this.providerService.listProviders();
|
|
207
426
|
}
|
|
@@ -226,9 +445,15 @@ export class OpenGoatService {
|
|
|
226
445
|
}
|
|
227
446
|
async setAgentProvider(agentId, providerId) {
|
|
228
447
|
const paths = this.pathsProvider.getPaths();
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
448
|
+
return this.providerService.setAgentProvider(paths, agentId, providerId);
|
|
449
|
+
}
|
|
450
|
+
async getOpenClawGatewayConfig() {
|
|
451
|
+
const paths = this.pathsProvider.getPaths();
|
|
452
|
+
return this.providerService.getOpenClawGatewayConfig(paths, process.env);
|
|
453
|
+
}
|
|
454
|
+
async setOpenClawGatewayConfig(config) {
|
|
455
|
+
const paths = this.pathsProvider.getPaths();
|
|
456
|
+
return this.providerService.setOpenClawGatewayConfig(paths, config);
|
|
232
457
|
}
|
|
233
458
|
async routeMessage(agentId, message) {
|
|
234
459
|
const paths = this.pathsProvider.getPaths();
|
|
@@ -238,46 +463,201 @@ export class OpenGoatService {
|
|
|
238
463
|
const paths = this.pathsProvider.getPaths();
|
|
239
464
|
return this.orchestrationService.runAgent(paths, agentId, options);
|
|
240
465
|
}
|
|
241
|
-
async
|
|
466
|
+
async createTask(actorId, options) {
|
|
242
467
|
const paths = this.pathsProvider.getPaths();
|
|
243
|
-
return this.
|
|
468
|
+
return this.boardService.createTask(paths, actorId, options);
|
|
244
469
|
}
|
|
245
|
-
async
|
|
470
|
+
async listTasks(options = {}) {
|
|
246
471
|
const paths = this.pathsProvider.getPaths();
|
|
247
|
-
return this.
|
|
472
|
+
return this.boardService.listTasks(paths, options);
|
|
248
473
|
}
|
|
249
|
-
async
|
|
474
|
+
async listLatestTasks(options = {}) {
|
|
475
|
+
const paths = this.pathsProvider.getPaths();
|
|
476
|
+
return this.boardService.listLatestTasks(paths, options);
|
|
477
|
+
}
|
|
478
|
+
async listLatestTasksPage(options = {}) {
|
|
479
|
+
const paths = this.pathsProvider.getPaths();
|
|
480
|
+
return this.boardService.listLatestTasksPage(paths, options);
|
|
481
|
+
}
|
|
482
|
+
async getTask(taskId) {
|
|
483
|
+
const paths = this.pathsProvider.getPaths();
|
|
484
|
+
return this.boardService.getTask(paths, taskId);
|
|
485
|
+
}
|
|
486
|
+
async deleteTasks(actorId, taskIds) {
|
|
250
487
|
const paths = this.pathsProvider.getPaths();
|
|
251
|
-
return this.
|
|
488
|
+
return this.boardService.deleteTasks(paths, actorId, taskIds);
|
|
252
489
|
}
|
|
253
|
-
async
|
|
490
|
+
async updateTaskStatus(actorId, taskId, status, reason) {
|
|
254
491
|
const paths = this.pathsProvider.getPaths();
|
|
255
|
-
return this.
|
|
492
|
+
return this.boardService.updateTaskStatus(paths, actorId, taskId, status, reason);
|
|
256
493
|
}
|
|
257
|
-
async
|
|
494
|
+
async addTaskBlocker(actorId, taskId, blocker) {
|
|
258
495
|
const paths = this.pathsProvider.getPaths();
|
|
259
|
-
return this.
|
|
496
|
+
return this.boardService.addTaskBlocker(paths, actorId, taskId, blocker);
|
|
260
497
|
}
|
|
261
|
-
async
|
|
498
|
+
async addTaskArtifact(actorId, taskId, content) {
|
|
262
499
|
const paths = this.pathsProvider.getPaths();
|
|
263
|
-
return this.
|
|
500
|
+
return this.boardService.addTaskArtifact(paths, actorId, taskId, content);
|
|
264
501
|
}
|
|
265
|
-
async
|
|
502
|
+
async addTaskWorklog(actorId, taskId, content) {
|
|
266
503
|
const paths = this.pathsProvider.getPaths();
|
|
267
|
-
|
|
504
|
+
return this.boardService.addTaskWorklog(paths, actorId, taskId, content);
|
|
268
505
|
}
|
|
269
|
-
async
|
|
506
|
+
async runTaskCronCycle(options = {}) {
|
|
270
507
|
const paths = this.pathsProvider.getPaths();
|
|
271
|
-
|
|
508
|
+
const ranAt = this.resolveNowIso();
|
|
509
|
+
const manifests = await this.agentManifestService.listManifests(paths);
|
|
510
|
+
const manifestsById = new Map(manifests.map((manifest) => [manifest.agentId, manifest]));
|
|
511
|
+
const inactiveMinutes = resolveInactiveMinutes(options.inactiveMinutes);
|
|
512
|
+
const notificationTarget = resolveInactiveAgentNotificationTarget(options.notificationTarget);
|
|
513
|
+
const notifyInactiveAgents = options.notifyInactiveAgents ?? true;
|
|
514
|
+
const inactiveCandidates = notifyInactiveAgents
|
|
515
|
+
? await this.collectInactiveAgents(paths, manifests, inactiveMinutes, notificationTarget)
|
|
516
|
+
: [];
|
|
517
|
+
const latestCeoProjectPath = notifyInactiveAgents
|
|
518
|
+
? await this.resolveLatestProjectPathForAgent(paths, DEFAULT_AGENT_ID)
|
|
519
|
+
: undefined;
|
|
520
|
+
const tasks = await this.boardService.listTasks(paths, { limit: 10_000 });
|
|
521
|
+
const dispatches = [];
|
|
522
|
+
let scannedTasks = tasks.length;
|
|
523
|
+
let todoTasks = 0;
|
|
524
|
+
let blockedTasks = 0;
|
|
525
|
+
for (const task of tasks) {
|
|
526
|
+
if (task.status !== "todo" && task.status !== "blocked") {
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
if (task.status === "todo") {
|
|
530
|
+
todoTasks += 1;
|
|
531
|
+
const targetAgentId = task.assignedTo;
|
|
532
|
+
const sessionRef = buildTaskSessionRef(targetAgentId, task.taskId);
|
|
533
|
+
const message = buildTodoTaskMessage({ task });
|
|
534
|
+
const result = await this.dispatchAutomationMessage(paths, targetAgentId, sessionRef, message);
|
|
535
|
+
dispatches.push({
|
|
536
|
+
kind: "todo",
|
|
537
|
+
targetAgentId,
|
|
538
|
+
sessionRef,
|
|
539
|
+
taskId: task.taskId,
|
|
540
|
+
ok: result.ok,
|
|
541
|
+
error: result.error,
|
|
542
|
+
});
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
blockedTasks += 1;
|
|
546
|
+
const assigneeManifest = manifestsById.get(task.assignedTo);
|
|
547
|
+
const managerAgentId = normalizeAgentId(assigneeManifest?.metadata.reportsTo ?? "") ||
|
|
548
|
+
DEFAULT_AGENT_ID;
|
|
549
|
+
const sessionRef = buildTaskSessionRef(managerAgentId, task.taskId);
|
|
550
|
+
const message = buildBlockedTaskMessage({ task });
|
|
551
|
+
const result = await this.dispatchAutomationMessage(paths, managerAgentId, sessionRef, message);
|
|
552
|
+
dispatches.push({
|
|
553
|
+
kind: "blocked",
|
|
554
|
+
targetAgentId: managerAgentId,
|
|
555
|
+
sessionRef,
|
|
556
|
+
taskId: task.taskId,
|
|
557
|
+
ok: result.ok,
|
|
558
|
+
error: result.error,
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
for (const candidate of inactiveCandidates) {
|
|
562
|
+
const sessionRef = buildInactiveSessionRef(candidate.managerAgentId, candidate.subjectAgentId);
|
|
563
|
+
const message = buildInactiveAgentMessage({
|
|
564
|
+
managerAgentId: candidate.managerAgentId,
|
|
565
|
+
subjectAgentId: candidate.subjectAgentId,
|
|
566
|
+
subjectName: candidate.subjectName,
|
|
567
|
+
role: candidate.role,
|
|
568
|
+
inactiveMinutes,
|
|
569
|
+
lastActionTimestamp: candidate.lastActionTimestamp,
|
|
570
|
+
});
|
|
571
|
+
const result = await this.dispatchAutomationMessage(paths, candidate.managerAgentId, sessionRef, message, {
|
|
572
|
+
cwd: latestCeoProjectPath,
|
|
573
|
+
});
|
|
574
|
+
dispatches.push({
|
|
575
|
+
kind: "inactive",
|
|
576
|
+
targetAgentId: candidate.managerAgentId,
|
|
577
|
+
sessionRef,
|
|
578
|
+
subjectAgentId: candidate.subjectAgentId,
|
|
579
|
+
ok: result.ok,
|
|
580
|
+
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
|
+
};
|
|
594
|
+
}
|
|
595
|
+
async listSkills(agentId = DEFAULT_AGENT_ID) {
|
|
596
|
+
const paths = this.pathsProvider.getPaths();
|
|
597
|
+
return this.skillService.listSkills(paths, agentId);
|
|
598
|
+
}
|
|
599
|
+
async listGlobalSkills() {
|
|
600
|
+
const paths = this.pathsProvider.getPaths();
|
|
601
|
+
return this.skillService.listGlobalSkills(paths);
|
|
272
602
|
}
|
|
273
|
-
async
|
|
603
|
+
async installSkill(request) {
|
|
274
604
|
const paths = this.pathsProvider.getPaths();
|
|
275
|
-
|
|
605
|
+
const result = await this.skillService.installSkill(paths, request);
|
|
606
|
+
if (result.scope === "agent" && result.agentId) {
|
|
607
|
+
await this.syncOpenClawRoleSkills(paths, result.agentId);
|
|
608
|
+
}
|
|
609
|
+
return result;
|
|
610
|
+
}
|
|
611
|
+
async runOpenClaw(args, options = {}) {
|
|
612
|
+
if (!this.commandRunner) {
|
|
613
|
+
throw new Error("OpenClaw passthrough is unavailable: command runner was not configured.");
|
|
614
|
+
}
|
|
615
|
+
const sanitized = args.map((value) => value.trim()).filter(Boolean);
|
|
616
|
+
if (sanitized.length === 0) {
|
|
617
|
+
throw new Error("OpenClaw passthrough requires at least one argument.");
|
|
618
|
+
}
|
|
619
|
+
const executionEnv = prepareOpenClawCommandEnv(options.env ?? process.env);
|
|
620
|
+
const command = executionEnv.OPENGOAT_OPENCLAW_CMD?.trim() ||
|
|
621
|
+
executionEnv.OPENCLAW_CMD?.trim() ||
|
|
622
|
+
process.env.OPENGOAT_OPENCLAW_CMD?.trim() ||
|
|
623
|
+
process.env.OPENCLAW_CMD?.trim() ||
|
|
624
|
+
"openclaw";
|
|
625
|
+
try {
|
|
626
|
+
return await this.commandRunner.run({
|
|
627
|
+
command,
|
|
628
|
+
args: sanitized,
|
|
629
|
+
cwd: options.cwd,
|
|
630
|
+
env: executionEnv,
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
catch (error) {
|
|
634
|
+
if (isSpawnPermissionOrMissing(error)) {
|
|
635
|
+
throw new ProviderCommandNotFoundError(OPENCLAW_PROVIDER_ID, command);
|
|
636
|
+
}
|
|
637
|
+
throw error;
|
|
638
|
+
}
|
|
276
639
|
}
|
|
277
640
|
async listSessions(agentId = DEFAULT_AGENT_ID, options = {}) {
|
|
278
641
|
const paths = this.pathsProvider.getPaths();
|
|
279
642
|
return this.sessionService.listSessions(paths, agentId, options);
|
|
280
643
|
}
|
|
644
|
+
async prepareSession(agentId = DEFAULT_AGENT_ID, options = {}) {
|
|
645
|
+
const paths = this.pathsProvider.getPaths();
|
|
646
|
+
const prepared = await this.sessionService.prepareRunSession(paths, agentId, {
|
|
647
|
+
sessionRef: options.sessionRef,
|
|
648
|
+
projectPath: options.projectPath,
|
|
649
|
+
forceNew: options.forceNew,
|
|
650
|
+
userMessage: "",
|
|
651
|
+
});
|
|
652
|
+
if (!prepared.enabled) {
|
|
653
|
+
throw new Error("Session preparation was disabled.");
|
|
654
|
+
}
|
|
655
|
+
return prepared.info;
|
|
656
|
+
}
|
|
657
|
+
async getAgentLastAction(agentId = DEFAULT_AGENT_ID) {
|
|
658
|
+
const paths = this.pathsProvider.getPaths();
|
|
659
|
+
return this.sessionService.getLastAgentAction(paths, agentId);
|
|
660
|
+
}
|
|
281
661
|
async getSessionHistory(agentId = DEFAULT_AGENT_ID, options = {}) {
|
|
282
662
|
const paths = this.pathsProvider.getPaths();
|
|
283
663
|
return this.sessionService.getSessionHistory(paths, agentId, options);
|
|
@@ -304,5 +684,537 @@ export class OpenGoatService {
|
|
|
304
684
|
getPaths() {
|
|
305
685
|
return this.pathsProvider.getPaths();
|
|
306
686
|
}
|
|
687
|
+
async dispatchAutomationMessage(paths, agentId, sessionRef, message, options = {}) {
|
|
688
|
+
try {
|
|
689
|
+
const result = await this.orchestrationService.runAgent(paths, agentId, {
|
|
690
|
+
message,
|
|
691
|
+
sessionRef,
|
|
692
|
+
cwd: options.cwd,
|
|
693
|
+
env: process.env,
|
|
694
|
+
});
|
|
695
|
+
if (result.code !== 0) {
|
|
696
|
+
return {
|
|
697
|
+
ok: false,
|
|
698
|
+
error: (result.stderr ||
|
|
699
|
+
result.stdout ||
|
|
700
|
+
`Runtime exited with code ${result.code}.`).trim(),
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
return { ok: true };
|
|
704
|
+
}
|
|
705
|
+
catch (error) {
|
|
706
|
+
return {
|
|
707
|
+
ok: false,
|
|
708
|
+
error: toErrorMessage(error),
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
resolveNowIso() {
|
|
713
|
+
return this.nowIso();
|
|
714
|
+
}
|
|
715
|
+
async initializeRuntimeDefaults() {
|
|
716
|
+
const initialization = await this.bootstrapService.initialize();
|
|
717
|
+
try {
|
|
718
|
+
await this.syncRuntimeDefaults();
|
|
719
|
+
}
|
|
720
|
+
catch {
|
|
721
|
+
// Startup remains functional even if OpenClaw CLI/runtime is unavailable.
|
|
722
|
+
}
|
|
723
|
+
return initialization;
|
|
724
|
+
}
|
|
725
|
+
resolveNowMs() {
|
|
726
|
+
return Date.now();
|
|
727
|
+
}
|
|
728
|
+
async syncOpenClawRoleSkills(paths, rawAgentId) {
|
|
729
|
+
const agentId = normalizeAgentId(rawAgentId);
|
|
730
|
+
if (!agentId) {
|
|
731
|
+
throw new Error("Agent id cannot be empty.");
|
|
732
|
+
}
|
|
733
|
+
const createdPaths = [];
|
|
734
|
+
const skippedPaths = [];
|
|
735
|
+
const removedPaths = [];
|
|
736
|
+
const managedSkillsSync = await this.removeOpenClawManagedRoleSkills(paths);
|
|
737
|
+
createdPaths.push(...managedSkillsSync.createdPaths);
|
|
738
|
+
skippedPaths.push(...managedSkillsSync.skippedPaths);
|
|
739
|
+
removedPaths.push(...managedSkillsSync.removedPaths);
|
|
740
|
+
const syncedAgents = new Set();
|
|
741
|
+
const syncAgent = async (targetAgentId) => {
|
|
742
|
+
if (syncedAgents.has(targetAgentId)) {
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
syncedAgents.add(targetAgentId);
|
|
746
|
+
const sync = await this.agentService.ensureAgentWorkspaceRoleSkills(paths, targetAgentId);
|
|
747
|
+
createdPaths.push(...sync.createdPaths);
|
|
748
|
+
skippedPaths.push(...sync.skippedPaths);
|
|
749
|
+
removedPaths.push(...sync.removedPaths);
|
|
750
|
+
};
|
|
751
|
+
await syncAgent(agentId);
|
|
752
|
+
const manifest = await this.agentManifestService.getManifest(paths, agentId);
|
|
753
|
+
const managerAgentId = normalizeAgentId(manifest.metadata.reportsTo ?? "");
|
|
754
|
+
if (managerAgentId) {
|
|
755
|
+
await syncAgent(managerAgentId);
|
|
756
|
+
}
|
|
757
|
+
return {
|
|
758
|
+
createdPaths,
|
|
759
|
+
skippedPaths,
|
|
760
|
+
removedPaths,
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
async removeOpenClawManagedRoleSkills(paths) {
|
|
764
|
+
if (!this.commandRunner) {
|
|
765
|
+
return {
|
|
766
|
+
createdPaths: [],
|
|
767
|
+
skippedPaths: ["openclaw-managed-skills:command-runner-unavailable"],
|
|
768
|
+
removedPaths: [],
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
const managedSkillsDir = await this.resolveOpenClawManagedSkillsDir(paths);
|
|
772
|
+
if (!managedSkillsDir) {
|
|
773
|
+
return {
|
|
774
|
+
createdPaths: [],
|
|
775
|
+
skippedPaths: ["openclaw-managed-skills:unresolved"],
|
|
776
|
+
removedPaths: [],
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
const skippedPaths = [];
|
|
780
|
+
const removedPaths = [];
|
|
781
|
+
for (const legacySkillId of [
|
|
782
|
+
"board-manager",
|
|
783
|
+
"board-individual",
|
|
784
|
+
"og-board-manager",
|
|
785
|
+
"og-board-individual",
|
|
786
|
+
"manager",
|
|
787
|
+
"board-user",
|
|
788
|
+
]) {
|
|
789
|
+
const legacyDir = this.pathPort.join(managedSkillsDir, legacySkillId);
|
|
790
|
+
if (!(await this.fileSystem.exists(legacyDir))) {
|
|
791
|
+
skippedPaths.push(legacyDir);
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
await this.fileSystem.removeDir(legacyDir);
|
|
795
|
+
removedPaths.push(legacyDir);
|
|
796
|
+
}
|
|
797
|
+
return {
|
|
798
|
+
createdPaths: [],
|
|
799
|
+
skippedPaths,
|
|
800
|
+
removedPaths,
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
async resolveOpenClawManagedSkillsDir(paths) {
|
|
804
|
+
if (this.openClawManagedSkillsDirCache !== undefined) {
|
|
805
|
+
return this.openClawManagedSkillsDirCache;
|
|
806
|
+
}
|
|
807
|
+
const providerConfig = await this.providerService.getProviderConfig(paths, OPENCLAW_PROVIDER_ID);
|
|
808
|
+
const env = {
|
|
809
|
+
...process.env,
|
|
810
|
+
...(providerConfig?.env ?? {}),
|
|
811
|
+
};
|
|
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
|
+
try {
|
|
820
|
+
parsed = JSON.parse(skillsList.stdout);
|
|
821
|
+
}
|
|
822
|
+
catch {
|
|
823
|
+
throw new Error("OpenClaw skills list returned non-JSON output; cannot resolve managed skills directory.");
|
|
824
|
+
}
|
|
825
|
+
const managedSkillsDir = extractManagedSkillsDir(parsed);
|
|
826
|
+
this.openClawManagedSkillsDirCache = managedSkillsDir;
|
|
827
|
+
return managedSkillsDir;
|
|
828
|
+
}
|
|
829
|
+
async listOpenClawAgents(paths) {
|
|
830
|
+
if (!this.commandRunner) {
|
|
831
|
+
return [];
|
|
832
|
+
}
|
|
833
|
+
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
|
+
try {
|
|
842
|
+
parsed = JSON.parse(listed.stdout);
|
|
843
|
+
}
|
|
844
|
+
catch {
|
|
845
|
+
throw new Error("OpenClaw agents list returned non-JSON output; cannot inspect agents.");
|
|
846
|
+
}
|
|
847
|
+
return extractOpenClawAgents(parsed);
|
|
848
|
+
}
|
|
849
|
+
async addWorkspaceAgentCandidates(paths, candidates, warnings) {
|
|
850
|
+
try {
|
|
851
|
+
const workspaceDirs = await this.fileSystem.listDirectories(paths.workspacesDir);
|
|
852
|
+
for (const workspaceDir of workspaceDirs) {
|
|
853
|
+
const workspaceAgentId = normalizeAgentId(workspaceDir);
|
|
854
|
+
if (workspaceAgentId &&
|
|
855
|
+
workspaceAgentId !== OPENCLAW_DEFAULT_AGENT_ID) {
|
|
856
|
+
candidates.add(workspaceAgentId);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
catch (error) {
|
|
861
|
+
warnings.push(`Workspace fallback discovery failed: ${toErrorMessage(error)}`);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
async ensureOpenClawAgentLocation(paths, params) {
|
|
865
|
+
if (!this.commandRunner) {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
const env = await this.resolveOpenClawEnv(paths);
|
|
869
|
+
const listed = await this.runOpenClaw(["agents", "list", "--json"], {
|
|
870
|
+
env,
|
|
871
|
+
});
|
|
872
|
+
if (listed.code !== 0) {
|
|
873
|
+
throw new Error(`OpenClaw agents list failed (exit ${listed.code}). ${listed.stderr.trim() || listed.stdout.trim() || ""}`.trim());
|
|
874
|
+
}
|
|
875
|
+
let parsed;
|
|
876
|
+
try {
|
|
877
|
+
parsed = JSON.parse(listed.stdout);
|
|
878
|
+
}
|
|
879
|
+
catch {
|
|
880
|
+
throw new Error("OpenClaw agents list returned non-JSON output; cannot verify agent location.");
|
|
881
|
+
}
|
|
882
|
+
const entry = extractOpenClawAgentEntry(parsed, params.agentId);
|
|
883
|
+
if (!entry) {
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
if (pathMatches(entry.workspace, params.workspaceDir) &&
|
|
887
|
+
pathMatches(entry.agentDir, params.internalConfigDir)) {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
const deleted = await this.providerService.deleteProviderAgent(paths, params.agentId, { providerId: OPENCLAW_PROVIDER_ID });
|
|
891
|
+
if (deleted.code !== 0) {
|
|
892
|
+
throw new Error(`OpenClaw agent location repair failed deleting "${params.agentId}" (exit ${deleted.code}). ${deleted.stderr.trim() || deleted.stdout.trim() || ""}`.trim());
|
|
893
|
+
}
|
|
894
|
+
const recreated = await this.providerService.createProviderAgent(paths, params.agentId, {
|
|
895
|
+
providerId: OPENCLAW_PROVIDER_ID,
|
|
896
|
+
displayName: params.displayName,
|
|
897
|
+
workspaceDir: params.workspaceDir,
|
|
898
|
+
internalConfigDir: params.internalConfigDir,
|
|
899
|
+
});
|
|
900
|
+
if (recreated.code !== 0 &&
|
|
901
|
+
!containsAlreadyExistsMessage(recreated.stdout, recreated.stderr)) {
|
|
902
|
+
throw new Error(`OpenClaw agent location repair failed creating "${params.agentId}" (exit ${recreated.code}). ${recreated.stderr.trim() || recreated.stdout.trim() || ""}`.trim());
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
async resolveOpenClawEnv(paths) {
|
|
906
|
+
const providerConfig = await this.providerService.getProviderConfig(paths, OPENCLAW_PROVIDER_ID);
|
|
907
|
+
return {
|
|
908
|
+
...(providerConfig?.env ?? {}),
|
|
909
|
+
...process.env,
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
async collectInactiveAgents(paths, manifests, inactiveMinutes, notificationTarget) {
|
|
913
|
+
const nowMs = this.resolveNowMs();
|
|
914
|
+
const inactiveCutoffMs = nowMs - inactiveMinutes * 60_000;
|
|
915
|
+
const inactive = [];
|
|
916
|
+
for (const manifest of manifests) {
|
|
917
|
+
const managerAgentId = normalizeAgentId(manifest.metadata.reportsTo ?? "");
|
|
918
|
+
if (!managerAgentId) {
|
|
919
|
+
continue;
|
|
920
|
+
}
|
|
921
|
+
if (notificationTarget === "ceo-only" &&
|
|
922
|
+
managerAgentId !== DEFAULT_AGENT_ID) {
|
|
923
|
+
continue;
|
|
924
|
+
}
|
|
925
|
+
const lastAction = await this.sessionService.getLastAgentAction(paths, manifest.agentId);
|
|
926
|
+
if (lastAction && lastAction.timestamp >= inactiveCutoffMs) {
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
inactive.push({
|
|
930
|
+
managerAgentId,
|
|
931
|
+
subjectAgentId: manifest.agentId,
|
|
932
|
+
subjectName: manifest.metadata.name,
|
|
933
|
+
role: manifest.metadata.description,
|
|
934
|
+
lastActionTimestamp: lastAction?.timestamp,
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
return inactive;
|
|
938
|
+
}
|
|
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
|
+
}
|
|
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;
|
|
960
|
+
}
|
|
961
|
+
return String(error);
|
|
962
|
+
}
|
|
963
|
+
function resolveInactiveMinutes(value) {
|
|
964
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
965
|
+
return 30;
|
|
966
|
+
}
|
|
967
|
+
return Math.floor(value);
|
|
968
|
+
}
|
|
969
|
+
function resolveInactiveAgentNotificationTarget(value) {
|
|
970
|
+
return value === "ceo-only" ? "ceo-only" : "all-managers";
|
|
971
|
+
}
|
|
972
|
+
function extractManagedSkillsDir(payload) {
|
|
973
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
974
|
+
return null;
|
|
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) {
|
|
1025
|
+
return false;
|
|
1026
|
+
}
|
|
1027
|
+
return leftNormalized === rightNormalized;
|
|
1028
|
+
}
|
|
1029
|
+
function pathIsWithin(containerPath, candidatePath) {
|
|
1030
|
+
const normalizedContainer = normalizePathForCompare(containerPath);
|
|
1031
|
+
const normalizedCandidate = normalizePathForCompare(candidatePath);
|
|
1032
|
+
if (!normalizedContainer || !normalizedCandidate) {
|
|
1033
|
+
return false;
|
|
1034
|
+
}
|
|
1035
|
+
const relative = path.relative(normalizedContainer, normalizedCandidate);
|
|
1036
|
+
if (!relative) {
|
|
1037
|
+
return true;
|
|
1038
|
+
}
|
|
1039
|
+
return !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
1040
|
+
}
|
|
1041
|
+
function normalizePathForCompare(value) {
|
|
1042
|
+
const trimmed = value.trim();
|
|
1043
|
+
if (!trimmed) {
|
|
1044
|
+
return "";
|
|
1045
|
+
}
|
|
1046
|
+
const resolved = path.resolve(trimmed);
|
|
1047
|
+
if (process.platform === "win32") {
|
|
1048
|
+
return resolved.toLowerCase();
|
|
1049
|
+
}
|
|
1050
|
+
return resolved;
|
|
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");
|
|
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) {
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1148
|
+
const reportees = byManager.get(reportsTo) ?? [];
|
|
1149
|
+
reportees.push(manifest.agentId);
|
|
1150
|
+
byManager.set(reportsTo, reportees);
|
|
1151
|
+
}
|
|
1152
|
+
const visited = new Set();
|
|
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;
|
|
1158
|
+
}
|
|
1159
|
+
visited.add(current);
|
|
1160
|
+
queue.push(...(byManager.get(current) ?? []));
|
|
1161
|
+
}
|
|
1162
|
+
return [...visited].sort((left, right) => left.localeCompare(right));
|
|
1163
|
+
}
|
|
1164
|
+
function prepareOpenClawCommandEnv(env) {
|
|
1165
|
+
const mergedPath = dedupePathEntries([
|
|
1166
|
+
...resolvePreferredOpenClawCommandPaths(env),
|
|
1167
|
+
...(env.PATH?.split(path.delimiter) ?? []),
|
|
1168
|
+
]);
|
|
1169
|
+
return {
|
|
1170
|
+
...env,
|
|
1171
|
+
PATH: mergedPath.join(path.delimiter),
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
function resolvePreferredOpenClawCommandPaths(env) {
|
|
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) {
|
|
1201
|
+
const seen = new Set();
|
|
1202
|
+
const deduped = [];
|
|
1203
|
+
for (const rawEntry of entries) {
|
|
1204
|
+
const entry = rawEntry.trim();
|
|
1205
|
+
if (!entry || seen.has(entry)) {
|
|
1206
|
+
continue;
|
|
1207
|
+
}
|
|
1208
|
+
seen.add(entry);
|
|
1209
|
+
deduped.push(entry);
|
|
1210
|
+
}
|
|
1211
|
+
return deduped;
|
|
1212
|
+
}
|
|
1213
|
+
function isSpawnPermissionOrMissing(error) {
|
|
1214
|
+
return (typeof error === "object" &&
|
|
1215
|
+
error !== null &&
|
|
1216
|
+
"code" in error &&
|
|
1217
|
+
((error.code ?? "") === "ENOENT" ||
|
|
1218
|
+
(error.code ?? "") === "EACCES"));
|
|
307
1219
|
}
|
|
308
1220
|
//# sourceMappingURL=opengoat.service.js.map
|