crewx 0.9.0-rc.2 → 0.9.0-rc.21

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 (104) hide show
  1. package/dist/assets/MarketPage-DDPL2d7x.js +36 -0
  2. package/dist/assets/{PromptTab-hdXDbbT6.js → PromptTab-UC3K4qx2.js} +3 -3
  3. package/dist/assets/{arc--CBVVcyH.js → arc-DdP3-fUR.js} +1 -1
  4. package/dist/assets/{architectureDiagram-UYN6MBPD-DUiYwqJn.js → architectureDiagram-UYN6MBPD-D16VON1i.js} +1 -1
  5. package/dist/assets/{blockDiagram-ZHA2E4KO-DV86vAgG.js → blockDiagram-ZHA2E4KO-qs1dIRkl.js} +4 -4
  6. package/dist/assets/{c4Diagram-6F5ED5ID-CvWE7Cx_.js → c4Diagram-6F5ED5ID-BeDy8akQ.js} +1 -1
  7. package/dist/assets/channel-DtrxBSkc.js +1 -0
  8. package/dist/assets/{chunk-5HRBRIJM-DAxM5Ul_.js → chunk-5HRBRIJM-BC1zIb11.js} +1 -1
  9. package/dist/assets/{chunk-7U56Z5CX-BuYaEj8L.js → chunk-7U56Z5CX-BuGil3QD.js} +1 -1
  10. package/dist/assets/{chunk-ASOPGD6M-2Kx0e45C.js → chunk-ASOPGD6M-DmKdDtBQ.js} +1 -1
  11. package/dist/assets/{chunk-KFBOBJHC-CR4QS6Rt.js → chunk-KFBOBJHC-D3BvHLPa.js} +1 -1
  12. package/dist/assets/{chunk-T2TOU4HS-CffcwNHx.js → chunk-T2TOU4HS-B5MoUp-N.js} +1 -1
  13. package/dist/assets/{chunk-TMUBEWPD-BGpYtekN.js → chunk-TMUBEWPD-AvWe_lsV.js} +1 -1
  14. package/dist/assets/classDiagram-LNE6IOMH-BMT5B_JB.js +1 -0
  15. package/dist/assets/classDiagram-v2-MQ7JQ4JX-BMT5B_JB.js +1 -0
  16. package/dist/assets/{dagre-4EVJKHTY-yBWukkrE.js → dagre-4EVJKHTY-B6DewFyM.js} +1 -1
  17. package/dist/assets/{diagram-QW4FP2JN-CIr1PLq3.js → diagram-QW4FP2JN-BTsQ8JSd.js} +1 -1
  18. package/dist/assets/{erDiagram-6RL3IURR-xG7SygyU.js → erDiagram-6RL3IURR-BM5nvgVR.js} +1 -1
  19. package/dist/assets/{flowDiagram-7ASYPVHJ-CKalfOqp.js → flowDiagram-7ASYPVHJ-D1UCCuOG.js} +1 -1
  20. package/dist/assets/{ganttDiagram-NTVNEXSI-Bnx7zrX4.js → ganttDiagram-NTVNEXSI-CNIGs7Cn.js} +1 -1
  21. package/dist/assets/{gitGraph-YCYPL57B-TQlF0fiA.js → gitGraph-YCYPL57B-DSLjx1yQ.js} +1 -1
  22. package/dist/assets/{gitGraphDiagram-NRZ2UAAF-DxlaFkwO.js → gitGraphDiagram-NRZ2UAAF-B11zX1Fa.js} +1 -1
  23. package/dist/assets/{infoDiagram-A4XQUW5V-B1-arkqa.js → infoDiagram-A4XQUW5V-DkjvODYl.js} +1 -1
  24. package/dist/assets/{journeyDiagram-G5WM74LC-Cj439GUX.js → journeyDiagram-G5WM74LC-BkCQPMn6.js} +1 -1
  25. package/dist/assets/{kanban-definition-QRCXZQQD-BvrIwEIc.js → kanban-definition-QRCXZQQD-_OX7pNfJ.js} +1 -1
  26. package/dist/assets/{linear-BKYNK0ri.js → linear-BYjwVAR6.js} +1 -1
  27. package/dist/assets/main-CZdKLdUl.css +10 -0
  28. package/dist/assets/main-CkkZNCy-.js +1561 -0
  29. package/dist/assets/{mindmap-definition-GWI6TPTV-BExfJY0G.js → mindmap-definition-GWI6TPTV-BzYpSnv8.js} +1 -1
  30. package/dist/assets/ort-wasm-simd-threaded.asyncify-DMmc6YqF.wasm +0 -0
  31. package/dist/assets/{pieDiagram-YF2LJOPJ-RPWpvrC8.js → pieDiagram-YF2LJOPJ-CEFg6ao0.js} +1 -1
  32. package/dist/assets/{quadrantDiagram-OS5C2QUG-DmbBzk5Q.js → quadrantDiagram-OS5C2QUG-_5w2x3VV.js} +1 -1
  33. package/dist/assets/{requirementDiagram-MIRIMTAZ-DFQuFXq3.js → requirementDiagram-MIRIMTAZ-B_VQYReZ.js} +1 -1
  34. package/dist/assets/{sankeyDiagram-Y46BX6SQ-p_bw56lJ.js → sankeyDiagram-Y46BX6SQ-CgmOlLhh.js} +1 -1
  35. package/dist/assets/{sequenceDiagram-G6AWOVSC-DTbBkLeD.js → sequenceDiagram-G6AWOVSC-MGVt0Ai2.js} +1 -1
  36. package/dist/assets/{stateDiagram-MAYHULR4-DBvC6UcF.js → stateDiagram-MAYHULR4-DaZjgTu-.js} +1 -1
  37. package/dist/assets/stateDiagram-v2-4JROLMXI-DN1wGYkV.js +1 -0
  38. package/dist/assets/{timeline-definition-U7ZMHBDA-B6utvIg1.js → timeline-definition-U7ZMHBDA-CusetmK6.js} +1 -1
  39. package/dist/assets/transformers.web-BN8Ug43_.js +45 -0
  40. package/dist/assets/{xychartDiagram-6QU3TZC5-BHG0s1Ye.js → xychartDiagram-6QU3TZC5-CTndf-if.js} +1 -1
  41. package/dist/index.html +2 -2
  42. package/dist-server/app.module.js +4 -0
  43. package/dist-server/bootstrap/codex-writable-roots.js +88 -0
  44. package/dist-server/bootstrap/crewx-server.js +10 -2
  45. package/dist-server/common/decorator/skip-request-log.decorator.js +8 -0
  46. package/dist-server/common/interceptor/http-logging.interceptor.js +17 -4
  47. package/dist-server/common/task-log/stdout-json-fallback.js +200 -0
  48. package/dist-server/domain/agent/agent.controller.js +14 -0
  49. package/dist-server/domain/agent/agent.service.js +246 -18
  50. package/dist-server/domain/agent/dto/create-agent.dto.js +12 -0
  51. package/dist-server/domain/agent/dto/remote-provider.dto.js +82 -0
  52. package/dist-server/domain/agent/dto/reorder-agents.dto.js +51 -0
  53. package/dist-server/domain/agent/dto/update-agent.dto.js +3 -1
  54. package/dist-server/domain/agent/provider.controller.js +12 -1
  55. package/dist-server/domain/doc/doc.controller.js +43 -9
  56. package/dist-server/domain/doc/doc.service.js +80 -6
  57. package/dist-server/domain/doc/image-constants.js +11 -1
  58. package/dist-server/domain/events/workspace-events.controller.js +2 -0
  59. package/dist-server/domain/message/dto/send-message.dto.js +49 -1
  60. package/dist-server/domain/message/message.controller.js +1 -1
  61. package/dist-server/domain/message/message.module.js +2 -1
  62. package/dist-server/domain/message/message.service.js +83 -13
  63. package/dist-server/domain/settings/settings.service.js +188 -1
  64. package/dist-server/domain/system/system.controller.js +41 -0
  65. package/dist-server/domain/system/system.module.js +19 -0
  66. package/dist-server/domain/task/task.constants.js +8 -1
  67. package/dist-server/domain/task/task.service.js +59 -67
  68. package/dist-server/domain/thread/dto/list-threads.dto.js +18 -0
  69. package/dist-server/domain/thread/overdrive.types.js +38 -0
  70. package/dist-server/domain/thread/thread.service.js +83 -70
  71. package/dist-server/domain/usage/usage.controller.js +18 -0
  72. package/dist-server/domain/usage/usage.module.js +1 -0
  73. package/dist-server/domain/usage/usage.service.js +246 -4
  74. package/dist-server/domain/usage-report/dto/usage-report.dto.js +2 -0
  75. package/dist-server/domain/usage-report/usage-report.controller.js +72 -0
  76. package/dist-server/domain/usage-report/usage-report.module.js +23 -0
  77. package/dist-server/domain/usage-report/usage-report.service.js +135 -0
  78. package/dist-server/domain/wi/wi.service.js +45 -3
  79. package/dist-server/main.js +1 -0
  80. package/dist-server/repository/repository.module.js +2 -0
  81. package/dist-server/repository/task.repository.js +56 -0
  82. package/dist-server/repository/thread.repository.js +13 -0
  83. package/dist-server/repository/usage-report.repository.js +27 -0
  84. package/package.json +9 -9
  85. package/packages/cli/dist/bootstrap/codex-writable-roots.d.ts +45 -0
  86. package/packages/cli/dist/bootstrap/codex-writable-roots.js +88 -0
  87. package/packages/cli/dist/bootstrap/crewx-cli.js +2 -0
  88. package/packages/cli/dist/commands/execute.js +10 -2
  89. package/packages/cli/dist/commands/parse-common-flags.d.ts +5 -1
  90. package/packages/cli/dist/commands/parse-common-flags.js +9 -2
  91. package/packages/cli/dist/commands/ps.js +51 -2
  92. package/packages/cli/dist/commands/query.js +10 -2
  93. package/packages/cli/dist/commands/restart.js +20 -6
  94. package/packages/cli/dist/commands/write-output.d.ts +3 -0
  95. package/packages/cli/dist/commands/write-output.js +24 -0
  96. package/packages/cli/dist/main.js +7 -0
  97. package/packages/cli/package.json +1 -1
  98. package/dist/assets/MarketPage-T1o_oIrw.js +0 -41
  99. package/dist/assets/channel-7q16D7P9.js +0 -1
  100. package/dist/assets/classDiagram-LNE6IOMH-C6qjk4uc.js +0 -1
  101. package/dist/assets/classDiagram-v2-MQ7JQ4JX-C6qjk4uc.js +0 -1
  102. package/dist/assets/main-C5mwv79r.css +0 -10
  103. package/dist/assets/main-DmvFaz-d.js +0 -1486
  104. package/dist/assets/stateDiagram-v2-4JROLMXI-CCMApx_v.js +0 -1
@@ -10,6 +10,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.AgentService = void 0;
13
+ exports.mergeProviderMetas = mergeProviderMetas;
13
14
  const common_1 = require("@nestjs/common");
14
15
  const fs_1 = require("fs");
15
16
  const path_1 = require("path");
@@ -20,13 +21,48 @@ const template_processor_service_js_1 = require("./template-processor.service.js
20
21
  const crewx_pool_service_js_1 = require("../../modules/crewx-pool.service.js");
21
22
  const workspace_context_store_js_1 = require("../../common/workspace-context.store.js");
22
23
  const limits_provider_js_1 = require("../../common/limits/limits-provider.js");
23
- const PROVIDER_CACHE_TTL_MS = 3_600_000; // 1 hour
24
+ // Assembly cache remote fetch TTL (15m) so KV edits surface promptly
25
+ // (WI-20260702-002). New models appear within ~10m of a registry update.
26
+ const PROVIDER_CACHE_TTL_MS = 600_000; // 10 minutes
27
+ const OPENCODE_MODELS_CACHE_TTL_MS = 300_000; // 5 minutes
28
+ /**
29
+ * Merge ACP + CLI provider metas under normalized ids (acp/x → cli/x).
30
+ *
31
+ * For entries sharing the same normalized id:
32
+ * - models: SDK mergeModels (override-first, catalog fallback)
33
+ * - modes/effort: non-empty wins (ACP dynamic values preferred)
34
+ * - capabilities: existing (first-seen) overrides incoming defaults
35
+ *
36
+ * Pure function — unit-testable without AgentService DI.
37
+ */
38
+ function mergeProviderMetas(metas) {
39
+ const byNormId = new Map();
40
+ for (const meta of metas) {
41
+ const normId = (0, sdk_1.normalizeProviderId)(meta.id);
42
+ const existing = byNormId.get(normId);
43
+ if (!existing) {
44
+ byNormId.set(normId, { ...meta, id: normId });
45
+ }
46
+ else {
47
+ byNormId.set(normId, {
48
+ id: normId,
49
+ name: existing.name || meta.name,
50
+ models: (0, sdk_1.mergeModels)(existing.models, meta.models),
51
+ modes: existing.modes.length > 0 ? existing.modes : meta.modes,
52
+ effort: existing.effort.length > 0 ? existing.effort : meta.effort,
53
+ capabilities: { ...meta.capabilities, ...existing.capabilities },
54
+ });
55
+ }
56
+ }
57
+ return Array.from(byNormId.values());
58
+ }
24
59
  let AgentService = class AgentService {
25
60
  documentService;
26
61
  templateProcessor;
27
62
  limitsProvider;
28
63
  crewxPool;
29
64
  providerMetaCache = null;
65
+ opencodeModelsCache = null;
30
66
  /** Per-workspace agent list cache keyed by configPath. */
31
67
  agentsCache = new Map();
32
68
  constructor(documentService, templateProcessor, limitsProvider, crewxPool) {
@@ -36,30 +72,42 @@ let AgentService = class AgentService {
36
72
  this.crewxPool = crewxPool;
37
73
  }
38
74
  // List agents from the current request workspace's crewx.yaml.
39
- // Uses mtime-based cache: returns cached result when the config file has not
40
- // changed on disk. Never triggers Crewx instance creation.
75
+ // Uses a self-contained mtime+size cache (independent of CrewxPoolService):
76
+ // returns cached result when the config file's stat is unchanged since the
77
+ // cache entry was created. Never triggers Crewx instance creation.
41
78
  getAgents() {
42
79
  try {
43
80
  const configPath = this.resolveCrewxConfigPath();
44
81
  if (!configPath || !(0, fs_1.existsSync)(configPath))
45
82
  return { agents: [] };
46
- const poolKey = process.env.CREWX_CONFIG ? '__crewx_config__' : (0, workspace_context_store_js_1.getWorkspacePath)();
47
- // Return cached result when the pool entry is still fresh.
48
83
  const cached = this.agentsCache.get(configPath);
49
- if (cached && !this.crewxPool.isStale(poolKey, configPath)) {
84
+ const currentStat = this.readFileStat(configPath);
85
+ // Cache hit when unchanged, or when statSync failed (treat as unchanged).
86
+ if (cached && (!currentStat || (currentStat.mtimeMs === cached.fileStat.mtimeMs && currentStat.size === cached.fileStat.size))) {
50
87
  return { agents: cached.agents };
51
88
  }
52
89
  const { config } = this.loadConfig(configPath);
53
90
  const rawProviders = Array.isArray(config.providers) ? config.providers : [];
54
91
  const rawAgents = Array.isArray(config.agents) ? config.agents : [];
55
92
  const agents = rawAgents.map((a) => this.toAgentInfo(a, rawProviders));
56
- this.agentsCache.set(configPath, { agents, poolKey });
93
+ const fileStat = currentStat ?? this.readFileStat(configPath) ?? { mtimeMs: 0, size: 0 };
94
+ this.agentsCache.set(configPath, { agents, fileStat });
57
95
  return { agents };
58
96
  }
59
97
  catch {
60
98
  return { agents: [] };
61
99
  }
62
100
  }
101
+ readFileStat(filePath) {
102
+ try {
103
+ const s = (0, fs_1.statSync)(filePath);
104
+ return { mtimeMs: s.mtimeMs, size: s.size };
105
+ }
106
+ catch {
107
+ // File temporarily unavailable (editor rename, atomic write). Treat as unchanged.
108
+ return null;
109
+ }
110
+ }
63
111
  // Create agent — POST /api/v1/agents
64
112
  createAgent(dto) {
65
113
  if (!dto.id || typeof dto.id !== 'string' || dto.id.trim() === '') {
@@ -81,6 +129,47 @@ let AgentService = class AgentService {
81
129
  if (exists) {
82
130
  throw new common_1.ConflictException(`Agent '${dto.id}' already exists`);
83
131
  }
132
+ // AGTUI002-R: remoteProvider → upsert providers[] with a { type: 'remote' }
133
+ // wrapper entry and force the agent's provider to remote/{id}.
134
+ let providerValue = dto.provider;
135
+ if (dto.remoteProvider) {
136
+ const remoteProvider = dto.remoteProvider;
137
+ this.validateAgentId(remoteProvider.id);
138
+ if (!/^(file|https?):\/\//.test(remoteProvider.location)) {
139
+ throw new common_1.BadRequestException('remoteProvider.location must start with file://, http://, or https://');
140
+ }
141
+ const providers = Array.isArray(config.providers) ? config.providers : [];
142
+ const existingIdx = providers.findIndex((p) => String(p.id || '') === remoteProvider.id);
143
+ const usedByOtherAgent = agents.some((a) => {
144
+ const raw = Array.isArray(a.provider) ? a.provider[0] : a.provider;
145
+ return String(raw || '') === `remote/${remoteProvider.id}`;
146
+ });
147
+ if (existingIdx !== -1) {
148
+ if (String(providers[existingIdx].type || '') !== 'remote') {
149
+ throw new common_1.ConflictException(`Provider '${remoteProvider.id}' already exists with a different type`);
150
+ }
151
+ if (usedByOtherAgent) {
152
+ throw new common_1.ConflictException(`Provider '${remoteProvider.id}' is already used by another agent`);
153
+ }
154
+ }
155
+ const providerEntry = {
156
+ id: remoteProvider.id,
157
+ type: 'remote',
158
+ location: remoteProvider.location,
159
+ external_agent_id: remoteProvider.external_agent_id,
160
+ ...(remoteProvider.display_name !== undefined && { display_name: remoteProvider.display_name }),
161
+ ...(remoteProvider.description !== undefined && { description: remoteProvider.description }),
162
+ ...(remoteProvider.timeout !== undefined && { timeout: remoteProvider.timeout }),
163
+ };
164
+ if (existingIdx !== -1) {
165
+ providers[existingIdx] = providerEntry;
166
+ }
167
+ else {
168
+ providers.push(providerEntry);
169
+ }
170
+ config.providers = providers;
171
+ providerValue = `remote/${remoteProvider.id}`;
172
+ }
84
173
  const normalizedPrompt = this.normalizePrompt(dto.prompt);
85
174
  const inlineFields = {};
86
175
  if (dto.model !== undefined)
@@ -93,16 +182,18 @@ let AgentService = class AgentService {
93
182
  ...(dto.role !== undefined && { role: dto.role }),
94
183
  ...(dto.team !== undefined && { team: dto.team }),
95
184
  ...(dto.description !== undefined && { description: dto.description }),
96
- ...(dto.provider !== undefined && { provider: dto.provider }),
185
+ ...(providerValue !== undefined && { provider: providerValue }),
97
186
  ...(Object.keys(inlineFields).length > 0 && { inline: inlineFields }),
98
187
  ...(dto.options !== undefined && { options: dto.options }),
99
188
  };
189
+ this.canonicalizeAndPruneOptions(newAgent);
100
190
  agents.push(newAgent);
101
191
  config.agents = agents;
102
192
  this.saveConfig(configPath, config, raw);
103
193
  this.agentsCache.delete(configPath);
104
194
  this.crewxPool.invalidate((0, workspace_context_store_js_1.getWorkspacePath)());
105
- return this.toAgentInfo(newAgent);
195
+ const rawProviders = Array.isArray(config.providers) ? config.providers : [];
196
+ return this.toAgentInfo(newAgent, rawProviders);
106
197
  }
107
198
  // Update agent — PUT /api/v1/agents/:id
108
199
  updateAgent(id, dto) {
@@ -170,6 +261,13 @@ let AgentService = class AgentService {
170
261
  const { query: _eq, execute: _ee, ...restExisting } = existingOptions;
171
262
  const mergedQuery = { ...existQuery, ...(dtoQuery ?? {}) };
172
263
  const mergedExecute = { ...existExecute, ...(dtoExecute ?? {}) };
264
+ // overdrive:null tombstone (WI-20260710-001): an explicit null in the
265
+ // patch means "delete this key", not "store a literal null" — the
266
+ // naive spread above would otherwise persist `overdrive: null` as-is.
267
+ if (mergedQuery.overdrive === null)
268
+ delete mergedQuery.overdrive;
269
+ if (mergedExecute.overdrive === null)
270
+ delete mergedExecute.overdrive;
173
271
  existing.options = {
174
272
  ...restExisting,
175
273
  ...restDto,
@@ -177,6 +275,7 @@ let AgentService = class AgentService {
177
275
  ...(Object.keys(mergedExecute).length > 0 && { execute: mergedExecute }),
178
276
  };
179
277
  }
278
+ this.canonicalizeAndPruneOptions(existing);
180
279
  agents[idx] = existing;
181
280
  config.agents = agents;
182
281
  this.saveConfig(configPath, config, raw);
@@ -203,6 +302,91 @@ let AgentService = class AgentService {
203
302
  this.agentsCache.delete(configPath);
204
303
  this.crewxPool.invalidate((0, workspace_context_store_js_1.getWorkspacePath)());
205
304
  }
305
+ // Reorder agents — PATCH /api/v1/agents/reorder
306
+ // Without teamByAgentId, this preserves the original within-team behavior.
307
+ // With teamByAgentId, agentIds represents the full workspace order and the
308
+ // provided team patches are applied before persisting crewx.yaml.
309
+ reorderAgents(dto) {
310
+ const configPath = this.requireConfigPath();
311
+ const { config, raw } = this.loadConfig(configPath);
312
+ const agents = Array.isArray(config.agents) ? config.agents : [];
313
+ const requestedIds = dto.agentIds;
314
+ const requestedIdSet = new Set(requestedIds);
315
+ if (requestedIdSet.size !== requestedIds.length) {
316
+ throw new common_1.BadRequestException('agentIds contains duplicate ids');
317
+ }
318
+ if (dto.teamByAgentId !== undefined) {
319
+ const allAgentIds = new Set(agents.map((a) => String(a.id || '')));
320
+ if (requestedIdSet.size !== allAgentIds.size || ![...requestedIdSet].every((id) => allAgentIds.has(id))) {
321
+ throw new common_1.BadRequestException('agentIds must exactly match the current workspace agent ids');
322
+ }
323
+ const teamByAgentId = dto.teamByAgentId;
324
+ if (teamByAgentId === null || typeof teamByAgentId !== 'object' || Array.isArray(teamByAgentId)) {
325
+ throw new common_1.BadRequestException('teamByAgentId must be an object');
326
+ }
327
+ for (const [agentId, team] of Object.entries(teamByAgentId)) {
328
+ if (!allAgentIds.has(agentId)) {
329
+ throw new common_1.BadRequestException(`teamByAgentId contains unknown agent '${agentId}'`);
330
+ }
331
+ if (team !== null && typeof team !== 'string') {
332
+ throw new common_1.BadRequestException('teamByAgentId values must be strings or null');
333
+ }
334
+ }
335
+ const idToAgent = new Map();
336
+ for (const agent of agents) {
337
+ idToAgent.set(String(agent.id || ''), agent);
338
+ }
339
+ const reorderedAgents = requestedIds.map((id) => {
340
+ const agent = idToAgent.get(id);
341
+ if (Object.prototype.hasOwnProperty.call(teamByAgentId, id)) {
342
+ const team = teamByAgentId[id];
343
+ const normalizedTeam = typeof team === 'string' ? team.trim() : '';
344
+ if (normalizedTeam) {
345
+ agent.team = normalizedTeam;
346
+ }
347
+ else {
348
+ delete agent.team;
349
+ }
350
+ }
351
+ return agent;
352
+ });
353
+ config.agents = reorderedAgents;
354
+ this.saveConfig(configPath, config, raw);
355
+ this.agentsCache.delete(configPath);
356
+ this.crewxPool.invalidate((0, workspace_context_store_js_1.getWorkspacePath)());
357
+ const rawProviders = Array.isArray(config.providers) ? config.providers : [];
358
+ return { agents: reorderedAgents.map((a) => this.toAgentInfo(a, rawProviders)) };
359
+ }
360
+ const normalizedTeam = dto.team ?? '';
361
+ const teamMemberIndices = [];
362
+ const teamMemberIds = new Set();
363
+ agents.forEach((a, idx) => {
364
+ if (String(a.team || '') === normalizedTeam) {
365
+ teamMemberIndices.push(idx);
366
+ teamMemberIds.add(String(a.id || ''));
367
+ }
368
+ });
369
+ if (teamMemberIndices.length === 0) {
370
+ throw new common_1.BadRequestException(`No agents found for team '${normalizedTeam}'`);
371
+ }
372
+ if (requestedIdSet.size !== teamMemberIds.size || ![...requestedIdSet].every((id) => teamMemberIds.has(id))) {
373
+ throw new common_1.BadRequestException("agentIds must exactly match the current team's agent ids");
374
+ }
375
+ const idToAgent = new Map();
376
+ for (const idx of teamMemberIndices) {
377
+ const agent = agents[idx];
378
+ idToAgent.set(String(agent.id || ''), agent);
379
+ }
380
+ requestedIds.forEach((id, i) => {
381
+ agents[teamMemberIndices[i]] = idToAgent.get(id);
382
+ });
383
+ config.agents = agents;
384
+ this.saveConfig(configPath, config, raw);
385
+ this.agentsCache.delete(configPath);
386
+ this.crewxPool.invalidate((0, workspace_context_store_js_1.getWorkspacePath)());
387
+ const rawProviders = Array.isArray(config.providers) ? config.providers : [];
388
+ return { agents: agents.map((a) => this.toAgentInfo(a, rawProviders)) };
389
+ }
206
390
  // Preview prompt — POST /api/v1/agents/prompt/preview
207
391
  async previewPrompt(dto) {
208
392
  // 1. Load documents from crewx.yaml
@@ -239,21 +423,30 @@ let AgentService = class AgentService {
239
423
  throw new common_1.UnprocessableEntityException(`Template compilation error: ${message}`);
240
424
  }
241
425
  }
242
- // PRV.LIST: Get ACP provider metadata with 1-hour cache
426
+ // PRV.LIST: Get provider metadata from the bundled/remote catalog.
243
427
  async getProviderMetas() {
244
428
  const now = Date.now();
245
429
  if (this.providerMetaCache && this.providerMetaCache.expiresAt > now) {
246
430
  return this.providerMetaCache.data;
247
431
  }
248
- const [acp, cli] = await Promise.all([
249
- (0, sdk_1.queryAllAcpProviderMetas)(),
250
- Promise.resolve((0, sdk_1.queryAllCliProviderMetas)()),
251
- ]);
252
- const data = [...acp, ...cli];
432
+ // Refresh the remote provider-models override before assembling metas so
433
+ // KV-registered models surface in this response (WI-20260702-002).
434
+ // Best-effort + TTL-guarded (15m) inside the SDK — safe to await every miss.
435
+ await (0, sdk_1.initModelsRemote)();
436
+ const data = await (0, sdk_1.queryAllCliProviderMetas)();
253
437
  data.sort((a, b) => (0, sdk_1.compareProviders)(a.id, b.id));
254
438
  this.providerMetaCache = { data, expiresAt: now + PROVIDER_CACHE_TTL_MS };
255
439
  return data;
256
440
  }
441
+ async getOpencodeModels() {
442
+ const now = Date.now();
443
+ if (this.opencodeModelsCache && this.opencodeModelsCache.expiresAt > now) {
444
+ return this.opencodeModelsCache.data;
445
+ }
446
+ const data = await (0, sdk_1.queryOpencodeModels)();
447
+ this.opencodeModelsCache = { data, expiresAt: now + OPENCODE_MODELS_CACHE_TTL_MS };
448
+ return data;
449
+ }
257
450
  // --- Private helpers ---
258
451
  requireConfigPath() {
259
452
  const configPath = this.resolveCrewxConfigPath();
@@ -323,15 +516,47 @@ let AgentService = class AgentService {
323
516
  const normalized = prompt.trim();
324
517
  return normalized === '' ? undefined : normalized;
325
518
  }
519
+ /**
520
+ * Canonicalize `agent.options.{query,execute}` mode/effort fields against
521
+ * the provider/model catalog right before the YAML is persisted
522
+ * (WI-20260710-001), and prune any empty `query`/`execute`/`options`
523
+ * containers left behind by that canonicalization or by the
524
+ * `overdrive: null` tombstone. Mutates `agent` in place; unknown option
525
+ * keys are always preserved untouched (see canonicalizeAgentOptions).
526
+ */
527
+ canonicalizeAndPruneOptions(agent) {
528
+ if (agent.options === undefined)
529
+ return;
530
+ const inline = agent.inline;
531
+ const model = inline?.model ?? agent.model;
532
+ const provider = typeof agent.provider === 'string' ? agent.provider : undefined;
533
+ const { options, changed } = (0, sdk_1.canonicalizeAgentOptions)({ provider, model, options: agent.options });
534
+ if (changed) {
535
+ agent.options = options;
536
+ }
537
+ const opts = agent.options;
538
+ if (opts && typeof opts === 'object' && !Array.isArray(opts)) {
539
+ for (const key of ['query', 'execute']) {
540
+ const block = opts[key];
541
+ if (block && typeof block === 'object' && !Array.isArray(block) && Object.keys(block).length === 0) {
542
+ delete opts[key];
543
+ }
544
+ }
545
+ if (Object.keys(opts).length === 0) {
546
+ delete agent.options;
547
+ }
548
+ }
549
+ }
326
550
  validateAgentId(id) {
327
551
  if (!/^[a-zA-Z0-9_-]+$/.test(id)) {
328
552
  throw new common_1.BadRequestException('Invalid agent id format');
329
553
  }
330
554
  }
555
+ // Request workspace path takes priority over CREWX_CONFIG (WI-AGTUI002R-BE):
556
+ // /ws/:slug/agents must read the selected workspace's crewx.yaml, not a
557
+ // globally pinned CREWX_CONFIG file. CREWX_CONFIG is only used as a fallback
558
+ // when the workspace path has no crewx.yaml/yml of its own.
331
559
  resolveCrewxConfigPath() {
332
- const envPath = process.env.CREWX_CONFIG;
333
- if (envPath && (0, fs_1.existsSync)(envPath))
334
- return envPath;
335
560
  const base = (0, workspace_context_store_js_1.getWorkspacePath)();
336
561
  const candidates = [
337
562
  (0, path_1.join)(base, 'crewx.yaml'),
@@ -341,6 +566,9 @@ let AgentService = class AgentService {
341
566
  if ((0, fs_1.existsSync)(p))
342
567
  return p;
343
568
  }
569
+ const envPath = process.env.CREWX_CONFIG;
570
+ if (envPath && (0, fs_1.existsSync)(envPath))
571
+ return envPath;
344
572
  return null;
345
573
  }
346
574
  };
@@ -12,6 +12,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.CreateAgentDto = void 0;
13
13
  const swagger_1 = require("@nestjs/swagger");
14
14
  const class_validator_1 = require("class-validator");
15
+ const class_transformer_1 = require("class-transformer");
16
+ const remote_provider_dto_js_1 = require("./remote-provider.dto.js");
15
17
  class CreateAgentDto {
16
18
  id;
17
19
  name;
@@ -22,6 +24,7 @@ class CreateAgentDto {
22
24
  model;
23
25
  prompt;
24
26
  options;
27
+ remoteProvider;
25
28
  }
26
29
  exports.CreateAgentDto = CreateAgentDto;
27
30
  __decorate([
@@ -78,3 +81,12 @@ __decorate([
78
81
  (0, class_validator_1.IsObject)(),
79
82
  __metadata("design:type", Object)
80
83
  ], CreateAgentDto.prototype, "options", void 0);
84
+ __decorate([
85
+ (0, swagger_1.ApiPropertyOptional)({
86
+ description: 'AGTUI002-R: remote provider wrapper config. When set, providers[] is upserted with a { type: "remote" } entry and the agent provider is forced to remote/{id}, ignoring the provider field above.',
87
+ }),
88
+ (0, class_validator_1.IsOptional)(),
89
+ (0, class_validator_1.ValidateNested)(),
90
+ (0, class_transformer_1.Type)(() => remote_provider_dto_js_1.RemoteProviderDto),
91
+ __metadata("design:type", remote_provider_dto_js_1.RemoteProviderDto)
92
+ ], CreateAgentDto.prototype, "remoteProvider", void 0);
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.RemoteProviderDto = exports.RemoteProviderTimeoutDto = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ const class_validator_1 = require("class-validator");
15
+ const class_transformer_1 = require("class-transformer");
16
+ // AGTUI002-R: per-mode timeout overrides for a remote provider wrapper.
17
+ class RemoteProviderTimeoutDto {
18
+ query;
19
+ execute;
20
+ }
21
+ exports.RemoteProviderTimeoutDto = RemoteProviderTimeoutDto;
22
+ __decorate([
23
+ (0, swagger_1.ApiPropertyOptional)({ description: 'Query mode timeout (ms)' }),
24
+ (0, class_validator_1.IsOptional)(),
25
+ (0, class_validator_1.IsInt)(),
26
+ __metadata("design:type", Number)
27
+ ], RemoteProviderTimeoutDto.prototype, "query", void 0);
28
+ __decorate([
29
+ (0, swagger_1.ApiPropertyOptional)({ description: 'Execute mode timeout (ms)' }),
30
+ (0, class_validator_1.IsOptional)(),
31
+ (0, class_validator_1.IsInt)(),
32
+ __metadata("design:type", Number)
33
+ ], RemoteProviderTimeoutDto.prototype, "execute", void 0);
34
+ // AGTUI002-R: remote provider wrapper config, embedded in CreateAgentDto to
35
+ // create a "dispatch connection" agent that proxies to another workspace's
36
+ // crewx.yaml (file://) or an MCP endpoint (http(s)://).
37
+ class RemoteProviderDto {
38
+ id;
39
+ location;
40
+ external_agent_id;
41
+ display_name;
42
+ description;
43
+ timeout;
44
+ }
45
+ exports.RemoteProviderDto = RemoteProviderDto;
46
+ __decorate([
47
+ (0, swagger_1.ApiProperty)({ description: 'Provider identifier (no slashes), stored under providers[].id' }),
48
+ (0, class_validator_1.IsString)(),
49
+ (0, class_validator_1.IsNotEmpty)(),
50
+ __metadata("design:type", String)
51
+ ], RemoteProviderDto.prototype, "id", void 0);
52
+ __decorate([
53
+ (0, swagger_1.ApiProperty)({ description: 'Target location: file://, http://, or https://' }),
54
+ (0, class_validator_1.IsString)(),
55
+ (0, class_validator_1.IsNotEmpty)(),
56
+ __metadata("design:type", String)
57
+ ], RemoteProviderDto.prototype, "location", void 0);
58
+ __decorate([
59
+ (0, swagger_1.ApiProperty)({ description: 'Agent id on the remote workspace' }),
60
+ (0, class_validator_1.IsString)(),
61
+ (0, class_validator_1.IsNotEmpty)(),
62
+ __metadata("design:type", String)
63
+ ], RemoteProviderDto.prototype, "external_agent_id", void 0);
64
+ __decorate([
65
+ (0, swagger_1.ApiPropertyOptional)({ description: 'Display name shown in UI' }),
66
+ (0, class_validator_1.IsOptional)(),
67
+ (0, class_validator_1.IsString)(),
68
+ __metadata("design:type", String)
69
+ ], RemoteProviderDto.prototype, "display_name", void 0);
70
+ __decorate([
71
+ (0, swagger_1.ApiPropertyOptional)({ description: 'Provider description' }),
72
+ (0, class_validator_1.IsOptional)(),
73
+ (0, class_validator_1.IsString)(),
74
+ __metadata("design:type", String)
75
+ ], RemoteProviderDto.prototype, "description", void 0);
76
+ __decorate([
77
+ (0, swagger_1.ApiPropertyOptional)({ description: 'Per-mode timeout overrides' }),
78
+ (0, class_validator_1.IsOptional)(),
79
+ (0, class_validator_1.ValidateNested)(),
80
+ (0, class_transformer_1.Type)(() => RemoteProviderTimeoutDto),
81
+ __metadata("design:type", RemoteProviderTimeoutDto)
82
+ ], RemoteProviderDto.prototype, "timeout", void 0);
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ReorderAgentsDto = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ const class_validator_1 = require("class-validator");
15
+ class ReorderAgentsDto {
16
+ team;
17
+ agentIds;
18
+ teamByAgentId;
19
+ }
20
+ exports.ReorderAgentsDto = ReorderAgentsDto;
21
+ __decorate([
22
+ (0, swagger_1.ApiPropertyOptional)({
23
+ description: 'Team name to reorder within. null/omitted targets agents without a team.',
24
+ nullable: true,
25
+ }),
26
+ (0, class_validator_1.IsOptional)(),
27
+ (0, class_validator_1.ValidateIf)((o) => o.team !== null),
28
+ (0, class_validator_1.IsString)(),
29
+ __metadata("design:type", Object)
30
+ ], ReorderAgentsDto.prototype, "team", void 0);
31
+ __decorate([
32
+ (0, swagger_1.ApiProperty)({
33
+ description: 'Ordered list of agent ids. In team mode, must match the target team. In full-order mode, must match all agents.',
34
+ type: [String],
35
+ }),
36
+ (0, class_validator_1.IsArray)(),
37
+ (0, class_validator_1.ArrayNotEmpty)(),
38
+ (0, class_validator_1.IsString)({ each: true }),
39
+ __metadata("design:type", Array)
40
+ ], ReorderAgentsDto.prototype, "agentIds", void 0);
41
+ __decorate([
42
+ (0, swagger_1.ApiPropertyOptional)({
43
+ description: 'Optional agent id to target team mapping. When provided, agentIds is treated as the full workspace order.',
44
+ additionalProperties: {
45
+ oneOf: [{ type: 'string' }, { type: 'null' }],
46
+ },
47
+ }),
48
+ (0, class_validator_1.IsOptional)(),
49
+ (0, class_validator_1.IsObject)(),
50
+ __metadata("design:type", Object)
51
+ ], ReorderAgentsDto.prototype, "teamByAgentId", void 0);
@@ -85,7 +85,9 @@ __decorate([
85
85
  __metadata("design:type", AgentSkillsDto)
86
86
  ], UpdateAgentDto.prototype, "skills", void 0);
87
87
  __decorate([
88
- (0, swagger_1.ApiPropertyOptional)({ description: 'CLI options per invocation mode' }),
88
+ (0, swagger_1.ApiPropertyOptional)({
89
+ description: 'CLI options per invocation mode. Set options.query.overdrive / options.execute.overdrive to null to delete the persisted overdrive profile (tombstone, WI-20260710-001).',
90
+ }),
89
91
  (0, class_validator_1.IsOptional)(),
90
92
  (0, class_validator_1.IsObject)(),
91
93
  __metadata("design:type", Object)
@@ -22,15 +22,26 @@ let ProviderController = class ProviderController {
22
22
  const data = await this.agentService.getProviderMetas();
23
23
  return { success: true, data };
24
24
  }
25
+ async getOpencodeModels() {
26
+ const data = await this.agentService.getOpencodeModels();
27
+ return { success: true, data };
28
+ }
25
29
  };
26
30
  exports.ProviderController = ProviderController;
27
31
  __decorate([
28
- (0, swagger_1.ApiOperation)({ operationId: 'PRV.LIST', summary: 'List ACP provider metadata' }),
32
+ (0, swagger_1.ApiOperation)({ operationId: 'PRV.LIST', summary: 'List provider metadata' }),
29
33
  (0, common_1.Get)(),
30
34
  __metadata("design:type", Function),
31
35
  __metadata("design:paramtypes", []),
32
36
  __metadata("design:returntype", Promise)
33
37
  ], ProviderController.prototype, "getProviders", null);
38
+ __decorate([
39
+ (0, swagger_1.ApiOperation)({ operationId: 'PRV.OPENCODE.MODELS', summary: 'List OpenCode runtime models' }),
40
+ (0, common_1.Get)('opencode/models'),
41
+ __metadata("design:type", Function),
42
+ __metadata("design:paramtypes", []),
43
+ __metadata("design:returntype", Promise)
44
+ ], ProviderController.prototype, "getOpencodeModels", null);
34
45
  exports.ProviderController = ProviderController = __decorate([
35
46
  (0, swagger_1.ApiTags)('providers'),
36
47
  (0, common_1.Controller)('ws/:slug/providers'),