@cgh567/agent 2.4.1 → 2.4.2

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 (146) 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/config/com.familiar.helios-daemon.plist +5 -0
  6. package/daemon/config/helios-daemon.service +4 -0
  7. package/daemon/context-enrichment.js +59 -21
  8. package/daemon/helios-api.js +149 -37
  9. package/daemon/helios-company-daemon.js +516 -124
  10. package/daemon/lib/harada/cascade-judge.js +12 -50
  11. package/daemon/lib/harada/mandala.js +20 -0
  12. package/daemon/lib/harada/pillar-dispatcher.js +1 -1
  13. package/daemon/lib/harada/project-factory.js +7 -2
  14. package/daemon/lib/hbo-bridge.js +31 -12
  15. package/daemon/lib/helios-hitl-host.js +15 -2
  16. package/daemon/lib/hitl-interaction-service.js +0 -0
  17. package/daemon/lib/memgraph-verify.js +38 -33
  18. package/daemon/lib/project-drift-detector.js +7 -17
  19. package/daemon/lib/project-semantic-updater.js +1 -14
  20. package/daemon/routes/channels.js +10 -5
  21. package/daemon/routes/harada-map.js +11 -48
  22. package/daemon/routes/hbo.js +89 -28
  23. package/daemon/routes/hitl.js +0 -0
  24. package/daemon/routes/project.js +4 -3
  25. package/daemon/routes/wizard.js +11 -4
  26. package/daemon/schema-migrations-hitl.js +0 -0
  27. package/extensions/001-tool-output-cap.ts +0 -0
  28. package/extensions/context-compaction.ts +45 -26
  29. package/extensions/cortex/activation-bridge.ts +5 -0
  30. package/extensions/cortex/learn.ts +26 -0
  31. package/extensions/email/backfill.ts +0 -0
  32. package/extensions/helios-governance/analysis/ambiguity.ts +0 -0
  33. package/extensions/helios-governance/analysis/compliance.ts +0 -0
  34. package/extensions/helios-governance/analysis/long-task-detector.ts +0 -0
  35. package/extensions/helios-governance/analysis/output-contract.ts +0 -0
  36. package/extensions/helios-governance/analysis/patterns.ts +0 -0
  37. package/extensions/helios-governance/analysis/preflight.ts +0 -0
  38. package/extensions/helios-governance/analysis/recurring-violations.ts +0 -0
  39. package/extensions/helios-governance/analysis/task-classification.ts +0 -0
  40. package/extensions/helios-governance/analysis/task-intent.ts +0 -0
  41. package/extensions/helios-governance/gates/high-impact.ts +1 -1
  42. package/extensions/helios-governance/handlers/_jiti-require.ts +15 -8
  43. package/extensions/helios-governance/handlers/proxy-test-detector.ts +0 -0
  44. package/extensions/hema-dispatch-v3/graph-memory.ts +10 -0
  45. package/extensions/hema-dispatch-v3/index.ts +59 -40
  46. package/extensions/lib/elo-engine.js +0 -0
  47. package/extensions/lib/elo-engine.test.js +0 -0
  48. package/extensions/memgraph-autostart.ts +13 -0
  49. package/extensions/neuroplastic-eval.ts +0 -0
  50. package/extensions/shadow-loop/index.ts +0 -0
  51. package/lib/brain-v2-budget.js +0 -0
  52. package/lib/brain-v2-circuit-breaker.js +0 -0
  53. package/lib/brain-v2.js +0 -0
  54. package/lib/broker/adaptive-throttle.js +0 -0
  55. package/lib/broker/batch-coalescer.js +0 -0
  56. package/lib/broker/bulkhead.js +0 -0
  57. package/lib/broker/channel-registry.js +0 -0
  58. package/lib/broker/circuit-breaker.js +0 -0
  59. package/lib/broker/evidence-cache.js +0 -0
  60. package/lib/broker/health-monitor.js +0 -0
  61. package/lib/broker/mage-queue.js +0 -0
  62. package/lib/broker/priority-queue.js +0 -0
  63. package/lib/broker/server.js.bak-error2-fix +0 -0
  64. package/lib/broker/session-registry.js +0 -0
  65. package/lib/broker/singleton-timers.js +0 -0
  66. package/lib/broker/types.d.ts +0 -0
  67. package/lib/broker/vegas-limit.js +0 -0
  68. package/lib/compression/dist/ccr-store.js +74 -0
  69. package/lib/compression/dist/content-router.js +115 -0
  70. package/lib/compression/dist/pipeline.js +113 -0
  71. package/lib/compression/dist/server.js +265 -0
  72. package/lib/compression/dist/smart-crusher.js +251 -0
  73. package/lib/context-budget.ts +0 -0
  74. package/lib/context-firewall.js +0 -0
  75. package/lib/crm/integration/triage-bridge.js +0 -0
  76. package/lib/email-utils.ts +0 -0
  77. package/lib/eval/__tests__/preflight-checker.test.ts +0 -0
  78. package/lib/eval/__tests__/task-instruction-parser.test.ts +0 -0
  79. package/lib/eval/__tests__/verifier-runner.test.ts +0 -0
  80. package/lib/eval/index.ts +0 -0
  81. package/lib/eval/preflight-checker.ts +0 -0
  82. package/lib/eval/task-domain-classifier.ts +0 -0
  83. package/lib/eval/task-instruction-parser.ts +0 -0
  84. package/lib/eval/verifier-runner.ts +0 -0
  85. package/lib/event-bus.d.ts +0 -0
  86. package/lib/governance-context-selector.ts +0 -0
  87. package/lib/graph/generate-extension-embeddings.js +0 -0
  88. package/lib/graph/generate-static-embeddings.js +0 -0
  89. package/lib/graph/lib/utils.js +1 -1
  90. package/lib/graph-audit.d.ts +0 -0
  91. package/lib/mesh-circuit-breaker.js +0 -0
  92. package/lib/mission-loop/lesson-extractor.ts +0 -0
  93. package/lib/mission-loop/mental-model-scorer.ts +0 -0
  94. package/lib/mission-loop/occ-detector.ts +0 -0
  95. package/lib/mission-loop/query-variants.ts +0 -0
  96. package/lib/mission-loop/verifier-check.ts +0 -0
  97. package/lib/skill-reference-builder.ts +0 -0
  98. package/lib/telemetry/token-breakdown.ts +0 -0
  99. package/lib/tool-compressor.ts +0 -0
  100. package/lib/triage-core/legal-routing.ts +0 -0
  101. package/lib/triage-core/mental-model/dunbar-classifier.ts +0 -0
  102. package/lib/triage-core/mental-model/enrich-all.ts +0 -0
  103. package/lib/triage-core/mental-model/identity-resolver.ts +0 -0
  104. package/lib/triage-core/mental-model/key-facts.ts +0 -0
  105. package/lib/triage-core/mental-model/model-assembler.ts +0 -0
  106. package/lib/triage-core/orchestrator.ts +0 -0
  107. package/lib/triage-core/orchestrator.ts.bak-r005-r006-r008 +0 -0
  108. package/package.json +10 -4
  109. package/skills/helios-business-operator/services/signals/upwork-signals.js +0 -0
  110. package/skills/talisman-ceo/SKILL.md +23 -25
  111. package/skills/talisman-comms/SKILL.md +5 -5
  112. package/skills/talisman-engineering/SKILL.md +5 -5
  113. package/skills/talisman-finance/SKILL.md +10 -8
  114. package/skills/talisman-marketing/SKILL.md +10 -10
  115. package/skills/talisman-sales/SKILL.md +12 -15
  116. package/skills/talisman-support/SKILL.md +5 -5
  117. package/agents/business/talisman-ceo.md +0 -183
  118. package/agents/business/talisman-comms.md +0 -257
  119. package/agents/business/talisman-cto.md +0 -153
  120. package/agents/business/talisman-finance.md +0 -246
  121. package/agents/business/talisman-marketing.md +0 -240
  122. package/agents/business/talisman-sales.md +0 -242
  123. package/agents/business/talisman-support.md +0 -236
  124. package/daemon/lib/approval-expiry.js +0 -162
  125. package/daemon/lib/blast-radius-analyzer.js +0 -75
  126. package/daemon/lib/domain-bootstrap-orchestrator.js +0 -267
  127. package/daemon/lib/forensic-log.js +0 -113
  128. package/daemon/lib/goal-research-pipeline.js +0 -644
  129. package/daemon/lib/harada/cascade-research-dispatcher.js +0 -261
  130. package/daemon/lib/headroom-middleware.js +0 -167
  131. package/daemon/lib/headroom-proxy-manager.js +0 -623
  132. package/daemon/lib/hed-engine.js +0 -307
  133. package/daemon/lib/mental-model-cache.js +0 -96
  134. package/daemon/lib/project-factory.js +0 -47
  135. package/daemon/lib/session-log-reader.js +0 -93
  136. package/daemon/routes/hed.js +0 -133
  137. package/lib/graph/learning/headroom-learn-bridge.js +0 -215
  138. package/skills/helios-bookkeeping/SKILL.md +0 -321
  139. package/skills/helios-briefer/SKILL.md +0 -44
  140. package/skills/helios-client-relations/SKILL.md +0 -322
  141. package/skills/helios-personal-triager/SKILL.md +0 -45
  142. package/skills/helios-recruitment/SKILL.md +0 -317
  143. package/skills/helios-relationship-nudger/SKILL.md +0 -77
  144. package/skills/helios-researcher/SKILL.md +0 -44
  145. package/skills/helios-scheduler/SKILL.md +0 -58
  146. package/skills/helios-tax-analyst/SKILL.md +0 -280
@@ -1,307 +0,0 @@
1
- 'use strict';
2
-
3
- const { randomUUID } = require('crypto');
4
-
5
- /**
6
- * HEDEngine — Helios Execution Document orchestration.
7
- * A HED is a PlanChangeProposal {scope:'execution'} with wave-based CRUD operations.
8
- * Agents execute against HEDOperations; Murray audits each completion.
9
- */
10
-
11
- class HEDEngine {
12
- constructor(mg) {
13
- this._mg = mg;
14
- }
15
-
16
- // ─── Create ────────────────────────────────────────────────────────────────
17
-
18
- async createHED({ companyId, title, intent, worldStateSnapshot, operations, goalId }) {
19
- const hedId = `hed_${Date.now()}_${randomUUID().slice(0, 8)}`;
20
- const now = new Date().toISOString();
21
-
22
- // Create PlanChangeProposal with scope='execution'
23
- await this._mg(
24
- `CREATE (pcp:PlanChangeProposal {
25
- id: $id, companyId: $companyId, scope: 'execution',
26
- title: $title, intent: $intent,
27
- worldStateSnapshot: $worldStateSnapshot,
28
- status: 'draft', requiresApproval: true,
29
- waveCount: $waveCount, currentWave: 1,
30
- createdAt: $now, resolvedAt: null, resolvedBy: null
31
- })`,
32
- {
33
- id: hedId, companyId, title, intent,
34
- worldStateSnapshot: JSON.stringify(worldStateSnapshot || {}),
35
- waveCount: Math.max(...operations.map(o => o.wave || 1), 1),
36
- now
37
- }
38
- );
39
-
40
- if (goalId) {
41
- await this._mg(
42
- `MATCH (pcp:PlanChangeProposal {id: $hedId}), (g:BusinessGoal {id: $goalId})
43
- MERGE (pcp)-[:IMPLEMENTS_GOAL]->(g)`,
44
- { hedId, goalId }
45
- ).catch(() => {});
46
- }
47
-
48
- // Create HEDOperation nodes
49
- for (const op of operations) {
50
- await this.createOperation(hedId, op, now);
51
- }
52
-
53
- return { hedId };
54
- }
55
-
56
- async createOperation(hedId, op, now = new Date().toISOString()) {
57
- const opId = op.opId || `op_${Date.now()}_${randomUUID().slice(0, 6)}`;
58
- await this._mg(
59
- `MATCH (pcp:PlanChangeProposal {id: $hedId})
60
- CREATE (o:HEDOperation {
61
- id: $opId, hedId: $hedId,
62
- type: $type, target: $target, targetScope: $targetScope,
63
- description: $description,
64
- acceptanceCriteria: $acceptanceCriteria,
65
- dependsOn: $dependsOn, wave: $wave,
66
- assignedAgentRole: $assignedAgentRole,
67
- deviationPolicy: $deviationPolicy,
68
- status: 'pending', createdAt: $now
69
- })
70
- MERGE (pcp)-[:HAS_OPERATION]->(o)`,
71
- {
72
- hedId, opId,
73
- type: op.type || 'UPDATE',
74
- target: op.target || '',
75
- targetScope: op.targetScope || '',
76
- description: op.description || '',
77
- acceptanceCriteria: JSON.stringify(op.acceptanceCriteria || []),
78
- dependsOn: JSON.stringify(op.dependsOn || []),
79
- wave: op.wave || 1,
80
- assignedAgentRole: op.assignedAgentRole || 'worker',
81
- deviationPolicy: op.deviationPolicy || 'smart',
82
- now
83
- }
84
- );
85
- return opId;
86
- }
87
-
88
- // ─── Read ──────────────────────────────────────────────────────────────────
89
-
90
- async getHED(hedId) {
91
- const rows = await this._mg(
92
- `MATCH (pcp:PlanChangeProposal {id: $hedId, scope: 'execution'})
93
- RETURN pcp`,
94
- { hedId }
95
- );
96
- if (!rows?.length) return null;
97
- const pcp = rows[0].pcp;
98
- try { pcp.worldStateSnapshot = JSON.parse(pcp.worldStateSnapshot); } catch {}
99
- return pcp;
100
- }
101
-
102
- async getOperations(hedId) {
103
- const rows = await this._mg(
104
- `MATCH (pcp:PlanChangeProposal {id: $hedId})-[:HAS_OPERATION]->(o:HEDOperation)
105
- RETURN o ORDER BY o.wave ASC, o.id ASC`,
106
- { hedId }
107
- );
108
- return (rows || []).map(r => {
109
- const o = r.o;
110
- try { o.acceptanceCriteria = JSON.parse(o.acceptanceCriteria); } catch { o.acceptanceCriteria = []; }
111
- try { o.dependsOn = JSON.parse(o.dependsOn); } catch { o.dependsOn = []; }
112
- return o;
113
- });
114
- }
115
-
116
- async getOperation(opId) {
117
- const rows = await this._mg(
118
- `MATCH (o:HEDOperation {id: $opId}) RETURN o`, { opId }
119
- );
120
- if (!rows?.length) return null;
121
- const o = rows[0].o;
122
- try { o.acceptanceCriteria = JSON.parse(o.acceptanceCriteria); } catch { o.acceptanceCriteria = []; }
123
- try { o.dependsOn = JSON.parse(o.dependsOn); } catch { o.dependsOn = []; }
124
- return o;
125
- }
126
-
127
- // ─── State transitions ─────────────────────────────────────────────────────
128
-
129
- async approveHED(hedId, approvedBy) {
130
- const now = new Date().toISOString();
131
- await this._mg(
132
- `MATCH (pcp:PlanChangeProposal {id: $hedId, scope: 'execution', status: 'draft'})
133
- SET pcp.status = 'approved', pcp.approvedAt = $now, pcp.approvedBy = $approvedBy`,
134
- { hedId, now, approvedBy }
135
- );
136
- }
137
-
138
- async claimOperation(opId, agentId) {
139
- const now = new Date().toISOString();
140
- const rows = await this._mg(
141
- `MATCH (o:HEDOperation {id: $opId, status: 'pending'})
142
- SET o.status = 'in_progress', o.executionLockedAt = $now, o.executionAgentId = $agentId
143
- RETURN o.id AS claimed`,
144
- { opId, now, agentId }
145
- );
146
- return rows?.[0]?.claimed === opId;
147
- }
148
-
149
- async completeOperation(opId, { summary, sessionKey, verdict = 'pending_review' }) {
150
- const now = new Date().toISOString();
151
- await this._mg(
152
- `MATCH (o:HEDOperation {id: $opId})
153
- SET o.status = $status, o.completedAt = $now,
154
- o.executionSummary = $summary, o.sessionKey = $sessionKey`,
155
- { opId, status: verdict === 'pending_review' ? 'done' : verdict, now, summary, sessionKey }
156
- );
157
- }
158
-
159
- async postReviewFinding(opId, finding) {
160
- const findingId = `hrf_${Date.now()}_${randomUUID().slice(0, 6)}`;
161
- const now = new Date().toISOString();
162
- await this._mg(
163
- `MATCH (o:HEDOperation {id: $opId})
164
- CREATE (f:HEDReviewFinding {
165
- id: $findingId, opId: $opId, hedId: o.hedId,
166
- verdict: $verdict, deviationClass: $deviationClass,
167
- criteriaResults: $criteriaResults,
168
- deviationNarrative: $deviationNarrative,
169
- blockingBehavior: $blockingBehavior,
170
- createdAt: $now
171
- })
172
- MERGE (o)-[:HAS_REVIEW]->(f)
173
- SET o.reviewVerdict = $verdict`,
174
- {
175
- findingId, opId,
176
- verdict: finding.verdict || 'ALIGNED',
177
- deviationClass: finding.deviationClass || null,
178
- criteriaResults: JSON.stringify(finding.criteriaResults || []),
179
- deviationNarrative: finding.deviationNarrative || '',
180
- blockingBehavior: finding.blockingBehavior || 'continue',
181
- now
182
- }
183
- );
184
- // If hard block — pause HED
185
- if (finding.blockingBehavior === 'hard-pause') {
186
- await this._mg(
187
- `MATCH (pcp:PlanChangeProposal {id: o.hedId})
188
- MATCH (o:HEDOperation {id: $opId})
189
- SET pcp.status = 'review'`,
190
- { opId }
191
- ).catch(() => {});
192
- }
193
- if (finding.blastRadius) {
194
- await this._writeBlastRadius(opId, finding.blastRadius, now);
195
- }
196
- return findingId;
197
- }
198
-
199
- async _writeBlastRadius(opId, br, now) {
200
- const brId = `bre_${Date.now()}_${randomUUID().slice(0, 6)}`;
201
- await this._mg(
202
- `MATCH (o:HEDOperation {id: $opId})
203
- CREATE (b:BlastRadiusEvent {
204
- id: $brId, opId: $opId,
205
- filesChanged: $filesChanged,
206
- nodesAffected: $nodesAffected,
207
- severity: $severity,
208
- createdAt: $now
209
- })
210
- MERGE (o)-[:HAS_BLAST_RADIUS]->(b)`,
211
- {
212
- brId, opId,
213
- filesChanged: JSON.stringify(br.filesChanged || []),
214
- nodesAffected: JSON.stringify(br.nodesAffected || []),
215
- severity: br.severity || 'none',
216
- now
217
- }
218
- ).catch(err => console.error('[hed-engine] blast radius write failed:', err.message));
219
- }
220
-
221
- // ─── Wave advancement ──────────────────────────────────────────────────────
222
-
223
- async checkWaveAdvancement(companyId) {
224
- // Find approved HEDs for this company that are executing
225
- const heds = await this._mg(
226
- `MATCH (pcp:PlanChangeProposal {companyId: $companyId, scope: 'execution', status: 'approved'})
227
- RETURN pcp.id AS hedId, pcp.currentWave AS currentWave, pcp.waveCount AS waveCount`,
228
- { companyId }
229
- );
230
- for (const { hedId, currentWave, waveCount } of (heds || [])) {
231
- await this._advanceWaveIfReady(hedId, currentWave, waveCount);
232
- }
233
- }
234
-
235
- async _advanceWaveIfReady(hedId, currentWave, waveCount) {
236
- // Check if all ops in currentWave are done or deviated (not blocked/failed)
237
- const pending = await this._mg(
238
- `MATCH (pcp:PlanChangeProposal {id: $hedId})-[:HAS_OPERATION]->(o:HEDOperation)
239
- WHERE o.wave = $wave AND o.status IN ['pending', 'in_progress']
240
- RETURN count(o) AS pendingCount`,
241
- { hedId, wave: currentWave }
242
- );
243
- const hardBlocked = await this._mg(
244
- `MATCH (pcp:PlanChangeProposal {id: $hedId})-[:HAS_OPERATION]->(o:HEDOperation)
245
- WHERE o.wave = $wave AND o.reviewVerdict = 'FAILED'
246
- RETURN count(o) AS blockedCount`,
247
- { hedId, wave: currentWave }
248
- );
249
- if ((pending?.[0]?.pendingCount || 0) > 0) return; // wave still running
250
- if ((hardBlocked?.[0]?.blockedCount || 0) > 0) {
251
- // Hard block — pause entire HED
252
- await this._mg(
253
- `MATCH (pcp:PlanChangeProposal {id: $hedId}) SET pcp.status = 'review'`,
254
- { hedId }
255
- );
256
- // Publish PLAN_STALLED to mesh bus — fires the already-wired reflexion loop subscriber
257
- try {
258
- const mesh = (global).__helios_session_mesh;
259
- if (mesh?.bus?.publish) {
260
- mesh.bus.publish('PLAN_STALLED', {
261
- hedId,
262
- wave: currentWave,
263
- reason: 'HEDOperation hard-blocked by reviewer verdict',
264
- timestamp: new Date().toISOString(),
265
- });
266
- }
267
- } catch (_meshErr) { /* mesh not available in daemon context — non-fatal */ }
268
- return;
269
- }
270
- if (currentWave >= waveCount) {
271
- // All waves complete
272
- await this._mg(
273
- `MATCH (pcp:PlanChangeProposal {id: $hedId}) SET pcp.status = 'closed', pcp.closedAt = $now`,
274
- { hedId, now: new Date().toISOString() }
275
- );
276
- } else {
277
- // Advance to next wave
278
- await this._mg(
279
- `MATCH (pcp:PlanChangeProposal {id: $hedId}) SET pcp.currentWave = $nextWave`,
280
- { hedId, nextWave: currentWave + 1 }
281
- );
282
- }
283
- }
284
-
285
- // ─── Context brief helper ──────────────────────────────────────────────────
286
-
287
- async buildOperationContext(opId) {
288
- const op = await this.getOperation(opId);
289
- if (!op) return null;
290
- const hed = await this.getHED(op.hedId);
291
- if (!hed) return null;
292
- return {
293
- hedIntent: hed.intent,
294
- worldStateSnapshot: hed.worldStateSnapshot,
295
- operation: {
296
- type: op.type,
297
- target: op.target,
298
- targetScope: op.targetScope,
299
- description: op.description,
300
- acceptanceCriteria: op.acceptanceCriteria,
301
- deviationPolicy: op.deviationPolicy
302
- }
303
- };
304
- }
305
- }
306
-
307
- module.exports = { HEDEngine };
@@ -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 };