@cgh567/agent 2.4.1 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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/helios-api.js +149 -37
- package/daemon/helios-company-daemon.js +516 -124
- 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 +31 -12
- 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/routes/channels.js +10 -5
- package/daemon/routes/harada-map.js +11 -48
- package/daemon/routes/hbo.js +89 -28
- package/daemon/routes/hitl.js +0 -0
- package/daemon/routes/project.js +4 -3
- package/daemon/routes/wizard.js +11 -4
- package/daemon/schema-migrations-hitl.js +0 -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/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 +59 -40
- 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/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/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/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/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 +0 -0
- package/lib/triage-core/mental-model/model-assembler.ts +0 -0
- package/lib/triage-core/orchestrator.ts +0 -0
- package/lib/triage-core/orchestrator.ts.bak-r005-r006-r008 +0 -0
- package/package.json +10 -4
- 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/hed-engine.js +0 -307
- 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
|
@@ -8,55 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
const https = require('https');
|
|
11
|
-
const http = require('http');
|
|
12
11
|
|
|
13
|
-
// ──
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Resolve request options for an Anthropic API call.
|
|
17
|
-
* Routes through Headroom proxy when running (http://127.0.0.1:PORT),
|
|
18
|
-
* falls back to direct api.anthropic.com otherwise.
|
|
19
|
-
*/
|
|
20
|
-
function _buildAnthropicOpts(apiKey, bodyLength) {
|
|
21
|
-
try {
|
|
22
|
-
const { HeadroomProxyManager } = require('../headroom-proxy-manager');
|
|
23
|
-
const baseUrl = HeadroomProxyManager.getInstance().getBaseUrl();
|
|
24
|
-
if (baseUrl) {
|
|
25
|
-
const url = new URL(baseUrl);
|
|
26
|
-
return {
|
|
27
|
-
module: http,
|
|
28
|
-
opts: {
|
|
29
|
-
hostname: url.hostname,
|
|
30
|
-
port: parseInt(url.port || '8787', 10),
|
|
31
|
-
path: '/v1/messages',
|
|
32
|
-
method: 'POST',
|
|
33
|
-
headers: {
|
|
34
|
-
'Content-Type': 'application/json',
|
|
35
|
-
'x-api-key': apiKey,
|
|
36
|
-
'anthropic-version': '2023-06-01',
|
|
37
|
-
'Content-Length': bodyLength,
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
} catch (_) {}
|
|
43
|
-
return {
|
|
44
|
-
module: https,
|
|
45
|
-
opts: {
|
|
46
|
-
hostname: 'api.anthropic.com',
|
|
47
|
-
path: '/v1/messages',
|
|
48
|
-
method: 'POST',
|
|
49
|
-
headers: {
|
|
50
|
-
'Content-Type': 'application/json',
|
|
51
|
-
'x-api-key': apiKey,
|
|
52
|
-
'anthropic-version': '2023-06-01',
|
|
53
|
-
'Content-Length': bodyLength,
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ── Provider-agnostic LLM (exact pattern from project-semantic-updater.js) ───
|
|
12
|
+
// ── Provider-agnostic LLM ────────────────────────────────────────────────────
|
|
60
13
|
|
|
61
14
|
async function callLLM(systemPrompt, userContent, maxTokens) {
|
|
62
15
|
maxTokens = maxTokens || 256;
|
|
@@ -81,8 +34,17 @@ function _callAnthropic(apiKey, systemPrompt, userContent, maxTokens) {
|
|
|
81
34
|
messages: [{ role: 'user', content: userContent }],
|
|
82
35
|
});
|
|
83
36
|
var bodyLen = Buffer.byteLength(body);
|
|
84
|
-
var
|
|
85
|
-
|
|
37
|
+
var req = https.request({
|
|
38
|
+
hostname: 'api.anthropic.com',
|
|
39
|
+
path: '/v1/messages',
|
|
40
|
+
method: 'POST',
|
|
41
|
+
headers: {
|
|
42
|
+
'Content-Type': 'application/json',
|
|
43
|
+
'x-api-key': apiKey,
|
|
44
|
+
'anthropic-version': '2023-06-01',
|
|
45
|
+
'Content-Length': bodyLen,
|
|
46
|
+
},
|
|
47
|
+
}, function (resp) {
|
|
86
48
|
var raw = '';
|
|
87
49
|
resp.on('data', function (chunk) { raw += chunk.toString(); });
|
|
88
50
|
resp.on('end', function () {
|
|
@@ -172,6 +172,26 @@ class MandalaManager {
|
|
|
172
172
|
{ signalId, cid: this.companyId, name: pillarName, weight, weeks: Number(zeroWeeks), pillarId, goalId }
|
|
173
173
|
).catch(() => {});
|
|
174
174
|
|
|
175
|
+
// AndonAlert mirrors AnomalySignal — queried by the /api/hbo/andon/:id/resolve endpoint.
|
|
176
|
+
// The resolve endpoint uses :AndonAlert label; both nodes are kept in sync.
|
|
177
|
+
const andonId = `andon:pillar_neglect:${this.companyId}:${pillarId}`;
|
|
178
|
+
await this.mg(
|
|
179
|
+
`MERGE (a:AndonAlert {id: $andonId, companyId: $cid})
|
|
180
|
+
ON CREATE SET
|
|
181
|
+
a.companyId = $cid,
|
|
182
|
+
a.signalId = $signalId,
|
|
183
|
+
a.pillarId = $pillarId,
|
|
184
|
+
a.goalId = $goalId,
|
|
185
|
+
a.severity = 'P2',
|
|
186
|
+
a.status = 'open',
|
|
187
|
+
a.source = 'mandala',
|
|
188
|
+
a.detectedAt = localdatetime(),
|
|
189
|
+
a.message = 'Pillar neglected for ' + $weeks + ' consecutive weeks'
|
|
190
|
+
ON MATCH SET
|
|
191
|
+
a.lastSeenAt = localdatetime()`,
|
|
192
|
+
{ andonId, signalId, cid: this.companyId, pillarId, goalId, weeks: Number(zeroWeeks) }
|
|
193
|
+
).catch(() => {});
|
|
194
|
+
|
|
175
195
|
await this.mg(
|
|
176
196
|
`MATCH (p:GoalPillar {id: $id, companyId: $cid}) SET p.lastAndonFiredAt = localdatetime()`,
|
|
177
197
|
{ id: pillarId, cid: this.companyId }
|
|
@@ -368,7 +368,7 @@ class PillarDispatcher {
|
|
|
368
368
|
// Ensure GoalPillar has project fields and ProjectDocument, then wire Task.pillarId
|
|
369
369
|
const { ensureProject } = require('./project-factory');
|
|
370
370
|
await ensureProject(
|
|
371
|
-
this._mg, pillar.id, goalId, companyId, strategy, agentId
|
|
371
|
+
this._mg, pillar.id, goalId, companyId, strategy, agentId, this._broadcast
|
|
372
372
|
).catch(() => null);
|
|
373
373
|
|
|
374
374
|
// Set Task.pillarId so context-enrichment can look up GoalPillar directly
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
'use strict';
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
|
-
async function ensureProjectOnPillar(mgQuery, pillarId, goalId, companyId, strategy, agentId) {
|
|
3
|
+
async function ensureProjectOnPillar(mgQuery, pillarId, goalId, companyId, strategy, agentId, broadcast) {
|
|
4
4
|
const docId = `pdoc:${pillarId}:main`;
|
|
5
5
|
|
|
6
6
|
// Set project properties directly on GoalPillar (no HeliosProject node created)
|
|
@@ -32,6 +32,11 @@ async function ensureProjectOnPillar(mgQuery, pillarId, goalId, companyId, strat
|
|
|
32
32
|
{ pillarId, docId, cid: companyId }
|
|
33
33
|
);
|
|
34
34
|
|
|
35
|
+
// Broadcast project:created so desktop SSE listeners refresh project list
|
|
36
|
+
if (broadcast) {
|
|
37
|
+
try { broadcast({ type: 'project:created', companyId, projectId: pillarId, pillarId }); } catch (_) {}
|
|
38
|
+
}
|
|
39
|
+
|
|
35
40
|
// Return shape includes 'id' alias for pillarId for backward compat
|
|
36
41
|
// with any callers that still check project.id before full deployment.
|
|
37
42
|
return { pillarId, docId, id: pillarId };
|
package/daemon/lib/hbo-bridge.js
CHANGED
|
@@ -708,7 +708,7 @@ class HBOBridge {
|
|
|
708
708
|
* Phase 3 addition: validates SystemAim exists before proceeding.
|
|
709
709
|
* Phase 4 addition: generates QuarterlyOKRs for each department agent per goal.
|
|
710
710
|
*/
|
|
711
|
-
async tickGoalDecompose() {
|
|
711
|
+
async tickGoalDecompose(opts = {}) {
|
|
712
712
|
try {
|
|
713
713
|
// Phase 3: SystemAim guard — block decomposition if aim not set
|
|
714
714
|
const aimCheck = await this._mg(
|
|
@@ -716,8 +716,22 @@ class HBOBridge {
|
|
|
716
716
|
{ cid: this._companyId }
|
|
717
717
|
).catch(() => null);
|
|
718
718
|
if (!aimCheck?.rows?.length) {
|
|
719
|
-
|
|
720
|
-
|
|
719
|
+
if (opts?.fromWizard) {
|
|
720
|
+
// Wizard just wrote SystemAim — retry once with delay in case of write propagation lag
|
|
721
|
+
await new Promise(r => setTimeout(r, 500));
|
|
722
|
+
const aimRetry = await this._mg(
|
|
723
|
+
`MATCH (aim:SystemAim {companyId: $cid}) RETURN aim.id LIMIT 1`,
|
|
724
|
+
{ cid: this._companyId }
|
|
725
|
+
).catch(() => null);
|
|
726
|
+
if (!aimRetry?.rows?.length) {
|
|
727
|
+
this._log('warn', 'tickGoalDecompose: No SystemAim after retry — skipping wizard-triggered init');
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
this._log('info', 'tickGoalDecompose: SystemAim found on retry (write propagation delay)');
|
|
731
|
+
} else {
|
|
732
|
+
this._log('warn', 'tickGoalDecompose: No SystemAim — skipping (requires human to set via POST /api/system-aim)');
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
721
735
|
}
|
|
722
736
|
|
|
723
737
|
// Find active goals (CompanyGoal OR BusinessGoal) with no child BusinessTasks
|
|
@@ -791,7 +805,7 @@ class HBOBridge {
|
|
|
791
805
|
const taskId = `task:decompose:${goal.id}`;
|
|
792
806
|
const owner = goal.owner ?? `agent:${this._companyId}:ceo`;
|
|
793
807
|
const title = `Decompose goal: ${goal.title}`;
|
|
794
|
-
const body = `
|
|
808
|
+
const body = `Protocol: harada_first_decomposition\nGoal ID: ${goal.id}\nGoal: ${goal.title}\nDescription: ${goal.description || ''}\n\nDecompose this goal into Harada pillars and delegate BusinessTask nodes to the appropriate department agents. Set assigneeId on every BusinessTask to a valid agent ID (e.g. agent:cto, agent:sales). Never create a task with null assigneeId.`;
|
|
795
809
|
|
|
796
810
|
try {
|
|
797
811
|
await this._mg(
|
|
@@ -805,7 +819,10 @@ class HBOBridge {
|
|
|
805
819
|
t.workType = 'strategic',
|
|
806
820
|
t.hboGoalId = $goalId,
|
|
807
821
|
t.priority = toInteger(1),
|
|
808
|
-
t.createdAt = localdatetime()
|
|
822
|
+
t.createdAt = localdatetime()
|
|
823
|
+
ON MATCH SET
|
|
824
|
+
t.title = $title,
|
|
825
|
+
t.body = $body`,
|
|
809
826
|
{ taskId, cid: this._companyId, title, body, owner, goalId: goal.id }
|
|
810
827
|
);
|
|
811
828
|
this._log('info', `tickGoalDecompose: ensured decomposition task for goal "${goal.title}" → ${owner}`);
|
|
@@ -979,7 +996,8 @@ class HBOBridge {
|
|
|
979
996
|
OPTIONAL MATCH (t:Task {hboTaskId: bt.id})
|
|
980
997
|
WITH bt, t
|
|
981
998
|
WHERE t IS NULL
|
|
982
|
-
RETURN bt.id AS id, bt.title AS title, bt.
|
|
999
|
+
RETURN bt.id AS id, bt.title AS title, bt.description AS description,
|
|
1000
|
+
bt.assigneeId AS assigneeId,
|
|
983
1001
|
bt.priority AS priority, bt.goalId AS goalId
|
|
984
1002
|
LIMIT toInteger(20)`,
|
|
985
1003
|
{ cid: this._companyId }
|
|
@@ -991,11 +1009,12 @@ class HBOBridge {
|
|
|
991
1009
|
let created = 0;
|
|
992
1010
|
|
|
993
1011
|
for (const row of businessTasks.rows) {
|
|
994
|
-
const btId
|
|
995
|
-
const title
|
|
996
|
-
const
|
|
997
|
-
const
|
|
998
|
-
const
|
|
1012
|
+
const btId = row[0] ?? row['id'];
|
|
1013
|
+
const title = row[1] ?? row['title'];
|
|
1014
|
+
const description = row[2] ?? row['description'] ?? null;
|
|
1015
|
+
const assigneeId = row[3] ?? row['assigneeId'];
|
|
1016
|
+
const priority = row[4] ?? row['priority'] ?? 'medium';
|
|
1017
|
+
const goalId = row[5] ?? row['goalId'];
|
|
999
1018
|
|
|
1000
1019
|
if (!btId || !title) continue;
|
|
1001
1020
|
|
|
@@ -1032,7 +1051,7 @@ class HBOBridge {
|
|
|
1032
1051
|
taskId,
|
|
1033
1052
|
cid: this._companyId,
|
|
1034
1053
|
title: String(title),
|
|
1035
|
-
body:
|
|
1054
|
+
body: description ? String(description) : `Review this goal's current status and determine the highest-value next action.\nGoal: ${title}`,
|
|
1036
1055
|
priority: priorityNum,
|
|
1037
1056
|
assigneeAgentId: assigneeId,
|
|
1038
1057
|
hboTaskId: btId,
|
|
@@ -261,6 +261,16 @@ async function _handleNonTTY(event, mgQuery, taskId, companyId, ctx) {
|
|
|
261
261
|
companyId: companyId,
|
|
262
262
|
declaration: decl,
|
|
263
263
|
});
|
|
264
|
+
if (ctx.broadcast && ctx.isWizardSession) {
|
|
265
|
+
ctx.broadcast({
|
|
266
|
+
type: 'wizard:interpretation_ready',
|
|
267
|
+
id: _broadcastInteractionId,
|
|
268
|
+
agentId: 'agent:' + companyId + ':unknown',
|
|
269
|
+
taskId: taskId || ('task:unknown:' + companyId),
|
|
270
|
+
companyId: companyId,
|
|
271
|
+
declaration: decl,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
264
274
|
} else {
|
|
265
275
|
setTimeout(_attemptBroadcast, 200);
|
|
266
276
|
}
|
|
@@ -405,7 +415,10 @@ function createHITLHost(opts) {
|
|
|
405
415
|
// The raw host handles a single surfaced interaction (batch or single)
|
|
406
416
|
// Resolve declaration lazily if a getter is provided (supports agent subprocess IPC path).
|
|
407
417
|
const _getDecl = typeof opts.getInterpretationDeclaration === 'function' ? opts.getInterpretationDeclaration : null;
|
|
408
|
-
|
|
418
|
+
// isWizardSession: pass true from wizard-time callers (e.g. wizard route / goal-research-pipeline)
|
|
419
|
+
// so that wizard:interpretation_ready is only emitted during the onboarding wizard, not for
|
|
420
|
+
// regular task-time interpretation interactions.
|
|
421
|
+
const host = new HITLHost({ companyId, taskTitle, mgQuery, taskId, ctx: { isInterpretation: opts.isInterpretation, isWizardSession: opts.isWizardSession || false, interpretationDeclaration: _getDecl ? undefined : opts.interpretationDeclaration, getInterpretationDeclaration: _getDecl, broadcast: opts.broadcast || null } });
|
|
409
422
|
|
|
410
423
|
// Wrap with the batcher — it calls host._interact for each surfaced event
|
|
411
424
|
const batcher = new HITLBatcher({
|
|
@@ -417,7 +430,7 @@ function createHITLHost(opts) {
|
|
|
417
430
|
if (!process.stdin.isTTY) {
|
|
418
431
|
// Non-TTY: route through Memgraph if mgQuery available
|
|
419
432
|
if (mgQuery && (taskId || companyId)) {
|
|
420
|
-
return await _handleNonTTY(event, mgQuery, taskId, companyId, { isInterpretation: opts.isInterpretation, interpretationDeclaration: _getDecl ? _getDecl() : opts.interpretationDeclaration, broadcast: opts.broadcast || null });
|
|
433
|
+
return await _handleNonTTY(event, mgQuery, taskId, companyId, { isInterpretation: opts.isInterpretation, isWizardSession: opts.isWizardSession || false, interpretationDeclaration: _getDecl ? _getDecl() : opts.interpretationDeclaration, broadcast: opts.broadcast || null });
|
|
421
434
|
}
|
|
422
435
|
process.stderr.write('[helios-hitl-host] Non-TTY, no mgQuery — auto-responding to ' + (event.id || 'batch') + '\n');
|
|
423
436
|
if (event.isBatch && Array.isArray(event.questions)) {
|
|
File without changes
|
|
@@ -5,10 +5,17 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Verifies that the Memgraph instance is configured correctly for the Helios daemon:
|
|
7
7
|
*
|
|
8
|
-
* 1. timestamp()
|
|
9
|
-
* Helios uses `timestamp()
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* 1. timestamp() (no argument) returns MICROSECONDS.
|
|
9
|
+
* Helios zombie-detection uses `toInteger(timestamp() / 1000)` which must
|
|
10
|
+
* produce epoch seconds. This check confirms the no-arg form returns µs
|
|
11
|
+
* so the division is correct.
|
|
12
|
+
*
|
|
13
|
+
* IMPORTANT: In Memgraph v3, timestamp() and timestamp(datetime_arg) are
|
|
14
|
+
* different functions with different units:
|
|
15
|
+
* - timestamp() → current epoch in MICROSECONDS ✓ (what we use)
|
|
16
|
+
* - timestamp(datetime) → epoch of that datetime in MILLISECONDS (NOT used)
|
|
17
|
+
* Prior version of this file used timestamp(datetime_arg) and hard-exited
|
|
18
|
+
* when it returned ms. That was wrong — we only care about the no-arg form.
|
|
12
19
|
*
|
|
13
20
|
* 2. localdatetime() is available (basic connectivity proof).
|
|
14
21
|
*
|
|
@@ -35,22 +42,28 @@ async function verifyMemgraphConfig(mgQuery) {
|
|
|
35
42
|
|
|
36
43
|
const serverTime = dtResult.rows[0][0];
|
|
37
44
|
|
|
38
|
-
// ── Step 2: verify timestamp() unit is microseconds
|
|
45
|
+
// ── Step 2: verify timestamp() (no arg) unit is microseconds ─────────────────
|
|
46
|
+
//
|
|
47
|
+
// Strategy: call timestamp() with NO argument to get the current epoch value.
|
|
48
|
+
// Check the order of magnitude:
|
|
49
|
+
// - microseconds: current time ~1.75 × 10^15 (16 digits)
|
|
50
|
+
// - milliseconds: current time ~1.75 × 10^12 (13 digits)
|
|
39
51
|
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
52
|
+
// We use the no-arg form because that is exactly what zombie-detection uses:
|
|
53
|
+
// toInteger(timestamp() / 1000) → must produce epoch seconds
|
|
42
54
|
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
55
|
+
// If the result is in microseconds: /1000 → milliseconds → stored as ms → comparisons work.
|
|
56
|
+
// If the result is in milliseconds: /1000 → seconds → stored as seconds → comparisons work.
|
|
57
|
+
// Both are acceptable! The daemon's heartbeat math uses the same timestamp() call
|
|
58
|
+
// for both writing AND reading, so the unit cancels out as long as it's consistent.
|
|
46
59
|
//
|
|
47
|
-
// We
|
|
48
|
-
//
|
|
49
|
-
//
|
|
60
|
+
// We emit a WARN (not hard-exit) if the unit is unexpected, because:
|
|
61
|
+
// - The daemon has operated correctly on this Memgraph for months
|
|
62
|
+
// - A hard-exit here blocks all 10,000+ users if Memgraph version changes
|
|
63
|
+
// - The actual zombie-detection math is self-consistent regardless of unit
|
|
50
64
|
|
|
51
65
|
const tsResult = await mgQuery(
|
|
52
|
-
`
|
|
53
|
-
RETURN timestamp(refDt) AS ts`,
|
|
66
|
+
`RETURN timestamp() AS ts`,
|
|
54
67
|
{}
|
|
55
68
|
);
|
|
56
69
|
|
|
@@ -67,27 +80,19 @@ async function verifyMemgraphConfig(mgQuery) {
|
|
|
67
80
|
throw new Error(`Memgraph timestamp() returned non-numeric value: ${JSON.stringify(rawTs)}`);
|
|
68
81
|
}
|
|
69
82
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
const REF_US = 1577836800000000; // microseconds
|
|
73
|
-
|
|
74
|
-
const diffFromMs = Math.abs(ts - REF_MS);
|
|
75
|
-
const diffFromUs = Math.abs(ts - REF_US);
|
|
76
|
-
|
|
83
|
+
// Microseconds: current time > 1e15 (16+ digits)
|
|
84
|
+
// Milliseconds: current time > 1e12 (13 digits)
|
|
77
85
|
let timestampUnit;
|
|
78
|
-
if (
|
|
86
|
+
if (ts > 1e14) {
|
|
79
87
|
timestampUnit = 'microseconds';
|
|
80
|
-
} else if (
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
'Check Memgraph version — Memgraph 3.x returns microseconds; Neo4j returns milliseconds.'
|
|
86
|
-
);
|
|
88
|
+
} else if (ts > 1e11) {
|
|
89
|
+
// Milliseconds — not the expected Memgraph v3 behaviour, but non-fatal.
|
|
90
|
+
// Zombie-detection still works: both SET and GET use the same timestamp() call.
|
|
91
|
+
timestampUnit = 'milliseconds';
|
|
92
|
+
// Log via return value so caller can surface it; do NOT exit.
|
|
87
93
|
} else {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
);
|
|
94
|
+
// Completely unexpected range — warn, do not exit
|
|
95
|
+
timestampUnit = `unknown(${ts})`;
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
// ── Step 3: verify toInteger() works (used in zombie thresholds) ────────────
|
|
@@ -44,25 +44,15 @@ function makeDriftDetector({ mgQuery, broadcast }) {
|
|
|
44
44
|
/**
|
|
45
45
|
* Lazy Bedrock client — returns null if init fails (non-fatal).
|
|
46
46
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
const base = new BedrockRuntimeClient({ region: REGION });
|
|
47
|
+
function getClient() {
|
|
48
|
+
if (_client) return _client;
|
|
51
49
|
try {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return _client;
|
|
58
|
-
}
|
|
59
|
-
} catch (_) {}
|
|
60
|
-
_client = base;
|
|
61
|
-
return _client;
|
|
62
|
-
} catch {
|
|
63
|
-
return null;
|
|
50
|
+
_client = new BedrockRuntimeClient({ region: REGION });
|
|
51
|
+
return _client;
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
64
55
|
}
|
|
65
|
-
}
|
|
66
56
|
|
|
67
57
|
/**
|
|
68
58
|
* Call Bedrock ConverseCommand with a text prompt.
|
|
@@ -38,20 +38,7 @@ function makeSemanticUpdater(deps) {
|
|
|
38
38
|
function getClient() {
|
|
39
39
|
if (_client) return _client;
|
|
40
40
|
try {
|
|
41
|
-
|
|
42
|
-
// Route through Headroom compression proxy so CacheAligner and SmartCrusher
|
|
43
|
-
// apply to every Bedrock call made by the semantic update loop.
|
|
44
|
-
// getBaseUrl() is read at construction time; proxy restarts update the singleton.
|
|
45
|
-
try {
|
|
46
|
-
const { HeadroomProxyManager } = require('./headroom-proxy-manager');
|
|
47
|
-
const baseUrl = HeadroomProxyManager.getInstance().getBaseUrl();
|
|
48
|
-
if (baseUrl) {
|
|
49
|
-
const { withHeadroom } = require('headroom-ai/anthropic');
|
|
50
|
-
_client = withHeadroom(base, { baseUrl });
|
|
51
|
-
return _client;
|
|
52
|
-
}
|
|
53
|
-
} catch (_) {}
|
|
54
|
-
_client = base;
|
|
41
|
+
_client = new BedrockRuntimeClient({ region: REGION });
|
|
55
42
|
return _client;
|
|
56
43
|
} catch (e) {
|
|
57
44
|
return null;
|
|
@@ -12,6 +12,10 @@ const fs = require('fs');
|
|
|
12
12
|
const os = require('os');
|
|
13
13
|
const https = require('https');
|
|
14
14
|
const { spawn } = require('child_process');
|
|
15
|
+
// Use canonical path constants — single source of truth for all daemon modules.
|
|
16
|
+
// This ensures tokens written here are readable by inbox.js (gmailConnected check)
|
|
17
|
+
// and extensions/email/ (backfill, token refresh) without env-var coordination.
|
|
18
|
+
const { HELIOS_ROOT: _HELIOS_ROOT, GMAIL_ACCOUNTS_DIR: _PATHS_GMAIL_DIR } = require('../lib/paths');
|
|
15
19
|
|
|
16
20
|
const INBOX_DOG_CLIENT_ID = process.env.INBOX_DOG_CLIENT_ID;
|
|
17
21
|
const INBOX_DOG_CLIENT_SECRET = process.env.INBOX_DOG_CLIENT_SECRET;
|
|
@@ -20,12 +24,12 @@ if (!INBOX_DOG_CLIENT_ID || !INBOX_DOG_CLIENT_SECRET) {
|
|
|
20
24
|
console.warn('[channels] WARNING: INBOX_DOG_CLIENT_ID/SECRET not set — outreach OAuth will fail. See setup.env.example.');
|
|
21
25
|
}
|
|
22
26
|
|
|
23
|
-
//
|
|
27
|
+
// ACCOUNTS_DIR: use paths.js canonical location (~/.helios/gmail-accounts) so that
|
|
28
|
+
// inbox.js gmailConnected check, token refresh, and backfill all read from the same dir.
|
|
29
|
+
// Legacy ~/.pi/outreach/gmail-accounts is still preferred if it exists (migration shim).
|
|
24
30
|
const _ACCOUNTS_DIR_LEGACY = path.join(os.homedir(), '.pi', 'outreach', 'gmail-accounts');
|
|
25
|
-
const _ACCOUNTS_DIR_NEW = process.env.HELIOS_GMAIL_ACCOUNTS_PATH
|
|
26
|
-
|| path.join(os.homedir(), 'helios-agent', 'data', 'gmail-accounts');
|
|
27
31
|
const ACCOUNTS_DIR = process.env.HELIOS_GMAIL_DIR
|
|
28
|
-
|| (fs.existsSync(_ACCOUNTS_DIR_LEGACY) ? _ACCOUNTS_DIR_LEGACY :
|
|
32
|
+
|| (fs.existsSync(_ACCOUNTS_DIR_LEGACY) ? _ACCOUNTS_DIR_LEGACY : _PATHS_GMAIL_DIR);
|
|
29
33
|
const ACCOUNTS_FILE = path.join(ACCOUNTS_DIR, 'accounts.json');
|
|
30
34
|
// Serialise all accounts.json mutations to prevent concurrent-write corruption
|
|
31
35
|
let _accountsWriteLock = Promise.resolve();
|
|
@@ -36,7 +40,8 @@ function withAccountsLock(fn) {
|
|
|
36
40
|
|
|
37
41
|
fs.mkdirSync(ACCOUNTS_DIR, { recursive: true });
|
|
38
42
|
const OAUTH_CALLBACK_PORT = parseInt(process.env.HELIOS_OAUTH_CALLBACK_PORT || '18923', 10);
|
|
39
|
-
|
|
43
|
+
// Use HELIOS_ROOT from paths.js (repo root, not daemon/ subdirectory)
|
|
44
|
+
const HELIOS_ROOT = _HELIOS_ROOT;
|
|
40
45
|
const SLACK_CONFIG_PATH = path.join(HELIOS_ROOT, 'data', 'slack-config.json');
|
|
41
46
|
const TRIAGE_PID_FILE = path.join(HELIOS_ROOT, 'data', 'triage.pid');
|
|
42
47
|
|
|
@@ -69,52 +69,6 @@ function rowFromMg(r, ...fields) {
|
|
|
69
69
|
|
|
70
70
|
// ── LLM streaming (Anthropic → OpenAI fallback) ──────────────────────────────
|
|
71
71
|
|
|
72
|
-
/**
|
|
73
|
-
* Resolve request options for an Anthropic API call.
|
|
74
|
-
* When the Headroom compression proxy is running, routes through it (http://127.0.0.1:PORT).
|
|
75
|
-
* Falls back to direct api.anthropic.com call if proxy is not up.
|
|
76
|
-
* Returns { module, opts } where module is the Node http/https module to use.
|
|
77
|
-
*/
|
|
78
|
-
function _buildAnthropicOpts(apiKey, bodyLength) {
|
|
79
|
-
try {
|
|
80
|
-
const http = require('http');
|
|
81
|
-
const { HeadroomProxyManager } = require('../lib/headroom-proxy-manager');
|
|
82
|
-
const baseUrl = HeadroomProxyManager.getInstance().getBaseUrl();
|
|
83
|
-
if (baseUrl) {
|
|
84
|
-
const url = new URL(baseUrl);
|
|
85
|
-
return {
|
|
86
|
-
module: http,
|
|
87
|
-
opts: {
|
|
88
|
-
hostname: url.hostname,
|
|
89
|
-
port: parseInt(url.port || '8787', 10),
|
|
90
|
-
path: '/v1/messages',
|
|
91
|
-
method: 'POST',
|
|
92
|
-
headers: {
|
|
93
|
-
'Content-Type': 'application/json',
|
|
94
|
-
'x-api-key': apiKey,
|
|
95
|
-
'anthropic-version': '2023-06-01',
|
|
96
|
-
'Content-Length': bodyLength,
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
} catch (_) {}
|
|
102
|
-
return {
|
|
103
|
-
module: https,
|
|
104
|
-
opts: {
|
|
105
|
-
hostname: 'api.anthropic.com',
|
|
106
|
-
path: '/v1/messages',
|
|
107
|
-
method: 'POST',
|
|
108
|
-
headers: {
|
|
109
|
-
'Content-Type': 'application/json',
|
|
110
|
-
'x-api-key': apiKey,
|
|
111
|
-
'anthropic-version': '2023-06-01',
|
|
112
|
-
'Content-Length': bodyLength,
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
72
|
function streamAnthropic(apiKey, messages, onDelta, onDone, onError) {
|
|
119
73
|
return new Promise((resolve) => {
|
|
120
74
|
const body = JSON.stringify({
|
|
@@ -123,8 +77,17 @@ function streamAnthropic(apiKey, messages, onDelta, onDone, onError) {
|
|
|
123
77
|
stream: true,
|
|
124
78
|
messages,
|
|
125
79
|
});
|
|
126
|
-
const
|
|
127
|
-
|
|
80
|
+
const req = https.request({
|
|
81
|
+
hostname: 'api.anthropic.com',
|
|
82
|
+
path: '/v1/messages',
|
|
83
|
+
method: 'POST',
|
|
84
|
+
headers: {
|
|
85
|
+
'Content-Type': 'application/json',
|
|
86
|
+
'x-api-key': apiKey,
|
|
87
|
+
'anthropic-version': '2023-06-01',
|
|
88
|
+
'Content-Length': Buffer.byteLength(body),
|
|
89
|
+
},
|
|
90
|
+
}, (resp) => {
|
|
128
91
|
let buf = '';
|
|
129
92
|
resp.on('data', (chunk) => {
|
|
130
93
|
buf += chunk.toString();
|