@cgh567/agent 2.4.1 → 2.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/bin/helios +0 -0
  2. package/bin/helios-rpc-node-wrapper.cjs +0 -0
  3. package/bin/helios-rpc-wrapper.sh +0 -0
  4. package/daemon/adapters/helios-rpc-adapter.js +47 -25
  5. package/daemon/adapters/tui_wakeup.js +8 -0
  6. package/daemon/config/com.familiar.helios-daemon.plist +5 -0
  7. package/daemon/config/helios-daemon.service +4 -0
  8. package/daemon/context-enrichment.js +59 -21
  9. package/daemon/daemon-manager.js +1 -1
  10. package/daemon/db/email-infrastructure-migrate.js +192 -0
  11. package/daemon/db/hbo-core-migrate.js +189 -0
  12. package/daemon/helios-api.js +723 -57
  13. package/daemon/helios-company-daemon.js +616 -134
  14. package/daemon/lib/harada/cascade-judge.js +12 -50
  15. package/daemon/lib/harada/mandala.js +20 -0
  16. package/daemon/lib/harada/pillar-dispatcher.js +1 -1
  17. package/daemon/lib/harada/project-factory.js +7 -2
  18. package/daemon/lib/hbo-bridge.js +32 -13
  19. package/daemon/lib/hed-engine.js +10 -292
  20. package/daemon/lib/helios-hitl-host.js +15 -2
  21. package/daemon/lib/hitl-interaction-service.js +0 -0
  22. package/daemon/lib/memgraph-verify.js +38 -33
  23. package/daemon/lib/project-drift-detector.js +7 -17
  24. package/daemon/lib/project-semantic-updater.js +1 -14
  25. package/daemon/lib/task-completion-processor.js +11 -0
  26. package/daemon/lib/wizard-engine.js +57 -6
  27. package/daemon/routes/channels.js +10 -5
  28. package/daemon/routes/harada-map.js +11 -48
  29. package/daemon/routes/hbo.js +342 -75
  30. package/daemon/routes/hitl.js +0 -0
  31. package/daemon/routes/project.js +194 -62
  32. package/daemon/routes/routines.js +14 -0
  33. package/daemon/routes/tasks.js +15 -1
  34. package/daemon/routes/wizard.js +11 -4
  35. package/daemon/schema-apply.js +174 -0
  36. package/daemon/schema-definitions.js +423 -0
  37. package/daemon/schema-migrations-hbo.js +10 -0
  38. package/daemon/schema-migrations-hed.js +18 -0
  39. package/daemon/schema-migrations-hitl.js +0 -0
  40. package/daemon/schema-migrations-proj.js +131 -0
  41. package/extensions/001-tool-output-cap.ts +0 -0
  42. package/extensions/context-compaction.ts +45 -26
  43. package/extensions/cortex/activation-bridge.ts +5 -0
  44. package/extensions/cortex/learn.ts +26 -0
  45. package/extensions/cortex/wal-replay.ts +91 -0
  46. package/extensions/email/backfill.ts +0 -0
  47. package/extensions/helios-governance/analysis/ambiguity.ts +0 -0
  48. package/extensions/helios-governance/analysis/compliance.ts +0 -0
  49. package/extensions/helios-governance/analysis/long-task-detector.ts +0 -0
  50. package/extensions/helios-governance/analysis/output-contract.ts +0 -0
  51. package/extensions/helios-governance/analysis/patterns.ts +0 -0
  52. package/extensions/helios-governance/analysis/preflight.ts +0 -0
  53. package/extensions/helios-governance/analysis/recurring-violations.ts +0 -0
  54. package/extensions/helios-governance/analysis/task-classification.ts +0 -0
  55. package/extensions/helios-governance/analysis/task-intent.ts +0 -0
  56. package/extensions/helios-governance/gates/high-impact.ts +1 -1
  57. package/extensions/helios-governance/handlers/_jiti-require.ts +15 -8
  58. package/extensions/helios-governance/handlers/proxy-test-detector.ts +0 -0
  59. package/extensions/hema-dispatch-v3/graph-memory.ts +10 -0
  60. package/extensions/hema-dispatch-v3/index.ts +72 -47
  61. package/extensions/lib/elo-engine.js +0 -0
  62. package/extensions/lib/elo-engine.test.js +0 -0
  63. package/extensions/memgraph-autostart.ts +13 -0
  64. package/extensions/neuroplastic-eval.ts +0 -0
  65. package/extensions/shadow-loop/index.ts +0 -0
  66. package/extensions/warm-tick/warm-tick-maintenance.ts +8 -0
  67. package/lib/__tests__/hbo-core-store.test.js +238 -0
  68. package/lib/brain-v2-budget.js +0 -0
  69. package/lib/brain-v2-circuit-breaker.js +0 -0
  70. package/lib/brain-v2.js +0 -0
  71. package/lib/broker/adaptive-throttle.js +0 -0
  72. package/lib/broker/batch-coalescer.js +0 -0
  73. package/lib/broker/bulkhead.js +0 -0
  74. package/lib/broker/channel-registry.js +0 -0
  75. package/lib/broker/circuit-breaker.js +0 -0
  76. package/lib/broker/evidence-cache.js +0 -0
  77. package/lib/broker/health-monitor.js +0 -0
  78. package/lib/broker/mage-queue.js +0 -0
  79. package/lib/broker/priority-queue.js +0 -0
  80. package/lib/broker/server.js.bak-error2-fix +0 -0
  81. package/lib/broker/session-registry.js +0 -0
  82. package/lib/broker/singleton-timers.js +0 -0
  83. package/lib/broker/types.d.ts +0 -0
  84. package/lib/broker/vegas-limit.js +0 -0
  85. package/lib/compression/dist/ccr-store.js +74 -0
  86. package/lib/compression/dist/content-router.js +115 -0
  87. package/lib/compression/dist/pipeline.js +113 -0
  88. package/lib/compression/dist/server.js +265 -0
  89. package/lib/compression/dist/smart-crusher.js +251 -0
  90. package/lib/context-budget.ts +0 -0
  91. package/lib/context-firewall.js +0 -0
  92. package/lib/crm/integration/triage-bridge.js +0 -0
  93. package/lib/email-utils.ts +0 -0
  94. package/lib/eval/__tests__/preflight-checker.test.ts +0 -0
  95. package/lib/eval/__tests__/task-instruction-parser.test.ts +0 -0
  96. package/lib/eval/__tests__/verifier-runner.test.ts +0 -0
  97. package/lib/eval/index.ts +0 -0
  98. package/lib/eval/preflight-checker.ts +0 -0
  99. package/lib/eval/task-domain-classifier.ts +0 -0
  100. package/lib/eval/task-instruction-parser.ts +0 -0
  101. package/lib/eval/verifier-runner.ts +0 -0
  102. package/lib/event-bus.d.ts +0 -0
  103. package/lib/event-bus.mts +1 -1
  104. package/lib/governance-context-selector.ts +0 -0
  105. package/lib/graph/generate-extension-embeddings.js +0 -0
  106. package/lib/graph/generate-static-embeddings.js +0 -0
  107. package/lib/graph/lib/utils.js +1 -1
  108. package/lib/graph-audit.d.ts +0 -0
  109. package/lib/graph-availability.js +62 -0
  110. package/lib/hbo-core-store.compiled.js +834 -0
  111. package/lib/hbo-core-store.js +124 -0
  112. package/lib/hbo-core-store.ts +908 -0
  113. package/lib/mesh-circuit-breaker.js +0 -0
  114. package/lib/mission-loop/lesson-extractor.ts +0 -0
  115. package/lib/mission-loop/mental-model-scorer.ts +0 -0
  116. package/lib/mission-loop/occ-detector.ts +0 -0
  117. package/lib/mission-loop/query-variants.ts +0 -0
  118. package/lib/mission-loop/verifier-check.ts +0 -0
  119. package/lib/skill-reference-builder.ts +0 -0
  120. package/lib/telemetry/token-breakdown.ts +0 -0
  121. package/lib/tool-compressor.ts +0 -0
  122. package/lib/triage-core/classifier.ts +3 -2
  123. package/lib/triage-core/graph/schema.cypher +10 -0
  124. package/lib/triage-core/legal-routing.ts +0 -0
  125. package/lib/triage-core/mental-model/dunbar-classifier.ts +0 -0
  126. package/lib/triage-core/mental-model/enrich-all.ts +0 -0
  127. package/lib/triage-core/mental-model/identity-resolver.ts +0 -0
  128. package/lib/triage-core/mental-model/key-facts.ts +1 -2
  129. package/lib/triage-core/mental-model/model-assembler.ts +0 -0
  130. package/lib/triage-core/orchestrator.ts +4 -11
  131. package/lib/triage-core/orchestrator.ts.bak-r005-r006-r008 +0 -0
  132. package/package.json +18 -8
  133. package/skills/helios-business-operator/services/signals/upwork-signals.js +0 -0
  134. package/skills/talisman-ceo/SKILL.md +23 -25
  135. package/skills/talisman-comms/SKILL.md +5 -5
  136. package/skills/talisman-engineering/SKILL.md +5 -5
  137. package/skills/talisman-finance/SKILL.md +10 -8
  138. package/skills/talisman-marketing/SKILL.md +10 -10
  139. package/skills/talisman-sales/SKILL.md +12 -15
  140. package/skills/talisman-support/SKILL.md +5 -5
  141. package/agents/business/talisman-ceo.md +0 -183
  142. package/agents/business/talisman-comms.md +0 -257
  143. package/agents/business/talisman-cto.md +0 -153
  144. package/agents/business/talisman-finance.md +0 -246
  145. package/agents/business/talisman-marketing.md +0 -240
  146. package/agents/business/talisman-sales.md +0 -242
  147. package/agents/business/talisman-support.md +0 -236
  148. package/daemon/lib/approval-expiry.js +0 -162
  149. package/daemon/lib/blast-radius-analyzer.js +0 -75
  150. package/daemon/lib/domain-bootstrap-orchestrator.js +0 -267
  151. package/daemon/lib/forensic-log.js +0 -113
  152. package/daemon/lib/goal-research-pipeline.js +0 -644
  153. package/daemon/lib/harada/cascade-research-dispatcher.js +0 -261
  154. package/daemon/lib/headroom-middleware.js +0 -167
  155. package/daemon/lib/headroom-proxy-manager.js +0 -623
  156. package/daemon/lib/mental-model-cache.js +0 -96
  157. package/daemon/lib/project-factory.js +0 -47
  158. package/daemon/lib/session-log-reader.js +0 -93
  159. package/daemon/routes/hed.js +0 -133
  160. package/lib/graph/learning/headroom-learn-bridge.js +0 -215
  161. package/skills/helios-bookkeeping/SKILL.md +0 -321
  162. package/skills/helios-briefer/SKILL.md +0 -44
  163. package/skills/helios-client-relations/SKILL.md +0 -322
  164. package/skills/helios-personal-triager/SKILL.md +0 -45
  165. package/skills/helios-recruitment/SKILL.md +0 -317
  166. package/skills/helios-relationship-nudger/SKILL.md +0 -77
  167. package/skills/helios-researcher/SKILL.md +0 -44
  168. package/skills/helios-scheduler/SKILL.md +0 -58
  169. package/skills/helios-tax-analyst/SKILL.md +0 -280
@@ -1,75 +0,0 @@
1
- 'use strict';
2
-
3
- const { execFile } = require('child_process');
4
- const { promisify } = require('util');
5
- const execFileAsync = promisify(execFile);
6
-
7
- /**
8
- * BlastRadiusAnalyzer — measures the impact of a HED operation's execution.
9
- * Compares declared target vs actual changes (git diff + Memgraph diff).
10
- */
11
-
12
- class BlastRadiusAnalyzer {
13
- constructor(mg, repoPath) {
14
- this._mg = mg;
15
- this._repoPath = repoPath || process.cwd();
16
- }
17
-
18
- async analyze(opId, operation, executionLockedAt, completedAt) {
19
- const [filesChanged, nodesAffected] = await Promise.allSettled([
20
- this._getChangedFiles(operation.target),
21
- this._getChangedNodes(executionLockedAt, completedAt)
22
- ]);
23
-
24
- const changedFiles = filesChanged.status === 'fulfilled' ? filesChanged.value : [];
25
- const changedNodes = nodesAffected.status === 'fulfilled' ? nodesAffected.value : [];
26
-
27
- const outsideTarget = changedFiles.filter(f => !f.includes(operation.target || ''));
28
- const severity = this._computeSeverity(outsideTarget, changedNodes);
29
-
30
- return {
31
- filesChanged: outsideTarget,
32
- allFilesChanged: changedFiles,
33
- nodesAffected: changedNodes,
34
- severity,
35
- opId
36
- };
37
- }
38
-
39
- async _getChangedFiles(declaredTarget) {
40
- try {
41
- const { stdout } = await execFileAsync('git', ['diff', '--name-only', 'HEAD~1'], {
42
- cwd: this._repoPath,
43
- windowsHide: true
44
- });
45
- return stdout.trim().split('\n').filter(Boolean);
46
- } catch {
47
- return [];
48
- }
49
- }
50
-
51
- async _getChangedNodes(executionLockedAt, completedAt) {
52
- if (!executionLockedAt || !completedAt) return [];
53
- try {
54
- const rows = await this._mg(
55
- `MATCH (n) WHERE n.updatedAt >= $from AND n.updatedAt <= $to
56
- RETURN DISTINCT labels(n)[0] AS label, count(n) AS count LIMIT 20`,
57
- { from: executionLockedAt, to: completedAt }
58
- );
59
- return (rows || []).map(r => `${r.label}(${r.count})`);
60
- } catch {
61
- return [];
62
- }
63
- }
64
-
65
- _computeSeverity(outsideFiles, changedNodes) {
66
- const score = outsideFiles.length * 2 + changedNodes.length;
67
- if (score === 0) return 'none';
68
- if (score <= 2) return 'low';
69
- if (score <= 6) return 'medium';
70
- if (score <= 12) return 'high';
71
- return 'critical';
72
- }
73
- }
74
-
75
- module.exports = { BlastRadiusAnalyzer };
@@ -1,267 +0,0 @@
1
- 'use strict';
2
- /**
3
- * DomainBootstrapOrchestrator — drives DomainBootstrapJob through all stages.
4
- *
5
- * Stage machine:
6
- * domain_search → domain_candidates_generated → domain_selected [HITL]
7
- * → domain_registered [HITL] → resend_provisioned → dns_configured
8
- * → dns_verified → forwarding_configured → email_channel_created → complete
9
- *
10
- * HITL gates at domain_selected (human picks domain) and domain_registered
11
- * (human confirms purchase). Both create Approval nodes that surface in
12
- * the Inbox → Approvals tab.
13
- *
14
- * Inbound: Porkbun email forwarding (company@domain → founder's personal inbox)
15
- * Outbound: Resend (sends FROM company@domain)
16
- */
17
-
18
- const STAGES = [
19
- 'domain_search',
20
- 'domain_candidates_generated',
21
- 'domain_selected', // HITL gate
22
- 'domain_registered', // HITL gate (money)
23
- 'resend_provisioned',
24
- 'dns_configured',
25
- 'dns_verified',
26
- 'forwarding_configured',
27
- 'email_channel_created',
28
- 'complete',
29
- ];
30
-
31
- class DomainBootstrapOrchestrator {
32
- constructor(mgQuery, companyId, log) {
33
- this._mg = mgQuery;
34
- this._companyId = companyId;
35
- this._log = log || ((level, msg, meta) => console.log(`[domain-bootstrap] ${msg}`, meta || ''));
36
- }
37
-
38
- /**
39
- * Called every N ticks. Finds pending jobs for this company and advances them.
40
- */
41
- async checkPendingJobs() {
42
- let EmailInfraStore, PorkbunApiClient, ResendApiClient, generateDomainCandidates, pollDnsRecord;
43
- try {
44
- EmailInfraStore = require('../lib/email-infrastructure-store').EmailInfraStore;
45
- PorkbunApiClient = require('../lib/porkbun-api').PorkbunApiClient;
46
- ResendApiClient = require('../lib/resend-api').ResendApiClient;
47
- generateDomainCandidates = require('../lib/domain-candidate-generator').generateDomainCandidates;
48
- pollDnsRecord = require('../lib/dns-propagation').pollDnsRecord;
49
- } catch (e) {
50
- this._log('warn', `domain-bootstrap: missing dependency — ${e.message}`);
51
- return;
52
- }
53
-
54
- const store = new EmailInfraStore(this._mg);
55
- const jobs = await store.getBootstrapJobs(this._companyId, 'pending').catch(() => []);
56
-
57
- for (const job of jobs) {
58
- try {
59
- await this._advanceJob(job, store, { PorkbunApiClient, ResendApiClient, generateDomainCandidates, pollDnsRecord });
60
- } catch (err) {
61
- this._log('error', `domain-bootstrap: job ${job.id} failed at stage ${job.currentStage}: ${err.message}`);
62
- await store.updateBootstrapJob(job.id, { lastError: err.message, errorAt: new Date().toISOString() }).catch(() => {});
63
- }
64
- }
65
- }
66
-
67
- async _advanceJob(job, store, deps) {
68
- const { PorkbunApiClient, ResendApiClient, generateDomainCandidates, pollDnsRecord } = deps;
69
-
70
- switch (job.currentStage) {
71
- case 'domain_search': {
72
- // Generate domain candidates from company name
73
- const companyRow = await this._mg(
74
- `MATCH (c:Company {id: $cid}) RETURN c.name AS name`, { cid: this._companyId }
75
- );
76
- const companyName = companyRow?.rows?.[0]?.[0] || this._companyId;
77
- const candidates = generateDomainCandidates(companyName, { tlds: ['.com', '.io', '.co'], maxCandidates: 15 });
78
-
79
- // Check availability via Porkbun (if key available, else skip availability check)
80
- let enrichedCandidates = candidates;
81
- if (process.env.PORKBUN_API_KEY) {
82
- const porkbun = new PorkbunApiClient();
83
- enrichedCandidates = await Promise.allSettled(
84
- candidates.map(async c => ({
85
- domain: c,
86
- available: await porkbun.checkDomain(c).then(r => r.status === 'AVAILABLE').catch(() => null),
87
- price: await porkbun.getPricing([c.split('.').pop()]).then(r => r[0]?.registration || null).catch(() => null),
88
- }))
89
- ).then(results => results.filter(r => r.status === 'fulfilled').map(r => r.value));
90
- } else {
91
- enrichedCandidates = candidates.map(c => ({ domain: c, available: null, price: null }));
92
- this._log('warn', 'domain-bootstrap: PORKBUN_API_KEY not set — skipping availability check');
93
- }
94
-
95
- await store.updateBootstrapJob(job.id, {
96
- currentStage: 'domain_candidates_generated',
97
- domainCandidates: JSON.stringify(enrichedCandidates),
98
- });
99
-
100
- // Create HITL Approval for domain selection
101
- const approvalId = `approval:domain-select:${job.id}`;
102
- await this._mg(
103
- `MERGE (a:Approval {id: $aid})
104
- ON CREATE SET
105
- a.companyId = $cid, a.type = 'domain_selection', a.status = 'pending',
106
- a.jobId = $jobId, a.candidates = $candidates,
107
- a.message = 'Choose a domain for your company email',
108
- a.createdAt = localdatetime()`,
109
- { aid: approvalId, cid: this._companyId, jobId: job.id, candidates: JSON.stringify(enrichedCandidates.slice(0, 8)) }
110
- );
111
- this._log('info', `domain-bootstrap: ${enrichedCandidates.length} candidates generated, HITL approval created`);
112
- break;
113
- }
114
-
115
- case 'domain_candidates_generated': {
116
- // Wait for HITL approval (domain selection)
117
- const approval = await this._mg(
118
- `MATCH (a:Approval {type: 'domain_selection', jobId: $jobId}) RETURN a.status, a.selectedDomain LIMIT 1`,
119
- { jobId: job.id }
120
- );
121
- const status = approval?.rows?.[0]?.[0];
122
- const selectedDomain = approval?.rows?.[0]?.[1];
123
- if (status === 'approved' && selectedDomain) {
124
- await store.updateBootstrapJob(job.id, { currentStage: 'domain_selected', selectedDomain });
125
-
126
- // Create HITL Approval for purchase confirmation
127
- const purchaseApprovalId = `approval:domain-purchase:${job.id}`;
128
- await this._mg(
129
- `MERGE (a:Approval {id: $aid})
130
- ON CREATE SET
131
- a.companyId = $cid, a.type = 'domain_purchase', a.status = 'pending',
132
- a.jobId = $jobId, a.domain = $domain,
133
- a.message = $msg, a.createdAt = localdatetime()`,
134
- { aid: purchaseApprovalId, cid: this._companyId, jobId: job.id, domain: selectedDomain,
135
- msg: `Confirm purchase of ${selectedDomain} for your company email. This will charge your Porkbun account.` }
136
- );
137
- this._log('info', `domain-bootstrap: domain ${selectedDomain} selected, purchase confirmation requested`);
138
- }
139
- break;
140
- }
141
-
142
- case 'domain_selected': {
143
- // Wait for purchase HITL approval
144
- const approval = await this._mg(
145
- `MATCH (a:Approval {type: 'domain_purchase', jobId: $jobId}) RETURN a.status LIMIT 1`,
146
- { jobId: job.id }
147
- );
148
- const status = approval?.rows?.[0]?.[0];
149
- if (status === 'approved') {
150
- if (!process.env.PORKBUN_API_KEY) {
151
- this._log('warn', 'domain-bootstrap: PORKBUN_API_KEY not set — cannot register domain');
152
- break;
153
- }
154
- const porkbun = new PorkbunApiClient();
155
- await porkbun.registerDomain(job.selectedDomain, 1, false);
156
- await store.updateBootstrapJob(job.id, { currentStage: 'domain_registered' });
157
- this._log('info', `domain-bootstrap: domain ${job.selectedDomain} registered`);
158
- }
159
- break;
160
- }
161
-
162
- case 'domain_registered': {
163
- if (!process.env.RESEND_API_KEY) {
164
- this._log('warn', 'domain-bootstrap: RESEND_API_KEY not set — cannot provision outbound email');
165
- break;
166
- }
167
- const resend = new ResendApiClient();
168
- const resendDomain = await resend.addDomain(job.selectedDomain);
169
- const dnsRecords = await resend.getDomainDnsRecords(resendDomain.id);
170
- await store.updateBootstrapJob(job.id, {
171
- currentStage: 'resend_provisioned',
172
- resendDomainId: resendDomain.id,
173
- resendDnsRecords: JSON.stringify(dnsRecords),
174
- });
175
- this._log('info', `domain-bootstrap: Resend provisioned, ${dnsRecords.length} DNS records needed`);
176
- break;
177
- }
178
-
179
- case 'resend_provisioned': {
180
- if (!process.env.PORKBUN_API_KEY) break;
181
- const porkbun = new PorkbunApiClient();
182
- const dnsRecords = JSON.parse(job.resendDnsRecords || '[]');
183
- for (const record of dnsRecords) {
184
- await porkbun.createDnsRecord(job.selectedDomain, record).catch(e =>
185
- this._log('warn', `domain-bootstrap: DNS record creation failed: ${e.message}`)
186
- );
187
- }
188
- await store.updateBootstrapJob(job.id, { currentStage: 'dns_configured' });
189
- this._log('info', `domain-bootstrap: DNS records configured, waiting for propagation`);
190
- break;
191
- }
192
-
193
- case 'dns_configured': {
194
- // Poll for DNS propagation (non-blocking — will be re-checked next tick)
195
- const verified = await pollDnsRecord(job.selectedDomain, 'MX', { maxAttempts: 3, intervalMs: 5000 }).catch(() => false);
196
- if (verified) {
197
- if (process.env.RESEND_API_KEY) {
198
- const resend = new ResendApiClient();
199
- await resend.verifyDomain(job.resendDomainId).catch(() => {});
200
- }
201
- await store.updateBootstrapJob(job.id, { currentStage: 'dns_verified' });
202
- this._log('info', `domain-bootstrap: DNS verified for ${job.selectedDomain}`);
203
- }
204
- break;
205
- }
206
-
207
- case 'dns_verified': {
208
- // Set up email forwarding: company@domain → founder's personal inbox
209
- // Get the company's EmailAccount to find the forwarding target
210
- const accounts = await this._mg(
211
- `MATCH (c:Company {id: $cid})-[:HAS_EMAIL_ACCOUNT]->(a:EmailAccount) RETURN a.email LIMIT 1`,
212
- { cid: this._companyId }
213
- );
214
- const forwardTo = accounts?.rows?.[0]?.[0];
215
- if (forwardTo && process.env.PORKBUN_API_KEY) {
216
- const porkbun = new PorkbunApiClient();
217
- const infoAlias = `info@${job.selectedDomain}`;
218
- await porkbun.createEmailForward(job.selectedDomain, 'info', forwardTo).catch(e =>
219
- this._log('warn', `domain-bootstrap: email forward creation failed: ${e.message}`)
220
- );
221
- await store.updateBootstrapJob(job.id, { currentStage: 'forwarding_configured', companyEmail: infoAlias });
222
- this._log('info', `domain-bootstrap: ${infoAlias} → ${forwardTo} forwarding configured`);
223
- } else {
224
- // No forwarding target — skip to channel creation
225
- const companyEmail = `info@${job.selectedDomain}`;
226
- await store.updateBootstrapJob(job.id, { currentStage: 'forwarding_configured', companyEmail });
227
- }
228
- break;
229
- }
230
-
231
- case 'forwarding_configured': {
232
- // Create EmailAccount node + EmailChannel
233
- const accountSlug = job.companyEmail.replace('@', '-at-').replace(/\./g, '-');
234
- const accountId = `acct:${this._companyId}:${accountSlug}`;
235
- await this._mg(
236
- `MERGE (a:EmailAccount {id: $aid})
237
- ON CREATE SET
238
- a.companyId = $cid, a.email = $email, a.provider = 'resend',
239
- a.authMethod = 'resend-api', a.enabled = true,
240
- a.domain = $domain, a.resendDomainId = $resendId,
241
- a.createdAt = localdatetime()
242
- WITH a
243
- MATCH (c:Company {id: $cid})
244
- MERGE (c)-[:HAS_EMAIL_ACCOUNT]->(a)`,
245
- { aid: accountId, cid: this._companyId, email: job.companyEmail,
246
- domain: job.selectedDomain, resendId: job.resendDomainId || null }
247
- );
248
- await store.updateBootstrapJob(job.id, { currentStage: 'email_channel_created', emailAccountId: accountId });
249
- // Broadcast SSE so wizard/frontend knows setup is complete
250
- this._log('info', `domain-bootstrap: EmailAccount ${accountId} created`);
251
- break;
252
- }
253
-
254
- case 'email_channel_created': {
255
- await store.updateBootstrapJob(job.id, { currentStage: 'complete', completedAt: new Date().toISOString() });
256
- this._log('info', `domain-bootstrap: job ${job.id} complete — ${job.companyEmail} ready`);
257
- break;
258
- }
259
-
260
- default:
261
- // Job is at complete or unknown stage — nothing to do
262
- break;
263
- }
264
- }
265
- }
266
-
267
- module.exports = { DomainBootstrapOrchestrator, STAGES };
@@ -1,113 +0,0 @@
1
- /**
2
- * forensic-log.js — Pino-based request/response logger for helios-api.js
3
- *
4
- * Usage:
5
- * const { forensicLog, createRequestLogger } = require('./lib/forensic-log');
6
- *
7
- * // In the route() function, at the top:
8
- * const reqLog = createRequestLogger(req);
9
- * reqLog.info('request received');
10
- *
11
- * // At the end:
12
- * reqLog.info({ statusCode: 200, durationMs: Date.now() - startMs }, 'response sent');
13
- *
14
- * Environment variables:
15
- * HELIOS_TRACE_REQUESTS=1 — log every HTTP request + response duration
16
- * HELIOS_TRACE_SSE=1 — log every SSE event broadcast
17
- * HELIOS_LOG_LEVEL — pino log level (default: 'info')
18
- * LOG_LEVEL — fallback log level
19
- */
20
- 'use strict';
21
-
22
- let pino;
23
- try {
24
- pino = require('pino');
25
- } catch {
26
- // Fallback if pino not installed — use the daemon's existing log format
27
- pino = null;
28
- }
29
-
30
- const LOG_LEVEL = process.env.HELIOS_LOG_LEVEL || process.env.LOG_LEVEL || 'info';
31
-
32
- // Base forensic logger — writes to stderr so it doesn't pollute daemon stdout JSONL
33
- const forensicLog = pino
34
- ? pino(
35
- {
36
- level: LOG_LEVEL,
37
- name: 'helios-api',
38
- timestamp: pino.stdTimeFunctions.isoTime,
39
- // Redact auth tokens from logs
40
- redact: {
41
- paths: [
42
- 'req.headers.authorization',
43
- 'headers.authorization',
44
- 'token',
45
- 'apiToken',
46
- ],
47
- censor: '[REDACTED]',
48
- },
49
- // Remove pid/hostname to keep logs lean
50
- base: null,
51
- },
52
- // fd 2 = stderr — daemon stdout is reserved for JSONL agent events
53
- pino.destination({ dest: 2, sync: false })
54
- )
55
- : {
56
- // Minimal fallback when pino is unavailable
57
- info: (obj, msg) =>
58
- process.stderr.write(
59
- JSON.stringify({ ts: new Date().toISOString(), level: 'info', ...(typeof obj === 'string' ? { msg: obj } : obj), ...(msg ? { msg } : {}) }) + '\n'
60
- ),
61
- warn: (obj, msg) =>
62
- process.stderr.write(
63
- JSON.stringify({ ts: new Date().toISOString(), level: 'warn', ...(typeof obj === 'string' ? { msg: obj } : obj), ...(msg ? { msg } : {}) }) + '\n'
64
- ),
65
- error: (obj, msg) =>
66
- process.stderr.write(
67
- JSON.stringify({ ts: new Date().toISOString(), level: 'error', ...(typeof obj === 'string' ? { msg: obj } : obj), ...(msg ? { msg } : {}) }) + '\n'
68
- ),
69
- debug: () => {},
70
- child(ctx) {
71
- const parent = this;
72
- return {
73
- ...parent,
74
- info: (obj, msg) => parent.info({ ...ctx, ...(typeof obj === 'string' ? { msg: obj } : obj) }, msg),
75
- warn: (obj, msg) => parent.warn({ ...ctx, ...(typeof obj === 'string' ? { msg: obj } : obj) }, msg),
76
- error: (obj, msg) => parent.error({ ...ctx, ...(typeof obj === 'string' ? { msg: obj } : obj) }, msg),
77
- debug: () => {},
78
- };
79
- },
80
- };
81
-
82
- let _reqCounter = 0;
83
-
84
- /**
85
- * Create a child logger bound to a specific HTTP request.
86
- * Every log line from this child includes method, url, requestId.
87
- */
88
- function createRequestLogger(req) {
89
- const requestId =
90
- req.headers['x-request-id'] || `req-${(++_reqCounter).toString().padStart(6, '0')}`;
91
- return forensicLog.child({
92
- requestId,
93
- method: req.method,
94
- url: req.url,
95
- remoteAddr: req.socket && req.socket.remoteAddress,
96
- });
97
- }
98
-
99
- /**
100
- * Log an SSE connection lifecycle event.
101
- */
102
- function logSseEvent(event, data) {
103
- forensicLog.info({ sseEvent: event, ...data }, `[sse] ${event}`);
104
- }
105
-
106
- /**
107
- * Log a task dispatch event (agent → daemon).
108
- */
109
- function logTaskDispatch(event, data) {
110
- forensicLog.info({ dispatchEvent: event, ...data }, `[dispatch] ${event}`);
111
- }
112
-
113
- module.exports = { forensicLog, createRequestLogger, logSseEvent, logTaskDispatch };