@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
package/daemon/routes/hbo.js
CHANGED
|
@@ -14,6 +14,7 @@ const { randomUUID } = crypto;
|
|
|
14
14
|
const { HELIOS_ROOT } = require('../lib/paths');
|
|
15
15
|
const { CompanyBeliefService } = require('../lib/company-belief-service');
|
|
16
16
|
const { CompanyBeliefDiscovery } = require('../lib/company-belief-discovery');
|
|
17
|
+
const hboStore = require('../../lib/hbo-core-store');
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
const HBO_ROOT = path.resolve(__dirname, '../../skills/helios-business-operator');
|
|
@@ -648,20 +649,42 @@ async function handleGetMandala(req, res, ctx) {
|
|
|
648
649
|
// Build the pillar MATCH differently depending on whether goalId is provided.
|
|
649
650
|
// Memgraph requires extracting all properties into aliases BEFORE OPTIONAL MATCH;
|
|
650
651
|
// referencing gp.property AFTER OPTIONAL MATCH causes "Unbound variable: gp."
|
|
651
|
-
const pillarQuery = goalId
|
|
652
|
-
? `MATCH (gp:GoalPillar {companyId: $cid, goalId: $goalId})
|
|
653
|
-
WITH gp.id AS id, gp.name AS name, gp.pillarIndex AS pillarIndex,
|
|
654
|
-
gp.goalId AS goalId, gp.description AS description,
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
652
|
+
const pillarQuery = goalId
|
|
653
|
+
? `MATCH (gp:GoalPillar {companyId: $cid, goalId: $goalId})
|
|
654
|
+
WITH gp.id AS id, gp.name AS name, gp.pillarIndex AS pillarIndex,
|
|
655
|
+
gp.goalId AS goalId, gp.description AS description,
|
|
656
|
+
coalesce(gp.l2ReviewStatus, null) AS l2ReviewStatus,
|
|
657
|
+
coalesce(gp.l2Strategy, null) AS l2Strategy,
|
|
658
|
+
gp AS gpNode
|
|
659
|
+
OPTIONAL MATCH (gpNode)-[:HAS_CELL]->(ac:ActionCell)
|
|
660
|
+
OPTIONAL MATCH (gpNode)<-[:FOR_PILLAR]-(approval:Approval)
|
|
661
|
+
WHERE approval.type IN ['harada_l2_review','harada_l3_review'] AND approval.status = 'pending'
|
|
662
|
+
WITH id, name, pillarIndex, goalId, description, l2ReviewStatus, l2Strategy, gpNode,
|
|
663
|
+
collect(DISTINCT {id: ac.id, description: ac.description, status: ac.status, cellIndex: ac.cellIndex}) AS cells,
|
|
664
|
+
count(DISTINCT approval) AS openReviewCount
|
|
665
|
+
WITH id, name, pillarIndex, goalId, description, l2ReviewStatus, l2Strategy, cells, openReviewCount,
|
|
666
|
+
size(cells) AS cellCount,
|
|
667
|
+
reduce(acc = 0, c IN cells | acc + CASE WHEN c.status = 'closed' THEN 1 ELSE 0 END) AS closedCellCount
|
|
668
|
+
RETURN id, name, pillarIndex, goalId, description, l2ReviewStatus, l2Strategy,
|
|
669
|
+
cells, openReviewCount, cellCount, closedCellCount
|
|
670
|
+
ORDER BY pillarIndex ASC LIMIT toInteger(8)`
|
|
671
|
+
: `MATCH (gp:GoalPillar {companyId: $cid})
|
|
672
|
+
WITH gp.id AS id, gp.name AS name, gp.pillarIndex AS pillarIndex,
|
|
673
|
+
gp.goalId AS goalId, gp.description AS description,
|
|
674
|
+
coalesce(gp.l2ReviewStatus, null) AS l2ReviewStatus,
|
|
675
|
+
coalesce(gp.l2Strategy, null) AS l2Strategy,
|
|
676
|
+
gp AS gpNode
|
|
677
|
+
OPTIONAL MATCH (gpNode)-[:HAS_CELL]->(ac:ActionCell)
|
|
678
|
+
OPTIONAL MATCH (gpNode)<-[:FOR_PILLAR]-(approval:Approval)
|
|
679
|
+
WHERE approval.type IN ['harada_l2_review','harada_l3_review'] AND approval.status = 'pending'
|
|
680
|
+
WITH id, name, pillarIndex, goalId, description, l2ReviewStatus, l2Strategy, gpNode,
|
|
681
|
+
collect(DISTINCT {id: ac.id, description: ac.description, status: ac.status, cellIndex: ac.cellIndex}) AS cells,
|
|
682
|
+
count(DISTINCT approval) AS openReviewCount
|
|
683
|
+
WITH id, name, pillarIndex, goalId, description, l2ReviewStatus, l2Strategy, cells, openReviewCount,
|
|
684
|
+
size(cells) AS cellCount,
|
|
685
|
+
reduce(acc = 0, c IN cells | acc + CASE WHEN c.status = 'closed' THEN 1 ELSE 0 END) AS closedCellCount
|
|
686
|
+
RETURN id, name, pillarIndex, goalId, description, l2ReviewStatus, l2Strategy,
|
|
687
|
+
cells, openReviewCount, cellCount, closedCellCount
|
|
665
688
|
ORDER BY pillarIndex ASC LIMIT toInteger(8)`;
|
|
666
689
|
|
|
667
690
|
const saQuery = agentId
|
|
@@ -715,7 +738,20 @@ async function handleGetMandala(req, res, ctx) {
|
|
|
715
738
|
mgQuery(deptAgentQuery, { cid }).catch(() => ({ rows: [], keys: [] })),
|
|
716
739
|
]);
|
|
717
740
|
|
|
718
|
-
|
|
741
|
+
// Map pillar names to department slugs — GoalPillar nodes never have a .department
|
|
742
|
+
// property written to them, so we derive the slug from the pillar's canonical name.
|
|
743
|
+
const PILLAR_NAME_TO_DEPT_SLUG = {
|
|
744
|
+
'Revenue & Sales': 'sales',
|
|
745
|
+
'Product & Delivery': 'engineering',
|
|
746
|
+
'Customer Success': 'customer_success',
|
|
747
|
+
'Technology & Infrastructure': 'engineering',
|
|
748
|
+
'Team & Capability': 'people',
|
|
749
|
+
'Brand & Positioning': 'marketing',
|
|
750
|
+
'Finance & Sustainability': 'finance',
|
|
751
|
+
'Operations & Process': 'operations',
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
const pillarKeys = ['id', 'name', 'pillarIndex', 'goalId', 'description', 'l2ReviewStatus', 'l2Strategy', 'cells', 'openReviewCount', 'cellCount', 'closedCellCount'];
|
|
719
755
|
const saKeys = ['id', 'agentId', 'strengths', 'weaknesses', 'opportunities', 'createdAt'];
|
|
720
756
|
const cascadeKeys = ['id', 'agentId', 'weekNumber', 'weeklyGoal', 'dailyTasks', 'completionRate'];
|
|
721
757
|
const deptAgentKeys = ['id', 'role', 'departmentId', 'status', 'currentTaskId', 'currentTask', 'taskStartedAt'];
|
|
@@ -733,7 +769,13 @@ async function handleGetMandala(req, res, ctx) {
|
|
|
733
769
|
});
|
|
734
770
|
|
|
735
771
|
jsonOk(res, {
|
|
736
|
-
pillars: parseRows(pillarResult).map(r =>
|
|
772
|
+
pillars: parseRows(pillarResult).map(r => {
|
|
773
|
+
const p = rowToObj(r, pillarKeys);
|
|
774
|
+
// Derive department slug from pillar name so the frontend can build dept nav items.
|
|
775
|
+
// GoalPillar.department is indexed in the schema but never written by any daemon path.
|
|
776
|
+
p.department = PILLAR_NAME_TO_DEPT_SLUG[p.name] || null;
|
|
777
|
+
return p;
|
|
778
|
+
}),
|
|
737
779
|
selfAnalysis: parseRows(selfAnalysisResult).map(r => rowToObj(r, saKeys)),
|
|
738
780
|
cascades: parseRows(cascadeResult).map(r => rowToObj(r, cascadeKeys)),
|
|
739
781
|
deptAgents, // live BusinessAgent + Task join for DepartmentPage TeamTab
|
|
@@ -1172,13 +1214,20 @@ async function handleCreatePdsa(req, res, ctx) {
|
|
|
1172
1214
|
// in every department's decisions tab regardless of which pillar they belong to.
|
|
1173
1215
|
// Task.pillarId is set by pillar-dispatcher.js when a task is dispatched to a pillar.
|
|
1174
1216
|
try {
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
)
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1217
|
+
// SQLite-first task read for pillarId (P2-10)
|
|
1218
|
+
let pillarId = null;
|
|
1219
|
+
const _storeTask = hboStore.getTask ? hboStore.getTask(taskId.trim(), cid) : null;
|
|
1220
|
+
if (_storeTask) {
|
|
1221
|
+
pillarId = _storeTask.pillarId ?? null;
|
|
1222
|
+
} else {
|
|
1223
|
+
const taskRows = await mgQuery(
|
|
1224
|
+
`MATCH (t:Task {id: $taskId, companyId: $cid}) RETURN t.pillarId AS pillarId LIMIT 1`,
|
|
1225
|
+
{ taskId: taskId.trim(), cid }
|
|
1226
|
+
);
|
|
1227
|
+
pillarId = taskRows && taskRows.rows && taskRows.rows[0]
|
|
1228
|
+
? (Array.isArray(taskRows.rows[0]) ? taskRows.rows[0][0] : taskRows.rows[0].pillarId)
|
|
1229
|
+
: null;
|
|
1230
|
+
}
|
|
1182
1231
|
if (pillarId && typeof pillarId === 'string' && pillarId.trim()) {
|
|
1183
1232
|
await mgQuery(
|
|
1184
1233
|
`MATCH (p:PDSACycle {id: $id, companyId: $cid}) SET p.pillarId = $pillarId`,
|
|
@@ -2126,6 +2175,7 @@ async function handleResolveEmailInfraApproval(req, res, ctx, approvalId) {
|
|
|
2126
2175
|
*/
|
|
2127
2176
|
async function handleGetHboApprovals(req, res, ctx) {
|
|
2128
2177
|
try {
|
|
2178
|
+
if (!ctx.mgQuery) { jsonErr(res, 503, 'Memgraph not connected'); return; }
|
|
2129
2179
|
const url = new URL(req.url, 'http://localhost');
|
|
2130
2180
|
const status = url.searchParams.get('status') || null;
|
|
2131
2181
|
const type = url.searchParams.get('type') || null;
|
|
@@ -2149,6 +2199,17 @@ async function handleGetHboApprovals(req, res, ctx) {
|
|
|
2149
2199
|
agentId: p?.agentId ?? null, taskId: p?.taskId ?? null,
|
|
2150
2200
|
urgency: p?.urgency ?? null, humanAnswer: p?.humanAnswer ?? null,
|
|
2151
2201
|
answeredVia: p?.answeredVia ?? null, createdAt: p?.createdAt ?? null,
|
|
2202
|
+
description: p?.description ?? null,
|
|
2203
|
+
question: p?.question ?? null,
|
|
2204
|
+
defaultAnswer: p?.defaultAnswer ?? null,
|
|
2205
|
+
ruleStrength: p?.ruleStrength ?? null,
|
|
2206
|
+
sourceAgent: p?.sourceAgent ?? null,
|
|
2207
|
+
pillarId: p?.pillarId ?? null,
|
|
2208
|
+
expiresAt: p?.expiresAt ?? null,
|
|
2209
|
+
kaizenProposalId: p?.kaizenProposalId ?? null,
|
|
2210
|
+
department: p?.department ?? null,
|
|
2211
|
+
templateKey: p?.templateKey ?? null,
|
|
2212
|
+
inferredAnswer: p?.inferredAnswer ?? null,
|
|
2152
2213
|
};
|
|
2153
2214
|
});
|
|
2154
2215
|
jsonOk(res, { approvals, count: approvals.length });
|
|
@@ -2338,14 +2399,14 @@ async function handleGetTriageSummary(req, res, ctx) {
|
|
|
2338
2399
|
{ cid }
|
|
2339
2400
|
);
|
|
2340
2401
|
const rows = parseRows(result);
|
|
2341
|
-
const counts = {
|
|
2402
|
+
const counts = { p0Count: 0, p1Count: 0, p2Count: 0, p3Count: 0, total: 0, topItems: [], generatedAt: new Date().toISOString() };
|
|
2342
2403
|
for (const row of rows) {
|
|
2343
2404
|
const pri = (row[0] ?? row['priority'] ?? '').toString().toUpperCase();
|
|
2344
2405
|
const cnt = Number(row[1] ?? row['cnt'] ?? 0);
|
|
2345
|
-
if (pri === 'P0') counts.
|
|
2346
|
-
else if (pri === 'P1') counts.
|
|
2347
|
-
else if (pri === 'P2') counts.
|
|
2348
|
-
else if (pri === 'P3') counts.
|
|
2406
|
+
if (pri === 'P0') counts.p0Count = cnt;
|
|
2407
|
+
else if (pri === 'P1') counts.p1Count = cnt;
|
|
2408
|
+
else if (pri === 'P2') counts.p2Count = cnt;
|
|
2409
|
+
else if (pri === 'P3') counts.p3Count = cnt;
|
|
2349
2410
|
counts.total += cnt;
|
|
2350
2411
|
}
|
|
2351
2412
|
jsonOk(res, counts);
|
package/daemon/routes/hitl.js
CHANGED
|
File without changes
|
package/daemon/routes/project.js
CHANGED
|
@@ -551,13 +551,14 @@ module.exports = function createProjectRoute({ mgQuery, broadcast, invalidateCon
|
|
|
551
551
|
const { answer, answeredBy } = body;
|
|
552
552
|
if (!answer) return jsonErr(res, 400, 'answer required', req);
|
|
553
553
|
try {
|
|
554
|
-
await mgQuery(
|
|
554
|
+
const mgResult = await mgQuery(
|
|
555
555
|
`MATCH (q:ProjectQuestion {id: $qid, projectId: $projId, companyId: $cid})
|
|
556
|
-
SET q.answer=$answer, q.answeredBy=$by, q.answeredAt=datetime(), q.status='answered'
|
|
556
|
+
SET q.answer=$answer, q.answeredBy=$by, q.answeredAt=datetime(), q.status='answered'
|
|
557
|
+
RETURN count(q) AS updated`,
|
|
557
558
|
{ qid, projId: projectId, cid: ctx?.cid || "", answer: String(answer), by: answeredBy || null }
|
|
558
559
|
);
|
|
559
560
|
// D2: if no row matched, task not found for this company
|
|
560
|
-
const _updated =
|
|
561
|
+
const _updated = mgResult?.rows?.[0]?.[0] ?? mgResult?.rows?.[0]?.updated ?? 0;
|
|
561
562
|
if (!Number(_updated)) return jsonErr(res, 404, "Task not found for this company", req);
|
|
562
563
|
jsonOk(res, { ok: true }, req);
|
|
563
564
|
} catch (e) {
|
package/daemon/routes/wizard.js
CHANGED
|
@@ -184,9 +184,16 @@ async function handlePostWizard(req, res, ctx) {
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
//
|
|
187
|
+
// tickGoalDecompose (creates GoalPillar × 8) and wizard:pillars_ready broadcast are
|
|
188
|
+
// now handled in helios-company-daemon.js registerCompany(), which runs after the
|
|
189
|
+
// desktop calls POST /api/daemon/register-company. That is the correct point because:
|
|
190
|
+
// (a) _modulesByCompany is guaranteed to have this company's entry
|
|
191
|
+
// (b) daemon._broadcast is wired before setBroadcast() is called
|
|
192
|
+
// (c) No race with the setImmediate firing before registerCompany completes
|
|
193
|
+
//
|
|
194
|
+
// tickGoalSync bridges any BusinessTask→Task nodes created by the wizard (CEO task).
|
|
195
|
+
// It still runs here because it needs to fire as soon as possible after wizard:done,
|
|
196
|
+
// not after the desktop round-trip to /api/daemon/register-company.
|
|
190
197
|
if (result.companyId && ctx.daemon?._modulesByCompany) {
|
|
191
198
|
setImmediate(async () => {
|
|
192
199
|
try {
|
|
@@ -194,7 +201,7 @@ async function handlePostWizard(req, res, ctx) {
|
|
|
194
201
|
if (mods?.hboBridge?.tickGoalSync) {
|
|
195
202
|
await mods.hboBridge.tickGoalSync();
|
|
196
203
|
}
|
|
197
|
-
} catch (_) { /* non-fatal
|
|
204
|
+
} catch (_) { /* non-fatal */ }
|
|
198
205
|
});
|
|
199
206
|
}
|
|
200
207
|
|
|
File without changes
|
|
File without changes
|
|
@@ -345,35 +345,55 @@ export default function contextCompaction(pi: ExtensionAPI): void {
|
|
|
345
345
|
metrics.compactionTriggered++;
|
|
346
346
|
metrics.lastCompactionAt = Date.now();
|
|
347
347
|
|
|
348
|
-
// ──
|
|
349
|
-
//
|
|
350
|
-
//
|
|
351
|
-
//
|
|
352
|
-
// per content type (JSON arrays → SmartCrusher, prose → Kompress-base).
|
|
353
|
-
// CCR stores originals locally so the agent can retrieve them on demand.
|
|
348
|
+
// ── Helios Compression (L1 + L2 replacement) ────────────────────────
|
|
349
|
+
// Calls the Helios Compression Server (lib/compression/server.ts) via
|
|
350
|
+
// HTTP. The server runs as a sidecar process managed by HeadroomProxyManager.
|
|
351
|
+
// Its URL is injected into the Pi subprocess env as HEADROOM_PROXY_URL.
|
|
354
352
|
//
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
//
|
|
358
|
-
//
|
|
359
|
-
//
|
|
360
|
-
//
|
|
353
|
+
// No npm package required — uses Node's built-in http module.
|
|
354
|
+
// Works identically on Windows and macOS (the server is pure TypeScript).
|
|
355
|
+
//
|
|
356
|
+
// SmartCrusher preserves statistical distribution:
|
|
357
|
+
// Lossless: CSV format for homogeneous arrays (51–84% savings)
|
|
358
|
+
// Lossy: 30% start + 55% importance-scored + 15% end kept;
|
|
359
|
+
// dropped rows stored in CCR and retrievable on demand.
|
|
361
360
|
let headroomApplied = false;
|
|
362
361
|
let headroomTokensSaved = 0;
|
|
363
362
|
|
|
364
363
|
try {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
364
|
+
const baseUrl = process.env.HEADROOM_PROXY_URL || process.env.ANTHROPIC_BASE_URL;
|
|
365
|
+
if (baseUrl && baseUrl.includes('127.0.0.1')) {
|
|
366
|
+
// POST /headroom/compress with the current messages
|
|
367
|
+
const payload = JSON.stringify({ messages });
|
|
368
|
+
const result: any = await new Promise((resolve, reject) => {
|
|
369
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
370
|
+
const http = require('http');
|
|
371
|
+
const url = new URL('/headroom/compress', baseUrl);
|
|
372
|
+
const req = http.request(
|
|
373
|
+
{
|
|
374
|
+
hostname: url.hostname,
|
|
375
|
+
port: parseInt(url.port || '8787', 10),
|
|
376
|
+
path: '/headroom/compress',
|
|
377
|
+
method: 'POST',
|
|
378
|
+
headers: {
|
|
379
|
+
'Content-Type': 'application/json',
|
|
380
|
+
'Content-Length': Buffer.byteLength(payload),
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
(res: any) => {
|
|
384
|
+
let body = '';
|
|
385
|
+
res.on('data', (c: Buffer) => { body += c; });
|
|
386
|
+
res.on('end', () => {
|
|
387
|
+
try { resolve(JSON.parse(body)); }
|
|
388
|
+
catch { reject(new Error('Invalid JSON from compression server')); }
|
|
389
|
+
});
|
|
390
|
+
res.on('error', reject);
|
|
391
|
+
}
|
|
392
|
+
);
|
|
393
|
+
req.setTimeout(5000, () => { req.destroy(); reject(new Error('Compression server timeout')); });
|
|
394
|
+
req.on('error', reject);
|
|
395
|
+
req.write(payload);
|
|
396
|
+
req.end();
|
|
377
397
|
});
|
|
378
398
|
|
|
379
399
|
if (result?.messages?.length) {
|
|
@@ -403,8 +423,7 @@ export default function contextCompaction(pi: ExtensionAPI): void {
|
|
|
403
423
|
}
|
|
404
424
|
}
|
|
405
425
|
} catch (hrErr) {
|
|
406
|
-
// Headroom compress failed —
|
|
407
|
-
// Log prominently and fall through to legacy L1 below.
|
|
426
|
+
// Headroom compress failed — log and fall through to legacy L1.
|
|
408
427
|
process.stderr.write(
|
|
409
428
|
`[context-compaction] ⚠️ Headroom compress error: ${String(hrErr)}\n` +
|
|
410
429
|
`[context-compaction] Falling back to legacy L1 tool result clearing.\n`
|
|
@@ -230,6 +230,11 @@ export async function spreadActivation(
|
|
|
230
230
|
initialActivation: EnrichedActivation,
|
|
231
231
|
graphClient?: { safeRead: (q: string, p?: any) => Promise<any[]> } | null,
|
|
232
232
|
): Promise<EnrichedActivation> {
|
|
233
|
+
const { isMemgraphAvailable } = require('../../lib/graph-availability');
|
|
234
|
+
if (!isMemgraphAvailable()) {
|
|
235
|
+
process.stderr.write('[cortex:activation-bridge] Memgraph unavailable — skipping activation spread\n');
|
|
236
|
+
return {};
|
|
237
|
+
}
|
|
233
238
|
if (!initialActivation.concepts.length) return initialActivation;
|
|
234
239
|
|
|
235
240
|
const mg = graphClient !== undefined ? graphClient : getDefaultMg();
|
|
@@ -154,6 +154,32 @@ try {
|
|
|
154
154
|
}
|
|
155
155
|
} catch (err) { /* fail-open: WAL module init */ if (process.env.HELIOS_DEBUG) console.error(`[cortex-learn] WAL init error: ${String(err)}`); }
|
|
156
156
|
|
|
157
|
+
// Register availability listener so cortex WAL replays automatically
|
|
158
|
+
// when Memgraph comes back up after a period of unavailability (P4-2).
|
|
159
|
+
// _walReplayInProgress guard prevents concurrent replays racing on the journal
|
|
160
|
+
// file when Memgraph flaps rapidly (F13).
|
|
161
|
+
let _walReplayInProgress = false;
|
|
162
|
+
try {
|
|
163
|
+
const { onAvailabilityChange } = require('../../lib/graph-availability');
|
|
164
|
+
onAvailabilityChange((available: boolean) => {
|
|
165
|
+
if (!available || _walReplayInProgress) return;
|
|
166
|
+
_walReplayInProgress = true;
|
|
167
|
+
// Fire-and-forget: WAL replay runs in background, never blocks dispatch
|
|
168
|
+
import('./wal-replay').then(({ replayCortexWal }) => {
|
|
169
|
+
replayCortexWal()
|
|
170
|
+
.catch((e: unknown) =>
|
|
171
|
+
process.stderr.write(`[cortex-learn] WAL replay failed: ${String(e)}\n`)
|
|
172
|
+
)
|
|
173
|
+
.finally(() => { _walReplayInProgress = false; });
|
|
174
|
+
}).catch(() => {
|
|
175
|
+
// wal-replay not available (e.g. pre-build) — silently skip
|
|
176
|
+
_walReplayInProgress = false;
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
} catch (_) {
|
|
180
|
+
// graph-availability not available — WAL replay disabled
|
|
181
|
+
}
|
|
182
|
+
|
|
157
183
|
function _persistCoherenceCounts(): void {
|
|
158
184
|
try {
|
|
159
185
|
const obj: Record<string, number> = {};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -11,7 +11,7 @@ const HIGH_IMPACT_SHARED_BRANCHES = /\b(main|master|dev|development|staging|stag
|
|
|
11
11
|
const HIGH_IMPACT_GIT_OPS = /\b(cherry.?pick|git\s+push|force.?push|git\s+merge|merge\s+(?:to|into)\s+|push\s+to\s+(?:origin|remote|upstream)|git\s+rebase|git\s+reset\s+--hard)\b/i;
|
|
12
12
|
const HIGH_IMPACT_AUTH_CRYPTO = /\b(?:auth|crypto|session|oauth|jwt|encrypt|decrypt|secret|cipher|password)\.(ts|js|tsx|jsx)\b|(?:session|token|secret|password|credential|encrypt|decrypt|hash|cipher|jwt|oauth)\s+(?:file|module|key|management)/i;
|
|
13
13
|
const HIGH_IMPACT_INFRA = /\b(?:(?:run|apply|execute|deploy|push|revert|rollback|sync)\s+migration|schema\s+change|drop\s+table|alter\s+table|environment\s+variable|aws\s+ssm|amplify\s+deploy|\.env\b)\b/i;
|
|
14
|
-
const HIGH_IMPACT_RUNTIME_UPGRADE = /\b(npm\s+install|npm\s+i|npm\s+update|npm\s+up)\b.*(pi-coding-agent|@mariozechner\/pi|@helios-agent\/pi)/i;
|
|
14
|
+
const HIGH_IMPACT_RUNTIME_UPGRADE = /\b(npm\s+install|npm\s+i|npm\s+update|npm\s+up|pnpm\s+add|pnpm\s+install)\b.*(pi-coding-agent|@mariozechner\/pi|@helios-agent\/pi|@cgh567\/cli|@cgh567\/agent|@cgh567\/pi)/i;
|
|
15
15
|
export const SHARED_INFRA_PATHS = /(?:~\/|\/Users\/\w+\/)?\.\.?pi\/agent\/(extensions|skills|agents)\//i;
|
|
16
16
|
|
|
17
17
|
export function isHighImpactOperation(task: string): boolean {
|
|
@@ -28,15 +28,22 @@ import { homedir } from 'os';
|
|
|
28
28
|
|
|
29
29
|
// Ordered list of jiti lib/jiti.cjs locations (lib/ not dist/ - dist requires module.createRequire which is unavailable in CJS context)
|
|
30
30
|
const JITI_PATHS = [
|
|
31
|
+
// ── npm package layout (production: @cgh567/agent installed in node_modules) ──
|
|
32
|
+
// When helios-agent runs from node_modules/@cgh567/agent, jiti is provided by
|
|
33
|
+
// @cgh567/cli which is in the same scope. Walk up to workspace root node_modules.
|
|
34
|
+
// Resolve relative to __dirname (this file's location in extensions/helios-governance/handlers/)
|
|
35
|
+
// node_modules/@cgh567/agent/extensions/helios-governance/handlers/ -> up 6 levels -> workspace root
|
|
36
|
+
join(__dirname, '..', '..', '..', '..', '..', '..', 'node_modules', '@mariozechner', 'jiti', 'lib', 'jiti.cjs'),
|
|
37
|
+
// @cgh567/cli bundles jiti under its own node_modules
|
|
38
|
+
join(__dirname, '..', '..', '..', '..', '..', '..', 'node_modules', '@cgh567', 'cli', 'node_modules', '@mariozechner', 'jiti', 'lib', 'jiti.cjs'),
|
|
39
|
+
// ── local clone layout (dev: ~/Desktop/Helios/helios-agent-main) ──
|
|
40
|
+
join(homedir(), 'Desktop', 'Helios', 'helios-agent-main', 'node_modules', '@mariozechner', 'jiti', 'lib', 'jiti.cjs'),
|
|
41
|
+
join(homedir(), 'helios-agent-main', 'node_modules', '@mariozechner', 'jiti', 'lib', 'jiti.cjs'),
|
|
31
42
|
join(homedir(), 'helios-agent', 'node_modules', '@mariozechner', 'jiti', 'lib', 'jiti.cjs'),
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
'@helios-agent', 'pi-coding-agent', 'node_modules', 'jiti', 'lib', 'jiti.cjs'),
|
|
37
|
-
join(process.env.NVM_DIR || join(homedir(), '.nvm'),
|
|
38
|
-
'versions', 'node', process.version, 'lib', 'node_modules',
|
|
39
|
-
'@earendil-works', 'pi-coding-agent', 'node_modules', 'jiti', 'lib', 'jiti.cjs'),
|
|
43
|
+
// ── HELIOS_ROOT env var override ──
|
|
44
|
+
...(process.env.HELIOS_ROOT
|
|
45
|
+
? [join(process.env.HELIOS_ROOT, 'node_modules', '@mariozechner', 'jiti', 'lib', 'jiti.cjs')]
|
|
46
|
+
: []),
|
|
40
47
|
];
|
|
41
48
|
|
|
42
49
|
/** Module-level jiti instance cache: callerFile → jiti fn */
|
|
File without changes
|
|
@@ -23,6 +23,11 @@ export async function queryGraphMemories(
|
|
|
23
23
|
taskText: string,
|
|
24
24
|
limit: number = 10
|
|
25
25
|
): Promise<GraphMemoryResult[]> {
|
|
26
|
+
const { isMemgraphAvailable } = require('../../lib/graph-availability');
|
|
27
|
+
if (!isMemgraphAvailable()) {
|
|
28
|
+
process.stderr.write('[hema:graph-memory] Memgraph unavailable — skipping graph memory recall\n');
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
26
31
|
try {
|
|
27
32
|
const mg = require('../../lib/safe-memgraph.js');
|
|
28
33
|
|
|
@@ -102,6 +107,11 @@ export async function queryCausalLessons(
|
|
|
102
107
|
taskText: string,
|
|
103
108
|
limit: number = 5
|
|
104
109
|
): Promise<GraphMemoryResult[]> {
|
|
110
|
+
const { isMemgraphAvailable } = require('../../lib/graph-availability');
|
|
111
|
+
if (!isMemgraphAvailable()) {
|
|
112
|
+
process.stderr.write('[hema:graph-memory] Memgraph unavailable — skipping causal lesson recall\n');
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
105
115
|
try {
|
|
106
116
|
const mg = require('../../lib/safe-memgraph.js');
|
|
107
117
|
|
|
@@ -1507,48 +1507,67 @@ export default function hemaDispatchV3(pi: any): void {
|
|
|
1507
1507
|
logHemaEvent('hema_no_context', { nativePacksUsed, legacyUsed: admitted.admitted?.length > 0 });
|
|
1508
1508
|
}
|
|
1509
1509
|
|
|
1510
|
-
// ──
|
|
1511
|
-
// The recall context
|
|
1512
|
-
//
|
|
1513
|
-
//
|
|
1514
|
-
//
|
|
1515
|
-
//
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
const
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1510
|
+
// ── Helios Compression: compress HEMA recall payload before injection ──
|
|
1511
|
+
// The recall context contains JSON graph payloads (leads, signals, tasks,
|
|
1512
|
+
// goals, code nodes). Send the assembled text as a tool_result block to
|
|
1513
|
+
// the compression server — it will find and compress embedded JSON arrays.
|
|
1514
|
+
//
|
|
1515
|
+
// Uses direct HTTP to HEADROOM_PROXY_URL (same pattern as context-compaction.ts)
|
|
1516
|
+
// rather than the headroom-ai npm package so this works in Pi subprocess context.
|
|
1517
|
+
// Applies to all companies: the role injection budgets enforce per-agent limits.
|
|
1518
|
+
if (enrichedTask.length > 2000) {
|
|
1519
|
+
const _hrUrl = process.env.HEADROOM_PROXY_URL;
|
|
1520
|
+
if (_hrUrl) {
|
|
1521
|
+
try {
|
|
1522
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
1523
|
+
const http = require('http');
|
|
1524
|
+
const _payload = JSON.stringify({
|
|
1525
|
+
messages: [{
|
|
1526
|
+
role: 'user',
|
|
1527
|
+
content: [{
|
|
1528
|
+
type: 'tool_result',
|
|
1529
|
+
tool_use_id: 'hema_recall',
|
|
1530
|
+
content: enrichedTask,
|
|
1531
|
+
}],
|
|
1532
|
+
}],
|
|
1533
|
+
});
|
|
1534
|
+
const _result: any = await new Promise((resolve, reject) => {
|
|
1535
|
+
const _url = new URL('/headroom/compress', _hrUrl);
|
|
1536
|
+
const _req = http.request(
|
|
1537
|
+
{
|
|
1538
|
+
hostname: _url.hostname,
|
|
1539
|
+
port: parseInt(_url.port || '8787', 10),
|
|
1540
|
+
path: '/headroom/compress',
|
|
1541
|
+
method: 'POST',
|
|
1542
|
+
headers: {
|
|
1543
|
+
'Content-Type': 'application/json',
|
|
1544
|
+
'Content-Length': Buffer.byteLength(_payload),
|
|
1545
|
+
},
|
|
1546
|
+
},
|
|
1547
|
+
(res: any) => {
|
|
1548
|
+
let body = '';
|
|
1549
|
+
res.on('data', (c: Buffer) => { body += c; });
|
|
1550
|
+
res.on('end', () => { try { resolve(JSON.parse(body)); } catch { reject(new Error('bad json')); } });
|
|
1551
|
+
res.on('error', reject);
|
|
1552
|
+
}
|
|
1553
|
+
);
|
|
1554
|
+
_req.setTimeout(3000, () => { _req.destroy(); reject(new Error('timeout')); });
|
|
1555
|
+
_req.on('error', reject);
|
|
1556
|
+
_req.write(_payload);
|
|
1557
|
+
_req.end();
|
|
1558
|
+
});
|
|
1559
|
+
|
|
1560
|
+
const _compressed = _result?.messages?.[0]?.content?.[0]?.content ?? enrichedTask;
|
|
1561
|
+
if (typeof _compressed === 'string' && _compressed.length < enrichedTask.length) {
|
|
1562
|
+
const _saved = enrichedTask.length - _compressed.length;
|
|
1563
|
+
process.stderr.write(`[hema-dispatch-v3] Headroom compressed recall context: -${_saved} chars (${agentType})\n`);
|
|
1564
|
+
enrichedTask = _compressed;
|
|
1565
|
+
logHemaEvent('hema_headroom_compressed', { saved: _saved, agentType });
|
|
1547
1566
|
}
|
|
1567
|
+
} catch (_hrErr: any) {
|
|
1568
|
+
// Non-fatal: log and continue with uncompressed enrichedTask
|
|
1569
|
+
process.stderr.write(`[hema-dispatch-v3] Headroom recall compress skipped: ${_hrErr?.message}\n`);
|
|
1548
1570
|
}
|
|
1549
|
-
} catch (_hrErr: any) {
|
|
1550
|
-
// Non-fatal: log and continue with uncompressed enrichedTask
|
|
1551
|
-
process.stderr.write(`[hema-dispatch-v3] Headroom recall compress failed (non-fatal): ${_hrErr?.message}\n`);
|
|
1552
1571
|
}
|
|
1553
1572
|
}
|
|
1554
1573
|
|
|
File without changes
|
|
File without changes
|
|
@@ -23,6 +23,17 @@ import { createRequire } from 'module';
|
|
|
23
23
|
const require = createRequire(import.meta.url);
|
|
24
24
|
const QUIET = process.env.PI_QUIET === '1' || process.env.PI_LOG_LEVEL === 'error';
|
|
25
25
|
|
|
26
|
+
// Graph availability singleton — updated whenever Memgraph comes up or goes down
|
|
27
|
+
let _graphAvailability: { setMemgraphAvailable: (v: boolean) => void } | null = null;
|
|
28
|
+
function getGraphAvailability() {
|
|
29
|
+
if (!_graphAvailability) {
|
|
30
|
+
try {
|
|
31
|
+
_graphAvailability = require('../lib/graph-availability');
|
|
32
|
+
} catch (_) {}
|
|
33
|
+
}
|
|
34
|
+
return _graphAvailability;
|
|
35
|
+
}
|
|
36
|
+
|
|
26
37
|
// ── Event emission helpers ───────────────────────────────────────────────────
|
|
27
38
|
|
|
28
39
|
/** Load MAGE query modules after Bolt becomes reachable.
|
|
@@ -109,6 +120,7 @@ function emitMemgraphReady(log: (msg: string) => void): void {
|
|
|
109
120
|
bus.emit('memgraph_ready', payload);
|
|
110
121
|
} catch (e) { process.stderr.write(`[extensions] non-fatal if bus unavailable: ${String(e)}\n`); }
|
|
111
122
|
log(`[memgraph-autostart] Memgraph ready — bolt reachable at ${BOLT_HOST}:${BOLT_PORT}`);
|
|
123
|
+
getGraphAvailability()?.setMemgraphAvailable(true);
|
|
112
124
|
}
|
|
113
125
|
|
|
114
126
|
/**
|
|
@@ -185,6 +197,7 @@ function emitMemgraphDegraded(log: (msg: string) => void, reason: string): void
|
|
|
185
197
|
try {
|
|
186
198
|
bus.emit('memgraph_degraded', payload);
|
|
187
199
|
} catch (e) { process.stderr.write(`[extensions] non-fatal: ${String(e)}\n`); }
|
|
200
|
+
getGraphAvailability()?.setMemgraphAvailable(false);
|
|
188
201
|
}
|
|
189
202
|
|
|
190
203
|
/** Returns true if Memgraph bolt was confirmed reachable during this session. */
|
|
File without changes
|