@cgh567/agent 2.4.1 → 2.4.3

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 (169) hide show
  1. package/bin/helios +0 -0
  2. package/bin/helios-rpc-node-wrapper.cjs +0 -0
  3. package/bin/helios-rpc-wrapper.sh +0 -0
  4. package/daemon/adapters/helios-rpc-adapter.js +47 -25
  5. package/daemon/adapters/tui_wakeup.js +8 -0
  6. package/daemon/config/com.familiar.helios-daemon.plist +5 -0
  7. package/daemon/config/helios-daemon.service +4 -0
  8. package/daemon/context-enrichment.js +59 -21
  9. package/daemon/daemon-manager.js +1 -1
  10. package/daemon/db/email-infrastructure-migrate.js +192 -0
  11. package/daemon/db/hbo-core-migrate.js +189 -0
  12. package/daemon/helios-api.js +723 -57
  13. package/daemon/helios-company-daemon.js +616 -134
  14. package/daemon/lib/harada/cascade-judge.js +12 -50
  15. package/daemon/lib/harada/mandala.js +20 -0
  16. package/daemon/lib/harada/pillar-dispatcher.js +1 -1
  17. package/daemon/lib/harada/project-factory.js +7 -2
  18. package/daemon/lib/hbo-bridge.js +32 -13
  19. package/daemon/lib/hed-engine.js +10 -292
  20. package/daemon/lib/helios-hitl-host.js +15 -2
  21. package/daemon/lib/hitl-interaction-service.js +0 -0
  22. package/daemon/lib/memgraph-verify.js +38 -33
  23. package/daemon/lib/project-drift-detector.js +7 -17
  24. package/daemon/lib/project-semantic-updater.js +1 -14
  25. package/daemon/lib/task-completion-processor.js +11 -0
  26. package/daemon/lib/wizard-engine.js +57 -6
  27. package/daemon/routes/channels.js +10 -5
  28. package/daemon/routes/harada-map.js +11 -48
  29. package/daemon/routes/hbo.js +342 -75
  30. package/daemon/routes/hitl.js +0 -0
  31. package/daemon/routes/project.js +194 -62
  32. package/daemon/routes/routines.js +14 -0
  33. package/daemon/routes/tasks.js +15 -1
  34. package/daemon/routes/wizard.js +11 -4
  35. package/daemon/schema-apply.js +174 -0
  36. package/daemon/schema-definitions.js +423 -0
  37. package/daemon/schema-migrations-hbo.js +10 -0
  38. package/daemon/schema-migrations-hed.js +18 -0
  39. package/daemon/schema-migrations-hitl.js +0 -0
  40. package/daemon/schema-migrations-proj.js +131 -0
  41. package/extensions/001-tool-output-cap.ts +0 -0
  42. package/extensions/context-compaction.ts +45 -26
  43. package/extensions/cortex/activation-bridge.ts +5 -0
  44. package/extensions/cortex/learn.ts +26 -0
  45. package/extensions/cortex/wal-replay.ts +91 -0
  46. package/extensions/email/backfill.ts +0 -0
  47. package/extensions/helios-governance/analysis/ambiguity.ts +0 -0
  48. package/extensions/helios-governance/analysis/compliance.ts +0 -0
  49. package/extensions/helios-governance/analysis/long-task-detector.ts +0 -0
  50. package/extensions/helios-governance/analysis/output-contract.ts +0 -0
  51. package/extensions/helios-governance/analysis/patterns.ts +0 -0
  52. package/extensions/helios-governance/analysis/preflight.ts +0 -0
  53. package/extensions/helios-governance/analysis/recurring-violations.ts +0 -0
  54. package/extensions/helios-governance/analysis/task-classification.ts +0 -0
  55. package/extensions/helios-governance/analysis/task-intent.ts +0 -0
  56. package/extensions/helios-governance/gates/high-impact.ts +1 -1
  57. package/extensions/helios-governance/handlers/_jiti-require.ts +15 -8
  58. package/extensions/helios-governance/handlers/proxy-test-detector.ts +0 -0
  59. package/extensions/hema-dispatch-v3/graph-memory.ts +10 -0
  60. package/extensions/hema-dispatch-v3/index.ts +72 -47
  61. package/extensions/lib/elo-engine.js +0 -0
  62. package/extensions/lib/elo-engine.test.js +0 -0
  63. package/extensions/memgraph-autostart.ts +13 -0
  64. package/extensions/neuroplastic-eval.ts +0 -0
  65. package/extensions/shadow-loop/index.ts +0 -0
  66. package/extensions/warm-tick/warm-tick-maintenance.ts +8 -0
  67. package/lib/__tests__/hbo-core-store.test.js +238 -0
  68. package/lib/brain-v2-budget.js +0 -0
  69. package/lib/brain-v2-circuit-breaker.js +0 -0
  70. package/lib/brain-v2.js +0 -0
  71. package/lib/broker/adaptive-throttle.js +0 -0
  72. package/lib/broker/batch-coalescer.js +0 -0
  73. package/lib/broker/bulkhead.js +0 -0
  74. package/lib/broker/channel-registry.js +0 -0
  75. package/lib/broker/circuit-breaker.js +0 -0
  76. package/lib/broker/evidence-cache.js +0 -0
  77. package/lib/broker/health-monitor.js +0 -0
  78. package/lib/broker/mage-queue.js +0 -0
  79. package/lib/broker/priority-queue.js +0 -0
  80. package/lib/broker/server.js.bak-error2-fix +0 -0
  81. package/lib/broker/session-registry.js +0 -0
  82. package/lib/broker/singleton-timers.js +0 -0
  83. package/lib/broker/types.d.ts +0 -0
  84. package/lib/broker/vegas-limit.js +0 -0
  85. package/lib/compression/dist/ccr-store.js +74 -0
  86. package/lib/compression/dist/content-router.js +115 -0
  87. package/lib/compression/dist/pipeline.js +113 -0
  88. package/lib/compression/dist/server.js +265 -0
  89. package/lib/compression/dist/smart-crusher.js +251 -0
  90. package/lib/context-budget.ts +0 -0
  91. package/lib/context-firewall.js +0 -0
  92. package/lib/crm/integration/triage-bridge.js +0 -0
  93. package/lib/email-utils.ts +0 -0
  94. package/lib/eval/__tests__/preflight-checker.test.ts +0 -0
  95. package/lib/eval/__tests__/task-instruction-parser.test.ts +0 -0
  96. package/lib/eval/__tests__/verifier-runner.test.ts +0 -0
  97. package/lib/eval/index.ts +0 -0
  98. package/lib/eval/preflight-checker.ts +0 -0
  99. package/lib/eval/task-domain-classifier.ts +0 -0
  100. package/lib/eval/task-instruction-parser.ts +0 -0
  101. package/lib/eval/verifier-runner.ts +0 -0
  102. package/lib/event-bus.d.ts +0 -0
  103. package/lib/event-bus.mts +1 -1
  104. package/lib/governance-context-selector.ts +0 -0
  105. package/lib/graph/generate-extension-embeddings.js +0 -0
  106. package/lib/graph/generate-static-embeddings.js +0 -0
  107. package/lib/graph/lib/utils.js +1 -1
  108. package/lib/graph-audit.d.ts +0 -0
  109. package/lib/graph-availability.js +62 -0
  110. package/lib/hbo-core-store.compiled.js +834 -0
  111. package/lib/hbo-core-store.js +124 -0
  112. package/lib/hbo-core-store.ts +908 -0
  113. package/lib/mesh-circuit-breaker.js +0 -0
  114. package/lib/mission-loop/lesson-extractor.ts +0 -0
  115. package/lib/mission-loop/mental-model-scorer.ts +0 -0
  116. package/lib/mission-loop/occ-detector.ts +0 -0
  117. package/lib/mission-loop/query-variants.ts +0 -0
  118. package/lib/mission-loop/verifier-check.ts +0 -0
  119. package/lib/skill-reference-builder.ts +0 -0
  120. package/lib/telemetry/token-breakdown.ts +0 -0
  121. package/lib/tool-compressor.ts +0 -0
  122. package/lib/triage-core/classifier.ts +3 -2
  123. package/lib/triage-core/graph/schema.cypher +10 -0
  124. package/lib/triage-core/legal-routing.ts +0 -0
  125. package/lib/triage-core/mental-model/dunbar-classifier.ts +0 -0
  126. package/lib/triage-core/mental-model/enrich-all.ts +0 -0
  127. package/lib/triage-core/mental-model/identity-resolver.ts +0 -0
  128. package/lib/triage-core/mental-model/key-facts.ts +1 -2
  129. package/lib/triage-core/mental-model/model-assembler.ts +0 -0
  130. package/lib/triage-core/orchestrator.ts +4 -11
  131. package/lib/triage-core/orchestrator.ts.bak-r005-r006-r008 +0 -0
  132. package/package.json +18 -8
  133. package/skills/helios-business-operator/services/signals/upwork-signals.js +0 -0
  134. package/skills/talisman-ceo/SKILL.md +23 -25
  135. package/skills/talisman-comms/SKILL.md +5 -5
  136. package/skills/talisman-engineering/SKILL.md +5 -5
  137. package/skills/talisman-finance/SKILL.md +10 -8
  138. package/skills/talisman-marketing/SKILL.md +10 -10
  139. package/skills/talisman-sales/SKILL.md +12 -15
  140. package/skills/talisman-support/SKILL.md +5 -5
  141. package/agents/business/talisman-ceo.md +0 -183
  142. package/agents/business/talisman-comms.md +0 -257
  143. package/agents/business/talisman-cto.md +0 -153
  144. package/agents/business/talisman-finance.md +0 -246
  145. package/agents/business/talisman-marketing.md +0 -240
  146. package/agents/business/talisman-sales.md +0 -242
  147. package/agents/business/talisman-support.md +0 -236
  148. package/daemon/lib/approval-expiry.js +0 -162
  149. package/daemon/lib/blast-radius-analyzer.js +0 -75
  150. package/daemon/lib/domain-bootstrap-orchestrator.js +0 -267
  151. package/daemon/lib/forensic-log.js +0 -113
  152. package/daemon/lib/goal-research-pipeline.js +0 -644
  153. package/daemon/lib/harada/cascade-research-dispatcher.js +0 -261
  154. package/daemon/lib/headroom-middleware.js +0 -167
  155. package/daemon/lib/headroom-proxy-manager.js +0 -623
  156. package/daemon/lib/mental-model-cache.js +0 -96
  157. package/daemon/lib/project-factory.js +0 -47
  158. package/daemon/lib/session-log-reader.js +0 -93
  159. package/daemon/routes/hed.js +0 -133
  160. package/lib/graph/learning/headroom-learn-bridge.js +0 -215
  161. package/skills/helios-bookkeeping/SKILL.md +0 -321
  162. package/skills/helios-briefer/SKILL.md +0 -44
  163. package/skills/helios-client-relations/SKILL.md +0 -322
  164. package/skills/helios-personal-triager/SKILL.md +0 -45
  165. package/skills/helios-recruitment/SKILL.md +0 -317
  166. package/skills/helios-relationship-nudger/SKILL.md +0 -77
  167. package/skills/helios-researcher/SKILL.md +0 -44
  168. package/skills/helios-scheduler/SKILL.md +0 -58
  169. package/skills/helios-tax-analyst/SKILL.md +0 -280
@@ -1,96 +0,0 @@
1
- 'use strict';
2
- /**
3
- * mental-model-cache.js — CachedPersonModel node CRUD
4
- * Pre-assembled mental models for fast agent brief injection.
5
- * Cache is invalidated by enrichmentNeeded flag on Person node.
6
- */
7
-
8
- const SCHEMA_VERSION = 1;
9
-
10
- /**
11
- * writeCachedModel — upsert a CachedPersonModel node for a person in a company.
12
- *
13
- * @param {Function} mgQuery — Memgraph query function (async)
14
- * @param {string} personId — Person node id
15
- * @param {string} companyId — Company node id
16
- * @param {object} assembledModel — Output from assembleMentalModel()
17
- */
18
- async function writeCachedModel(mgQuery, personId, companyId, assembledModel) {
19
- const modelId = `model:${companyId}:${personId}`;
20
- const contextBrief = assembledModel.contextBrief || '';
21
- const faveeJson = JSON.stringify(assembledModel.favee || {});
22
- const topicsJson = JSON.stringify((assembledModel.activeTopics || []).slice(0, 10));
23
- const questionsJson = JSON.stringify((assembledModel.openQuestions || []).slice(0, 5));
24
- const completeness = contextBrief ? 'full' : (faveeJson !== '{}' ? 'partial' : 'none');
25
-
26
- await mgQuery(
27
- `MERGE (m:CachedPersonModel {id: $id})
28
- ON CREATE SET
29
- m.personId = $personId, m.companyId = $companyId,
30
- m.contextBrief = $brief, m.faveeJson = $favee,
31
- m.topicsJson = $topics, m.questionsJson = $questions,
32
- m.assembledAt = localdatetime(), m.completeness = $completeness,
33
- m.schemaVersion = $sv
34
- ON MATCH SET
35
- m.contextBrief = $brief, m.faveeJson = $favee,
36
- m.topicsJson = $topics, m.questionsJson = $questions,
37
- m.assembledAt = localdatetime(), m.completeness = $completeness,
38
- m.dirtyAt = null
39
- WITH m
40
- MATCH (p:Person {id: $personId})
41
- MERGE (p)-[:HAS_CACHED_MODEL]->(m)`,
42
- { id: modelId, personId, companyId, brief: contextBrief,
43
- favee: faveeJson, topics: topicsJson, questions: questionsJson,
44
- completeness, sv: SCHEMA_VERSION }
45
- );
46
- }
47
-
48
- /**
49
- * invalidateCache — mark a CachedPersonModel dirty so next read triggers a refresh.
50
- * Non-fatal: silently ignores errors if the model node doesn't exist yet.
51
- *
52
- * @param {Function} mgQuery
53
- * @param {string} personId
54
- * @param {string} companyId
55
- */
56
- async function invalidateCache(mgQuery, personId, companyId) {
57
- const modelId = `model:${companyId}:${personId}`;
58
- await mgQuery(
59
- `MATCH (m:CachedPersonModel {id: $id}) SET m.dirtyAt = localdatetime()`,
60
- { id: modelId }
61
- ).catch(() => {});
62
- }
63
-
64
- /**
65
- * getCachedModel — return a cached model if it exists, is not dirty, and is within maxAge.
66
- * Returns null if the cache is cold, dirty, or expired.
67
- *
68
- * @param {Function} mgQuery
69
- * @param {string} personId
70
- * @param {string} companyId
71
- * @param {number} maxAgeMinutes — default 60 minutes
72
- * @returns {object|null}
73
- */
74
- async function getCachedModel(mgQuery, personId, companyId, maxAgeMinutes = 60) {
75
- const modelId = `model:${companyId}:${personId}`;
76
- const rows = await mgQuery(
77
- `MATCH (m:CachedPersonModel {id: $id})
78
- WHERE m.dirtyAt IS NULL
79
- AND m.assembledAt > localdatetime() - duration({minutes: $maxAge})
80
- RETURN m.contextBrief, m.faveeJson, m.topicsJson, m.questionsJson,
81
- m.assembledAt, m.completeness`,
82
- { id: modelId, maxAge: maxAgeMinutes }
83
- );
84
- if (!rows?.rows?.length) return null;
85
- const r = rows.rows[0];
86
- return {
87
- contextBrief: r[0],
88
- faveeJson: r[1],
89
- topicsJson: r[2],
90
- questionsJson: r[3],
91
- assembledAt: r[4],
92
- completeness: r[5],
93
- };
94
- }
95
-
96
- module.exports = { writeCachedModel, invalidateCache, getCachedModel, SCHEMA_VERSION };
@@ -1,47 +0,0 @@
1
- 'use strict';
2
- /**
3
- * project-factory.js — Creates HeliosProject + ProjectDocument nodes when
4
- * a GoalPillar is initialized via tickGoalDecompose().
5
- *
6
- * Called from daemon/lib/hbo-bridge.js tickGoalDecompose() after
7
- * mandala.initializeMandala() succeeds for each GoalPillar.
8
- *
9
- * ID conventions:
10
- * HeliosProject: proj:<companyId>:<pillarSlug>
11
- * ProjectDocument: pdoc:<projectId>:main
12
- */
13
-
14
- async function ensureProject(mgQuery, companyId, goalId, pillarId, pillarTitle) {
15
- const slug = pillarId.replace(/[^a-z0-9]/gi, '-').toLowerCase().slice(0, 40);
16
- const projId = `proj:${companyId}:${slug}`;
17
- const docId = `pdoc:${projId}:main`;
18
-
19
- await mgQuery(
20
- `MERGE (p:HeliosProject {id: $projId})
21
- ON CREATE SET
22
- p.companyId = $cid,
23
- p.goalId = $goalId,
24
- p.pillarId = $pillarId,
25
- p.name = $name,
26
- p.status = 'planning',
27
- p.phase = 'planning',
28
- p.createdAt = datetime()`,
29
- { projId, cid: companyId, goalId, pillarId, name: pillarTitle }
30
- ).catch(() => {});
31
-
32
- await mgQuery(
33
- `MERGE (d:ProjectDocument {id: $docId})
34
- ON CREATE SET
35
- d.projectId = $projId,
36
- d.purpose = '',
37
- d.approach = '',
38
- d.successCriteria = '[]',
39
- d.intentAnchor = '',
40
- d.exclusions = '[]',
41
- d.version = toInteger(1),
42
- d.updatedAt = datetime()`,
43
- { docId, projId }
44
- ).catch(() => {});
45
- }
46
-
47
- module.exports = { ensureProject };
@@ -1,93 +0,0 @@
1
- 'use strict';
2
-
3
- const path = require('path');
4
- const os = require('os');
5
-
6
- /**
7
- * SessionLogReader — reads OpenCode session logs from opencode.db
8
- * to reconstruct agent decision traces for HED audit.
9
- * Uses better-sqlite3 (already a dependency via daemon) for read-only access.
10
- */
11
-
12
- // OpenCode stores sessions at platform-specific paths:
13
- // Linux/WSL: ~/.local/share/opencode/opencode.db
14
- // macOS: ~/Library/Application Support/opencode/opencode.db
15
- // Windows: %APPDATA%\opencode\opencode.db
16
- function getOpenCodeDbPath() {
17
- if (process.platform === 'linux') {
18
- return path.join(os.homedir(), '.local', 'share', 'opencode', 'opencode.db');
19
- }
20
- if (process.platform === 'darwin') {
21
- return path.join(os.homedir(), 'Library', 'Application Support', 'opencode', 'opencode.db');
22
- }
23
- return path.join(os.homedir(), 'AppData', 'Local', 'opencode', 'opencode.db');
24
- }
25
-
26
- async function readSessionLog(sessionKey) {
27
- if (!sessionKey) return [];
28
- let Database;
29
- try {
30
- Database = require('better-sqlite3');
31
- } catch {
32
- console.warn('[session-log-reader] better-sqlite3 not available — cannot read session log');
33
- return [];
34
- }
35
- const dbPath = getOpenCodeDbPath();
36
- let db;
37
- try {
38
- db = new Database(dbPath, { readonly: true, fileMustExist: true });
39
- } catch (err) {
40
- console.warn('[session-log-reader] cannot open opencode.db:', err.message);
41
- return [];
42
- }
43
- try {
44
- const messages = db.prepare(
45
- `SELECT role, content, created_at FROM messages WHERE session_id = ? ORDER BY created_at ASC`
46
- ).all(sessionKey);
47
- return messages.map((m, i) => ({
48
- index: i,
49
- role: m.role,
50
- content: typeof m.content === 'string' ? m.content : JSON.stringify(m.content),
51
- createdAt: m.created_at
52
- }));
53
- } catch (err) {
54
- console.warn('[session-log-reader] query failed:', err.message);
55
- return [];
56
- } finally {
57
- db.close();
58
- }
59
- }
60
-
61
- // Classify messages into XEPV sequence: eXplore, Execute, Plan, Verify
62
- function classifyXEPV(messages) {
63
- return messages.map(m => {
64
- const content = m.content || '';
65
- // Tool calls in content indicate action type
66
- if (content.includes('"edit"') || content.includes('"write"')) return { ...m, xepv: 'X_Execute' };
67
- if (content.includes('"read"') || content.includes('"grep"') || content.includes('"search_codebase"') || content.includes('"glob"')) return { ...m, xepv: 'X_Explore' };
68
- if (content.includes('"bash"') && (content.includes('test') || content.includes('vitest') || content.includes('grep -n'))) return { ...m, xepv: 'X_Verify' };
69
- if (m.role === 'assistant' && (content.includes('plan') || content.includes('approach') || content.includes('strategy') || content.includes('first'))) return { ...m, xepv: 'X_Plan' };
70
- return { ...m, xepv: 'X_Unknown' };
71
- });
72
- }
73
-
74
- // Find the branch point where the agent deviated from the declared target
75
- function findBranchPoint(xepvMessages, opTarget) {
76
- const exploreBeforeExecute = xepvMessages.filter(m => m.xepv === 'X_Explore');
77
- for (const msg of exploreBeforeExecute) {
78
- // If the agent explored files NOT in the declared target, that's the branch point
79
- if (opTarget && !msg.content.includes(opTarget)) {
80
- return { message: msg, reason: `Agent explored ${msg.content.slice(0, 200)} instead of declared target ${opTarget}` };
81
- }
82
- }
83
- return null;
84
- }
85
-
86
- // Compute P-ratio: plan steps vs execution steps
87
- function computePRatio(xepvMessages) {
88
- const plan = xepvMessages.filter(m => m.xepv === 'X_Plan').length;
89
- const execute = xepvMessages.filter(m => m.xepv === 'X_Execute').length;
90
- return { planSteps: plan, executeSteps: execute, ratio: execute > 0 ? plan / execute : plan };
91
- }
92
-
93
- module.exports = { readSessionLog, classifyXEPV, findBranchPoint, computePRatio };
@@ -1,133 +0,0 @@
1
- 'use strict';
2
-
3
- const { HEDEngine } = require('../lib/hed-engine');
4
-
5
- /**
6
- * HED API routes — /api/hed/*
7
- * Follows the standard Helios router pattern: returns true if handled, false otherwise.
8
- * Initialized lazily inside startApi() via createHedRoutes(mgQuery).
9
- */
10
-
11
- function jsonOk(res, data, status = 200) {
12
- res.writeHead(status, {
13
- 'Content-Type': 'application/json',
14
- 'Access-Control-Allow-Origin': '*',
15
- 'Access-Control-Allow-Methods': 'GET, POST, PATCH, OPTIONS',
16
- 'Access-Control-Allow-Headers': 'Content-Type, Accept',
17
- });
18
- res.end(JSON.stringify(data));
19
- }
20
-
21
- function jsonErr(res, status, message) {
22
- res.writeHead(status, {
23
- 'Content-Type': 'application/json',
24
- 'Access-Control-Allow-Origin': '*',
25
- 'Access-Control-Allow-Methods': 'GET, POST, PATCH, OPTIONS',
26
- 'Access-Control-Allow-Headers': 'Content-Type, Accept',
27
- });
28
- res.end(JSON.stringify({ error: message }));
29
- }
30
-
31
- function readBody(req) {
32
- return new Promise((resolve, reject) => {
33
- let body = '';
34
- req.on('data', chunk => { body += chunk; });
35
- req.on('end', () => {
36
- try { resolve(body ? JSON.parse(body) : {}); }
37
- catch (e) { reject(new Error('Invalid JSON body')); }
38
- });
39
- req.on('error', reject);
40
- });
41
- }
42
-
43
- function createHedRoutes(mgQuery) {
44
- const engine = new HEDEngine(mgQuery);
45
-
46
- return async function hedRoute(req, res, ctx, pathname, method) {
47
- // Only handle /api/hed paths
48
- if (!pathname.startsWith('/api/hed')) return false;
49
-
50
- const mg = ctx?.mgQuery ?? mgQuery;
51
- const _engine = ctx?.mgQuery ? new HEDEngine(ctx.mgQuery) : engine;
52
-
53
- try {
54
- // POST /api/hed — create a new HED
55
- if (method === 'POST' && pathname === '/api/hed') {
56
- const body = await readBody(req);
57
- const { companyId, title, intent, worldStateSnapshot, operations, goalId } = body;
58
- if (!companyId || !title || !operations?.length) {
59
- jsonErr(res, 400, 'companyId, title, and operations are required');
60
- return true;
61
- }
62
- const result = await _engine.createHED({ companyId, title, intent, worldStateSnapshot, operations, goalId });
63
- jsonOk(res, result, 201);
64
- return true;
65
- }
66
-
67
- // POST /api/hed/:id/approve — HITL approval
68
- const approveMatch = method === 'POST' && pathname.match(/^\/api\/hed\/([^/]+)\/approve$/);
69
- if (approveMatch) {
70
- const hedId = approveMatch[1];
71
- const body = await readBody(req);
72
- const { approvedBy } = body;
73
- await _engine.approveHED(hedId, approvedBy || 'human');
74
- jsonOk(res, { hedId, approved: true });
75
- return true;
76
- }
77
-
78
- // GET /api/hed/:id/review — aggregated review report
79
- const reviewMatch = method === 'GET' && pathname.match(/^\/api\/hed\/([^/]+)\/review$/);
80
- if (reviewMatch) {
81
- const hedId = reviewMatch[1];
82
- const operations = await _engine.getOperations(hedId);
83
- const findings = operations.map(op => ({
84
- opId: op.id,
85
- status: op.status,
86
- reviewVerdict: op.reviewVerdict || 'pending'
87
- }));
88
- const aligned = findings.filter(f => f.reviewVerdict === 'ALIGNED').length;
89
- const deviated = findings.filter(f => f.reviewVerdict === 'DEVIATED').length;
90
- const failed = findings.filter(f => f.reviewVerdict === 'FAILED').length;
91
- jsonOk(res, {
92
- hedId,
93
- findings,
94
- summary: { aligned, deviated, failed, pending: findings.length - aligned - deviated - failed }
95
- });
96
- return true;
97
- }
98
-
99
- // PATCH /api/hed/:hedId/operations/:opId — update operation status
100
- const opPatchMatch = method === 'PATCH' && pathname.match(/^\/api\/hed\/([^/]+)\/operations\/([^/]+)$/);
101
- if (opPatchMatch) {
102
- const opId = opPatchMatch[2];
103
- const body = await readBody(req);
104
- const { status, summary, sessionKey, verdict } = body;
105
- if (status === 'done' || status === 'completed') {
106
- await _engine.completeOperation(opId, { summary, sessionKey, verdict });
107
- }
108
- jsonOk(res, { opId, updated: true });
109
- return true;
110
- }
111
-
112
- // GET /api/hed/:id — get HED + all operations
113
- const hedMatch = method === 'GET' && pathname.match(/^\/api\/hed\/([^/]+)$/);
114
- if (hedMatch) {
115
- const hedId = hedMatch[1];
116
- const hed = await _engine.getHED(hedId);
117
- if (!hed) { jsonErr(res, 404, 'HED not found'); return true; }
118
- const operations = await _engine.getOperations(hedId);
119
- jsonOk(res, { ...hed, operations });
120
- return true;
121
- }
122
-
123
- } catch (err) {
124
- console.error('[hed-routes] error:', err.message);
125
- jsonErr(res, 500, err.message);
126
- return true;
127
- }
128
-
129
- return false;
130
- };
131
- }
132
-
133
- module.exports = { createHedRoutes };
@@ -1,215 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * lib/graph/learning/headroom-learn-bridge.js
5
- *
6
- * Bridges headroom's session failure-learning system with Helios's Memgraph
7
- * learning graph. Called asynchronously from the RETROSPECTIVE phase after
8
- * each CausalLesson extraction.
9
- *
10
- * Pipeline:
11
- * 1. Run `headroom learn --apply --project {helios-agent-root} --target AGENTS.md`
12
- * using the vendored headroom package at vendor/headroom-proxy/
13
- * 2. headroom learn scans Pi session logs (SQLite DB + tool-output blobs)
14
- * and writes corrections to AGENTS.md (marker-delimited, git-tracked)
15
- * 3. Parse corrections from AGENTS.md headroom:learn markers
16
- * 4. Write corrections as KnowledgeAsset nodes to Memgraph
17
- * 5. KnowledgeAsset nodes surface via biz-lesson-recall + lesson-recall channels
18
- * in future sessions
19
- *
20
- * Why this improves the whole system (not just one company):
21
- * - Structural patterns (path corrections, command patterns, search scope fixes)
22
- * are cross-company: they describe helios-agent behavior, not company data
23
- * - KnowledgeAsset confidence increases each time a pattern is reinforced
24
- * - Corrections in AGENTS.md are git-tracked and manually reviewable
25
- * - The global AGENTS.md (~/.config/opencode/AGENTS.md) is NEVER touched
26
- *
27
- * Debounce: runs at most once per 5 minutes across all sessions to avoid
28
- * hammering the Pi session log scanner.
29
- */
30
-
31
- const path = require('path');
32
- const fs = require('fs');
33
- const crypto = require('crypto');
34
- const { execFile } = require('child_process');
35
-
36
- const HELIOS_ROOT = process.env.HELIOS_ROOT || path.resolve(__dirname, '..', '..', '..');
37
- const AGENTS_MD = path.join(HELIOS_ROOT, 'AGENTS.md');
38
- const VENDOR_PATH = path.join(HELIOS_ROOT, 'vendor', 'headroom-proxy');
39
- const MARKER_START = '<!-- headroom:learn:start -->';
40
- const MARKER_END = '<!-- headroom:learn:end -->';
41
- const DEBOUNCE_MS = 5 * 60 * 1000; // 5 minutes between runs
42
-
43
- let _lastRunAt = 0;
44
-
45
- /**
46
- * Main entry point. Called from retrospective.ts after CausalLesson extraction.
47
- * @param {Function} mgQueryAsync - Memgraph query function
48
- * @param {string} sessionId - Current session ID
49
- * @param {string} missionRunId - Current mission run ID
50
- */
51
- async function runBridge(mgQueryAsync, sessionId, missionRunId) {
52
- const now = Date.now();
53
- if (now - _lastRunAt < DEBOUNCE_MS) return;
54
- _lastRunAt = now;
55
-
56
- // Verify vendor path exists before attempting anything
57
- if (!fs.existsSync(VENDOR_PATH)) {
58
- process.stderr.write('[headroom-learn-bridge] vendor/headroom-proxy not found — skipping learn run\n');
59
- return;
60
- }
61
-
62
- try {
63
- // Step 1: Run headroom learn
64
- await _runHeadroomLearn();
65
-
66
- // Step 2: Parse corrections from AGENTS.md
67
- const corrections = _parseCorrections();
68
- if (!corrections.length) {
69
- process.stderr.write('[headroom-learn-bridge] No corrections found in AGENTS.md markers\n');
70
- return;
71
- }
72
-
73
- // Step 3: Write to Memgraph
74
- await _writeToMemgraph(mgQueryAsync, corrections, sessionId, missionRunId);
75
-
76
- process.stderr.write(
77
- `[headroom-learn-bridge] Wrote ${corrections.length} corrections to Memgraph KnowledgeAsset nodes\n`
78
- );
79
- } catch (err) {
80
- // Completely non-fatal — never block RETROSPECTIVE phase
81
- process.stderr.write(`[headroom-learn-bridge] Failed (non-fatal): ${err.message}\n`);
82
- }
83
- }
84
-
85
- /**
86
- * Run `headroom learn --apply` using the vendored package.
87
- * Writes to AGENTS.md with <!-- headroom:learn:start/end --> markers.
88
- * Only the marker block is replaced — existing AGENTS.md content is preserved.
89
- */
90
- function _runHeadroomLearn() {
91
- return new Promise((resolve, reject) => {
92
- const pythonBin = process.env.HEADROOM_PYTHON || 'python3';
93
-
94
- // Use CLAUDE.local.md as target to avoid polluting shared AGENTS.md
95
- // The corrections are written to a separate local file, then we parse
96
- // them and write to Memgraph. Per-repo AGENTS.md gets the marker block.
97
- const args = [
98
- '-m', 'headroom.learn',
99
- '--apply',
100
- '--project', HELIOS_ROOT,
101
- '--target', 'AGENTS.md', // per-repo AGENTS.md (NOT global ~/.config/opencode/AGENTS.md)
102
- ];
103
-
104
- execFile(pythonBin, args, {
105
- cwd: VENDOR_PATH,
106
- env: {
107
- ...process.env,
108
- PYTHONPATH: VENDOR_PATH,
109
- HEADROOM_TELEMETRY: 'off',
110
- },
111
- timeout: 120_000, // 2-minute timeout (session log scanning can be slow on large projects)
112
- }, (err, stdout, stderr) => {
113
- if (err) {
114
- // Non-zero exit is common when no sessions have been scanned yet
115
- // Only reject on hard failures (ENOENT, etc.)
116
- if (err.code === 'ENOENT') {
117
- return reject(new Error(`Python not found: ${pythonBin}`));
118
- }
119
- // For other errors, log but resolve — partial output may still be useful
120
- if (stderr) process.stderr.write(`[headroom-learn] ${stderr.slice(0, 500)}\n`);
121
- return resolve(null); // resolve, not reject
122
- }
123
- if (stdout) process.stderr.write(`[headroom-learn] ${stdout.slice(0, 200)}\n`);
124
- resolve(null);
125
- });
126
- });
127
- }
128
-
129
- /**
130
- * Parse the headroom:learn marker block from AGENTS.md.
131
- * Returns an array of { category, content } corrections.
132
- */
133
- function _parseCorrections() {
134
- try {
135
- if (!fs.existsSync(AGENTS_MD)) return [];
136
- const content = fs.readFileSync(AGENTS_MD, 'utf-8');
137
- const start = content.indexOf(MARKER_START);
138
- const end = content.indexOf(MARKER_END);
139
- if (start === -1 || end === -1 || end <= start) return [];
140
-
141
- const block = content.slice(start + MARKER_START.length, end);
142
- return _parseMarkdownBlock(block);
143
- } catch {
144
- return [];
145
- }
146
- }
147
-
148
- /**
149
- * Parse markdown sections into structured corrections.
150
- * Headings become categories; bullet items become corrections.
151
- */
152
- function _parseMarkdownBlock(markdown) {
153
- const corrections = [];
154
- const lines = markdown.split('\n');
155
- let currentCategory = 'general';
156
-
157
- for (const line of lines) {
158
- const trimmed = line.trim();
159
- if (trimmed.startsWith('### ')) {
160
- currentCategory = trimmed.slice(4).toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
161
- } else if (trimmed.startsWith('## ') && !trimmed.startsWith('### ')) {
162
- currentCategory = trimmed.slice(3).toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
163
- } else if (trimmed.startsWith('- ') && trimmed.length > 5) {
164
- const content = trimmed.slice(2).trim();
165
- // Filter out boilerplate / empty corrections
166
- if (content.length >= 15 && !content.startsWith('*Auto-generated')) {
167
- corrections.push({ category: currentCategory, content });
168
- }
169
- }
170
- }
171
- return corrections;
172
- }
173
-
174
- /**
175
- * Write corrections as KnowledgeAsset nodes to Memgraph.
176
- * Uses MERGE on a stable content-hash ID for idempotency.
177
- * Confidence increases each time the same pattern is reinforced.
178
- */
179
- async function _writeToMemgraph(mgQueryAsync, corrections, sessionId, missionRunId) {
180
- for (const correction of corrections) {
181
- const id = 'headroom-learn:' + crypto.createHash('sha256')
182
- .update(correction.content).digest('hex').slice(0, 16);
183
-
184
- await mgQueryAsync(`
185
- MERGE (ka:KnowledgeAsset {id: $id})
186
- ON CREATE SET
187
- ka.type = 'headroom-correction',
188
- ka.category = $category,
189
- ka.content = $content,
190
- ka.confidence = 0.65,
191
- ka.source = 'headroom-learn',
192
- ka.sessionId = $sessionId,
193
- ka.missionRunId = $missionRunId,
194
- ka.createdAt = localdatetime(),
195
- ka.lastSeenAt = localdatetime()
196
- ON MATCH SET
197
- ka.confidence = CASE
198
- WHEN ka.confidence < 0.95 THEN ka.confidence + 0.05
199
- ELSE 0.95
200
- END,
201
- ka.lastSeenAt = localdatetime(),
202
- ka.updatedAt = localdatetime(),
203
- ka.sessionId = $sessionId,
204
- ka.missionRunId = $missionRunId
205
- `, {
206
- id,
207
- category: correction.category,
208
- content: correction.content,
209
- sessionId: sessionId ?? 'unknown',
210
- missionRunId: missionRunId ?? 'unknown',
211
- });
212
- }
213
- }
214
-
215
- module.exports = { runBridge };