chati-dev 4.5.1 → 4.5.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.
- package/framework/config.yaml +2 -2
- package/framework/context/root.md +1 -1
- package/framework/i18n/en.yaml +8 -2
- package/framework/i18n/es.yaml +8 -2
- package/framework/i18n/fr.yaml +8 -2
- package/framework/i18n/pt.yaml +8 -2
- package/framework/manifest.json +12 -12
- package/framework/manifest.sig +1 -1
- package/node_modules/@chati/provider-registry/src/index.js +21 -5
- package/package.json +1 -1
- package/src/installer/core.js +14 -13
- package/src/installer/provider-overlay.js +8 -15
- package/src/installer/templates.js +9 -22
- package/src/installer/validator.js +4 -9
- package/src/installer-v2/catalog-client.js +32 -8
- package/src/installer-v2/index.js +4 -1
- package/src/installer-v2/model-catalog-envelope.json +224 -5
- package/src/installer-v2/model-catalog.json +101 -4
- package/src/installer-v2/model-catalog.sig +1 -1
- package/src/installer-v2/wizard-installation.js +12 -11
- package/src/license/client.js +26 -0
- package/src/orchestrator/runtime-installation-v2.js +23 -4
- package/src/terminal/cli-registry.js +6 -5
- package/src/terminal/prompt-builder.js +2 -2
- package/src/utils/config-parser.js +3 -8
- package/src/wizard/i18n.js +8 -8
- package/src/wizard/index.js +20 -23
- package/src/wizard/questions.js +31 -66
package/framework/config.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Chati.dev System Context
|
|
2
2
|
|
|
3
3
|
## Framework
|
|
4
|
-
- **Version**: 4.5.
|
|
4
|
+
- **Version**: 4.5.2
|
|
5
5
|
- **Agents**: Specialized discovery and planning agents feeding provider-neutral RAIL execution and a sealed release lane
|
|
6
6
|
- **Constitution**: 25 Articles + Preamble
|
|
7
7
|
- **Quality**: 5 pipeline gates + 3-tier verdicts + Fault Vector Protocol (Article XXII)
|
package/framework/i18n/en.yaml
CHANGED
|
@@ -22,8 +22,14 @@ installer:
|
|
|
22
22
|
llm_provider_not_installed: "(CLI not detected)"
|
|
23
23
|
llm_provider_label: "AI Provider"
|
|
24
24
|
ide_selection_title: "Select your IDEs / CLIs (multiple allowed):"
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
installation_mode_title: "Select the installation mode"
|
|
26
|
+
installation_mode_open: "Open"
|
|
27
|
+
installation_mode_open_hint: "Public framework without mandatory company integrations"
|
|
28
|
+
installation_mode_internal: "Internal"
|
|
29
|
+
installation_mode_internal_hint: "Requires an authorized license and enables ClickUp tracking plus internal policies"
|
|
30
|
+
installation_mode_label: "Installation mode"
|
|
31
|
+
model_routing_label: "Model routing"
|
|
32
|
+
model_routing_automatic: "Automatic per task across all enabled models"
|
|
27
33
|
providers_label: "Providers"
|
|
28
34
|
quick_start_switch_hint: "Switch CLIs anytime — your session continues from where you left off"
|
|
29
35
|
created_overlays: "Created provider overlay directories"
|
package/framework/i18n/es.yaml
CHANGED
|
@@ -22,8 +22,14 @@ installer:
|
|
|
22
22
|
llm_provider_not_installed: "(CLI no detectada)"
|
|
23
23
|
llm_provider_label: "Proveedor de IA"
|
|
24
24
|
ide_selection_title: "Seleccione sus IDEs / CLIs (multiples permitidos):"
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
installation_mode_title: "Seleccione el modo de instalacion"
|
|
26
|
+
installation_mode_open: "Open"
|
|
27
|
+
installation_mode_open_hint: "Framework publico sin integraciones corporativas obligatorias"
|
|
28
|
+
installation_mode_internal: "Internal"
|
|
29
|
+
installation_mode_internal_hint: "Requiere una licencia autorizada y activa el tracking en ClickUp y las politicas internas"
|
|
30
|
+
installation_mode_label: "Modo de instalacion"
|
|
31
|
+
model_routing_label: "Enrutamiento de modelos"
|
|
32
|
+
model_routing_automatic: "Automatico por tarea entre todos los modelos habilitados"
|
|
27
33
|
providers_label: "Proveedores"
|
|
28
34
|
quick_start_switch_hint: "Cambie de CLI en cualquier momento — su sesion continua desde donde quedo"
|
|
29
35
|
created_overlays: "Creados directorios de overlay de proveedores"
|
package/framework/i18n/fr.yaml
CHANGED
|
@@ -22,8 +22,14 @@ installer:
|
|
|
22
22
|
llm_provider_not_installed: "(CLI non detecte)"
|
|
23
23
|
llm_provider_label: "Fournisseur IA"
|
|
24
24
|
ide_selection_title: "Selectionnez vos IDEs / CLIs (multiples autorises):"
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
installation_mode_title: "Selectionnez le mode d'installation"
|
|
26
|
+
installation_mode_open: "Open"
|
|
27
|
+
installation_mode_open_hint: "Framework public sans integrations d'entreprise obligatoires"
|
|
28
|
+
installation_mode_internal: "Internal"
|
|
29
|
+
installation_mode_internal_hint: "Necessite une licence autorisee et active le suivi ClickUp et les politiques internes"
|
|
30
|
+
installation_mode_label: "Mode d'installation"
|
|
31
|
+
model_routing_label: "Routage des modeles"
|
|
32
|
+
model_routing_automatic: "Automatique par tache entre tous les modeles actives"
|
|
27
33
|
providers_label: "Fournisseurs"
|
|
28
34
|
quick_start_switch_hint: "Changez de CLI a tout moment — votre session continue la ou vous l'avez laissee"
|
|
29
35
|
created_overlays: "Crees les repertoires d'overlay des fournisseurs"
|
package/framework/i18n/pt.yaml
CHANGED
|
@@ -22,8 +22,14 @@ installer:
|
|
|
22
22
|
llm_provider_not_installed: "(CLI nao detectada)"
|
|
23
23
|
llm_provider_label: "Provedor de IA"
|
|
24
24
|
ide_selection_title: "Selecione suas IDEs / CLIs (multiplos permitidos):"
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
installation_mode_title: "Selecione o modo de instalacao"
|
|
26
|
+
installation_mode_open: "Open"
|
|
27
|
+
installation_mode_open_hint: "Framework publico sem integracoes corporativas obrigatorias"
|
|
28
|
+
installation_mode_internal: "Internal"
|
|
29
|
+
installation_mode_internal_hint: "Exige uma licenca autorizada e ativa o tracking no ClickUp e as politicas internas"
|
|
30
|
+
installation_mode_label: "Modo de instalacao"
|
|
31
|
+
model_routing_label: "Roteamento de modelos"
|
|
32
|
+
model_routing_automatic: "Automatico por task entre todos os modelos habilitados"
|
|
27
33
|
providers_label: "Provedores"
|
|
28
34
|
quick_start_switch_hint: "Troque de CLI a qualquer momento — sua sessao continua de onde parou"
|
|
29
35
|
created_overlays: "Criados diretorios de overlay de provedores"
|
package/framework/manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"createdAt": "2026-08-
|
|
2
|
+
"createdAt": "2026-08-27T07:27:47.925Z",
|
|
3
3
|
"files": {
|
|
4
4
|
"agents/build/dev.md": {
|
|
5
5
|
"hash": "5c8da2eee3357bbe84a6c468c76fe60a0c77357575503c3797aa94128282c565",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"type": "md"
|
|
98
98
|
},
|
|
99
99
|
"config.yaml": {
|
|
100
|
-
"hash": "
|
|
100
|
+
"hash": "f7c77483aeaa44f37d8492d85a376304cb24ec28d91dbed3155f41faa627b7ad",
|
|
101
101
|
"size": 2841,
|
|
102
102
|
"type": "yaml"
|
|
103
103
|
},
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
"type": "md"
|
|
123
123
|
},
|
|
124
124
|
"context/root.md": {
|
|
125
|
-
"hash": "
|
|
125
|
+
"hash": "1ac04cbce1e85009ecdf224cdbee4fe64bcc80232366c2122805e9c9c3191f9a",
|
|
126
126
|
"size": 1217,
|
|
127
127
|
"type": "md"
|
|
128
128
|
},
|
|
@@ -377,23 +377,23 @@
|
|
|
377
377
|
"type": "js"
|
|
378
378
|
},
|
|
379
379
|
"i18n/en.yaml": {
|
|
380
|
-
"hash": "
|
|
381
|
-
"size":
|
|
380
|
+
"hash": "5eb0e9dfa7baec166fc12ca8a1c0e9623c9a023c51c09455b80a6d8df1e0205f",
|
|
381
|
+
"size": 4941,
|
|
382
382
|
"type": "yaml"
|
|
383
383
|
},
|
|
384
384
|
"i18n/es.yaml": {
|
|
385
|
-
"hash": "
|
|
386
|
-
"size":
|
|
385
|
+
"hash": "1ed8c732c13c23e3fc2d5578526c1285a2a1ffc1d79b85d8e12365c3bcbbd882",
|
|
386
|
+
"size": 5225,
|
|
387
387
|
"type": "yaml"
|
|
388
388
|
},
|
|
389
389
|
"i18n/fr.yaml": {
|
|
390
|
-
"hash": "
|
|
391
|
-
"size":
|
|
390
|
+
"hash": "ae88b914ea39d6613879aae3a3fcdf92a771d3df0f207def6cbb2cc76455cfe9",
|
|
391
|
+
"size": 5290,
|
|
392
392
|
"type": "yaml"
|
|
393
393
|
},
|
|
394
394
|
"i18n/pt.yaml": {
|
|
395
|
-
"hash": "
|
|
396
|
-
"size":
|
|
395
|
+
"hash": "9e766ca8c7eba6362816bdc9d1f6162d615f1b5ca1a9b2f75071a53b7ab9f6b3",
|
|
396
|
+
"size": 5163,
|
|
397
397
|
"type": "yaml"
|
|
398
398
|
},
|
|
399
399
|
"intelligence/confidence.yaml": {
|
|
@@ -1347,5 +1347,5 @@
|
|
|
1347
1347
|
"type": "yaml"
|
|
1348
1348
|
}
|
|
1349
1349
|
},
|
|
1350
|
-
"version": "4.5.
|
|
1350
|
+
"version": "4.5.2"
|
|
1351
1351
|
}
|
package/framework/manifest.sig
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
Y5XGGQ07UbiwrVZytFSTJvXNifA8l16S9QI+ret6AVjrYfWvN6E5ChUG0BGKWLsE2wpjVtEW4TVJosYaFDANAg==
|
|
@@ -30,6 +30,7 @@ function readClock(clock) {
|
|
|
30
30
|
export function validateCapabilitySnapshot(snapshot, { clock } = {}) {
|
|
31
31
|
assertObject(snapshot, 'INVALID_CATALOG', 'snapshot');
|
|
32
32
|
assertString(snapshot.snapshot_id, 'INVALID_CATALOG', 'snapshot_id');
|
|
33
|
+
if (snapshot.published_at !== undefined) parseTime(snapshot.published_at, 'INVALID_CATALOG', 'published_at');
|
|
33
34
|
const expiresAt = parseTime(snapshot.expires_at, 'INVALID_CATALOG', 'expires_at');
|
|
34
35
|
if (expiresAt <= readClock(clock)) throw new ContractError('CATALOG_SNAPSHOT_EXPIRED', 'capability snapshot is expired', { snapshot_id: snapshot.snapshot_id });
|
|
35
36
|
if (!Array.isArray(snapshot.models) || snapshot.models.length === 0) throw new ContractError('INVALID_CATALOG', 'models must be a non-empty array');
|
|
@@ -49,6 +50,15 @@ export function validateCapabilitySnapshot(snapshot, { clock } = {}) {
|
|
|
49
50
|
} else if (model.highest_reasoning_configuration !== undefined) {
|
|
50
51
|
assertString(model.highest_reasoning_configuration, 'INVALID_CATALOG', 'model.highest_reasoning_configuration');
|
|
51
52
|
}
|
|
53
|
+
if (model.routing_priority !== undefined) {
|
|
54
|
+
assertObject(model.routing_priority, 'INVALID_CATALOG', 'model.routing_priority');
|
|
55
|
+
for (const [route, priority] of Object.entries(model.routing_priority)) {
|
|
56
|
+
assertString(route, 'INVALID_CATALOG', 'model.routing_priority key');
|
|
57
|
+
if (!Number.isFinite(priority) || priority < 0) throw new ContractError('INVALID_CATALOG', 'model.routing_priority values must be non-negative numbers');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (model.tier !== undefined) assertString(model.tier, 'INVALID_CATALOG', 'model.tier');
|
|
61
|
+
if (model.natural_role !== undefined) assertString(model.natural_role, 'INVALID_CATALOG', 'model.natural_role');
|
|
52
62
|
}
|
|
53
63
|
return Object.freeze(canonicalize(snapshot));
|
|
54
64
|
}
|
|
@@ -91,11 +101,17 @@ export function validateInstallation(installation, snapshot, { bindingMap = DEFA
|
|
|
91
101
|
selectedHarnesses.add(binding.harness_id);
|
|
92
102
|
selectedBindings.set(`${binding.provider_id}:${binding.harness_id}`, binding);
|
|
93
103
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
104
|
+
// v2.1 installations are symmetric across enabled CLIs. Legacy v2 artifacts
|
|
105
|
+
// may retain a primary binding, which remains validated but is never required.
|
|
106
|
+
if (installation.primary_binding !== undefined) {
|
|
107
|
+
assertObject(installation.primary_binding, 'INVALID_INSTALLATION', 'primary_binding');
|
|
108
|
+
const primaryKey = `${installation.primary_binding.provider_id}:${installation.primary_binding.harness_id}`;
|
|
109
|
+
if (!selectedBindings.has(primaryKey)) throw new ContractError('PRIMARY_BINDING_NOT_SELECTED', 'primary_binding must be selected');
|
|
110
|
+
if (installation.primary_harness !== undefined && installation.primary_harness !== installation.primary_binding.harness_id) {
|
|
111
|
+
throw new ContractError('PRIMARY_HARNESS_MISMATCH', 'primary_harness must match primary_binding.harness_id');
|
|
112
|
+
}
|
|
113
|
+
} else if (installation.primary_harness !== undefined) {
|
|
114
|
+
throw new ContractError('PRIMARY_BINDING_MISSING', 'legacy primary_harness requires primary_binding');
|
|
99
115
|
}
|
|
100
116
|
if (installation.profile === 'focus-ai-internal' && installation.external_integrations?.clickup !== 'required') {
|
|
101
117
|
throw new ContractError('PROFILE_DEPENDENCY_MISSING', 'focus-ai-internal requires ClickUp');
|
package/package.json
CHANGED
package/src/installer/core.js
CHANGED
|
@@ -23,7 +23,7 @@ const FRAMEWORK_SOURCE = existsSync(BUNDLED_SOURCE) ? BUNDLED_SOURCE : MONOREPO_
|
|
|
23
23
|
* Install Chati.dev framework into target directory
|
|
24
24
|
*/
|
|
25
25
|
export async function installFramework(config) {
|
|
26
|
-
const { targetDir, projectType, language, selectedIDEs, selectedMCPs, projectName,
|
|
26
|
+
const { targetDir, projectType, language, selectedIDEs, selectedMCPs, projectName, allProviders } = config;
|
|
27
27
|
// Always derive version from package.json (single source of truth)
|
|
28
28
|
let { version } = config;
|
|
29
29
|
if (!version) {
|
|
@@ -55,7 +55,7 @@ export async function installFramework(config) {
|
|
|
55
55
|
if (!existsSync(sessionPath)) {
|
|
56
56
|
writeFileSync(
|
|
57
57
|
sessionPath,
|
|
58
|
-
generateSessionYaml({ projectName, projectType, language, selectedIDEs, selectedMCPs,
|
|
58
|
+
generateSessionYaml({ projectName, projectType, language, selectedIDEs, selectedMCPs, allProviders }),
|
|
59
59
|
'utf-8'
|
|
60
60
|
);
|
|
61
61
|
}
|
|
@@ -114,12 +114,13 @@ export async function installFramework(config) {
|
|
|
114
114
|
createDir(join(memoriesBase, dir));
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
// Copy framework
|
|
118
|
-
|
|
117
|
+
// Copy the canonical framework as a provider-neutral fallback. Every enabled
|
|
118
|
+
// CLI receives its own explicit adapted overlay below.
|
|
119
|
+
copyFrameworkFiles(frameworkDir, 'claude');
|
|
119
120
|
|
|
120
|
-
// Generate provider overlays
|
|
121
|
-
if (allProviders && allProviders.length >
|
|
122
|
-
generateProviderOverlays(targetDir, FRAMEWORK_SOURCE,
|
|
121
|
+
// Generate symmetric provider overlays. No CLI is permanently primary.
|
|
122
|
+
if (allProviders && allProviders.length > 0) {
|
|
123
|
+
generateProviderOverlays(targetDir, FRAMEWORK_SOURCE, allProviders);
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
// Bundle the CLI source into the framework dir so chati-router.js can run
|
|
@@ -159,19 +160,19 @@ export async function installFramework(config) {
|
|
|
159
160
|
// Write config.yaml
|
|
160
161
|
writeFileSync(
|
|
161
162
|
join(frameworkDir, 'config.yaml'),
|
|
162
|
-
generateConfigYaml({ version, projectType, language, selectedIDEs,
|
|
163
|
+
generateConfigYaml({ version, projectType, language, selectedIDEs, allProviders }),
|
|
163
164
|
'utf-8'
|
|
164
165
|
);
|
|
165
166
|
|
|
166
|
-
// 3. Configure
|
|
167
|
-
|
|
167
|
+
// 3. Configure every CLI against its own overlay. The CLI that opens a
|
|
168
|
+
// session becomes that session's orchestrator.
|
|
168
169
|
for (const ideKey of selectedIDEs) {
|
|
169
170
|
const ideProvider = IDE_TO_PROVIDER[ideKey];
|
|
170
|
-
const
|
|
171
|
-
const orchestratorPath =
|
|
171
|
+
const hasProviderOverlay = ideProvider && allProviders?.includes(ideProvider);
|
|
172
|
+
const orchestratorPath = hasProviderOverlay
|
|
172
173
|
? `.chati.dev/.adapted/${ideProvider}/orchestrator/chati.md`
|
|
173
174
|
: '.chati.dev/orchestrator/chati.md';
|
|
174
|
-
await configureIDE(targetDir, ideKey, selectedMCPs, { orchestratorPath, isSecondary, providerName: ideProvider });
|
|
175
|
+
await configureIDE(targetDir, ideKey, selectedMCPs, { orchestratorPath, isSecondary: false, providerName: ideProvider });
|
|
175
176
|
}
|
|
176
177
|
|
|
177
178
|
const hasClaude = selectedIDEs.includes('claude-code');
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* @fileoverview Provider Overlay Generator for multi-CLI continuity.
|
|
3
3
|
*
|
|
4
4
|
* When multiple CLI providers are selected (e.g. Claude Code + Gemini CLI),
|
|
5
|
-
* the
|
|
6
|
-
*
|
|
5
|
+
* the provider-neutral framework lives in chati.dev/ (main). For each
|
|
6
|
+
* enabled provider, this module generates an overlay directory at
|
|
7
7
|
* chati.dev/.adapted/<provider>/ with the ADAPTABLE_FILES adapted for
|
|
8
8
|
* that provider. Each CLI reads only its own files — zero runtime translation.
|
|
9
9
|
*
|
|
@@ -15,31 +15,24 @@ import { join, dirname } from 'path';
|
|
|
15
15
|
import { adaptFrameworkFile, ADAPTABLE_FILES } from '../config/framework-adapter.js';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* Generate adapted framework overlays for
|
|
18
|
+
* Generate adapted framework overlays for all enabled providers.
|
|
19
19
|
*
|
|
20
|
-
* For each
|
|
20
|
+
* For each CLI provider, creates chati.dev/.adapted/<provider>/
|
|
21
21
|
* with provider-adapted copies of all ADAPTABLE_FILES.
|
|
22
22
|
*
|
|
23
|
-
* Reads
|
|
24
|
-
* each
|
|
25
|
-
* primary provider is not Claude.
|
|
23
|
+
* Reads canonical source content from frameworkSource, then adapts it for
|
|
24
|
+
* each enabled provider. Every CLI resolves its own overlay symmetrically.
|
|
26
25
|
*
|
|
27
26
|
* @param {string} targetDir - Project root directory
|
|
28
27
|
* @param {string} frameworkSource - Source directory with canonical framework files
|
|
29
|
-
* @param {string} primaryProvider - Primary provider name ('claude', 'gemini', 'codex')
|
|
30
28
|
* @param {string[]} allProviders - All CLI provider names
|
|
31
29
|
* @returns {{ generated: string[], skipped: string[] }}
|
|
32
30
|
*/
|
|
33
|
-
export function generateProviderOverlays(targetDir, frameworkSource,
|
|
31
|
+
export function generateProviderOverlays(targetDir, frameworkSource, allProviders) {
|
|
34
32
|
const result = { generated: [], skipped: [] };
|
|
35
33
|
const frameworkDir = join(targetDir, '.chati.dev');
|
|
36
34
|
|
|
37
35
|
for (const provider of allProviders) {
|
|
38
|
-
if (provider === primaryProvider) {
|
|
39
|
-
result.skipped.push(provider);
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
36
|
const overlayDir = join(frameworkDir, '.adapted', provider);
|
|
44
37
|
|
|
45
38
|
for (const file of ADAPTABLE_FILES) {
|
|
@@ -64,7 +57,7 @@ export function generateProviderOverlays(targetDir, frameworkSource, primaryProv
|
|
|
64
57
|
/**
|
|
65
58
|
* Resolve the correct framework file path for a given provider.
|
|
66
59
|
*
|
|
67
|
-
* Returns the overlay path
|
|
60
|
+
* Returns the provider overlay path when it exists,
|
|
68
61
|
* otherwise falls back to the main chati.dev/ path.
|
|
69
62
|
*
|
|
70
63
|
* @param {string} projectDir - Project root directory
|
|
@@ -4,7 +4,7 @@ import * as yaml from 'js-yaml';
|
|
|
4
4
|
* Generate session.yaml content
|
|
5
5
|
*/
|
|
6
6
|
export function generateSessionYaml(config) {
|
|
7
|
-
const { projectName, projectType, language, selectedIDEs, selectedMCPs,
|
|
7
|
+
const { projectName, projectType, language, selectedIDEs, selectedMCPs, allProviders } = config;
|
|
8
8
|
|
|
9
9
|
// Schema MUST match session-manager.js DEFAULT_SESSION fields,
|
|
10
10
|
// otherwise validateSession() rejects installer-generated sessions.
|
|
@@ -27,9 +27,7 @@ export function generateSessionYaml(config) {
|
|
|
27
27
|
user_level_confidence: 0.0,
|
|
28
28
|
ides: selectedIDEs,
|
|
29
29
|
mcps: selectedMCPs,
|
|
30
|
-
providers_enabled: allProviders && allProviders.length > 0 ? allProviders : [
|
|
31
|
-
primary_provider: llmProvider || 'claude',
|
|
32
|
-
active_provider: llmProvider || 'claude',
|
|
30
|
+
providers_enabled: allProviders && allProviders.length > 0 ? allProviders : ['claude'],
|
|
33
31
|
completed_agents: [],
|
|
34
32
|
agent_results: {},
|
|
35
33
|
mode_transitions: [],
|
|
@@ -102,10 +100,9 @@ export const PROVIDER_MODEL_MAPS = {
|
|
|
102
100
|
* Generate config.yaml content
|
|
103
101
|
*/
|
|
104
102
|
export function generateConfigYaml(config) {
|
|
105
|
-
const { version, projectType, language,
|
|
103
|
+
const { version, projectType, language, allProviders } = config;
|
|
106
104
|
const selectedIDEs = config.selectedIDEs || [];
|
|
107
|
-
const
|
|
108
|
-
const providers = allProviders && allProviders.length > 0 ? allProviders : [provider];
|
|
105
|
+
const providers = allProviders && allProviders.length > 0 ? allProviders : ['claude'];
|
|
109
106
|
|
|
110
107
|
const configData = {
|
|
111
108
|
version: version,
|
|
@@ -116,9 +113,10 @@ export function generateConfigYaml(config) {
|
|
|
116
113
|
language: language,
|
|
117
114
|
ides: selectedIDEs,
|
|
118
115
|
providers: {
|
|
119
|
-
claude: { enabled: providers.includes('claude')
|
|
120
|
-
gemini: { enabled: providers.includes('gemini') || selectedIDEs.includes('gemini-cli')
|
|
121
|
-
codex: { enabled: providers.includes('codex') || selectedIDEs.includes('codex-cli')
|
|
116
|
+
claude: { enabled: providers.includes('claude') },
|
|
117
|
+
gemini: { enabled: providers.includes('gemini') || selectedIDEs.includes('gemini-cli') },
|
|
118
|
+
codex: { enabled: providers.includes('codex') || selectedIDEs.includes('codex-cli') },
|
|
119
|
+
grok: { enabled: providers.includes('grok') || selectedIDEs.includes('grok-cli') },
|
|
122
120
|
},
|
|
123
121
|
};
|
|
124
122
|
|
|
@@ -148,7 +146,7 @@ export function generateConfigYaml(config) {
|
|
|
148
146
|
frustration_detection: true,
|
|
149
147
|
bash_security_checks: true,
|
|
150
148
|
// Agent Teams (Article XXI) — default ON in v4.2.2.
|
|
151
|
-
// Only effective when
|
|
149
|
+
// Only effective when the active session runs in Claude (gated in cli.js
|
|
152
150
|
// isAgentTeamsEnabled). Gemini and Codex always fall back to sequential
|
|
153
151
|
// pipeline silently. See plan: was previously false for "safe rollout"
|
|
154
152
|
// but the v4.2.0 launch never actually shipped, so this is a fresh start.
|
|
@@ -165,17 +163,6 @@ export function generateConfigYaml(config) {
|
|
|
165
163
|
anonymous_id: null,
|
|
166
164
|
};
|
|
167
165
|
|
|
168
|
-
// Add agent_overrides with optimal model map for the selected provider
|
|
169
|
-
if (provider !== 'claude') {
|
|
170
|
-
const modelMap = PROVIDER_MODEL_MAPS[provider];
|
|
171
|
-
if (modelMap) {
|
|
172
|
-
configData.agent_overrides = {};
|
|
173
|
-
for (const [agent, model] of Object.entries(modelMap.agents)) {
|
|
174
|
-
configData.agent_overrides[agent] = { provider, model };
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
166
|
return yaml.dump(configData, { lineWidth: -1, quoteStyle: 'double', forceQuotes: false });
|
|
180
167
|
}
|
|
181
168
|
|
|
@@ -187,28 +187,23 @@ export async function validateInstallation(targetDir) {
|
|
|
187
187
|
try {
|
|
188
188
|
const configContent = readFileSync(configYamlPath, 'utf-8');
|
|
189
189
|
const enabledProviders = [];
|
|
190
|
-
let primaryProvider = null;
|
|
191
190
|
let currentProvider = null;
|
|
192
191
|
|
|
193
192
|
for (const line of configContent.split('\n')) {
|
|
194
193
|
const provLine = line.match(/^\s{4}(\w+):$/);
|
|
195
|
-
if (provLine && ['claude', 'gemini', 'codex'].includes(provLine[1])) {
|
|
194
|
+
if (provLine && ['claude', 'gemini', 'codex', 'grok'].includes(provLine[1])) {
|
|
196
195
|
currentProvider = provLine[1];
|
|
197
196
|
}
|
|
198
197
|
if (currentProvider && line.includes('enabled: true')) {
|
|
199
198
|
enabledProviders.push(currentProvider);
|
|
200
199
|
}
|
|
201
|
-
if (currentProvider && line.includes('primary: true')) {
|
|
202
|
-
primaryProvider = currentProvider;
|
|
203
|
-
}
|
|
204
200
|
}
|
|
205
201
|
|
|
206
|
-
// Validate overlay completeness for
|
|
207
|
-
if (enabledProviders.length >
|
|
208
|
-
const secondaryProviders = enabledProviders.filter(p => p !== primaryProvider);
|
|
202
|
+
// Validate overlay completeness for every enabled provider.
|
|
203
|
+
if (enabledProviders.length > 0) {
|
|
209
204
|
const overlayDetails = [];
|
|
210
205
|
|
|
211
|
-
for (const provider of
|
|
206
|
+
for (const provider of enabledProviders) {
|
|
212
207
|
let present = 0;
|
|
213
208
|
let missing = 0;
|
|
214
209
|
const missingFiles = [];
|
|
@@ -84,10 +84,17 @@ function readBundledPayload() {
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function catalogPublishedAt(catalog) {
|
|
88
|
+
const parsed = catalog.published_at ? Date.parse(catalog.published_at) : 0;
|
|
89
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const SOURCE_PRIORITY = Object.freeze({ remote: 3, cache: 2, bundled: 1 });
|
|
93
|
+
|
|
87
94
|
/**
|
|
88
|
-
* Resolves
|
|
89
|
-
*
|
|
90
|
-
* local binary inference
|
|
95
|
+
* Resolves every available signed CHATI model catalog and selects the newest
|
|
96
|
+
* published snapshot. Remote wins ties, then cache, then the bundled release.
|
|
97
|
+
* Vendor APIs and local binary inference stay outside this trust boundary.
|
|
91
98
|
*/
|
|
92
99
|
export async function resolveCapabilityCatalog({
|
|
93
100
|
projectDir,
|
|
@@ -98,6 +105,7 @@ export async function resolveCapabilityCatalog({
|
|
|
98
105
|
timeoutMs = 5000,
|
|
99
106
|
} = {}) {
|
|
100
107
|
const failures = [];
|
|
108
|
+
const candidates = [];
|
|
101
109
|
|
|
102
110
|
if (catalogUrl && typeof fetchImpl === 'function') {
|
|
103
111
|
try {
|
|
@@ -109,8 +117,7 @@ export async function resolveCapabilityCatalog({
|
|
|
109
117
|
if (!response.ok) throw catalogError('CATALOG_HTTP_ERROR', `catalog endpoint returned HTTP ${response.status}`);
|
|
110
118
|
const payload = parseSignedPayload(await response.json());
|
|
111
119
|
const catalog = verifySignedCapabilityCatalog(payload, { clock, publicKeyPem });
|
|
112
|
-
|
|
113
|
-
return Object.freeze({ source: 'remote', source_url: safeCatalogUrl, cache_path, catalog });
|
|
120
|
+
candidates.push({ source: 'remote', source_url: safeCatalogUrl, payload, catalog });
|
|
114
121
|
} catch (error) {
|
|
115
122
|
failures.push({ source: 'remote', code: error.code || error.name || 'CATALOG_FETCH_FAILED' });
|
|
116
123
|
}
|
|
@@ -122,7 +129,7 @@ export async function resolveCapabilityCatalog({
|
|
|
122
129
|
try {
|
|
123
130
|
const payload = readSignedCatalogFile(cachePath);
|
|
124
131
|
const catalog = verifySignedCapabilityCatalog(payload, { clock, publicKeyPem });
|
|
125
|
-
|
|
132
|
+
candidates.push({ source: 'cache', cache_path: CATALOG_CACHE_PATH, payload, catalog });
|
|
126
133
|
} catch (error) {
|
|
127
134
|
failures.push({ source: 'cache', code: error.code || error.name || 'CATALOG_CACHE_INVALID' });
|
|
128
135
|
}
|
|
@@ -132,10 +139,27 @@ export async function resolveCapabilityCatalog({
|
|
|
132
139
|
try {
|
|
133
140
|
const payload = readBundledPayload();
|
|
134
141
|
const catalog = verifySignedCapabilityCatalog(payload, { clock, publicKeyPem });
|
|
135
|
-
|
|
142
|
+
candidates.push({ source: 'bundled', cache_path: null, payload, catalog });
|
|
136
143
|
} catch (error) {
|
|
137
144
|
failures.push({ source: 'bundled', code: error.code || error.name || 'CATALOG_BUNDLE_INVALID' });
|
|
138
145
|
}
|
|
139
146
|
|
|
140
|
-
|
|
147
|
+
if (candidates.length === 0) {
|
|
148
|
+
throw catalogError('CATALOG_UNAVAILABLE', 'no fresh, signed CHATI model catalog is available', { failures });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
candidates.sort((left, right) =>
|
|
152
|
+
catalogPublishedAt(right.catalog) - catalogPublishedAt(left.catalog)
|
|
153
|
+
|| SOURCE_PRIORITY[right.source] - SOURCE_PRIORITY[left.source]
|
|
154
|
+
);
|
|
155
|
+
const selected = candidates[0];
|
|
156
|
+
const cache_path = selected.source === 'remote' && projectDir
|
|
157
|
+
? writeCache(projectDir, selected.payload)
|
|
158
|
+
: (selected.cache_path ?? null);
|
|
159
|
+
return Object.freeze({
|
|
160
|
+
source: selected.source,
|
|
161
|
+
...(selected.source_url ? { source_url: selected.source_url } : {}),
|
|
162
|
+
cache_path,
|
|
163
|
+
catalog: selected.catalog,
|
|
164
|
+
});
|
|
141
165
|
}
|
|
@@ -88,12 +88,15 @@ function artifactCore(input, createdAt) {
|
|
|
88
88
|
expires_at: input.capability_snapshot.expires_at,
|
|
89
89
|
models: input.capability_snapshot.models
|
|
90
90
|
.filter((model) => selectedModelKeys.has(`${model.provider_id}:${model.model_id}`))
|
|
91
|
-
.map(({ provider_id, model_id, actions, adjudication_priority, highest_reasoning_configuration }) => ({
|
|
91
|
+
.map(({ provider_id, model_id, actions, adjudication_priority, highest_reasoning_configuration, routing_priority, tier, natural_role }) => ({
|
|
92
92
|
provider_id,
|
|
93
93
|
model_id,
|
|
94
94
|
actions,
|
|
95
95
|
...(adjudication_priority !== undefined ? { adjudication_priority } : {}),
|
|
96
96
|
...(highest_reasoning_configuration !== undefined ? { highest_reasoning_configuration } : {}),
|
|
97
|
+
...(routing_priority !== undefined ? { routing_priority } : {}),
|
|
98
|
+
...(tier !== undefined ? { tier } : {}),
|
|
99
|
+
...(natural_role !== undefined ? { natural_role } : {}),
|
|
97
100
|
})),
|
|
98
101
|
};
|
|
99
102
|
return {
|