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.
Files changed (107) hide show
  1. package/.aios-core/cli/commands/migrate/analyze.js +6 -6
  2. package/.aios-core/cli/commands/migrate/backup.js +2 -2
  3. package/.aios-core/cli/commands/migrate/execute.js +4 -4
  4. package/.aios-core/cli/commands/migrate/index.js +5 -5
  5. package/.aios-core/cli/commands/migrate/rollback.js +6 -6
  6. package/.aios-core/cli/commands/migrate/update-imports.js +2 -2
  7. package/.aios-core/cli/commands/migrate/validate.js +2 -2
  8. package/.aios-core/cli/commands/pro/index.js +52 -0
  9. package/.aios-core/cli/index.js +1 -1
  10. package/.aios-core/core/ids/registry-updater.js +29 -3
  11. package/.aios-core/core/migration/migration-config.yaml +2 -2
  12. package/.aios-core/core/migration/module-mapping.yaml +2 -2
  13. package/.aios-core/core/registry/README.md +2 -2
  14. package/.aios-core/core/synapse/context/context-builder.js +34 -0
  15. package/.aios-core/core/synapse/diagnostics/collectors/consistency-collector.js +168 -0
  16. package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +129 -0
  17. package/.aios-core/core/synapse/diagnostics/collectors/manifest-collector.js +82 -0
  18. package/.aios-core/core/synapse/diagnostics/collectors/output-analyzer.js +134 -0
  19. package/.aios-core/core/synapse/diagnostics/collectors/pipeline-collector.js +75 -0
  20. package/.aios-core/core/synapse/diagnostics/collectors/quality-collector.js +252 -0
  21. package/.aios-core/core/synapse/diagnostics/collectors/relevance-matrix.js +174 -0
  22. package/.aios-core/core/synapse/diagnostics/collectors/safe-read-json.js +31 -0
  23. package/.aios-core/core/synapse/diagnostics/collectors/session-collector.js +102 -0
  24. package/.aios-core/core/synapse/diagnostics/collectors/timing-collector.js +126 -0
  25. package/.aios-core/core/synapse/diagnostics/collectors/uap-collector.js +83 -0
  26. package/.aios-core/core/synapse/diagnostics/report-formatter.js +484 -0
  27. package/.aios-core/core/synapse/diagnostics/synapse-diagnostics.js +95 -0
  28. package/.aios-core/core/synapse/engine.js +73 -20
  29. package/.aios-core/core/synapse/runtime/hook-runtime.js +60 -0
  30. package/.aios-core/core-config.yaml +6 -0
  31. package/.aios-core/data/agent-config-requirements.yaml +2 -2
  32. package/.aios-core/data/aios-kb.md +4 -4
  33. package/.aios-core/data/entity-registry.yaml +210 -10
  34. package/.aios-core/data/registry-update-log.jsonl +52 -0
  35. package/.aios-core/development/agents/architect.md +10 -10
  36. package/.aios-core/development/agents/devops.md +93 -50
  37. package/.aios-core/development/agents/qa.md +94 -40
  38. package/.aios-core/development/agents/ux-design-expert.md +25 -25
  39. package/.aios-core/development/scripts/activation-runtime.js +63 -0
  40. package/.aios-core/development/scripts/generate-greeting.js +9 -8
  41. package/.aios-core/development/scripts/unified-activation-pipeline.js +102 -2
  42. package/.aios-core/development/tasks/{db-expansion-pack-integration.md → db-squad-integration.md} +5 -5
  43. package/.aios-core/development/tasks/{integrate-expansion-pack.md → integrate-squad.md} +2 -2
  44. package/.aios-core/development/tasks/next.md +3 -3
  45. package/.aios-core/development/tasks/pr-automation.md +2 -2
  46. package/.aios-core/development/tasks/publish-npm.md +257 -0
  47. package/.aios-core/development/tasks/release-management.md +4 -4
  48. package/.aios-core/development/tasks/setup-github.md +1 -1
  49. package/.aios-core/development/tasks/squad-creator-migrate.md +1 -1
  50. package/.aios-core/development/tasks/squad-creator-sync-ide-command.md +14 -14
  51. package/.aios-core/development/tasks/update-aios.md +1 -1
  52. package/.aios-core/development/tasks/validate-next-story.md +99 -2
  53. package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-QUICK-REFERENCE.md +1 -1
  54. package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-V2.1.md +5 -5
  55. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +21 -21
  56. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.2-SUMMARY.md +25 -25
  57. package/.aios-core/docs/standards/OPEN-SOURCE-VS-SERVICE-DIFFERENCES.md +4 -4
  58. package/.aios-core/docs/standards/QUALITY-GATES-SPECIFICATION.md +3 -3
  59. package/.aios-core/docs/standards/STANDARDS-INDEX.md +13 -13
  60. package/.aios-core/docs/standards/STORY-TEMPLATE-V2-SPECIFICATION.md +1 -1
  61. package/.aios-core/framework-config.yaml +4 -0
  62. package/.aios-core/infrastructure/scripts/codex-skills-sync/index.js +182 -0
  63. package/.aios-core/infrastructure/scripts/codex-skills-sync/validate.js +172 -0
  64. package/.aios-core/infrastructure/scripts/ide-sync/README.md +14 -0
  65. package/.aios-core/infrastructure/scripts/ide-sync/index.js +6 -0
  66. package/.aios-core/infrastructure/scripts/tool-resolver.js +4 -4
  67. package/.aios-core/infrastructure/scripts/validate-paths.js +142 -0
  68. package/.aios-core/infrastructure/templates/aios-sync.yaml.template +11 -11
  69. package/.aios-core/infrastructure/templates/github-workflows/README.md +1 -1
  70. package/.aios-core/install-manifest.yaml +193 -109
  71. package/.aios-core/local-config.yaml.template +2 -0
  72. package/.aios-core/manifests/agents.csv +29 -1
  73. package/.aios-core/manifests/tasks.csv +80 -3
  74. package/.aios-core/product/README.md +2 -2
  75. package/.aios-core/product/data/integration-patterns.md +1 -1
  76. package/.aios-core/product/templates/ide-rules/cline-rules.md +1 -1
  77. package/.aios-core/product/templates/ide-rules/codex-rules.md +65 -0
  78. package/.aios-core/product/templates/ide-rules/copilot-rules.md +1 -1
  79. package/.aios-core/product/templates/ide-rules/roo-rules.md +1 -1
  80. package/.aios-core/user-guide.md +15 -14
  81. package/.aios-core/workflow-intelligence/engine/output-formatter.js +1 -1
  82. package/.claude/hooks/synapse-engine.js +9 -20
  83. package/README.md +14 -7
  84. package/bin/aios-init.js +255 -184
  85. package/bin/aios-minimal.js +2 -2
  86. package/bin/aios.js +4 -4
  87. package/package.json +6 -1
  88. package/packages/aios-pro-cli/bin/aios-pro.js +75 -2
  89. package/packages/aios-pro-cli/package.json +5 -1
  90. package/packages/aios-pro-cli/src/recover.js +100 -0
  91. package/packages/installer/src/__tests__/performance-benchmark.js +382 -0
  92. package/packages/installer/src/config/ide-configs.js +12 -1
  93. package/packages/installer/src/config/templates/core-config-template.js +2 -2
  94. package/packages/installer/src/installer/aios-core-installer.js +2 -2
  95. package/packages/installer/src/installer/file-hasher.js +97 -0
  96. package/packages/installer/src/installer/post-install-validator.js +41 -1
  97. package/packages/installer/src/pro/pro-scaffolder.js +335 -0
  98. package/packages/installer/src/utils/aios-colors.js +2 -2
  99. package/packages/installer/src/wizard/feedback.js +1 -1
  100. package/packages/installer/src/wizard/ide-config-generator.js +2 -2
  101. package/packages/installer/src/wizard/index.js +58 -19
  102. package/packages/installer/src/wizard/pro-setup.js +931 -0
  103. package/packages/installer/src/wizard/questions.js +20 -14
  104. package/packages/installer/src/wizard/validators.js +1 -1
  105. package/scripts/code-intel-health-check.js +343 -0
  106. package/scripts/package-synapse.js +323 -0
  107. 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 };