auroq-os 1.0.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/.auroq-core/constitution.md +153 -0
- package/.auroq-core/core/synapse/context/context-builder.js +34 -0
- package/.auroq-core/core/synapse/context/context-tracker.js +198 -0
- package/.auroq-core/core/synapse/diagnostics/collectors/consistency-collector.js +168 -0
- package/.auroq-core/core/synapse/diagnostics/collectors/hook-collector.js +129 -0
- package/.auroq-core/core/synapse/diagnostics/collectors/manifest-collector.js +82 -0
- package/.auroq-core/core/synapse/diagnostics/collectors/output-analyzer.js +134 -0
- package/.auroq-core/core/synapse/diagnostics/collectors/pipeline-collector.js +75 -0
- package/.auroq-core/core/synapse/diagnostics/collectors/quality-collector.js +252 -0
- package/.auroq-core/core/synapse/diagnostics/collectors/relevance-matrix.js +174 -0
- package/.auroq-core/core/synapse/diagnostics/collectors/safe-read-json.js +31 -0
- package/.auroq-core/core/synapse/diagnostics/collectors/session-collector.js +102 -0
- package/.auroq-core/core/synapse/diagnostics/collectors/timing-collector.js +126 -0
- package/.auroq-core/core/synapse/diagnostics/collectors/uap-collector.js +83 -0
- package/.auroq-core/core/synapse/diagnostics/report-formatter.js +484 -0
- package/.auroq-core/core/synapse/diagnostics/synapse-diagnostics.js +95 -0
- package/.auroq-core/core/synapse/domain/domain-loader.js +322 -0
- package/.auroq-core/core/synapse/engine.js +400 -0
- package/.auroq-core/core/synapse/layers/l0-constitution.js +80 -0
- package/.auroq-core/core/synapse/layers/l1-global.js +102 -0
- package/.auroq-core/core/synapse/layers/l2-agent.js +94 -0
- package/.auroq-core/core/synapse/layers/l3-workflow.js +94 -0
- package/.auroq-core/core/synapse/layers/l4-task.js +83 -0
- package/.auroq-core/core/synapse/layers/l5-squad.js +244 -0
- package/.auroq-core/core/synapse/layers/l6-keyword.js +154 -0
- package/.auroq-core/core/synapse/layers/l7-star-command.js +169 -0
- package/.auroq-core/core/synapse/layers/layer-processor.js +82 -0
- package/.auroq-core/core/synapse/memory/memory-bridge.js +220 -0
- package/.auroq-core/core/synapse/memory/synapse-memory-provider.js +201 -0
- package/.auroq-core/core/synapse/output/formatter.js +561 -0
- package/.auroq-core/core/synapse/runtime/hook-runtime.js +98 -0
- package/.auroq-core/core/synapse/scripts/generate-constitution.js +204 -0
- package/.auroq-core/core/synapse/session/session-manager.js +404 -0
- package/.auroq-core/core/synapse/utils/atomic-write.js +79 -0
- package/.auroq-core/core/synapse/utils/paths.js +57 -0
- package/.auroq-core/core/synapse/utils/tokens.js +25 -0
- package/.auroq-core/core-config.yaml +80 -0
- package/.auroq-core/development/sistema-gestao-projetos.md +193 -0
- package/.auroq-core/development/sistema-memoria.md +157 -0
- package/.auroq-core/dna-operacional.md +134 -0
- package/.claude/CLAUDE.md +399 -0
- package/.claude/commands/AuroqOS/agents/forge.md +374 -0
- package/.claude/commands/AuroqOS/agents/ops.md +277 -0
- package/.claude/commands/companion.md +7 -0
- package/.claude/hooks/precompact-session-digest.cjs +46 -0
- package/.claude/hooks/synapse-engine.cjs +99 -0
- package/.claude/rules/agent-authority.md +79 -0
- package/.claude/rules/agent-handoff.md +97 -0
- package/.claude/rules/commit-inteligente.md +86 -0
- package/.claude/rules/dna-operacional.md +53 -0
- package/.claude/rules/evolucao-incremental.md +43 -0
- package/.claude/rules/mcp-usage.md +38 -0
- package/.claude/rules/memoria-inteligente.md +65 -0
- package/.claude/rules/project-tracker.md +49 -0
- package/.claude/rules/tool-response-filtering.md +57 -0
- package/.claude/settings.json +1 -0
- package/.claude/settings.local.json +33 -0
- package/.env.example +22 -0
- package/.gitignore +23 -0
- package/.synapse/constitution +21 -0
- package/.synapse/context +8 -0
- package/.synapse/global +10 -0
- package/.synapse/manifest +35 -0
- package/README.md +67 -0
- package/agents/companion/agents/companion.md +306 -0
- package/agents/companion/data/contexto-dinamico.md +23 -0
- package/agents/companion/data/demandas-backlog.md +19 -0
- package/agents/companion/data/log-decisoes.md +11 -0
- package/agents/companion/data/padroes-observados.md +10 -0
- package/agents/companion/knowledge/companion-foundation.md +49 -0
- package/agents/companion/knowledge/expert-essencial.md +30 -0
- package/agents/companion/knowledge/modus-operandi.md +161 -0
- package/agents/companion/tasks/new-project.md +111 -0
- package/agents/companion/tasks/save-memory.md +73 -0
- package/agents/companion/tasks/start.md +136 -0
- package/agents/companion/tasks/weekly-review.md +82 -0
- package/bin/auroq-os.js +346 -0
- package/business/campanhas/_template/BRIEFING.md +45 -0
- package/business/campanhas/_template/CHECKLIST.md +29 -0
- package/business/campanhas/_template/DIARIO.md +15 -0
- package/business/campanhas/_template/RETROVISOR.md +34 -0
- package/business/cockpit.md +72 -0
- package/business/templates/tracker-template.md +71 -0
- package/docs/knowledge/expert-business/posicionamento.md +27 -0
- package/docs/knowledge/expert-business/publico-alvo.md +39 -0
- package/docs/knowledge/expert-mind/historia.md +27 -0
- package/docs/knowledge/expert-mind/identidade.md +23 -0
- package/docs/knowledge/expert-mind/tom-de-voz.md +30 -0
- package/docs/knowledge/expert-mind/valores.md +32 -0
- package/package.json +26 -0
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SYNAPSE Output Formatter
|
|
3
|
+
*
|
|
4
|
+
* Converts layer processor results into the <synapse-rules> XML block
|
|
5
|
+
* that is injected into each user prompt. Enforces section ordering
|
|
6
|
+
* per DESIGN doc section 14 and applies token budget truncation.
|
|
7
|
+
*
|
|
8
|
+
* @module core/synapse/output/formatter
|
|
9
|
+
* @version 1.0.0
|
|
10
|
+
* @created Story SYN-6 - SynapseEngine Orchestrator + Output Formatter
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { estimateTokens } = require('../utils/tokens');
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Section ordering (DESIGN doc section 14)
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Ordered list of section identifiers matching layer outputs.
|
|
21
|
+
* Sections are rendered in this order; null results are omitted.
|
|
22
|
+
*
|
|
23
|
+
* Truncation removes from the END of this array first (SUMMARY, then
|
|
24
|
+
* KEYWORD, then SQUAD, etc.) to preserve highest-priority sections.
|
|
25
|
+
*/
|
|
26
|
+
const SECTION_ORDER = [
|
|
27
|
+
'CONTEXT_BRACKET',
|
|
28
|
+
'CONSTITUTION',
|
|
29
|
+
'AGENT',
|
|
30
|
+
'WORKFLOW',
|
|
31
|
+
'TASK',
|
|
32
|
+
'SQUAD',
|
|
33
|
+
'KEYWORD',
|
|
34
|
+
'MEMORY_HINTS',
|
|
35
|
+
'STAR_COMMANDS',
|
|
36
|
+
'DEVMODE',
|
|
37
|
+
'SUMMARY',
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Map layer names to section identifiers.
|
|
42
|
+
*/
|
|
43
|
+
const LAYER_TO_SECTION = {
|
|
44
|
+
constitution: 'CONSTITUTION',
|
|
45
|
+
global: 'CONTEXT_BRACKET', // global rules go into bracket section
|
|
46
|
+
agent: 'AGENT',
|
|
47
|
+
workflow: 'WORKFLOW',
|
|
48
|
+
task: 'TASK',
|
|
49
|
+
squad: 'SQUAD',
|
|
50
|
+
keyword: 'KEYWORD',
|
|
51
|
+
memory: 'MEMORY_HINTS',
|
|
52
|
+
'star-command': 'STAR_COMMANDS',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Section Formatters
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Format the CONTEXT BRACKET header section.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} bracket - Current bracket name
|
|
63
|
+
* @param {number} contextPercent - Remaining context %
|
|
64
|
+
* @param {object[]} globalResults - Results from global/context layers
|
|
65
|
+
* @returns {string}
|
|
66
|
+
*/
|
|
67
|
+
function formatContextBracket(bracket, contextPercent, globalResults) {
|
|
68
|
+
const lines = [`[CONTEXT BRACKET]\nCONTEXT BRACKET: [${bracket}] (${contextPercent.toFixed(1)}% remaining)`];
|
|
69
|
+
|
|
70
|
+
// Include global/context rules if present
|
|
71
|
+
for (const result of globalResults) {
|
|
72
|
+
if (result && result.rules && result.rules.length > 0) {
|
|
73
|
+
lines.push(`[${bracket}] CONTEXT RULES:`);
|
|
74
|
+
result.rules.forEach((rule, i) => {
|
|
75
|
+
lines.push(` ${i + 1}. ${rule}`);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return lines.join('\n');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Format the CONSTITUTION section.
|
|
85
|
+
*
|
|
86
|
+
* @param {object} result - Layer result { rules, metadata }
|
|
87
|
+
* @returns {string}
|
|
88
|
+
*/
|
|
89
|
+
function formatConstitution(result) {
|
|
90
|
+
const lines = ['[CONSTITUTION] (NON-NEGOTIABLE)'];
|
|
91
|
+
for (const rule of result.rules) {
|
|
92
|
+
lines.push(` ${rule}`);
|
|
93
|
+
}
|
|
94
|
+
return lines.join('\n');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Format the AGENT section.
|
|
99
|
+
*
|
|
100
|
+
* @param {object} result - Layer result { rules, metadata }
|
|
101
|
+
* @returns {string}
|
|
102
|
+
*/
|
|
103
|
+
function formatAgent(result) {
|
|
104
|
+
const meta = result.metadata || {};
|
|
105
|
+
const agentId = meta.agentId || meta.source || 'unknown';
|
|
106
|
+
const domain = meta.domain || meta.source || '';
|
|
107
|
+
const lines = [`[ACTIVE AGENT: @${agentId}]`];
|
|
108
|
+
|
|
109
|
+
if (domain) {
|
|
110
|
+
lines.push(` DOMAIN: ${domain}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (meta.authority && Array.isArray(meta.authority)) {
|
|
114
|
+
lines.push(' AUTHORITY BOUNDARIES:');
|
|
115
|
+
for (const auth of meta.authority) {
|
|
116
|
+
lines.push(` - ${auth}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (result.rules.length > 0) {
|
|
121
|
+
lines.push(' RULES:');
|
|
122
|
+
result.rules.forEach((rule, i) => {
|
|
123
|
+
lines.push(` ${i}. ${rule}`);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return lines.join('\n');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Format the WORKFLOW section.
|
|
132
|
+
*
|
|
133
|
+
* @param {object} result - Layer result { rules, metadata }
|
|
134
|
+
* @returns {string}
|
|
135
|
+
*/
|
|
136
|
+
function formatWorkflow(result) {
|
|
137
|
+
const meta = result.metadata || {};
|
|
138
|
+
const workflowId = meta.workflowId || meta.source || 'unknown';
|
|
139
|
+
const phase = meta.phase || '';
|
|
140
|
+
const lines = [`[ACTIVE WORKFLOW: ${workflowId}]`];
|
|
141
|
+
|
|
142
|
+
if (phase) {
|
|
143
|
+
lines.push(` PHASE: ${phase}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (result.rules.length > 0) {
|
|
147
|
+
lines.push(' RULES:');
|
|
148
|
+
result.rules.forEach((rule, i) => {
|
|
149
|
+
lines.push(` ${i}. ${rule}`);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return lines.join('\n');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Format the TASK section.
|
|
158
|
+
*
|
|
159
|
+
* @param {object} result - Layer result { rules, metadata }
|
|
160
|
+
* @returns {string}
|
|
161
|
+
*/
|
|
162
|
+
function formatTask(result) {
|
|
163
|
+
const meta = result.metadata || {};
|
|
164
|
+
const lines = ['[TASK CONTEXT]'];
|
|
165
|
+
|
|
166
|
+
if (meta.taskId) {
|
|
167
|
+
lines.push(` Active Task: ${meta.taskId}`);
|
|
168
|
+
}
|
|
169
|
+
if (meta.storyId) {
|
|
170
|
+
lines.push(` Story: ${meta.storyId}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
for (const rule of result.rules) {
|
|
174
|
+
lines.push(` ${rule}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return lines.join('\n');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Format the SQUAD section.
|
|
182
|
+
*
|
|
183
|
+
* @param {object} result - Layer result { rules, metadata }
|
|
184
|
+
* @returns {string}
|
|
185
|
+
*/
|
|
186
|
+
function formatSquad(result) {
|
|
187
|
+
const meta = result.metadata || {};
|
|
188
|
+
const squadName = meta.squadName || meta.source || 'unknown';
|
|
189
|
+
const lines = [`[SQUAD: ${squadName}]`];
|
|
190
|
+
|
|
191
|
+
for (const rule of result.rules) {
|
|
192
|
+
lines.push(` ${rule}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return lines.join('\n');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Format the KEYWORD section.
|
|
200
|
+
*
|
|
201
|
+
* @param {object} result - Layer result { rules, metadata }
|
|
202
|
+
* @returns {string}
|
|
203
|
+
*/
|
|
204
|
+
function formatKeyword(result) {
|
|
205
|
+
const meta = result.metadata || {};
|
|
206
|
+
const lines = ['[KEYWORD MATCHES]'];
|
|
207
|
+
|
|
208
|
+
if (meta.matches && Array.isArray(meta.matches)) {
|
|
209
|
+
for (const match of meta.matches) {
|
|
210
|
+
lines.push(` "${match.keyword}" matched ${match.domain} (${match.reason || 'keyword match'})`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
for (const rule of result.rules) {
|
|
215
|
+
lines.push(` ${rule}`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return lines.join('\n');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Format the STAR-COMMANDS section.
|
|
223
|
+
*
|
|
224
|
+
* @param {object} result - Layer result { rules, metadata }
|
|
225
|
+
* @returns {string}
|
|
226
|
+
*/
|
|
227
|
+
function formatStarCommands(result) {
|
|
228
|
+
const meta = result.metadata || {};
|
|
229
|
+
const command = meta.command || meta.source || 'unknown';
|
|
230
|
+
const lines = [
|
|
231
|
+
'[STAR-COMMANDS]',
|
|
232
|
+
'============================================================',
|
|
233
|
+
`[*${command}] COMMAND:`,
|
|
234
|
+
];
|
|
235
|
+
|
|
236
|
+
result.rules.forEach((rule, i) => {
|
|
237
|
+
lines.push(` ${i}. ${rule}`);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
lines.push('============================================================');
|
|
241
|
+
return lines.join('\n');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
// Memory Hints Section (SYN-10)
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Format the MEMORY HINTS section.
|
|
250
|
+
*
|
|
251
|
+
* Only included when hints array is non-empty.
|
|
252
|
+
* Each hint displays source, relevance, and content.
|
|
253
|
+
*
|
|
254
|
+
* @param {object} result - Layer result { rules (hint objects), metadata }
|
|
255
|
+
* @returns {string}
|
|
256
|
+
*/
|
|
257
|
+
function formatMemoryHints(result) {
|
|
258
|
+
const lines = ['[MEMORY HINTS]'];
|
|
259
|
+
|
|
260
|
+
for (const hint of result.rules) {
|
|
261
|
+
const source = hint.source || 'memory';
|
|
262
|
+
const relevance = typeof hint.relevance === 'number'
|
|
263
|
+
? `${(hint.relevance * 100).toFixed(0)}%`
|
|
264
|
+
: '?%';
|
|
265
|
+
const content = hint.content || '';
|
|
266
|
+
lines.push(` [${source}] (relevance: ${relevance}) ${content}`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return lines.join('\n');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
// DEVMODE Section (DESIGN doc section 13)
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Format the DEVMODE debug section.
|
|
278
|
+
*
|
|
279
|
+
* @param {string} bracket - Current bracket name
|
|
280
|
+
* @param {number} contextPercent - Remaining context %
|
|
281
|
+
* @param {object} session - Session state
|
|
282
|
+
* @param {object} metrics - Pipeline metrics summary
|
|
283
|
+
* @param {object[]} results - Layer results for domain analysis
|
|
284
|
+
* @returns {string}
|
|
285
|
+
*/
|
|
286
|
+
function formatDevmode(bracket, contextPercent, session, metrics, results) {
|
|
287
|
+
const lines = [
|
|
288
|
+
'[DEVMODE STATUS]',
|
|
289
|
+
'---',
|
|
290
|
+
'SYNAPSE DEVMODE',
|
|
291
|
+
'',
|
|
292
|
+
`Bracket: [${bracket}] (${contextPercent.toFixed(1)}% remaining)`,
|
|
293
|
+
'',
|
|
294
|
+
];
|
|
295
|
+
|
|
296
|
+
// Layers loaded
|
|
297
|
+
const loaded = Object.entries(metrics.per_layer || {})
|
|
298
|
+
.filter(([, v]) => v.status === 'ok');
|
|
299
|
+
if (loaded.length > 0) {
|
|
300
|
+
lines.push('Layers Loaded:');
|
|
301
|
+
for (const [name, info] of loaded) {
|
|
302
|
+
const duration = info.duration != null ? `${info.duration}ms` : '?ms';
|
|
303
|
+
lines.push(` [L${info.layer != null ? info.layer : '?'}] ${name.toUpperCase()}: ${info.rules || 0} rules (${duration})`);
|
|
304
|
+
}
|
|
305
|
+
lines.push('');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Layers skipped
|
|
309
|
+
const skipped = Object.entries(metrics.per_layer || {})
|
|
310
|
+
.filter(([, v]) => v.status === 'skipped');
|
|
311
|
+
if (skipped.length > 0) {
|
|
312
|
+
lines.push('Layers Skipped:');
|
|
313
|
+
for (const [name, info] of skipped) {
|
|
314
|
+
lines.push(` [${name.toUpperCase()}] ${info.reason || 'Unknown reason'}`);
|
|
315
|
+
}
|
|
316
|
+
lines.push('');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Session info
|
|
320
|
+
if (session) {
|
|
321
|
+
lines.push('Session:');
|
|
322
|
+
if (session.id || session.sessionId) {
|
|
323
|
+
lines.push(` UUID: ${session.id || session.sessionId || 'unknown'}`);
|
|
324
|
+
}
|
|
325
|
+
if (session.active_agent) {
|
|
326
|
+
lines.push(` Agent: @${session.active_agent}`);
|
|
327
|
+
}
|
|
328
|
+
lines.push(` Prompts: ${session.prompt_count || 0} | Last bracket: ${bracket}`);
|
|
329
|
+
lines.push('');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Pipeline metrics
|
|
333
|
+
lines.push('Pipeline Metrics:');
|
|
334
|
+
lines.push(` Total: ${metrics.total_ms}ms | Layers: ${metrics.layers_loaded}/${metrics.layers_loaded + metrics.layers_skipped + metrics.layers_errored} | Rules: ${metrics.total_rules}`);
|
|
335
|
+
|
|
336
|
+
// Available domains (not loaded) — based on results metadata
|
|
337
|
+
const loadedDomains = new Set(results.map(r => (r.metadata && r.metadata.source) || '').filter(Boolean));
|
|
338
|
+
if (loadedDomains.size > 0) {
|
|
339
|
+
lines.push('');
|
|
340
|
+
lines.push('Loaded Domains:');
|
|
341
|
+
for (const domain of loadedDomains) {
|
|
342
|
+
const result = results.find(r => r.metadata && r.metadata.source === domain);
|
|
343
|
+
const ruleCount = result ? result.rules.length : 0;
|
|
344
|
+
lines.push(` [${domain.toUpperCase()}] (${ruleCount} rules)`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
lines.push('---');
|
|
349
|
+
return lines.join('\n');
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// ---------------------------------------------------------------------------
|
|
353
|
+
// Summary Section
|
|
354
|
+
// ---------------------------------------------------------------------------
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Format the LOADED DOMAINS SUMMARY section.
|
|
358
|
+
*
|
|
359
|
+
* @param {object[]} results - All layer results
|
|
360
|
+
* @param {object} metrics - Pipeline metrics summary
|
|
361
|
+
* @returns {string}
|
|
362
|
+
*/
|
|
363
|
+
function formatSummary(results, _metrics) {
|
|
364
|
+
const lines = ['[LOADED DOMAINS SUMMARY]', ' LOADED DOMAINS:'];
|
|
365
|
+
|
|
366
|
+
for (const result of results) {
|
|
367
|
+
if (!result || !result.rules || result.rules.length === 0) continue;
|
|
368
|
+
const meta = result.metadata || {};
|
|
369
|
+
const source = (meta.source || meta.domain || 'unknown').toUpperCase();
|
|
370
|
+
const reason = meta.activationReason || meta.reason || 'active layer';
|
|
371
|
+
lines.push(` [${source}] ${reason} (${result.rules.length} rules)`);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return lines.join('\n');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ---------------------------------------------------------------------------
|
|
378
|
+
// Token Budget Enforcement
|
|
379
|
+
// ---------------------------------------------------------------------------
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Enforce a token budget by removing sections from the end.
|
|
383
|
+
*
|
|
384
|
+
* Truncation order (last removed first): SUMMARY, KEYWORD, SQUAD,
|
|
385
|
+
* STAR_COMMANDS, TASK, WORKFLOW. CONSTITUTION and AGENT are never removed.
|
|
386
|
+
*
|
|
387
|
+
* @param {string[]} sections - Ordered section strings
|
|
388
|
+
* @param {string[]} sectionIds - Corresponding section identifiers
|
|
389
|
+
* @param {number} tokenBudget - Max tokens allowed
|
|
390
|
+
* @returns {string[]} Filtered sections within budget
|
|
391
|
+
*/
|
|
392
|
+
function enforceTokenBudget(sections, sectionIds, tokenBudget) {
|
|
393
|
+
if (!tokenBudget || tokenBudget <= 0) {
|
|
394
|
+
return sections;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Sections that should never be removed
|
|
398
|
+
const PROTECTED = new Set(['CONTEXT_BRACKET', 'CONSTITUTION', 'AGENT']);
|
|
399
|
+
|
|
400
|
+
// Truncation priority: remove from end first
|
|
401
|
+
const TRUNCATION_ORDER = [
|
|
402
|
+
'SUMMARY',
|
|
403
|
+
'KEYWORD',
|
|
404
|
+
'MEMORY_HINTS',
|
|
405
|
+
'SQUAD',
|
|
406
|
+
'STAR_COMMANDS',
|
|
407
|
+
'DEVMODE',
|
|
408
|
+
'TASK',
|
|
409
|
+
'WORKFLOW',
|
|
410
|
+
];
|
|
411
|
+
|
|
412
|
+
const result = [...sections];
|
|
413
|
+
const ids = [...sectionIds];
|
|
414
|
+
|
|
415
|
+
let totalTokens = estimateTokens(result.join('\n\n'));
|
|
416
|
+
|
|
417
|
+
if (totalTokens <= tokenBudget) {
|
|
418
|
+
return result;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Remove sections in truncation order
|
|
422
|
+
for (const sectionToRemove of TRUNCATION_ORDER) {
|
|
423
|
+
if (totalTokens <= tokenBudget) {
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const idx = ids.indexOf(sectionToRemove);
|
|
428
|
+
if (idx !== -1 && !PROTECTED.has(sectionToRemove)) {
|
|
429
|
+
result.splice(idx, 1);
|
|
430
|
+
ids.splice(idx, 1);
|
|
431
|
+
totalTokens = estimateTokens(result.join('\n\n'));
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return result;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// ---------------------------------------------------------------------------
|
|
439
|
+
// Main Formatter
|
|
440
|
+
// ---------------------------------------------------------------------------
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Section formatter dispatch table.
|
|
444
|
+
*/
|
|
445
|
+
const SECTION_FORMATTERS = {
|
|
446
|
+
CONSTITUTION: formatConstitution,
|
|
447
|
+
AGENT: formatAgent,
|
|
448
|
+
WORKFLOW: formatWorkflow,
|
|
449
|
+
TASK: formatTask,
|
|
450
|
+
SQUAD: formatSquad,
|
|
451
|
+
KEYWORD: formatKeyword,
|
|
452
|
+
MEMORY_HINTS: formatMemoryHints,
|
|
453
|
+
STAR_COMMANDS: formatStarCommands,
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Format all layer results into the <synapse-rules> XML output.
|
|
458
|
+
*
|
|
459
|
+
* @param {object[]} results - Layer results array
|
|
460
|
+
* @param {string} bracket - Current bracket name
|
|
461
|
+
* @param {number} contextPercent - Remaining context percentage
|
|
462
|
+
* @param {object} session - Session state
|
|
463
|
+
* @param {boolean} devmode - Whether DEVMODE is enabled
|
|
464
|
+
* @param {object} metrics - Pipeline metrics summary from PipelineMetrics.getSummary()
|
|
465
|
+
* @param {number} tokenBudget - Max tokens for this bracket
|
|
466
|
+
* @param {boolean} showHandoffWarning - Whether to include handoff warning
|
|
467
|
+
* @returns {string} Formatted <synapse-rules> XML string
|
|
468
|
+
*/
|
|
469
|
+
function formatSynapseRules(results, bracket, contextPercent, session, devmode, metrics, tokenBudget, showHandoffWarning) {
|
|
470
|
+
if (!results || results.length === 0) {
|
|
471
|
+
return '';
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Categorize results by section
|
|
475
|
+
const sectionResults = {};
|
|
476
|
+
const globalResults = [];
|
|
477
|
+
|
|
478
|
+
for (const result of results) {
|
|
479
|
+
if (!result || !result.rules || result.rules.length === 0) {
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const meta = result.metadata || {};
|
|
484
|
+
const layerName = meta.source || '';
|
|
485
|
+
const section = LAYER_TO_SECTION[layerName];
|
|
486
|
+
|
|
487
|
+
if (section === 'CONTEXT_BRACKET') {
|
|
488
|
+
globalResults.push(result);
|
|
489
|
+
} else if (section) {
|
|
490
|
+
sectionResults[section] = result;
|
|
491
|
+
} else {
|
|
492
|
+
// Fallback: try to match by layer number
|
|
493
|
+
const layerNum = meta.layer;
|
|
494
|
+
if (layerNum === 0) sectionResults['CONSTITUTION'] = result;
|
|
495
|
+
else if (layerNum === 1) globalResults.push(result);
|
|
496
|
+
else if (layerNum === 2) sectionResults['AGENT'] = result;
|
|
497
|
+
else if (layerNum === 3) sectionResults['WORKFLOW'] = result;
|
|
498
|
+
else if (layerNum === 4) sectionResults['TASK'] = result;
|
|
499
|
+
else if (layerNum === 5) sectionResults['SQUAD'] = result;
|
|
500
|
+
else if (layerNum === 6) sectionResults['KEYWORD'] = result;
|
|
501
|
+
else if (layerNum === 7) sectionResults['STAR_COMMANDS'] = result;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Build sections in order
|
|
506
|
+
const sections = [];
|
|
507
|
+
const sectionIds = [];
|
|
508
|
+
|
|
509
|
+
// CONTEXT BRACKET — always first
|
|
510
|
+
sections.push(formatContextBracket(bracket, contextPercent, globalResults));
|
|
511
|
+
sectionIds.push('CONTEXT_BRACKET');
|
|
512
|
+
|
|
513
|
+
// Remaining sections in order (skip CONTEXT_BRACKET since already added)
|
|
514
|
+
for (const sectionId of SECTION_ORDER) {
|
|
515
|
+
if (sectionId === 'CONTEXT_BRACKET') continue;
|
|
516
|
+
if (sectionId === 'DEVMODE') continue;
|
|
517
|
+
if (sectionId === 'SUMMARY') continue;
|
|
518
|
+
|
|
519
|
+
const result = sectionResults[sectionId];
|
|
520
|
+
if (!result) continue;
|
|
521
|
+
|
|
522
|
+
const formatter = SECTION_FORMATTERS[sectionId];
|
|
523
|
+
if (formatter) {
|
|
524
|
+
sections.push(formatter(result));
|
|
525
|
+
sectionIds.push(sectionId);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Handoff warning (CRITICAL bracket)
|
|
530
|
+
if (showHandoffWarning) {
|
|
531
|
+
sections.push('[HANDOFF WARNING]\n Context is nearly exhausted. Consider starting a new session to preserve quality.');
|
|
532
|
+
sectionIds.push('HANDOFF_WARNING');
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// DEVMODE section (conditional)
|
|
536
|
+
if (devmode && metrics) {
|
|
537
|
+
sections.push(formatDevmode(bracket, contextPercent, session, metrics, results));
|
|
538
|
+
sectionIds.push('DEVMODE');
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// SUMMARY — always last
|
|
542
|
+
if (results.length > 0) {
|
|
543
|
+
sections.push(formatSummary(results, metrics || {}));
|
|
544
|
+
sectionIds.push('SUMMARY');
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Token budget enforcement
|
|
548
|
+
const finalSections = enforceTokenBudget(sections, sectionIds, tokenBudget);
|
|
549
|
+
|
|
550
|
+
// Wrap in <synapse-rules> tags
|
|
551
|
+
const body = finalSections.join('\n\n');
|
|
552
|
+
return `<synapse-rules>\n\n${body}\n\n</synapse-rules>`;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
module.exports = {
|
|
556
|
+
formatSynapseRules,
|
|
557
|
+
enforceTokenBudget,
|
|
558
|
+
estimateTokens,
|
|
559
|
+
SECTION_ORDER,
|
|
560
|
+
LAYER_TO_SECTION,
|
|
561
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
|
|
6
|
+
const DEFAULT_STALE_TTL_HOURS = 168; // 7 days
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Read stale session TTL from core-config.yaml.
|
|
10
|
+
* Falls back to DEFAULT_STALE_TTL_HOURS (168h = 7 days).
|
|
11
|
+
*
|
|
12
|
+
* @param {string} cwd - Working directory
|
|
13
|
+
* @returns {number} TTL in hours
|
|
14
|
+
*/
|
|
15
|
+
function getStaleSessionTTL(cwd) {
|
|
16
|
+
try {
|
|
17
|
+
const yaml = require('js-yaml');
|
|
18
|
+
const configPath = path.join(cwd, '.auroq-core', 'core-config.yaml');
|
|
19
|
+
if (!fs.existsSync(configPath)) return DEFAULT_STALE_TTL_HOURS;
|
|
20
|
+
const config = yaml.load(fs.readFileSync(configPath, 'utf8'));
|
|
21
|
+
const ttl = config && config.synapse && config.synapse.session && config.synapse.session.staleTTLHours;
|
|
22
|
+
return typeof ttl === 'number' && ttl > 0 ? ttl : DEFAULT_STALE_TTL_HOURS;
|
|
23
|
+
} catch (_err) {
|
|
24
|
+
return DEFAULT_STALE_TTL_HOURS;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Resolve runtime dependencies for Synapse hook execution.
|
|
30
|
+
*
|
|
31
|
+
* On the first prompt of a session (prompt_count === 0), runs
|
|
32
|
+
* cleanStaleSessions() fire-and-forget to remove expired sessions.
|
|
33
|
+
*
|
|
34
|
+
* @param {{cwd?: string, session_id?: string, sessionId?: string}} input
|
|
35
|
+
* @returns {{
|
|
36
|
+
* engine: import('../engine').SynapseEngine,
|
|
37
|
+
* session: Object
|
|
38
|
+
* } | null}
|
|
39
|
+
*/
|
|
40
|
+
function resolveHookRuntime(input) {
|
|
41
|
+
const cwd = input && input.cwd;
|
|
42
|
+
const sessionId = input && (input.session_id || input.sessionId);
|
|
43
|
+
if (!cwd || typeof cwd !== 'string') return null;
|
|
44
|
+
|
|
45
|
+
const synapsePath = path.join(cwd, '.synapse');
|
|
46
|
+
if (!fs.existsSync(synapsePath)) return null;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const { loadSession, cleanStaleSessions } = require(
|
|
50
|
+
path.join(cwd, '.auroq-core', 'core', 'synapse', 'session', 'session-manager.js'),
|
|
51
|
+
);
|
|
52
|
+
const { SynapseEngine } = require(
|
|
53
|
+
path.join(cwd, '.auroq-core', 'core', 'synapse', 'engine.js'),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const sessionsDir = path.join(synapsePath, 'sessions');
|
|
57
|
+
const session = loadSession(sessionId, sessionsDir) || { prompt_count: 0 };
|
|
58
|
+
const engine = new SynapseEngine(synapsePath);
|
|
59
|
+
|
|
60
|
+
// AC3: Run cleanup on first prompt only (fire-and-forget)
|
|
61
|
+
if (session.prompt_count === 0) {
|
|
62
|
+
try {
|
|
63
|
+
const ttlHours = getStaleSessionTTL(cwd);
|
|
64
|
+
const removed = cleanStaleSessions(sessionsDir, ttlHours);
|
|
65
|
+
if (removed > 0 && process.env.DEBUG === '1') {
|
|
66
|
+
console.error(`[hook-runtime] Cleaned ${removed} stale session(s) (TTL: ${ttlHours}h)`);
|
|
67
|
+
}
|
|
68
|
+
} catch (_cleanupErr) {
|
|
69
|
+
// Fire-and-forget: never block hook execution
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { engine, session, sessionId, sessionsDir, cwd };
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (process.env.DEBUG === '1') {
|
|
76
|
+
console.error(`[hook-runtime] Failed to resolve runtime: ${error.message}`);
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Normalize hook output payload shape.
|
|
84
|
+
* @param {string} xml
|
|
85
|
+
* @returns {{hookSpecificOutput: {additionalContext: string}}}
|
|
86
|
+
*/
|
|
87
|
+
function buildHookOutput(xml) {
|
|
88
|
+
return {
|
|
89
|
+
hookSpecificOutput: {
|
|
90
|
+
additionalContext: xml || '',
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = {
|
|
96
|
+
resolveHookRuntime,
|
|
97
|
+
buildHookOutput,
|
|
98
|
+
};
|