@opengoat/core 2026.2.9 → 2026.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/README.md +3 -3
  2. package/dist/core/acp/application/acp-agent.js +4 -8
  3. package/dist/core/acp/application/acp-agent.js.map +1 -1
  4. package/dist/core/agents/application/agent-manifest.service.d.ts +1 -4
  5. package/dist/core/agents/application/agent-manifest.service.js +39 -52
  6. package/dist/core/agents/application/agent-manifest.service.js.map +1 -1
  7. package/dist/core/agents/application/agent.service.d.ts +42 -4
  8. package/dist/core/agents/application/agent.service.js +439 -55
  9. package/dist/core/agents/application/agent.service.js.map +1 -1
  10. package/dist/core/agents/domain/agent-manifest.d.ts +10 -4
  11. package/dist/core/agents/domain/agent-manifest.js +118 -25
  12. package/dist/core/agents/domain/agent-manifest.js.map +1 -1
  13. package/dist/core/agents/index.d.ts +1 -3
  14. package/dist/core/agents/index.js +1 -3
  15. package/dist/core/agents/index.js.map +1 -1
  16. package/dist/core/boards/application/board.service.d.ts +64 -0
  17. package/dist/core/boards/application/board.service.js +590 -0
  18. package/dist/core/boards/application/board.service.js.map +1 -0
  19. package/dist/core/boards/domain/board.d.ts +30 -0
  20. package/dist/core/boards/domain/board.js +2 -0
  21. package/dist/core/boards/domain/board.js.map +1 -0
  22. package/dist/core/boards/index.d.ts +2 -0
  23. package/dist/core/boards/index.js +2 -0
  24. package/dist/core/boards/index.js.map +1 -0
  25. package/dist/core/bootstrap/application/bootstrap.service.d.ts +1 -2
  26. package/dist/core/bootstrap/application/bootstrap.service.js +14 -19
  27. package/dist/core/bootstrap/application/bootstrap.service.js.map +1 -1
  28. package/dist/core/domain/agent-id.d.ts +1 -1
  29. package/dist/core/domain/agent-id.js +1 -1
  30. package/dist/core/domain/agent-id.js.map +1 -1
  31. package/dist/core/domain/agent.d.ts +31 -8
  32. package/dist/core/opengoat/application/opengoat.service.d.ts +106 -27
  33. package/dist/core/opengoat/application/opengoat.service.js +1006 -136
  34. package/dist/core/opengoat/application/opengoat.service.js.map +1 -1
  35. package/dist/core/opengoat/index.d.ts +1 -0
  36. package/dist/core/orchestration/application/orchestration.service.d.ts +8 -21
  37. package/dist/core/orchestration/application/orchestration.service.js +59 -871
  38. package/dist/core/orchestration/application/orchestration.service.js.map +1 -1
  39. package/dist/core/orchestration/application/routing.service.js +10 -8
  40. package/dist/core/orchestration/application/routing.service.js.map +1 -1
  41. package/dist/core/orchestration/domain/routing.d.ts +0 -17
  42. package/dist/core/orchestration/domain/run-events.d.ts +1 -1
  43. package/dist/core/orchestration/index.d.ts +0 -2
  44. package/dist/core/orchestration/index.js +0 -1
  45. package/dist/core/orchestration/index.js.map +1 -1
  46. package/dist/core/providers/application/provider.service.d.ts +15 -32
  47. package/dist/core/providers/application/provider.service.js +115 -201
  48. package/dist/core/providers/application/provider.service.js.map +1 -1
  49. package/dist/core/providers/cli-provider.d.ts +1 -0
  50. package/dist/core/providers/cli-provider.js +66 -1
  51. package/dist/core/providers/cli-provider.js.map +1 -1
  52. package/dist/core/providers/image-input.d.ts +16 -0
  53. package/dist/core/providers/image-input.js +158 -0
  54. package/dist/core/providers/image-input.js.map +1 -0
  55. package/dist/core/providers/index.d.ts +1 -7
  56. package/dist/core/providers/index.js +0 -13
  57. package/dist/core/providers/index.js.map +1 -1
  58. package/dist/core/providers/loader.js +1 -95
  59. package/dist/core/providers/loader.js.map +1 -1
  60. package/dist/core/providers/providers/openclaw/provider.js +213 -32
  61. package/dist/core/providers/providers/openclaw/provider.js.map +1 -1
  62. package/dist/core/providers/types.d.ts +8 -3
  63. package/dist/core/scenarios/application/scenario-runner.service.d.ts +0 -1
  64. package/dist/core/scenarios/application/scenario-runner.service.js +30 -87
  65. package/dist/core/scenarios/application/scenario-runner.service.js.map +1 -1
  66. package/dist/core/scenarios/domain/scenario.d.ts +0 -15
  67. package/dist/core/sessions/application/session.service.d.ts +10 -4
  68. package/dist/core/sessions/application/session.service.js +87 -89
  69. package/dist/core/sessions/application/session.service.js.map +1 -1
  70. package/dist/core/sessions/domain/session.d.ts +3 -3
  71. package/dist/core/sessions/domain/transcript.d.ts +1 -1
  72. package/dist/core/sessions/index.d.ts +1 -1
  73. package/dist/core/sessions/index.js.map +1 -1
  74. package/dist/core/skills/application/skill.service.d.ts +2 -3
  75. package/dist/core/skills/application/skill.service.js +153 -56
  76. package/dist/core/skills/application/skill.service.js.map +1 -1
  77. package/dist/core/skills/domain/skill.d.ts +2 -2
  78. package/dist/core/skills/domain/skill.js +4 -2
  79. package/dist/core/skills/domain/skill.js.map +1 -1
  80. package/dist/core/templates/assets/ceo/AGENTS.md +20 -0
  81. package/dist/core/templates/assets/ceo/ROLE.md +13 -0
  82. package/dist/core/templates/assets/ceo/SOUL.md +15 -0
  83. package/dist/core/templates/assets/skills/og-board-individual/SKILL.md +148 -0
  84. package/dist/core/templates/assets/skills/og-board-manager/SKILL.md +140 -0
  85. package/dist/core/templates/default-templates.d.ts +12 -14
  86. package/dist/core/templates/default-templates.js +81 -213
  87. package/dist/core/templates/default-templates.js.map +1 -1
  88. package/dist/index.d.ts +1 -4
  89. package/dist/index.js +1 -4
  90. package/dist/index.js.map +1 -1
  91. package/package.json +16 -13
  92. package/dist/core/agents/application/workspace-context.service.d.ts +0 -28
  93. package/dist/core/agents/application/workspace-context.service.js +0 -157
  94. package/dist/core/agents/application/workspace-context.service.js.map +0 -1
  95. package/dist/core/agents/domain/workspace-context.d.ts +0 -13
  96. package/dist/core/agents/domain/workspace-context.js +0 -14
  97. package/dist/core/agents/domain/workspace-context.js.map +0 -1
  98. package/dist/core/gateway/domain/protocol.d.ts +0 -113
  99. package/dist/core/gateway/domain/protocol.js +0 -394
  100. package/dist/core/gateway/domain/protocol.js.map +0 -1
  101. package/dist/core/gateway/index.d.ts +0 -2
  102. package/dist/core/gateway/index.js +0 -2
  103. package/dist/core/gateway/index.js.map +0 -1
  104. package/dist/core/llm/application/vercel-ai-text-runtime.d.ts +0 -26
  105. package/dist/core/llm/application/vercel-ai-text-runtime.js +0 -223
  106. package/dist/core/llm/application/vercel-ai-text-runtime.js.map +0 -1
  107. package/dist/core/llm/domain/text-runtime.d.ts +0 -22
  108. package/dist/core/llm/domain/text-runtime.js +0 -2
  109. package/dist/core/llm/domain/text-runtime.js.map +0 -1
  110. package/dist/core/llm/index.d.ts +0 -2
  111. package/dist/core/llm/index.js +0 -2
  112. package/dist/core/llm/index.js.map +0 -1
  113. package/dist/core/orchestration/application/orchestration-planner.service.d.ts +0 -28
  114. package/dist/core/orchestration/application/orchestration-planner.service.js +0 -279
  115. package/dist/core/orchestration/application/orchestration-planner.service.js.map +0 -1
  116. package/dist/core/orchestration/domain/loop.d.ts +0 -119
  117. package/dist/core/orchestration/domain/loop.js +0 -2
  118. package/dist/core/orchestration/domain/loop.js.map +0 -1
  119. package/dist/core/plugins/application/plugin.service.d.ts +0 -32
  120. package/dist/core/plugins/application/plugin.service.js +0 -236
  121. package/dist/core/plugins/application/plugin.service.js.map +0 -1
  122. package/dist/core/plugins/domain/openclaw-compat.d.ts +0 -60
  123. package/dist/core/plugins/domain/openclaw-compat.js +0 -9
  124. package/dist/core/plugins/domain/openclaw-compat.js.map +0 -1
  125. package/dist/core/plugins/index.d.ts +0 -3
  126. package/dist/core/plugins/index.js +0 -3
  127. package/dist/core/plugins/index.js.map +0 -1
  128. package/dist/core/providers/onboarding.d.ts +0 -13
  129. package/dist/core/providers/onboarding.js +0 -149
  130. package/dist/core/providers/onboarding.js.map +0 -1
  131. package/dist/core/providers/providers/claude/index.d.ts +0 -4
  132. package/dist/core/providers/providers/claude/index.js +0 -19
  133. package/dist/core/providers/providers/claude/index.js.map +0 -1
  134. package/dist/core/providers/providers/claude/provider.d.ts +0 -8
  135. package/dist/core/providers/providers/claude/provider.js +0 -106
  136. package/dist/core/providers/providers/claude/provider.js.map +0 -1
  137. package/dist/core/providers/providers/codex/index.d.ts +0 -4
  138. package/dist/core/providers/providers/codex/index.js +0 -19
  139. package/dist/core/providers/providers/codex/index.js.map +0 -1
  140. package/dist/core/providers/providers/codex/provider.d.ts +0 -7
  141. package/dist/core/providers/providers/codex/provider.js +0 -31
  142. package/dist/core/providers/providers/codex/provider.js.map +0 -1
  143. package/dist/core/providers/providers/cursor/index.d.ts +0 -4
  144. package/dist/core/providers/providers/cursor/index.js +0 -19
  145. package/dist/core/providers/providers/cursor/index.js.map +0 -1
  146. package/dist/core/providers/providers/cursor/provider.d.ts +0 -11
  147. package/dist/core/providers/providers/cursor/provider.js +0 -90
  148. package/dist/core/providers/providers/cursor/provider.js.map +0 -1
  149. package/dist/core/providers/providers/extended-http/catalog.d.ts +0 -40
  150. package/dist/core/providers/providers/extended-http/catalog.js +0 -728
  151. package/dist/core/providers/providers/extended-http/catalog.js.map +0 -1
  152. package/dist/core/providers/providers/extended-http/index.d.ts +0 -5
  153. package/dist/core/providers/providers/extended-http/index.js +0 -13
  154. package/dist/core/providers/providers/extended-http/index.js.map +0 -1
  155. package/dist/core/providers/providers/extended-http/provider.d.ts +0 -31
  156. package/dist/core/providers/providers/extended-http/provider.js +0 -580
  157. package/dist/core/providers/providers/extended-http/provider.js.map +0 -1
  158. package/dist/core/providers/providers/gemini/index.d.ts +0 -4
  159. package/dist/core/providers/providers/gemini/index.js +0 -37
  160. package/dist/core/providers/providers/gemini/index.js.map +0 -1
  161. package/dist/core/providers/providers/gemini/provider.d.ts +0 -7
  162. package/dist/core/providers/providers/gemini/provider.js +0 -59
  163. package/dist/core/providers/providers/gemini/provider.js.map +0 -1
  164. package/dist/core/providers/providers/grok/index.d.ts +0 -4
  165. package/dist/core/providers/providers/grok/index.js +0 -41
  166. package/dist/core/providers/providers/grok/index.js.map +0 -1
  167. package/dist/core/providers/providers/grok/provider.d.ts +0 -13
  168. package/dist/core/providers/providers/grok/provider.js +0 -95
  169. package/dist/core/providers/providers/grok/provider.js.map +0 -1
  170. package/dist/core/providers/providers/openai/index.d.ts +0 -4
  171. package/dist/core/providers/providers/openai/index.js +0 -45
  172. package/dist/core/providers/providers/openai/index.js.map +0 -1
  173. package/dist/core/providers/providers/openai/provider.d.ts +0 -14
  174. package/dist/core/providers/providers/openai/provider.js +0 -203
  175. package/dist/core/providers/providers/openai/provider.js.map +0 -1
  176. package/dist/core/providers/providers/opencode/index.d.ts +0 -4
  177. package/dist/core/providers/providers/opencode/index.js +0 -23
  178. package/dist/core/providers/providers/opencode/index.js.map +0 -1
  179. package/dist/core/providers/providers/opencode/provider.d.ts +0 -11
  180. package/dist/core/providers/providers/opencode/provider.js +0 -215
  181. package/dist/core/providers/providers/opencode/provider.js.map +0 -1
  182. package/dist/core/providers/providers/openrouter/index.d.ts +0 -4
  183. package/dist/core/providers/providers/openrouter/index.js +0 -37
  184. package/dist/core/providers/providers/openrouter/index.js.map +0 -1
  185. package/dist/core/providers/providers/openrouter/provider.d.ts +0 -13
  186. package/dist/core/providers/providers/openrouter/provider.js +0 -80
  187. package/dist/core/providers/providers/openrouter/provider.js.map +0 -1
  188. package/dist/platform/node/opengoat-gateway-client.d.ts +0 -19
  189. package/dist/platform/node/opengoat-gateway-client.js +0 -194
  190. package/dist/platform/node/opengoat-gateway-client.js.map +0 -1
  191. package/dist/platform/node/opengoat-gateway-server.d.ts +0 -53
  192. package/dist/platform/node/opengoat-gateway-server.js +0 -906
  193. package/dist/platform/node/opengoat-gateway-server.js.map +0 -1
@@ -1,207 +1,424 @@
1
- import { DEFAULT_AGENT_ID, normalizeAgentId } from "../../domain/agent-id.js";
2
- import { createNoopLogger } from "../../logging/index.js";
3
- import { createDefaultProviderRegistry } from "../../providers/index.js";
1
+ import path from "node:path";
2
+ import { homedir } from "node:os";
4
3
  import { AgentManifestService } from "../../agents/application/agent-manifest.service.js";
5
4
  import { AgentService } from "../../agents/application/agent.service.js";
6
- import { WorkspaceContextService } from "../../agents/application/workspace-context.service.js";
5
+ import { BoardService, } from "../../boards/index.js";
7
6
  import { BootstrapService } from "../../bootstrap/application/bootstrap.service.js";
8
- import { OrchestrationService } from "../../orchestration/index.js";
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 { SkillService } from "../../skills/index.js";
11
- import { PluginService } from "../../plugins/index.js";
12
- import { SessionService } from "../../sessions/index.js";
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({ scope: "opengoat-service" });
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,
41
54
  pathsProvider: deps.pathsProvider,
42
55
  agentService: this.agentService,
43
- nowIso
44
- });
45
- const workspaceContextService = new WorkspaceContextService({
46
- fileSystem: deps.fileSystem,
47
- pathPort: deps.pathPort
56
+ nowIso,
48
57
  });
49
- this.pluginService =
50
- deps.pluginService ??
51
- new PluginService({
52
- fileSystem: deps.fileSystem,
53
- pathPort: deps.pathPort,
54
- commandRunner: deps.commandRunner
55
- });
56
58
  this.skillService = new SkillService({
57
59
  fileSystem: deps.fileSystem,
58
60
  pathPort: deps.pathPort,
59
- pluginSkillDirsProvider: (paths) => this.pluginService.resolvePluginSkillDirectories(paths)
60
61
  });
61
62
  this.providerService = new ProviderService({
62
63
  fileSystem: deps.fileSystem,
63
64
  pathPort: deps.pathPort,
64
65
  providerRegistry: providerRegistryFactory,
65
- workspaceContextService,
66
- skillService: this.skillService,
67
66
  nowIso,
68
- logger: rootLogger.child({ scope: "provider" })
67
+ logger: rootLogger.child({ scope: "provider" }),
69
68
  });
70
69
  this.sessionService = new SessionService({
71
70
  fileSystem: deps.fileSystem,
72
71
  pathPort: deps.pathPort,
73
72
  commandRunner: deps.commandRunner,
74
73
  nowIso,
75
- nowMs: () => Date.now()
74
+ nowMs: () => Date.now(),
76
75
  });
76
+ this.commandRunner = deps.commandRunner;
77
77
  this.orchestrationService = new OrchestrationService({
78
78
  providerService: this.providerService,
79
- skillService: this.skillService,
80
79
  agentManifestService: this.agentManifestService,
81
80
  sessionService: this.sessionService,
82
- commandRunner: deps.commandRunner,
83
81
  fileSystem: deps.fileSystem,
84
82
  pathPort: deps.pathPort,
85
83
  nowIso,
86
- logger: rootLogger.child({ scope: "orchestration" })
84
+ logger: rootLogger.child({ scope: "orchestration" }),
85
+ });
86
+ this.boardService = new BoardService({
87
+ fileSystem: deps.fileSystem,
88
+ pathPort: deps.pathPort,
89
+ nowIso,
90
+ agentManifestService: this.agentManifestService,
87
91
  });
88
92
  }
89
93
  initialize() {
90
- return this.bootstrapService.initialize();
94
+ return this.initializeRuntimeDefaults();
91
95
  }
92
- async createAgent(rawName, options = {}) {
93
- const requestedProviderId = options.providerId?.trim();
94
- if (requestedProviderId && options.createExternalAgent === true) {
95
- const supportsRequestedProvider = await this.providerSupportsExternalAgentCreation(requestedProviderId);
96
- if (!supportsRequestedProvider) {
97
- throw new Error(`Provider "${requestedProviderId}" does not support external agent creation.`);
96
+ async hardReset() {
97
+ const paths = this.pathsProvider.getPaths();
98
+ const warnings = [];
99
+ const deletedOpenClawAgents = [];
100
+ const failedOpenClawAgents = [];
101
+ const removedOpenClawManagedSkillDirs = [];
102
+ const candidateOpenClawAgentIds = new Set();
103
+ const localAgents = await this.agentService.listAgents(paths);
104
+ for (const agent of localAgents) {
105
+ if (agent.id === OPENCLAW_DEFAULT_AGENT_ID) {
106
+ continue;
107
+ }
108
+ candidateOpenClawAgentIds.add(agent.id);
109
+ }
110
+ try {
111
+ const openClawAgents = await this.listOpenClawAgents(paths);
112
+ for (const entry of openClawAgents) {
113
+ if (entry.id !== OPENCLAW_DEFAULT_AGENT_ID &&
114
+ (pathIsWithin(paths.homeDir, entry.workspace) ||
115
+ pathIsWithin(paths.homeDir, entry.agentDir))) {
116
+ candidateOpenClawAgentIds.add(entry.id);
117
+ }
118
+ }
119
+ }
120
+ catch (error) {
121
+ warnings.push(`OpenClaw agent discovery failed: ${toErrorMessage(error)}`);
122
+ }
123
+ for (const agentId of [...candidateOpenClawAgentIds].sort((left, right) => left.localeCompare(right))) {
124
+ if (agentId === OPENCLAW_DEFAULT_AGENT_ID) {
125
+ continue;
126
+ }
127
+ try {
128
+ const deleted = await this.providerService.deleteProviderAgent(paths, agentId, { providerId: OPENCLAW_PROVIDER_ID });
129
+ if (deleted.code === 0 ||
130
+ containsAgentNotFoundMessage(deleted.stdout, deleted.stderr)) {
131
+ deletedOpenClawAgents.push(agentId);
132
+ continue;
133
+ }
134
+ const failureReason = deleted.stderr.trim() || deleted.stdout.trim();
135
+ if (!failureReason) {
136
+ warnings.push(`OpenClaw delete for "${agentId}" failed with code ${deleted.code}.`);
137
+ continue;
138
+ }
139
+ failedOpenClawAgents.push({
140
+ agentId,
141
+ reason: failureReason,
142
+ });
143
+ }
144
+ catch (error) {
145
+ if (error instanceof ProviderCommandNotFoundError) {
146
+ deletedOpenClawAgents.push(agentId);
147
+ continue;
148
+ }
149
+ failedOpenClawAgents.push({
150
+ agentId,
151
+ reason: toErrorMessage(error),
152
+ });
153
+ }
154
+ }
155
+ try {
156
+ const managedSkillsCleanup = await this.removeOpenClawManagedRoleSkills(paths);
157
+ removedOpenClawManagedSkillDirs.push(...managedSkillsCleanup.removedPaths);
158
+ }
159
+ catch (error) {
160
+ warnings.push(`OpenClaw managed skills cleanup failed: ${toErrorMessage(error)}`);
161
+ }
162
+ await this.fileSystem.removeDir(paths.homeDir);
163
+ this.openClawManagedSkillsDirCache = undefined;
164
+ const homeRemoved = !(await this.fileSystem.exists(paths.homeDir));
165
+ return {
166
+ homeDir: paths.homeDir,
167
+ homeRemoved,
168
+ deletedOpenClawAgents,
169
+ failedOpenClawAgents,
170
+ removedOpenClawManagedSkillDirs,
171
+ warnings,
172
+ };
173
+ }
174
+ async syncRuntimeDefaults() {
175
+ const paths = this.pathsProvider.getPaths();
176
+ const warnings = [];
177
+ let ceoSynced = false;
178
+ let ceoSyncCode;
179
+ let ceoDescriptor = (await this.agentService.listAgents(paths)).find((agent) => agent.id === DEFAULT_AGENT_ID);
180
+ if (!ceoDescriptor) {
181
+ const created = await this.agentService.ensureAgent(paths, {
182
+ id: DEFAULT_AGENT_ID,
183
+ displayName: "CEO",
184
+ }, {
185
+ type: "manager",
186
+ reportsTo: null,
187
+ role: "CEO",
188
+ });
189
+ ceoDescriptor = created.agent;
190
+ }
191
+ try {
192
+ await this.syncOpenClawRoleSkills(paths, DEFAULT_AGENT_ID);
193
+ }
194
+ catch (error) {
195
+ warnings.push(`OpenClaw role skill assignment sync for "ceo" failed: ${toErrorMessage(error)}`);
196
+ }
197
+ try {
198
+ const ceoSync = await this.providerService.createProviderAgent(paths, DEFAULT_AGENT_ID, {
199
+ providerId: OPENCLAW_PROVIDER_ID,
200
+ displayName: ceoDescriptor.displayName,
201
+ workspaceDir: ceoDescriptor.workspaceDir,
202
+ internalConfigDir: ceoDescriptor.internalConfigDir,
203
+ });
204
+ ceoSyncCode = ceoSync.code;
205
+ ceoSynced =
206
+ ceoSync.code === 0 ||
207
+ containsAlreadyExistsMessage(ceoSync.stdout, ceoSync.stderr);
208
+ if (!ceoSynced) {
209
+ warnings.push(`OpenClaw sync for "ceo" failed (code ${ceoSync.code}). ${(ceoSync.stderr || ceoSync.stdout).trim()}`);
98
210
  }
99
211
  }
212
+ catch (error) {
213
+ warnings.push(`OpenClaw sync for "ceo" failed: ${toErrorMessage(error)}`);
214
+ }
215
+ if (ceoSynced) {
216
+ try {
217
+ await this.ensureOpenClawAgentLocation(paths, {
218
+ agentId: DEFAULT_AGENT_ID,
219
+ displayName: ceoDescriptor.displayName,
220
+ workspaceDir: ceoDescriptor.workspaceDir,
221
+ internalConfigDir: ceoDescriptor.internalConfigDir,
222
+ });
223
+ }
224
+ catch (error) {
225
+ warnings.push(`OpenClaw ceo location sync failed: ${toErrorMessage(error)}`);
226
+ }
227
+ }
228
+ try {
229
+ await this.agentService.ensureCeoWorkspaceBootstrap(paths);
230
+ }
231
+ catch (error) {
232
+ warnings.push(`OpenGoat workspace bootstrap for "ceo" failed: ${toErrorMessage(error)}`);
233
+ }
234
+ return {
235
+ ceoSyncCode,
236
+ ceoSynced,
237
+ warnings,
238
+ };
239
+ }
240
+ async createAgent(rawName, options = {}) {
100
241
  const identity = this.agentService.normalizeAgentName(rawName);
101
242
  const paths = this.pathsProvider.getPaths();
102
- const created = await this.agentService.ensureAgent(paths, identity);
103
- let binding;
104
- if (requestedProviderId) {
105
- binding = await this.providerService.setAgentProvider(paths, created.agent.id, requestedProviderId);
106
- await this.agentManifestService.syncManifestProvider(paths, created.agent.id, binding.providerId);
107
- }
108
- const shouldCreateExternalAgentByDefault = Boolean(requestedProviderId)
109
- ? options.createExternalAgent !== false
110
- : false;
111
- const shouldCreateExternalAgentExplicitlyWithoutProvider = !requestedProviderId && options.createExternalAgent === true;
112
- if (!shouldCreateExternalAgentByDefault && !shouldCreateExternalAgentExplicitlyWithoutProvider) {
113
- return created;
114
- }
115
- const resolvedBinding = binding ?? (await this.providerService.getAgentProvider(paths, created.agent.id));
116
- const supportsExternalAgentCreation = await this.providerSupportsExternalAgentCreation(resolvedBinding.providerId);
117
- if (options.createExternalAgent === true && !supportsExternalAgentCreation) {
118
- throw new Error(`Provider "${resolvedBinding.providerId}" does not support external agent creation.`);
119
- }
120
- if (!supportsExternalAgentCreation) {
121
- return created;
122
- }
123
- const externalAgentCreation = await this.providerService.createProviderAgent(paths, created.agent.id, {
124
- providerId: resolvedBinding.providerId,
243
+ const created = await this.agentService.ensureAgent(paths, identity, {
244
+ type: options.type,
245
+ reportsTo: options.reportsTo,
246
+ skills: options.skills,
247
+ role: options.role,
248
+ });
249
+ try {
250
+ const workspaceSkillSync = await this.syncOpenClawRoleSkills(paths, created.agent.id);
251
+ created.createdPaths.push(...workspaceSkillSync.createdPaths);
252
+ created.skippedPaths.push(...workspaceSkillSync.skippedPaths);
253
+ created.skippedPaths.push(...workspaceSkillSync.removedPaths);
254
+ }
255
+ catch (error) {
256
+ if (!created.alreadyExisted) {
257
+ await this.agentService.removeAgent(paths, created.agent.id);
258
+ }
259
+ throw new Error(`Failed to sync OpenClaw role skills for "${created.agent.id}". ${toErrorMessage(error)}`);
260
+ }
261
+ const runtimeSync = await this.providerService.createProviderAgent(paths, created.agent.id, {
262
+ providerId: OPENCLAW_PROVIDER_ID,
125
263
  displayName: created.agent.displayName,
126
264
  workspaceDir: created.agent.workspaceDir,
127
- internalConfigDir: created.agent.internalConfigDir
265
+ internalConfigDir: created.agent.internalConfigDir,
128
266
  });
267
+ if (runtimeSync.code !== 0 &&
268
+ !containsAlreadyExistsMessage(runtimeSync.stdout, runtimeSync.stderr)) {
269
+ if (!created.alreadyExisted) {
270
+ await this.agentService.removeAgent(paths, created.agent.id);
271
+ }
272
+ throw new Error(`OpenClaw agent creation failed for "${created.agent.id}" (exit ${runtimeSync.code}). ${runtimeSync.stderr.trim() || runtimeSync.stdout.trim() || ""}`.trim());
273
+ }
274
+ try {
275
+ await this.ensureOpenClawAgentLocation(paths, {
276
+ agentId: created.agent.id,
277
+ displayName: created.agent.displayName,
278
+ workspaceDir: created.agent.workspaceDir,
279
+ internalConfigDir: created.agent.internalConfigDir,
280
+ });
281
+ }
282
+ catch (error) {
283
+ if (!created.alreadyExisted) {
284
+ await this.agentService.removeAgent(paths, created.agent.id);
285
+ }
286
+ throw new Error(`OpenClaw agent location sync failed for "${created.agent.id}". ${toErrorMessage(error)}`);
287
+ }
288
+ try {
289
+ const workspaceBootstrap = await this.agentService.ensureAgentWorkspaceBootstrap(paths, {
290
+ agentId: created.agent.id,
291
+ displayName: created.agent.displayName,
292
+ role: options.role?.trim() ??
293
+ (created.alreadyExisted ? created.agent.role : ""),
294
+ });
295
+ created.createdPaths.push(...workspaceBootstrap.createdPaths);
296
+ created.skippedPaths.push(...workspaceBootstrap.skippedPaths);
297
+ created.skippedPaths.push(...workspaceBootstrap.removedPaths);
298
+ }
299
+ catch (error) {
300
+ if (!created.alreadyExisted) {
301
+ await this.agentService.removeAgent(paths, created.agent.id);
302
+ }
303
+ throw new Error(`Failed to update workspace bootstrap for "${created.agent.id}". ${toErrorMessage(error)}`);
304
+ }
129
305
  return {
130
306
  ...created,
131
- externalAgentCreation: {
132
- providerId: externalAgentCreation.providerId,
133
- code: externalAgentCreation.code,
134
- stdout: externalAgentCreation.stdout,
135
- stderr: externalAgentCreation.stderr
136
- }
307
+ runtimeSync: {
308
+ runtimeId: runtimeSync.providerId,
309
+ code: runtimeSync.code,
310
+ stdout: runtimeSync.stdout,
311
+ stderr: runtimeSync.stderr,
312
+ },
137
313
  };
138
314
  }
139
- async createExternalAgent(rawAgentId, options = {}) {
315
+ async deleteAgent(rawAgentId, options = {}) {
140
316
  const paths = this.pathsProvider.getPaths();
141
317
  const agentId = normalizeAgentId(rawAgentId);
142
318
  if (!agentId) {
143
319
  throw new Error("Agent id cannot be empty.");
144
320
  }
145
- const agent = (await this.agentService.listAgents(paths)).find((entry) => entry.id === agentId);
146
- if (!agent) {
147
- throw new Error(`Agent "${agentId}" was not found.`);
148
- }
149
- const resolvedProviderId = options.providerId?.trim().toLowerCase() ??
150
- (await this.providerService.getAgentProvider(paths, agentId)).providerId;
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
321
+ const existing = (await this.agentService.listAgents(paths)).find((entry) => entry.id === agentId);
322
+ if (!existing) {
323
+ return this.agentService.removeAgent(paths, agentId);
324
+ }
325
+ const runtimeSync = await this.providerService.deleteProviderAgent(paths, agentId, {
326
+ providerId: OPENCLAW_PROVIDER_ID,
160
327
  });
328
+ if (runtimeSync.code !== 0 && !options.force) {
329
+ throw new Error(`OpenClaw agent deletion failed for "${agentId}" (exit ${runtimeSync.code}). ${runtimeSync.stderr.trim() || runtimeSync.stdout.trim() || ""}`.trim());
330
+ }
331
+ const removed = await this.agentService.removeAgent(paths, agentId);
161
332
  return {
162
- providerId: externalAgentCreation.providerId,
163
- code: externalAgentCreation.code,
164
- stdout: externalAgentCreation.stdout,
165
- stderr: externalAgentCreation.stderr
333
+ ...removed,
334
+ runtimeSync: {
335
+ runtimeId: runtimeSync.providerId,
336
+ code: runtimeSync.code,
337
+ stdout: runtimeSync.stdout,
338
+ stderr: runtimeSync.stderr,
339
+ },
166
340
  };
167
341
  }
168
- async providerSupportsExternalAgentCreation(providerId) {
169
- const providers = await this.providerService.listProviders();
170
- const provider = providers.find((entry) => entry.id === providerId);
171
- return Boolean(provider?.capabilities.agentCreate);
342
+ async setAgentManager(rawAgentId, rawReportsTo) {
343
+ const paths = this.pathsProvider.getPaths();
344
+ const updated = await this.agentService.setAgentManager(paths, rawAgentId, rawReportsTo);
345
+ await this.syncOpenClawRoleSkills(paths, updated.agentId);
346
+ if (updated.previousReportsTo) {
347
+ await this.syncOpenClawRoleSkills(paths, updated.previousReportsTo);
348
+ }
349
+ if (updated.reportsTo) {
350
+ await this.syncOpenClawRoleSkills(paths, updated.reportsTo);
351
+ }
352
+ return updated;
172
353
  }
173
- async deleteAgent(rawAgentId, options = {}) {
354
+ async listAgents() {
174
355
  const paths = this.pathsProvider.getPaths();
356
+ return this.agentService.listAgents(paths);
357
+ }
358
+ async listDirectReportees(rawAgentId) {
175
359
  const agentId = normalizeAgentId(rawAgentId);
176
360
  if (!agentId) {
177
361
  throw new Error("Agent id cannot be empty.");
178
362
  }
179
- let externalProviderId = options.providerId?.trim().toLowerCase();
180
- if (options.deleteExternalAgent && !externalProviderId) {
181
- const binding = await this.providerService.getAgentProvider(paths, agentId);
182
- externalProviderId = binding.providerId;
363
+ const paths = this.pathsProvider.getPaths();
364
+ const manifests = await this.agentManifestService.listManifests(paths);
365
+ assertAgentExists(manifests, agentId);
366
+ return manifests
367
+ .filter((manifest) => manifest.metadata.reportsTo === agentId)
368
+ .map((manifest) => manifest.agentId)
369
+ .sort((left, right) => left.localeCompare(right));
370
+ }
371
+ async listAllReportees(rawAgentId) {
372
+ const agentId = normalizeAgentId(rawAgentId);
373
+ if (!agentId) {
374
+ throw new Error("Agent id cannot be empty.");
183
375
  }
184
- const removed = await this.agentService.removeAgent(paths, agentId);
185
- if (!options.deleteExternalAgent) {
186
- return removed;
376
+ const paths = this.pathsProvider.getPaths();
377
+ const manifests = await this.agentManifestService.listManifests(paths);
378
+ assertAgentExists(manifests, agentId);
379
+ return collectAllReportees(manifests, agentId);
380
+ }
381
+ async getAgentInfo(rawAgentId) {
382
+ const agentId = normalizeAgentId(rawAgentId);
383
+ if (!agentId) {
384
+ throw new Error("Agent id cannot be empty.");
187
385
  }
188
- const externalAgentDeletion = await this.providerService.deleteProviderAgent(paths, agentId, {
189
- providerId: externalProviderId
190
- });
386
+ const paths = this.pathsProvider.getPaths();
387
+ const [agents, manifests] = await Promise.all([
388
+ this.agentService.listAgents(paths),
389
+ this.agentManifestService.listManifests(paths),
390
+ ]);
391
+ const descriptorsById = new Map(agents.map((agent) => [agent.id, agent]));
392
+ const agent = descriptorsById.get(agentId);
393
+ if (!agent) {
394
+ throw new Error(`Agent "${agentId}" does not exist.`);
395
+ }
396
+ const totalReportees = collectAllReportees(manifests, agentId).length;
397
+ const directReportees = manifests
398
+ .filter((manifest) => manifest.metadata.reportsTo === agentId)
399
+ .map((manifest) => {
400
+ const descriptor = descriptorsById.get(manifest.agentId);
401
+ const name = descriptor?.displayName?.trim() ||
402
+ manifest.metadata.name ||
403
+ manifest.agentId;
404
+ const role = descriptor?.role?.trim() || manifest.metadata.description || "Agent";
405
+ return {
406
+ id: manifest.agentId,
407
+ name,
408
+ role,
409
+ totalReportees: collectAllReportees(manifests, manifest.agentId)
410
+ .length,
411
+ };
412
+ })
413
+ .sort((left, right) => left.id.localeCompare(right.id));
191
414
  return {
192
- ...removed,
193
- externalAgentDeletion: {
194
- providerId: externalAgentDeletion.providerId,
195
- code: externalAgentDeletion.code,
196
- stdout: externalAgentDeletion.stdout,
197
- stderr: externalAgentDeletion.stderr
198
- }
415
+ id: agent.id,
416
+ name: agent.displayName,
417
+ role: agent.role,
418
+ totalReportees,
419
+ directReportees,
199
420
  };
200
421
  }
201
- async listAgents() {
202
- const paths = this.pathsProvider.getPaths();
203
- return this.agentService.listAgents(paths);
204
- }
205
422
  listProviders() {
206
423
  return this.providerService.listProviders();
207
424
  }
@@ -226,9 +443,15 @@ export class OpenGoatService {
226
443
  }
227
444
  async setAgentProvider(agentId, providerId) {
228
445
  const paths = this.pathsProvider.getPaths();
229
- const binding = await this.providerService.setAgentProvider(paths, agentId, providerId);
230
- await this.agentManifestService.syncManifestProvider(paths, binding.agentId, binding.providerId);
231
- return binding;
446
+ return this.providerService.setAgentProvider(paths, agentId, providerId);
447
+ }
448
+ async getOpenClawGatewayConfig() {
449
+ const paths = this.pathsProvider.getPaths();
450
+ return this.providerService.getOpenClawGatewayConfig(paths, process.env);
451
+ }
452
+ async setOpenClawGatewayConfig(config) {
453
+ const paths = this.pathsProvider.getPaths();
454
+ return this.providerService.setOpenClawGatewayConfig(paths, config);
232
455
  }
233
456
  async routeMessage(agentId, message) {
234
457
  const paths = this.pathsProvider.getPaths();
@@ -238,46 +461,192 @@ export class OpenGoatService {
238
461
  const paths = this.pathsProvider.getPaths();
239
462
  return this.orchestrationService.runAgent(paths, agentId, options);
240
463
  }
241
- async listSkills(agentId = DEFAULT_AGENT_ID) {
464
+ async createTask(actorId, options) {
242
465
  const paths = this.pathsProvider.getPaths();
243
- return this.skillService.listSkills(paths, agentId);
466
+ return this.boardService.createTask(paths, actorId, options);
244
467
  }
245
- async listGlobalSkills() {
468
+ async listTasks(options = {}) {
246
469
  const paths = this.pathsProvider.getPaths();
247
- return this.skillService.listGlobalSkills(paths);
470
+ return this.boardService.listTasks(paths, options);
248
471
  }
249
- async installSkill(request) {
472
+ async listLatestTasks(options = {}) {
473
+ const paths = this.pathsProvider.getPaths();
474
+ return this.boardService.listLatestTasks(paths, options);
475
+ }
476
+ async listLatestTasksPage(options = {}) {
250
477
  const paths = this.pathsProvider.getPaths();
251
- return this.skillService.installSkill(paths, request);
478
+ return this.boardService.listLatestTasksPage(paths, options);
252
479
  }
253
- async listPlugins(options = {}) {
480
+ async getTask(taskId) {
254
481
  const paths = this.pathsProvider.getPaths();
255
- return this.pluginService.listPlugins(paths, options);
482
+ return this.boardService.getTask(paths, taskId);
256
483
  }
257
- async getPluginInfo(pluginId) {
484
+ async deleteTasks(actorId, taskIds) {
258
485
  const paths = this.pathsProvider.getPaths();
259
- return this.pluginService.getPluginInfo(paths, pluginId);
486
+ return this.boardService.deleteTasks(paths, actorId, taskIds);
260
487
  }
261
- async installPlugin(request) {
488
+ async updateTaskStatus(actorId, taskId, status, reason) {
262
489
  const paths = this.pathsProvider.getPaths();
263
- return this.pluginService.installPlugin(paths, request);
490
+ return this.boardService.updateTaskStatus(paths, actorId, taskId, status, reason);
264
491
  }
265
- async enablePlugin(pluginId) {
492
+ async addTaskBlocker(actorId, taskId, blocker) {
266
493
  const paths = this.pathsProvider.getPaths();
267
- await this.pluginService.enablePlugin(paths, pluginId);
494
+ return this.boardService.addTaskBlocker(paths, actorId, taskId, blocker);
268
495
  }
269
- async disablePlugin(pluginId) {
496
+ async addTaskArtifact(actorId, taskId, content) {
270
497
  const paths = this.pathsProvider.getPaths();
271
- await this.pluginService.disablePlugin(paths, pluginId);
498
+ return this.boardService.addTaskArtifact(paths, actorId, taskId, content);
272
499
  }
273
- async pluginDoctor() {
500
+ async addTaskWorklog(actorId, taskId, content) {
274
501
  const paths = this.pathsProvider.getPaths();
275
- return this.pluginService.doctor(paths);
502
+ return this.boardService.addTaskWorklog(paths, actorId, taskId, content);
503
+ }
504
+ async runTaskCronCycle(options = {}) {
505
+ const paths = this.pathsProvider.getPaths();
506
+ const ranAt = this.resolveNowIso();
507
+ const manifests = await this.agentManifestService.listManifests(paths);
508
+ const manifestsById = new Map(manifests.map((manifest) => [manifest.agentId, manifest]));
509
+ const inactiveMinutes = resolveInactiveMinutes(options.inactiveMinutes);
510
+ const inactiveCandidates = await this.collectInactiveAgents(paths, manifests, inactiveMinutes);
511
+ const tasks = await this.boardService.listTasks(paths, { limit: 10_000 });
512
+ const dispatches = [];
513
+ let scannedTasks = tasks.length;
514
+ let todoTasks = 0;
515
+ let blockedTasks = 0;
516
+ for (const task of tasks) {
517
+ if (task.status !== "todo" && task.status !== "blocked") {
518
+ continue;
519
+ }
520
+ if (task.status === "todo") {
521
+ todoTasks += 1;
522
+ const targetAgentId = task.assignedTo;
523
+ const sessionRef = buildTaskSessionRef(targetAgentId, task.taskId);
524
+ const message = buildTodoTaskMessage({ task });
525
+ const result = await this.dispatchAutomationMessage(paths, targetAgentId, sessionRef, message);
526
+ dispatches.push({
527
+ kind: "todo",
528
+ targetAgentId,
529
+ sessionRef,
530
+ taskId: task.taskId,
531
+ ok: result.ok,
532
+ error: result.error,
533
+ });
534
+ continue;
535
+ }
536
+ blockedTasks += 1;
537
+ const assigneeManifest = manifestsById.get(task.assignedTo);
538
+ const managerAgentId = normalizeAgentId(assigneeManifest?.metadata.reportsTo ?? "") ||
539
+ DEFAULT_AGENT_ID;
540
+ const sessionRef = buildTaskSessionRef(managerAgentId, task.taskId);
541
+ const message = buildBlockedTaskMessage({ task });
542
+ const result = await this.dispatchAutomationMessage(paths, managerAgentId, sessionRef, message);
543
+ dispatches.push({
544
+ kind: "blocked",
545
+ targetAgentId: managerAgentId,
546
+ sessionRef,
547
+ taskId: task.taskId,
548
+ ok: result.ok,
549
+ error: result.error,
550
+ });
551
+ }
552
+ for (const candidate of inactiveCandidates) {
553
+ const sessionRef = buildInactiveSessionRef(candidate.managerAgentId, candidate.subjectAgentId);
554
+ const message = buildInactiveAgentMessage({
555
+ managerAgentId: candidate.managerAgentId,
556
+ subjectAgentId: candidate.subjectAgentId,
557
+ subjectName: candidate.subjectName,
558
+ role: candidate.role,
559
+ inactiveMinutes,
560
+ lastActionTimestamp: candidate.lastActionTimestamp,
561
+ });
562
+ const result = await this.dispatchAutomationMessage(paths, candidate.managerAgentId, sessionRef, message);
563
+ dispatches.push({
564
+ kind: "inactive",
565
+ targetAgentId: candidate.managerAgentId,
566
+ sessionRef,
567
+ subjectAgentId: candidate.subjectAgentId,
568
+ ok: result.ok,
569
+ error: result.error,
570
+ });
571
+ }
572
+ const failed = dispatches.filter((entry) => !entry.ok).length;
573
+ return {
574
+ ranAt,
575
+ scannedTasks,
576
+ todoTasks,
577
+ blockedTasks,
578
+ inactiveAgents: inactiveCandidates.length,
579
+ sent: dispatches.length - failed,
580
+ failed,
581
+ dispatches,
582
+ };
583
+ }
584
+ async listSkills(agentId = DEFAULT_AGENT_ID) {
585
+ const paths = this.pathsProvider.getPaths();
586
+ return this.skillService.listSkills(paths, agentId);
587
+ }
588
+ async listGlobalSkills() {
589
+ const paths = this.pathsProvider.getPaths();
590
+ return this.skillService.listGlobalSkills(paths);
591
+ }
592
+ async installSkill(request) {
593
+ const paths = this.pathsProvider.getPaths();
594
+ const result = await this.skillService.installSkill(paths, request);
595
+ if (result.scope === "agent" && result.agentId) {
596
+ await this.syncOpenClawRoleSkills(paths, result.agentId);
597
+ }
598
+ return result;
599
+ }
600
+ async runOpenClaw(args, options = {}) {
601
+ if (!this.commandRunner) {
602
+ throw new Error("OpenClaw passthrough is unavailable: command runner was not configured.");
603
+ }
604
+ const sanitized = args.map((value) => value.trim()).filter(Boolean);
605
+ if (sanitized.length === 0) {
606
+ throw new Error("OpenClaw passthrough requires at least one argument.");
607
+ }
608
+ const executionEnv = prepareOpenClawCommandEnv(options.env ?? process.env);
609
+ const command = executionEnv.OPENGOAT_OPENCLAW_CMD?.trim() ||
610
+ executionEnv.OPENCLAW_CMD?.trim() ||
611
+ process.env.OPENGOAT_OPENCLAW_CMD?.trim() ||
612
+ process.env.OPENCLAW_CMD?.trim() ||
613
+ "openclaw";
614
+ try {
615
+ return await this.commandRunner.run({
616
+ command,
617
+ args: sanitized,
618
+ cwd: options.cwd,
619
+ env: executionEnv,
620
+ });
621
+ }
622
+ catch (error) {
623
+ if (isSpawnPermissionOrMissing(error)) {
624
+ throw new ProviderCommandNotFoundError(OPENCLAW_PROVIDER_ID, command);
625
+ }
626
+ throw error;
627
+ }
276
628
  }
277
629
  async listSessions(agentId = DEFAULT_AGENT_ID, options = {}) {
278
630
  const paths = this.pathsProvider.getPaths();
279
631
  return this.sessionService.listSessions(paths, agentId, options);
280
632
  }
633
+ async prepareSession(agentId = DEFAULT_AGENT_ID, options = {}) {
634
+ const paths = this.pathsProvider.getPaths();
635
+ const prepared = await this.sessionService.prepareRunSession(paths, agentId, {
636
+ sessionRef: options.sessionRef,
637
+ projectPath: options.projectPath,
638
+ forceNew: options.forceNew,
639
+ userMessage: "",
640
+ });
641
+ if (!prepared.enabled) {
642
+ throw new Error("Session preparation was disabled.");
643
+ }
644
+ return prepared.info;
645
+ }
646
+ async getAgentLastAction(agentId = DEFAULT_AGENT_ID) {
647
+ const paths = this.pathsProvider.getPaths();
648
+ return this.sessionService.getLastAgentAction(paths, agentId);
649
+ }
281
650
  async getSessionHistory(agentId = DEFAULT_AGENT_ID, options = {}) {
282
651
  const paths = this.pathsProvider.getPaths();
283
652
  return this.sessionService.getSessionHistory(paths, agentId, options);
@@ -304,5 +673,506 @@ export class OpenGoatService {
304
673
  getPaths() {
305
674
  return this.pathsProvider.getPaths();
306
675
  }
676
+ async dispatchAutomationMessage(paths, agentId, sessionRef, message) {
677
+ try {
678
+ const result = await this.orchestrationService.runAgent(paths, agentId, {
679
+ message,
680
+ sessionRef,
681
+ env: process.env,
682
+ });
683
+ if (result.code !== 0) {
684
+ return {
685
+ ok: false,
686
+ error: (result.stderr ||
687
+ result.stdout ||
688
+ `Runtime exited with code ${result.code}.`).trim(),
689
+ };
690
+ }
691
+ return { ok: true };
692
+ }
693
+ catch (error) {
694
+ return {
695
+ ok: false,
696
+ error: toErrorMessage(error),
697
+ };
698
+ }
699
+ }
700
+ resolveNowIso() {
701
+ return this.nowIso();
702
+ }
703
+ async initializeRuntimeDefaults() {
704
+ const initialization = await this.bootstrapService.initialize();
705
+ try {
706
+ await this.syncRuntimeDefaults();
707
+ }
708
+ catch {
709
+ // Startup remains functional even if OpenClaw CLI/runtime is unavailable.
710
+ }
711
+ return initialization;
712
+ }
713
+ resolveNowMs() {
714
+ return Date.now();
715
+ }
716
+ async syncOpenClawRoleSkills(paths, rawAgentId) {
717
+ const agentId = normalizeAgentId(rawAgentId);
718
+ if (!agentId) {
719
+ throw new Error("Agent id cannot be empty.");
720
+ }
721
+ const createdPaths = [];
722
+ const skippedPaths = [];
723
+ const removedPaths = [];
724
+ const managedSkillsSync = await this.removeOpenClawManagedRoleSkills(paths);
725
+ createdPaths.push(...managedSkillsSync.createdPaths);
726
+ skippedPaths.push(...managedSkillsSync.skippedPaths);
727
+ removedPaths.push(...managedSkillsSync.removedPaths);
728
+ const syncedAgents = new Set();
729
+ const syncAgent = async (targetAgentId) => {
730
+ if (syncedAgents.has(targetAgentId)) {
731
+ return;
732
+ }
733
+ syncedAgents.add(targetAgentId);
734
+ const sync = await this.agentService.ensureAgentWorkspaceRoleSkills(paths, targetAgentId);
735
+ createdPaths.push(...sync.createdPaths);
736
+ skippedPaths.push(...sync.skippedPaths);
737
+ removedPaths.push(...sync.removedPaths);
738
+ };
739
+ await syncAgent(agentId);
740
+ const manifest = await this.agentManifestService.getManifest(paths, agentId);
741
+ const managerAgentId = normalizeAgentId(manifest.metadata.reportsTo ?? "");
742
+ if (managerAgentId) {
743
+ await syncAgent(managerAgentId);
744
+ }
745
+ return {
746
+ createdPaths,
747
+ skippedPaths,
748
+ removedPaths,
749
+ };
750
+ }
751
+ async removeOpenClawManagedRoleSkills(paths) {
752
+ if (!this.commandRunner) {
753
+ return {
754
+ createdPaths: [],
755
+ skippedPaths: ["openclaw-managed-skills:command-runner-unavailable"],
756
+ removedPaths: [],
757
+ };
758
+ }
759
+ const managedSkillsDir = await this.resolveOpenClawManagedSkillsDir(paths);
760
+ if (!managedSkillsDir) {
761
+ return {
762
+ createdPaths: [],
763
+ skippedPaths: ["openclaw-managed-skills:unresolved"],
764
+ removedPaths: [],
765
+ };
766
+ }
767
+ const skippedPaths = [];
768
+ const removedPaths = [];
769
+ for (const legacySkillId of [
770
+ "board-manager",
771
+ "board-individual",
772
+ "og-board-manager",
773
+ "og-board-individual",
774
+ "manager",
775
+ "board-user",
776
+ ]) {
777
+ const legacyDir = this.pathPort.join(managedSkillsDir, legacySkillId);
778
+ if (!(await this.fileSystem.exists(legacyDir))) {
779
+ skippedPaths.push(legacyDir);
780
+ continue;
781
+ }
782
+ await this.fileSystem.removeDir(legacyDir);
783
+ removedPaths.push(legacyDir);
784
+ }
785
+ return {
786
+ createdPaths: [],
787
+ skippedPaths,
788
+ removedPaths,
789
+ };
790
+ }
791
+ async resolveOpenClawManagedSkillsDir(paths) {
792
+ if (this.openClawManagedSkillsDirCache !== undefined) {
793
+ return this.openClawManagedSkillsDirCache;
794
+ }
795
+ const providerConfig = await this.providerService.getProviderConfig(paths, OPENCLAW_PROVIDER_ID);
796
+ const env = {
797
+ ...process.env,
798
+ ...(providerConfig?.env ?? {}),
799
+ };
800
+ const skillsList = await this.runOpenClaw(["skills", "list", "--json"], {
801
+ env,
802
+ });
803
+ if (skillsList.code !== 0) {
804
+ throw new Error(`OpenClaw skills list failed (exit ${skillsList.code}). ${skillsList.stderr.trim() || skillsList.stdout.trim() || ""}`.trim());
805
+ }
806
+ let parsed;
807
+ try {
808
+ parsed = JSON.parse(skillsList.stdout);
809
+ }
810
+ catch {
811
+ throw new Error("OpenClaw skills list returned non-JSON output; cannot resolve managed skills directory.");
812
+ }
813
+ const managedSkillsDir = extractManagedSkillsDir(parsed);
814
+ this.openClawManagedSkillsDirCache = managedSkillsDir;
815
+ return managedSkillsDir;
816
+ }
817
+ async listOpenClawAgents(paths) {
818
+ if (!this.commandRunner) {
819
+ return [];
820
+ }
821
+ const env = await this.resolveOpenClawEnv(paths);
822
+ const listed = await this.runOpenClaw(["agents", "list", "--json"], {
823
+ env,
824
+ });
825
+ if (listed.code !== 0) {
826
+ throw new Error(`OpenClaw agents list failed (exit ${listed.code}). ${listed.stderr.trim() || listed.stdout.trim() || ""}`.trim());
827
+ }
828
+ let parsed;
829
+ try {
830
+ parsed = JSON.parse(listed.stdout);
831
+ }
832
+ catch {
833
+ throw new Error("OpenClaw agents list returned non-JSON output; cannot inspect agents.");
834
+ }
835
+ return extractOpenClawAgents(parsed);
836
+ }
837
+ async ensureOpenClawAgentLocation(paths, params) {
838
+ if (!this.commandRunner) {
839
+ return;
840
+ }
841
+ const env = await this.resolveOpenClawEnv(paths);
842
+ const listed = await this.runOpenClaw(["agents", "list", "--json"], {
843
+ env,
844
+ });
845
+ if (listed.code !== 0) {
846
+ throw new Error(`OpenClaw agents list failed (exit ${listed.code}). ${listed.stderr.trim() || listed.stdout.trim() || ""}`.trim());
847
+ }
848
+ let parsed;
849
+ try {
850
+ parsed = JSON.parse(listed.stdout);
851
+ }
852
+ catch {
853
+ throw new Error("OpenClaw agents list returned non-JSON output; cannot verify agent location.");
854
+ }
855
+ const entry = extractOpenClawAgentEntry(parsed, params.agentId);
856
+ if (!entry) {
857
+ return;
858
+ }
859
+ if (pathMatches(entry.workspace, params.workspaceDir) &&
860
+ pathMatches(entry.agentDir, params.internalConfigDir)) {
861
+ return;
862
+ }
863
+ const deleted = await this.providerService.deleteProviderAgent(paths, params.agentId, { providerId: OPENCLAW_PROVIDER_ID });
864
+ if (deleted.code !== 0) {
865
+ throw new Error(`OpenClaw agent location repair failed deleting "${params.agentId}" (exit ${deleted.code}). ${deleted.stderr.trim() || deleted.stdout.trim() || ""}`.trim());
866
+ }
867
+ const recreated = await this.providerService.createProviderAgent(paths, params.agentId, {
868
+ providerId: OPENCLAW_PROVIDER_ID,
869
+ displayName: params.displayName,
870
+ workspaceDir: params.workspaceDir,
871
+ internalConfigDir: params.internalConfigDir,
872
+ });
873
+ if (recreated.code !== 0 &&
874
+ !containsAlreadyExistsMessage(recreated.stdout, recreated.stderr)) {
875
+ throw new Error(`OpenClaw agent location repair failed creating "${params.agentId}" (exit ${recreated.code}). ${recreated.stderr.trim() || recreated.stdout.trim() || ""}`.trim());
876
+ }
877
+ }
878
+ async resolveOpenClawEnv(paths) {
879
+ const providerConfig = await this.providerService.getProviderConfig(paths, OPENCLAW_PROVIDER_ID);
880
+ return {
881
+ ...(providerConfig?.env ?? {}),
882
+ ...process.env,
883
+ };
884
+ }
885
+ async collectInactiveAgents(paths, manifests, inactiveMinutes) {
886
+ const nowMs = this.resolveNowMs();
887
+ const inactiveCutoffMs = nowMs - inactiveMinutes * 60_000;
888
+ const inactive = [];
889
+ for (const manifest of manifests) {
890
+ const managerAgentId = normalizeAgentId(manifest.metadata.reportsTo ?? "");
891
+ if (!managerAgentId) {
892
+ continue;
893
+ }
894
+ const lastAction = await this.sessionService.getLastAgentAction(paths, manifest.agentId);
895
+ if (lastAction && lastAction.timestamp >= inactiveCutoffMs) {
896
+ continue;
897
+ }
898
+ inactive.push({
899
+ managerAgentId,
900
+ subjectAgentId: manifest.agentId,
901
+ subjectName: manifest.metadata.name,
902
+ role: manifest.metadata.description,
903
+ lastActionTimestamp: lastAction?.timestamp,
904
+ });
905
+ }
906
+ return inactive;
907
+ }
908
+ }
909
+ function containsAlreadyExistsMessage(stdout, stderr) {
910
+ const text = `${stdout}\n${stderr}`.toLowerCase();
911
+ return /\balready exists?\b/.test(text);
912
+ }
913
+ function containsAgentNotFoundMessage(stdout, stderr) {
914
+ const text = `${stdout}\n${stderr}`.toLowerCase();
915
+ return /\b(not found|does not exist|no such agent|unknown agent|could not find|no agent found|not exist)\b/.test(text);
916
+ }
917
+ function toErrorMessage(error) {
918
+ if (error instanceof Error) {
919
+ return error.message;
920
+ }
921
+ return String(error);
922
+ }
923
+ function resolveInactiveMinutes(value) {
924
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
925
+ return 30;
926
+ }
927
+ return Math.floor(value);
928
+ }
929
+ function extractManagedSkillsDir(payload) {
930
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
931
+ return null;
932
+ }
933
+ const record = payload;
934
+ if (typeof record.managedSkillsDir !== "string") {
935
+ return null;
936
+ }
937
+ const managedSkillsDir = record.managedSkillsDir.trim();
938
+ return managedSkillsDir || null;
939
+ }
940
+ function extractOpenClawAgents(payload) {
941
+ if (!Array.isArray(payload)) {
942
+ return [];
943
+ }
944
+ const entries = [];
945
+ for (const entry of payload) {
946
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
947
+ continue;
948
+ }
949
+ const record = entry;
950
+ const id = normalizeAgentId(String(record.id ?? ""));
951
+ if (!id) {
952
+ continue;
953
+ }
954
+ entries.push({
955
+ id,
956
+ workspace: typeof record.workspace === "string" ? record.workspace : "",
957
+ agentDir: typeof record.agentDir === "string" ? record.agentDir : "",
958
+ });
959
+ }
960
+ return entries;
961
+ }
962
+ function extractOpenClawAgentEntry(payload, agentId) {
963
+ const normalizedAgentId = normalizeAgentId(agentId);
964
+ if (!normalizedAgentId) {
965
+ return null;
966
+ }
967
+ for (const entry of extractOpenClawAgents(payload)) {
968
+ if (entry.id !== normalizedAgentId) {
969
+ continue;
970
+ }
971
+ return {
972
+ workspace: entry.workspace,
973
+ agentDir: entry.agentDir,
974
+ };
975
+ }
976
+ return null;
977
+ }
978
+ function pathMatches(left, right) {
979
+ const leftNormalized = normalizePathForCompare(left);
980
+ const rightNormalized = normalizePathForCompare(right);
981
+ if (!leftNormalized || !rightNormalized) {
982
+ return false;
983
+ }
984
+ return leftNormalized === rightNormalized;
985
+ }
986
+ function pathIsWithin(containerPath, candidatePath) {
987
+ const normalizedContainer = normalizePathForCompare(containerPath);
988
+ const normalizedCandidate = normalizePathForCompare(candidatePath);
989
+ if (!normalizedContainer || !normalizedCandidate) {
990
+ return false;
991
+ }
992
+ const relative = path.relative(normalizedContainer, normalizedCandidate);
993
+ if (!relative) {
994
+ return true;
995
+ }
996
+ return !relative.startsWith("..") && !path.isAbsolute(relative);
997
+ }
998
+ function normalizePathForCompare(value) {
999
+ const trimmed = value.trim();
1000
+ if (!trimmed) {
1001
+ return "";
1002
+ }
1003
+ const resolved = path.resolve(trimmed);
1004
+ if (process.platform === "win32") {
1005
+ return resolved.toLowerCase();
1006
+ }
1007
+ return resolved;
1008
+ }
1009
+ function buildTaskSessionRef(agentId, taskId) {
1010
+ const normalizedAgentId = normalizeAgentId(agentId) || DEFAULT_AGENT_ID;
1011
+ const normalizedTaskId = normalizeAgentId(taskId) || "task";
1012
+ return `agent:${normalizedAgentId}:agent_${normalizedAgentId}_task_${normalizedTaskId}`;
1013
+ }
1014
+ function buildInactiveSessionRef(managerAgentId, subjectAgentId) {
1015
+ const manager = normalizeAgentId(managerAgentId) || DEFAULT_AGENT_ID;
1016
+ const subject = normalizeAgentId(subjectAgentId) || "agent";
1017
+ return `agent:${manager}:agent_${manager}_inactive_${subject}`;
1018
+ }
1019
+ function buildTodoTaskMessage(params) {
1020
+ const blockers = params.task.blockers.length > 0 ? params.task.blockers.join("; ") : "None";
1021
+ const artifacts = params.task.artifacts.length > 0
1022
+ ? params.task.artifacts
1023
+ .map((entry) => `- ${entry.createdAt} @${entry.createdBy}: ${entry.content}`)
1024
+ .join("\n")
1025
+ : "- None";
1026
+ const worklog = params.task.worklog.length > 0
1027
+ ? params.task.worklog
1028
+ .map((entry) => `- ${entry.createdAt} @${entry.createdBy}: ${entry.content}`)
1029
+ .join("\n")
1030
+ : "- None";
1031
+ return [
1032
+ `Task #${params.task.taskId} is assigned to you and currently in TODO. Please work on it now.`,
1033
+ "",
1034
+ `Task ID: ${params.task.taskId}`,
1035
+ `Title: ${params.task.title}`,
1036
+ `Description: ${params.task.description}`,
1037
+ `Project: ${params.task.project}`,
1038
+ `Status: ${params.task.status}`,
1039
+ `Owner: @${params.task.owner}`,
1040
+ `Assigned to: @${params.task.assignedTo}`,
1041
+ `Created at: ${params.task.createdAt}`,
1042
+ `Blockers: ${blockers}`,
1043
+ "Artifacts:",
1044
+ artifacts,
1045
+ "Worklog:",
1046
+ worklog,
1047
+ ].join("\n");
1048
+ }
1049
+ function buildBlockedTaskMessage(params) {
1050
+ const blockerReason = params.task.blockers.length > 0
1051
+ ? params.task.blockers.join("; ")
1052
+ : params.task.statusReason?.trim() || "no blocker details were provided";
1053
+ const artifacts = params.task.artifacts.length > 0
1054
+ ? params.task.artifacts
1055
+ .map((entry) => `- ${entry.createdAt} @${entry.createdBy}: ${entry.content}`)
1056
+ .join("\n")
1057
+ : "- None";
1058
+ const worklog = params.task.worklog.length > 0
1059
+ ? params.task.worklog
1060
+ .map((entry) => `- ${entry.createdAt} @${entry.createdBy}: ${entry.content}`)
1061
+ .join("\n")
1062
+ : "- None";
1063
+ return [
1064
+ `Task #${params.task.taskId}, assigned to your reportee "@${params.task.assignedTo}" is blocked because of ${blockerReason}. Help unblocking it.`,
1065
+ "",
1066
+ `Task ID: ${params.task.taskId}`,
1067
+ `Title: ${params.task.title}`,
1068
+ `Description: ${params.task.description}`,
1069
+ `Project: ${params.task.project}`,
1070
+ `Status: ${params.task.status}`,
1071
+ `Owner: @${params.task.owner}`,
1072
+ `Assigned to: @${params.task.assignedTo}`,
1073
+ `Created at: ${params.task.createdAt}`,
1074
+ "Artifacts:",
1075
+ artifacts,
1076
+ "Worklog:",
1077
+ worklog,
1078
+ ].join("\n");
1079
+ }
1080
+ function buildInactiveAgentMessage(params) {
1081
+ const lastAction = typeof params.lastActionTimestamp === "number" &&
1082
+ Number.isFinite(params.lastActionTimestamp)
1083
+ ? new Date(params.lastActionTimestamp).toISOString()
1084
+ : "No recorded assistant actions yet";
1085
+ return [
1086
+ `Your reportee "@${params.subjectAgentId}" (${params.subjectName}) has no activity in the last ${params.inactiveMinutes} minutes.`,
1087
+ `Role: ${params.role}`,
1088
+ `Last action: ${lastAction}`,
1089
+ `Manager: @${params.managerAgentId}`,
1090
+ "Please check in and unblock progress.",
1091
+ ].join("\n");
1092
+ }
1093
+ function assertAgentExists(manifests, agentId) {
1094
+ if (manifests.some((manifest) => manifest.agentId === agentId)) {
1095
+ return;
1096
+ }
1097
+ throw new Error(`Agent "${agentId}" does not exist.`);
1098
+ }
1099
+ function collectAllReportees(manifests, managerAgentId) {
1100
+ const byManager = new Map();
1101
+ for (const manifest of manifests) {
1102
+ const reportsTo = manifest.metadata.reportsTo;
1103
+ if (!reportsTo) {
1104
+ continue;
1105
+ }
1106
+ const reportees = byManager.get(reportsTo) ?? [];
1107
+ reportees.push(manifest.agentId);
1108
+ byManager.set(reportsTo, reportees);
1109
+ }
1110
+ const visited = new Set();
1111
+ const queue = [...(byManager.get(managerAgentId) ?? [])];
1112
+ while (queue.length > 0) {
1113
+ const current = queue.shift();
1114
+ if (!current || current === managerAgentId || visited.has(current)) {
1115
+ continue;
1116
+ }
1117
+ visited.add(current);
1118
+ queue.push(...(byManager.get(current) ?? []));
1119
+ }
1120
+ return [...visited].sort((left, right) => left.localeCompare(right));
1121
+ }
1122
+ function prepareOpenClawCommandEnv(env) {
1123
+ const mergedPath = dedupePathEntries([
1124
+ ...resolvePreferredOpenClawCommandPaths(env),
1125
+ ...(env.PATH?.split(path.delimiter) ?? []),
1126
+ ]);
1127
+ return {
1128
+ ...env,
1129
+ PATH: mergedPath.join(path.delimiter),
1130
+ };
1131
+ }
1132
+ function resolvePreferredOpenClawCommandPaths(env) {
1133
+ const homeDir = homedir();
1134
+ const preferredPaths = [
1135
+ path.dirname(process.execPath),
1136
+ path.join(homeDir, ".npm-global", "bin"),
1137
+ path.join(homeDir, ".npm", "bin"),
1138
+ path.join(homeDir, ".local", "bin"),
1139
+ path.join(homeDir, ".volta", "bin"),
1140
+ path.join(homeDir, ".fnm", "current", "bin"),
1141
+ path.join(homeDir, ".asdf", "shims"),
1142
+ path.join(homeDir, "bin"),
1143
+ ];
1144
+ const npmPrefixCandidates = dedupePathEntries([
1145
+ env.npm_config_prefix ?? "",
1146
+ env.NPM_CONFIG_PREFIX ?? "",
1147
+ process.env.npm_config_prefix ?? "",
1148
+ process.env.NPM_CONFIG_PREFIX ?? "",
1149
+ ]);
1150
+ for (const prefix of npmPrefixCandidates) {
1151
+ preferredPaths.push(path.join(prefix, "bin"));
1152
+ }
1153
+ if (process.platform === "darwin") {
1154
+ preferredPaths.push("/opt/homebrew/bin", "/opt/homebrew/opt/node@22/bin", "/usr/local/opt/node@22/bin");
1155
+ }
1156
+ return preferredPaths;
1157
+ }
1158
+ function dedupePathEntries(entries) {
1159
+ const seen = new Set();
1160
+ const deduped = [];
1161
+ for (const rawEntry of entries) {
1162
+ const entry = rawEntry.trim();
1163
+ if (!entry || seen.has(entry)) {
1164
+ continue;
1165
+ }
1166
+ seen.add(entry);
1167
+ deduped.push(entry);
1168
+ }
1169
+ return deduped;
1170
+ }
1171
+ function isSpawnPermissionOrMissing(error) {
1172
+ return (typeof error === "object" &&
1173
+ error !== null &&
1174
+ "code" in error &&
1175
+ ((error.code ?? "") === "ENOENT" ||
1176
+ (error.code ?? "") === "EACCES"));
307
1177
  }
308
1178
  //# sourceMappingURL=opengoat.service.js.map