@axhub/genie 0.2.11 → 0.2.12

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 (96) hide show
  1. package/dist/api-docs.html +2 -2
  2. package/dist/assets/App-Clb2COtW.js +274 -0
  3. package/dist/assets/ImagePlaygroundPage-DqhMSbM8.js +106 -0
  4. package/dist/assets/ImagePlaygroundPage-MEn3NN80.css +1 -0
  5. package/dist/assets/ReviewApp-CDcLYe-u.js +1 -0
  6. package/dist/assets/{_basePickBy-BDnj7-0Z.js → _basePickBy-jUZsM51q.js} +1 -1
  7. package/dist/assets/{_baseUniq-Bl0JKOyl.js → _baseUniq-BXglE6_v.js} +1 -1
  8. package/dist/assets/{arc-DY-4Kev3.js → arc-D-oFCFBv.js} +1 -1
  9. package/dist/assets/{architectureDiagram-2XIMDMQ5-qw7crNVd.js → architectureDiagram-2XIMDMQ5-DC8bAnQt.js} +1 -1
  10. package/dist/assets/{blockDiagram-WCTKOSBZ-B9xg7ep3.js → blockDiagram-WCTKOSBZ-C4semIRc.js} +1 -1
  11. package/dist/assets/{c4Diagram-IC4MRINW-H9xp3ytb.js → c4Diagram-IC4MRINW-FHj1QO3y.js} +1 -1
  12. package/dist/assets/channel-BF4woPXX.js +1 -0
  13. package/dist/assets/{chunk-4BX2VUAB-B3EVDUxI.js → chunk-4BX2VUAB-D-LjsQ_s.js} +1 -1
  14. package/dist/assets/{chunk-55IACEB6-CGv945ef.js → chunk-55IACEB6-DI3j_d7A.js} +1 -1
  15. package/dist/assets/{chunk-FMBD7UC4-uAT4CKWM.js → chunk-FMBD7UC4-BEVnaLFN.js} +1 -1
  16. package/dist/assets/{chunk-JSJVCQXG-Cbvlpkf7.js → chunk-JSJVCQXG-CSxpcErk.js} +1 -1
  17. package/dist/assets/{chunk-KX2RTZJC-CcqIuGat.js → chunk-KX2RTZJC-BbuhDN4h.js} +1 -1
  18. package/dist/assets/{chunk-NQ4KR5QH-CgrcsRuX.js → chunk-NQ4KR5QH-C3x61XQa.js} +1 -1
  19. package/dist/assets/{chunk-QZHKN3VN-Cx0APOoV.js → chunk-QZHKN3VN-DxWOFtPh.js} +1 -1
  20. package/dist/assets/{chunk-WL4C6EOR-BbZirvBk.js → chunk-WL4C6EOR-Bt2OauD2.js} +1 -1
  21. package/dist/assets/classDiagram-VBA2DB6C-D2kHlnQ7.js +1 -0
  22. package/dist/assets/classDiagram-v2-RAHNMMFH-D2kHlnQ7.js +1 -0
  23. package/dist/assets/clone-CqBvwCJW.js +1 -0
  24. package/dist/assets/{cose-bilkent-S5V4N54A-CrvmGFLD.js → cose-bilkent-S5V4N54A-Dexadrue.js} +1 -1
  25. package/dist/assets/{dagre-KLK3FWXG-C-W6VPjS.js → dagre-KLK3FWXG-F9U4X2xC.js} +1 -1
  26. package/dist/assets/{diagram-E7M64L7V-IP2q3bL0.js → diagram-E7M64L7V-B3V17aH3.js} +1 -1
  27. package/dist/assets/{diagram-IFDJBPK2-CQaL-XyV.js → diagram-IFDJBPK2-CdHAmLL1.js} +1 -1
  28. package/dist/assets/{diagram-P4PSJMXO-BxBLThfv.js → diagram-P4PSJMXO-CrTNfk8K.js} +1 -1
  29. package/dist/assets/{erDiagram-INFDFZHY-Dyl7bJTt.js → erDiagram-INFDFZHY-vDh9SWK9.js} +1 -1
  30. package/dist/assets/{flowDiagram-PKNHOUZH-B7NFMgFK.js → flowDiagram-PKNHOUZH-DpltMg7L.js} +1 -1
  31. package/dist/assets/{ganttDiagram-A5KZAMGK-hReWSDu2.js → ganttDiagram-A5KZAMGK-COTk2xur.js} +1 -1
  32. package/dist/assets/{gitGraphDiagram-K3NZZRJ6-gVgcr0ST.js → gitGraphDiagram-K3NZZRJ6-BNV7bvvj.js} +1 -1
  33. package/dist/assets/{graph-DNDiJhTn.js → graph-Dkeg9oys.js} +1 -1
  34. package/dist/assets/{highlighted-body-TPN3WLV5-DclLmTou.js → highlighted-body-TPN3WLV5-DaiQEBwR.js} +1 -1
  35. package/dist/assets/index-DgGmiqsP.css +1 -0
  36. package/dist/assets/index-DvA901Vs.js +2 -0
  37. package/dist/assets/{infoDiagram-LFFYTUFH-CqQOOzDA.js → infoDiagram-LFFYTUFH-CZioW3Gt.js} +1 -1
  38. package/dist/assets/{ishikawaDiagram-PHBUUO56-CZ0iLiHg.js → ishikawaDiagram-PHBUUO56-BbqR3i1B.js} +1 -1
  39. package/dist/assets/{journeyDiagram-4ABVD52K-DdfYKfNh.js → journeyDiagram-4ABVD52K-wfb-WHzl.js} +1 -1
  40. package/dist/assets/{kanban-definition-K7BYSVSG-C5Vf32u6.js → kanban-definition-K7BYSVSG-B3c4y3VN.js} +1 -1
  41. package/dist/assets/{layout-rvTEu2KS.js → layout-Xr9Z2VGF.js} +1 -1
  42. package/dist/assets/{linear-CD9SiYze.js → linear-JBmzAJtl.js} +1 -1
  43. package/dist/assets/{mermaid-O7DHMXV3-OZ8qWWwa.js → mermaid-O7DHMXV3-fDuyNLKe.js} +230 -222
  44. package/dist/assets/{mindmap-definition-YRQLILUH-CQxrLNVc.js → mindmap-definition-YRQLILUH-B5NTN_jD.js} +1 -1
  45. package/dist/assets/{pieDiagram-SKSYHLDU-XgAUByWg.js → pieDiagram-SKSYHLDU-CuO98GVu.js} +1 -1
  46. package/dist/assets/{quadrantDiagram-337W2JSQ-CH16ls7G.js → quadrantDiagram-337W2JSQ-LL3f4vLf.js} +1 -1
  47. package/dist/assets/{requirementDiagram-Z7DCOOCP-B_kQO06L.js → requirementDiagram-Z7DCOOCP-Di-2O6LH.js} +1 -1
  48. package/dist/assets/{sankeyDiagram-WA2Y5GQK-ofe78CyS.js → sankeyDiagram-WA2Y5GQK-9lHqrXqR.js} +1 -1
  49. package/dist/assets/{sequenceDiagram-2WXFIKYE-Ckbxwny6.js → sequenceDiagram-2WXFIKYE-BQu-SoGr.js} +1 -1
  50. package/dist/assets/{stateDiagram-RAJIS63D-DNtzCk14.js → stateDiagram-RAJIS63D-BUxvd2BC.js} +1 -1
  51. package/dist/assets/stateDiagram-v2-FVOUBMTO-CDVexTiR.js +1 -0
  52. package/dist/assets/{timeline-definition-YZTLITO2-zT6CklKt.js → timeline-definition-YZTLITO2-oP47UEU6.js} +1 -1
  53. package/dist/assets/{treemap-KZPCXAKY-y0U2c3xG.js → treemap-KZPCXAKY-BRjDo2aE.js} +1 -1
  54. package/dist/assets/{vendor-codemirror-CMHSJ_9p.js → vendor-codemirror-BiCeS-y4.js} +1 -1
  55. package/dist/assets/{vendor-react-xmA_f8ig.js → vendor-react-DVlYPmi3.js} +1 -1
  56. package/dist/assets/{vennDiagram-LZ73GAT5-xKj3SjYG.js → vennDiagram-LZ73GAT5-DrRqcDqo.js} +1 -1
  57. package/dist/assets/{xychartDiagram-JWTSCODW-Da_qyEoX.js → xychartDiagram-JWTSCODW-DUXrymAi.js} +1 -1
  58. package/dist/index.html +4 -4
  59. package/package.json +25 -6
  60. package/scripts/refresh-acp-default-capabilities.mjs +160 -0
  61. package/server/acp-runtime/client.js +1137 -181
  62. package/server/acp-runtime/command-overrides.js +48 -0
  63. package/server/acp-runtime/index.js +576 -16
  64. package/server/acp-runtime/registry.js +6 -4
  65. package/server/acp-runtime/session-store.js +235 -92
  66. package/server/database/db.js +12 -3
  67. package/server/external-agent/ws.js +212 -11
  68. package/server/index.js +145 -52
  69. package/server/projects-watcher-config.js +4 -0
  70. package/server/projects.js +466 -125
  71. package/server/routes/cc-connect.js +5 -4
  72. package/server/routes/codex.js +24 -0
  73. package/server/routes/commands.js +144 -1
  74. package/server/routes/runs.js +641 -0
  75. package/server/routes/session-core.js +357 -109
  76. package/server/session-core/eventStore.js +0 -121
  77. package/server/session-core/providerAdapters.js +644 -163
  78. package/server/session-core/providerDiscovery.js +66 -38
  79. package/server/session-core/runRegistry.js +244 -0
  80. package/server/session-core/runtimeState.js +75 -3
  81. package/server/session-core/runtimeWriter.js +132 -10
  82. package/server/utils/codexImagePlayground.js +479 -0
  83. package/server/utils/localTerminal.js +56 -0
  84. package/server/utils/shellCommand.js +70 -0
  85. package/shared/acpCapabilities.js +393 -0
  86. package/shared/acpDefaultCapabilities.generated.json +141 -0
  87. package/shared/conversationEvents.js +425 -121
  88. package/dist/assets/App-VH1wNUHs.js +0 -259
  89. package/dist/assets/ReviewApp-D_9EN4TM.js +0 -1
  90. package/dist/assets/channel-CyNUnRfc.js +0 -1
  91. package/dist/assets/classDiagram-VBA2DB6C-DxBtyz2A.js +0 -1
  92. package/dist/assets/classDiagram-v2-RAHNMMFH-DxBtyz2A.js +0 -1
  93. package/dist/assets/clone-C341l3d0.js +0 -1
  94. package/dist/assets/index-DBkz_W_P.css +0 -1
  95. package/dist/assets/index-DdRyoXKh.js +0 -2
  96. package/dist/assets/stateDiagram-v2-FVOUBMTO-B3VPhiE1.js +0 -1
@@ -0,0 +1,70 @@
1
+ import os from 'os';
2
+
3
+ function quoteForPosix(value) {
4
+ return `'${String(value).replace(/'/g, `'\\''`)}'`;
5
+ }
6
+
7
+ function quoteForPowerShell(value) {
8
+ return `"${String(value).replace(/`/g, '``').replace(/"/g, '`"')}"`;
9
+ }
10
+
11
+ function buildPosixProviderCommand({ provider = 'claude', sessionId = null, hasSession = false } = {}) {
12
+ const normalizedProvider = String(provider || 'claude').trim().toLowerCase();
13
+ const normalizedSessionId = typeof sessionId === 'string' ? sessionId.trim() : '';
14
+ const shouldResume = Boolean(hasSession && normalizedSessionId);
15
+
16
+ switch (normalizedProvider) {
17
+ case 'codex':
18
+ return shouldResume ? `codex resume ${quoteForPosix(normalizedSessionId)}` : 'codex';
19
+ case 'gemini':
20
+ return shouldResume ? `npx -y @google/gemini-cli --resume ${quoteForPosix(normalizedSessionId)}` : 'npx -y @google/gemini-cli';
21
+ case 'opencode':
22
+ return shouldResume ? `opencode --session ${quoteForPosix(normalizedSessionId)}` : 'opencode';
23
+ case 'claude':
24
+ default:
25
+ return shouldResume ? `claude --resume ${quoteForPosix(normalizedSessionId)} || claude` : 'claude';
26
+ }
27
+ }
28
+
29
+ function buildPowerShellProviderCommand({ provider = 'claude', sessionId = null, hasSession = false } = {}) {
30
+ const normalizedProvider = String(provider || 'claude').trim().toLowerCase();
31
+ const normalizedSessionId = typeof sessionId === 'string' ? sessionId.trim() : '';
32
+ const shouldResume = Boolean(hasSession && normalizedSessionId);
33
+
34
+ switch (normalizedProvider) {
35
+ case 'codex':
36
+ return shouldResume ? `codex resume ${quoteForPowerShell(normalizedSessionId)}` : 'codex';
37
+ case 'gemini':
38
+ return shouldResume ? `npx -y @google/gemini-cli --resume ${quoteForPowerShell(normalizedSessionId)}` : 'npx -y @google/gemini-cli';
39
+ case 'opencode':
40
+ return shouldResume ? `opencode --session ${quoteForPowerShell(normalizedSessionId)}` : 'opencode';
41
+ case 'claude':
42
+ default:
43
+ return shouldResume
44
+ ? `claude --resume ${quoteForPowerShell(normalizedSessionId)}; if ($LASTEXITCODE -ne 0) { claude }`
45
+ : 'claude';
46
+ }
47
+ }
48
+
49
+ export function buildShellCommand({
50
+ projectPath,
51
+ provider = 'claude',
52
+ sessionId = null,
53
+ hasSession = false,
54
+ initialCommand = null,
55
+ isPlainShell = false,
56
+ platform = os.platform()
57
+ }) {
58
+ const projectDirectory = String(projectPath || '').trim();
59
+ const isWindows = platform === 'win32';
60
+ const cdCommand = isWindows
61
+ ? `Set-Location -Path ${quoteForPowerShell(projectDirectory)}`
62
+ : `cd ${quoteForPosix(projectDirectory)}`;
63
+ const providerCommand = isPlainShell
64
+ ? String(initialCommand || '')
65
+ : isWindows
66
+ ? buildPowerShellProviderCommand({ provider, sessionId, hasSession })
67
+ : buildPosixProviderCommand({ provider, sessionId, hasSession });
68
+
69
+ return `${cdCommand}; ${providerCommand}`;
70
+ }
@@ -0,0 +1,393 @@
1
+ export const ACP_CAPABILITY_KEYS = ['model', 'mode', 'thought_level'];
2
+ export const ACP_CAPABILITY_CACHE_TTL_MS = 30 * 60 * 1000;
3
+ export const ACP_DEFAULT_CAPABILITY_SCHEMA_VERSION = 1;
4
+
5
+ function cloneJsonValue(value) {
6
+ if (value === undefined) return undefined;
7
+ return JSON.parse(JSON.stringify(value));
8
+ }
9
+
10
+ function normalizeString(value) {
11
+ return typeof value === 'string' ? value.trim() : '';
12
+ }
13
+
14
+ function normalizeAcpCommandKey(command) {
15
+ return normalizeString(command?.name ?? command?.command ?? command?.id)
16
+ .replace(/^\/+/, '')
17
+ .toLowerCase();
18
+ }
19
+
20
+ function normalizeCapabilityOption(option, index = 0) {
21
+ if (option == null) return null;
22
+
23
+ if (typeof option === 'string' || typeof option === 'number' || typeof option === 'boolean') {
24
+ const value = String(option).trim();
25
+ return value ? { value, label: value, description: '', order: index } : null;
26
+ }
27
+
28
+ if (typeof option !== 'object' || Array.isArray(option)) return null;
29
+
30
+ const rawValue = option.value ?? option.id ?? option.modeId ?? option.const ?? option.enum?.[0] ?? option.name;
31
+ const value = normalizeString(rawValue);
32
+ if (!value) return null;
33
+
34
+ return {
35
+ value,
36
+ label: normalizeString(option.label ?? option.title ?? option.name ?? option.description) || value,
37
+ description: normalizeString(option.description),
38
+ ...(option.default === true ? { default: true } : {}),
39
+ order: Number.isInteger(option.order) ? option.order : index
40
+ };
41
+ }
42
+
43
+ function collectSelectOptions(options) {
44
+ if (!Array.isArray(options)) return [];
45
+
46
+ return options.flatMap((option, index) => {
47
+ if (Array.isArray(option?.options)) {
48
+ return option.options.map((item, childIndex) => normalizeCapabilityOption(item, childIndex)).filter(Boolean);
49
+ }
50
+ return normalizeCapabilityOption(option, index) || [];
51
+ });
52
+ }
53
+
54
+ function collectSchemaOptions(schema) {
55
+ if (!schema || typeof schema !== 'object') return [];
56
+
57
+ const direct =
58
+ (Array.isArray(schema.options) && schema.options) ||
59
+ (Array.isArray(schema.enum) && schema.enum) ||
60
+ (Array.isArray(schema.oneOf) && schema.oneOf) ||
61
+ (Array.isArray(schema.anyOf) && schema.anyOf) ||
62
+ [];
63
+
64
+ return collectSelectOptions(direct);
65
+ }
66
+
67
+ function dedupeOptions(options = []) {
68
+ const seen = new Set();
69
+ return options.filter((option) => {
70
+ const value = normalizeString(option?.value);
71
+ if (!value || seen.has(value)) return false;
72
+ seen.add(value);
73
+ return true;
74
+ });
75
+ }
76
+
77
+ export function normalizeAcpConfigOptions(value) {
78
+ const configOptions = Array.isArray(value?.configOptions)
79
+ ? value.configOptions
80
+ : Array.isArray(value)
81
+ ? value
82
+ : [];
83
+
84
+ return configOptions
85
+ .filter((option) => {
86
+ if (!option || typeof option !== 'object') return false;
87
+ return normalizeString(option.key ?? option.id ?? option.category);
88
+ })
89
+ .map((option) => ({
90
+ key: normalizeString(option.key ?? option.id ?? option.category),
91
+ id: normalizeString(option.id ?? option.key),
92
+ category: normalizeString(option.category),
93
+ type: normalizeString(option.type),
94
+ name: normalizeString(option.name),
95
+ description: normalizeString(option.description),
96
+ value: cloneJsonValue(option.value ?? option.currentValue),
97
+ currentValue: cloneJsonValue(option.currentValue),
98
+ options: Array.isArray(option.options) ? cloneJsonValue(option.options) : [],
99
+ schema: option.schema && typeof option.schema === 'object' ? cloneJsonValue(option.schema) : null
100
+ }));
101
+ }
102
+
103
+ export function normalizeAcpModeState(value) {
104
+ if (!value || typeof value !== 'object') return null;
105
+
106
+ const availableModes = Array.isArray(value.availableModes)
107
+ ? value.availableModes
108
+ .map((mode, index) => {
109
+ if (!mode || typeof mode !== 'object') return null;
110
+ const id = normalizeString(mode.id ?? mode.modeId ?? mode.value);
111
+ if (!id) return null;
112
+ return {
113
+ id,
114
+ name: normalizeString(mode.name ?? mode.label ?? id) || id,
115
+ description: mode.description == null ? null : String(mode.description),
116
+ order: index
117
+ };
118
+ })
119
+ .filter(Boolean)
120
+ : [];
121
+ const currentModeId = normalizeString(value.currentModeId ?? value.currentMode?.modeId ?? value.currentMode?.id);
122
+
123
+ if (!availableModes.length && !currentModeId) return null;
124
+
125
+ return {
126
+ availableModes,
127
+ currentModeId: currentModeId || availableModes[0]?.id || null
128
+ };
129
+ }
130
+
131
+ export function normalizeAcpTokenUsage(value) {
132
+ if (!value || typeof value !== 'object') return null;
133
+
134
+ if (value.used == null || (value.total == null && value.size == null)) {
135
+ return null;
136
+ }
137
+
138
+ const used = Number(value.used);
139
+ const total = Number(value.total ?? value.size);
140
+ if (!Number.isFinite(used) || used < 0 || !Number.isFinite(total) || total < 0) {
141
+ return null;
142
+ }
143
+
144
+ const safeUsed = Math.round(used);
145
+ const safeTotal = Math.round(total);
146
+ const percentage = safeTotal > 0
147
+ ? Math.min(100, Math.max(0, Math.round((safeUsed / safeTotal) * 100)))
148
+ : 0;
149
+
150
+ return {
151
+ used: safeUsed,
152
+ total: safeTotal,
153
+ percentage,
154
+ remaining: Math.max(0, safeTotal - safeUsed),
155
+ cost: value.cost && typeof value.cost === 'object' ? cloneJsonValue(value.cost) : null
156
+ };
157
+ }
158
+
159
+ export function normalizeAcpAvailableCommands(value) {
160
+ const availableCommands = Array.isArray(value?.availableCommands)
161
+ ? value.availableCommands
162
+ : Array.isArray(value)
163
+ ? value
164
+ : [];
165
+
166
+ return availableCommands
167
+ .map((command) => {
168
+ if (!command || typeof command !== 'object' || Array.isArray(command)) {
169
+ return null;
170
+ }
171
+
172
+ const name = normalizeString(command.name ?? command.command ?? command.id).replace(/^\/+/, '');
173
+ if (!name) return null;
174
+
175
+ return {
176
+ name,
177
+ description: command.description == null ? '' : String(command.description),
178
+ input: command.input && typeof command.input === 'object' ? cloneJsonValue(command.input) : null,
179
+ source: 'acp'
180
+ };
181
+ })
182
+ .filter(Boolean);
183
+ }
184
+
185
+ function capabilityFromConfigOption(option) {
186
+ const key = normalizeString(option?.category) || normalizeString(option?.key);
187
+ if (!ACP_CAPABILITY_KEYS.includes(key)) return null;
188
+
189
+ const currentValueOption = normalizeCapabilityOption(option.currentValue);
190
+ const valueOption = normalizeCapabilityOption(option.value);
191
+ const options = dedupeOptions([
192
+ ...collectSelectOptions(option.options),
193
+ ...collectSchemaOptions(option.schema),
194
+ ...(currentValueOption ? [currentValueOption] : valueOption ? [valueOption] : [])
195
+ ]);
196
+ const currentValue = currentValueOption?.value || valueOption?.value || options.find((item) => item.default)?.value || '';
197
+
198
+ return {
199
+ key,
200
+ configId: normalizeString(option.id) || normalizeString(option.key) || key,
201
+ configKey: normalizeString(option.key) || normalizeString(option.id) || key,
202
+ category: normalizeString(option.category) || key,
203
+ description: normalizeString(option.description),
204
+ value: option.value === undefined ? null : cloneJsonValue(option.value),
205
+ currentValue,
206
+ options,
207
+ schema: option.schema && typeof option.schema === 'object' ? cloneJsonValue(option.schema) : null
208
+ };
209
+ }
210
+
211
+ function capabilityFromModeState(modeState) {
212
+ const normalized = normalizeAcpModeState(modeState);
213
+ if (!normalized) return null;
214
+
215
+ return {
216
+ key: 'mode',
217
+ description: '',
218
+ value: normalized.currentModeId,
219
+ currentValue: normalized.currentModeId || '',
220
+ options: normalized.availableModes.map((mode, index) => ({
221
+ value: mode.id,
222
+ label: mode.name || mode.id,
223
+ description: mode.description || '',
224
+ order: index
225
+ })),
226
+ modeState: normalized
227
+ };
228
+ }
229
+
230
+ function mergeCapability(previous, next) {
231
+ if (!previous) return next || null;
232
+ if (!next) return previous;
233
+
234
+ const options = Array.isArray(next.options)
235
+ ? dedupeOptions(next.options)
236
+ : dedupeOptions(Array.isArray(previous.options) ? previous.options : []);
237
+
238
+ return {
239
+ ...previous,
240
+ ...next,
241
+ options,
242
+ currentValue: Object.prototype.hasOwnProperty.call(next, 'currentValue')
243
+ ? (next.currentValue || '')
244
+ : (previous.currentValue || '')
245
+ };
246
+ }
247
+
248
+ export function buildAcpCapabilities({
249
+ configOptions = [],
250
+ modeState = null,
251
+ tokenUsage = null,
252
+ availableCommands = [],
253
+ provider = null,
254
+ source = 'session',
255
+ agentVersion = null,
256
+ updatedAt = null
257
+ } = {}) {
258
+ const capabilities = {};
259
+
260
+ for (const option of normalizeAcpConfigOptions(configOptions)) {
261
+ const capability = capabilityFromConfigOption(option);
262
+ if (capability) {
263
+ capabilities[capability.key] = mergeCapability(capabilities[capability.key], capability);
264
+ }
265
+ }
266
+
267
+ const modeCapability = capabilityFromModeState(modeState);
268
+ if (modeCapability) {
269
+ capabilities.mode = mergeCapability(capabilities.mode, modeCapability);
270
+ }
271
+
272
+ return {
273
+ provider: provider ? normalizeString(provider).toLowerCase() : null,
274
+ source,
275
+ updatedAt: updatedAt || new Date().toISOString(),
276
+ ttl: ACP_CAPABILITY_CACHE_TTL_MS,
277
+ agentVersion: agentVersion || null,
278
+ capabilities,
279
+ metadata: {
280
+ tokenUsage: normalizeAcpTokenUsage(tokenUsage),
281
+ availableCommands: normalizeAcpAvailableCommands(availableCommands)
282
+ }
283
+ };
284
+ }
285
+
286
+ function mergeAcpAvailableCommands(previousCommands = [], nextCommands = []) {
287
+ const merged = [];
288
+ const seen = new Set();
289
+
290
+ for (const command of [
291
+ ...normalizeAcpAvailableCommands(nextCommands),
292
+ ...normalizeAcpAvailableCommands(previousCommands)
293
+ ]) {
294
+ const key = normalizeAcpCommandKey(command);
295
+ if (!key || seen.has(key)) continue;
296
+ seen.add(key);
297
+ merged.push(command);
298
+ }
299
+
300
+ return merged;
301
+ }
302
+
303
+ export function mergeAcpCapabilitySnapshots(previous = null, next = null) {
304
+ if (!previous) return next || null;
305
+ if (!next) return previous;
306
+
307
+ const capabilities = { ...(previous.capabilities || {}) };
308
+ for (const key of ACP_CAPABILITY_KEYS) {
309
+ capabilities[key] = mergeCapability(capabilities[key], next.capabilities?.[key]);
310
+ }
311
+
312
+ return {
313
+ ...previous,
314
+ ...next,
315
+ capabilities,
316
+ metadata: {
317
+ ...(previous.metadata || {}),
318
+ ...(next.metadata || {}),
319
+ tokenUsage: next.metadata?.tokenUsage || previous.metadata?.tokenUsage || null,
320
+ availableCommands: mergeAcpAvailableCommands(
321
+ previous.metadata?.availableCommands,
322
+ next.metadata?.availableCommands
323
+ )
324
+ }
325
+ };
326
+ }
327
+
328
+ function normalizeCapabilitySnapshot(provider, snapshot, { source = null, updatedAt = null } = {}) {
329
+ if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) {
330
+ return null;
331
+ }
332
+
333
+ const normalizedProvider = normalizeString(snapshot.provider || provider).toLowerCase();
334
+ if (!normalizedProvider) {
335
+ return null;
336
+ }
337
+
338
+ return {
339
+ ...cloneJsonValue(snapshot),
340
+ provider: normalizedProvider,
341
+ source: source || normalizeString(snapshot.source) || 'generated-default',
342
+ updatedAt: normalizeString(snapshot.updatedAt) || updatedAt || new Date().toISOString(),
343
+ ttl: Number.isFinite(Number(snapshot.ttl)) && Number(snapshot.ttl) > 0
344
+ ? Number(snapshot.ttl)
345
+ : ACP_CAPABILITY_CACHE_TTL_MS,
346
+ agentVersion: snapshot.agentVersion || null,
347
+ capabilities: snapshot.capabilities && typeof snapshot.capabilities === 'object'
348
+ ? cloneJsonValue(snapshot.capabilities)
349
+ : {},
350
+ metadata: snapshot.metadata && typeof snapshot.metadata === 'object'
351
+ ? {
352
+ ...cloneJsonValue(snapshot.metadata),
353
+ tokenUsage: normalizeAcpTokenUsage(snapshot.metadata.tokenUsage),
354
+ availableCommands: normalizeAcpAvailableCommands(snapshot.metadata.availableCommands)
355
+ }
356
+ : {
357
+ tokenUsage: null,
358
+ availableCommands: []
359
+ }
360
+ };
361
+ }
362
+
363
+ export function normalizeAcpDefaultCapabilityRegistry(value = {}) {
364
+ const generatedAt = normalizeString(value?.generatedAt) || new Date().toISOString();
365
+ const providers = {};
366
+ const rawProviders = value?.providers && typeof value.providers === 'object'
367
+ ? value.providers
368
+ : {};
369
+
370
+ for (const [providerName, snapshot] of Object.entries(rawProviders)) {
371
+ const normalized = normalizeCapabilitySnapshot(providerName, snapshot, {
372
+ source: 'generated-default',
373
+ updatedAt: generatedAt
374
+ });
375
+ if (normalized) {
376
+ providers[normalized.provider] = normalized;
377
+ }
378
+ }
379
+
380
+ return {
381
+ schemaVersion: Number(value?.schemaVersion) || ACP_DEFAULT_CAPABILITY_SCHEMA_VERSION,
382
+ generatedAt,
383
+ providers
384
+ };
385
+ }
386
+
387
+ export function getAcpDefaultCapabilitySnapshot(registry, provider) {
388
+ const normalizedRegistry = normalizeAcpDefaultCapabilityRegistry(registry);
389
+ const normalizedProvider = normalizeString(provider).toLowerCase();
390
+ return normalizedProvider
391
+ ? normalizedRegistry.providers[normalizedProvider] || null
392
+ : null;
393
+ }
@@ -0,0 +1,141 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "generatedAt": "2026-05-14T03:19:23.238Z",
4
+ "providers": {
5
+ "codex": {
6
+ "provider": "codex",
7
+ "source": "generated-default",
8
+ "updatedAt": "2026-05-14T03:19:23.238Z",
9
+ "ttl": 1800000,
10
+ "agentVersion": null,
11
+ "capabilities": {
12
+ "model": {
13
+ "key": "model",
14
+ "description": "Choose which model Codex should use",
15
+ "value": "gpt-5.5",
16
+ "currentValue": "gpt-5.5",
17
+ "options": [
18
+ {
19
+ "value": "gpt-5.5",
20
+ "label": "GPT-5.5",
21
+ "description": "Frontier model for complex coding, research, and real-world work.",
22
+ "order": 0
23
+ },
24
+ {
25
+ "value": "gpt-5.4",
26
+ "label": "gpt-5.4",
27
+ "description": "Strong model for everyday coding.",
28
+ "order": 1
29
+ },
30
+ {
31
+ "value": "gpt-5.4-mini",
32
+ "label": "GPT-5.4-Mini",
33
+ "description": "Small, fast, and cost-efficient model for simpler coding tasks.",
34
+ "order": 2
35
+ },
36
+ {
37
+ "value": "gpt-5.3-codex",
38
+ "label": "gpt-5.3-codex",
39
+ "description": "Coding-optimized model.",
40
+ "order": 3
41
+ },
42
+ {
43
+ "value": "gpt-5.2",
44
+ "label": "gpt-5.2",
45
+ "description": "Optimized for professional work and long-running agents.",
46
+ "order": 4
47
+ }
48
+ ],
49
+ "schema": null
50
+ },
51
+ "mode": {
52
+ "key": "mode",
53
+ "description": "Choose an approval and sandboxing preset for your session",
54
+ "value": "auto",
55
+ "currentValue": "auto",
56
+ "options": [
57
+ {
58
+ "value": "read-only",
59
+ "label": "Read Only",
60
+ "description": "Codex can read files in the current workspace. Approval is required to edit files or access the internet.",
61
+ "order": 0
62
+ },
63
+ {
64
+ "value": "auto",
65
+ "label": "Default",
66
+ "description": "Codex can read and edit files in the current workspace, and run commands. Approval is required to access the internet or edit other files. (Identical to Agent mode)",
67
+ "order": 1
68
+ },
69
+ {
70
+ "value": "full-access",
71
+ "label": "Full Access",
72
+ "description": "Codex can edit files outside this workspace and access the internet without asking for approval. Exercise caution when using.",
73
+ "order": 2
74
+ }
75
+ ],
76
+ "schema": null,
77
+ "modeState": {
78
+ "availableModes": [
79
+ {
80
+ "id": "read-only",
81
+ "name": "Read Only",
82
+ "description": "Codex can read files in the current workspace. Approval is required to edit files or access the internet.",
83
+ "order": 0
84
+ },
85
+ {
86
+ "id": "auto",
87
+ "name": "Default",
88
+ "description": "Codex can read and edit files in the current workspace, and run commands. Approval is required to access the internet or edit other files. (Identical to Agent mode)",
89
+ "order": 1
90
+ },
91
+ {
92
+ "id": "full-access",
93
+ "name": "Full Access",
94
+ "description": "Codex can edit files outside this workspace and access the internet without asking for approval. Exercise caution when using.",
95
+ "order": 2
96
+ }
97
+ ],
98
+ "currentModeId": "auto"
99
+ }
100
+ },
101
+ "thought_level": {
102
+ "key": "thought_level",
103
+ "description": "Choose how much reasoning effort the model should use",
104
+ "value": "xhigh",
105
+ "currentValue": "xhigh",
106
+ "options": [
107
+ {
108
+ "value": "low",
109
+ "label": "Low",
110
+ "description": "Fast responses with lighter reasoning",
111
+ "order": 0
112
+ },
113
+ {
114
+ "value": "medium",
115
+ "label": "Medium",
116
+ "description": "Balances speed and reasoning depth for everyday tasks",
117
+ "order": 1
118
+ },
119
+ {
120
+ "value": "high",
121
+ "label": "High",
122
+ "description": "Greater reasoning depth for complex problems",
123
+ "order": 2
124
+ },
125
+ {
126
+ "value": "xhigh",
127
+ "label": "Xhigh",
128
+ "description": "Extra high reasoning depth for complex problems",
129
+ "order": 3
130
+ }
131
+ ],
132
+ "schema": null
133
+ }
134
+ },
135
+ "metadata": {
136
+ "tokenUsage": null,
137
+ "availableCommands": []
138
+ }
139
+ }
140
+ }
141
+ }