chati-dev 4.5.5 → 4.5.7
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/bin/chati.js +35 -1
- package/framework/agents/plan/tasks.md +1 -1
- package/framework/config.yaml +8 -17
- package/framework/constitution.md +30 -20
- package/framework/context/governance.md +3 -1
- package/framework/context/root.md +1 -1
- package/framework/data/entity-registry.yaml +2 -2
- package/framework/domains/agents/orchestrator.yaml +1 -1
- package/framework/domains/constitution.yaml +1 -1
- package/framework/domains/global.yaml +2 -2
- package/framework/hooks/model-governance.js +9 -0
- package/framework/manifest.json +38 -38
- package/framework/manifest.sig +1 -1
- package/framework/orchestrator/chati.md +27 -3
- package/framework/schemas/session.schema.json +27 -10
- package/framework/tasks/brownfield-wu-architecture-map.md +1 -1
- package/framework/tasks/brownfield-wu-deep-discovery.md +1 -1
- package/framework/tasks/brownfield-wu-dependency-scan.md +1 -1
- package/framework/tasks/brownfield-wu-migration-plan.md +1 -1
- package/framework/tasks/brownfield-wu-report.md +1 -1
- package/framework/tasks/brownfield-wu-risk-assess.md +1 -1
- package/framework/tasks/greenfield-wu-report.md +1 -1
- package/node_modules/@chati/provider-registry/src/index.js +3 -2
- package/node_modules/@chati/tracking-clickup/src/index.js +22 -0
- package/package.json +2 -1
- package/src/config/gemini-hooks-generator.js +10 -4
- package/src/dashboard/layout.js +6 -4
- package/src/installer/core.js +20 -0
- package/src/installer/templates.js +21 -1
- package/src/installer-v2/clickup-preflight.js +32 -0
- package/src/installer-v2/model-catalog-envelope.json +15 -13
- package/src/installer-v2/model-catalog.json +5 -5
- package/src/installer-v2/model-catalog.sig +1 -1
- package/src/installer-v2/wizard-installation.js +1 -1
- package/src/intelligence/registry-manager.js +9 -3
- package/src/orchestrator/cli.js +41 -23
- package/src/orchestrator/clickup-projection.js +25 -8
- package/src/orchestrator/clickup-runtime.js +89 -1
- package/src/orchestrator/planning-runtime.js +1 -2
- package/src/orchestrator/rail-runtime.js +1 -2
- package/src/orchestrator/runtime-installation-v2.js +10 -1
- package/src/orchestrator/session-manager.js +134 -96
- package/src/terminal/adapters/claude-adapter.js +9 -2
- package/src/terminal/adapters/codex-adapter.js +9 -2
- package/src/terminal/adapters/gemini-adapter.js +5 -2
- package/src/terminal/adapters/grok-adapter.js +13 -3
- package/src/terminal/cli-registry.js +5 -0
- package/src/terminal/run-agent.js +5 -0
- package/src/terminal/run-parallel.js +7 -0
- package/src/terminal/spawner.js +49 -10
- package/src/wizard/index.js +8 -0
- package/src/wizard/questions.js +7 -3
|
@@ -113,7 +113,7 @@ Combine all sections into comprehensive YAML with clear structure and actionable
|
|
|
113
113
|
# wu-report.yaml
|
|
114
114
|
report_type: brownfield
|
|
115
115
|
timestamp: 2026-02-13T12:15:00Z
|
|
116
|
-
project_path: /
|
|
116
|
+
project_path: /workspace/legacy-app
|
|
117
117
|
|
|
118
118
|
executive_summary: |
|
|
119
119
|
Analyzed mature 1793-day-old Next.js + Express application with 30k LOC and 87 dependencies.
|
|
@@ -185,7 +185,7 @@ For each high and critical risk:
|
|
|
185
185
|
```yaml
|
|
186
186
|
# risk-assessment.yaml
|
|
187
187
|
timestamp: 2026-02-13T11:45:00Z
|
|
188
|
-
project_path: /
|
|
188
|
+
project_path: /workspace/legacy-app
|
|
189
189
|
|
|
190
190
|
executive_summary: |
|
|
191
191
|
Identified 47 risks across security, performance, technical debt, and maintainability.
|
|
@@ -128,7 +128,7 @@ executive_summary: |
|
|
|
128
128
|
Estimated 30 minutes to initial scaffold, 2-4 hours to complete tooling setup.
|
|
129
129
|
|
|
130
130
|
environment:
|
|
131
|
-
project_path: /
|
|
131
|
+
project_path: /workspace/my-app
|
|
132
132
|
current_state: empty
|
|
133
133
|
maturity_score: 0
|
|
134
134
|
runtime:
|
|
@@ -113,8 +113,9 @@ export function validateInstallation(installation, snapshot, { bindingMap = DEFA
|
|
|
113
113
|
} else if (installation.primary_harness !== undefined) {
|
|
114
114
|
throw new ContractError('PRIMARY_BINDING_MISSING', 'legacy primary_harness requires primary_binding');
|
|
115
115
|
}
|
|
116
|
-
if (installation.profile === '
|
|
117
|
-
|
|
116
|
+
if (installation.profile === 'authorized-internal'
|
|
117
|
+
&& installation.external_integrations?.clickup !== 'required') {
|
|
118
|
+
throw new ContractError('PROFILE_DEPENDENCY_MISSING', 'the internal profile requires ClickUp');
|
|
118
119
|
}
|
|
119
120
|
return Object.freeze(canonicalize(installation));
|
|
120
121
|
}
|
|
@@ -200,6 +200,28 @@ export class TrackingClickUp {
|
|
|
200
200
|
return Object.freeze(canonicalize(projection));
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
/** Records a receipt produced by an independently authorized MCP boundary. */
|
|
204
|
+
confirmExternalDelivery({ projection_id, receipt_ref } = {}) {
|
|
205
|
+
const projection = this.#find(projection_id);
|
|
206
|
+
string(receipt_ref, 'INVALID_TRANSPORT_RECEIPT', 'receipt_ref');
|
|
207
|
+
if (this.#verifyReference(receipt_ref, 'receipt') !== true) {
|
|
208
|
+
fail('UNVERIFIED_RECEIPT', 'external delivery receipt must be an immutable verified reference');
|
|
209
|
+
}
|
|
210
|
+
if (projection.delivery_state === 'permanent_failure') fail('PERMANENT_FAILURE', 'permanently failed projection cannot be confirmed');
|
|
211
|
+
if (projection.delivery_state === 'confirmed') {
|
|
212
|
+
if (projection.receipt_ref !== receipt_ref) fail('RECEIPT_CONFLICT', 'projection is already confirmed by a different receipt');
|
|
213
|
+
return Object.freeze(canonicalize(projection));
|
|
214
|
+
}
|
|
215
|
+
projection.delivery_state = 'confirmed';
|
|
216
|
+
projection.receipt_ref = receipt_ref;
|
|
217
|
+
projection.reconciliation_state = 'pending';
|
|
218
|
+
projection.last_attempt_at = timestamp(this.#clock);
|
|
219
|
+
projection.attempts += 1;
|
|
220
|
+
this.#setEvolution(projection);
|
|
221
|
+
this.#persist();
|
|
222
|
+
return Object.freeze(canonicalize(projection));
|
|
223
|
+
}
|
|
224
|
+
|
|
203
225
|
reconcile({ projection_id } = {}) {
|
|
204
226
|
const projection = this.#find(projection_id);
|
|
205
227
|
if (!this.#transport || typeof this.#transport.reconcile !== 'function') fail('RECONCILIATION_NOT_CONFIGURED', 'no authorized reconciliation transport is configured');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chati-dev",
|
|
3
|
-
"version": "4.5.
|
|
3
|
+
"version": "4.5.7",
|
|
4
4
|
"description": "AI-Powered Multi-Agent Orchestration System - Structured vibe coding for Full Stack Development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
"chalk": "^5.3.0",
|
|
78
78
|
"js-yaml": "^5.4.0",
|
|
79
79
|
"ora": "^8.0.1",
|
|
80
|
+
"proper-lockfile": "4.1.2",
|
|
80
81
|
"semver": "^7.6.0"
|
|
81
82
|
},
|
|
82
83
|
"bundleDependencies": [
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Constitution Article XIX — hooks are generated at install time, zero runtime overhead.
|
|
10
10
|
*
|
|
11
11
|
* Gemini CLI Hook Events (used by chati.dev):
|
|
12
|
-
* - BeforeModel: runs before model inference (PRISM injection, model advisory)
|
|
12
|
+
* - BeforeModel: runs before model inference (PRISM injection, legacy model advisory)
|
|
13
13
|
* - BeforeTool: runs before tool execution (mode governance, constitution guard, read protection)
|
|
14
14
|
* - PreCompress: runs before context compression (session digest)
|
|
15
15
|
*/
|
|
@@ -27,7 +27,7 @@ export const HOOK_MAP = {
|
|
|
27
27
|
// indefinitely from a long-running session.
|
|
28
28
|
'license-guard': { event: 'BeforeModel', description: 'Validate license on every turn (chati.dev governance)' },
|
|
29
29
|
'prism-engine': { event: 'BeforeModel', description: 'Inject PRISM context into model prompt' },
|
|
30
|
-
'model-governance': { event: 'BeforeModel', description: '
|
|
30
|
+
'model-governance': { event: 'BeforeModel', description: 'Legacy advisory for pre-v2 installations' },
|
|
31
31
|
'mode-governance': { event: 'BeforeTool', description: 'Block writes outside current mode scope' },
|
|
32
32
|
'constitution-guard': { event: 'BeforeTool', description: 'Block destructive commands and secret writes' },
|
|
33
33
|
'read-protection': { event: 'BeforeTool', description: 'Block reads of sensitive files' },
|
|
@@ -121,13 +121,14 @@ main();
|
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
123
|
* Generate the model governance hook for Gemini CLI.
|
|
124
|
-
* BeforeModel event — advisory
|
|
124
|
+
* BeforeModel event — legacy advisory for pre-v2 installations.
|
|
125
125
|
*/
|
|
126
126
|
function generateModelGovernance() {
|
|
127
127
|
return `${HOOK_HEADER}
|
|
128
128
|
/**
|
|
129
129
|
* Model Governance — BeforeModel
|
|
130
|
-
*
|
|
130
|
+
* V2 routing is automatic and invisible. This wrapper only supports legacy
|
|
131
|
+
* installations that do not have a v2 installation contract.
|
|
131
132
|
*/
|
|
132
133
|
async function main() {
|
|
133
134
|
let input = '';
|
|
@@ -139,6 +140,11 @@ async function main() {
|
|
|
139
140
|
const event = JSON.parse(input);
|
|
140
141
|
const cwd = event.cwd || process.cwd();
|
|
141
142
|
|
|
143
|
+
if (existsSync(join(cwd, '.chati', 'v2', 'installation.json'))) {
|
|
144
|
+
console.log(JSON.stringify({}));
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
142
148
|
const sessionPath = join(cwd, '.chati', 'session.yaml');
|
|
143
149
|
if (!existsSync(sessionPath)) {
|
|
144
150
|
console.log(JSON.stringify({}));
|
package/src/dashboard/layout.js
CHANGED
|
@@ -65,14 +65,16 @@ export function buildProjectInfo(data) {
|
|
|
65
65
|
const rawMode = session.execution_mode;
|
|
66
66
|
const mode = formatMode(rawMode && typeof rawMode === 'object' ? rawMode.mode : rawMode);
|
|
67
67
|
const lang = session.language || 'en';
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
const providers = Array.isArray(session.providers_enabled) && session.providers_enabled.length > 0
|
|
69
|
+
? session.providers_enabled.join('/')
|
|
70
|
+
: 'unknown';
|
|
71
|
+
const languageVisibleWidth = 12 + lang.length;
|
|
72
|
+
const providersVisibleWidth = 11 + providers.length;
|
|
71
73
|
|
|
72
74
|
return [
|
|
73
75
|
brand('│') + ` ${dim('Project:')} ${name}` + ' '.repeat(Math.max(1, 30 - name.length)) + `${dim('Type:')} ${type}` + ' '.repeat(Math.max(1, 18 - type.length)) + brand('│'),
|
|
74
76
|
brand('│') + ` ${dim('Phase:')} ${state}` + ' '.repeat(Math.max(1, 28)) + `${dim('Mode:')} ${mode}` + ' '.repeat(Math.max(1, 10)) + brand('│'),
|
|
75
|
-
brand('│') + ` ${dim('Language:')} ${lang}` + ' '.repeat(Math.max(1,
|
|
77
|
+
brand('│') + ` ${dim('Language:')} ${lang}` + ' '.repeat(Math.max(1, 28 - languageVisibleWidth)) + `${dim('Providers:')} ${providers}` + ' '.repeat(Math.max(1, 31 - providersVisibleWidth)) + brand('│'),
|
|
76
78
|
];
|
|
77
79
|
}
|
|
78
80
|
|
package/src/installer/core.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync, copyFileSync, existsSync, readFileSync, readdirSync, statSync } from 'fs';
|
|
2
|
+
import { execFileSync } from 'child_process';
|
|
2
3
|
import { join, dirname, basename } from 'path';
|
|
3
4
|
import { fileURLToPath } from 'url';
|
|
4
5
|
import { createRequire } from 'module';
|
|
@@ -215,6 +216,21 @@ export async function installFramework(config) {
|
|
|
215
216
|
|
|
216
217
|
// 7. Update .gitignore with runtime session lock files
|
|
217
218
|
updateGitignore(targetDir, selectedIDEs);
|
|
219
|
+
|
|
220
|
+
// RAIL evidence and release lanes require a Git worktree. For a genuinely
|
|
221
|
+
// new project, initialize it automatically. Nested projects already covered
|
|
222
|
+
// by a parent worktree are left untouched.
|
|
223
|
+
ensureGitRepository(targetDir);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function ensureGitRepository(targetDir) {
|
|
227
|
+
try {
|
|
228
|
+
execFileSync('git', ['-C', targetDir, 'rev-parse', '--show-toplevel'], { stdio: 'ignore' });
|
|
229
|
+
return { initialized: false };
|
|
230
|
+
} catch {
|
|
231
|
+
execFileSync('git', ['-C', targetDir, 'init', '-q'], { stdio: 'ignore' });
|
|
232
|
+
return { initialized: true };
|
|
233
|
+
}
|
|
218
234
|
}
|
|
219
235
|
|
|
220
236
|
/**
|
|
@@ -562,6 +578,10 @@ export function updateGitignore(targetDir, selectedIDEs) {
|
|
|
562
578
|
'.chati/memories/*/session/',
|
|
563
579
|
'# NOTE: durable/ and daily/ memories ARE committed — they contain project knowledge',
|
|
564
580
|
'',
|
|
581
|
+
'# Operating system metadata',
|
|
582
|
+
'.DS_Store',
|
|
583
|
+
'._*',
|
|
584
|
+
'',
|
|
565
585
|
'# Claude Code integration (framework portions only)',
|
|
566
586
|
'.claude/commands/chati.md',
|
|
567
587
|
'.claude/rules/chati/',
|
|
@@ -9,7 +9,7 @@ export function generateSessionYaml(config) {
|
|
|
9
9
|
// Schema MUST match session-manager.js DEFAULT_SESSION fields,
|
|
10
10
|
// otherwise validateSession() rejects installer-generated sessions.
|
|
11
11
|
const session = {
|
|
12
|
-
schema_version: '1.
|
|
12
|
+
schema_version: '1.2',
|
|
13
13
|
version: '1.0',
|
|
14
14
|
mode: 'discover',
|
|
15
15
|
project: {
|
|
@@ -35,6 +35,26 @@ export function generateSessionYaml(config) {
|
|
|
35
35
|
agents: {},
|
|
36
36
|
backlog: [],
|
|
37
37
|
last_handoff: '',
|
|
38
|
+
scaffold_candidates: [],
|
|
39
|
+
scaffold_signals: {},
|
|
40
|
+
scaffold_applied: [],
|
|
41
|
+
active_model: null,
|
|
42
|
+
active_provider: null,
|
|
43
|
+
context_tokens_used: 0,
|
|
44
|
+
context_window_tokens: null,
|
|
45
|
+
context_last_bracket: null,
|
|
46
|
+
decision_trail: [],
|
|
47
|
+
teams: [],
|
|
48
|
+
team_events: [],
|
|
49
|
+
preview: {
|
|
50
|
+
status: 'inactive',
|
|
51
|
+
url: null,
|
|
52
|
+
port: null,
|
|
53
|
+
framework: null,
|
|
54
|
+
logs_captured: 0,
|
|
55
|
+
adjustment_count: 0,
|
|
56
|
+
},
|
|
57
|
+
model_selections: [],
|
|
38
58
|
// Article XXIII — Reasoning Tier Governance.
|
|
39
59
|
// Tier starts at 'standard' and is promoted by reasoning-escalator.js
|
|
40
60
|
// in response to friction signals. See chati.dev/hooks/reasoning-escalator.js.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
const COMMANDS = Object.freeze({ claude: 'claude', codex: 'codex', grok: 'grok' });
|
|
4
|
+
|
|
5
|
+
export function outputHasConnectedClickUp(output) {
|
|
6
|
+
if (typeof output !== 'string') return false;
|
|
7
|
+
return output.split(/\r?\n/).some((line) => {
|
|
8
|
+
if (!/\bclickup\b/i.test(line)) return false;
|
|
9
|
+
return !/\b(disabled|failed|error|not configured|unhealthy|unauthorized|disconnected)\b|authentication required/i.test(line);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Read-only preflight. It never writes provider config or reads credentials. */
|
|
14
|
+
export function checkClickUpMcp(selectedProviders, { exec = execFileSync } = {}) {
|
|
15
|
+
const providers = [...new Set(selectedProviders || [])].filter((provider) => COMMANDS[provider]);
|
|
16
|
+
const checks = providers.map((provider) => {
|
|
17
|
+
try {
|
|
18
|
+
const output = exec(COMMANDS[provider], ['mcp', 'list'], {
|
|
19
|
+
encoding: 'utf8', timeout: 15_000, stdio: ['ignore', 'pipe', 'pipe'],
|
|
20
|
+
});
|
|
21
|
+
return { provider, connected: outputHasConnectedClickUp(output) };
|
|
22
|
+
} catch {
|
|
23
|
+
return { provider, connected: false };
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
const connectedProviders = checks.filter((check) => check.connected).map((check) => check.provider);
|
|
27
|
+
return Object.freeze({
|
|
28
|
+
passed: connectedProviders.length > 0,
|
|
29
|
+
connected_providers: connectedProviders,
|
|
30
|
+
checks,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"catalog": {
|
|
3
|
-
"snapshot_id": "chati-model-catalog-2026-08-27-
|
|
4
|
-
"published_at": "2026-08-
|
|
3
|
+
"snapshot_id": "chati-model-catalog-2026-08-27-v3",
|
|
4
|
+
"published_at": "2026-08-27T12:00:00.000Z",
|
|
5
5
|
"expires_at": "2026-11-25T00:00:00.000Z",
|
|
6
6
|
"models": [
|
|
7
7
|
{
|
|
@@ -17,13 +17,12 @@
|
|
|
17
17
|
"tier": "frontier",
|
|
18
18
|
"natural_role": "architecture and design",
|
|
19
19
|
"routing_priority": {
|
|
20
|
-
"discovery":
|
|
21
|
-
"planning":
|
|
20
|
+
"discovery": 78,
|
|
21
|
+
"planning": 94,
|
|
22
22
|
"build": 45,
|
|
23
23
|
"review": 90,
|
|
24
24
|
"adjudication": 95,
|
|
25
25
|
"architect": 100,
|
|
26
|
-
"detail": 100,
|
|
27
26
|
"ux": 100
|
|
28
27
|
},
|
|
29
28
|
"adjudication_priority": 95,
|
|
@@ -42,11 +41,13 @@
|
|
|
42
41
|
"tier": "frontier",
|
|
43
42
|
"natural_role": "quality rules and judgment-heavy work",
|
|
44
43
|
"routing_priority": {
|
|
45
|
-
"discovery":
|
|
46
|
-
"planning":
|
|
44
|
+
"discovery": 92,
|
|
45
|
+
"planning": 96,
|
|
47
46
|
"build": 75,
|
|
48
47
|
"review": 95,
|
|
49
48
|
"adjudication": 100,
|
|
49
|
+
"brownfield-wu": 100,
|
|
50
|
+
"detail": 100,
|
|
50
51
|
"qa-planning": 97,
|
|
51
52
|
"qa-implementation": 97
|
|
52
53
|
},
|
|
@@ -65,13 +66,14 @@
|
|
|
65
66
|
"tier": "workhorse",
|
|
66
67
|
"natural_role": "daily implementation and planning craft",
|
|
67
68
|
"routing_priority": {
|
|
68
|
-
"discovery":
|
|
69
|
-
"planning":
|
|
69
|
+
"discovery": 90,
|
|
70
|
+
"planning": 92,
|
|
70
71
|
"build": 98,
|
|
71
72
|
"review": 80,
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
73
|
+
"greenfield-wu": 100,
|
|
74
|
+
"brief": 100,
|
|
75
|
+
"phases": 100,
|
|
76
|
+
"tasks": 100,
|
|
75
77
|
"dev": 98
|
|
76
78
|
},
|
|
77
79
|
"adjudication_priority": 60
|
|
@@ -211,5 +213,5 @@
|
|
|
211
213
|
}
|
|
212
214
|
]
|
|
213
215
|
},
|
|
214
|
-
"signature": "
|
|
216
|
+
"signature": "tL1JS1RkPfOBDE6rcctrEPN98oQ6FjLurwZnzYglMvrsvvceRbS19INcxmPYQjT+NJv507Vuo/HIkm+J0nQ1Cw=="
|
|
215
217
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"snapshot_id": "chati-model-catalog-2026-08-27-
|
|
3
|
-
"published_at": "2026-08-
|
|
2
|
+
"snapshot_id": "chati-model-catalog-2026-08-27-v3",
|
|
3
|
+
"published_at": "2026-08-27T12:00:00.000Z",
|
|
4
4
|
"expires_at": "2026-11-25T00:00:00.000Z",
|
|
5
5
|
"models": [
|
|
6
6
|
{
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"actions": ["discovery", "planning", "build", "review", "adjudication"],
|
|
10
10
|
"tier": "frontier",
|
|
11
11
|
"natural_role": "architecture and design",
|
|
12
|
-
"routing_priority": { "discovery":
|
|
12
|
+
"routing_priority": { "discovery": 78, "planning": 94, "build": 45, "review": 90, "adjudication": 95, "architect": 100, "ux": 100 },
|
|
13
13
|
"adjudication_priority": 95,
|
|
14
14
|
"highest_reasoning_configuration": "xhigh"
|
|
15
15
|
},
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"actions": ["discovery", "planning", "build", "review", "adjudication"],
|
|
20
20
|
"tier": "frontier",
|
|
21
21
|
"natural_role": "quality rules and judgment-heavy work",
|
|
22
|
-
"routing_priority": { "discovery":
|
|
22
|
+
"routing_priority": { "discovery": 92, "planning": 96, "build": 75, "review": 95, "adjudication": 100, "brownfield-wu": 100, "detail": 100, "qa-planning": 97, "qa-implementation": 97 },
|
|
23
23
|
"adjudication_priority": 100,
|
|
24
24
|
"highest_reasoning_configuration": "xhigh"
|
|
25
25
|
},
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"actions": ["discovery", "planning", "build", "review"],
|
|
30
30
|
"tier": "workhorse",
|
|
31
31
|
"natural_role": "daily implementation and planning craft",
|
|
32
|
-
"routing_priority": { "discovery":
|
|
32
|
+
"routing_priority": { "discovery": 90, "planning": 92, "build": 98, "review": 80, "greenfield-wu": 100, "brief": 100, "phases": 100, "tasks": 100, "dev": 98 },
|
|
33
33
|
"adjudication_priority": 60
|
|
34
34
|
},
|
|
35
35
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
tL1JS1RkPfOBDE6rcctrEPN98oQ6FjLurwZnzYglMvrsvvceRbS19INcxmPYQjT+NJv507Vuo/HIkm+J0nQ1Cw==
|
|
@@ -100,7 +100,7 @@ export function buildWizardV2InstallationInput({
|
|
|
100
100
|
const projectSlug = projectName.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '') || 'project';
|
|
101
101
|
const installation = {
|
|
102
102
|
installation_id: `wizard-${projectSlug}`,
|
|
103
|
-
profile: installationMode === 'internal' ? '
|
|
103
|
+
profile: installationMode === 'internal' ? 'authorized-internal' : 'open',
|
|
104
104
|
enabled_providers,
|
|
105
105
|
capability_snapshot_ref: snapshotId,
|
|
106
106
|
policy_ref: `policy://installation/${snapshotId}`,
|
|
@@ -36,8 +36,15 @@ export function checkRegistry(targetDir) {
|
|
|
36
36
|
// Check each registered entity exists on disk
|
|
37
37
|
const entities = flattenEntitiesMap(registry.entities);
|
|
38
38
|
for (const entity of entities) {
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
// Registry entries describe the monorepo source layout. Installed projects
|
|
40
|
+
// bundle the CLI source under .chati.dev/_cli, so health must resolve both
|
|
41
|
+
// layouts instead of reporting bundled runtime files as missing.
|
|
42
|
+
const candidates = [join(targetDir, entity.path)];
|
|
43
|
+
const bundledSourcePrefix = 'packages/chati-dev/src/';
|
|
44
|
+
if (entity.path.startsWith(bundledSourcePrefix)) {
|
|
45
|
+
candidates.push(join(targetDir, resolveFrameworkDir(targetDir), '_cli', entity.path.slice(bundledSourcePrefix.length)));
|
|
46
|
+
}
|
|
47
|
+
if (candidates.some((filePath) => existsSync(filePath))) {
|
|
41
48
|
found.push(entity);
|
|
42
49
|
} else {
|
|
43
50
|
missing.push(entity);
|
|
@@ -193,4 +200,3 @@ function loadRegistry(registryPath) {
|
|
|
193
200
|
return null;
|
|
194
201
|
}
|
|
195
202
|
}
|
|
196
|
-
|
package/src/orchestrator/cli.js
CHANGED
|
@@ -548,7 +548,7 @@ function sessionToPipelineState(session, projectDir) {
|
|
|
548
548
|
* Build the spawn command string for an autonomous agent.
|
|
549
549
|
* Exported for testing (path resolution must hold in every install layout).
|
|
550
550
|
*/
|
|
551
|
-
export function buildSpawnCommand(agent, projectDir, previousAgent, provider, timeout, model = null, strictProvider = false) {
|
|
551
|
+
export function buildSpawnCommand(agent, projectDir, previousAgent, provider, timeout, model = null, strictProvider = false, routing = {}) {
|
|
552
552
|
const parts = [
|
|
553
553
|
'node', RUNNER('run-agent.js'),
|
|
554
554
|
'--agent', agent,
|
|
@@ -557,6 +557,9 @@ export function buildSpawnCommand(agent, projectDir, previousAgent, provider, ti
|
|
|
557
557
|
'--previous-agent', previousAgent || 'none',
|
|
558
558
|
'--provider', provider || 'claude',
|
|
559
559
|
...(model ? ['--model', model] : []),
|
|
560
|
+
...(routing.provider_id ? ['--provider-id', routing.provider_id] : []),
|
|
561
|
+
...(routing.reasoning_configuration ? ['--reasoning-configuration', routing.reasoning_configuration] : []),
|
|
562
|
+
...(routing.catalog_snapshot_ref ? ['--catalog-snapshot-ref', routing.catalog_snapshot_ref] : []),
|
|
560
563
|
...(strictProvider ? ['--strict-provider'] : []),
|
|
561
564
|
'--timeout', String(timeout || 600000),
|
|
562
565
|
];
|
|
@@ -583,6 +586,19 @@ export function buildParallelSpawnCommand(agents, projectDir, previousAgent, pro
|
|
|
583
586
|
return parts.join(' ');
|
|
584
587
|
}
|
|
585
588
|
|
|
589
|
+
export function buildRoutedParallelSpawnCommands(agents, projectDir, previousAgent, timeout = 900000) {
|
|
590
|
+
return agents.map((agent) => {
|
|
591
|
+
const modelInfo = resolveAgentModel(agent, projectDir);
|
|
592
|
+
return Object.freeze({
|
|
593
|
+
agent,
|
|
594
|
+
provider: modelInfo.provider,
|
|
595
|
+
model: modelInfo.model,
|
|
596
|
+
reasoning_configuration: modelInfo.reasoning_configuration,
|
|
597
|
+
command: buildSpawnCommand(agent, projectDir, previousAgent, modelInfo.provider, timeout, modelInfo.model, modelInfo.source === 'installation-v2', modelInfo),
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
586
602
|
/**
|
|
587
603
|
* Model map — canonical model tier per agent.
|
|
588
604
|
* Matches framework/hooks/model-governance.js AGENT_MODELS.
|
|
@@ -748,7 +764,7 @@ async function handleNext(projectDir) {
|
|
|
748
764
|
// Centralized session lock update: applies to ALL return paths.
|
|
749
765
|
// Activates lock when an agent is returned, deactivates on completion.
|
|
750
766
|
if (result && result.agent && result.action !== 'error' && result.action !== 'setup') {
|
|
751
|
-
const activating = ['activate_interactive', 'spawn_autonomous', 'spawn_team', 'spawn_parallel'];
|
|
767
|
+
const activating = ['activate_interactive', 'spawn_routed_interactive', 'spawn_autonomous', 'spawn_team', 'spawn_parallel'];
|
|
752
768
|
if (activating.includes(result.action)) {
|
|
753
769
|
try {
|
|
754
770
|
writeSessionLock(projectDir, result.agent, {
|
|
@@ -830,20 +846,18 @@ async function _handleNextInner(projectDir) {
|
|
|
830
846
|
};
|
|
831
847
|
}
|
|
832
848
|
|
|
849
|
+
const modelInfo = resolveAgentModel(agent, projectDir);
|
|
833
850
|
return {
|
|
834
|
-
action: isInteractive ? '
|
|
851
|
+
action: isInteractive ? 'spawn_routed_interactive' : 'spawn_autonomous',
|
|
835
852
|
agent,
|
|
836
853
|
agent_file: agentFile,
|
|
837
854
|
phase: agentDef?.phase || session.mode,
|
|
838
|
-
spawn_command:
|
|
839
|
-
const modelInfo = resolveAgentModel(agent, projectDir);
|
|
840
|
-
return buildSpawnCommand(agent, projectDir, session.last_handoff || 'none', modelInfo.provider, 600000, modelInfo.model, modelInfo.source === 'installation-v2');
|
|
841
|
-
})(),
|
|
855
|
+
spawn_command: buildSpawnCommand(agent, projectDir, session.last_handoff || 'none', modelInfo.provider, 600000, modelInfo.model, modelInfo.source === 'installation-v2', modelInfo),
|
|
842
856
|
parallel_spawn_command: null,
|
|
843
857
|
parallel_agents: [],
|
|
844
858
|
handoff_status: { valid: true, missing: [], warnings: ['Resuming in-progress agent'] },
|
|
845
859
|
gate_status: { canAdvance: true, reason: 'Agent already in progress' },
|
|
846
|
-
model_info:
|
|
860
|
+
model_info: modelInfo,
|
|
847
861
|
context_bracket: estimateContextBracket(completedAgents.length, AGENT_PIPELINE.length),
|
|
848
862
|
pipeline_progress: getPipelineProgress(pipelineState),
|
|
849
863
|
session: { language: session.language, project_type: session.project_type || session.project?.type, execution_mode: session.execution_mode, user_level: session.user_level || 'auto' },
|
|
@@ -861,17 +875,18 @@ async function _handleNextInner(projectDir) {
|
|
|
861
875
|
const firstAgent = projectType === 'brownfield' ? 'brownfield-wu' : 'greenfield-wu';
|
|
862
876
|
const agentFile = getAgentFile(firstAgent, projectDir) || null;
|
|
863
877
|
|
|
878
|
+
const modelInfo = resolveAgentModel(firstAgent, projectDir);
|
|
864
879
|
return {
|
|
865
|
-
action: '
|
|
880
|
+
action: 'spawn_routed_interactive',
|
|
866
881
|
agent: firstAgent,
|
|
867
882
|
agent_file: agentFile,
|
|
868
883
|
phase: 'discover',
|
|
869
|
-
spawn_command:
|
|
884
|
+
spawn_command: buildSpawnCommand(firstAgent, projectDir, 'none', modelInfo.provider, 600000, modelInfo.model, modelInfo.source === 'installation-v2', modelInfo),
|
|
870
885
|
parallel_spawn_command: null,
|
|
871
886
|
parallel_agents: [],
|
|
872
887
|
handoff_status: { valid: true, missing: [], warnings: [] },
|
|
873
888
|
gate_status: { canAdvance: true, reason: 'First agent in pipeline' },
|
|
874
|
-
model_info:
|
|
889
|
+
model_info: modelInfo,
|
|
875
890
|
context_bracket: estimateContextBracket(0, AGENT_PIPELINE.length),
|
|
876
891
|
pipeline_progress: getPipelineProgress(pipelineState),
|
|
877
892
|
session: { language: session.language, project_type: projectType, execution_mode: session.execution_mode, user_level: session.user_level || 'auto' },
|
|
@@ -932,7 +947,7 @@ async function _handleNextInner(projectDir) {
|
|
|
932
947
|
const modelInfo = resolveAgentModel(nextAgent, projectDir);
|
|
933
948
|
|
|
934
949
|
// Check for parallel group — with Agent Teams override (Article XXI)
|
|
935
|
-
let action, spawnCommand = null, parallelSpawnCommand = null, parallelAgents = [];
|
|
950
|
+
let action, spawnCommand = null, parallelSpawnCommand = null, parallelSpawnCommands = [], parallelAgents = [];
|
|
936
951
|
let teamData = null;
|
|
937
952
|
const teamsEnabled = isAgentTeamsEnabled(projectDir);
|
|
938
953
|
|
|
@@ -948,7 +963,11 @@ async function _handleNextInner(projectDir) {
|
|
|
948
963
|
} else {
|
|
949
964
|
action = 'spawn_parallel';
|
|
950
965
|
parallelAgents = nextInfo.group;
|
|
951
|
-
|
|
966
|
+
if (modelInfo.source === 'installation-v2') {
|
|
967
|
+
parallelSpawnCommands = buildRoutedParallelSpawnCommands(nextInfo.group, projectDir, lastAgent);
|
|
968
|
+
} else {
|
|
969
|
+
parallelSpawnCommand = buildParallelSpawnCommand(nextInfo.group, projectDir, lastAgent, modelInfo.provider, 900000, modelInfo.model, false);
|
|
970
|
+
}
|
|
952
971
|
}
|
|
953
972
|
} else if (nextAgent === 'dev' && teamsEnabled) {
|
|
954
973
|
// Build Team auto-spawn: dev + qa-implementation are NOT parallel in the
|
|
@@ -962,10 +981,11 @@ async function _handleNextInner(projectDir) {
|
|
|
962
981
|
parallelAgents = teamConfig.members;
|
|
963
982
|
teamData = { team_id: teamId, team_type: 'build', members: teamConfig.members };
|
|
964
983
|
} else if (isInteractive) {
|
|
965
|
-
action = '
|
|
984
|
+
action = 'spawn_routed_interactive';
|
|
985
|
+
spawnCommand = buildSpawnCommand(nextAgent, projectDir, lastAgent, modelInfo.provider, 600000, modelInfo.model, modelInfo.source === 'installation-v2', modelInfo);
|
|
966
986
|
} else {
|
|
967
987
|
action = 'spawn_autonomous';
|
|
968
|
-
spawnCommand = buildSpawnCommand(nextAgent, projectDir, lastAgent, modelInfo.provider, 600000, modelInfo.model, modelInfo.source === 'installation-v2');
|
|
988
|
+
spawnCommand = buildSpawnCommand(nextAgent, projectDir, lastAgent, modelInfo.provider, 600000, modelInfo.model, modelInfo.source === 'installation-v2', modelInfo);
|
|
969
989
|
}
|
|
970
990
|
|
|
971
991
|
// Surface criteria (Article XVII): even in autonomous mode, a high-stakes
|
|
@@ -1000,6 +1020,7 @@ async function _handleNextInner(projectDir) {
|
|
|
1000
1020
|
phase: agentDef?.phase || session.mode,
|
|
1001
1021
|
spawn_command: spawnCommand,
|
|
1002
1022
|
parallel_spawn_command: parallelSpawnCommand,
|
|
1023
|
+
parallel_spawn_commands: parallelSpawnCommands,
|
|
1003
1024
|
parallel_agents: parallelAgents,
|
|
1004
1025
|
handoff_status: handoffStatus,
|
|
1005
1026
|
gate_status: gateStatus,
|
|
@@ -2418,16 +2439,13 @@ async function handleProviders(projectDir) {
|
|
|
2418
2439
|
agentModels[agentDef.name] = resolveAgentModel(agentDef.name, projectDir);
|
|
2419
2440
|
}
|
|
2420
2441
|
|
|
2421
|
-
|
|
2422
|
-
const
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
const providerMatch = raw.match(/primary_provider:\s*["']?(\w+)/);
|
|
2426
|
-
if (providerMatch) primaryProvider = providerMatch[1].toLowerCase();
|
|
2427
|
-
}
|
|
2442
|
+
const artifact = loadRuntimeInstallationV2(projectDir);
|
|
2443
|
+
const enabledProviders = artifact
|
|
2444
|
+
? artifact.installation.enabled_providers.map(({ provider_id, harness_id }) => ({ provider_id, harness_id }))
|
|
2445
|
+
: [...new Set(Object.values(agentModels).map(({ provider }) => provider).filter(Boolean))].map((harness_id) => ({ harness_id }));
|
|
2428
2446
|
|
|
2429
2447
|
return {
|
|
2430
|
-
|
|
2448
|
+
enabled_providers: enabledProviders,
|
|
2431
2449
|
agent_models: agentModels,
|
|
2432
2450
|
};
|
|
2433
2451
|
}
|
|
@@ -61,14 +61,8 @@ export function enqueueClickUpProjectionFromState({ projectDir, handoff, records
|
|
|
61
61
|
...(completion?.reviews || []),
|
|
62
62
|
...(completion?.acceptance_evidence_refs || []),
|
|
63
63
|
]);
|
|
64
|
-
const tracking =
|
|
65
|
-
|
|
66
|
-
project_evolution_dir: join(projectDir, '.chati/v2/project-evolution'),
|
|
67
|
-
clock, verify_completion: (candidate) => completionIsCanonical(records, candidate),
|
|
68
|
-
verify_reference: (ref) => refs.has(ref),
|
|
69
|
-
resolve_reference: (ref, type) => type === 'handoff'
|
|
70
|
-
? { phase: 'rail-execution', handoff_id: handoff.handoff_id }
|
|
71
|
-
: { task_state: taskState(records, handoff.handoff_id, task_id), blockers: journalBlockers(records, handoff.handoff_id, task_id) },
|
|
64
|
+
const tracking = createClickUpTrackingFromState({
|
|
65
|
+
projectDir, handoff, records, task_id, clock, extra_verified_refs: [...refs],
|
|
72
66
|
});
|
|
73
67
|
const projection = tracking.enqueue({
|
|
74
68
|
projection_id: `clickup-${sha256({ handoff_id: handoff.handoff_id, task_id, attempt_id, operation, payload }).slice(0, 24)}`,
|
|
@@ -82,3 +76,26 @@ export function enqueueClickUpProjectionFromState({ projectDir, handoff, records
|
|
|
82
76
|
}
|
|
83
77
|
return projection;
|
|
84
78
|
}
|
|
79
|
+
|
|
80
|
+
export function createClickUpTrackingFromState({ projectDir, handoff, records, task_id, clock = () => new Date(), extra_verified_refs = [] } = {}) {
|
|
81
|
+
const task = handoff.tasks.find((item) => item.task_id === task_id);
|
|
82
|
+
if (!task) throw Object.assign(new Error(`task ${task_id} is absent from handoff`), { code: 'TASK_NOT_FOUND' });
|
|
83
|
+
const handoff_ref = `rail-handoff://${handoff.handoff_id}/${handoff.seal.manifest_digest}`;
|
|
84
|
+
const journal_ref = `rail-journal://sha256/${sha256(records)}`;
|
|
85
|
+
const derivedDecisions = journalDecisions(records, handoff.handoff_id, task_id);
|
|
86
|
+
const refs = new Set([
|
|
87
|
+
handoff_ref,
|
|
88
|
+
journal_ref,
|
|
89
|
+
...derivedDecisions.flatMap((item) => [item.decision_ref, item.review_ref]),
|
|
90
|
+
...extra_verified_refs,
|
|
91
|
+
]);
|
|
92
|
+
return new TrackingClickUp({
|
|
93
|
+
outbox_path: join(projectDir, '.chati/v2/tracking/clickup-outbox.json'),
|
|
94
|
+
project_evolution_dir: join(projectDir, '.chati/v2/project-evolution'),
|
|
95
|
+
clock, verify_completion: (candidate) => completionIsCanonical(records, candidate),
|
|
96
|
+
verify_reference: (ref) => refs.has(ref),
|
|
97
|
+
resolve_reference: (ref, type) => type === 'handoff'
|
|
98
|
+
? { phase: 'rail-execution', handoff_id: handoff.handoff_id }
|
|
99
|
+
: { task_state: taskState(records, handoff.handoff_id, task_id), blockers: journalBlockers(records, handoff.handoff_id, task_id) },
|
|
100
|
+
});
|
|
101
|
+
}
|