aios-core 4.0.4 → 4.2.0
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/.aios-core/cli/commands/migrate/analyze.js +6 -6
- package/.aios-core/cli/commands/migrate/backup.js +2 -2
- package/.aios-core/cli/commands/migrate/execute.js +4 -4
- package/.aios-core/cli/commands/migrate/index.js +5 -5
- package/.aios-core/cli/commands/migrate/rollback.js +6 -6
- package/.aios-core/cli/commands/migrate/update-imports.js +2 -2
- package/.aios-core/cli/commands/migrate/validate.js +2 -2
- package/.aios-core/cli/commands/pro/index.js +52 -0
- package/.aios-core/cli/index.js +1 -1
- package/.aios-core/core/ids/registry-updater.js +29 -3
- package/.aios-core/core/migration/migration-config.yaml +2 -2
- package/.aios-core/core/migration/module-mapping.yaml +2 -2
- package/.aios-core/core/registry/README.md +2 -2
- package/.aios-core/core/synapse/context/context-builder.js +34 -0
- package/.aios-core/core/synapse/diagnostics/collectors/consistency-collector.js +168 -0
- package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +129 -0
- package/.aios-core/core/synapse/diagnostics/collectors/manifest-collector.js +82 -0
- package/.aios-core/core/synapse/diagnostics/collectors/output-analyzer.js +134 -0
- package/.aios-core/core/synapse/diagnostics/collectors/pipeline-collector.js +75 -0
- package/.aios-core/core/synapse/diagnostics/collectors/quality-collector.js +252 -0
- package/.aios-core/core/synapse/diagnostics/collectors/relevance-matrix.js +174 -0
- package/.aios-core/core/synapse/diagnostics/collectors/safe-read-json.js +31 -0
- package/.aios-core/core/synapse/diagnostics/collectors/session-collector.js +102 -0
- package/.aios-core/core/synapse/diagnostics/collectors/timing-collector.js +126 -0
- package/.aios-core/core/synapse/diagnostics/collectors/uap-collector.js +83 -0
- package/.aios-core/core/synapse/diagnostics/report-formatter.js +484 -0
- package/.aios-core/core/synapse/diagnostics/synapse-diagnostics.js +95 -0
- package/.aios-core/core/synapse/engine.js +73 -20
- package/.aios-core/core/synapse/runtime/hook-runtime.js +60 -0
- package/.aios-core/core-config.yaml +6 -0
- package/.aios-core/data/agent-config-requirements.yaml +2 -2
- package/.aios-core/data/aios-kb.md +4 -4
- package/.aios-core/data/entity-registry.yaml +210 -10
- package/.aios-core/data/registry-update-log.jsonl +52 -0
- package/.aios-core/development/agents/architect.md +10 -10
- package/.aios-core/development/agents/devops.md +93 -50
- package/.aios-core/development/agents/qa.md +94 -40
- package/.aios-core/development/agents/ux-design-expert.md +25 -25
- package/.aios-core/development/scripts/activation-runtime.js +63 -0
- package/.aios-core/development/scripts/generate-greeting.js +9 -8
- package/.aios-core/development/scripts/unified-activation-pipeline.js +102 -2
- package/.aios-core/development/tasks/{db-expansion-pack-integration.md → db-squad-integration.md} +5 -5
- package/.aios-core/development/tasks/{integrate-expansion-pack.md → integrate-squad.md} +2 -2
- package/.aios-core/development/tasks/next.md +3 -3
- package/.aios-core/development/tasks/pr-automation.md +2 -2
- package/.aios-core/development/tasks/publish-npm.md +257 -0
- package/.aios-core/development/tasks/release-management.md +4 -4
- package/.aios-core/development/tasks/setup-github.md +1 -1
- package/.aios-core/development/tasks/squad-creator-migrate.md +1 -1
- package/.aios-core/development/tasks/squad-creator-sync-ide-command.md +14 -14
- package/.aios-core/development/tasks/update-aios.md +1 -1
- package/.aios-core/development/tasks/validate-next-story.md +99 -2
- package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-QUICK-REFERENCE.md +1 -1
- package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-V2.1.md +5 -5
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +21 -21
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.2-SUMMARY.md +25 -25
- package/.aios-core/docs/standards/OPEN-SOURCE-VS-SERVICE-DIFFERENCES.md +4 -4
- package/.aios-core/docs/standards/QUALITY-GATES-SPECIFICATION.md +3 -3
- package/.aios-core/docs/standards/STANDARDS-INDEX.md +13 -13
- package/.aios-core/docs/standards/STORY-TEMPLATE-V2-SPECIFICATION.md +1 -1
- package/.aios-core/framework-config.yaml +4 -0
- package/.aios-core/infrastructure/scripts/codex-skills-sync/index.js +182 -0
- package/.aios-core/infrastructure/scripts/codex-skills-sync/validate.js +172 -0
- package/.aios-core/infrastructure/scripts/ide-sync/README.md +14 -0
- package/.aios-core/infrastructure/scripts/ide-sync/index.js +6 -0
- package/.aios-core/infrastructure/scripts/tool-resolver.js +4 -4
- package/.aios-core/infrastructure/scripts/validate-paths.js +142 -0
- package/.aios-core/infrastructure/templates/aios-sync.yaml.template +11 -11
- package/.aios-core/infrastructure/templates/github-workflows/README.md +1 -1
- package/.aios-core/install-manifest.yaml +193 -109
- package/.aios-core/local-config.yaml.template +2 -0
- package/.aios-core/manifests/agents.csv +29 -1
- package/.aios-core/manifests/tasks.csv +80 -3
- package/.aios-core/product/README.md +2 -2
- package/.aios-core/product/data/integration-patterns.md +1 -1
- package/.aios-core/product/templates/ide-rules/cline-rules.md +1 -1
- package/.aios-core/product/templates/ide-rules/codex-rules.md +65 -0
- package/.aios-core/product/templates/ide-rules/copilot-rules.md +1 -1
- package/.aios-core/product/templates/ide-rules/roo-rules.md +1 -1
- package/.aios-core/user-guide.md +15 -14
- package/.aios-core/workflow-intelligence/engine/output-formatter.js +1 -1
- package/.claude/hooks/synapse-engine.js +9 -20
- package/README.md +14 -7
- package/bin/aios-init.js +255 -184
- package/bin/aios-minimal.js +2 -2
- package/bin/aios.js +4 -4
- package/package.json +6 -1
- package/packages/aios-pro-cli/bin/aios-pro.js +75 -2
- package/packages/aios-pro-cli/package.json +5 -1
- package/packages/aios-pro-cli/src/recover.js +100 -0
- package/packages/installer/src/__tests__/performance-benchmark.js +382 -0
- package/packages/installer/src/config/ide-configs.js +12 -1
- package/packages/installer/src/config/templates/core-config-template.js +2 -2
- package/packages/installer/src/installer/aios-core-installer.js +2 -2
- package/packages/installer/src/installer/file-hasher.js +97 -0
- package/packages/installer/src/installer/post-install-validator.js +41 -1
- package/packages/installer/src/pro/pro-scaffolder.js +335 -0
- package/packages/installer/src/utils/aios-colors.js +2 -2
- package/packages/installer/src/wizard/feedback.js +1 -1
- package/packages/installer/src/wizard/ide-config-generator.js +2 -2
- package/packages/installer/src/wizard/index.js +58 -19
- package/packages/installer/src/wizard/pro-setup.js +931 -0
- package/packages/installer/src/wizard/questions.js +20 -14
- package/packages/installer/src/wizard/validators.js +1 -1
- package/scripts/code-intel-health-check.js +343 -0
- package/scripts/package-synapse.js +323 -0
- package/scripts/validate-package-completeness.js +317 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relevance Matrix — Agent-specific importance mapping for context components.
|
|
3
|
+
*
|
|
4
|
+
* Maps each UAP loader and Hook layer to an importance level per agent type.
|
|
5
|
+
* Used by diagnostics to show which context is most critical for the active agent.
|
|
6
|
+
*
|
|
7
|
+
* @module core/synapse/diagnostics/collectors/relevance-matrix
|
|
8
|
+
* @version 1.0.0
|
|
9
|
+
* @created Story SYN-14
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { safeReadJson } = require('./safe-read-json');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Importance levels for context components.
|
|
19
|
+
* @enum {string}
|
|
20
|
+
*/
|
|
21
|
+
const IMPORTANCE = {
|
|
22
|
+
CRITICAL: 'critical',
|
|
23
|
+
IMPORTANT: 'important',
|
|
24
|
+
OPTIONAL: 'optional',
|
|
25
|
+
IRRELEVANT: 'irrelevant',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Default relevance map — applies to agents without specific overrides.
|
|
30
|
+
* @type {Object.<string, string>}
|
|
31
|
+
*/
|
|
32
|
+
const DEFAULT_RELEVANCE = {
|
|
33
|
+
// UAP loaders
|
|
34
|
+
agentConfig: IMPORTANCE.CRITICAL,
|
|
35
|
+
permissionMode: IMPORTANCE.OPTIONAL,
|
|
36
|
+
gitConfig: IMPORTANCE.OPTIONAL,
|
|
37
|
+
sessionContext: IMPORTANCE.IMPORTANT,
|
|
38
|
+
projectStatus: IMPORTANCE.OPTIONAL,
|
|
39
|
+
memories: IMPORTANCE.IMPORTANT,
|
|
40
|
+
synapseSession: IMPORTANCE.OPTIONAL,
|
|
41
|
+
// Hook layers
|
|
42
|
+
constitution: IMPORTANCE.CRITICAL,
|
|
43
|
+
global: IMPORTANCE.IMPORTANT,
|
|
44
|
+
agent: IMPORTANCE.CRITICAL,
|
|
45
|
+
workflow: IMPORTANCE.OPTIONAL,
|
|
46
|
+
task: IMPORTANCE.OPTIONAL,
|
|
47
|
+
squad: IMPORTANCE.IRRELEVANT,
|
|
48
|
+
keyword: IMPORTANCE.OPTIONAL,
|
|
49
|
+
'star-command': IMPORTANCE.OPTIONAL,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Agent-specific overrides. Only components that differ from DEFAULT_RELEVANCE.
|
|
54
|
+
* @type {Object.<string, Object.<string, string>>}
|
|
55
|
+
*/
|
|
56
|
+
const AGENT_OVERRIDES = {
|
|
57
|
+
dev: {
|
|
58
|
+
gitConfig: IMPORTANCE.IMPORTANT,
|
|
59
|
+
projectStatus: IMPORTANCE.IMPORTANT,
|
|
60
|
+
workflow: IMPORTANCE.IMPORTANT,
|
|
61
|
+
task: IMPORTANCE.CRITICAL,
|
|
62
|
+
},
|
|
63
|
+
qa: {
|
|
64
|
+
projectStatus: IMPORTANCE.IMPORTANT,
|
|
65
|
+
task: IMPORTANCE.IMPORTANT,
|
|
66
|
+
},
|
|
67
|
+
devops: {
|
|
68
|
+
gitConfig: IMPORTANCE.CRITICAL,
|
|
69
|
+
permissionMode: IMPORTANCE.IMPORTANT,
|
|
70
|
+
squad: IMPORTANCE.OPTIONAL,
|
|
71
|
+
},
|
|
72
|
+
architect: {
|
|
73
|
+
projectStatus: IMPORTANCE.IMPORTANT,
|
|
74
|
+
memories: IMPORTANCE.CRITICAL,
|
|
75
|
+
},
|
|
76
|
+
pm: {
|
|
77
|
+
projectStatus: IMPORTANCE.CRITICAL,
|
|
78
|
+
workflow: IMPORTANCE.IMPORTANT,
|
|
79
|
+
},
|
|
80
|
+
sm: {
|
|
81
|
+
workflow: IMPORTANCE.IMPORTANT,
|
|
82
|
+
task: IMPORTANCE.IMPORTANT,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Build relevance matrix for a given agent.
|
|
88
|
+
*
|
|
89
|
+
* @param {string} projectRoot - Absolute path to project root
|
|
90
|
+
* @returns {{
|
|
91
|
+
* available: boolean,
|
|
92
|
+
* agentId: string,
|
|
93
|
+
* matrix: Array<{ component: string, importance: string, status: string, gap: boolean }>,
|
|
94
|
+
* gaps: Array<{ component: string, importance: string }>,
|
|
95
|
+
* score: number
|
|
96
|
+
* }}
|
|
97
|
+
*/
|
|
98
|
+
function collectRelevanceMatrix(projectRoot) {
|
|
99
|
+
const metricsDir = path.join(projectRoot, '.synapse', 'metrics');
|
|
100
|
+
|
|
101
|
+
const uapData = safeReadJson(path.join(metricsDir, 'uap-metrics.json'));
|
|
102
|
+
const hookData = safeReadJson(path.join(metricsDir, 'hook-metrics.json'));
|
|
103
|
+
const bridgeData = safeReadJson(path.join(projectRoot, '.synapse', 'sessions', '_active-agent.json'));
|
|
104
|
+
|
|
105
|
+
const agentId = (uapData && uapData.agentId) || (bridgeData && bridgeData.id) || 'unknown';
|
|
106
|
+
|
|
107
|
+
if (!uapData && !hookData) {
|
|
108
|
+
return { available: false, agentId, matrix: [], gaps: [], score: 0 };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const relevanceMap = _getRelevanceForAgent(agentId);
|
|
112
|
+
const matrix = [];
|
|
113
|
+
const gaps = [];
|
|
114
|
+
let totalWeight = 0;
|
|
115
|
+
let achievedWeight = 0;
|
|
116
|
+
|
|
117
|
+
const weightMap = { critical: 4, important: 2, optional: 1, irrelevant: 0 };
|
|
118
|
+
|
|
119
|
+
for (const [component, importance] of Object.entries(relevanceMap)) {
|
|
120
|
+
const weight = weightMap[importance] || 0;
|
|
121
|
+
totalWeight += weight;
|
|
122
|
+
|
|
123
|
+
const status = _getComponentStatus(component, uapData, hookData);
|
|
124
|
+
const gap = importance !== IMPORTANCE.IRRELEVANT && status !== 'ok' && status !== 'skipped';
|
|
125
|
+
|
|
126
|
+
if (gap && weight > 0) {
|
|
127
|
+
gaps.push({ component, importance });
|
|
128
|
+
} else {
|
|
129
|
+
achievedWeight += weight;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
matrix.push({ component, importance, status, gap });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const score = totalWeight > 0 ? Math.round((achievedWeight / totalWeight) * 100) : 0;
|
|
136
|
+
|
|
137
|
+
return { available: true, agentId, matrix, gaps, score };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get relevance map for a specific agent (default + overrides).
|
|
142
|
+
* @param {string} agentId
|
|
143
|
+
* @returns {Object.<string, string>}
|
|
144
|
+
*/
|
|
145
|
+
function _getRelevanceForAgent(agentId) {
|
|
146
|
+
const overrides = AGENT_OVERRIDES[agentId] || {};
|
|
147
|
+
return { ...DEFAULT_RELEVANCE, ...overrides };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get the status of a component from UAP or Hook data.
|
|
152
|
+
* @param {string} component
|
|
153
|
+
* @param {Object|null} uapData
|
|
154
|
+
* @param {Object|null} hookData
|
|
155
|
+
* @returns {string}
|
|
156
|
+
*/
|
|
157
|
+
function _getComponentStatus(component, uapData, hookData) {
|
|
158
|
+
// Check UAP loaders
|
|
159
|
+
if (uapData && uapData.loaders && uapData.loaders[component]) {
|
|
160
|
+
return uapData.loaders[component].status || 'unknown';
|
|
161
|
+
}
|
|
162
|
+
// Check Hook layers
|
|
163
|
+
if (hookData && hookData.perLayer && hookData.perLayer[component]) {
|
|
164
|
+
return hookData.perLayer[component].status || 'unknown';
|
|
165
|
+
}
|
|
166
|
+
return 'missing';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = {
|
|
170
|
+
collectRelevanceMatrix,
|
|
171
|
+
IMPORTANCE,
|
|
172
|
+
DEFAULT_RELEVANCE,
|
|
173
|
+
AGENT_OVERRIDES,
|
|
174
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utility — safely reads and parses a JSON file.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from multiple collectors to eliminate duplication.
|
|
5
|
+
*
|
|
6
|
+
* @module core/synapse/diagnostics/collectors/safe-read-json
|
|
7
|
+
* @version 1.0.0
|
|
8
|
+
* @created Story SYN-14 (QA refactor)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Safely read and parse a JSON file.
|
|
17
|
+
* Returns null on any error (file not found, invalid JSON, permissions, etc.)
|
|
18
|
+
*
|
|
19
|
+
* @param {string} filePath - Absolute path to JSON file
|
|
20
|
+
* @returns {Object|null} Parsed data or null
|
|
21
|
+
*/
|
|
22
|
+
function safeReadJson(filePath) {
|
|
23
|
+
try {
|
|
24
|
+
if (!fs.existsSync(filePath)) return null;
|
|
25
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = { safeReadJson };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Collector — Reads current SYNAPSE session state.
|
|
3
|
+
*
|
|
4
|
+
* Validates session fields: active_agent, prompt_count, bracket, schema.
|
|
5
|
+
*
|
|
6
|
+
* @module core/synapse/diagnostics/collectors/session-collector
|
|
7
|
+
* @version 1.0.0
|
|
8
|
+
* @created Story SYN-13
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Collect session status data.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} projectRoot - Absolute path to project root
|
|
20
|
+
* @param {string} [sessionId] - Optional session UUID to load
|
|
21
|
+
* @returns {{ fields: Array<{ field: string, expected: string, actual: string, status: string }>, raw: object|null }}
|
|
22
|
+
*/
|
|
23
|
+
function collectSessionStatus(projectRoot, sessionId) {
|
|
24
|
+
const sessionsDir = path.join(projectRoot, '.synapse', 'sessions');
|
|
25
|
+
const fields = [];
|
|
26
|
+
let raw = null;
|
|
27
|
+
|
|
28
|
+
// Try to load session by ID, or find the most recent one
|
|
29
|
+
let session = null;
|
|
30
|
+
|
|
31
|
+
if (sessionId) {
|
|
32
|
+
const sessionPath = path.join(sessionsDir, `${sessionId}.json`);
|
|
33
|
+
session = _readJsonSafe(sessionPath);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Fallback: read _active-agent.json bridge file
|
|
37
|
+
const bridgePath = path.join(sessionsDir, '_active-agent.json');
|
|
38
|
+
const bridgeData = _readJsonSafe(bridgePath);
|
|
39
|
+
|
|
40
|
+
// Check active_agent
|
|
41
|
+
const agentId = session?.active_agent?.id || bridgeData?.id || null;
|
|
42
|
+
fields.push({
|
|
43
|
+
field: 'active_agent.id',
|
|
44
|
+
expected: '(any agent)',
|
|
45
|
+
actual: agentId || '(none)',
|
|
46
|
+
status: agentId ? 'PASS' : 'WARN',
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Check activation_quality
|
|
50
|
+
const quality = session?.active_agent?.activation_quality || bridgeData?.activation_quality || null;
|
|
51
|
+
fields.push({
|
|
52
|
+
field: 'activation_quality',
|
|
53
|
+
expected: 'full|partial|fallback',
|
|
54
|
+
actual: quality || '(none)',
|
|
55
|
+
status: quality ? 'PASS' : 'WARN',
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Check prompt_count
|
|
59
|
+
const promptCount = session?.prompt_count ?? null;
|
|
60
|
+
fields.push({
|
|
61
|
+
field: 'prompt_count',
|
|
62
|
+
expected: '>= 0',
|
|
63
|
+
actual: promptCount !== null ? String(promptCount) : '(no session)',
|
|
64
|
+
status: promptCount !== null ? 'PASS' : 'INFO',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Check bracket
|
|
68
|
+
const bracket = session?.context?.last_bracket || null;
|
|
69
|
+
fields.push({
|
|
70
|
+
field: 'bracket',
|
|
71
|
+
expected: 'FRESH|MODERATE|DEPLETED|CRITICAL',
|
|
72
|
+
actual: bracket || '(no session)',
|
|
73
|
+
status: bracket ? 'PASS' : 'INFO',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Check bridge file exists
|
|
77
|
+
fields.push({
|
|
78
|
+
field: '_active-agent.json',
|
|
79
|
+
expected: 'exists',
|
|
80
|
+
actual: bridgeData ? 'exists' : 'missing',
|
|
81
|
+
status: bridgeData ? 'PASS' : 'WARN',
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
raw = { session, bridgeData };
|
|
85
|
+
return { fields, raw };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Safely read and parse a JSON file.
|
|
90
|
+
* @param {string} filePath
|
|
91
|
+
* @returns {object|null}
|
|
92
|
+
*/
|
|
93
|
+
function _readJsonSafe(filePath) {
|
|
94
|
+
try {
|
|
95
|
+
if (!fs.existsSync(filePath)) return null;
|
|
96
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
97
|
+
} catch (_) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = { collectSessionStatus };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timing Collector — Reads persisted UAP and Hook metrics for diagnostics.
|
|
3
|
+
*
|
|
4
|
+
* Reads `.synapse/metrics/uap-metrics.json` and `.synapse/metrics/hook-metrics.json`
|
|
5
|
+
* written by the UAP activation pipeline and SynapseEngine respectively.
|
|
6
|
+
*
|
|
7
|
+
* @module core/synapse/diagnostics/collectors/timing-collector
|
|
8
|
+
* @version 1.0.0
|
|
9
|
+
* @created Story SYN-12
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { safeReadJson } = require('./safe-read-json');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Loader tier mapping for UAP loaders.
|
|
19
|
+
* @type {Object.<string, string>}
|
|
20
|
+
*/
|
|
21
|
+
/** Maximum staleness threshold in ms (5 minutes). Data older than this is WARN. */
|
|
22
|
+
const MAX_STALENESS_MS = 5 * 60 * 1000;
|
|
23
|
+
|
|
24
|
+
const LOADER_TIER_MAP = {
|
|
25
|
+
agentConfig: 'Critical',
|
|
26
|
+
permissionMode: 'High',
|
|
27
|
+
gitConfig: 'High',
|
|
28
|
+
sessionContext: 'Best-effort',
|
|
29
|
+
projectStatus: 'Best-effort',
|
|
30
|
+
memories: 'Pro',
|
|
31
|
+
synapseSession: 'Bridge',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Collect timing metrics from persisted UAP and Hook metrics files.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} projectRoot - Absolute path to project root
|
|
38
|
+
* @returns {{
|
|
39
|
+
* uap: { available: boolean, totalDuration: number, quality: string, loaders: Array },
|
|
40
|
+
* hook: { available: boolean, totalDuration: number, bracket: string, layers: Array },
|
|
41
|
+
* combined: { totalMs: number }
|
|
42
|
+
* }}
|
|
43
|
+
*/
|
|
44
|
+
function collectTimingMetrics(projectRoot) {
|
|
45
|
+
const metricsDir = path.join(projectRoot, '.synapse', 'metrics');
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
|
|
48
|
+
// --- UAP Metrics ---
|
|
49
|
+
const uapData = safeReadJson(path.join(metricsDir, 'uap-metrics.json'));
|
|
50
|
+
const uap = _buildUapTiming(uapData, now);
|
|
51
|
+
|
|
52
|
+
// --- Hook Metrics ---
|
|
53
|
+
const hookData = safeReadJson(path.join(metricsDir, 'hook-metrics.json'));
|
|
54
|
+
const hook = _buildHookTiming(hookData, now);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
uap,
|
|
58
|
+
hook,
|
|
59
|
+
combined: {
|
|
60
|
+
totalMs: (uap.available ? uap.totalDuration : 0) + (hook.available ? hook.totalDuration : 0),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Build UAP timing section from persisted data.
|
|
67
|
+
* @param {Object|null} data - Parsed uap-metrics.json
|
|
68
|
+
* @returns {Object} UAP timing data
|
|
69
|
+
*/
|
|
70
|
+
function _buildUapTiming(data, now) {
|
|
71
|
+
if (!data || !data.loaders) {
|
|
72
|
+
return { available: false, totalDuration: 0, quality: 'unknown', loaders: [], stale: false, ageMs: 0 };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const ageMs = data.timestamp ? now - new Date(data.timestamp).getTime() : 0;
|
|
76
|
+
const stale = ageMs > MAX_STALENESS_MS;
|
|
77
|
+
|
|
78
|
+
const loaders = Object.entries(data.loaders).map(([name, info]) => ({
|
|
79
|
+
name,
|
|
80
|
+
duration: info.duration || 0,
|
|
81
|
+
status: info.status || 'unknown',
|
|
82
|
+
tier: LOADER_TIER_MAP[name] || 'Unknown',
|
|
83
|
+
}));
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
available: true,
|
|
87
|
+
totalDuration: data.totalDuration || 0,
|
|
88
|
+
quality: data.quality || 'unknown',
|
|
89
|
+
loaders,
|
|
90
|
+
stale,
|
|
91
|
+
ageMs,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Build Hook timing section from persisted data.
|
|
97
|
+
* @param {Object|null} data - Parsed hook-metrics.json
|
|
98
|
+
* @returns {Object} Hook timing data
|
|
99
|
+
*/
|
|
100
|
+
function _buildHookTiming(data, now) {
|
|
101
|
+
if (!data || !data.perLayer) {
|
|
102
|
+
return { available: false, totalDuration: 0, bracket: 'unknown', layers: [], stale: false, ageMs: 0 };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const ageMs = data.timestamp ? now - new Date(data.timestamp).getTime() : 0;
|
|
106
|
+
const stale = ageMs > MAX_STALENESS_MS;
|
|
107
|
+
|
|
108
|
+
const layers = Object.entries(data.perLayer).map(([name, info]) => ({
|
|
109
|
+
name,
|
|
110
|
+
duration: info.duration || 0,
|
|
111
|
+
status: info.status || 'unknown',
|
|
112
|
+
rules: info.rules || 0,
|
|
113
|
+
}));
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
available: true,
|
|
117
|
+
totalDuration: data.totalDuration || 0,
|
|
118
|
+
hookBootMs: data.hookBootMs || 0,
|
|
119
|
+
bracket: data.bracket || 'unknown',
|
|
120
|
+
layers,
|
|
121
|
+
stale,
|
|
122
|
+
ageMs,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = { collectTimingMetrics, LOADER_TIER_MAP, MAX_STALENESS_MS };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UAP Collector — Verifies UAP → SYNAPSE bridge status.
|
|
3
|
+
*
|
|
4
|
+
* Checks if _active-agent.json exists and contains valid data.
|
|
5
|
+
*
|
|
6
|
+
* @module core/synapse/diagnostics/collectors/uap-collector
|
|
7
|
+
* @version 1.0.0
|
|
8
|
+
* @created Story SYN-13
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Collect UAP bridge status data.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} projectRoot - Absolute path to project root
|
|
20
|
+
* @returns {{ checks: Array<{ name: string, status: string, detail: string }> }}
|
|
21
|
+
*/
|
|
22
|
+
function collectUapBridgeStatus(projectRoot) {
|
|
23
|
+
const checks = [];
|
|
24
|
+
const bridgePath = path.join(projectRoot, '.synapse', 'sessions', '_active-agent.json');
|
|
25
|
+
|
|
26
|
+
// Check 1: Bridge file exists
|
|
27
|
+
const exists = fs.existsSync(bridgePath);
|
|
28
|
+
checks.push({
|
|
29
|
+
name: '_active-agent.json exists',
|
|
30
|
+
status: exists ? 'PASS' : 'FAIL',
|
|
31
|
+
detail: exists ? 'Written at activation' : 'File not found — UAP may not have written it',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (!exists) {
|
|
35
|
+
checks.push({
|
|
36
|
+
name: 'active_agent matches',
|
|
37
|
+
status: 'SKIP',
|
|
38
|
+
detail: 'Bridge file does not exist',
|
|
39
|
+
});
|
|
40
|
+
return { checks };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check 2: File is valid JSON with expected fields
|
|
44
|
+
try {
|
|
45
|
+
const data = JSON.parse(fs.readFileSync(bridgePath, 'utf8'));
|
|
46
|
+
|
|
47
|
+
if (data.id) {
|
|
48
|
+
checks.push({
|
|
49
|
+
name: 'active_agent matches',
|
|
50
|
+
status: 'PASS',
|
|
51
|
+
detail: `Agent: ${data.id}, quality: ${data.activation_quality || 'unknown'}, source: ${data.source || 'unknown'}`,
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
checks.push({
|
|
55
|
+
name: 'active_agent matches',
|
|
56
|
+
status: 'FAIL',
|
|
57
|
+
detail: 'Bridge file exists but id field is missing',
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check 3: Freshness (was it written recently?)
|
|
62
|
+
if (data.activated_at) {
|
|
63
|
+
const activatedAt = new Date(data.activated_at);
|
|
64
|
+
const ageMs = Date.now() - activatedAt.getTime();
|
|
65
|
+
const ageMinutes = Math.round(ageMs / 60000);
|
|
66
|
+
checks.push({
|
|
67
|
+
name: 'Bridge freshness',
|
|
68
|
+
status: ageMinutes < 60 ? 'PASS' : 'WARN',
|
|
69
|
+
detail: `Activated ${ageMinutes}m ago${ageMinutes >= 60 ? ' (stale — may be from previous session)' : ''}`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
checks.push({
|
|
74
|
+
name: 'active_agent matches',
|
|
75
|
+
status: 'ERROR',
|
|
76
|
+
detail: `Failed to parse bridge file: ${error.message}`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { checks };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = { collectUapBridgeStatus };
|