chati-dev 1.3.3 → 2.0.1
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/README.md +7 -6
- package/framework/agents/build/dev.md +343 -0
- package/framework/agents/clarity/architect.md +113 -0
- package/framework/agents/clarity/brief.md +183 -0
- package/framework/agents/clarity/brownfield-wu.md +182 -0
- package/framework/agents/clarity/detail.md +111 -0
- package/framework/agents/clarity/greenfield-wu.md +154 -0
- package/framework/agents/clarity/phases.md +1 -0
- package/framework/agents/clarity/tasks.md +1 -0
- package/framework/agents/clarity/ux.md +113 -0
- package/framework/agents/deploy/devops.md +1 -0
- package/framework/agents/quality/qa-implementation.md +1 -0
- package/framework/agents/quality/qa-planning.md +1 -0
- package/framework/config.yaml +3 -3
- package/framework/constitution.md +58 -1
- package/framework/context/governance.md +37 -0
- package/framework/context/protocols.md +34 -0
- package/framework/context/quality.md +27 -0
- package/framework/context/root.md +24 -0
- package/framework/data/entity-registry.yaml +1 -1
- package/framework/domains/agents/architect.yaml +51 -0
- package/framework/domains/agents/brief.yaml +47 -0
- package/framework/domains/agents/brownfield-wu.yaml +49 -0
- package/framework/domains/agents/detail.yaml +47 -0
- package/framework/domains/agents/dev.yaml +49 -0
- package/framework/domains/agents/devops.yaml +43 -0
- package/framework/domains/agents/greenfield-wu.yaml +47 -0
- package/framework/domains/agents/orchestrator.yaml +49 -0
- package/framework/domains/agents/phases.yaml +47 -0
- package/framework/domains/agents/qa-implementation.yaml +43 -0
- package/framework/domains/agents/qa-planning.yaml +44 -0
- package/framework/domains/agents/tasks.yaml +48 -0
- package/framework/domains/agents/ux.yaml +50 -0
- package/framework/domains/constitution.yaml +77 -0
- package/framework/domains/global.yaml +64 -0
- package/framework/domains/workflows/brownfield-discovery.yaml +16 -0
- package/framework/domains/workflows/brownfield-fullstack.yaml +26 -0
- package/framework/domains/workflows/brownfield-service.yaml +22 -0
- package/framework/domains/workflows/brownfield-ui.yaml +22 -0
- package/framework/domains/workflows/greenfield-fullstack.yaml +26 -0
- package/framework/hooks/constitution-guard.js +101 -0
- package/framework/hooks/mode-governance.js +92 -0
- package/framework/hooks/model-governance.js +76 -0
- package/framework/hooks/prism-engine.js +89 -0
- package/framework/hooks/session-digest.js +60 -0
- package/framework/hooks/settings.json +44 -0
- package/framework/i18n/en.yaml +3 -3
- package/framework/i18n/es.yaml +3 -3
- package/framework/i18n/fr.yaml +3 -3
- package/framework/i18n/pt.yaml +3 -3
- package/framework/intelligence/context-engine.md +2 -2
- package/framework/intelligence/decision-engine.md +1 -1
- package/framework/migrations/v1.4-to-v2.0.yaml +167 -0
- package/framework/migrations/v2.0-to-v2.0.1.yaml +132 -0
- package/framework/orchestrator/chati.md +350 -7
- package/framework/schemas/session.schema.json +15 -0
- package/framework/tasks/architect-api-design.md +63 -0
- package/framework/tasks/architect-consolidate.md +47 -0
- package/framework/tasks/architect-db-design.md +73 -0
- package/framework/tasks/architect-design.md +95 -0
- package/framework/tasks/architect-security-review.md +62 -0
- package/framework/tasks/architect-stack-selection.md +53 -0
- package/framework/tasks/brief-consolidate.md +249 -0
- package/framework/tasks/brief-constraint-identify.md +277 -0
- package/framework/tasks/brief-extract-requirements.md +339 -0
- package/framework/tasks/brief-stakeholder-map.md +176 -0
- package/framework/tasks/brief-validate-completeness.md +121 -0
- package/framework/tasks/brownfield-wu-architecture-map.md +394 -0
- package/framework/tasks/brownfield-wu-deep-discovery.md +312 -0
- package/framework/tasks/brownfield-wu-dependency-scan.md +359 -0
- package/framework/tasks/brownfield-wu-migration-plan.md +483 -0
- package/framework/tasks/brownfield-wu-report.md +325 -0
- package/framework/tasks/brownfield-wu-risk-assess.md +424 -0
- package/framework/tasks/detail-acceptance-criteria.md +372 -0
- package/framework/tasks/detail-consolidate.md +138 -0
- package/framework/tasks/detail-edge-case-analysis.md +300 -0
- package/framework/tasks/detail-expand-prd.md +389 -0
- package/framework/tasks/detail-nfr-extraction.md +223 -0
- package/framework/tasks/dev-code-review.md +404 -0
- package/framework/tasks/dev-consolidate.md +543 -0
- package/framework/tasks/dev-debug.md +322 -0
- package/framework/tasks/dev-implement.md +252 -0
- package/framework/tasks/dev-iterate.md +411 -0
- package/framework/tasks/dev-pr-prepare.md +497 -0
- package/framework/tasks/dev-refactor.md +342 -0
- package/framework/tasks/dev-test-write.md +306 -0
- package/framework/tasks/devops-ci-setup.md +412 -0
- package/framework/tasks/devops-consolidate.md +712 -0
- package/framework/tasks/devops-deploy-config.md +598 -0
- package/framework/tasks/devops-monitoring-setup.md +658 -0
- package/framework/tasks/devops-release-prepare.md +673 -0
- package/framework/tasks/greenfield-wu-analyze-empty.md +169 -0
- package/framework/tasks/greenfield-wu-report.md +266 -0
- package/framework/tasks/greenfield-wu-scaffold-detection.md +203 -0
- package/framework/tasks/greenfield-wu-tech-stack-assess.md +255 -0
- package/framework/tasks/orchestrator-deviation.md +260 -0
- package/framework/tasks/orchestrator-escalate.md +276 -0
- package/framework/tasks/orchestrator-handoff.md +243 -0
- package/framework/tasks/orchestrator-health.md +372 -0
- package/framework/tasks/orchestrator-mode-switch.md +262 -0
- package/framework/tasks/orchestrator-resume.md +189 -0
- package/framework/tasks/orchestrator-route.md +169 -0
- package/framework/tasks/orchestrator-spawn-terminal.md +358 -0
- package/framework/tasks/orchestrator-status.md +260 -0
- package/framework/tasks/orchestrator-suggest-mode.md +372 -0
- package/framework/tasks/phases-breakdown.md +91 -0
- package/framework/tasks/phases-dependency-mapping.md +67 -0
- package/framework/tasks/phases-mvp-scoping.md +94 -0
- package/framework/tasks/qa-impl-consolidate.md +522 -0
- package/framework/tasks/qa-impl-performance-test.md +487 -0
- package/framework/tasks/qa-impl-regression-check.md +413 -0
- package/framework/tasks/qa-impl-sast-scan.md +402 -0
- package/framework/tasks/qa-impl-test-execute.md +344 -0
- package/framework/tasks/qa-impl-verdict.md +339 -0
- package/framework/tasks/qa-planning-consolidate.md +309 -0
- package/framework/tasks/qa-planning-coverage-plan.md +338 -0
- package/framework/tasks/qa-planning-gate-define.md +339 -0
- package/framework/tasks/qa-planning-risk-matrix.md +631 -0
- package/framework/tasks/qa-planning-test-strategy.md +217 -0
- package/framework/tasks/tasks-acceptance-write.md +75 -0
- package/framework/tasks/tasks-consolidate.md +57 -0
- package/framework/tasks/tasks-decompose.md +80 -0
- package/framework/tasks/tasks-estimate.md +66 -0
- package/framework/tasks/ux-a11y-check.md +49 -0
- package/framework/tasks/ux-component-map.md +55 -0
- package/framework/tasks/ux-consolidate.md +46 -0
- package/framework/tasks/ux-user-flow.md +46 -0
- package/framework/tasks/ux-wireframe.md +76 -0
- package/package.json +1 -1
- package/scripts/bundle-framework.js +2 -0
- package/scripts/changelog-generator.js +222 -0
- package/scripts/codebase-mapper.js +728 -0
- package/scripts/commit-message-generator.js +167 -0
- package/scripts/coverage-analyzer.js +260 -0
- package/scripts/dependency-analyzer.js +280 -0
- package/scripts/framework-analyzer.js +308 -0
- package/scripts/generate-constitution-domain.js +253 -0
- package/scripts/health-check.js +481 -0
- package/scripts/ide-sync.js +327 -0
- package/scripts/performance-analyzer.js +325 -0
- package/scripts/plan-tracker.js +278 -0
- package/scripts/populate-entity-registry.js +481 -0
- package/scripts/pr-review.js +317 -0
- package/scripts/rollback-manager.js +310 -0
- package/scripts/stuck-detector.js +343 -0
- package/scripts/test-quality-assessment.js +257 -0
- package/scripts/validate-agents.js +367 -0
- package/scripts/validate-tasks.js +465 -0
- package/src/autonomy/autonomous-gate.js +293 -0
- package/src/autonomy/index.js +51 -0
- package/src/autonomy/mode-manager.js +225 -0
- package/src/autonomy/mode-suggester.js +283 -0
- package/src/autonomy/progress-reporter.js +268 -0
- package/src/autonomy/safety-net.js +320 -0
- package/src/context/bracket-tracker.js +79 -0
- package/src/context/domain-loader.js +107 -0
- package/src/context/engine.js +144 -0
- package/src/context/formatter.js +184 -0
- package/src/context/index.js +4 -0
- package/src/context/layers/l0-constitution.js +28 -0
- package/src/context/layers/l1-global.js +37 -0
- package/src/context/layers/l2-agent.js +39 -0
- package/src/context/layers/l3-workflow.js +42 -0
- package/src/context/layers/l4-task.js +24 -0
- package/src/decision/analyzer.js +167 -0
- package/src/decision/engine.js +270 -0
- package/src/decision/index.js +38 -0
- package/src/decision/registry-healer.js +450 -0
- package/src/decision/registry-updater.js +330 -0
- package/src/gates/circuit-breaker.js +119 -0
- package/src/gates/g1-planning-complete.js +153 -0
- package/src/gates/g2-qa-planning.js +153 -0
- package/src/gates/g3-implementation.js +188 -0
- package/src/gates/g4-qa-implementation.js +207 -0
- package/src/gates/g5-deploy-ready.js +180 -0
- package/src/gates/gate-base.js +144 -0
- package/src/gates/index.js +46 -0
- package/src/installer/brownfield-upgrader.js +249 -0
- package/src/installer/core.js +55 -3
- package/src/installer/file-hasher.js +51 -0
- package/src/installer/manifest.js +117 -0
- package/src/installer/templates.js +17 -15
- package/src/installer/transaction.js +229 -0
- package/src/installer/validator.js +18 -1
- package/src/intelligence/registry-manager.js +2 -2
- package/src/memory/agent-memory.js +255 -0
- package/src/memory/gotchas-injector.js +72 -0
- package/src/memory/gotchas.js +361 -0
- package/src/memory/index.js +35 -0
- package/src/memory/search.js +233 -0
- package/src/memory/session-digest.js +239 -0
- package/src/merger/env-merger.js +112 -0
- package/src/merger/index.js +56 -0
- package/src/merger/replace-merger.js +51 -0
- package/src/merger/yaml-merger.js +127 -0
- package/src/orchestrator/agent-selector.js +285 -0
- package/src/orchestrator/deviation-handler.js +350 -0
- package/src/orchestrator/handoff-engine.js +271 -0
- package/src/orchestrator/index.js +67 -0
- package/src/orchestrator/intent-classifier.js +264 -0
- package/src/orchestrator/pipeline-manager.js +492 -0
- package/src/orchestrator/pipeline-state.js +223 -0
- package/src/orchestrator/session-manager.js +409 -0
- package/src/tasks/executor.js +195 -0
- package/src/tasks/handoff.js +226 -0
- package/src/tasks/index.js +4 -0
- package/src/tasks/loader.js +210 -0
- package/src/tasks/router.js +182 -0
- package/src/terminal/collector.js +216 -0
- package/src/terminal/index.js +30 -0
- package/src/terminal/isolation.js +129 -0
- package/src/terminal/monitor.js +277 -0
- package/src/terminal/spawner.js +269 -0
- package/src/upgrade/checker.js +1 -1
- package/src/wizard/i18n.js +3 -3
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Manager — Complete session lifecycle management.
|
|
3
|
+
*
|
|
4
|
+
* Manages session initialization, state updates, mode transitions,
|
|
5
|
+
* agent completions, and session validation for the chati.dev pipeline.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import yaml from 'js-yaml';
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
10
|
+
import { join, dirname } from 'path';
|
|
11
|
+
|
|
12
|
+
const SESSION_FILE = '.chati/session.yaml';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Default session template.
|
|
16
|
+
*/
|
|
17
|
+
const DEFAULT_SESSION = {
|
|
18
|
+
version: '1.0',
|
|
19
|
+
mode: 'clarity',
|
|
20
|
+
language: 'en',
|
|
21
|
+
project_type: 'greenfield',
|
|
22
|
+
started_at: null,
|
|
23
|
+
current_agent: '',
|
|
24
|
+
pipeline_position: 0,
|
|
25
|
+
execution_mode: 'interactive',
|
|
26
|
+
user_level: 'auto',
|
|
27
|
+
user_level_confidence: 0.0,
|
|
28
|
+
ides: [],
|
|
29
|
+
mcps: [],
|
|
30
|
+
completed_agents: [],
|
|
31
|
+
agent_results: {},
|
|
32
|
+
mode_transitions: [],
|
|
33
|
+
deviations: [],
|
|
34
|
+
agents: {
|
|
35
|
+
'greenfield-wu': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
|
|
36
|
+
'brownfield-wu': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
|
|
37
|
+
'brief': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
|
|
38
|
+
'detail': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
|
|
39
|
+
'architect': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
|
|
40
|
+
'ux': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
|
|
41
|
+
'phases': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
|
|
42
|
+
'tasks': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
|
|
43
|
+
'qa-planning': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
|
|
44
|
+
'dev': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
|
|
45
|
+
'qa-implementation': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
|
|
46
|
+
'devops': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
|
|
47
|
+
},
|
|
48
|
+
backlog: [],
|
|
49
|
+
last_handoff: '',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Initialize a new session.
|
|
54
|
+
* @param {string} projectDir
|
|
55
|
+
* @param {object} options - { mode, isGreenfield, language }
|
|
56
|
+
* @returns {{ created: boolean, session: object, path: string }}
|
|
57
|
+
*/
|
|
58
|
+
export function initSession(projectDir, options = {}) {
|
|
59
|
+
const sessionPath = join(projectDir, SESSION_FILE);
|
|
60
|
+
const dir = dirname(sessionPath);
|
|
61
|
+
|
|
62
|
+
if (!existsSync(dir)) {
|
|
63
|
+
mkdirSync(dir, { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const session = {
|
|
67
|
+
...DEFAULT_SESSION,
|
|
68
|
+
mode: options.mode || 'clarity',
|
|
69
|
+
language: options.language || 'en',
|
|
70
|
+
project_type: options.isGreenfield === false ? 'brownfield' : 'greenfield',
|
|
71
|
+
started_at: new Date().toISOString(),
|
|
72
|
+
ides: options.ides || [],
|
|
73
|
+
mcps: options.mcps || [],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const yamlContent = yaml.dump(session, {
|
|
78
|
+
lineWidth: -1,
|
|
79
|
+
noRefs: true,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
writeFileSync(sessionPath, yamlContent, 'utf-8');
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
created: true,
|
|
86
|
+
session,
|
|
87
|
+
path: sessionPath,
|
|
88
|
+
};
|
|
89
|
+
} catch (err) {
|
|
90
|
+
return {
|
|
91
|
+
created: false,
|
|
92
|
+
session: null,
|
|
93
|
+
path: sessionPath,
|
|
94
|
+
error: `Failed to create session: ${err.message}`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Load current session state.
|
|
101
|
+
* @param {string} projectDir
|
|
102
|
+
* @returns {{ loaded: boolean, session: object|null, error: string|null }}
|
|
103
|
+
*/
|
|
104
|
+
export function loadSession(projectDir) {
|
|
105
|
+
const sessionPath = join(projectDir, SESSION_FILE);
|
|
106
|
+
|
|
107
|
+
if (!existsSync(sessionPath)) {
|
|
108
|
+
return {
|
|
109
|
+
loaded: false,
|
|
110
|
+
session: null,
|
|
111
|
+
error: 'Session file not found',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const content = readFileSync(sessionPath, 'utf-8');
|
|
117
|
+
const session = yaml.load(content);
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
loaded: true,
|
|
121
|
+
session,
|
|
122
|
+
error: null,
|
|
123
|
+
};
|
|
124
|
+
} catch (err) {
|
|
125
|
+
return {
|
|
126
|
+
loaded: false,
|
|
127
|
+
session: null,
|
|
128
|
+
error: `Failed to load session: ${err.message}`,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Update session state (merge with existing).
|
|
135
|
+
* @param {string} projectDir
|
|
136
|
+
* @param {object} updates - Partial session updates
|
|
137
|
+
* @returns {{ saved: boolean }}
|
|
138
|
+
*/
|
|
139
|
+
export function updateSession(projectDir, updates) {
|
|
140
|
+
const sessionPath = join(projectDir, SESSION_FILE);
|
|
141
|
+
|
|
142
|
+
// Load existing session
|
|
143
|
+
const loadResult = loadSession(projectDir);
|
|
144
|
+
|
|
145
|
+
if (!loadResult.loaded) {
|
|
146
|
+
return {
|
|
147
|
+
saved: false,
|
|
148
|
+
error: loadResult.error,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
// Merge updates
|
|
154
|
+
const merged = {
|
|
155
|
+
...loadResult.session,
|
|
156
|
+
...updates,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Deep merge agents if provided
|
|
160
|
+
if (updates.agents) {
|
|
161
|
+
merged.agents = {
|
|
162
|
+
...loadResult.session.agents,
|
|
163
|
+
...updates.agents,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Merge individual agent data
|
|
167
|
+
for (const [agentName, agentData] of Object.entries(updates.agents)) {
|
|
168
|
+
merged.agents[agentName] = {
|
|
169
|
+
...loadResult.session.agents[agentName],
|
|
170
|
+
...agentData,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Write back
|
|
176
|
+
const yamlContent = yaml.dump(merged, {
|
|
177
|
+
lineWidth: -1,
|
|
178
|
+
noRefs: true,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
writeFileSync(sessionPath, yamlContent, 'utf-8');
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
saved: true,
|
|
185
|
+
session: merged,
|
|
186
|
+
};
|
|
187
|
+
} catch (err) {
|
|
188
|
+
return {
|
|
189
|
+
saved: false,
|
|
190
|
+
error: `Failed to update session: ${err.message}`,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Record a mode transition in session history.
|
|
197
|
+
* @param {string} projectDir
|
|
198
|
+
* @param {object} transition - { from, to, trigger, reason }
|
|
199
|
+
* @returns {{ saved: boolean }}
|
|
200
|
+
*/
|
|
201
|
+
export function recordModeTransition(projectDir, transition) {
|
|
202
|
+
const loadResult = loadSession(projectDir);
|
|
203
|
+
|
|
204
|
+
if (!loadResult.loaded) {
|
|
205
|
+
return {
|
|
206
|
+
saved: false,
|
|
207
|
+
error: loadResult.error,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const session = loadResult.session;
|
|
212
|
+
|
|
213
|
+
// Add transition to history
|
|
214
|
+
session.mode_transitions = session.mode_transitions || [];
|
|
215
|
+
session.mode_transitions.push({
|
|
216
|
+
from: transition.from,
|
|
217
|
+
to: transition.to,
|
|
218
|
+
trigger: transition.trigger || 'manual',
|
|
219
|
+
reason: transition.reason || '',
|
|
220
|
+
timestamp: new Date().toISOString(),
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Update current mode
|
|
224
|
+
session.mode = transition.to;
|
|
225
|
+
|
|
226
|
+
return updateSession(projectDir, session);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Record an agent completion in session.
|
|
231
|
+
* @param {string} projectDir
|
|
232
|
+
* @param {object} completion - { agent, status, score, outputs }
|
|
233
|
+
* @returns {{ saved: boolean }}
|
|
234
|
+
*/
|
|
235
|
+
export function recordAgentCompletion(projectDir, completion) {
|
|
236
|
+
const loadResult = loadSession(projectDir);
|
|
237
|
+
|
|
238
|
+
if (!loadResult.loaded) {
|
|
239
|
+
return {
|
|
240
|
+
saved: false,
|
|
241
|
+
error: loadResult.error,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const session = loadResult.session;
|
|
246
|
+
const { agent, status, score, outputs = [] } = completion;
|
|
247
|
+
|
|
248
|
+
// Update agent data
|
|
249
|
+
if (!session.agents[agent]) {
|
|
250
|
+
session.agents[agent] = {
|
|
251
|
+
status: 'pending',
|
|
252
|
+
score: 0,
|
|
253
|
+
criteria_count: 0,
|
|
254
|
+
completed_at: null,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
session.agents[agent].status = status;
|
|
259
|
+
session.agents[agent].score = score || 0;
|
|
260
|
+
session.agents[agent].completed_at = new Date().toISOString();
|
|
261
|
+
|
|
262
|
+
// Add to completed agents list
|
|
263
|
+
session.completed_agents = session.completed_agents || [];
|
|
264
|
+
if (status === 'completed' && !session.completed_agents.includes(agent)) {
|
|
265
|
+
session.completed_agents.push(agent);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Store detailed results
|
|
269
|
+
session.agent_results = session.agent_results || {};
|
|
270
|
+
session.agent_results[agent] = {
|
|
271
|
+
status,
|
|
272
|
+
score,
|
|
273
|
+
outputs,
|
|
274
|
+
completed_at: session.agents[agent].completed_at,
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// Update last handoff
|
|
278
|
+
session.last_handoff = agent;
|
|
279
|
+
|
|
280
|
+
return updateSession(projectDir, session);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Get session summary for display.
|
|
285
|
+
* @param {string} projectDir
|
|
286
|
+
* @returns {object} Summary with progress, mode, agents, timeline
|
|
287
|
+
*/
|
|
288
|
+
export function getSessionSummary(projectDir) {
|
|
289
|
+
const loadResult = loadSession(projectDir);
|
|
290
|
+
|
|
291
|
+
if (!loadResult.loaded) {
|
|
292
|
+
return {
|
|
293
|
+
error: loadResult.error,
|
|
294
|
+
summary: null,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const session = loadResult.session;
|
|
299
|
+
|
|
300
|
+
// Calculate progress
|
|
301
|
+
const totalAgents = Object.keys(session.agents || {}).length;
|
|
302
|
+
const completedCount = (session.completed_agents || []).length;
|
|
303
|
+
const progressPercent = totalAgents > 0
|
|
304
|
+
? Math.round((completedCount / totalAgents) * 100)
|
|
305
|
+
: 0;
|
|
306
|
+
|
|
307
|
+
// Get active agents
|
|
308
|
+
const activeAgents = Object.entries(session.agents || {})
|
|
309
|
+
.filter(([, data]) => data.status === 'in_progress')
|
|
310
|
+
.map(([name]) => name);
|
|
311
|
+
|
|
312
|
+
// Calculate duration
|
|
313
|
+
let durationMs = 0;
|
|
314
|
+
if (session.started_at) {
|
|
315
|
+
durationMs = Date.now() - new Date(session.started_at).getTime();
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const durationHours = Math.floor(durationMs / (1000 * 60 * 60));
|
|
319
|
+
const durationMinutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60));
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
mode: session.mode,
|
|
323
|
+
language: session.language,
|
|
324
|
+
project_type: session.project_type,
|
|
325
|
+
current_agent: session.current_agent,
|
|
326
|
+
progress: {
|
|
327
|
+
percent: progressPercent,
|
|
328
|
+
completed: completedCount,
|
|
329
|
+
total: totalAgents,
|
|
330
|
+
},
|
|
331
|
+
active_agents: activeAgents,
|
|
332
|
+
completed_agents: session.completed_agents || [],
|
|
333
|
+
duration: {
|
|
334
|
+
ms: durationMs,
|
|
335
|
+
hours: durationHours,
|
|
336
|
+
minutes: durationMinutes,
|
|
337
|
+
formatted: `${durationHours}h ${durationMinutes}m`,
|
|
338
|
+
},
|
|
339
|
+
mode_transitions: (session.mode_transitions || []).length,
|
|
340
|
+
deviations: (session.deviations || []).length,
|
|
341
|
+
started_at: session.started_at,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Check if a session exists and is valid.
|
|
347
|
+
* @param {string} projectDir
|
|
348
|
+
* @returns {{ exists: boolean, valid: boolean, reason: string }}
|
|
349
|
+
*/
|
|
350
|
+
export function validateSession(projectDir) {
|
|
351
|
+
const sessionPath = join(projectDir, SESSION_FILE);
|
|
352
|
+
|
|
353
|
+
if (!existsSync(sessionPath)) {
|
|
354
|
+
return {
|
|
355
|
+
exists: false,
|
|
356
|
+
valid: false,
|
|
357
|
+
reason: 'Session file does not exist',
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const loadResult = loadSession(projectDir);
|
|
362
|
+
|
|
363
|
+
if (!loadResult.loaded) {
|
|
364
|
+
return {
|
|
365
|
+
exists: true,
|
|
366
|
+
valid: false,
|
|
367
|
+
reason: loadResult.error,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const session = loadResult.session;
|
|
372
|
+
|
|
373
|
+
// Validate required fields
|
|
374
|
+
const requiredFields = ['version', 'mode', 'language', 'project_type', 'agents'];
|
|
375
|
+
for (const field of requiredFields) {
|
|
376
|
+
if (!session[field]) {
|
|
377
|
+
return {
|
|
378
|
+
exists: true,
|
|
379
|
+
valid: false,
|
|
380
|
+
reason: `Missing required field: ${field}`,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Validate mode
|
|
386
|
+
const validModes = ['clarity', 'build', 'validate', 'deploy', 'completed'];
|
|
387
|
+
if (!validModes.includes(session.mode)) {
|
|
388
|
+
return {
|
|
389
|
+
exists: true,
|
|
390
|
+
valid: false,
|
|
391
|
+
reason: `Invalid mode: ${session.mode}`,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Validate agents structure
|
|
396
|
+
if (typeof session.agents !== 'object') {
|
|
397
|
+
return {
|
|
398
|
+
exists: true,
|
|
399
|
+
valid: false,
|
|
400
|
+
reason: 'Invalid agents structure',
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
exists: true,
|
|
406
|
+
valid: true,
|
|
407
|
+
reason: 'Session is valid',
|
|
408
|
+
};
|
|
409
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Executor — Executes a task definition with PRISM context injection.
|
|
3
|
+
*
|
|
4
|
+
* The executor prepares the full execution context for an agent by:
|
|
5
|
+
* 1. Loading the task definition
|
|
6
|
+
* 2. Injecting PRISM context layers
|
|
7
|
+
* 3. Building the instruction payload
|
|
8
|
+
* 4. Validating criteria post-execution (for autonomous mode)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Build the execution payload for a task.
|
|
13
|
+
*
|
|
14
|
+
* This produces the complete context that gets injected into
|
|
15
|
+
* the agent's prompt when executing a task.
|
|
16
|
+
*
|
|
17
|
+
* @param {object} task - Task definition from loader
|
|
18
|
+
* @param {object} options
|
|
19
|
+
* @param {object} [options.prismContext] - PRISM context output (xml, bracket, layers)
|
|
20
|
+
* @param {object} [options.handoff] - Handoff data from previous agent
|
|
21
|
+
* @param {object} [options.sessionState] - Current session state
|
|
22
|
+
* @param {string} [options.mode] - Execution mode ('human-in-the-loop' | 'autonomous')
|
|
23
|
+
* @returns {{ payload: object, warnings: string[] }}
|
|
24
|
+
*/
|
|
25
|
+
export function buildExecutionPayload(task, options = {}) {
|
|
26
|
+
const warnings = [];
|
|
27
|
+
|
|
28
|
+
if (!task) {
|
|
29
|
+
return { payload: null, warnings: ['No task provided to executor.'] };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const mode = options.mode || 'human-in-the-loop';
|
|
33
|
+
const isAutonomous = mode === 'autonomous';
|
|
34
|
+
|
|
35
|
+
// Warn if task requires input but we're in autonomous mode
|
|
36
|
+
if (task.requires_input && isAutonomous) {
|
|
37
|
+
warnings.push(
|
|
38
|
+
`Task '${task.id}' requires user input but running in autonomous mode. ` +
|
|
39
|
+
'Gate will pause for input.'
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Warn if task doesn't support autonomous gate but mode is autonomous
|
|
44
|
+
if (isAutonomous && !task.autonomous_gate) {
|
|
45
|
+
warnings.push(
|
|
46
|
+
`Task '${task.id}' does not have autonomous_gate enabled. ` +
|
|
47
|
+
'Human validation will be required.'
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const payload = {
|
|
52
|
+
task: {
|
|
53
|
+
id: task.id,
|
|
54
|
+
agent: task.agent,
|
|
55
|
+
phase: task.phase,
|
|
56
|
+
instructions: task.instructions,
|
|
57
|
+
criteria: task.criteria,
|
|
58
|
+
outputs: task.outputs,
|
|
59
|
+
handoff_to: task.handoff_to,
|
|
60
|
+
},
|
|
61
|
+
context: {
|
|
62
|
+
prism: options.prismContext || null,
|
|
63
|
+
handoff: options.handoff || null,
|
|
64
|
+
session: sanitizeSessionState(options.sessionState),
|
|
65
|
+
},
|
|
66
|
+
execution: {
|
|
67
|
+
mode,
|
|
68
|
+
autonomous_gate: task.autonomous_gate,
|
|
69
|
+
requires_input: task.requires_input,
|
|
70
|
+
parallelizable: task.parallelizable,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return { payload, warnings };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Validate task execution results against criteria.
|
|
79
|
+
*
|
|
80
|
+
* Each criterion is a string describing what must be true.
|
|
81
|
+
* The validator returns a structured result for the orchestrator
|
|
82
|
+
* to decide whether to proceed or require intervention.
|
|
83
|
+
*
|
|
84
|
+
* @param {object} task - Task definition
|
|
85
|
+
* @param {object} results - Execution results to validate
|
|
86
|
+
* @param {string[]} [results.completedCriteria] - Criteria marked as met
|
|
87
|
+
* @param {string[]} [results.outputs] - Produced output files
|
|
88
|
+
* @param {number} [results.confidence] - Self-reported confidence (0-100)
|
|
89
|
+
* @returns {{ valid: boolean, score: number, unmet: string[], details: string }}
|
|
90
|
+
*/
|
|
91
|
+
export function validateResults(task, results = {}) {
|
|
92
|
+
if (!task || !task.criteria || task.criteria.length === 0) {
|
|
93
|
+
return { valid: true, score: 100, unmet: [], details: 'No criteria defined.' };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const completed = new Set(results.completedCriteria || []);
|
|
97
|
+
const unmet = [];
|
|
98
|
+
|
|
99
|
+
for (const criterion of task.criteria) {
|
|
100
|
+
if (!completed.has(criterion)) {
|
|
101
|
+
unmet.push(criterion);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const score = Math.round(((task.criteria.length - unmet.length) / task.criteria.length) * 100);
|
|
106
|
+
|
|
107
|
+
// Check outputs
|
|
108
|
+
const expectedOutputs = task.outputs || [];
|
|
109
|
+
const producedOutputs = results.outputs || [];
|
|
110
|
+
const missingOutputs = expectedOutputs.filter(o => !producedOutputs.includes(o));
|
|
111
|
+
|
|
112
|
+
if (missingOutputs.length > 0) {
|
|
113
|
+
unmet.push(`Missing outputs: ${missingOutputs.join(', ')}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Confidence check for autonomous mode
|
|
117
|
+
const confidence = results.confidence ?? 100;
|
|
118
|
+
const confidenceMet = confidence >= 90;
|
|
119
|
+
|
|
120
|
+
const valid = unmet.length === 0 && confidenceMet;
|
|
121
|
+
|
|
122
|
+
let details;
|
|
123
|
+
if (valid) {
|
|
124
|
+
details = `All ${task.criteria.length} criteria met. Confidence: ${confidence}%.`;
|
|
125
|
+
} else {
|
|
126
|
+
const parts = [];
|
|
127
|
+
if (unmet.length > 0) parts.push(`${unmet.length} unmet criteria`);
|
|
128
|
+
if (!confidenceMet) parts.push(`Low confidence: ${confidence}%`);
|
|
129
|
+
details = parts.join('. ');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return { valid, score, unmet, details };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Determine the post-execution action based on validation results.
|
|
137
|
+
*
|
|
138
|
+
* @param {object} validation - Result from validateResults
|
|
139
|
+
* @param {string} mode - 'autonomous' | 'human-in-the-loop'
|
|
140
|
+
* @param {object} task - Task definition
|
|
141
|
+
* @returns {{ action: string, reason: string }}
|
|
142
|
+
* action: 'proceed' | 'retry' | 'escalate' | 'gate'
|
|
143
|
+
*/
|
|
144
|
+
export function determinePostAction(validation, mode, task) {
|
|
145
|
+
// Human-in-the-loop always gates
|
|
146
|
+
if (mode !== 'autonomous') {
|
|
147
|
+
return {
|
|
148
|
+
action: 'gate',
|
|
149
|
+
reason: 'Human-in-the-loop mode requires user approval.',
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Autonomous mode with autonomous gate
|
|
154
|
+
if (validation.valid) {
|
|
155
|
+
return {
|
|
156
|
+
action: 'proceed',
|
|
157
|
+
reason: `All criteria met (score: ${validation.score}%). Proceeding to ${task.handoff_to || 'next'}.`,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Partial pass — try to determine severity
|
|
162
|
+
if (validation.score >= 90) {
|
|
163
|
+
return {
|
|
164
|
+
action: 'gate',
|
|
165
|
+
reason: `Score ${validation.score}% is close but below threshold. Human review recommended.`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (validation.score >= 50) {
|
|
170
|
+
return {
|
|
171
|
+
action: 'retry',
|
|
172
|
+
reason: `Score ${validation.score}%. ${validation.unmet.length} criteria unmet. Retry with focused attention.`,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
action: 'escalate',
|
|
178
|
+
reason: `Score ${validation.score}%. Major gaps detected. Escalating to orchestrator.`,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Sanitize session state to remove sensitive or oversized data.
|
|
184
|
+
*/
|
|
185
|
+
function sanitizeSessionState(state) {
|
|
186
|
+
if (!state) return null;
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
mode: state.mode || 'clarity',
|
|
190
|
+
current_agent: state.current_agent || null,
|
|
191
|
+
pipeline_position: state.pipeline_position || null,
|
|
192
|
+
completed_agents: state.completed_agents || [],
|
|
193
|
+
phase: state.phase || null,
|
|
194
|
+
};
|
|
195
|
+
}
|