@cgh567/agent 2.4.1 → 2.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/helios +0 -0
- package/bin/helios-rpc-node-wrapper.cjs +0 -0
- package/bin/helios-rpc-wrapper.sh +0 -0
- package/daemon/adapters/helios-rpc-adapter.js +47 -25
- package/daemon/adapters/tui_wakeup.js +8 -0
- package/daemon/config/com.familiar.helios-daemon.plist +5 -0
- package/daemon/config/helios-daemon.service +4 -0
- package/daemon/context-enrichment.js +59 -21
- package/daemon/daemon-manager.js +1 -1
- package/daemon/db/email-infrastructure-migrate.js +192 -0
- package/daemon/db/hbo-core-migrate.js +189 -0
- package/daemon/helios-api.js +723 -57
- package/daemon/helios-company-daemon.js +616 -134
- package/daemon/lib/harada/cascade-judge.js +12 -50
- package/daemon/lib/harada/mandala.js +20 -0
- package/daemon/lib/harada/pillar-dispatcher.js +1 -1
- package/daemon/lib/harada/project-factory.js +7 -2
- package/daemon/lib/hbo-bridge.js +32 -13
- package/daemon/lib/hed-engine.js +10 -292
- package/daemon/lib/helios-hitl-host.js +15 -2
- package/daemon/lib/hitl-interaction-service.js +0 -0
- package/daemon/lib/memgraph-verify.js +38 -33
- package/daemon/lib/project-drift-detector.js +7 -17
- package/daemon/lib/project-semantic-updater.js +1 -14
- package/daemon/lib/task-completion-processor.js +11 -0
- package/daemon/lib/wizard-engine.js +57 -6
- package/daemon/routes/channels.js +10 -5
- package/daemon/routes/harada-map.js +11 -48
- package/daemon/routes/hbo.js +342 -75
- package/daemon/routes/hitl.js +0 -0
- package/daemon/routes/project.js +194 -62
- package/daemon/routes/routines.js +14 -0
- package/daemon/routes/tasks.js +15 -1
- package/daemon/routes/wizard.js +11 -4
- package/daemon/schema-apply.js +174 -0
- package/daemon/schema-definitions.js +423 -0
- package/daemon/schema-migrations-hbo.js +10 -0
- package/daemon/schema-migrations-hed.js +18 -0
- package/daemon/schema-migrations-hitl.js +0 -0
- package/daemon/schema-migrations-proj.js +131 -0
- package/extensions/001-tool-output-cap.ts +0 -0
- package/extensions/context-compaction.ts +45 -26
- package/extensions/cortex/activation-bridge.ts +5 -0
- package/extensions/cortex/learn.ts +26 -0
- package/extensions/cortex/wal-replay.ts +91 -0
- package/extensions/email/backfill.ts +0 -0
- package/extensions/helios-governance/analysis/ambiguity.ts +0 -0
- package/extensions/helios-governance/analysis/compliance.ts +0 -0
- package/extensions/helios-governance/analysis/long-task-detector.ts +0 -0
- package/extensions/helios-governance/analysis/output-contract.ts +0 -0
- package/extensions/helios-governance/analysis/patterns.ts +0 -0
- package/extensions/helios-governance/analysis/preflight.ts +0 -0
- package/extensions/helios-governance/analysis/recurring-violations.ts +0 -0
- package/extensions/helios-governance/analysis/task-classification.ts +0 -0
- package/extensions/helios-governance/analysis/task-intent.ts +0 -0
- package/extensions/helios-governance/gates/high-impact.ts +1 -1
- package/extensions/helios-governance/handlers/_jiti-require.ts +15 -8
- package/extensions/helios-governance/handlers/proxy-test-detector.ts +0 -0
- package/extensions/hema-dispatch-v3/graph-memory.ts +10 -0
- package/extensions/hema-dispatch-v3/index.ts +72 -47
- package/extensions/lib/elo-engine.js +0 -0
- package/extensions/lib/elo-engine.test.js +0 -0
- package/extensions/memgraph-autostart.ts +13 -0
- package/extensions/neuroplastic-eval.ts +0 -0
- package/extensions/shadow-loop/index.ts +0 -0
- package/extensions/warm-tick/warm-tick-maintenance.ts +8 -0
- package/lib/__tests__/hbo-core-store.test.js +238 -0
- package/lib/brain-v2-budget.js +0 -0
- package/lib/brain-v2-circuit-breaker.js +0 -0
- package/lib/brain-v2.js +0 -0
- package/lib/broker/adaptive-throttle.js +0 -0
- package/lib/broker/batch-coalescer.js +0 -0
- package/lib/broker/bulkhead.js +0 -0
- package/lib/broker/channel-registry.js +0 -0
- package/lib/broker/circuit-breaker.js +0 -0
- package/lib/broker/evidence-cache.js +0 -0
- package/lib/broker/health-monitor.js +0 -0
- package/lib/broker/mage-queue.js +0 -0
- package/lib/broker/priority-queue.js +0 -0
- package/lib/broker/server.js.bak-error2-fix +0 -0
- package/lib/broker/session-registry.js +0 -0
- package/lib/broker/singleton-timers.js +0 -0
- package/lib/broker/types.d.ts +0 -0
- package/lib/broker/vegas-limit.js +0 -0
- package/lib/compression/dist/ccr-store.js +74 -0
- package/lib/compression/dist/content-router.js +115 -0
- package/lib/compression/dist/pipeline.js +113 -0
- package/lib/compression/dist/server.js +265 -0
- package/lib/compression/dist/smart-crusher.js +251 -0
- package/lib/context-budget.ts +0 -0
- package/lib/context-firewall.js +0 -0
- package/lib/crm/integration/triage-bridge.js +0 -0
- package/lib/email-utils.ts +0 -0
- package/lib/eval/__tests__/preflight-checker.test.ts +0 -0
- package/lib/eval/__tests__/task-instruction-parser.test.ts +0 -0
- package/lib/eval/__tests__/verifier-runner.test.ts +0 -0
- package/lib/eval/index.ts +0 -0
- package/lib/eval/preflight-checker.ts +0 -0
- package/lib/eval/task-domain-classifier.ts +0 -0
- package/lib/eval/task-instruction-parser.ts +0 -0
- package/lib/eval/verifier-runner.ts +0 -0
- package/lib/event-bus.d.ts +0 -0
- package/lib/event-bus.mts +1 -1
- package/lib/governance-context-selector.ts +0 -0
- package/lib/graph/generate-extension-embeddings.js +0 -0
- package/lib/graph/generate-static-embeddings.js +0 -0
- package/lib/graph/lib/utils.js +1 -1
- package/lib/graph-audit.d.ts +0 -0
- package/lib/graph-availability.js +62 -0
- package/lib/hbo-core-store.compiled.js +834 -0
- package/lib/hbo-core-store.js +124 -0
- package/lib/hbo-core-store.ts +908 -0
- package/lib/mesh-circuit-breaker.js +0 -0
- package/lib/mission-loop/lesson-extractor.ts +0 -0
- package/lib/mission-loop/mental-model-scorer.ts +0 -0
- package/lib/mission-loop/occ-detector.ts +0 -0
- package/lib/mission-loop/query-variants.ts +0 -0
- package/lib/mission-loop/verifier-check.ts +0 -0
- package/lib/skill-reference-builder.ts +0 -0
- package/lib/telemetry/token-breakdown.ts +0 -0
- package/lib/tool-compressor.ts +0 -0
- package/lib/triage-core/classifier.ts +3 -2
- package/lib/triage-core/graph/schema.cypher +10 -0
- package/lib/triage-core/legal-routing.ts +0 -0
- package/lib/triage-core/mental-model/dunbar-classifier.ts +0 -0
- package/lib/triage-core/mental-model/enrich-all.ts +0 -0
- package/lib/triage-core/mental-model/identity-resolver.ts +0 -0
- package/lib/triage-core/mental-model/key-facts.ts +1 -2
- package/lib/triage-core/mental-model/model-assembler.ts +0 -0
- package/lib/triage-core/orchestrator.ts +4 -11
- package/lib/triage-core/orchestrator.ts.bak-r005-r006-r008 +0 -0
- package/package.json +18 -8
- package/skills/helios-business-operator/services/signals/upwork-signals.js +0 -0
- package/skills/talisman-ceo/SKILL.md +23 -25
- package/skills/talisman-comms/SKILL.md +5 -5
- package/skills/talisman-engineering/SKILL.md +5 -5
- package/skills/talisman-finance/SKILL.md +10 -8
- package/skills/talisman-marketing/SKILL.md +10 -10
- package/skills/talisman-sales/SKILL.md +12 -15
- package/skills/talisman-support/SKILL.md +5 -5
- package/agents/business/talisman-ceo.md +0 -183
- package/agents/business/talisman-comms.md +0 -257
- package/agents/business/talisman-cto.md +0 -153
- package/agents/business/talisman-finance.md +0 -246
- package/agents/business/talisman-marketing.md +0 -240
- package/agents/business/talisman-sales.md +0 -242
- package/agents/business/talisman-support.md +0 -236
- package/daemon/lib/approval-expiry.js +0 -162
- package/daemon/lib/blast-radius-analyzer.js +0 -75
- package/daemon/lib/domain-bootstrap-orchestrator.js +0 -267
- package/daemon/lib/forensic-log.js +0 -113
- package/daemon/lib/goal-research-pipeline.js +0 -644
- package/daemon/lib/harada/cascade-research-dispatcher.js +0 -261
- package/daemon/lib/headroom-middleware.js +0 -167
- package/daemon/lib/headroom-proxy-manager.js +0 -623
- package/daemon/lib/mental-model-cache.js +0 -96
- package/daemon/lib/project-factory.js +0 -47
- package/daemon/lib/session-log-reader.js +0 -93
- package/daemon/routes/hed.js +0 -133
- package/lib/graph/learning/headroom-learn-bridge.js +0 -215
- package/skills/helios-bookkeeping/SKILL.md +0 -321
- package/skills/helios-briefer/SKILL.md +0 -44
- package/skills/helios-client-relations/SKILL.md +0 -322
- package/skills/helios-personal-triager/SKILL.md +0 -45
- package/skills/helios-recruitment/SKILL.md +0 -317
- package/skills/helios-relationship-nudger/SKILL.md +0 -77
- package/skills/helios-researcher/SKILL.md +0 -44
- package/skills/helios-scheduler/SKILL.md +0 -58
- package/skills/helios-tax-analyst/SKILL.md +0 -280
package/bin/helios
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -33,11 +33,19 @@ const path = require('path');
|
|
|
33
33
|
const fs = require('fs');
|
|
34
34
|
const { randomUUID } = require('crypto');
|
|
35
35
|
|
|
36
|
-
const DEFAULT_TASK_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
|
|
37
|
-
const MAX_STDOUT_BUF = 10 * 1024 * 1024; // 10MB
|
|
38
|
-
|
|
39
36
|
// Resolve helios-agent root relative to this file: daemon/adapters/ → two levels up
|
|
40
37
|
const HELIOS_ROOT = path.resolve(__dirname, '..', '..');
|
|
38
|
+
|
|
39
|
+
// Lazy hboStore for SQLite-first CostEvent writes (P2-7)
|
|
40
|
+
let _hboStoreRpc = null;
|
|
41
|
+
function _getHboStoreRpc() {
|
|
42
|
+
if (_hboStoreRpc) return _hboStoreRpc;
|
|
43
|
+
try { _hboStoreRpc = require(path.join(HELIOS_ROOT, 'lib', 'hbo-core-store')); } catch (_) {}
|
|
44
|
+
return _hboStoreRpc;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const DEFAULT_TASK_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
|
|
48
|
+
const MAX_STDOUT_BUF = 10 * 1024 * 1024; // 10MB
|
|
41
49
|
const HELIOS_RPC_BIN = path.join(HELIOS_ROOT, 'bin', 'helios-rpc.js');
|
|
42
50
|
|
|
43
51
|
class HRpcAdapter {
|
|
@@ -120,21 +128,22 @@ class HRpcAdapter {
|
|
|
120
128
|
skill: skill,
|
|
121
129
|
wakeReason: config.originKind ?? 'heartbeat_timer',
|
|
122
130
|
}),
|
|
123
|
-
// ──
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
//
|
|
131
|
+
// ── Helios Compression Server ───────────────────────────────────────────
|
|
132
|
+
// Inject HEADROOM_PROXY_URL so the context-compaction.ts extension can
|
|
133
|
+
// call the compression server directly from the Pi subprocess.
|
|
134
|
+
//
|
|
135
|
+
// IMPORTANT: We do NOT set ANTHROPIC_BASE_URL — that would route all LLM
|
|
136
|
+
// calls through the compression server, which is not a full LLM proxy.
|
|
137
|
+
// Instead, compression happens in the context-compaction.ts extension hook
|
|
138
|
+
// BEFORE the LLM call (compress messages array → Pi sends compressed to Bedrock).
|
|
139
|
+
// This is the correct architecture: compress at assembly time, not at wire time.
|
|
129
140
|
...(() => {
|
|
130
141
|
try {
|
|
131
142
|
const { HeadroomProxyManager } = require('../lib/headroom-proxy-manager');
|
|
132
143
|
const baseUrl = HeadroomProxyManager.getInstance().getBaseUrl();
|
|
133
144
|
if (baseUrl) {
|
|
134
145
|
return {
|
|
135
|
-
|
|
136
|
-
OPENAI_BASE_URL: `${baseUrl}/v1`,
|
|
137
|
-
HEADROOM_PROXY_URL: baseUrl,
|
|
146
|
+
HEADROOM_PROXY_URL: baseUrl,
|
|
138
147
|
};
|
|
139
148
|
}
|
|
140
149
|
} catch (_) {}
|
|
@@ -395,20 +404,33 @@ class HRpcAdapter {
|
|
|
395
404
|
onMeta?.({ type: 'agent_end', data: { taskId, textLength: lastAssistantText.length } });
|
|
396
405
|
closeStdin(child);
|
|
397
406
|
// Write real Bedrock spend as CostEvent so BudgetEnforcer can track it
|
|
398
|
-
if (totalCostCents > 0 &&
|
|
407
|
+
if (totalCostCents > 0 && config.companyId) {
|
|
399
408
|
const ceId = 'ce:rpc:' + taskId + ':' + Date.now();
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
409
|
+
// SQLite-first CostEvent write (P2-7)
|
|
410
|
+
try {
|
|
411
|
+
const _store = _getHboStoreRpc();
|
|
412
|
+
if (_store && _store.createCostEvent) {
|
|
413
|
+
_store.createCostEvent({
|
|
414
|
+
id: ceId, companyId: config.companyId ?? '', agentId: config.agentId ?? agentId ?? '',
|
|
415
|
+
costCents: totalCostCents, source: 'helios_rpc', taskId, createdAt: Date.now(),
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
} catch (_) {}
|
|
419
|
+
// Non-blocking Memgraph projection (fire-and-forget)
|
|
420
|
+
if (context.mgQuery) {
|
|
421
|
+
setImmediate(() => context.mgQuery(
|
|
422
|
+
`MERGE (ce:CostEvent {id: $id})
|
|
423
|
+
ON CREATE SET
|
|
424
|
+
ce.companyId = $cid,
|
|
425
|
+
ce.agentId = $agentId,
|
|
426
|
+
ce.costCents = toInteger($costCents),
|
|
427
|
+
ce.source = 'helios_rpc',
|
|
428
|
+
ce.taskId = $taskId,
|
|
429
|
+
ce.createdAt = localdatetime()`,
|
|
430
|
+
{ id: ceId, cid: config.companyId ?? '', agentId: config.agentId ?? agentId ?? '',
|
|
431
|
+
costCents: totalCostCents, taskId }
|
|
432
|
+
).catch(e => onLog?.({ stream: 'stderr', chunk: '[helios-rpc-adapter] CostEvent write failed: ' + e.message, ts: new Date().toISOString() })));
|
|
433
|
+
}
|
|
412
434
|
}
|
|
413
435
|
|
|
414
436
|
// F1-3: if child doesn't exit within 30s after agent_end, settle now.
|
|
@@ -25,6 +25,14 @@ class TuiWakeupAdapter {
|
|
|
25
25
|
if (!heliosConfig.apiUrl || !agentHelios.agentId || !agentHelios.apiKey) {
|
|
26
26
|
return { exitCode: 1, signal: null, timedOut: false, errorCode: 'environment_unhealthy', resultJson: null };
|
|
27
27
|
}
|
|
28
|
+
if (!heliosConfig.companyId) {
|
|
29
|
+
onLog?.({
|
|
30
|
+
stream: 'stderr',
|
|
31
|
+
chunk: 'HBO wakeup disabled: HELIOS_SAAS_COMPANY_ID is required for tui_wakeup adapter',
|
|
32
|
+
ts: new Date().toISOString(),
|
|
33
|
+
});
|
|
34
|
+
return { exitCode: 1, signal: null, timedOut: false, errorCode: 'environment_unhealthy', resultJson: null };
|
|
35
|
+
}
|
|
28
36
|
|
|
29
37
|
// Ensure Helios issue exists
|
|
30
38
|
let heliosIssueId = null;
|
|
@@ -90,6 +90,11 @@
|
|
|
90
90
|
<string>unified</string>
|
|
91
91
|
<key>HELIOS_ALLOW_SEND</key>
|
|
92
92
|
<string>0</string>
|
|
93
|
+
<!-- --experimental-sqlite enables node:sqlite built-in (hbo-core-store).
|
|
94
|
+
Stable in Node ≥ 23; required flag in Node 22.
|
|
95
|
+
--max-old-space-size=1024 matches PM2 ecosystem.config.js memory cap (SVC-2 fix). -->
|
|
96
|
+
<key>NODE_OPTIONS</key>
|
|
97
|
+
<string>--experimental-sqlite --max-old-space-size=1024</string>
|
|
93
98
|
<!-- PATH extended to cover common Node.js install locations:
|
|
94
99
|
nvm default, volta, homebrew, system. The launch script will
|
|
95
100
|
source the user's shell profile which adds nvm to PATH. -->
|
|
@@ -35,6 +35,10 @@ Environment=TZ=UTC
|
|
|
35
35
|
Environment=ROUTING_AUTHORITY=unified
|
|
36
36
|
Environment=AWS_REGION=us-east-1
|
|
37
37
|
Environment=MEMGRAPH_BOLT_URL=bolt://127.0.0.1:7687
|
|
38
|
+
# --experimental-sqlite enables node:sqlite built-in (hbo-core-store fallback store).
|
|
39
|
+
# Stable in Node ≥ 23; required flag in Node 22.
|
|
40
|
+
# --max-old-space-size=1024 matches the PM2 ecosystem.config.js memory cap (SVC-1 fix).
|
|
41
|
+
Environment=NODE_OPTIONS=--experimental-sqlite --max-old-space-size=1024
|
|
38
42
|
# HELIOS_ROOT is resolved by launch-daemon.sh — not hardcoded here
|
|
39
43
|
# EnvironmentFile uses - prefix (dash) to not fail if file is missing
|
|
40
44
|
EnvironmentFile=-%h/helios-agent/.env
|
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
const { HELIOS_ROOT: _HELIOS_ROOT_CE } = require('./lib/paths');
|
|
6
6
|
|
|
7
|
+
// Lazy-require hbo-core-store for SQLite-first business entity reads (P2-11)
|
|
8
|
+
let _hboStoreCE = null;
|
|
9
|
+
function _getHboStoreCE() {
|
|
10
|
+
if (_hboStoreCE) return _hboStoreCE;
|
|
11
|
+
try { _hboStoreCE = require('../lib/hbo-core-store'); } catch (_) {}
|
|
12
|
+
return _hboStoreCE;
|
|
13
|
+
}
|
|
14
|
+
|
|
7
15
|
/** Marker appended when buildContextBrief truncates an oversized brief. */
|
|
8
16
|
const CONTEXT_BRIEF_TRUNCATED = '[CONTEXT BRIEF TRUNCATED — exceeded 30k char cap (aligned to adapter delivery limit)]';
|
|
9
17
|
|
|
@@ -430,19 +438,33 @@ async function buildContextBrief(mgQuery, agentId, taskTitle, companyId, hboBrid
|
|
|
430
438
|
} catch (e) { /* non-fatal */ }
|
|
431
439
|
|
|
432
440
|
// 3. Agent's own pending tasks (what else is on their plate)
|
|
441
|
+
// Memgraph primary — SQLite fallback on unavailability
|
|
433
442
|
try {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
443
|
+
let pendingRecords = null;
|
|
444
|
+
try {
|
|
445
|
+
const pendingResult = await mgQuery(
|
|
446
|
+
`MATCH (t:Task {assigneeAgentId: $aid, companyId: $cid})
|
|
447
|
+
WHERE t.status IN ['todo', 'in_progress']
|
|
448
|
+
RETURN t.title AS title, t.status AS status, t.priority AS priority
|
|
449
|
+
ORDER BY CASE t.priority WHEN 'P0' THEN 0 WHEN 'P1' THEN 1 WHEN 'P2' THEN 2 ELSE 3 END
|
|
450
|
+
LIMIT 5`,
|
|
451
|
+
{ aid: agentId, cid: companyId }
|
|
452
|
+
);
|
|
453
|
+
pendingRecords = toRecords(pendingResult).map(r => ({ _title: r.get('title'), _status: r.get('status'), _priority: r.get('priority') }));
|
|
454
|
+
} catch (_mgErr) {
|
|
455
|
+
// Memgraph unavailable — fall back to SQLite pending tasks
|
|
456
|
+
try {
|
|
457
|
+
const _storeCE = _getHboStoreCE();
|
|
458
|
+
if (_storeCE && _storeCE.getTasksByCompanyStatus) {
|
|
459
|
+
const _rows = _storeCE.getTasksByCompanyStatus(companyId, ['todo', 'in_progress'])
|
|
460
|
+
.filter(t => t.assigneeAgentId === agentId);
|
|
461
|
+
pendingRecords = _rows.map(t => ({ _title: t.title, _status: t.status, _priority: t.priority }));
|
|
462
|
+
}
|
|
463
|
+
} catch (_sqliteErr) { /* SQLite also unavailable — skip pending tasks section */ }
|
|
464
|
+
}
|
|
465
|
+
if (pendingRecords && pendingRecords.length) {
|
|
444
466
|
const lines = pendingRecords.map(r =>
|
|
445
|
-
`- [${r.
|
|
467
|
+
`- [${r._priority || 'P3'}] ${r._title} (${r._status})`
|
|
446
468
|
);
|
|
447
469
|
sections.push({ id: 'active-tasks', content: `## Your Other Tasks\n${lines.join('\n')}` });
|
|
448
470
|
}
|
|
@@ -502,16 +524,32 @@ async function buildContextBrief(mgQuery, agentId, taskTitle, companyId, hboBrid
|
|
|
502
524
|
}
|
|
503
525
|
} catch (_) { /* non-fatal */ }
|
|
504
526
|
|
|
505
|
-
// § QUARTERLY OKR
|
|
527
|
+
// § QUARTERLY OKR — Memgraph primary, SQLite fallback on unavailability
|
|
506
528
|
try {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
529
|
+
let okrRec = null;
|
|
530
|
+
try {
|
|
531
|
+
const okrResult = await mgQuery(
|
|
532
|
+
`MATCH (o:QuarterlyOKR {companyId: $cid, status: 'active'})
|
|
533
|
+
WHERE o.agentId = $agentId OR o.assigneeAgentId = $agentId
|
|
534
|
+
RETURN o.objective AS objective, o.keyResults AS keyResults, o.quarter AS quarter
|
|
535
|
+
ORDER BY o.createdAt DESC LIMIT 1`,
|
|
536
|
+
{ agentId, cid: companyId }
|
|
537
|
+
);
|
|
538
|
+
okrRec = toRecords(okrResult)?.[0] ?? null;
|
|
539
|
+
} catch (_mgErr) {
|
|
540
|
+
// Memgraph unavailable — fall back to SQLite OKR data
|
|
541
|
+
try {
|
|
542
|
+
const _storeOKR = _getHboStoreCE();
|
|
543
|
+
if (_storeOKR && _storeOKR.getOKRsByCompanyType) {
|
|
544
|
+
const _okrs = _storeOKR.getOKRsByCompanyType(companyId, 'quarterly_okr')
|
|
545
|
+
.filter(o => (o.agentId === agentId || o.assigneeAgentId === agentId) && o.status === 'active');
|
|
546
|
+
if (_okrs.length) {
|
|
547
|
+
const o = _okrs[0];
|
|
548
|
+
okrRec = { get: (k) => o[k] ?? null };
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
} catch (_sqliteErr) { /* SQLite also unavailable — skip OKR section */ }
|
|
552
|
+
}
|
|
515
553
|
if (okrRec) {
|
|
516
554
|
const objective = okrRec.get('objective');
|
|
517
555
|
const keyResults = okrRec.get('keyResults') || [];
|
|
@@ -1736,7 +1774,7 @@ ${lines.join('\n')}` });
|
|
|
1736
1774
|
try {
|
|
1737
1775
|
const excl = JSON.parse(pillarData.exclusions || '[]');
|
|
1738
1776
|
if (excl.length > 0) exclusionNote = `\nDo NOT: ${excl.slice(0, 3).join(', ')}`;
|
|
1739
|
-
|
|
1777
|
+
} catch (_) {}
|
|
1740
1778
|
sections.push(
|
|
1741
1779
|
`<north_star_reminder>\n` +
|
|
1742
1780
|
`Before responding, confirm your answer serves this intent:\n` +
|
package/daemon/daemon-manager.js
CHANGED
|
@@ -19,7 +19,7 @@ const os = require('os');
|
|
|
19
19
|
const fs = require('fs');
|
|
20
20
|
const { spawn } = require('child_process');
|
|
21
21
|
|
|
22
|
-
const DEFAULT_PORT = parseInt(process.env.HELIOS_DAEMON_PORT || '
|
|
22
|
+
const DEFAULT_PORT = parseInt(process.env.HELIOS_DAEMON_PORT || '9093', 10);
|
|
23
23
|
const DAEMON_SCRIPT = path.join(__dirname, 'helios-company-daemon.js');
|
|
24
24
|
// Use HELIOS_ROOT from lib/paths.js so PID_FILE is correct for all install locations
|
|
25
25
|
// (Desktop at C:\Users\...\Desktop\Helios\helios-agent-main, macOS ~/helios-agent, etc.)
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const Database = require("better-sqlite3");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
|
|
7
|
+
const DB_PATH = path.join(process.env.HELIOS_ROOT || path.join(os.homedir(), "helios-agent"), "data", "email-infrastructure.db");
|
|
8
|
+
fs.mkdirSync(path.dirname(DB_PATH), { recursive: true });
|
|
9
|
+
|
|
10
|
+
const db = new Database(DB_PATH);
|
|
11
|
+
db.pragma("journal_mode = WAL");
|
|
12
|
+
db.pragma("foreign_keys = ON");
|
|
13
|
+
|
|
14
|
+
db.exec(`
|
|
15
|
+
CREATE TABLE IF NOT EXISTS email_domains (
|
|
16
|
+
id TEXT PRIMARY KEY,
|
|
17
|
+
company_id TEXT NOT NULL,
|
|
18
|
+
domain TEXT NOT NULL,
|
|
19
|
+
scenario TEXT NOT NULL DEFAULT 'existing',
|
|
20
|
+
registrar TEXT,
|
|
21
|
+
registrar_account_vault_key TEXT,
|
|
22
|
+
registrar_api_key_vault_key TEXT,
|
|
23
|
+
registered_at TEXT,
|
|
24
|
+
expires_at TEXT,
|
|
25
|
+
auto_renew INTEGER DEFAULT 1,
|
|
26
|
+
registration_cost_cents INTEGER,
|
|
27
|
+
annual_renewal_cents INTEGER,
|
|
28
|
+
porkbun_order_id TEXT,
|
|
29
|
+
mx_records TEXT DEFAULT '[]',
|
|
30
|
+
spf_record TEXT,
|
|
31
|
+
spf_verified INTEGER DEFAULT 0,
|
|
32
|
+
dkim_selectors TEXT DEFAULT '[]',
|
|
33
|
+
dmarc_policy TEXT,
|
|
34
|
+
dmarc_verified INTEGER DEFAULT 0,
|
|
35
|
+
status TEXT DEFAULT 'bootstrapping',
|
|
36
|
+
verified_at TEXT,
|
|
37
|
+
last_verified_at TEXT,
|
|
38
|
+
verification_error TEXT,
|
|
39
|
+
dns_provisioned_at TEXT,
|
|
40
|
+
inbound_provider TEXT,
|
|
41
|
+
outbound_provider TEXT,
|
|
42
|
+
inbound_provider_vault_key TEXT,
|
|
43
|
+
outbound_provider_vault_key TEXT,
|
|
44
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
45
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
46
|
+
UNIQUE(company_id, domain)
|
|
47
|
+
);
|
|
48
|
+
CREATE INDEX IF NOT EXISTS idx_email_domains_company ON email_domains(company_id);
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_email_domains_status ON email_domains(status);
|
|
50
|
+
CREATE INDEX IF NOT EXISTS idx_email_domains_expires ON email_domains(expires_at);
|
|
51
|
+
|
|
52
|
+
CREATE TABLE IF NOT EXISTS email_personas (
|
|
53
|
+
id TEXT PRIMARY KEY,
|
|
54
|
+
company_id TEXT NOT NULL,
|
|
55
|
+
slug TEXT NOT NULL,
|
|
56
|
+
first_name TEXT NOT NULL,
|
|
57
|
+
last_name TEXT,
|
|
58
|
+
full_name TEXT NOT NULL,
|
|
59
|
+
title TEXT NOT NULL,
|
|
60
|
+
company_display TEXT,
|
|
61
|
+
bio TEXT,
|
|
62
|
+
avatar_url TEXT,
|
|
63
|
+
persona_type TEXT NOT NULL DEFAULT 'ai_branded',
|
|
64
|
+
tone TEXT DEFAULT 'friendly',
|
|
65
|
+
writing_style TEXT,
|
|
66
|
+
avoid_phrases TEXT DEFAULT '[]',
|
|
67
|
+
signature_html TEXT,
|
|
68
|
+
signature_text TEXT,
|
|
69
|
+
linkedin_url TEXT,
|
|
70
|
+
calendar_url TEXT,
|
|
71
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
72
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
73
|
+
UNIQUE(company_id, slug)
|
|
74
|
+
);
|
|
75
|
+
CREATE INDEX IF NOT EXISTS idx_email_personas_company ON email_personas(company_id);
|
|
76
|
+
|
|
77
|
+
CREATE TABLE IF NOT EXISTS email_channels (
|
|
78
|
+
id TEXT PRIMARY KEY,
|
|
79
|
+
company_id TEXT NOT NULL,
|
|
80
|
+
domain_id TEXT NOT NULL,
|
|
81
|
+
address TEXT NOT NULL,
|
|
82
|
+
local_part TEXT NOT NULL,
|
|
83
|
+
sender_identity_mode TEXT NOT NULL DEFAULT 'branded_ai_agent',
|
|
84
|
+
display_name TEXT NOT NULL,
|
|
85
|
+
agent_persona_name TEXT,
|
|
86
|
+
agent_persona_title TEXT,
|
|
87
|
+
persona_id TEXT,
|
|
88
|
+
ai_disclosure_enabled INTEGER DEFAULT 1,
|
|
89
|
+
ai_disclosure_statement TEXT,
|
|
90
|
+
ai_disclosure_placement TEXT DEFAULT 'footer',
|
|
91
|
+
reply_to TEXT,
|
|
92
|
+
signature_html TEXT,
|
|
93
|
+
signature_text TEXT,
|
|
94
|
+
agent_id TEXT,
|
|
95
|
+
role_pattern TEXT,
|
|
96
|
+
smtp_provider TEXT,
|
|
97
|
+
smtp_vault_key TEXT,
|
|
98
|
+
imap_provider TEXT,
|
|
99
|
+
imap_vault_key TEXT,
|
|
100
|
+
helios_mail_inbox_id TEXT,
|
|
101
|
+
hitl_required INTEGER DEFAULT 0,
|
|
102
|
+
max_daily_outbound INTEGER DEFAULT 50,
|
|
103
|
+
warmup_enabled INTEGER DEFAULT 1,
|
|
104
|
+
warmup_current_limit INTEGER DEFAULT 10,
|
|
105
|
+
warmup_started_at TEXT,
|
|
106
|
+
eu_compliance_acknowledged INTEGER DEFAULT 0,
|
|
107
|
+
can_email_eu_recipients INTEGER DEFAULT 0,
|
|
108
|
+
physical_address TEXT,
|
|
109
|
+
unsubscribe_url TEXT,
|
|
110
|
+
preferred_non_eu_mode TEXT,
|
|
111
|
+
status TEXT DEFAULT 'pending_verification',
|
|
112
|
+
total_sent INTEGER DEFAULT 0,
|
|
113
|
+
sent_today INTEGER DEFAULT 0,
|
|
114
|
+
bounce_rate REAL DEFAULT 0.0,
|
|
115
|
+
complaint_rate REAL DEFAULT 0.0,
|
|
116
|
+
last_sent_at TEXT,
|
|
117
|
+
last_bounce_at TEXT,
|
|
118
|
+
last_error_at TEXT,
|
|
119
|
+
last_error_message TEXT,
|
|
120
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
121
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
122
|
+
UNIQUE(company_id, address)
|
|
123
|
+
);
|
|
124
|
+
CREATE INDEX IF NOT EXISTS idx_email_channels_company ON email_channels(company_id);
|
|
125
|
+
CREATE INDEX IF NOT EXISTS idx_email_channels_agent ON email_channels(agent_id);
|
|
126
|
+
CREATE INDEX IF NOT EXISTS idx_email_channels_status ON email_channels(status);
|
|
127
|
+
CREATE INDEX IF NOT EXISTS idx_email_channels_domain ON email_channels(domain_id);
|
|
128
|
+
|
|
129
|
+
CREATE TABLE IF NOT EXISTS domain_bootstrap_jobs (
|
|
130
|
+
id TEXT PRIMARY KEY,
|
|
131
|
+
company_id TEXT NOT NULL,
|
|
132
|
+
domain_id TEXT,
|
|
133
|
+
triggered_by TEXT NOT NULL,
|
|
134
|
+
approval_id TEXT,
|
|
135
|
+
current_stage TEXT NOT NULL DEFAULT 'domain_search',
|
|
136
|
+
completed_stages TEXT DEFAULT '[]',
|
|
137
|
+
failed_stage TEXT,
|
|
138
|
+
fail_reason TEXT,
|
|
139
|
+
retry_count INTEGER DEFAULT 0,
|
|
140
|
+
max_retries INTEGER DEFAULT 3,
|
|
141
|
+
candidate_domains TEXT DEFAULT '[]',
|
|
142
|
+
selected_domain TEXT,
|
|
143
|
+
estimated_cost_cents INTEGER,
|
|
144
|
+
paid_cost_cents INTEGER,
|
|
145
|
+
porkbun_order_id TEXT,
|
|
146
|
+
porkbun_account_registry_id TEXT,
|
|
147
|
+
resend_account_registry_id TEXT,
|
|
148
|
+
zoho_account_registry_id TEXT,
|
|
149
|
+
zoho_org_id TEXT,
|
|
150
|
+
captcha_solves_attempted INTEGER DEFAULT 0,
|
|
151
|
+
captcha_solves_succeeded INTEGER DEFAULT 0,
|
|
152
|
+
captcha_solve_cost_cents INTEGER DEFAULT 0,
|
|
153
|
+
started_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
154
|
+
completed_at TEXT,
|
|
155
|
+
last_activity_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
156
|
+
next_retry_at TEXT,
|
|
157
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
158
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
159
|
+
);
|
|
160
|
+
CREATE INDEX IF NOT EXISTS idx_bootstrap_jobs_company ON domain_bootstrap_jobs(company_id);
|
|
161
|
+
CREATE INDEX IF NOT EXISTS idx_bootstrap_jobs_stage ON domain_bootstrap_jobs(current_stage);
|
|
162
|
+
CREATE INDEX IF NOT EXISTS idx_bootstrap_jobs_domain ON domain_bootstrap_jobs(domain_id);
|
|
163
|
+
|
|
164
|
+
CREATE TABLE IF NOT EXISTS _schema_versions (
|
|
165
|
+
name TEXT PRIMARY KEY,
|
|
166
|
+
version INTEGER NOT NULL DEFAULT 1,
|
|
167
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
168
|
+
);
|
|
169
|
+
CREATE TABLE IF NOT EXISTS email_draft_reviews (
|
|
170
|
+
id TEXT PRIMARY KEY,
|
|
171
|
+
company_id TEXT NOT NULL,
|
|
172
|
+
agent_id TEXT NOT NULL,
|
|
173
|
+
channel_id TEXT NOT NULL,
|
|
174
|
+
recipient_email TEXT NOT NULL,
|
|
175
|
+
subject TEXT NOT NULL,
|
|
176
|
+
body_text TEXT NOT NULL,
|
|
177
|
+
body_html TEXT,
|
|
178
|
+
generated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
179
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
180
|
+
reviewed_by TEXT,
|
|
181
|
+
reviewed_at TEXT,
|
|
182
|
+
review_note TEXT,
|
|
183
|
+
expires_at TEXT NOT NULL,
|
|
184
|
+
send_after_approval INTEGER DEFAULT 1
|
|
185
|
+
);
|
|
186
|
+
CREATE INDEX IF NOT EXISTS idx_drafts_company_pending ON email_draft_reviews(company_id, status);
|
|
187
|
+
|
|
188
|
+
INSERT OR REPLACE INTO _schema_versions(name, version) VALUES('email_infrastructure', 2);
|
|
189
|
+
`);
|
|
190
|
+
|
|
191
|
+
db.close();
|
|
192
|
+
console.log("email-infrastructure.db created/migrated successfully at:", DB_PATH);
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* hbo-core-migrate.js
|
|
4
|
+
* Migrates HBO core entity data from Memgraph → SQLite (hbo-core-store).
|
|
5
|
+
*
|
|
6
|
+
* Entity types: Task, Approval, BudgetPolicy, CostEvent, BusinessAgent,
|
|
7
|
+
* QuarterlyOKR, CompanyGoal, SystemAim, GoalPillar,
|
|
8
|
+
* Lead, CRMContact, Account, Opportunity
|
|
9
|
+
*
|
|
10
|
+
* Design:
|
|
11
|
+
* - Idempotent: safe to run multiple times (INSERT OR REPLACE)
|
|
12
|
+
* - Graceful: catches Memgraph connection errors, logs, exits 0
|
|
13
|
+
* - Reports counts per entity type on completion
|
|
14
|
+
* - Never throws to process for expected errors
|
|
15
|
+
*
|
|
16
|
+
* Phase 1 — TCP probe checks if Memgraph port 7687 is open.
|
|
17
|
+
* If unreachable, logs "Memgraph unavailable" and exits 0.
|
|
18
|
+
* Phase 2 — Loads neo4j-driver and hbo-core-store, runs MATCH queries, upserts.
|
|
19
|
+
*/
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
process.env.HELIOS_SKIP_BROKER = '1';
|
|
23
|
+
globalThis.__helios_skip_broker = true;
|
|
24
|
+
|
|
25
|
+
const net = require('net');
|
|
26
|
+
|
|
27
|
+
function parseBoltUrl(url) {
|
|
28
|
+
try {
|
|
29
|
+
const u = new URL(url.replace(/^bolt:\/\//, 'tcp://'));
|
|
30
|
+
return { host: u.hostname || '127.0.0.1', port: parseInt(u.port || '7687', 10) };
|
|
31
|
+
} catch {
|
|
32
|
+
return { host: '127.0.0.1', port: 7687 };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function probeMemgraph(host, port, timeoutMs) {
|
|
37
|
+
return new Promise((resolve) => {
|
|
38
|
+
const socket = new net.Socket();
|
|
39
|
+
let done = false;
|
|
40
|
+
const finish = (ok) => {
|
|
41
|
+
if (done) return;
|
|
42
|
+
done = true;
|
|
43
|
+
try { socket.destroy(); } catch {}
|
|
44
|
+
resolve(ok);
|
|
45
|
+
};
|
|
46
|
+
socket.setTimeout(timeoutMs);
|
|
47
|
+
socket.once('connect', () => finish(true));
|
|
48
|
+
socket.once('error', () => finish(false));
|
|
49
|
+
socket.once('timeout', () => finish(false));
|
|
50
|
+
socket.connect(port, host);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const ENTITY_SPECS = [
|
|
55
|
+
{ label: 'Task', cypher: 'MATCH (n:Task) RETURN n', storeMethod: 'createTask' },
|
|
56
|
+
{ label: 'Approval', cypher: 'MATCH (n:Approval) RETURN n', storeMethod: 'createApproval' },
|
|
57
|
+
{ label: 'BudgetPolicy', cypher: 'MATCH (n:BudgetPolicy) RETURN n', storeMethod: 'upsertBudgetPolicy' },
|
|
58
|
+
{ label: 'CostEvent', cypher: 'MATCH (n:CostEvent) RETURN n', storeMethod: 'createCostEvent' },
|
|
59
|
+
{ label: 'BusinessAgent', cypher: 'MATCH (n:BusinessAgent) RETURN n', storeMethod: 'upsertBusinessAgent' },
|
|
60
|
+
{ label: 'QuarterlyOKR', cypher: 'MATCH (n:QuarterlyOKR) RETURN n', storeMethod: 'upsertOKR', typeOverride: 'quarterly_okr' },
|
|
61
|
+
{ label: 'CompanyGoal', cypher: 'MATCH (n:CompanyGoal) RETURN n', storeMethod: 'upsertOKR', typeOverride: 'company_goal' },
|
|
62
|
+
{ label: 'SystemAim', cypher: 'MATCH (n:SystemAim) RETURN n', storeMethod: 'upsertOKR', typeOverride: 'system_aim' },
|
|
63
|
+
{ label: 'GoalPillar', cypher: 'MATCH (n:GoalPillar) RETURN n', storeMethod: 'upsertOKR', typeOverride: 'goal_pillar' },
|
|
64
|
+
{ label: 'Lead', cypher: 'MATCH (n:Lead) RETURN n', storeMethod: 'upsertLead' },
|
|
65
|
+
{ label: 'CRMContact', cypher: 'MATCH (n:CRMContact) RETURN n', storeMethod: 'upsertCRMContact' },
|
|
66
|
+
{ label: 'Account', cypher: 'MATCH (n:Account) RETURN n', storeMethod: 'upsertAccount' },
|
|
67
|
+
{ label: 'Opportunity', cypher: 'MATCH (n:Opportunity) RETURN n', storeMethod: 'upsertOpportunity' },
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
async function main() {
|
|
71
|
+
console.log('[hbo-core-migrate] Starting HBO core data migration from Memgraph → SQLite');
|
|
72
|
+
|
|
73
|
+
const boltUrl = process.env.MEMGRAPH_BOLT_URL || 'bolt://127.0.0.1:7687';
|
|
74
|
+
const { host, port } = parseBoltUrl(boltUrl);
|
|
75
|
+
const available = await probeMemgraph(host, port, 3000);
|
|
76
|
+
|
|
77
|
+
if (!available) {
|
|
78
|
+
console.log('[hbo-core-migrate] Memgraph unavailable — skipping migration, SQLite will populate on first write');
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log(`[hbo-core-migrate] Memgraph reachable at ${host}:${port} — loading modules`);
|
|
83
|
+
|
|
84
|
+
const neo4j = require('neo4j-driver');
|
|
85
|
+
const store = require('../../lib/hbo-core-store');
|
|
86
|
+
|
|
87
|
+
if (typeof store.createTask !== 'function') {
|
|
88
|
+
console.error('[hbo-core-migrate] ERROR: hbo-core-store failed to load (createTask is not a function).');
|
|
89
|
+
console.error('[hbo-core-migrate] Ensure NODE_OPTIONS=--experimental-sqlite and jiti is available.');
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const auth = process.env.MEMGRAPH_USER
|
|
94
|
+
? neo4j.auth.basic(process.env.MEMGRAPH_USER, process.env.MEMGRAPH_PASSWORD || '')
|
|
95
|
+
: neo4j.auth.basic('', '');
|
|
96
|
+
|
|
97
|
+
const driver = neo4j.driver(boltUrl, auth, {
|
|
98
|
+
maxConnectionPoolSize: 4,
|
|
99
|
+
connectionAcquisitionTimeout: 10000,
|
|
100
|
+
connectionTimeout: 8000,
|
|
101
|
+
maxTransactionRetryTime: 0,
|
|
102
|
+
encrypted: 'ENCRYPTION_OFF',
|
|
103
|
+
disableLosslessIntegers: true,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
async function queryMemgraph(cypher) {
|
|
107
|
+
const session = driver.session({ defaultAccessMode: neo4j.session.READ });
|
|
108
|
+
try {
|
|
109
|
+
const result = await session.run(cypher);
|
|
110
|
+
return result.records.map(r => r.toObject());
|
|
111
|
+
} finally {
|
|
112
|
+
try { await session.close(); } catch {}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const counts = {};
|
|
117
|
+
const errors = [];
|
|
118
|
+
|
|
119
|
+
for (const spec of ENTITY_SPECS) {
|
|
120
|
+
let rows;
|
|
121
|
+
try {
|
|
122
|
+
rows = await queryMemgraph(spec.cypher);
|
|
123
|
+
} catch (err) {
|
|
124
|
+
console.warn(`[hbo-core-migrate] Warning: failed to read ${spec.label}: ${err.message}`);
|
|
125
|
+
errors.push({ label: spec.label, error: err.message });
|
|
126
|
+
counts[spec.label] = 0;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let count = 0;
|
|
131
|
+
const fn = store[spec.storeMethod];
|
|
132
|
+
if (typeof fn !== 'function') {
|
|
133
|
+
console.warn(`[hbo-core-migrate] Warning: store method '${spec.storeMethod}' not found — skipping ${spec.label}`);
|
|
134
|
+
counts[spec.label] = 0;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
for (const row of rows) {
|
|
139
|
+
try {
|
|
140
|
+
const props = row.n && row.n.properties ? row.n.properties : (row.n || {});
|
|
141
|
+
const record = spec.typeOverride ? { ...props, type: spec.typeOverride } : { ...props };
|
|
142
|
+
fn(record);
|
|
143
|
+
count++;
|
|
144
|
+
} catch (writeErr) {
|
|
145
|
+
if (!errors.find(e => e.label === spec.label)) {
|
|
146
|
+
errors.push({ label: spec.label, error: writeErr.message });
|
|
147
|
+
console.warn(`[hbo-core-migrate] Warning: failed to upsert ${spec.label}: ${writeErr.message}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
counts[spec.label] = count;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
try { await driver.close(); } catch {}
|
|
155
|
+
|
|
156
|
+
const tasks = counts['Task'] ?? 0;
|
|
157
|
+
const approvals = counts['Approval'] ?? 0;
|
|
158
|
+
const budgetPols = counts['BudgetPolicy'] ?? 0;
|
|
159
|
+
const costEvts = counts['CostEvent'] ?? 0;
|
|
160
|
+
const bizAgents = counts['BusinessAgent'] ?? 0;
|
|
161
|
+
const qOKRs = counts['QuarterlyOKR'] ?? 0;
|
|
162
|
+
const compGoals = counts['CompanyGoal'] ?? 0;
|
|
163
|
+
const sysAims = counts['SystemAim'] ?? 0;
|
|
164
|
+
const goalPillars = counts['GoalPillar'] ?? 0;
|
|
165
|
+
const leads = counts['Lead'] ?? 0;
|
|
166
|
+
const contacts = counts['CRMContact'] ?? 0;
|
|
167
|
+
const accounts = counts['Account'] ?? 0;
|
|
168
|
+
const opps = counts['Opportunity'] ?? 0;
|
|
169
|
+
|
|
170
|
+
console.log(
|
|
171
|
+
`[hbo-core-migrate] Migrated ${tasks} tasks, ${approvals} approvals, ` +
|
|
172
|
+
`${budgetPols} budget policies, ${costEvts} cost events, ` +
|
|
173
|
+
`${bizAgents} agents, ${qOKRs + compGoals + sysAims + goalPillars} OKRs, ` +
|
|
174
|
+
`${leads} leads, ${contacts} CRM contacts, ${accounts} accounts, ${opps} opportunities`
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (errors.length > 0) {
|
|
178
|
+
console.warn(`[hbo-core-migrate] Completed with ${errors.length} warning(s).`);
|
|
179
|
+
} else {
|
|
180
|
+
console.log('[hbo-core-migrate] Migration complete — no errors');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
process.exit(0);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
main().catch(err => {
|
|
187
|
+
console.error('[hbo-core-migrate] Unexpected error:', err.message || err);
|
|
188
|
+
process.exit(0);
|
|
189
|
+
});
|