@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
@@ -0,0 +1,251 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SmartCrusher = exports.DEFAULT_CRUSHER_CONFIG = void 0;
4
+ /**
5
+ * lib/compression/smart-crusher.ts
6
+ *
7
+ * TypeScript implementation of the Headroom SmartCrusher algorithm.
8
+ *
9
+ * SmartCrusher compresses JSON arrays by:
10
+ * 1. Lossless path: for homogeneous arrays, render as CSV+schema (removes
11
+ * repeated key names). Wins when savings ≥ 15%.
12
+ * 2. Lossy path: keep first 30% + last 15% + importance-scored middle 55%.
13
+ * Dropped rows are stored in the CCR store and retrievable on demand.
14
+ *
15
+ * Ported from headroom/crates/headroom-core (Rust) algorithm.
16
+ * No external dependencies — pure TypeScript.
17
+ *
18
+ * Every company's HBO data (signals, leads, pipeline, tasks, goals) flows
19
+ * through this compressor before reaching the LLM, reducing token cost
20
+ * by 70–92% for typical array payloads.
21
+ */
22
+ const crypto_1 = require("crypto");
23
+ exports.DEFAULT_CRUSHER_CONFIG = {
24
+ minItemsToAnalyze: 5,
25
+ minCharsToCompress: 200,
26
+ maxItemsAfterCrush: 15,
27
+ firstFraction: 0.3,
28
+ lastFraction: 0.15,
29
+ losslessMinSavingsRatio: 0.15,
30
+ compactionCoreFieldFraction: 0.8,
31
+ };
32
+ // ── Helpers ───────────────────────────────────────────────────────────────────
33
+ function shortHash(data) {
34
+ return (0, crypto_1.createHash)('sha256').update(data).digest('hex').slice(0, 12);
35
+ }
36
+ /**
37
+ * Check if an array is homogeneous enough for lossless CSV compaction.
38
+ *
39
+ * Conditions (all must be true):
40
+ * 1. ≥90% of items are plain objects
41
+ * 2. At least 3 fields appear in ≥ coreFieldFraction of items
42
+ * (single shared field like 'id' is not enough structure for CSV)
43
+ * 3. Core fields cover ≥50% of the average object's field count
44
+ * (prevents trivial matches where most fields are non-shared)
45
+ */
46
+ function isHomogeneousArray(items, coreFieldFraction) {
47
+ if (items.length === 0)
48
+ return { homogeneous: false, coreFields: [] };
49
+ const objectItems = items.filter((x) => x !== null && typeof x === 'object' && !Array.isArray(x));
50
+ if (objectItems.length / items.length < 0.9) {
51
+ return { homogeneous: false, coreFields: [] };
52
+ }
53
+ // Count field frequencies
54
+ const fieldFreq = new Map();
55
+ let totalFields = 0;
56
+ for (const obj of objectItems) {
57
+ const keys = Object.keys(obj);
58
+ totalFields += keys.length;
59
+ for (const key of keys) {
60
+ fieldFreq.set(key, (fieldFreq.get(key) ?? 0) + 1);
61
+ }
62
+ }
63
+ // Core fields: appear in ≥ coreFieldFraction of objects
64
+ const threshold = objectItems.length * coreFieldFraction;
65
+ const coreFields = [...fieldFreq.entries()]
66
+ .filter(([, count]) => count >= threshold)
67
+ .map(([key]) => key)
68
+ .sort();
69
+ // Gate 1: must have at least 3 core fields
70
+ // A single shared key (like 'id') is not enough structure for meaningful CSV
71
+ if (coreFields.length < 3) {
72
+ return { homogeneous: false, coreFields: [] };
73
+ }
74
+ // Gate 2: core fields must cover ≥50% of the average object's field count
75
+ // Prevents trivial CSV on objects where most fields are non-shared
76
+ const avgFieldCount = totalFields / objectItems.length;
77
+ const coverage = coreFields.length / avgFieldCount;
78
+ if (coverage < 0.5) {
79
+ return { homogeneous: false, coreFields: [] };
80
+ }
81
+ return { homogeneous: true, coreFields };
82
+ }
83
+ /**
84
+ * Lossless CSV compaction for homogeneous arrays.
85
+ * Format: header row + value rows, tab-separated within each row,
86
+ * pipe-separated rows. Significantly reduces token count for repeated keys.
87
+ */
88
+ function losslessCsvCompress(items, coreFields) {
89
+ const header = coreFields.join('\t');
90
+ const rows = items.map((item) => coreFields
91
+ .map((f) => {
92
+ const v = item[f];
93
+ if (v === null || v === undefined)
94
+ return '';
95
+ if (typeof v === 'object')
96
+ return JSON.stringify(v);
97
+ return String(v);
98
+ })
99
+ .join('\t'));
100
+ return `[csv:${coreFields.length}cols×${items.length}rows]\n${header}\n${rows.join('\n')}`;
101
+ }
102
+ /**
103
+ * Score items for importance. Higher = more important to keep.
104
+ * Keeps items that: have errors, have non-null/non-zero values,
105
+ * or are at structural boundaries (first/last).
106
+ * Mid-range items are scored by field diversity (more unique values = more important).
107
+ */
108
+ function scoreItems(items) {
109
+ return items.map((item) => {
110
+ let score = 0;
111
+ const str = JSON.stringify(item);
112
+ // Error/anomaly signals — always preserve
113
+ if (str.includes('"error"') ||
114
+ str.includes('"failed"') ||
115
+ str.includes('"ERROR"') ||
116
+ str.includes('"FAILED"')) {
117
+ score += 100;
118
+ }
119
+ // Non-trivial values (not null, not 0, not empty string)
120
+ const values = Object.values(item);
121
+ const nonTrivial = values.filter((v) => v !== null && v !== undefined && v !== '' && v !== 0 && v !== false).length;
122
+ score += (nonTrivial / Math.max(values.length, 1)) * 10;
123
+ // Field diversity (unique values signal interesting data)
124
+ const uniqueVals = new Set(values.map((v) => JSON.stringify(v))).size;
125
+ score += (uniqueVals / Math.max(values.length, 1)) * 5;
126
+ return score;
127
+ });
128
+ }
129
+ // ── Main SmartCrusher ─────────────────────────────────────────────────────────
130
+ class SmartCrusher {
131
+ cfg;
132
+ constructor(config = {}) {
133
+ this.cfg = { ...exports.DEFAULT_CRUSHER_CONFIG, ...config };
134
+ }
135
+ /**
136
+ * Compress a JSON array string.
137
+ * If input is not a JSON array, returns passthrough.
138
+ * If array is too small, returns passthrough.
139
+ * Otherwise attempts lossless CSV then lossy sampling.
140
+ */
141
+ compress(input, ccrStore) {
142
+ const passthrough = {
143
+ output: input,
144
+ compressed: false,
145
+ strategy: 'passthrough',
146
+ ccrHash: null,
147
+ rowsDropped: 0,
148
+ originalCount: 0,
149
+ keptCount: 0,
150
+ };
151
+ // Gate: too short
152
+ if (input.length < this.cfg.minCharsToCompress)
153
+ return passthrough;
154
+ // Parse JSON
155
+ let parsed;
156
+ try {
157
+ parsed = JSON.parse(input);
158
+ }
159
+ catch {
160
+ return passthrough;
161
+ }
162
+ // Must be a non-empty array
163
+ if (!Array.isArray(parsed) || parsed.length < this.cfg.minItemsToAnalyze) {
164
+ return passthrough;
165
+ }
166
+ const items = parsed;
167
+ // ── Lossless path ────────────────────────────────────────────────────────
168
+ const { homogeneous, coreFields } = isHomogeneousArray(items, this.cfg.compactionCoreFieldFraction);
169
+ if (homogeneous) {
170
+ const objectItems = items.filter((x) => x !== null && typeof x === 'object' && !Array.isArray(x));
171
+ const losslessOutput = losslessCsvCompress(objectItems, coreFields);
172
+ const savingsRatio = 1 - losslessOutput.length / input.length;
173
+ if (savingsRatio >= this.cfg.losslessMinSavingsRatio) {
174
+ return {
175
+ output: losslessOutput,
176
+ compressed: true,
177
+ strategy: 'lossless-csv',
178
+ ccrHash: null,
179
+ rowsDropped: 0,
180
+ originalCount: items.length,
181
+ keptCount: items.length,
182
+ };
183
+ }
184
+ }
185
+ // ── Lossy path ───────────────────────────────────────────────────────────
186
+ const n = items.length;
187
+ const maxKeep = this.cfg.maxItemsAfterCrush;
188
+ if (n <= maxKeep) {
189
+ // Already small enough — check if lossless was worth it
190
+ return passthrough;
191
+ }
192
+ const firstCount = Math.max(1, Math.floor(n * this.cfg.firstFraction));
193
+ const lastCount = Math.max(1, Math.floor(n * this.cfg.lastFraction));
194
+ const midBudget = Math.max(0, maxKeep - firstCount - lastCount);
195
+ const firstItems = items.slice(0, firstCount);
196
+ const lastItems = items.slice(n - lastCount);
197
+ const midItems = items.slice(firstCount, n - lastCount);
198
+ // Score mid-range items and keep the most important
199
+ const objectMidItems = midItems.filter((x) => x !== null && typeof x === 'object' && !Array.isArray(x));
200
+ let keptMid;
201
+ let droppedMid;
202
+ if (objectMidItems.length > 0 && midBudget > 0) {
203
+ const scores = scoreItems(objectMidItems);
204
+ const indexed = scores.map((s, i) => ({ s, i }));
205
+ indexed.sort((a, b) => b.s - a.s);
206
+ const keepIndices = new Set(indexed.slice(0, midBudget).map((x) => x.i));
207
+ keptMid = objectMidItems.filter((_, i) => keepIndices.has(i));
208
+ droppedMid = objectMidItems.filter((_, i) => !keepIndices.has(i));
209
+ // Dedup: remove items from keptMid that are identical to firstItems or lastItems
210
+ const existingSet = new Set([...firstItems, ...lastItems].map((x) => JSON.stringify(x)));
211
+ keptMid = keptMid.filter((x) => !existingSet.has(JSON.stringify(x)));
212
+ }
213
+ else {
214
+ keptMid = midBudget > 0 ? midItems.slice(0, midBudget) : [];
215
+ droppedMid = midItems.slice(midBudget);
216
+ }
217
+ const keptItems = [...firstItems, ...keptMid, ...lastItems];
218
+ const droppedItems = droppedMid;
219
+ const rowsDropped = droppedItems.length;
220
+ // Store dropped rows in CCR
221
+ let ccrHash = null;
222
+ if (rowsDropped > 0 && ccrStore) {
223
+ const droppedJson = JSON.stringify(droppedItems);
224
+ ccrHash = shortHash(droppedJson);
225
+ ccrStore.set(ccrHash, droppedJson);
226
+ }
227
+ // Build output
228
+ const ccrMarker = ccrHash && rowsDropped > 0
229
+ ? `\n{"_ccr_dropped":"<<ccr:${ccrHash} ${rowsDropped}_rows_offloaded>>"}`
230
+ : '';
231
+ const output = JSON.stringify(keptItems) + ccrMarker;
232
+ const savingsRatio = 1 - output.length / input.length;
233
+ // Only return lossy result if it actually saved something meaningful
234
+ if (savingsRatio < this.cfg.losslessMinSavingsRatio) {
235
+ // Clean up CCR entry — not worth it
236
+ if (ccrHash && ccrStore)
237
+ ccrStore.delete(ccrHash);
238
+ return passthrough;
239
+ }
240
+ return {
241
+ output,
242
+ compressed: true,
243
+ strategy: 'lossy-sample',
244
+ ccrHash,
245
+ rowsDropped,
246
+ originalCount: items.length,
247
+ keptCount: keptItems.length,
248
+ };
249
+ }
250
+ }
251
+ exports.SmartCrusher = SmartCrusher;
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/lib/eval/index.ts CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -468,7 +468,7 @@ async function mgQueryAsync(cypher, params = {}, opts = {}) {
468
468
  let _queryTimer;
469
469
  const result = await Promise.race([
470
470
  session.run(cleanCypher, params),
471
- new Promise((_, rej) => { _queryTimer = setTimeout(() => rej(new Error('direct bolt query timeout (15s)')), 15000); })
471
+ new Promise((_, rej) => { _queryTimer = setTimeout(() => rej(new Error('direct bolt query timeout (15s)')), parseInt(process.env.HELIOS_BOLT_QUERY_TIMEOUT_MS || '25000', 10)); })
472
472
  ]);
473
473
  clearTimeout(_queryTimer);
474
474
  const keys = result.records.length > 0 ? result.records[0].keys : [];
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cgh567/agent",
3
- "version": "2.4.1",
4
- "description": "Helios agent runtime full stack: extensions, skills, daemon, brain-v2, HEMA, governance",
3
+ "version": "2.4.2",
4
+ "description": "Helios agent runtime \u2014 full stack: extensions, skills, daemon, brain-v2, HEMA, governance",
5
5
  "type": "commonjs",
6
6
  "bin": {
7
7
  "helios-rpc": "./bin/helios-rpc.cjs",
@@ -10,6 +10,7 @@
10
10
  },
11
11
  "scripts": {
12
12
  "build": "node scripts/build-brain-service.js",
13
+ "build:compression": "node node_modules/typescript/bin/tsc -p lib/compression/tsconfig.json",
13
14
  "postinstall": "node postinstall.cjs || echo '[helios-agent] postinstall skipped'",
14
15
  "patch-aliases": "node scripts/patch-pi-aliases.js",
15
16
  "update": "helios update",
@@ -25,7 +26,12 @@
25
26
  "test:hbo-wizard": "node tests/hbo/wizard-infra-gate.test.js && node tests/hbo/wizard-provider-validation.test.js && node tests/hbo/wizard-company-creation.test.js",
26
27
  "test:harbor": "python3 -m pytest evals/harbor/ -v --timeout=60 --tb=short",
27
28
  "test:harada": "python3 -m pytest evals/harbor/test_harada.py -v --timeout=60 --tb=short",
28
- "test:harbor:all": "python3 -m pytest evals/harbor/ -v --timeout=60 --tb=short"
29
+ "test:harbor:all": "python3 -m pytest evals/harbor/ -v --timeout=60 --tb=short",
30
+ "test:compression": "python3 -m pytest evals/harbor/test_compression.py -v --tb=short",
31
+ "db:migrate-hbo": "node --experimental-sqlite daemon/db/hbo-core-migrate.js",
32
+ "db:migrate-all": "node --experimental-sqlite daemon/db/email-infrastructure-migrate.js && node --experimental-sqlite daemon/db/hbo-core-migrate.js",
33
+ "test:hbo-store": "node --experimental-sqlite lib/__tests__/hbo-core-store.test.js",
34
+ "test:hbo-fallback": "NODE_OPTIONS=--experimental-sqlite npx vitest run daemon/__tests__/hbo-core-fallback.test.js"
29
35
  },
30
36
  "pi": {
31
37
  "extensions": [
@@ -79,6 +85,7 @@
79
85
  "croner": "^9.0.0",
80
86
  "ghost-cursor": "^1.4.2",
81
87
  "googleapis": "^171.4.0",
88
+ "headroom-ai": "^0.22.4",
82
89
  "httpsms": "^0.0.4",
83
90
  "ioredis": "^5.10.1",
84
91
  "js-tiktoken": "^1.0.21",
@@ -94,7 +101,6 @@
94
101
  "tree-sitter-javascript": "^0.25.0",
95
102
  "tree-sitter-python": "^0.25.0",
96
103
  "tree-sitter-typescript": "^0.23.2",
97
- "headroom-ai": "^0.22.4",
98
104
  "write-file-atomic": "^7.0.1"
99
105
  },
100
106
  "devDependencies": {
@@ -1,15 +1,13 @@
1
1
  ---
2
2
  disableModelInvocation: true
3
3
  name: talisman-ceo
4
- description: CEO/Strategy agent for Talisman — weekly planning, goal decomposition, department delegation
4
+ description: CEO/Strategy agent — weekly planning, goal decomposition, department delegation
5
5
  when: Loaded by the CEO agent on all planning, review, and delegation tasks
6
6
  adapter:
7
7
  type: helios_rpc
8
8
  skills:
9
9
  - helios-prime
10
10
  - helios-business-operator
11
- agent_id: "agent:ceo"
12
- company_id: talisman
13
11
  departments:
14
12
  - Strategy
15
13
  - Engineering
@@ -30,14 +28,14 @@ If **YES** → Check the approval first. Do NOT start new planning cycles while
30
28
  ### ✅ Example of Good CEO Output:
31
29
  ```
32
30
  Weekly Plan — Week of 2026-05-19
33
- Goal: Ship Talisman v1.2 (30% complete)
31
+ Goal: Grow revenue 30% this quarter (30% complete)
34
32
 
35
33
  Delegated This Week:
36
- - Engineering: Implement bank statement auto-import (priority: P1)
34
+ - Engineering: Implement key product feature (priority: P1)
37
35
  - Sales: Follow up on 3 warm leads from last week (priority: P2)
38
- - Marketing: Draft blog post "5 Signs Your Accounting Firm Needs Automation" (priority: P3)
36
+ - Marketing: Draft blog post on company value proposition (priority: P3)
39
37
 
40
- Pending Approvals: 1 (promote v1.1 to production — review before EOD)
38
+ Pending Approvals: 1 (review before EOD)
41
39
  Budget Status: ✅ 42% used ($25/$60 monthly)
42
40
  ```
43
41
 
@@ -47,20 +45,20 @@ Budget Status: ✅ 42% used ($25/$60 monthly)
47
45
 
48
46
  **Step 1**: Query Memgraph for all pending approvals and current goal progress
49
47
  ```cypher
50
- MATCH (a:Approval {status: 'pending'}) RETURN a.title, a.type, a.createdAt;
51
- MATCH (g:Goal {companyId: 'talisman'}) WHERE g.status <> 'completed' RETURN g.title, g.level, g.status;
48
+ MATCH (a:Approval {companyId: $HELIOS_COMPANY_ID, status: 'pending'}) RETURN a.title, a.type, a.createdAt;
49
+ MATCH (g:CompanyGoal {companyId: $HELIOS_COMPANY_ID}) WHERE g.status <> 'completed' RETURN g.title, g.status;
52
50
  ```
53
51
 
54
52
  **Step 2**: Query all department statuses (Task counts by status for each department)
55
53
  ```cypher
56
- MATCH (t:Task {companyId: 'talisman'})
54
+ MATCH (t:Task {companyId: $HELIOS_COMPANY_ID})
57
55
  WHERE t.status IN ['todo', 'in_progress', 'blocked']
58
56
  RETURN t.status, count(t) AS count;
59
57
  ```
60
58
 
61
59
  **Step 3**: Check budget health
62
- ```bash
63
- cat ~/helios-agent/brainv2/budget-status.json
60
+ ```cypher
61
+ MATCH (bp:BudgetPolicy {companyId: $HELIOS_COMPANY_ID}) RETURN bp.scope, bp.agentId, bp.limitCents, bp.spentCents, bp.percentUsed, bp.status
64
62
  ```
65
63
 
66
64
  ---
@@ -99,7 +97,7 @@ Company Goal (e.g., "Ship Talisman v1.2")
99
97
  └── Task (e.g., "Implement CSV parser for Chase format")
100
98
  ```
101
99
 
102
- - Goals must have: `title`, `level`, `status`, `companyId: 'talisman'`, `ownerId` (dept)
100
+ - Goals must have: `title`, `level`, `status`, `companyId: $HELIOS_COMPANY_ID`, `ownerId` (dept)
103
101
  - Tasks must have: `title`, `priority`, `acceptanceCriteria`, `assignedTo` (dept head), `dueDate`
104
102
  - Never create a task without acceptance criteria
105
103
 
@@ -178,7 +176,7 @@ Approval gates. Your job is quality reviewer:
178
176
  - Any external payment processing → Approval required
179
177
  - New vendor agreements → Approval required
180
178
  - Budget override requests → Approval required
181
- - Approval node schema: `{title, type, status: 'pending', requestedBy, createdAt, companyId: 'talisman'}`
179
+ - Approval node schema: `{title, type, status: 'pending', requestedBy, createdAt, companyId: $HELIOS_COMPANY_ID}`
182
180
 
183
181
  **Goal Rules:**
184
182
  - Goals have 4 levels: Company → Department → Project → Task
@@ -192,7 +190,7 @@ Approval gates. Your job is quality reviewer:
192
190
  - P2/P3 messages: handle in daily triage — batch processing OK
193
191
 
194
192
  **Budget Rules:**
195
- - Budget source of truth: `~/helios-agent/brainv2/budget-status.json`
193
+ - Budget source of truth: Memgraph `MATCH (bp:BudgetPolicy {companyId: $HELIOS_COMPANY_ID})`
196
194
  - Alert threshold: 80% of monthly budget consumed
197
195
  - Hard stop: 95% — no new tasks until next billing cycle
198
196
  - Finance dept owns budget recovery plans
@@ -217,10 +215,10 @@ Before completing any planning cycle:
217
215
  ```cypher
218
216
  CREATE (wp:WeeklyPlan {
219
217
  id: randomUUID(),
220
- companyId: 'talisman',
218
+ companyId: $HELIOS_COMPANY_ID,
221
219
  weekOf: date(),
222
220
  status: 'active',
223
- createdBy: 'agent:ceo',
221
+ createdBy: $HELIOS_AGENT_ID,
224
222
  createdAt: datetime()
225
223
  });
226
224
  ```
@@ -229,7 +227,7 @@ CREATE (wp:WeeklyPlan {
229
227
  ```cypher
230
228
  CREATE (t:Task {
231
229
  id: randomUUID(),
232
- companyId: 'talisman',
230
+ companyId: $HELIOS_COMPANY_ID,
233
231
  title: $title,
234
232
  priority: toInteger($priority),
235
233
  status: 'todo',
@@ -238,7 +236,7 @@ CREATE (t:Task {
238
236
  acceptanceCriteria: $criteria,
239
237
  dueDate: $dueDate,
240
238
  originKind: 'ceo_delegation',
241
- createdBy: 'agent:ceo',
239
+ createdBy: $HELIOS_AGENT_ID,
242
240
  createdAt: localDateTime()
243
241
  });
244
242
  ```
@@ -247,18 +245,18 @@ CREATE (t:Task {
247
245
  ```cypher
248
246
  CREATE (a:Approval {
249
247
  id: randomUUID(),
250
- companyId: 'talisman',
248
+ companyId: $HELIOS_COMPANY_ID,
251
249
  title: $title,
252
250
  type: $type,
253
251
  status: 'pending',
254
- requestedBy: 'agent:ceo',
252
+ requestedBy: $HELIOS_AGENT_ID,
255
253
  createdAt: datetime()
256
254
  });
257
255
  ```
258
256
 
259
257
  ### Flag At-Risk Goal
260
258
  ```cypher
261
- MATCH (g:Goal {companyId: 'talisman', id: $goalId})
259
+ MATCH (g:CompanyGoal {companyId: $HELIOS_COMPANY_ID, id: $goalId})
262
260
  SET g.atRisk = true, g.atRiskReason = $reason, g.atRiskFlaggedAt = datetime();
263
261
  ```
264
262
 
@@ -269,17 +267,17 @@ When you need to create follow-up work or delegate, create a BusinessTask node:
269
267
  ```cypher
270
268
  MERGE (bt:BusinessTask {id: $id})
271
269
  ON CREATE SET
272
- bt.companyId = 'talisman',
270
+ bt.companyId = $HELIOS_COMPANY_ID,
273
271
  bt.title = $title,
274
272
  bt.description = $description,
275
- bt.assigneeId = 'agent:ceo',
273
+ bt.assigneeId = $assigneeAgentId,
276
274
  bt.priority = $priority,
277
275
  bt.status = 'todo',
278
276
  bt.goalId = $goalId,
279
277
  bt.createdAt = datetime()
280
278
  ```
281
279
 
282
- **Your agent ID**: `agent:ceo`
280
+ **Your agent ID**: use `$HELIOS_AGENT_ID` env var
283
281
  **Priority values**: `'critical'`, `'high'`, `'medium'`, `'low'`
284
282
  **CRITICAL**: `assigneeId` must always be set — null means the task is never dispatched.
285
283
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  disableModelInvocation: true
3
3
  name: talisman-comms
4
- description: Communications/Triage department for Talisman — auto-routing, response drafting, and escalation via triage pipeline
4
+ description: Communications/Triage department — auto-routing, response drafting, and escalation via triage pipeline
5
5
  when: Loaded by the Communications agent when running triage, classifying messages, or drafting responses
6
6
  disable-model-invocation: true
7
7
  ---
@@ -37,7 +37,7 @@ cd ~/helios-agent && node extensions/email/commands/triage.js --limit 20 2>&1 |
37
37
 
38
38
  **Step 2**: Query classification results — sort by priority
39
39
  ```
40
- MATCH (m:Message {companyId: 'talisman', processed: false})
40
+ MATCH (m:Message {companyId: $HELIOS_COMPANY_ID, processed: false})
41
41
  RETURN m.priority, count(m) as count ORDER BY m.priority
42
42
  ```
43
43
 
@@ -83,17 +83,17 @@ When you need to create follow-up work or delegate, create a BusinessTask node:
83
83
  ```cypher
84
84
  MERGE (bt:BusinessTask {id: $id})
85
85
  ON CREATE SET
86
- bt.companyId = 'talisman',
86
+ bt.companyId = $HELIOS_COMPANY_ID,
87
87
  bt.title = $title,
88
88
  bt.description = $description,
89
- bt.assigneeId = 'agent:comms',
89
+ bt.assigneeId = $HELIOS_AGENT_ID,
90
90
  bt.priority = $priority,
91
91
  bt.status = 'todo',
92
92
  bt.goalId = $goalId,
93
93
  bt.createdAt = datetime()
94
94
  ```
95
95
 
96
- **Your agent ID**: `agent:comms`
96
+ **Your agent ID**: use `$HELIOS_AGENT_ID` env var
97
97
  **Priority values**: `'critical'`, `'high'`, `'medium'`, `'low'`
98
98
  **CRITICAL**: `assigneeId` must always be set — null means the task is never dispatched.
99
99
 
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  disableModelInvocation: true
3
3
  name: talisman-engineering
4
- description: "Engineering department for Talisman — autonomous feature development from backlog to staging PR. Runs the complete pipeline: Plan → Code → Test → PR → Staging."
5
- when: Loaded by the CTO/Engineering agent when processing engineering tasks for the Talisman project
4
+ description: "Engineering department — autonomous feature development from backlog to staging PR. Runs the complete pipeline: Plan → Code → Test → PR → Staging."
5
+ when: Loaded by the CTO/Engineering agent when processing engineering tasks
6
6
  disable-model-invocation: true
7
7
  ---
8
8
 
@@ -261,17 +261,17 @@ When you need to create follow-up work or delegate, create a BusinessTask node:
261
261
  ```cypher
262
262
  MERGE (bt:BusinessTask {id: $id})
263
263
  ON CREATE SET
264
- bt.companyId = 'talisman',
264
+ bt.companyId = $HELIOS_COMPANY_ID,
265
265
  bt.title = $title,
266
266
  bt.description = $description,
267
- bt.assigneeId = 'agent:cto',
267
+ bt.assigneeId = $HELIOS_AGENT_ID,
268
268
  bt.priority = $priority,
269
269
  bt.status = 'todo',
270
270
  bt.goalId = $goalId,
271
271
  bt.createdAt = datetime()
272
272
  ```
273
273
 
274
- **Your agent ID**: `agent:cto`
274
+ **Your agent ID**: use `$HELIOS_AGENT_ID` env var
275
275
  **Priority values**: `'critical'`, `'high'`, `'medium'`, `'low'`
276
276
  **CRITICAL**: `assigneeId` must always be set — null means the task is never dispatched.
277
277