@cgh567/agent 2.4.2 → 2.4.4

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 (157) hide show
  1. package/agents/business/talisman-ceo.md +183 -0
  2. package/agents/business/talisman-comms.md +257 -0
  3. package/agents/business/talisman-cto.md +153 -0
  4. package/agents/business/talisman-finance.md +246 -0
  5. package/agents/business/talisman-marketing.md +240 -0
  6. package/agents/business/talisman-sales.md +242 -0
  7. package/agents/business/talisman-support.md +236 -0
  8. package/bin/helios-rpc.js +19 -0
  9. package/daemon/adapters/helios-rpc-adapter.js +5 -12
  10. package/daemon/adapters/tui_wakeup.js +8 -0
  11. package/daemon/context-enrichment.js +27 -0
  12. package/daemon/daemon-manager.js +1 -1
  13. package/daemon/db/email-infrastructure-migrate.js +192 -0
  14. package/daemon/db/hbo-core-migrate.js +189 -0
  15. package/daemon/helios-api.js +863 -64
  16. package/daemon/helios-company-daemon.js +233 -33
  17. package/daemon/lib/blast-radius-analyzer.js +75 -0
  18. package/daemon/lib/domain-bootstrap-orchestrator.js +267 -0
  19. package/daemon/lib/forensic-log.js +113 -0
  20. package/daemon/lib/goal-research-pipeline.js +644 -0
  21. package/daemon/lib/harada/cascade-judge.js +84 -1
  22. package/daemon/lib/harada/cascade-research-dispatcher.js +282 -0
  23. package/daemon/lib/harada/pillar-dispatcher.js +23 -2
  24. package/daemon/lib/hbo-bridge.js +74 -6
  25. package/daemon/lib/headroom-middleware.js +129 -0
  26. package/daemon/lib/headroom-proxy-manager.js +309 -0
  27. package/daemon/lib/hed-engine.js +25 -0
  28. package/daemon/lib/intelligence/department-page-generator.js +46 -1
  29. package/daemon/lib/interpretation-engine.js +92 -0
  30. package/daemon/lib/mental-model-cache.js +96 -0
  31. package/daemon/lib/project-factory.js +47 -0
  32. package/daemon/lib/session-log-reader.js +93 -0
  33. package/daemon/lib/standard-work-bootstrap.js +87 -1
  34. package/daemon/lib/task-completion-processor.js +23 -0
  35. package/daemon/lib/wizard-engine.js +57 -6
  36. package/daemon/package.json +2 -1
  37. package/daemon/routes/agents.js +51 -6
  38. package/daemon/routes/channels.js +116 -2
  39. package/daemon/routes/crm.js +85 -0
  40. package/daemon/routes/dashboard.js +62 -16
  41. package/daemon/routes/dept.js +10 -1
  42. package/daemon/routes/email-triage.js +19 -10
  43. package/daemon/routes/hbo.js +618 -58
  44. package/daemon/routes/hed.js +133 -0
  45. package/daemon/routes/inbox.js +397 -8
  46. package/daemon/routes/project.js +580 -66
  47. package/daemon/routes/routines.js +14 -0
  48. package/daemon/routes/tasks.js +15 -1
  49. package/daemon/schema-apply.js +174 -0
  50. package/daemon/schema-definitions.js +433 -0
  51. package/daemon/schema-migrations-hbo.js +20 -0
  52. package/daemon/schema-migrations-hed.js +18 -0
  53. package/daemon/schema-migrations-proj.js +153 -0
  54. package/extensions/__tests__/codebase-index.test.ts +73 -0
  55. package/extensions/__tests__/extension-command-registration.test.ts +35 -0
  56. package/extensions/__tests__/git-push-guard.test.ts +68 -0
  57. package/extensions/context-compaction.ts +104 -76
  58. package/extensions/cortex/__tests__/cortex-core.test.ts +100 -0
  59. package/extensions/cortex/wal-replay.ts +91 -0
  60. package/extensions/email/actions/draft-response.ts +21 -1
  61. package/extensions/email/auth/accounts.ts +5 -11
  62. package/extensions/email/auth/inbox-dog.ts +5 -2
  63. package/extensions/email/backfill.ts +20 -13
  64. package/extensions/email/providers/gmail.ts +164 -0
  65. package/extensions/email/providers/google-calendar.ts +34 -5
  66. package/extensions/helios-browser/__tests__/browser-routing.test.ts +57 -0
  67. package/extensions/helios-browser/backends/playwright.ts +3 -1
  68. package/extensions/helios-governance/__tests__/governance-gates.test.ts +40 -0
  69. package/extensions/helios-governance/__tests__/tournament-consumer.test.js +66 -0
  70. package/extensions/hema-dispatch-v3/headroom-compress.ts +103 -0
  71. package/extensions/hema-dispatch-v3/index.ts +46 -72
  72. package/extensions/interview/__tests__/server.test.ts +117 -0
  73. package/extensions/lib/helios-root.cjs +46 -0
  74. package/extensions/subagent-mesh/__tests__/handlers.test.ts +98 -0
  75. package/extensions/warm-tick/warm-tick-maintenance.ts +164 -0
  76. package/lib/__tests__/bulk-ingest.live.test.ts +66 -0
  77. package/lib/__tests__/crash-fixes.test.ts +49 -0
  78. package/lib/__tests__/hbo-core-store.test.js +238 -0
  79. package/lib/__tests__/maintenance-mission-wiring.test.ts +35 -0
  80. package/lib/broker/__tests__/jit-subscription.test.js +44 -1
  81. package/lib/broker/__tests__/lifecycle-channels.test.js +25 -1
  82. package/lib/compression/__tests__/ccr-store.test.js +138 -0
  83. package/lib/compression/__tests__/pipeline.test.js +280 -0
  84. package/lib/compression/__tests__/smart-crusher.test.js +242 -0
  85. package/lib/compression/dist/server.js +34 -1
  86. package/lib/compression/dist/start-server.js +77 -0
  87. package/lib/event-bus.mts +1 -1
  88. package/lib/graph/learning/headroom-learn-bridge.js +175 -0
  89. package/lib/graph-availability.js +62 -0
  90. package/lib/hbo-core-store.compiled.js +834 -0
  91. package/lib/hbo-core-store.js +124 -0
  92. package/lib/hbo-core-store.ts +979 -0
  93. package/lib/mission-loop/__tests__/research-handler.test.ts +143 -0
  94. package/lib/skill-sync.js +6 -1
  95. package/lib/startup-integrity.js +9 -2
  96. package/lib/triage-core/__tests__/classifier-fixture.test.ts +254 -0
  97. package/lib/triage-core/__tests__/classifier-post-norm.test.ts +1 -1
  98. package/lib/triage-core/__tests__/classifier.test.ts +45 -7
  99. package/lib/triage-core/__tests__/correction-detector.test.ts +36 -0
  100. package/lib/triage-core/__tests__/d6-dunbar-boost.test.ts +5 -5
  101. package/lib/triage-core/__tests__/orchestrator-pipeline.test.ts +107 -0
  102. package/lib/triage-core/__tests__/orchestrator.test.ts +113 -1
  103. package/lib/triage-core/__tests__/signals.test.ts +357 -0
  104. package/lib/triage-core/__tests__/sql-parity.test.ts +216 -0
  105. package/lib/triage-core/backfill-cost-estimator.ts +91 -0
  106. package/lib/triage-core/backfill-orchestrator.ts +119 -0
  107. package/lib/triage-core/classifier.ts +41 -8
  108. package/lib/triage-core/cos/cross-channel-escalation.ts +2 -3
  109. package/lib/triage-core/cos/response-debt.ts +2 -2
  110. package/lib/triage-core/graph/__tests__/batch-persistence.test.ts +283 -0
  111. package/lib/triage-core/graph/batch-persistence.ts +66 -2
  112. package/lib/triage-core/graph/betweenness-worker.js +75 -0
  113. package/lib/triage-core/graph/graph-rank-sql.ts +67 -0
  114. package/lib/triage-core/graph/persistence.ts +1 -1
  115. package/lib/triage-core/graph/schema-v2.ts +2 -0
  116. package/lib/triage-core/graph/schema.cypher +11 -0
  117. package/lib/triage-core/graph/triage-query.ts +1 -1
  118. package/lib/triage-core/learning.ts +15 -20
  119. package/lib/triage-core/mental-model/bedrock-config.ts +78 -0
  120. package/lib/triage-core/mental-model/cos-integration.ts +1 -1
  121. package/lib/triage-core/mental-model/entity-extractor.ts +51 -4
  122. package/lib/triage-core/mental-model/identity-resolver.ts +5 -5
  123. package/lib/triage-core/mental-model/key-facts.ts +1 -2
  124. package/lib/triage-core/mental-model/model-assembler-sql.ts +200 -0
  125. package/lib/triage-core/mental-model/model-assembler.ts +16 -3
  126. package/lib/triage-core/orchestrator.ts +8 -15
  127. package/lib/triage-core/scheduled-sends.ts +39 -2
  128. package/lib/triage-core/signals/comms-style.ts +1 -1
  129. package/lib/triage-core/signals/cross-channel-escalation.ts +2 -2
  130. package/lib/triage-core/signals/favee-type.ts +6 -1
  131. package/lib/triage-core/signals/goal-relevance.ts +31 -2
  132. package/lib/triage-core/signals/personal-importance.ts +1 -1
  133. package/lib/triage-core/signals/referral-chain.ts +0 -1
  134. package/lib/triage-core/signals/relationship-decay.ts +4 -0
  135. package/lib/triage-core/signals/relationship-health.ts +6 -1
  136. package/lib/triage-core/signals/trajectory-signal.ts +38 -3
  137. package/lib/triage-core/tournament-runner.js +11 -1
  138. package/lib/triage-core/triage-llm-factory.ts +110 -0
  139. package/lib/triage-core/triage-local-llm.ts +145 -0
  140. package/lib/triage-core/triage-sql-store.ts +337 -0
  141. package/lib/triage-core/types.ts +2 -2
  142. package/lib/unified-graph.atomic.test.ts +52 -0
  143. package/lib/unified-graph.failure-categories.test.ts +55 -0
  144. package/package.json +18 -7
  145. package/prebuilds/darwin-arm64/better_sqlite3.node +0 -0
  146. package/prebuilds/linux-x64/better_sqlite3.node +0 -0
  147. package/prebuilds/win32-x64/better_sqlite3.node +0 -0
  148. package/skills/helios-bookkeeping/SKILL.md +321 -0
  149. package/skills/helios-briefer/SKILL.md +44 -0
  150. package/skills/helios-client-relations/SKILL.md +322 -0
  151. package/skills/helios-personal-triager/SKILL.md +45 -0
  152. package/skills/helios-recruitment/SKILL.md +317 -0
  153. package/skills/helios-relationship-nudger/SKILL.md +77 -0
  154. package/skills/helios-researcher/SKILL.md +44 -0
  155. package/skills/helios-scheduler/SKILL.md +58 -0
  156. package/skills/helios-tax-analyst/SKILL.md +280 -0
  157. package/lib/triage-core/orchestrator.ts.bak-r005-r006-r008 +0 -1823
@@ -0,0 +1,834 @@
1
+ /**
2
+ * hbo-core-store.compiled.js
3
+ * Pre-compiled CJS version of hbo-core-store.ts — no TypeScript runtime required.
4
+ *
5
+ * Generated from hbo-core-store.ts by stripping type annotations.
6
+ * This file ships in the npm tarball so the store loads on Node 16+ without
7
+ * jiti, ts-node, or --experimental-sqlite (uses better-sqlite3 as fallback).
8
+ * node:sqlite is used when available (Node >= 22.13 without flags, or
9
+ * Node >= 22.5 with --experimental-sqlite).
10
+ *
11
+ * DO NOT EDIT this file directly. Edit hbo-core-store.ts and regenerate.
12
+ * Regenerate with: node -e "require('./lib/hbo-core-store.js')" (uses native strip path)
13
+ */
14
+ 'use strict';
15
+
16
+ const path = require('path');
17
+ const os = require('os');
18
+ const { mkdirSync } = require('fs');
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Engine detection — node:sqlite (Node 22+) preferred, better-sqlite3 fallback
22
+ // ---------------------------------------------------------------------------
23
+
24
+ let _engine = null;
25
+
26
+ function _openDatabase(dbPath) {
27
+ // Attempt 1: node:sqlite built-in (Node >= 22.13 no flags needed; Node >= 22.5 with --experimental-sqlite)
28
+ try {
29
+ const { DatabaseSync } = require('node:sqlite');
30
+ const db = new DatabaseSync(dbPath);
31
+ _engine = 'node-sqlite';
32
+ return db;
33
+ } catch (_nodeSqliteErr) {
34
+ // node:sqlite unavailable (Node < 22.13 without flag, or Node < 22.5)
35
+ }
36
+
37
+ // Attempt 2: better-sqlite3 — synchronous, compiled native module, Node >= 16
38
+ try {
39
+ const BetterSqlite3 = require('better-sqlite3');
40
+ const db = new BetterSqlite3(dbPath);
41
+ _engine = 'better-sqlite3';
42
+ return db;
43
+ } catch (_betterSqliteErr) {
44
+ // better-sqlite3 unavailable (native build failed or not installed)
45
+ }
46
+
47
+ throw new Error(
48
+ '[hbo-core-store] No SQLite engine available. ' +
49
+ 'Requires Node >= 22.13 (no flags) or Node >= 22.5 with --experimental-sqlite, ' +
50
+ 'OR better-sqlite3 installed. Run: npm install better-sqlite3'
51
+ );
52
+ }
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // DB singleton
56
+ // ---------------------------------------------------------------------------
57
+
58
+ let _db = null;
59
+
60
+ function getDb() {
61
+ if (_db) return _db;
62
+
63
+ const dbPath = path.join(
64
+ process.env.HELIOS_ROOT || path.join(os.homedir(), 'helios-agent'),
65
+ 'data',
66
+ 'hbo-core.db',
67
+ );
68
+
69
+ mkdirSync(path.join(dbPath, '..'), { recursive: true });
70
+
71
+ _db = _openDatabase(dbPath);
72
+
73
+ // WAL mode for concurrent readers + single writer.
74
+ // node:sqlite uses .exec('PRAGMA ...'); better-sqlite3 uses .pragma('...')
75
+ if (_engine === 'better-sqlite3') {
76
+ _db.pragma('journal_mode = WAL');
77
+ _db.pragma('foreign_keys = ON');
78
+ } else {
79
+ _db.exec('PRAGMA journal_mode = WAL');
80
+ _db.exec('PRAGMA foreign_keys = ON');
81
+ }
82
+
83
+ initSchema(_db);
84
+ return _db;
85
+ }
86
+
87
+ // ---------------------------------------------------------------------------
88
+ // Schema
89
+ // ---------------------------------------------------------------------------
90
+
91
+ function initSchema(db) {
92
+ db.exec(`
93
+ CREATE TABLE IF NOT EXISTS tasks (
94
+ id TEXT PRIMARY KEY,
95
+ company_id TEXT NOT NULL,
96
+ title TEXT,
97
+ status TEXT,
98
+ assignee_agent_id TEXT,
99
+ goal_id TEXT,
100
+ origin_kind TEXT,
101
+ helios_issue_id TEXT,
102
+ execution_locked_at INTEGER,
103
+ created_at INTEGER,
104
+ updated_at INTEGER,
105
+ data_json TEXT
106
+ );
107
+ CREATE INDEX IF NOT EXISTS idx_tasks_company_status ON tasks(company_id, status);
108
+ CREATE INDEX IF NOT EXISTS idx_tasks_assignee ON tasks(assignee_agent_id, company_id);
109
+
110
+ CREATE TABLE IF NOT EXISTS approvals (
111
+ id TEXT PRIMARY KEY,
112
+ company_id TEXT NOT NULL,
113
+ type TEXT,
114
+ status TEXT,
115
+ strategy_id TEXT,
116
+ requested_by TEXT,
117
+ created_at INTEGER,
118
+ updated_at INTEGER,
119
+ data_json TEXT
120
+ );
121
+ CREATE INDEX IF NOT EXISTS idx_approvals_company_status ON approvals(company_id, status);
122
+
123
+ CREATE TABLE IF NOT EXISTS budget_policies (
124
+ id TEXT PRIMARY KEY,
125
+ company_id TEXT NOT NULL,
126
+ agent_id TEXT,
127
+ spent_cents INTEGER DEFAULT 0,
128
+ percent_used REAL DEFAULT 0,
129
+ updated_at INTEGER,
130
+ data_json TEXT
131
+ );
132
+ CREATE INDEX IF NOT EXISTS idx_budget_company_agent ON budget_policies(company_id, agent_id);
133
+
134
+ CREATE TABLE IF NOT EXISTS cost_events (
135
+ id TEXT PRIMARY KEY,
136
+ company_id TEXT NOT NULL,
137
+ agent_id TEXT,
138
+ feature TEXT,
139
+ model TEXT,
140
+ amount_usd REAL,
141
+ created_at INTEGER,
142
+ data_json TEXT
143
+ );
144
+ CREATE INDEX IF NOT EXISTS idx_cost_company ON cost_events(company_id, created_at);
145
+
146
+ CREATE TABLE IF NOT EXISTS business_agents (
147
+ id TEXT PRIMARY KEY,
148
+ company_id TEXT NOT NULL,
149
+ role TEXT,
150
+ status TEXT,
151
+ current_task_id TEXT,
152
+ skill TEXT,
153
+ adapter TEXT,
154
+ updated_at INTEGER,
155
+ data_json TEXT
156
+ );
157
+ CREATE INDEX IF NOT EXISTS idx_agents_company_status ON business_agents(company_id, status);
158
+
159
+ CREATE TABLE IF NOT EXISTS okrs (
160
+ id TEXT PRIMARY KEY,
161
+ company_id TEXT NOT NULL,
162
+ type TEXT,
163
+ status TEXT,
164
+ title TEXT,
165
+ updated_at INTEGER,
166
+ data_json TEXT
167
+ );
168
+ CREATE INDEX IF NOT EXISTS idx_okrs_company_type ON okrs(company_id, type, status);
169
+
170
+ CREATE TABLE IF NOT EXISTS goals (
171
+ id TEXT PRIMARY KEY,
172
+ company_id TEXT NOT NULL,
173
+ title TEXT,
174
+ description TEXT,
175
+ level TEXT,
176
+ status TEXT,
177
+ parent_id TEXT,
178
+ created_at INTEGER,
179
+ updated_at INTEGER,
180
+ data_json TEXT
181
+ );
182
+ CREATE INDEX IF NOT EXISTS idx_goals_company_status ON goals(company_id, status);
183
+ CREATE INDEX IF NOT EXISTS idx_goals_parent ON goals(company_id, parent_id);
184
+
185
+ CREATE TABLE IF NOT EXISTS leads (
186
+ id TEXT PRIMARY KEY,
187
+ company_id TEXT NOT NULL,
188
+ status TEXT,
189
+ email TEXT,
190
+ name TEXT,
191
+ updated_at INTEGER,
192
+ data_json TEXT
193
+ );
194
+ CREATE INDEX IF NOT EXISTS idx_leads_company_status ON leads(company_id, status);
195
+
196
+ CREATE TABLE IF NOT EXISTS crm_contacts (
197
+ id TEXT PRIMARY KEY,
198
+ company_id TEXT NOT NULL,
199
+ email TEXT,
200
+ name TEXT,
201
+ updated_at INTEGER,
202
+ data_json TEXT
203
+ );
204
+ CREATE INDEX IF NOT EXISTS idx_crm_company ON crm_contacts(company_id);
205
+
206
+ CREATE TABLE IF NOT EXISTS accounts (
207
+ id TEXT PRIMARY KEY,
208
+ company_id TEXT NOT NULL,
209
+ name TEXT,
210
+ updated_at INTEGER,
211
+ data_json TEXT
212
+ );
213
+
214
+ CREATE TABLE IF NOT EXISTS opportunities (
215
+ id TEXT PRIMARY KEY,
216
+ company_id TEXT NOT NULL,
217
+ title TEXT,
218
+ status TEXT,
219
+ updated_at INTEGER,
220
+ data_json TEXT
221
+ );
222
+
223
+ CREATE TABLE IF NOT EXISTS helios_projects (
224
+ id TEXT PRIMARY KEY,
225
+ company_id TEXT NOT NULL,
226
+ name TEXT,
227
+ pillar_id TEXT,
228
+ goal_id TEXT,
229
+ status TEXT,
230
+ phase TEXT,
231
+ created_at INTEGER,
232
+ updated_at INTEGER,
233
+ data_json TEXT
234
+ );
235
+ CREATE INDEX IF NOT EXISTS idx_helios_projects_company ON helios_projects(company_id, status);
236
+
237
+ CREATE TABLE IF NOT EXISTS project_documents (
238
+ id TEXT PRIMARY KEY,
239
+ project_id TEXT NOT NULL,
240
+ company_id TEXT,
241
+ purpose TEXT,
242
+ approach TEXT,
243
+ intent_anchor TEXT,
244
+ success_criteria TEXT,
245
+ exclusions TEXT,
246
+ content TEXT,
247
+ version INTEGER DEFAULT 1,
248
+ updated_at INTEGER,
249
+ data_json TEXT
250
+ );
251
+ CREATE INDEX IF NOT EXISTS idx_project_docs_project ON project_documents(project_id);
252
+ `);
253
+
254
+ // H-7 migration: add created_at column to existing goals tables that predate this commit.
255
+ try { db.exec(`ALTER TABLE goals ADD COLUMN created_at INTEGER`); } catch (_) { /* column already exists */ }
256
+ }
257
+
258
+ // ---------------------------------------------------------------------------
259
+ // Helpers
260
+ // ---------------------------------------------------------------------------
261
+
262
+ function _parseJson(value, fallback) {
263
+ if (!value) return fallback;
264
+ try { return JSON.parse(value); } catch { return fallback; }
265
+ }
266
+
267
+ const _SQLITE_COLUMN_KEYS = new Set([
268
+ 'id', 'company_id', 'title', 'description', 'level', 'status', 'parent_id',
269
+ 'created_at', 'updated_at', 'data_json',
270
+ // tasks
271
+ 'assignee_agent_id', 'goal_id', 'origin_kind', 'helios_issue_id', 'execution_locked_at',
272
+ // approvals
273
+ 'type', 'strategy_id', 'requested_by',
274
+ // cost_events
275
+ 'agent_id', 'feature', 'model', 'amount_usd',
276
+ // business_agents
277
+ 'role', 'last_heartbeat_at', 'pause_reason',
278
+ // okrs / leads / crm_contacts / accounts / opportunities
279
+ 'email', 'name', 'amount',
280
+ // helios_projects
281
+ 'pillar_id', 'phase',
282
+ // project_documents
283
+ 'project_id', 'section', 'value', 'updated_by',
284
+ 'intent_anchor', 'success_criteria', 'exclusions', 'content', 'purpose', 'approach', 'version',
285
+ ]);
286
+
287
+ function _stripSqliteCols(obj) {
288
+ const result = {};
289
+ for (const key of Object.keys(obj)) {
290
+ if (!_SQLITE_COLUMN_KEYS.has(key)) result[key] = obj[key];
291
+ }
292
+ return result;
293
+ }
294
+
295
+ function _mergeRow(row) {
296
+ const base = _parseJson(row.data_json, {});
297
+ const merged = { ...base };
298
+ for (const key of Object.keys(row)) {
299
+ if (key !== 'data_json') merged[key] = row[key];
300
+ }
301
+ return merged;
302
+ }
303
+
304
+ function _statusParams(statuses) {
305
+ const placeholders = statuses.map(() => '?').join(', ');
306
+ return { placeholders, params: statuses };
307
+ }
308
+
309
+ // ---------------------------------------------------------------------------
310
+ // Tasks
311
+ // ---------------------------------------------------------------------------
312
+
313
+ function createTask(task) {
314
+ const companyId = task.company_id || task.companyId;
315
+ if (!companyId) throw new Error(`[hbo-core-store] createTask: company_id is required (task id=${task.id})`);
316
+ const db = getDb();
317
+ const now = Date.now();
318
+ db.prepare(`
319
+ INSERT OR REPLACE INTO tasks
320
+ (id, company_id, title, status, assignee_agent_id, goal_id,
321
+ origin_kind, helios_issue_id, execution_locked_at, created_at, updated_at, data_json)
322
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
323
+ `).run(
324
+ task.id,
325
+ companyId,
326
+ task.title || null,
327
+ task.status || null,
328
+ task.assignee_agent_id || task.assigneeAgentId || null,
329
+ task.goal_id || task.goalId || null,
330
+ task.origin_kind || task.originKind || null,
331
+ task.helios_issue_id || task.heliosIssueId || null,
332
+ task.execution_locked_at != null ? task.execution_locked_at : (task.executionLockedAt != null ? task.executionLockedAt : null),
333
+ task.created_at || task.createdAt || now,
334
+ task.updated_at || task.updatedAt || now,
335
+ JSON.stringify(task),
336
+ );
337
+ }
338
+
339
+ function getTask(id, companyId) {
340
+ const db = getDb();
341
+ const row = db.prepare('SELECT * FROM tasks WHERE id = ? AND company_id = ?').get(id, companyId);
342
+ return row ? _mergeRow(row) : null;
343
+ }
344
+
345
+ function updateTask(id, companyId, update) {
346
+ const existing = getTask(id, companyId) || { id, company_id: companyId };
347
+ const merged = { ...existing, ...update, id, company_id: companyId, updated_at: Date.now() };
348
+ createTask(merged);
349
+ }
350
+
351
+ function getTasksByCompanyStatus(companyId, status) {
352
+ const db = getDb();
353
+ const statuses = Array.isArray(status) ? status : [status];
354
+ const { placeholders, params } = _statusParams(statuses);
355
+ return db.prepare(
356
+ `SELECT * FROM tasks WHERE company_id = ? AND status IN (${placeholders})`
357
+ ).all(companyId, ...params).map(_mergeRow);
358
+ }
359
+
360
+ function deleteTask(id, companyId) {
361
+ const db = getDb();
362
+ db.prepare('DELETE FROM tasks WHERE id = ? AND company_id = ?').run(id, companyId);
363
+ }
364
+
365
+ // ---------------------------------------------------------------------------
366
+ // Approvals
367
+ // ---------------------------------------------------------------------------
368
+
369
+ function createApproval(approval) {
370
+ const companyId = approval.company_id || approval.companyId;
371
+ if (!companyId) throw new Error(`[hbo-core-store] createApproval: company_id is required (approval id=${approval.id})`);
372
+ const db = getDb();
373
+ const now = Date.now();
374
+ db.prepare(`
375
+ INSERT OR REPLACE INTO approvals
376
+ (id, company_id, type, status, strategy_id, requested_by, created_at, updated_at, data_json)
377
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
378
+ `).run(
379
+ approval.id,
380
+ companyId,
381
+ approval.type || null,
382
+ approval.status || null,
383
+ approval.strategy_id || approval.strategyId || null,
384
+ approval.requested_by || approval.requestedBy || null,
385
+ approval.created_at || approval.createdAt || now,
386
+ approval.updated_at || approval.updatedAt || now,
387
+ JSON.stringify(approval),
388
+ );
389
+ }
390
+
391
+ function getApproval(id, companyId) {
392
+ const db = getDb();
393
+ const row = db.prepare('SELECT * FROM approvals WHERE id = ? AND company_id = ?').get(id, companyId);
394
+ return row ? _mergeRow(row) : null;
395
+ }
396
+
397
+ function updateApproval(id, companyId, update) {
398
+ const existing = getApproval(id, companyId) || { id, company_id: companyId };
399
+ const merged = { ...existing, ...update, id, company_id: companyId, updated_at: Date.now() };
400
+ createApproval(merged);
401
+ }
402
+
403
+ function getApprovalsByCompanyStatus(companyId, status) {
404
+ const db = getDb();
405
+ const statuses = Array.isArray(status) ? status : [status];
406
+ const { placeholders, params } = _statusParams(statuses);
407
+ return db.prepare(
408
+ `SELECT * FROM approvals WHERE company_id = ? AND status IN (${placeholders})`
409
+ ).all(companyId, ...params).map(_mergeRow);
410
+ }
411
+
412
+ // ---------------------------------------------------------------------------
413
+ // Budget Policies
414
+ // ---------------------------------------------------------------------------
415
+
416
+ function upsertBudgetPolicy(policy) {
417
+ const companyId = policy.company_id || policy.companyId;
418
+ if (!companyId) throw new Error(`[hbo-core-store] upsertBudgetPolicy: company_id is required`);
419
+ const db = getDb();
420
+ db.prepare(`
421
+ INSERT OR REPLACE INTO budget_policies
422
+ (id, company_id, agent_id, spent_cents, percent_used, updated_at, data_json)
423
+ VALUES (?, ?, ?, ?, ?, ?, ?)
424
+ `).run(
425
+ policy.id,
426
+ companyId,
427
+ policy.agent_id || policy.agentId || null,
428
+ policy.spent_cents != null ? policy.spent_cents : (policy.spentCents != null ? policy.spentCents : 0),
429
+ policy.percent_used != null ? policy.percent_used : (policy.percentUsed != null ? policy.percentUsed : 0),
430
+ policy.updated_at || policy.updatedAt || Date.now(),
431
+ JSON.stringify(policy),
432
+ );
433
+ }
434
+
435
+ function getBudgetPolicy(id, companyId) {
436
+ const db = getDb();
437
+ const row = db.prepare('SELECT * FROM budget_policies WHERE id = ? AND company_id = ?').get(id, companyId);
438
+ return row ? _mergeRow(row) : null;
439
+ }
440
+
441
+ function getBudgetPoliciesByCompany(companyId) {
442
+ const db = getDb();
443
+ return db.prepare('SELECT * FROM budget_policies WHERE company_id = ?').all(companyId).map(_mergeRow);
444
+ }
445
+
446
+ // ---------------------------------------------------------------------------
447
+ // Cost Events
448
+ // ---------------------------------------------------------------------------
449
+
450
+ function createCostEvent(event) {
451
+ const companyId = event.company_id || event.companyId;
452
+ if (!companyId) throw new Error(`[hbo-core-store] createCostEvent: company_id is required`);
453
+ const db = getDb();
454
+ const now = Date.now();
455
+ db.prepare(`
456
+ INSERT OR REPLACE INTO cost_events
457
+ (id, company_id, agent_id, feature, model, amount_usd, created_at, data_json)
458
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
459
+ `).run(
460
+ event.id,
461
+ companyId,
462
+ event.agent_id || event.agentId || null,
463
+ event.feature || null,
464
+ event.model || null,
465
+ event.amount_usd != null ? event.amount_usd : (event.amountUsd != null ? event.amountUsd : null),
466
+ event.created_at || event.createdAt || now,
467
+ JSON.stringify(event),
468
+ );
469
+ }
470
+
471
+ function getCostEventsByCompanyRange(companyId, startMs, endMs) {
472
+ const db = getDb();
473
+ return db.prepare(
474
+ 'SELECT * FROM cost_events WHERE company_id = ? AND created_at >= ? AND created_at <= ? ORDER BY created_at ASC'
475
+ ).all(companyId, startMs, endMs).map(_mergeRow);
476
+ }
477
+
478
+ // ---------------------------------------------------------------------------
479
+ // Business Agents
480
+ // ---------------------------------------------------------------------------
481
+
482
+ function upsertBusinessAgent(agent) {
483
+ const companyId = agent.company_id || agent.companyId;
484
+ if (!companyId) throw new Error(`[hbo-core-store] upsertBusinessAgent: company_id is required`);
485
+ const db = getDb();
486
+ db.prepare(`
487
+ INSERT OR REPLACE INTO business_agents
488
+ (id, company_id, role, status, current_task_id, skill, adapter, updated_at, data_json)
489
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
490
+ `).run(
491
+ agent.id,
492
+ companyId,
493
+ agent.role || null,
494
+ agent.status || null,
495
+ agent.current_task_id || agent.currentTaskId || null,
496
+ agent.skill || null,
497
+ agent.adapter || null,
498
+ agent.updated_at || agent.updatedAt || Date.now(),
499
+ JSON.stringify(agent),
500
+ );
501
+ }
502
+
503
+ function getBusinessAgent(id, companyId) {
504
+ const db = getDb();
505
+ const row = db.prepare('SELECT * FROM business_agents WHERE id = ? AND company_id = ?').get(id, companyId);
506
+ return row ? _mergeRow(row) : null;
507
+ }
508
+
509
+ function getBusinessAgentsByCompany(companyId) {
510
+ const db = getDb();
511
+ return db.prepare('SELECT * FROM business_agents WHERE company_id = ?').all(companyId).map(_mergeRow);
512
+ }
513
+
514
+ // ---------------------------------------------------------------------------
515
+ // OKRs
516
+ // ---------------------------------------------------------------------------
517
+
518
+ function upsertOKR(okr) {
519
+ const companyId = okr.company_id || okr.companyId;
520
+ if (!companyId) throw new Error(`[hbo-core-store] upsertOKR: company_id is required`);
521
+ const db = getDb();
522
+ db.prepare(`
523
+ INSERT OR REPLACE INTO okrs
524
+ (id, company_id, type, status, title, updated_at, data_json)
525
+ VALUES (?, ?, ?, ?, ?, ?, ?)
526
+ `).run(
527
+ okr.id,
528
+ companyId,
529
+ okr.type || null,
530
+ okr.status || null,
531
+ okr.title || null,
532
+ okr.updated_at || okr.updatedAt || Date.now(),
533
+ JSON.stringify(okr),
534
+ );
535
+ }
536
+
537
+ function getOKRsByCompanyType(companyId, type, status) {
538
+ const db = getDb();
539
+ if (type !== undefined && status !== undefined) {
540
+ return db.prepare('SELECT * FROM okrs WHERE company_id = ? AND type = ? AND status = ?').all(companyId, type, status).map(_mergeRow);
541
+ }
542
+ if (type !== undefined) {
543
+ return db.prepare('SELECT * FROM okrs WHERE company_id = ? AND type = ?').all(companyId, type).map(_mergeRow);
544
+ }
545
+ if (status !== undefined) {
546
+ return db.prepare('SELECT * FROM okrs WHERE company_id = ? AND status = ?').all(companyId, status).map(_mergeRow);
547
+ }
548
+ return db.prepare('SELECT * FROM okrs WHERE company_id = ?').all(companyId).map(_mergeRow);
549
+ }
550
+
551
+ // ---------------------------------------------------------------------------
552
+ // Goals
553
+ // ---------------------------------------------------------------------------
554
+
555
+ function createGoal(goal) {
556
+ const companyId = goal.company_id || goal.companyId;
557
+ if (!companyId) throw new Error(`[hbo-core-store] createGoal: company_id is required (goal id=${goal.id})`);
558
+ const db = getDb();
559
+ const now = Date.now();
560
+ db.prepare(`
561
+ INSERT OR REPLACE INTO goals
562
+ (id, company_id, title, description, level, status, parent_id, created_at, updated_at, data_json)
563
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
564
+ `).run(
565
+ goal.id,
566
+ companyId,
567
+ goal.title || null,
568
+ goal.description || goal.desc || null,
569
+ goal.level || null,
570
+ goal.status || null,
571
+ goal.parent_id || goal.parentId || goal.parentGoalId || null,
572
+ goal.created_at || goal.createdAt || now,
573
+ goal.updated_at || goal.updatedAt || now,
574
+ JSON.stringify(goal),
575
+ );
576
+ }
577
+
578
+ function getGoal(id, companyId) {
579
+ const db = getDb();
580
+ const row = db.prepare('SELECT * FROM goals WHERE id = ? AND company_id = ?').get(id, companyId);
581
+ return row ? _mergeRow(row) : null;
582
+ }
583
+
584
+ function updateGoal(id, companyId, update) {
585
+ const existing = getGoal(id, companyId) || { id, company_id: companyId };
586
+ const cleanExisting = _stripSqliteCols(existing);
587
+ createGoal({ ...cleanExisting, ...update, id, company_id: companyId, updated_at: Date.now() });
588
+ }
589
+
590
+ function deleteGoal(id, companyId) {
591
+ const db = getDb();
592
+ db.prepare('DELETE FROM goals WHERE id = ? AND company_id = ?').run(id, companyId);
593
+ }
594
+
595
+ function getGoalsByCompany(companyId) {
596
+ const db = getDb();
597
+ return db.prepare('SELECT * FROM goals WHERE company_id = ?').all(companyId).map(_mergeRow);
598
+ }
599
+
600
+ // ---------------------------------------------------------------------------
601
+ // HeliosProjects
602
+ // ---------------------------------------------------------------------------
603
+
604
+ function upsertProject(project) {
605
+ const companyId = project.company_id || project.companyId;
606
+ if (!companyId) throw new Error(`[hbo-core-store] upsertProject: company_id is required`);
607
+ const db = getDb();
608
+ const now = Date.now();
609
+ db.prepare(`
610
+ INSERT OR REPLACE INTO helios_projects
611
+ (id, company_id, name, pillar_id, goal_id, status, phase, created_at, updated_at, data_json)
612
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
613
+ `).run(
614
+ project.id,
615
+ companyId,
616
+ project.name || null,
617
+ project.pillar_id || project.pillarId || null,
618
+ project.goal_id || project.goalId || null,
619
+ project.status || 'planning',
620
+ project.phase || 'planning',
621
+ project.created_at || project.createdAt || now,
622
+ project.updated_at || project.updatedAt || now,
623
+ JSON.stringify(project),
624
+ );
625
+ }
626
+
627
+ function getProject(id, companyId) {
628
+ const db = getDb();
629
+ const row = db.prepare('SELECT * FROM helios_projects WHERE id = ? AND company_id = ?').get(id, companyId);
630
+ return row ? _mergeRow(row) : null;
631
+ }
632
+
633
+ function getProjectsByCompany(companyId) {
634
+ const db = getDb();
635
+ return db.prepare('SELECT * FROM helios_projects WHERE company_id = ? ORDER BY created_at DESC LIMIT 100').all(companyId).map(_mergeRow);
636
+ }
637
+
638
+ function updateProject(id, companyId, update) {
639
+ const existing = getProject(id, companyId) || { id, company_id: companyId };
640
+ const cleanExisting = _stripSqliteCols(existing);
641
+ upsertProject({ ...cleanExisting, ...update, id, company_id: companyId, updated_at: Date.now() });
642
+ }
643
+
644
+ // ---------------------------------------------------------------------------
645
+ // ProjectDocuments
646
+ // ---------------------------------------------------------------------------
647
+
648
+ function upsertProjectDocument(doc) {
649
+ const projectId = doc.project_id || doc.projectId;
650
+ if (!projectId) throw new Error(`[hbo-core-store] upsertProjectDocument: project_id is required`);
651
+ const db = getDb();
652
+ const id = doc.id || `pdoc:${projectId}:main`;
653
+ const now = Date.now();
654
+ db.prepare(`
655
+ INSERT OR REPLACE INTO project_documents
656
+ (id, project_id, company_id, purpose, approach, intent_anchor,
657
+ success_criteria, exclusions, content, version, updated_at, data_json)
658
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
659
+ `).run(
660
+ id,
661
+ projectId,
662
+ doc.company_id || doc.companyId || null,
663
+ doc.purpose || null,
664
+ doc.approach || null,
665
+ doc.intent_anchor || doc.intentAnchor || null,
666
+ doc.success_criteria || doc.successCriteria || '[]',
667
+ doc.exclusions || '[]',
668
+ doc.content || null,
669
+ doc.version != null ? doc.version : 1,
670
+ doc.updated_at || doc.updatedAt || now,
671
+ JSON.stringify(doc),
672
+ );
673
+ }
674
+
675
+ function getProjectDocument(projectId) {
676
+ const db = getDb();
677
+ const row = db.prepare('SELECT * FROM project_documents WHERE project_id = ? ORDER BY updated_at DESC LIMIT 1').get(projectId);
678
+ return row ? _mergeRow(row) : null;
679
+ }
680
+
681
+ // ---------------------------------------------------------------------------
682
+ // Leads
683
+ // ---------------------------------------------------------------------------
684
+
685
+ function upsertLead(lead) {
686
+ const companyId = lead.company_id || lead.companyId;
687
+ if (!companyId) throw new Error(`[hbo-core-store] upsertLead: company_id is required`);
688
+ const db = getDb();
689
+ db.prepare(`
690
+ INSERT OR REPLACE INTO leads
691
+ (id, company_id, status, email, name, updated_at, data_json)
692
+ VALUES (?, ?, ?, ?, ?, ?, ?)
693
+ `).run(
694
+ lead.id,
695
+ companyId,
696
+ lead.status || null,
697
+ lead.email || null,
698
+ lead.name || null,
699
+ lead.updated_at || lead.updatedAt || Date.now(),
700
+ JSON.stringify(lead),
701
+ );
702
+ }
703
+
704
+ function getLeadsByCompanyStatus(companyId, statuses) {
705
+ const db = getDb();
706
+ const { placeholders, params } = _statusParams(statuses);
707
+ return db.prepare(
708
+ `SELECT * FROM leads WHERE company_id = ? AND status IN (${placeholders})`
709
+ ).all(companyId, ...params).map(_mergeRow);
710
+ }
711
+
712
+ // ---------------------------------------------------------------------------
713
+ // CRM Contacts
714
+ // ---------------------------------------------------------------------------
715
+
716
+ function upsertCRMContact(contact) {
717
+ const companyId = contact.company_id || contact.companyId;
718
+ if (!companyId) throw new Error(`[hbo-core-store] upsertCRMContact: company_id is required`);
719
+ const db = getDb();
720
+ db.prepare(`
721
+ INSERT OR REPLACE INTO crm_contacts
722
+ (id, company_id, email, name, updated_at, data_json)
723
+ VALUES (?, ?, ?, ?, ?, ?)
724
+ `).run(
725
+ contact.id,
726
+ companyId,
727
+ contact.email || null,
728
+ contact.name || null,
729
+ contact.updated_at || contact.updatedAt || Date.now(),
730
+ JSON.stringify(contact),
731
+ );
732
+ }
733
+
734
+ function getCRMContactsByCompany(companyId) {
735
+ const db = getDb();
736
+ return db.prepare('SELECT * FROM crm_contacts WHERE company_id = ?').all(companyId).map(_mergeRow);
737
+ }
738
+
739
+ // ---------------------------------------------------------------------------
740
+ // Accounts
741
+ // ---------------------------------------------------------------------------
742
+
743
+ function upsertAccount(account) {
744
+ const companyId = account.company_id || account.companyId;
745
+ if (!companyId) throw new Error(`[hbo-core-store] upsertAccount: company_id is required`);
746
+ const db = getDb();
747
+ db.prepare(`
748
+ INSERT OR REPLACE INTO accounts
749
+ (id, company_id, name, updated_at, data_json)
750
+ VALUES (?, ?, ?, ?, ?)
751
+ `).run(
752
+ account.id,
753
+ companyId,
754
+ account.name || null,
755
+ account.updated_at || account.updatedAt || Date.now(),
756
+ JSON.stringify(account),
757
+ );
758
+ }
759
+
760
+ // ---------------------------------------------------------------------------
761
+ // Opportunities
762
+ // ---------------------------------------------------------------------------
763
+
764
+ function upsertOpportunity(opp) {
765
+ const companyId = opp.company_id || opp.companyId;
766
+ if (!companyId) throw new Error(`[hbo-core-store] upsertOpportunity: company_id is required`);
767
+ const db = getDb();
768
+ db.prepare(`
769
+ INSERT OR REPLACE INTO opportunities
770
+ (id, company_id, title, status, updated_at, data_json)
771
+ VALUES (?, ?, ?, ?, ?, ?)
772
+ `).run(
773
+ opp.id,
774
+ companyId,
775
+ opp.title || null,
776
+ opp.status || null,
777
+ opp.updated_at || opp.updatedAt || Date.now(),
778
+ JSON.stringify(opp),
779
+ );
780
+ }
781
+
782
+ // ---------------------------------------------------------------------------
783
+ // Test / lifecycle helpers
784
+ // ---------------------------------------------------------------------------
785
+
786
+ function resetHboCoreStore() {
787
+ try { if (_db) _db.close(); } catch (_) { /* ignore */ }
788
+ _db = null;
789
+ _engine = null;
790
+ }
791
+
792
+ // ---------------------------------------------------------------------------
793
+ // Exports
794
+ // ---------------------------------------------------------------------------
795
+
796
+ module.exports = {
797
+ createTask,
798
+ getTask,
799
+ updateTask,
800
+ getTasksByCompanyStatus,
801
+ deleteTask,
802
+ createApproval,
803
+ getApproval,
804
+ updateApproval,
805
+ getApprovalsByCompanyStatus,
806
+ upsertBudgetPolicy,
807
+ getBudgetPolicy,
808
+ getBudgetPoliciesByCompany,
809
+ createCostEvent,
810
+ getCostEventsByCompanyRange,
811
+ upsertBusinessAgent,
812
+ getBusinessAgent,
813
+ getBusinessAgentsByCompany,
814
+ upsertOKR,
815
+ getOKRsByCompanyType,
816
+ createGoal,
817
+ getGoal,
818
+ updateGoal,
819
+ deleteGoal,
820
+ getGoalsByCompany,
821
+ upsertProject,
822
+ getProject,
823
+ getProjectsByCompany,
824
+ updateProject,
825
+ upsertProjectDocument,
826
+ getProjectDocument,
827
+ upsertLead,
828
+ getLeadsByCompanyStatus,
829
+ upsertCRMContact,
830
+ getCRMContactsByCompany,
831
+ upsertAccount,
832
+ upsertOpportunity,
833
+ resetHboCoreStore,
834
+ };