@opengoat/core 2026.2.15 → 2026.2.18-2

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 (87) hide show
  1. package/dist/core/agents/application/agent.service.d.ts +20 -3
  2. package/dist/core/agents/application/agent.service.js +138 -64
  3. package/dist/core/agents/application/agent.service.js.map +1 -1
  4. package/dist/core/boards/application/board.service.d.ts +2 -0
  5. package/dist/core/boards/application/board.service.js +36 -2
  6. package/dist/core/boards/application/board.service.js.map +1 -1
  7. package/dist/core/bootstrap/application/bootstrap.service.js +4 -1
  8. package/dist/core/bootstrap/application/bootstrap.service.js.map +1 -1
  9. package/dist/core/opengoat/application/opengoat.service.d.ts +8 -4
  10. package/dist/core/opengoat/application/opengoat.service.helpers.d.ts +24 -2
  11. package/dist/core/opengoat/application/opengoat.service.helpers.js +145 -19
  12. package/dist/core/opengoat/application/opengoat.service.helpers.js.map +1 -1
  13. package/dist/core/opengoat/application/opengoat.service.js +285 -109
  14. package/dist/core/opengoat/application/opengoat.service.js.map +1 -1
  15. package/dist/core/orchestration/application/orchestration.service.d.ts +1 -0
  16. package/dist/core/orchestration/application/orchestration.service.js +46 -29
  17. package/dist/core/orchestration/application/orchestration.service.js.map +1 -1
  18. package/dist/core/providers/application/provider.service.d.ts +43 -4
  19. package/dist/core/providers/application/provider.service.js +957 -82
  20. package/dist/core/providers/application/provider.service.js.map +1 -1
  21. package/dist/core/providers/index.d.ts +1 -1
  22. package/dist/core/providers/index.js.map +1 -1
  23. package/dist/core/providers/loader.js +4 -2
  24. package/dist/core/providers/loader.js.map +1 -1
  25. package/dist/core/providers/openclaw-gateway-rpc.d.ts +20 -0
  26. package/dist/core/providers/openclaw-gateway-rpc.js +629 -0
  27. package/dist/core/providers/openclaw-gateway-rpc.js.map +1 -0
  28. package/dist/core/providers/provider-module.d.ts +15 -0
  29. package/dist/core/providers/provider-module.js +12 -1
  30. package/dist/core/providers/provider-module.js.map +1 -1
  31. package/dist/core/providers/providers/claude-code/index.d.ts +4 -0
  32. package/dist/core/providers/providers/claude-code/index.js +36 -0
  33. package/dist/core/providers/providers/claude-code/index.js.map +1 -0
  34. package/dist/core/providers/providers/claude-code/provider.d.ts +13 -0
  35. package/dist/core/providers/providers/claude-code/provider.js +153 -0
  36. package/dist/core/providers/providers/claude-code/provider.js.map +1 -0
  37. package/dist/core/providers/providers/codex/index.d.ts +4 -0
  38. package/dist/core/providers/providers/codex/index.js +36 -0
  39. package/dist/core/providers/providers/codex/index.js.map +1 -0
  40. package/dist/core/providers/providers/codex/provider.d.ts +12 -0
  41. package/dist/core/providers/providers/codex/provider.js +161 -0
  42. package/dist/core/providers/providers/codex/provider.js.map +1 -0
  43. package/dist/core/providers/providers/cursor/index.d.ts +4 -0
  44. package/dist/core/providers/providers/cursor/index.js +36 -0
  45. package/dist/core/providers/providers/cursor/index.js.map +1 -0
  46. package/dist/core/providers/providers/cursor/provider.d.ts +12 -0
  47. package/dist/core/providers/providers/cursor/provider.js +162 -0
  48. package/dist/core/providers/providers/cursor/provider.js.map +1 -0
  49. package/dist/core/providers/providers/gemini-cli/index.d.ts +4 -0
  50. package/dist/core/providers/providers/gemini-cli/index.js +40 -0
  51. package/dist/core/providers/providers/gemini-cli/index.js.map +1 -0
  52. package/dist/core/providers/providers/gemini-cli/provider.d.ts +11 -0
  53. package/dist/core/providers/providers/gemini-cli/provider.js +130 -0
  54. package/dist/core/providers/providers/gemini-cli/provider.js.map +1 -0
  55. package/dist/core/providers/providers/openclaw/index.js +12 -0
  56. package/dist/core/providers/providers/openclaw/index.js.map +1 -1
  57. package/dist/core/providers/providers/openclaw/provider.js +1 -0
  58. package/dist/core/providers/providers/openclaw/provider.js.map +1 -1
  59. package/dist/core/providers/providers/opencode/index.d.ts +4 -0
  60. package/dist/core/providers/providers/opencode/index.js +31 -0
  61. package/dist/core/providers/providers/opencode/index.js.map +1 -0
  62. package/dist/core/providers/providers/opencode/provider.d.ts +13 -0
  63. package/dist/core/providers/providers/opencode/provider.js +143 -0
  64. package/dist/core/providers/providers/opencode/provider.js.map +1 -0
  65. package/dist/core/providers/providers/registry.d.ts +2 -0
  66. package/dist/core/providers/providers/registry.js +17 -0
  67. package/dist/core/providers/providers/registry.js.map +1 -0
  68. package/dist/core/providers/registry.d.ts +2 -1
  69. package/dist/core/providers/registry.js +66 -0
  70. package/dist/core/providers/registry.js.map +1 -1
  71. package/dist/core/providers/types.d.ts +1 -0
  72. package/dist/core/scenarios/application/scenario-runner.service.js +2 -1
  73. package/dist/core/scenarios/application/scenario-runner.service.js.map +1 -1
  74. package/dist/core/sessions/application/session.service.d.ts +0 -2
  75. package/dist/core/sessions/application/session.service.js +27 -73
  76. package/dist/core/sessions/application/session.service.js.map +1 -1
  77. package/dist/core/sessions/domain/session.d.ts +0 -3
  78. package/dist/core/sessions/domain/session.js.map +1 -1
  79. package/dist/core/skills/application/skill.service.js +4 -1
  80. package/dist/core/skills/application/skill.service.js.map +1 -1
  81. package/dist/core/templates/assets/ceo/BOOTSTRAP.md +3 -3
  82. package/dist/core/templates/assets/ceo/ROLE.md +3 -2
  83. package/dist/core/templates/assets/skills/og-boards/SKILL.md +65 -0
  84. package/dist/core/templates/default-templates.d.ts +1 -2
  85. package/dist/core/templates/default-templates.js +13 -7
  86. package/dist/core/templates/default-templates.js.map +1 -1
  87. package/package.json +3 -2
@@ -1,7 +1,17 @@
1
+ import { normalizeAgentId } from "../../domain/agent-id.js";
1
2
  import { createNoopLogger } from "../../logging/index.js";
2
3
  import { executeCommand } from "../command-executor.js";
3
- import { InvalidProviderConfigError, UnsupportedProviderActionError } from "../index.js";
4
+ import { callOpenClawGatewayRpc, resolveGatewayAgentCallTimeoutMs, } from "../openclaw-gateway-rpc.js";
5
+ import { AgentConfigNotFoundError, InvalidAgentConfigError, InvalidProviderConfigError, ProviderCommandNotFoundError, UnsupportedProviderActionError } from "../index.js";
4
6
  const OPENCLAW_PROVIDER_ID = "openclaw";
7
+ const AGENT_CONFIG_FILE_NAME = "config.json";
8
+ const PROVIDER_SESSION_BINDINGS_SCHEMA_VERSION = 1;
9
+ const OPENCLAW_RETRYABLE_FAILURE_MAX_ATTEMPTS = 4;
10
+ const OPENCLAW_RETRY_BACKOFF_BASE_MS = 100;
11
+ const OPENCLAW_RETRY_BACKOFF_MAX_MS = 1_500;
12
+ const OPENCLAW_COMMAND_RESPONSE_TIMEOUT_MS = 30_000;
13
+ const OPENCLAW_COMMAND_RESPONSE_POLL_MS = 250;
14
+ const OPENCLAW_COMMAND_HISTORY_LIMIT = 250;
5
15
  export class ProviderService {
6
16
  fileSystem;
7
17
  pathPort;
@@ -18,27 +28,26 @@ export class ProviderService {
18
28
  }
19
29
  async listProviders() {
20
30
  const registry = await this.getProviderRegistry();
21
- const provider = registry.create(OPENCLAW_PROVIDER_ID);
22
- return [
23
- {
24
- id: provider.id,
25
- displayName: provider.displayName,
26
- kind: provider.kind,
27
- capabilities: provider.capabilities
28
- }
29
- ];
31
+ return registry.listProviders().map((provider) => ({
32
+ id: provider.id,
33
+ displayName: provider.displayName,
34
+ kind: provider.kind,
35
+ capabilities: provider.capabilities
36
+ }));
30
37
  }
31
38
  async getProviderOnboarding(providerId) {
32
- assertOpenClawProviderId(providerId);
39
+ const normalizedProviderId = normalizeProviderId(providerId);
33
40
  const registry = await this.getProviderRegistry();
34
- return registry.getProviderOnboarding(OPENCLAW_PROVIDER_ID);
41
+ return registry.getProviderOnboarding(normalizedProviderId);
35
42
  }
36
43
  async invokeProviderAuth(paths, providerId, options = {}) {
37
- assertOpenClawProviderId(providerId);
44
+ const normalizedProviderId = normalizeProviderId(providerId);
38
45
  const registry = await this.getProviderRegistry();
39
- const provider = registry.create(OPENCLAW_PROVIDER_ID);
40
- const env = await this.resolveProviderEnv(paths, options.env);
41
- this.logger.info("Invoking OpenClaw auth.");
46
+ const provider = registry.create(normalizedProviderId);
47
+ const env = await this.resolveProviderEnv(paths, normalizedProviderId, options.env);
48
+ this.logger.info("Invoking provider auth.", {
49
+ providerId: provider.id
50
+ });
42
51
  return provider.invokeAuth?.({
43
52
  ...options,
44
53
  env
@@ -49,8 +58,8 @@ export class ProviderService {
49
58
  };
50
59
  }
51
60
  async getProviderConfig(paths, providerId) {
52
- assertOpenClawProviderId(providerId);
53
- const configPath = this.getProviderConfigPath(paths);
61
+ const normalizedProviderId = normalizeProviderId(providerId);
62
+ const configPath = this.getProviderConfigPath(paths, normalizedProviderId);
54
63
  const exists = await this.fileSystem.exists(configPath);
55
64
  if (!exists) {
56
65
  return null;
@@ -61,18 +70,21 @@ export class ProviderService {
61
70
  parsed = JSON.parse(raw);
62
71
  }
63
72
  catch {
64
- throw new InvalidProviderConfigError(providerId, configPath);
73
+ throw new InvalidProviderConfigError(normalizedProviderId, configPath);
65
74
  }
66
75
  if (!isProviderStoredConfig(parsed)) {
67
- throw new InvalidProviderConfigError(providerId, configPath, "schema mismatch");
76
+ throw new InvalidProviderConfigError(normalizedProviderId, configPath, "schema mismatch");
77
+ }
78
+ if (parsed.providerId.trim().toLowerCase() !== normalizedProviderId) {
79
+ throw new InvalidProviderConfigError(normalizedProviderId, configPath, `provider id mismatch (found "${parsed.providerId}")`);
68
80
  }
69
81
  return parsed;
70
82
  }
71
83
  async setProviderConfig(paths, providerId, env, options = {}) {
72
- assertOpenClawProviderId(providerId);
73
- const providerDir = this.pathPort.join(paths.providersDir, OPENCLAW_PROVIDER_ID);
74
- const configPath = this.getProviderConfigPath(paths);
75
- const existing = await this.getProviderConfig(paths, OPENCLAW_PROVIDER_ID);
84
+ const normalizedProviderId = normalizeProviderId(providerId);
85
+ const providerDir = this.pathPort.join(paths.providersDir, normalizedProviderId);
86
+ const configPath = this.getProviderConfigPath(paths, normalizedProviderId);
87
+ const existing = await this.getProviderConfig(paths, normalizedProviderId);
76
88
  const replace = options.replace ?? false;
77
89
  const mergedEnv = sanitizeEnvMap({
78
90
  ...(replace ? {} : (existing?.env ?? {})),
@@ -80,7 +92,7 @@ export class ProviderService {
80
92
  });
81
93
  const next = {
82
94
  schemaVersion: 1,
83
- providerId: OPENCLAW_PROVIDER_ID,
95
+ providerId: normalizedProviderId,
84
96
  env: mergedEnv,
85
97
  updatedAt: this.nowIso()
86
98
  };
@@ -89,7 +101,7 @@ export class ProviderService {
89
101
  return next;
90
102
  }
91
103
  async getOpenClawGatewayConfig(paths, inputEnv) {
92
- const env = await this.resolveProviderEnv(paths, inputEnv);
104
+ const env = await this.resolveProviderEnv(paths, OPENCLAW_PROVIDER_ID, inputEnv);
93
105
  const explicitMode = env.OPENGOAT_OPENCLAW_GATEWAY_MODE?.trim().toLowerCase();
94
106
  const parsedArgs = parseOpenClawArguments(env.OPENCLAW_ARGUMENTS ?? "");
95
107
  const mode = explicitMode === "external" || parsedArgs.remoteUrl || parsedArgs.token ? "external" : "local";
@@ -125,116 +137,319 @@ export class ProviderService {
125
137
  await this.setProviderConfig(paths, OPENCLAW_PROVIDER_ID, nextEnv, { replace: true });
126
138
  return this.getOpenClawGatewayConfig(paths);
127
139
  }
128
- async getAgentProvider(_paths, agentId) {
140
+ async getAgentProvider(paths, agentId) {
141
+ const normalizedAgentId = normalizeAgentIdentity(agentId);
142
+ const providerId = await this.resolveAgentProviderId(paths, normalizedAgentId);
129
143
  return {
130
- agentId,
131
- providerId: OPENCLAW_PROVIDER_ID
144
+ agentId: normalizedAgentId,
145
+ providerId,
132
146
  };
133
147
  }
134
- async setAgentProvider(_paths, agentId, providerId) {
135
- assertOpenClawProviderId(providerId);
148
+ async setAgentProvider(paths, agentId, providerId) {
149
+ const normalizedAgentId = normalizeAgentIdentity(agentId);
150
+ const normalizedProviderId = normalizeProviderId(providerId);
151
+ await this.assertProviderExists(normalizedProviderId);
152
+ if (normalizedAgentId === "ceo" &&
153
+ normalizedProviderId !== OPENCLAW_PROVIDER_ID) {
154
+ throw new Error("ceo provider is fixed to \"openclaw\".");
155
+ }
156
+ const configPath = this.pathPort.join(paths.agentsDir, normalizedAgentId, AGENT_CONFIG_FILE_NAME);
157
+ const config = await this.readAgentConfig(configPath, normalizedAgentId);
158
+ const runtime = asRecord(config.runtime);
159
+ const currentProvider = asRecord(runtime.provider);
160
+ runtime.provider = {
161
+ ...currentProvider,
162
+ id: normalizedProviderId,
163
+ };
164
+ if ("adapter" in runtime) {
165
+ delete runtime.adapter;
166
+ }
167
+ config.runtime = runtime;
168
+ await this.fileSystem.writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`);
136
169
  return {
137
- agentId,
138
- providerId: OPENCLAW_PROVIDER_ID
170
+ agentId: normalizedAgentId,
171
+ providerId: normalizedProviderId,
139
172
  };
140
173
  }
141
174
  async invokeAgent(paths, agentId, options, runtimeContext = {}) {
175
+ const normalizedAgentId = normalizeAgentIdentity(agentId);
176
+ const binding = await this.getAgentProvider(paths, normalizedAgentId);
142
177
  const registry = await this.getProviderRegistry();
143
- const provider = registry.create(OPENCLAW_PROVIDER_ID);
178
+ const provider = registry.create(binding.providerId);
144
179
  const mergedSystemPrompt = options.systemPrompt?.trim();
180
+ const env = await this.resolveProviderEnv(paths, provider.id, options.env);
181
+ const providerSessionAlias = options.providerSessionId?.trim();
182
+ const mappedProviderSessionId = await this.resolveProviderSessionIdAlias(paths, provider.id, normalizedAgentId, providerSessionAlias);
145
183
  const invokeOptions = {
146
184
  ...options,
147
185
  systemPrompt: mergedSystemPrompt || undefined,
148
186
  cwd: options.cwd,
149
- env: await this.resolveProviderEnv(paths, options.env),
150
- agent: provider.capabilities.agent ? options.agent || agentId : options.agent
187
+ env,
188
+ providerSessionId: mappedProviderSessionId,
189
+ agent: provider.capabilities.agent
190
+ ? options.agent || normalizedAgentId
191
+ : options.agent
151
192
  };
152
- this.logger.info("Invoking OpenClaw for agent.", {
153
- agentId,
193
+ this.logger.info("Invoking provider for agent.", {
194
+ agentId: normalizedAgentId,
195
+ providerId: provider.id,
154
196
  cwd: invokeOptions.cwd
155
197
  });
156
198
  runtimeContext.hooks?.onInvocationStarted?.({
157
199
  runId: runtimeContext.runId,
158
200
  timestamp: this.nowIso(),
159
201
  step: runtimeContext.step,
160
- agentId,
202
+ agentId: normalizedAgentId,
161
203
  providerId: provider.id
162
204
  });
163
- let result = await provider.invoke(invokeOptions);
164
- result = await this.retryGatewayInvocationOnUvCwdFailure(paths, provider, invokeOptions, result);
205
+ let result;
206
+ try {
207
+ if (provider.id === OPENCLAW_PROVIDER_ID &&
208
+ isOpenClawCommandMessage(invokeOptions.message)) {
209
+ result = await this.invokeOpenClawCommandViaGateway(normalizedAgentId, invokeOptions, env);
210
+ }
211
+ else if (provider.id === OPENCLAW_PROVIDER_ID) {
212
+ result = await this.invokeOpenClawProviderWithRecovery(paths, provider, invokeOptions);
213
+ }
214
+ else {
215
+ result = await provider.invoke(invokeOptions);
216
+ }
217
+ }
218
+ catch (error) {
219
+ if (error instanceof ProviderCommandNotFoundError &&
220
+ provider.id === OPENCLAW_PROVIDER_ID) {
221
+ result = await this.invokeAgentViaGateway(normalizedAgentId, invokeOptions, env);
222
+ }
223
+ else {
224
+ throw error;
225
+ }
226
+ }
227
+ await this.persistProviderSessionIdAlias(paths, provider.id, normalizedAgentId, providerSessionAlias, result.providerSessionId);
165
228
  runtimeContext.hooks?.onInvocationCompleted?.({
166
229
  runId: runtimeContext.runId,
167
230
  timestamp: this.nowIso(),
168
231
  step: runtimeContext.step,
169
- agentId,
232
+ agentId: normalizedAgentId,
170
233
  providerId: provider.id,
171
234
  code: result.code
172
235
  });
173
236
  return {
174
237
  ...result,
175
- agentId,
238
+ agentId: normalizedAgentId,
176
239
  providerId: provider.id
177
240
  };
178
241
  }
179
242
  async createProviderAgent(paths, agentId, options) {
180
- if (options.providerId) {
181
- assertOpenClawProviderId(options.providerId);
182
- }
243
+ const normalizedAgentId = normalizeAgentIdentity(agentId);
244
+ const requestedProviderId = options.providerId?.trim()
245
+ ? normalizeProviderId(options.providerId)
246
+ : OPENCLAW_PROVIDER_ID;
183
247
  const registry = await this.getProviderRegistry();
184
- const provider = registry.create(OPENCLAW_PROVIDER_ID);
248
+ const provider = registry.create(requestedProviderId);
185
249
  if (!provider.capabilities.agentCreate || !provider.createAgent) {
186
250
  throw new UnsupportedProviderActionError(provider.id, "create_agent");
187
251
  }
188
- const result = await provider.createAgent({
189
- agentId,
190
- displayName: options.displayName,
191
- workspaceDir: options.workspaceDir,
192
- internalConfigDir: options.internalConfigDir,
193
- cwd: options.cwd,
194
- env: await this.resolveProviderEnv(paths, options.env),
195
- onStdout: options.onStdout,
196
- onStderr: options.onStderr
197
- });
252
+ const env = await this.resolveProviderEnv(paths, provider.id, options.env);
253
+ let result;
254
+ try {
255
+ result = await provider.createAgent({
256
+ agentId: normalizedAgentId,
257
+ displayName: options.displayName,
258
+ workspaceDir: options.workspaceDir,
259
+ internalConfigDir: options.internalConfigDir,
260
+ cwd: options.cwd,
261
+ env,
262
+ onStdout: options.onStdout,
263
+ onStderr: options.onStderr
264
+ });
265
+ }
266
+ catch (error) {
267
+ if (error instanceof ProviderCommandNotFoundError &&
268
+ provider.id === OPENCLAW_PROVIDER_ID) {
269
+ result = await this.createProviderAgentViaGateway(normalizedAgentId, {
270
+ displayName: options.displayName,
271
+ workspaceDir: options.workspaceDir,
272
+ internalConfigDir: options.internalConfigDir,
273
+ env,
274
+ });
275
+ }
276
+ else {
277
+ throw error;
278
+ }
279
+ }
198
280
  return {
199
281
  ...result,
200
- agentId,
282
+ agentId: normalizedAgentId,
201
283
  providerId: provider.id
202
284
  };
203
285
  }
204
286
  async deleteProviderAgent(paths, agentId, options) {
205
- if (options.providerId) {
206
- assertOpenClawProviderId(options.providerId);
207
- }
287
+ const normalizedAgentId = normalizeAgentIdentity(agentId);
288
+ const requestedProviderId = options.providerId?.trim()
289
+ ? normalizeProviderId(options.providerId)
290
+ : OPENCLAW_PROVIDER_ID;
208
291
  const registry = await this.getProviderRegistry();
209
- const provider = registry.create(OPENCLAW_PROVIDER_ID);
292
+ const provider = registry.create(requestedProviderId);
210
293
  if (!provider.capabilities.agentDelete || !provider.deleteAgent) {
211
294
  throw new UnsupportedProviderActionError(provider.id, "delete_agent");
212
295
  }
213
- const result = await provider.deleteAgent({
214
- agentId,
215
- cwd: options.cwd,
216
- env: await this.resolveProviderEnv(paths, options.env),
217
- onStdout: options.onStdout,
218
- onStderr: options.onStderr
219
- });
296
+ const env = await this.resolveProviderEnv(paths, provider.id, options.env);
297
+ let result;
298
+ try {
299
+ result = await provider.deleteAgent({
300
+ agentId: normalizedAgentId,
301
+ cwd: options.cwd,
302
+ env,
303
+ onStdout: options.onStdout,
304
+ onStderr: options.onStderr
305
+ });
306
+ }
307
+ catch (error) {
308
+ if (error instanceof ProviderCommandNotFoundError &&
309
+ provider.id === OPENCLAW_PROVIDER_ID) {
310
+ result = await this.deleteProviderAgentViaGateway(normalizedAgentId, env);
311
+ }
312
+ else {
313
+ throw error;
314
+ }
315
+ }
220
316
  return {
221
317
  ...result,
222
- agentId,
318
+ agentId: normalizedAgentId,
223
319
  providerId: provider.id
224
320
  };
225
321
  }
226
- async getAgentRuntimeProfile(_paths, agentId) {
322
+ async listOpenClawAgentsViaGateway(paths, inputEnv) {
323
+ const env = await this.resolveProviderEnv(paths, OPENCLAW_PROVIDER_ID, inputEnv);
324
+ const payload = await this.callGatewayMethod(env, "config.get", {});
325
+ const parsed = parseGatewayConfigPayload(payload);
326
+ if (!parsed) {
327
+ return [];
328
+ }
329
+ return readGatewayAgentsList(parsed);
330
+ }
331
+ async getOpenClawSkillsStatusViaGateway(paths, inputEnv) {
332
+ const env = await this.resolveProviderEnv(paths, OPENCLAW_PROVIDER_ID, inputEnv);
333
+ const payload = await this.callGatewayMethod(env, "skills.status", {});
334
+ return asRecord(payload);
335
+ }
336
+ async syncOpenClawAgentExecutionPoliciesViaGateway(paths, rawAgentIds, inputEnv) {
337
+ const env = await this.resolveProviderEnv(paths, OPENCLAW_PROVIDER_ID, inputEnv);
338
+ const warnings = [];
339
+ const normalizedAgentIds = [
340
+ ...new Set(rawAgentIds
341
+ .map((agentId) => normalizeAgentId(agentId))
342
+ .filter((agentId) => Boolean(agentId))),
343
+ ];
344
+ if (normalizedAgentIds.length === 0) {
345
+ return warnings;
346
+ }
347
+ const snapshot = await this.getOpenClawConfigViaGateway(paths, env);
348
+ const root = snapshot.config;
349
+ const agents = asRecord(root.agents);
350
+ const list = Array.isArray(agents.list) ? [...agents.list] : [];
351
+ const indexById = new Map();
352
+ for (let index = 0; index < list.length; index += 1) {
353
+ const entry = asRecord(list[index]);
354
+ const id = normalizeAgentId(String(entry.id ?? ""));
355
+ if (!id || indexById.has(id)) {
356
+ continue;
357
+ }
358
+ indexById.set(id, index);
359
+ }
360
+ let changed = false;
361
+ for (const agentId of normalizedAgentIds) {
362
+ const index = indexById.get(agentId);
363
+ if (index === undefined) {
364
+ warnings.push(`OpenClaw gateway policy sync skipped for "${agentId}" because no agents.list entry was found.`);
365
+ continue;
366
+ }
367
+ const current = asRecord(list[index]);
368
+ const next = {
369
+ ...current,
370
+ };
371
+ if (readAgentSandboxMode(current) !== "off") {
372
+ next.sandbox = {
373
+ ...asRecord(current.sandbox),
374
+ mode: "off",
375
+ };
376
+ changed = true;
377
+ }
378
+ if (!hasAgentToolsAllowAll(current)) {
379
+ next.tools = {
380
+ ...asRecord(current.tools),
381
+ allow: ["*"],
382
+ };
383
+ changed = true;
384
+ }
385
+ list[index] = next;
386
+ }
387
+ if (!changed) {
388
+ return warnings;
389
+ }
390
+ const nextRoot = {
391
+ ...root,
392
+ agents: {
393
+ ...agents,
394
+ list,
395
+ },
396
+ };
397
+ await this.applyOpenClawConfigViaGateway(paths, nextRoot, snapshot.hash, env);
398
+ return warnings;
399
+ }
400
+ async getAgentRuntimeProfile(paths, agentId) {
401
+ const normalizedAgentId = normalizeAgentIdentity(agentId);
402
+ const providerId = await this.resolveAgentProviderId(paths, normalizedAgentId);
227
403
  const registry = await this.getProviderRegistry();
228
- const provider = registry.create(OPENCLAW_PROVIDER_ID);
404
+ const provider = registry.create(providerId);
405
+ const runtimePolicy = registry.getProviderRuntimePolicy(provider.id);
229
406
  return {
230
- agentId,
407
+ agentId: normalizedAgentId,
231
408
  providerId: provider.id,
232
409
  providerKind: provider.kind,
233
- workspaceAccess: "internal"
410
+ workspaceAccess: runtimePolicy.invocation.cwd,
411
+ roleSkillDirectories: [...runtimePolicy.skills.directories],
412
+ roleSkillIds: {
413
+ manager: [...runtimePolicy.skills.roleSkillIds.manager],
414
+ individual: [...runtimePolicy.skills.roleSkillIds.individual],
415
+ },
234
416
  };
235
417
  }
418
+ async listProviderRoleSkillDirectories() {
419
+ const registry = await this.getProviderRegistry();
420
+ const directories = new Set();
421
+ for (const providerId of registry.listProviderIds()) {
422
+ const runtimePolicy = registry.getProviderRuntimePolicy(providerId);
423
+ for (const directory of runtimePolicy.skills.directories) {
424
+ const normalized = directory.trim();
425
+ if (!normalized) {
426
+ continue;
427
+ }
428
+ directories.add(normalized);
429
+ }
430
+ }
431
+ return [...directories].sort((left, right) => left.localeCompare(right));
432
+ }
433
+ async listProviderRoleSkillIds() {
434
+ const registry = await this.getProviderRegistry();
435
+ const roleSkillIds = new Set();
436
+ for (const providerId of registry.listProviderIds()) {
437
+ const runtimePolicy = registry.getProviderRuntimePolicy(providerId);
438
+ for (const skillId of [
439
+ ...runtimePolicy.skills.roleSkillIds.manager,
440
+ ...runtimePolicy.skills.roleSkillIds.individual,
441
+ ]) {
442
+ const normalized = skillId.trim().toLowerCase();
443
+ if (!normalized) {
444
+ continue;
445
+ }
446
+ roleSkillIds.add(normalized);
447
+ }
448
+ }
449
+ return [...roleSkillIds].sort((left, right) => left.localeCompare(right));
450
+ }
236
451
  async restartLocalGateway(paths, inputEnv) {
237
- const env = await this.resolveProviderEnv(paths, inputEnv);
452
+ const env = await this.resolveProviderEnv(paths, OPENCLAW_PROVIDER_ID, inputEnv);
238
453
  const gatewayConfig = await this.getOpenClawGatewayConfig(paths, env);
239
454
  if (gatewayConfig.mode !== "local") {
240
455
  return false;
@@ -263,16 +478,392 @@ export class ProviderService {
263
478
  this.logger.warn("OpenClaw gateway restarted.");
264
479
  return true;
265
480
  }
266
- getProviderConfigPath(paths) {
267
- return this.pathPort.join(paths.providersDir, OPENCLAW_PROVIDER_ID, "config.json");
481
+ async invokeAgentViaGateway(fallbackAgentId, invokeOptions, env) {
482
+ const agentId = (invokeOptions.agent || fallbackAgentId).trim();
483
+ const payload = await this.callGatewayMethod(env, "agent", buildGatewayAgentParams({
484
+ message: invokeOptions.message,
485
+ agentId,
486
+ model: invokeOptions.model,
487
+ providerSessionId: invokeOptions.providerSessionId,
488
+ idempotencyKey: invokeOptions.idempotencyKey,
489
+ }), {
490
+ expectFinal: true,
491
+ timeoutMs: resolveGatewayAgentCallTimeoutMs(),
492
+ });
493
+ const normalized = normalizeGatewayAgentPayload(payload);
494
+ return {
495
+ code: 0,
496
+ stdout: normalized.stdout,
497
+ stderr: "",
498
+ providerSessionId: normalized.providerSessionId,
499
+ };
500
+ }
501
+ async invokeOpenClawCommandViaGateway(fallbackAgentId, invokeOptions, env) {
502
+ const agentId = (invokeOptions.agent || fallbackAgentId).trim();
503
+ const idempotencyKey = invokeOptions.idempotencyKey?.trim() || `opengoat-${Date.now()}-${Math.random()}`;
504
+ const providerSessionId = invokeOptions.providerSessionId?.trim() || idempotencyKey;
505
+ const sessionKey = buildOpenClawSessionKey(agentId, providerSessionId);
506
+ const knownSignatures = await this.captureKnownGatewayAssistantSignatures(env, sessionKey);
507
+ await this.callGatewayMethod(env, "chat.send", {
508
+ sessionKey,
509
+ message: invokeOptions.message,
510
+ idempotencyKey,
511
+ deliver: false,
512
+ }, {
513
+ timeoutMs: resolveGatewayAgentCallTimeoutMs(OPENCLAW_COMMAND_RESPONSE_TIMEOUT_MS),
514
+ });
515
+ const commandResponse = await this.awaitOpenClawProgrammaticCommandResponse({
516
+ env,
517
+ sessionKey,
518
+ knownSignatures,
519
+ timeoutMs: OPENCLAW_COMMAND_RESPONSE_TIMEOUT_MS,
520
+ pollIntervalMs: OPENCLAW_COMMAND_RESPONSE_POLL_MS,
521
+ });
522
+ return {
523
+ code: 0,
524
+ stdout: commandResponse,
525
+ stderr: "",
526
+ providerSessionId,
527
+ };
528
+ }
529
+ async captureKnownGatewayAssistantSignatures(env, sessionKey) {
530
+ const messages = await this.readOpenClawChatHistoryMessages(env, sessionKey);
531
+ const signatures = new Set();
532
+ for (const message of messages) {
533
+ const signature = buildGatewayChatMessageSignature(message);
534
+ if (signature) {
535
+ signatures.add(signature);
536
+ }
537
+ }
538
+ return signatures;
539
+ }
540
+ async readOpenClawChatHistoryMessages(env, sessionKey) {
541
+ const payload = await this.callGatewayMethod(env, "chat.history", {
542
+ sessionKey,
543
+ limit: OPENCLAW_COMMAND_HISTORY_LIMIT,
544
+ }, {
545
+ timeoutMs: resolveGatewayAgentCallTimeoutMs(10_000),
546
+ });
547
+ const messages = asRecord(payload).messages;
548
+ if (!Array.isArray(messages)) {
549
+ return [];
550
+ }
551
+ return messages.map((message) => asRecord(message));
552
+ }
553
+ async awaitOpenClawProgrammaticCommandResponse(params) {
554
+ const deadlineMs = Date.now() + Math.max(0, params.timeoutMs);
555
+ while (Date.now() <= deadlineMs) {
556
+ const messages = await this.readOpenClawChatHistoryMessages(params.env, params.sessionKey);
557
+ const nextText = findNewProgrammaticAssistantText(messages, params.knownSignatures);
558
+ if (nextText) {
559
+ return nextText;
560
+ }
561
+ if (Date.now() >= deadlineMs) {
562
+ break;
563
+ }
564
+ await sleep(params.pollIntervalMs);
565
+ }
566
+ throw new Error(`OpenClaw command timed out waiting for programmatic response for session "${params.sessionKey}".`);
567
+ }
568
+ async createProviderAgentViaGateway(agentId, options) {
569
+ const payload = await this.callGatewayMethod(options.env, "config.get", {});
570
+ const configPayload = asRecord(payload);
571
+ const rawConfig = parseGatewayConfigPayload(configPayload);
572
+ if (!rawConfig) {
573
+ throw new Error("OpenClaw gateway config.get did not return valid JSON.");
574
+ }
575
+ const root = asRecord(rawConfig);
576
+ const agents = asRecord(root.agents);
577
+ const list = Array.isArray(agents.list) ? [...agents.list] : [];
578
+ const normalizedTarget = normalizeAgentId(agentId);
579
+ if (!normalizedTarget) {
580
+ throw new Error("Agent id cannot be empty.");
581
+ }
582
+ const index = list.findIndex((entry) => {
583
+ const id = normalizeAgentId(asRecord(entry).id);
584
+ return id === normalizedTarget;
585
+ });
586
+ const nextEntry = {
587
+ ...(index >= 0 ? asRecord(list[index]) : {}),
588
+ id: normalizedTarget,
589
+ name: options.displayName,
590
+ workspace: options.workspaceDir,
591
+ agentDir: options.internalConfigDir,
592
+ sandbox: {
593
+ mode: "off",
594
+ },
595
+ tools: {
596
+ allow: ["*"],
597
+ },
598
+ };
599
+ if (index >= 0) {
600
+ list[index] = nextEntry;
601
+ }
602
+ else {
603
+ list.push(nextEntry);
604
+ }
605
+ const nextRoot = {
606
+ ...root,
607
+ agents: {
608
+ ...agents,
609
+ list,
610
+ },
611
+ };
612
+ const applyParams = {
613
+ raw: `${JSON.stringify(nextRoot, null, 2)}\n`,
614
+ };
615
+ const hash = configPayload.hash;
616
+ if (typeof hash === "string" && hash.trim().length > 0) {
617
+ applyParams.baseHash = hash.trim();
618
+ }
619
+ await this.callGatewayMethod(options.env, "config.apply", applyParams);
620
+ return {
621
+ code: 0,
622
+ stdout: "created via OpenClaw gateway config.apply",
623
+ stderr: "",
624
+ };
625
+ }
626
+ async deleteProviderAgentViaGateway(agentId, env) {
627
+ const normalizedTarget = normalizeAgentId(agentId);
628
+ if (!normalizedTarget) {
629
+ throw new Error("Agent id cannot be empty.");
630
+ }
631
+ const payload = await this.callGatewayMethod(env, "config.get", {});
632
+ const configPayload = asRecord(payload);
633
+ const rawConfig = parseGatewayConfigPayload(configPayload);
634
+ if (!rawConfig) {
635
+ throw new Error("OpenClaw gateway config.get did not return valid JSON.");
636
+ }
637
+ const root = asRecord(rawConfig);
638
+ const agents = asRecord(root.agents);
639
+ const list = Array.isArray(agents.list) ? [...agents.list] : [];
640
+ const nextList = list.filter((entry) => {
641
+ const id = normalizeAgentId(asRecord(entry).id);
642
+ return id !== normalizedTarget;
643
+ });
644
+ if (nextList.length === list.length) {
645
+ return {
646
+ code: 0,
647
+ stdout: "agent already absent in OpenClaw config",
648
+ stderr: "",
649
+ };
650
+ }
651
+ const nextRoot = {
652
+ ...root,
653
+ agents: {
654
+ ...agents,
655
+ list: nextList,
656
+ },
657
+ };
658
+ const applyParams = {
659
+ raw: `${JSON.stringify(nextRoot, null, 2)}\n`,
660
+ };
661
+ const hash = configPayload.hash;
662
+ if (typeof hash === "string" && hash.trim().length > 0) {
663
+ applyParams.baseHash = hash.trim();
664
+ }
665
+ await this.callGatewayMethod(env, "config.apply", applyParams);
666
+ return {
667
+ code: 0,
668
+ stdout: "deleted via OpenClaw gateway config.apply",
669
+ stderr: "",
670
+ };
671
+ }
672
+ async callGatewayMethod(env, method, params, options = {}) {
673
+ return callOpenClawGatewayRpc({
674
+ env,
675
+ method,
676
+ params,
677
+ options,
678
+ });
268
679
  }
269
- async resolveProviderEnv(paths, inputEnv) {
270
- const config = await this.getProviderConfig(paths, OPENCLAW_PROVIDER_ID);
680
+ async getOpenClawConfigViaGateway(paths, inputEnv) {
681
+ const env = await this.resolveProviderEnv(paths, OPENCLAW_PROVIDER_ID, inputEnv);
682
+ const payload = await this.callGatewayMethod(env, "config.get", {});
683
+ const record = asRecord(payload);
684
+ const config = parseGatewayConfigPayload(record);
685
+ if (!config) {
686
+ throw new Error("OpenClaw gateway config.get did not return valid JSON.");
687
+ }
688
+ const hash = record.hash;
689
+ return {
690
+ config,
691
+ hash: typeof hash === "string" && hash.trim().length > 0
692
+ ? hash.trim()
693
+ : undefined,
694
+ };
695
+ }
696
+ async applyOpenClawConfigViaGateway(paths, config, baseHash, inputEnv) {
697
+ const env = await this.resolveProviderEnv(paths, OPENCLAW_PROVIDER_ID, inputEnv);
698
+ const params = {
699
+ raw: `${JSON.stringify(config, null, 2)}\n`,
700
+ };
701
+ if (typeof baseHash === "string" && baseHash.trim().length > 0) {
702
+ params.baseHash = baseHash.trim();
703
+ }
704
+ await this.callGatewayMethod(env, "config.apply", params);
705
+ }
706
+ getProviderConfigPath(paths, providerId) {
707
+ return this.pathPort.join(paths.providersDir, providerId, "config.json");
708
+ }
709
+ async resolveProviderEnv(paths, providerId, inputEnv) {
710
+ const config = await this.getProviderConfig(paths, providerId);
271
711
  return {
272
712
  ...(config?.env ?? {}),
273
713
  ...(inputEnv ?? process.env)
274
714
  };
275
715
  }
716
+ async resolveAgentProviderId(paths, agentId) {
717
+ if (agentId === "ceo") {
718
+ return OPENCLAW_PROVIDER_ID;
719
+ }
720
+ const configPath = this.pathPort.join(paths.agentsDir, agentId, AGENT_CONFIG_FILE_NAME);
721
+ const config = await this.readAgentConfig(configPath, agentId);
722
+ const runtime = asRecord(config.runtime);
723
+ const provider = asRecord(runtime.provider);
724
+ const explicitProviderId = readOptionalString(provider.id);
725
+ if (explicitProviderId) {
726
+ return this.assertProviderExists(explicitProviderId);
727
+ }
728
+ const legacyProviderId = readOptionalString(runtime.adapter);
729
+ if (legacyProviderId) {
730
+ return this.assertProviderExists(legacyProviderId);
731
+ }
732
+ return OPENCLAW_PROVIDER_ID;
733
+ }
734
+ async assertProviderExists(rawProviderId) {
735
+ const providerId = normalizeProviderId(rawProviderId);
736
+ const registry = await this.getProviderRegistry();
737
+ registry.create(providerId);
738
+ return providerId;
739
+ }
740
+ async readAgentConfig(configPath, agentId) {
741
+ const exists = await this.fileSystem.exists(configPath);
742
+ if (!exists) {
743
+ throw new AgentConfigNotFoundError(agentId);
744
+ }
745
+ const raw = await this.fileSystem.readFile(configPath);
746
+ let parsed;
747
+ try {
748
+ parsed = JSON.parse(raw);
749
+ }
750
+ catch {
751
+ throw new InvalidAgentConfigError(agentId, configPath);
752
+ }
753
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
754
+ throw new InvalidAgentConfigError(agentId, configPath, "schema mismatch");
755
+ }
756
+ return parsed;
757
+ }
758
+ async resolveProviderSessionIdAlias(paths, providerId, agentId, providerSessionAlias) {
759
+ const alias = providerSessionAlias?.trim();
760
+ if (!alias) {
761
+ return undefined;
762
+ }
763
+ if (providerId === OPENCLAW_PROVIDER_ID) {
764
+ return alias;
765
+ }
766
+ const bindings = await this.readProviderSessionBindings(paths, providerId, agentId);
767
+ const mapped = bindings.bindings[alias]?.trim();
768
+ return mapped || undefined;
769
+ }
770
+ async persistProviderSessionIdAlias(paths, providerId, agentId, providerSessionAlias, providerSessionId) {
771
+ if (providerId === OPENCLAW_PROVIDER_ID) {
772
+ return;
773
+ }
774
+ const alias = providerSessionAlias?.trim();
775
+ const resolvedProviderSessionId = providerSessionId?.trim();
776
+ if (!alias || !resolvedProviderSessionId) {
777
+ return;
778
+ }
779
+ const bindings = await this.readProviderSessionBindings(paths, providerId, agentId);
780
+ if (bindings.bindings[alias] === resolvedProviderSessionId) {
781
+ return;
782
+ }
783
+ bindings.bindings[alias] = resolvedProviderSessionId;
784
+ bindings.updatedAt = this.nowIso();
785
+ await this.writeProviderSessionBindings(paths, providerId, agentId, bindings);
786
+ }
787
+ async readProviderSessionBindings(paths, providerId, agentId) {
788
+ const normalizedProviderId = normalizeProviderId(providerId);
789
+ const normalizedAgentId = normalizeAgentIdentity(agentId);
790
+ const bindingsPath = this.getProviderSessionBindingsPath(paths, normalizedProviderId, normalizedAgentId);
791
+ const exists = await this.fileSystem.exists(bindingsPath);
792
+ if (!exists) {
793
+ return createProviderSessionBindingsShape({
794
+ providerId: normalizedProviderId,
795
+ agentId: normalizedAgentId,
796
+ updatedAt: this.nowIso(),
797
+ });
798
+ }
799
+ let parsed;
800
+ try {
801
+ parsed = JSON.parse(await this.fileSystem.readFile(bindingsPath));
802
+ }
803
+ catch {
804
+ return createProviderSessionBindingsShape({
805
+ providerId: normalizedProviderId,
806
+ agentId: normalizedAgentId,
807
+ updatedAt: this.nowIso(),
808
+ });
809
+ }
810
+ if (!isProviderSessionBindingsShape(parsed, normalizedProviderId, normalizedAgentId)) {
811
+ return createProviderSessionBindingsShape({
812
+ providerId: normalizedProviderId,
813
+ agentId: normalizedAgentId,
814
+ updatedAt: this.nowIso(),
815
+ });
816
+ }
817
+ return parsed;
818
+ }
819
+ async writeProviderSessionBindings(paths, providerId, agentId, bindings) {
820
+ const bindingsDir = this.pathPort.join(paths.providersDir, providerId, "sessions");
821
+ const bindingsPath = this.getProviderSessionBindingsPath(paths, providerId, agentId);
822
+ await this.fileSystem.ensureDir(bindingsDir);
823
+ await this.fileSystem.writeFile(bindingsPath, `${JSON.stringify(bindings, null, 2)}\n`);
824
+ }
825
+ async invokeOpenClawProviderWithRecovery(paths, provider, invokeOptions) {
826
+ let attempt = 0;
827
+ while (attempt < OPENCLAW_RETRYABLE_FAILURE_MAX_ATTEMPTS) {
828
+ attempt += 1;
829
+ let result;
830
+ try {
831
+ result = await provider.invoke(invokeOptions);
832
+ result = await this.retryGatewayInvocationOnUvCwdFailure(paths, provider, invokeOptions, result);
833
+ }
834
+ catch (error) {
835
+ if (!isGatewayRetryableLockFailureError(error) || attempt >= OPENCLAW_RETRYABLE_FAILURE_MAX_ATTEMPTS) {
836
+ throw error;
837
+ }
838
+ const delayMs = resolveOpenClawRetryDelayMs(attempt);
839
+ this.logger.warn("OpenClaw invocation failed in locked/in-flight state; retrying.", {
840
+ attempt,
841
+ maxAttempts: OPENCLAW_RETRYABLE_FAILURE_MAX_ATTEMPTS,
842
+ delayMs,
843
+ error: summarizeRetryFailureOutput(error instanceof Error ? error.message : String(error))
844
+ });
845
+ await sleep(delayMs);
846
+ continue;
847
+ }
848
+ if (!isGatewayRetryableLockFailureResult(result) || attempt >= OPENCLAW_RETRYABLE_FAILURE_MAX_ATTEMPTS) {
849
+ return result;
850
+ }
851
+ const delayMs = resolveOpenClawRetryDelayMs(attempt);
852
+ this.logger.warn("OpenClaw invocation returned locked/in-flight result; retrying.", {
853
+ attempt,
854
+ maxAttempts: OPENCLAW_RETRYABLE_FAILURE_MAX_ATTEMPTS,
855
+ delayMs,
856
+ code: result.code,
857
+ stderr: summarizeRetryFailureOutput(result.stderr),
858
+ stdout: summarizeRetryFailureOutput(result.stdout)
859
+ });
860
+ await sleep(delayMs);
861
+ }
862
+ return provider.invoke(invokeOptions);
863
+ }
864
+ getProviderSessionBindingsPath(paths, providerId, agentId) {
865
+ return this.pathPort.join(paths.providersDir, providerId, "sessions", `${agentId}.json`);
866
+ }
276
867
  async retryGatewayInvocationOnUvCwdFailure(paths, provider, invokeOptions, result) {
277
868
  if (!isGatewayUvCwdFailure(result)) {
278
869
  return result;
@@ -347,6 +938,47 @@ function isGatewayUvCwdFailure(result) {
347
938
  const details = `${result.stderr}\n${result.stdout}`.toLowerCase();
348
939
  return details.includes("process.cwd failed") && details.includes("uv_cwd");
349
940
  }
941
+ function isGatewayRetryableLockFailureResult(result) {
942
+ if (result.code === 0) {
943
+ return false;
944
+ }
945
+ return isGatewayRetryableLockFailureText(`${result.stderr}\n${result.stdout}`);
946
+ }
947
+ function isGatewayRetryableLockFailureError(error) {
948
+ if (!(error instanceof Error)) {
949
+ return false;
950
+ }
951
+ return isGatewayRetryableLockFailureText(error.message);
952
+ }
953
+ function isGatewayRetryableLockFailureText(value) {
954
+ const details = value.trim().toLowerCase();
955
+ if (!details) {
956
+ return false;
957
+ }
958
+ return (details.includes("session file locked") ||
959
+ details.includes("timeout waiting for session store lock") ||
960
+ details.includes("in_flight") ||
961
+ details.includes("already in flight") ||
962
+ details.includes("run is already active"));
963
+ }
964
+ function resolveOpenClawRetryDelayMs(attempt) {
965
+ const exponent = Math.max(0, attempt - 1);
966
+ const next = OPENCLAW_RETRY_BACKOFF_BASE_MS * (2 ** exponent);
967
+ return Math.min(OPENCLAW_RETRY_BACKOFF_MAX_MS, next);
968
+ }
969
+ function summarizeRetryFailureOutput(value) {
970
+ const trimmed = value.trim();
971
+ if (!trimmed) {
972
+ return undefined;
973
+ }
974
+ if (trimmed.length <= 240) {
975
+ return trimmed;
976
+ }
977
+ return `${trimmed.slice(0, 237)}...`;
978
+ }
979
+ async function sleep(durationMs) {
980
+ await new Promise((resolve) => setTimeout(resolve, Math.max(0, durationMs)));
981
+ }
350
982
  function extractOpenClawGlobalArgs(raw) {
351
983
  const parts = (raw ?? "")
352
984
  .split(/\s+/)
@@ -369,9 +1001,252 @@ function extractOpenClawGlobalArgs(raw) {
369
1001
  }
370
1002
  return result;
371
1003
  }
372
- function assertOpenClawProviderId(providerId) {
373
- if (providerId.trim().toLowerCase() !== OPENCLAW_PROVIDER_ID) {
374
- throw new Error(`Only \"${OPENCLAW_PROVIDER_ID}\" is supported in this OpenGoat version.`);
1004
+ function buildGatewayAgentParams(options) {
1005
+ const idempotencyKey = options.idempotencyKey?.trim() || `opengoat-${Date.now()}-${Math.random()}`;
1006
+ const params = {
1007
+ message: options.message,
1008
+ agentId: options.agentId,
1009
+ idempotencyKey,
1010
+ };
1011
+ if (options.model?.trim()) {
1012
+ params.model = options.model.trim();
1013
+ }
1014
+ if (options.providerSessionId?.trim()) {
1015
+ params.sessionId = options.providerSessionId.trim();
1016
+ params.sessionKey = buildOpenClawSessionKey(options.agentId, options.providerSessionId.trim());
1017
+ }
1018
+ return params;
1019
+ }
1020
+ function buildOpenClawSessionKey(agentId, providerSessionId) {
1021
+ if (providerSessionId.includes(":")) {
1022
+ return providerSessionId.toLowerCase();
1023
+ }
1024
+ return `agent:${normalizeSessionSegment(agentId) || "main"}:${normalizeSessionSegment(providerSessionId) || "main"}`;
1025
+ }
1026
+ function normalizeSessionSegment(value) {
1027
+ return value
1028
+ .trim()
1029
+ .toLowerCase()
1030
+ .replace(/[^a-z0-9:-]+/g, "-")
1031
+ .replace(/^-+|-+$/g, "");
1032
+ }
1033
+ function isOpenClawCommandMessage(message) {
1034
+ return message.trim().startsWith("/");
1035
+ }
1036
+ function normalizeGatewayAgentPayload(payload) {
1037
+ const record = asRecord(payload);
1038
+ const payloads = Array.isArray(record.payloads) ? record.payloads : [];
1039
+ const chunks = [];
1040
+ for (const payloadEntry of payloads) {
1041
+ const entry = asRecord(payloadEntry);
1042
+ const text = entry.text;
1043
+ if (typeof text === "string" && text.trim().length > 0) {
1044
+ chunks.push(text.trim());
1045
+ }
1046
+ }
1047
+ const sessionId = asRecord(asRecord(record.meta).agentMeta).sessionId;
1048
+ return {
1049
+ stdout: chunks.join("\n\n").trim(),
1050
+ providerSessionId: typeof sessionId === "string" && sessionId.trim().length > 0
1051
+ ? sessionId.trim()
1052
+ : undefined,
1053
+ };
1054
+ }
1055
+ function buildGatewayChatMessageSignature(message) {
1056
+ const role = readOptionalString(message.role);
1057
+ const timestamp = typeof message.timestamp === "number" ? String(message.timestamp) : "";
1058
+ const text = extractGatewayChatText(message);
1059
+ if (!role || !timestamp || !text) {
1060
+ return undefined;
1061
+ }
1062
+ return `${role}|${timestamp}|${text}`;
1063
+ }
1064
+ function findNewProgrammaticAssistantText(messages, knownSignatures) {
1065
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
1066
+ const message = messages[index];
1067
+ if (!message || !isProgrammaticGatewayAssistantMessage(message)) {
1068
+ continue;
1069
+ }
1070
+ const signature = buildGatewayChatMessageSignature(message);
1071
+ if (!signature || knownSignatures.has(signature)) {
1072
+ continue;
1073
+ }
1074
+ const text = extractGatewayChatText(message);
1075
+ if (!text) {
1076
+ continue;
1077
+ }
1078
+ knownSignatures.add(signature);
1079
+ return text;
1080
+ }
1081
+ return undefined;
1082
+ }
1083
+ function isProgrammaticGatewayAssistantMessage(message) {
1084
+ if (readOptionalString(message.role) !== "assistant") {
1085
+ return false;
1086
+ }
1087
+ const provider = readOptionalString(message.provider);
1088
+ const model = readOptionalString(message.model);
1089
+ if (provider === "openclaw" && model === "gateway-injected") {
1090
+ return true;
1091
+ }
1092
+ const usage = asRecord(message.usage);
1093
+ const totalTokens = usage.totalTokens;
1094
+ return typeof totalTokens === "number" && totalTokens === 0;
1095
+ }
1096
+ function extractGatewayChatText(message) {
1097
+ const content = message.content;
1098
+ if (!Array.isArray(content)) {
1099
+ return "";
1100
+ }
1101
+ const lines = [];
1102
+ for (const chunk of content) {
1103
+ const record = asRecord(chunk);
1104
+ if (readOptionalString(record.type) !== "text") {
1105
+ continue;
1106
+ }
1107
+ const text = readOptionalString(record.text);
1108
+ if (text) {
1109
+ lines.push(text);
1110
+ }
1111
+ }
1112
+ return lines.join("\n\n").trim();
1113
+ }
1114
+ function parseGatewayConfigPayload(payload) {
1115
+ const record = asRecord(payload);
1116
+ const raw = record.raw;
1117
+ if (typeof raw !== "string" || raw.trim().length === 0) {
1118
+ return undefined;
1119
+ }
1120
+ const parsed = parseLooseJson(raw);
1121
+ if (!parsed) {
1122
+ return undefined;
1123
+ }
1124
+ return parsed;
1125
+ }
1126
+ function readGatewayAgentsList(config) {
1127
+ const agents = asRecord(config.agents);
1128
+ const list = Array.isArray(agents.list) ? agents.list : [];
1129
+ const entries = [];
1130
+ for (const entry of list) {
1131
+ const record = asRecord(entry);
1132
+ const id = normalizeAgentId(String(record.id ?? ""));
1133
+ if (!id) {
1134
+ continue;
1135
+ }
1136
+ entries.push({
1137
+ id,
1138
+ name: typeof record.name === "string" && record.name.trim().length > 0
1139
+ ? record.name.trim()
1140
+ : undefined,
1141
+ workspace: typeof record.workspace === "string" ? record.workspace : "",
1142
+ agentDir: typeof record.agentDir === "string" ? record.agentDir : "",
1143
+ });
1144
+ }
1145
+ return entries;
1146
+ }
1147
+ function readAgentSandboxMode(entry) {
1148
+ const sandbox = asRecord(entry.sandbox);
1149
+ const mode = sandbox.mode;
1150
+ if (typeof mode !== "string") {
1151
+ return undefined;
1152
+ }
1153
+ const normalized = mode.trim();
1154
+ return normalized.length > 0 ? normalized : undefined;
1155
+ }
1156
+ function hasAgentToolsAllowAll(entry) {
1157
+ const tools = asRecord(entry.tools);
1158
+ const allow = tools.allow;
1159
+ if (!Array.isArray(allow)) {
1160
+ return false;
1161
+ }
1162
+ return allow.some((value) => typeof value === "string" && value.trim() === "*");
1163
+ }
1164
+ function parseLooseJson(raw) {
1165
+ const trimmed = raw.trim();
1166
+ if (!trimmed) {
1167
+ return undefined;
1168
+ }
1169
+ try {
1170
+ const parsed = JSON.parse(trimmed);
1171
+ return asRecord(parsed);
375
1172
  }
1173
+ catch {
1174
+ // continue
1175
+ }
1176
+ const starts = [
1177
+ trimmed.indexOf("{"),
1178
+ trimmed.lastIndexOf("{"),
1179
+ trimmed.indexOf("["),
1180
+ trimmed.lastIndexOf("["),
1181
+ ].filter((value, index, arr) => value >= 0 && arr.indexOf(value) === index);
1182
+ for (const start of starts) {
1183
+ const candidate = trimmed.slice(start).trim();
1184
+ if (!candidate) {
1185
+ continue;
1186
+ }
1187
+ try {
1188
+ const parsed = JSON.parse(candidate);
1189
+ return asRecord(parsed);
1190
+ }
1191
+ catch {
1192
+ // keep trying
1193
+ }
1194
+ }
1195
+ return undefined;
1196
+ }
1197
+ function asRecord(value) {
1198
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1199
+ return {};
1200
+ }
1201
+ return value;
1202
+ }
1203
+ function normalizeProviderId(providerId) {
1204
+ const normalized = providerId.trim().toLowerCase();
1205
+ if (!normalized) {
1206
+ throw new Error("Provider id cannot be empty.");
1207
+ }
1208
+ return normalized;
1209
+ }
1210
+ function normalizeAgentIdentity(agentId) {
1211
+ const normalized = normalizeAgentId(agentId);
1212
+ if (!normalized) {
1213
+ throw new Error("Agent id cannot be empty.");
1214
+ }
1215
+ return normalized;
1216
+ }
1217
+ function readOptionalString(value) {
1218
+ if (typeof value !== "string") {
1219
+ return undefined;
1220
+ }
1221
+ const normalized = value.trim();
1222
+ return normalized.length > 0 ? normalized : undefined;
1223
+ }
1224
+ function isProviderSessionBindingsShape(value, providerId, agentId) {
1225
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1226
+ return false;
1227
+ }
1228
+ const record = value;
1229
+ if (record.schemaVersion !== PROVIDER_SESSION_BINDINGS_SCHEMA_VERSION) {
1230
+ return false;
1231
+ }
1232
+ if (record.providerId !== providerId || record.agentId !== agentId) {
1233
+ return false;
1234
+ }
1235
+ if (typeof record.updatedAt !== "string" || !record.updatedAt.trim()) {
1236
+ return false;
1237
+ }
1238
+ if (!record.bindings || typeof record.bindings !== "object" || Array.isArray(record.bindings)) {
1239
+ return false;
1240
+ }
1241
+ return true;
1242
+ }
1243
+ function createProviderSessionBindingsShape(params) {
1244
+ return {
1245
+ schemaVersion: PROVIDER_SESSION_BINDINGS_SCHEMA_VERSION,
1246
+ providerId: params.providerId,
1247
+ agentId: params.agentId,
1248
+ updatedAt: params.updatedAt,
1249
+ bindings: {},
1250
+ };
376
1251
  }
377
1252
  //# sourceMappingURL=provider.service.js.map