@cgh567/agent 2.4.1 → 2.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/helios +0 -0
- package/bin/helios-rpc-node-wrapper.cjs +0 -0
- package/bin/helios-rpc-wrapper.sh +0 -0
- package/daemon/adapters/helios-rpc-adapter.js +47 -25
- package/daemon/adapters/tui_wakeup.js +8 -0
- package/daemon/config/com.familiar.helios-daemon.plist +5 -0
- package/daemon/config/helios-daemon.service +4 -0
- package/daemon/context-enrichment.js +59 -21
- package/daemon/daemon-manager.js +1 -1
- package/daemon/db/email-infrastructure-migrate.js +192 -0
- package/daemon/db/hbo-core-migrate.js +189 -0
- package/daemon/helios-api.js +723 -57
- package/daemon/helios-company-daemon.js +616 -134
- package/daemon/lib/harada/cascade-judge.js +12 -50
- package/daemon/lib/harada/mandala.js +20 -0
- package/daemon/lib/harada/pillar-dispatcher.js +1 -1
- package/daemon/lib/harada/project-factory.js +7 -2
- package/daemon/lib/hbo-bridge.js +32 -13
- package/daemon/lib/hed-engine.js +10 -292
- package/daemon/lib/helios-hitl-host.js +15 -2
- package/daemon/lib/hitl-interaction-service.js +0 -0
- package/daemon/lib/memgraph-verify.js +38 -33
- package/daemon/lib/project-drift-detector.js +7 -17
- package/daemon/lib/project-semantic-updater.js +1 -14
- package/daemon/lib/task-completion-processor.js +11 -0
- package/daemon/lib/wizard-engine.js +57 -6
- package/daemon/routes/channels.js +10 -5
- package/daemon/routes/harada-map.js +11 -48
- package/daemon/routes/hbo.js +342 -75
- package/daemon/routes/hitl.js +0 -0
- package/daemon/routes/project.js +194 -62
- package/daemon/routes/routines.js +14 -0
- package/daemon/routes/tasks.js +15 -1
- package/daemon/routes/wizard.js +11 -4
- package/daemon/schema-apply.js +174 -0
- package/daemon/schema-definitions.js +423 -0
- package/daemon/schema-migrations-hbo.js +10 -0
- package/daemon/schema-migrations-hed.js +18 -0
- package/daemon/schema-migrations-hitl.js +0 -0
- package/daemon/schema-migrations-proj.js +131 -0
- package/extensions/001-tool-output-cap.ts +0 -0
- package/extensions/context-compaction.ts +45 -26
- package/extensions/cortex/activation-bridge.ts +5 -0
- package/extensions/cortex/learn.ts +26 -0
- package/extensions/cortex/wal-replay.ts +91 -0
- package/extensions/email/backfill.ts +0 -0
- package/extensions/helios-governance/analysis/ambiguity.ts +0 -0
- package/extensions/helios-governance/analysis/compliance.ts +0 -0
- package/extensions/helios-governance/analysis/long-task-detector.ts +0 -0
- package/extensions/helios-governance/analysis/output-contract.ts +0 -0
- package/extensions/helios-governance/analysis/patterns.ts +0 -0
- package/extensions/helios-governance/analysis/preflight.ts +0 -0
- package/extensions/helios-governance/analysis/recurring-violations.ts +0 -0
- package/extensions/helios-governance/analysis/task-classification.ts +0 -0
- package/extensions/helios-governance/analysis/task-intent.ts +0 -0
- package/extensions/helios-governance/gates/high-impact.ts +1 -1
- package/extensions/helios-governance/handlers/_jiti-require.ts +15 -8
- package/extensions/helios-governance/handlers/proxy-test-detector.ts +0 -0
- package/extensions/hema-dispatch-v3/graph-memory.ts +10 -0
- package/extensions/hema-dispatch-v3/index.ts +72 -47
- package/extensions/lib/elo-engine.js +0 -0
- package/extensions/lib/elo-engine.test.js +0 -0
- package/extensions/memgraph-autostart.ts +13 -0
- package/extensions/neuroplastic-eval.ts +0 -0
- package/extensions/shadow-loop/index.ts +0 -0
- package/extensions/warm-tick/warm-tick-maintenance.ts +8 -0
- package/lib/__tests__/hbo-core-store.test.js +238 -0
- package/lib/brain-v2-budget.js +0 -0
- package/lib/brain-v2-circuit-breaker.js +0 -0
- package/lib/brain-v2.js +0 -0
- package/lib/broker/adaptive-throttle.js +0 -0
- package/lib/broker/batch-coalescer.js +0 -0
- package/lib/broker/bulkhead.js +0 -0
- package/lib/broker/channel-registry.js +0 -0
- package/lib/broker/circuit-breaker.js +0 -0
- package/lib/broker/evidence-cache.js +0 -0
- package/lib/broker/health-monitor.js +0 -0
- package/lib/broker/mage-queue.js +0 -0
- package/lib/broker/priority-queue.js +0 -0
- package/lib/broker/server.js.bak-error2-fix +0 -0
- package/lib/broker/session-registry.js +0 -0
- package/lib/broker/singleton-timers.js +0 -0
- package/lib/broker/types.d.ts +0 -0
- package/lib/broker/vegas-limit.js +0 -0
- package/lib/compression/dist/ccr-store.js +74 -0
- package/lib/compression/dist/content-router.js +115 -0
- package/lib/compression/dist/pipeline.js +113 -0
- package/lib/compression/dist/server.js +265 -0
- package/lib/compression/dist/smart-crusher.js +251 -0
- package/lib/context-budget.ts +0 -0
- package/lib/context-firewall.js +0 -0
- package/lib/crm/integration/triage-bridge.js +0 -0
- package/lib/email-utils.ts +0 -0
- package/lib/eval/__tests__/preflight-checker.test.ts +0 -0
- package/lib/eval/__tests__/task-instruction-parser.test.ts +0 -0
- package/lib/eval/__tests__/verifier-runner.test.ts +0 -0
- package/lib/eval/index.ts +0 -0
- package/lib/eval/preflight-checker.ts +0 -0
- package/lib/eval/task-domain-classifier.ts +0 -0
- package/lib/eval/task-instruction-parser.ts +0 -0
- package/lib/eval/verifier-runner.ts +0 -0
- package/lib/event-bus.d.ts +0 -0
- package/lib/event-bus.mts +1 -1
- package/lib/governance-context-selector.ts +0 -0
- package/lib/graph/generate-extension-embeddings.js +0 -0
- package/lib/graph/generate-static-embeddings.js +0 -0
- package/lib/graph/lib/utils.js +1 -1
- package/lib/graph-audit.d.ts +0 -0
- package/lib/graph-availability.js +62 -0
- package/lib/hbo-core-store.compiled.js +834 -0
- package/lib/hbo-core-store.js +124 -0
- package/lib/hbo-core-store.ts +908 -0
- package/lib/mesh-circuit-breaker.js +0 -0
- package/lib/mission-loop/lesson-extractor.ts +0 -0
- package/lib/mission-loop/mental-model-scorer.ts +0 -0
- package/lib/mission-loop/occ-detector.ts +0 -0
- package/lib/mission-loop/query-variants.ts +0 -0
- package/lib/mission-loop/verifier-check.ts +0 -0
- package/lib/skill-reference-builder.ts +0 -0
- package/lib/telemetry/token-breakdown.ts +0 -0
- package/lib/tool-compressor.ts +0 -0
- package/lib/triage-core/classifier.ts +3 -2
- package/lib/triage-core/graph/schema.cypher +10 -0
- package/lib/triage-core/legal-routing.ts +0 -0
- package/lib/triage-core/mental-model/dunbar-classifier.ts +0 -0
- package/lib/triage-core/mental-model/enrich-all.ts +0 -0
- package/lib/triage-core/mental-model/identity-resolver.ts +0 -0
- package/lib/triage-core/mental-model/key-facts.ts +1 -2
- package/lib/triage-core/mental-model/model-assembler.ts +0 -0
- package/lib/triage-core/orchestrator.ts +4 -11
- package/lib/triage-core/orchestrator.ts.bak-r005-r006-r008 +0 -0
- package/package.json +18 -8
- package/skills/helios-business-operator/services/signals/upwork-signals.js +0 -0
- package/skills/talisman-ceo/SKILL.md +23 -25
- package/skills/talisman-comms/SKILL.md +5 -5
- package/skills/talisman-engineering/SKILL.md +5 -5
- package/skills/talisman-finance/SKILL.md +10 -8
- package/skills/talisman-marketing/SKILL.md +10 -10
- package/skills/talisman-sales/SKILL.md +12 -15
- package/skills/talisman-support/SKILL.md +5 -5
- package/agents/business/talisman-ceo.md +0 -183
- package/agents/business/talisman-comms.md +0 -257
- package/agents/business/talisman-cto.md +0 -153
- package/agents/business/talisman-finance.md +0 -246
- package/agents/business/talisman-marketing.md +0 -240
- package/agents/business/talisman-sales.md +0 -242
- package/agents/business/talisman-support.md +0 -236
- package/daemon/lib/approval-expiry.js +0 -162
- package/daemon/lib/blast-radius-analyzer.js +0 -75
- package/daemon/lib/domain-bootstrap-orchestrator.js +0 -267
- package/daemon/lib/forensic-log.js +0 -113
- package/daemon/lib/goal-research-pipeline.js +0 -644
- package/daemon/lib/harada/cascade-research-dispatcher.js +0 -261
- package/daemon/lib/headroom-middleware.js +0 -167
- package/daemon/lib/headroom-proxy-manager.js +0 -623
- package/daemon/lib/mental-model-cache.js +0 -96
- package/daemon/lib/project-factory.js +0 -47
- package/daemon/lib/session-log-reader.js +0 -93
- package/daemon/routes/hed.js +0 -133
- package/lib/graph/learning/headroom-learn-bridge.js +0 -215
- package/skills/helios-bookkeeping/SKILL.md +0 -321
- package/skills/helios-briefer/SKILL.md +0 -44
- package/skills/helios-client-relations/SKILL.md +0 -322
- package/skills/helios-personal-triager/SKILL.md +0 -45
- package/skills/helios-recruitment/SKILL.md +0 -317
- package/skills/helios-relationship-nudger/SKILL.md +0 -77
- package/skills/helios-researcher/SKILL.md +0 -44
- package/skills/helios-scheduler/SKILL.md +0 -58
- package/skills/helios-tax-analyst/SKILL.md +0 -280
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
/**
|
|
3
|
-
* mental-model-cache.js — CachedPersonModel node CRUD
|
|
4
|
-
* Pre-assembled mental models for fast agent brief injection.
|
|
5
|
-
* Cache is invalidated by enrichmentNeeded flag on Person node.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const SCHEMA_VERSION = 1;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* writeCachedModel — upsert a CachedPersonModel node for a person in a company.
|
|
12
|
-
*
|
|
13
|
-
* @param {Function} mgQuery — Memgraph query function (async)
|
|
14
|
-
* @param {string} personId — Person node id
|
|
15
|
-
* @param {string} companyId — Company node id
|
|
16
|
-
* @param {object} assembledModel — Output from assembleMentalModel()
|
|
17
|
-
*/
|
|
18
|
-
async function writeCachedModel(mgQuery, personId, companyId, assembledModel) {
|
|
19
|
-
const modelId = `model:${companyId}:${personId}`;
|
|
20
|
-
const contextBrief = assembledModel.contextBrief || '';
|
|
21
|
-
const faveeJson = JSON.stringify(assembledModel.favee || {});
|
|
22
|
-
const topicsJson = JSON.stringify((assembledModel.activeTopics || []).slice(0, 10));
|
|
23
|
-
const questionsJson = JSON.stringify((assembledModel.openQuestions || []).slice(0, 5));
|
|
24
|
-
const completeness = contextBrief ? 'full' : (faveeJson !== '{}' ? 'partial' : 'none');
|
|
25
|
-
|
|
26
|
-
await mgQuery(
|
|
27
|
-
`MERGE (m:CachedPersonModel {id: $id})
|
|
28
|
-
ON CREATE SET
|
|
29
|
-
m.personId = $personId, m.companyId = $companyId,
|
|
30
|
-
m.contextBrief = $brief, m.faveeJson = $favee,
|
|
31
|
-
m.topicsJson = $topics, m.questionsJson = $questions,
|
|
32
|
-
m.assembledAt = localdatetime(), m.completeness = $completeness,
|
|
33
|
-
m.schemaVersion = $sv
|
|
34
|
-
ON MATCH SET
|
|
35
|
-
m.contextBrief = $brief, m.faveeJson = $favee,
|
|
36
|
-
m.topicsJson = $topics, m.questionsJson = $questions,
|
|
37
|
-
m.assembledAt = localdatetime(), m.completeness = $completeness,
|
|
38
|
-
m.dirtyAt = null
|
|
39
|
-
WITH m
|
|
40
|
-
MATCH (p:Person {id: $personId})
|
|
41
|
-
MERGE (p)-[:HAS_CACHED_MODEL]->(m)`,
|
|
42
|
-
{ id: modelId, personId, companyId, brief: contextBrief,
|
|
43
|
-
favee: faveeJson, topics: topicsJson, questions: questionsJson,
|
|
44
|
-
completeness, sv: SCHEMA_VERSION }
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* invalidateCache — mark a CachedPersonModel dirty so next read triggers a refresh.
|
|
50
|
-
* Non-fatal: silently ignores errors if the model node doesn't exist yet.
|
|
51
|
-
*
|
|
52
|
-
* @param {Function} mgQuery
|
|
53
|
-
* @param {string} personId
|
|
54
|
-
* @param {string} companyId
|
|
55
|
-
*/
|
|
56
|
-
async function invalidateCache(mgQuery, personId, companyId) {
|
|
57
|
-
const modelId = `model:${companyId}:${personId}`;
|
|
58
|
-
await mgQuery(
|
|
59
|
-
`MATCH (m:CachedPersonModel {id: $id}) SET m.dirtyAt = localdatetime()`,
|
|
60
|
-
{ id: modelId }
|
|
61
|
-
).catch(() => {});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* getCachedModel — return a cached model if it exists, is not dirty, and is within maxAge.
|
|
66
|
-
* Returns null if the cache is cold, dirty, or expired.
|
|
67
|
-
*
|
|
68
|
-
* @param {Function} mgQuery
|
|
69
|
-
* @param {string} personId
|
|
70
|
-
* @param {string} companyId
|
|
71
|
-
* @param {number} maxAgeMinutes — default 60 minutes
|
|
72
|
-
* @returns {object|null}
|
|
73
|
-
*/
|
|
74
|
-
async function getCachedModel(mgQuery, personId, companyId, maxAgeMinutes = 60) {
|
|
75
|
-
const modelId = `model:${companyId}:${personId}`;
|
|
76
|
-
const rows = await mgQuery(
|
|
77
|
-
`MATCH (m:CachedPersonModel {id: $id})
|
|
78
|
-
WHERE m.dirtyAt IS NULL
|
|
79
|
-
AND m.assembledAt > localdatetime() - duration({minutes: $maxAge})
|
|
80
|
-
RETURN m.contextBrief, m.faveeJson, m.topicsJson, m.questionsJson,
|
|
81
|
-
m.assembledAt, m.completeness`,
|
|
82
|
-
{ id: modelId, maxAge: maxAgeMinutes }
|
|
83
|
-
);
|
|
84
|
-
if (!rows?.rows?.length) return null;
|
|
85
|
-
const r = rows.rows[0];
|
|
86
|
-
return {
|
|
87
|
-
contextBrief: r[0],
|
|
88
|
-
faveeJson: r[1],
|
|
89
|
-
topicsJson: r[2],
|
|
90
|
-
questionsJson: r[3],
|
|
91
|
-
assembledAt: r[4],
|
|
92
|
-
completeness: r[5],
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
module.exports = { writeCachedModel, invalidateCache, getCachedModel, SCHEMA_VERSION };
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
/**
|
|
3
|
-
* project-factory.js — Creates HeliosProject + ProjectDocument nodes when
|
|
4
|
-
* a GoalPillar is initialized via tickGoalDecompose().
|
|
5
|
-
*
|
|
6
|
-
* Called from daemon/lib/hbo-bridge.js tickGoalDecompose() after
|
|
7
|
-
* mandala.initializeMandala() succeeds for each GoalPillar.
|
|
8
|
-
*
|
|
9
|
-
* ID conventions:
|
|
10
|
-
* HeliosProject: proj:<companyId>:<pillarSlug>
|
|
11
|
-
* ProjectDocument: pdoc:<projectId>:main
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
async function ensureProject(mgQuery, companyId, goalId, pillarId, pillarTitle) {
|
|
15
|
-
const slug = pillarId.replace(/[^a-z0-9]/gi, '-').toLowerCase().slice(0, 40);
|
|
16
|
-
const projId = `proj:${companyId}:${slug}`;
|
|
17
|
-
const docId = `pdoc:${projId}:main`;
|
|
18
|
-
|
|
19
|
-
await mgQuery(
|
|
20
|
-
`MERGE (p:HeliosProject {id: $projId})
|
|
21
|
-
ON CREATE SET
|
|
22
|
-
p.companyId = $cid,
|
|
23
|
-
p.goalId = $goalId,
|
|
24
|
-
p.pillarId = $pillarId,
|
|
25
|
-
p.name = $name,
|
|
26
|
-
p.status = 'planning',
|
|
27
|
-
p.phase = 'planning',
|
|
28
|
-
p.createdAt = datetime()`,
|
|
29
|
-
{ projId, cid: companyId, goalId, pillarId, name: pillarTitle }
|
|
30
|
-
).catch(() => {});
|
|
31
|
-
|
|
32
|
-
await mgQuery(
|
|
33
|
-
`MERGE (d:ProjectDocument {id: $docId})
|
|
34
|
-
ON CREATE SET
|
|
35
|
-
d.projectId = $projId,
|
|
36
|
-
d.purpose = '',
|
|
37
|
-
d.approach = '',
|
|
38
|
-
d.successCriteria = '[]',
|
|
39
|
-
d.intentAnchor = '',
|
|
40
|
-
d.exclusions = '[]',
|
|
41
|
-
d.version = toInteger(1),
|
|
42
|
-
d.updatedAt = datetime()`,
|
|
43
|
-
{ docId, projId }
|
|
44
|
-
).catch(() => {});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
module.exports = { ensureProject };
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const os = require('os');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* SessionLogReader — reads OpenCode session logs from opencode.db
|
|
8
|
-
* to reconstruct agent decision traces for HED audit.
|
|
9
|
-
* Uses better-sqlite3 (already a dependency via daemon) for read-only access.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
// OpenCode stores sessions at platform-specific paths:
|
|
13
|
-
// Linux/WSL: ~/.local/share/opencode/opencode.db
|
|
14
|
-
// macOS: ~/Library/Application Support/opencode/opencode.db
|
|
15
|
-
// Windows: %APPDATA%\opencode\opencode.db
|
|
16
|
-
function getOpenCodeDbPath() {
|
|
17
|
-
if (process.platform === 'linux') {
|
|
18
|
-
return path.join(os.homedir(), '.local', 'share', 'opencode', 'opencode.db');
|
|
19
|
-
}
|
|
20
|
-
if (process.platform === 'darwin') {
|
|
21
|
-
return path.join(os.homedir(), 'Library', 'Application Support', 'opencode', 'opencode.db');
|
|
22
|
-
}
|
|
23
|
-
return path.join(os.homedir(), 'AppData', 'Local', 'opencode', 'opencode.db');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function readSessionLog(sessionKey) {
|
|
27
|
-
if (!sessionKey) return [];
|
|
28
|
-
let Database;
|
|
29
|
-
try {
|
|
30
|
-
Database = require('better-sqlite3');
|
|
31
|
-
} catch {
|
|
32
|
-
console.warn('[session-log-reader] better-sqlite3 not available — cannot read session log');
|
|
33
|
-
return [];
|
|
34
|
-
}
|
|
35
|
-
const dbPath = getOpenCodeDbPath();
|
|
36
|
-
let db;
|
|
37
|
-
try {
|
|
38
|
-
db = new Database(dbPath, { readonly: true, fileMustExist: true });
|
|
39
|
-
} catch (err) {
|
|
40
|
-
console.warn('[session-log-reader] cannot open opencode.db:', err.message);
|
|
41
|
-
return [];
|
|
42
|
-
}
|
|
43
|
-
try {
|
|
44
|
-
const messages = db.prepare(
|
|
45
|
-
`SELECT role, content, created_at FROM messages WHERE session_id = ? ORDER BY created_at ASC`
|
|
46
|
-
).all(sessionKey);
|
|
47
|
-
return messages.map((m, i) => ({
|
|
48
|
-
index: i,
|
|
49
|
-
role: m.role,
|
|
50
|
-
content: typeof m.content === 'string' ? m.content : JSON.stringify(m.content),
|
|
51
|
-
createdAt: m.created_at
|
|
52
|
-
}));
|
|
53
|
-
} catch (err) {
|
|
54
|
-
console.warn('[session-log-reader] query failed:', err.message);
|
|
55
|
-
return [];
|
|
56
|
-
} finally {
|
|
57
|
-
db.close();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Classify messages into XEPV sequence: eXplore, Execute, Plan, Verify
|
|
62
|
-
function classifyXEPV(messages) {
|
|
63
|
-
return messages.map(m => {
|
|
64
|
-
const content = m.content || '';
|
|
65
|
-
// Tool calls in content indicate action type
|
|
66
|
-
if (content.includes('"edit"') || content.includes('"write"')) return { ...m, xepv: 'X_Execute' };
|
|
67
|
-
if (content.includes('"read"') || content.includes('"grep"') || content.includes('"search_codebase"') || content.includes('"glob"')) return { ...m, xepv: 'X_Explore' };
|
|
68
|
-
if (content.includes('"bash"') && (content.includes('test') || content.includes('vitest') || content.includes('grep -n'))) return { ...m, xepv: 'X_Verify' };
|
|
69
|
-
if (m.role === 'assistant' && (content.includes('plan') || content.includes('approach') || content.includes('strategy') || content.includes('first'))) return { ...m, xepv: 'X_Plan' };
|
|
70
|
-
return { ...m, xepv: 'X_Unknown' };
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Find the branch point where the agent deviated from the declared target
|
|
75
|
-
function findBranchPoint(xepvMessages, opTarget) {
|
|
76
|
-
const exploreBeforeExecute = xepvMessages.filter(m => m.xepv === 'X_Explore');
|
|
77
|
-
for (const msg of exploreBeforeExecute) {
|
|
78
|
-
// If the agent explored files NOT in the declared target, that's the branch point
|
|
79
|
-
if (opTarget && !msg.content.includes(opTarget)) {
|
|
80
|
-
return { message: msg, reason: `Agent explored ${msg.content.slice(0, 200)} instead of declared target ${opTarget}` };
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Compute P-ratio: plan steps vs execution steps
|
|
87
|
-
function computePRatio(xepvMessages) {
|
|
88
|
-
const plan = xepvMessages.filter(m => m.xepv === 'X_Plan').length;
|
|
89
|
-
const execute = xepvMessages.filter(m => m.xepv === 'X_Execute').length;
|
|
90
|
-
return { planSteps: plan, executeSteps: execute, ratio: execute > 0 ? plan / execute : plan };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
module.exports = { readSessionLog, classifyXEPV, findBranchPoint, computePRatio };
|
package/daemon/routes/hed.js
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { HEDEngine } = require('../lib/hed-engine');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* HED API routes — /api/hed/*
|
|
7
|
-
* Follows the standard Helios router pattern: returns true if handled, false otherwise.
|
|
8
|
-
* Initialized lazily inside startApi() via createHedRoutes(mgQuery).
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
function jsonOk(res, data, status = 200) {
|
|
12
|
-
res.writeHead(status, {
|
|
13
|
-
'Content-Type': 'application/json',
|
|
14
|
-
'Access-Control-Allow-Origin': '*',
|
|
15
|
-
'Access-Control-Allow-Methods': 'GET, POST, PATCH, OPTIONS',
|
|
16
|
-
'Access-Control-Allow-Headers': 'Content-Type, Accept',
|
|
17
|
-
});
|
|
18
|
-
res.end(JSON.stringify(data));
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function jsonErr(res, status, message) {
|
|
22
|
-
res.writeHead(status, {
|
|
23
|
-
'Content-Type': 'application/json',
|
|
24
|
-
'Access-Control-Allow-Origin': '*',
|
|
25
|
-
'Access-Control-Allow-Methods': 'GET, POST, PATCH, OPTIONS',
|
|
26
|
-
'Access-Control-Allow-Headers': 'Content-Type, Accept',
|
|
27
|
-
});
|
|
28
|
-
res.end(JSON.stringify({ error: message }));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function readBody(req) {
|
|
32
|
-
return new Promise((resolve, reject) => {
|
|
33
|
-
let body = '';
|
|
34
|
-
req.on('data', chunk => { body += chunk; });
|
|
35
|
-
req.on('end', () => {
|
|
36
|
-
try { resolve(body ? JSON.parse(body) : {}); }
|
|
37
|
-
catch (e) { reject(new Error('Invalid JSON body')); }
|
|
38
|
-
});
|
|
39
|
-
req.on('error', reject);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function createHedRoutes(mgQuery) {
|
|
44
|
-
const engine = new HEDEngine(mgQuery);
|
|
45
|
-
|
|
46
|
-
return async function hedRoute(req, res, ctx, pathname, method) {
|
|
47
|
-
// Only handle /api/hed paths
|
|
48
|
-
if (!pathname.startsWith('/api/hed')) return false;
|
|
49
|
-
|
|
50
|
-
const mg = ctx?.mgQuery ?? mgQuery;
|
|
51
|
-
const _engine = ctx?.mgQuery ? new HEDEngine(ctx.mgQuery) : engine;
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
// POST /api/hed — create a new HED
|
|
55
|
-
if (method === 'POST' && pathname === '/api/hed') {
|
|
56
|
-
const body = await readBody(req);
|
|
57
|
-
const { companyId, title, intent, worldStateSnapshot, operations, goalId } = body;
|
|
58
|
-
if (!companyId || !title || !operations?.length) {
|
|
59
|
-
jsonErr(res, 400, 'companyId, title, and operations are required');
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
const result = await _engine.createHED({ companyId, title, intent, worldStateSnapshot, operations, goalId });
|
|
63
|
-
jsonOk(res, result, 201);
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// POST /api/hed/:id/approve — HITL approval
|
|
68
|
-
const approveMatch = method === 'POST' && pathname.match(/^\/api\/hed\/([^/]+)\/approve$/);
|
|
69
|
-
if (approveMatch) {
|
|
70
|
-
const hedId = approveMatch[1];
|
|
71
|
-
const body = await readBody(req);
|
|
72
|
-
const { approvedBy } = body;
|
|
73
|
-
await _engine.approveHED(hedId, approvedBy || 'human');
|
|
74
|
-
jsonOk(res, { hedId, approved: true });
|
|
75
|
-
return true;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// GET /api/hed/:id/review — aggregated review report
|
|
79
|
-
const reviewMatch = method === 'GET' && pathname.match(/^\/api\/hed\/([^/]+)\/review$/);
|
|
80
|
-
if (reviewMatch) {
|
|
81
|
-
const hedId = reviewMatch[1];
|
|
82
|
-
const operations = await _engine.getOperations(hedId);
|
|
83
|
-
const findings = operations.map(op => ({
|
|
84
|
-
opId: op.id,
|
|
85
|
-
status: op.status,
|
|
86
|
-
reviewVerdict: op.reviewVerdict || 'pending'
|
|
87
|
-
}));
|
|
88
|
-
const aligned = findings.filter(f => f.reviewVerdict === 'ALIGNED').length;
|
|
89
|
-
const deviated = findings.filter(f => f.reviewVerdict === 'DEVIATED').length;
|
|
90
|
-
const failed = findings.filter(f => f.reviewVerdict === 'FAILED').length;
|
|
91
|
-
jsonOk(res, {
|
|
92
|
-
hedId,
|
|
93
|
-
findings,
|
|
94
|
-
summary: { aligned, deviated, failed, pending: findings.length - aligned - deviated - failed }
|
|
95
|
-
});
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// PATCH /api/hed/:hedId/operations/:opId — update operation status
|
|
100
|
-
const opPatchMatch = method === 'PATCH' && pathname.match(/^\/api\/hed\/([^/]+)\/operations\/([^/]+)$/);
|
|
101
|
-
if (opPatchMatch) {
|
|
102
|
-
const opId = opPatchMatch[2];
|
|
103
|
-
const body = await readBody(req);
|
|
104
|
-
const { status, summary, sessionKey, verdict } = body;
|
|
105
|
-
if (status === 'done' || status === 'completed') {
|
|
106
|
-
await _engine.completeOperation(opId, { summary, sessionKey, verdict });
|
|
107
|
-
}
|
|
108
|
-
jsonOk(res, { opId, updated: true });
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// GET /api/hed/:id — get HED + all operations
|
|
113
|
-
const hedMatch = method === 'GET' && pathname.match(/^\/api\/hed\/([^/]+)$/);
|
|
114
|
-
if (hedMatch) {
|
|
115
|
-
const hedId = hedMatch[1];
|
|
116
|
-
const hed = await _engine.getHED(hedId);
|
|
117
|
-
if (!hed) { jsonErr(res, 404, 'HED not found'); return true; }
|
|
118
|
-
const operations = await _engine.getOperations(hedId);
|
|
119
|
-
jsonOk(res, { ...hed, operations });
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
} catch (err) {
|
|
124
|
-
console.error('[hed-routes] error:', err.message);
|
|
125
|
-
jsonErr(res, 500, err.message);
|
|
126
|
-
return true;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return false;
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
module.exports = { createHedRoutes };
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* lib/graph/learning/headroom-learn-bridge.js
|
|
5
|
-
*
|
|
6
|
-
* Bridges headroom's session failure-learning system with Helios's Memgraph
|
|
7
|
-
* learning graph. Called asynchronously from the RETROSPECTIVE phase after
|
|
8
|
-
* each CausalLesson extraction.
|
|
9
|
-
*
|
|
10
|
-
* Pipeline:
|
|
11
|
-
* 1. Run `headroom learn --apply --project {helios-agent-root} --target AGENTS.md`
|
|
12
|
-
* using the vendored headroom package at vendor/headroom-proxy/
|
|
13
|
-
* 2. headroom learn scans Pi session logs (SQLite DB + tool-output blobs)
|
|
14
|
-
* and writes corrections to AGENTS.md (marker-delimited, git-tracked)
|
|
15
|
-
* 3. Parse corrections from AGENTS.md headroom:learn markers
|
|
16
|
-
* 4. Write corrections as KnowledgeAsset nodes to Memgraph
|
|
17
|
-
* 5. KnowledgeAsset nodes surface via biz-lesson-recall + lesson-recall channels
|
|
18
|
-
* in future sessions
|
|
19
|
-
*
|
|
20
|
-
* Why this improves the whole system (not just one company):
|
|
21
|
-
* - Structural patterns (path corrections, command patterns, search scope fixes)
|
|
22
|
-
* are cross-company: they describe helios-agent behavior, not company data
|
|
23
|
-
* - KnowledgeAsset confidence increases each time a pattern is reinforced
|
|
24
|
-
* - Corrections in AGENTS.md are git-tracked and manually reviewable
|
|
25
|
-
* - The global AGENTS.md (~/.config/opencode/AGENTS.md) is NEVER touched
|
|
26
|
-
*
|
|
27
|
-
* Debounce: runs at most once per 5 minutes across all sessions to avoid
|
|
28
|
-
* hammering the Pi session log scanner.
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
const path = require('path');
|
|
32
|
-
const fs = require('fs');
|
|
33
|
-
const crypto = require('crypto');
|
|
34
|
-
const { execFile } = require('child_process');
|
|
35
|
-
|
|
36
|
-
const HELIOS_ROOT = process.env.HELIOS_ROOT || path.resolve(__dirname, '..', '..', '..');
|
|
37
|
-
const AGENTS_MD = path.join(HELIOS_ROOT, 'AGENTS.md');
|
|
38
|
-
const VENDOR_PATH = path.join(HELIOS_ROOT, 'vendor', 'headroom-proxy');
|
|
39
|
-
const MARKER_START = '<!-- headroom:learn:start -->';
|
|
40
|
-
const MARKER_END = '<!-- headroom:learn:end -->';
|
|
41
|
-
const DEBOUNCE_MS = 5 * 60 * 1000; // 5 minutes between runs
|
|
42
|
-
|
|
43
|
-
let _lastRunAt = 0;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Main entry point. Called from retrospective.ts after CausalLesson extraction.
|
|
47
|
-
* @param {Function} mgQueryAsync - Memgraph query function
|
|
48
|
-
* @param {string} sessionId - Current session ID
|
|
49
|
-
* @param {string} missionRunId - Current mission run ID
|
|
50
|
-
*/
|
|
51
|
-
async function runBridge(mgQueryAsync, sessionId, missionRunId) {
|
|
52
|
-
const now = Date.now();
|
|
53
|
-
if (now - _lastRunAt < DEBOUNCE_MS) return;
|
|
54
|
-
_lastRunAt = now;
|
|
55
|
-
|
|
56
|
-
// Verify vendor path exists before attempting anything
|
|
57
|
-
if (!fs.existsSync(VENDOR_PATH)) {
|
|
58
|
-
process.stderr.write('[headroom-learn-bridge] vendor/headroom-proxy not found — skipping learn run\n');
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
// Step 1: Run headroom learn
|
|
64
|
-
await _runHeadroomLearn();
|
|
65
|
-
|
|
66
|
-
// Step 2: Parse corrections from AGENTS.md
|
|
67
|
-
const corrections = _parseCorrections();
|
|
68
|
-
if (!corrections.length) {
|
|
69
|
-
process.stderr.write('[headroom-learn-bridge] No corrections found in AGENTS.md markers\n');
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Step 3: Write to Memgraph
|
|
74
|
-
await _writeToMemgraph(mgQueryAsync, corrections, sessionId, missionRunId);
|
|
75
|
-
|
|
76
|
-
process.stderr.write(
|
|
77
|
-
`[headroom-learn-bridge] Wrote ${corrections.length} corrections to Memgraph KnowledgeAsset nodes\n`
|
|
78
|
-
);
|
|
79
|
-
} catch (err) {
|
|
80
|
-
// Completely non-fatal — never block RETROSPECTIVE phase
|
|
81
|
-
process.stderr.write(`[headroom-learn-bridge] Failed (non-fatal): ${err.message}\n`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Run `headroom learn --apply` using the vendored package.
|
|
87
|
-
* Writes to AGENTS.md with <!-- headroom:learn:start/end --> markers.
|
|
88
|
-
* Only the marker block is replaced — existing AGENTS.md content is preserved.
|
|
89
|
-
*/
|
|
90
|
-
function _runHeadroomLearn() {
|
|
91
|
-
return new Promise((resolve, reject) => {
|
|
92
|
-
const pythonBin = process.env.HEADROOM_PYTHON || 'python3';
|
|
93
|
-
|
|
94
|
-
// Use CLAUDE.local.md as target to avoid polluting shared AGENTS.md
|
|
95
|
-
// The corrections are written to a separate local file, then we parse
|
|
96
|
-
// them and write to Memgraph. Per-repo AGENTS.md gets the marker block.
|
|
97
|
-
const args = [
|
|
98
|
-
'-m', 'headroom.learn',
|
|
99
|
-
'--apply',
|
|
100
|
-
'--project', HELIOS_ROOT,
|
|
101
|
-
'--target', 'AGENTS.md', // per-repo AGENTS.md (NOT global ~/.config/opencode/AGENTS.md)
|
|
102
|
-
];
|
|
103
|
-
|
|
104
|
-
execFile(pythonBin, args, {
|
|
105
|
-
cwd: VENDOR_PATH,
|
|
106
|
-
env: {
|
|
107
|
-
...process.env,
|
|
108
|
-
PYTHONPATH: VENDOR_PATH,
|
|
109
|
-
HEADROOM_TELEMETRY: 'off',
|
|
110
|
-
},
|
|
111
|
-
timeout: 120_000, // 2-minute timeout (session log scanning can be slow on large projects)
|
|
112
|
-
}, (err, stdout, stderr) => {
|
|
113
|
-
if (err) {
|
|
114
|
-
// Non-zero exit is common when no sessions have been scanned yet
|
|
115
|
-
// Only reject on hard failures (ENOENT, etc.)
|
|
116
|
-
if (err.code === 'ENOENT') {
|
|
117
|
-
return reject(new Error(`Python not found: ${pythonBin}`));
|
|
118
|
-
}
|
|
119
|
-
// For other errors, log but resolve — partial output may still be useful
|
|
120
|
-
if (stderr) process.stderr.write(`[headroom-learn] ${stderr.slice(0, 500)}\n`);
|
|
121
|
-
return resolve(null); // resolve, not reject
|
|
122
|
-
}
|
|
123
|
-
if (stdout) process.stderr.write(`[headroom-learn] ${stdout.slice(0, 200)}\n`);
|
|
124
|
-
resolve(null);
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Parse the headroom:learn marker block from AGENTS.md.
|
|
131
|
-
* Returns an array of { category, content } corrections.
|
|
132
|
-
*/
|
|
133
|
-
function _parseCorrections() {
|
|
134
|
-
try {
|
|
135
|
-
if (!fs.existsSync(AGENTS_MD)) return [];
|
|
136
|
-
const content = fs.readFileSync(AGENTS_MD, 'utf-8');
|
|
137
|
-
const start = content.indexOf(MARKER_START);
|
|
138
|
-
const end = content.indexOf(MARKER_END);
|
|
139
|
-
if (start === -1 || end === -1 || end <= start) return [];
|
|
140
|
-
|
|
141
|
-
const block = content.slice(start + MARKER_START.length, end);
|
|
142
|
-
return _parseMarkdownBlock(block);
|
|
143
|
-
} catch {
|
|
144
|
-
return [];
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Parse markdown sections into structured corrections.
|
|
150
|
-
* Headings become categories; bullet items become corrections.
|
|
151
|
-
*/
|
|
152
|
-
function _parseMarkdownBlock(markdown) {
|
|
153
|
-
const corrections = [];
|
|
154
|
-
const lines = markdown.split('\n');
|
|
155
|
-
let currentCategory = 'general';
|
|
156
|
-
|
|
157
|
-
for (const line of lines) {
|
|
158
|
-
const trimmed = line.trim();
|
|
159
|
-
if (trimmed.startsWith('### ')) {
|
|
160
|
-
currentCategory = trimmed.slice(4).toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
|
161
|
-
} else if (trimmed.startsWith('## ') && !trimmed.startsWith('### ')) {
|
|
162
|
-
currentCategory = trimmed.slice(3).toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
|
163
|
-
} else if (trimmed.startsWith('- ') && trimmed.length > 5) {
|
|
164
|
-
const content = trimmed.slice(2).trim();
|
|
165
|
-
// Filter out boilerplate / empty corrections
|
|
166
|
-
if (content.length >= 15 && !content.startsWith('*Auto-generated')) {
|
|
167
|
-
corrections.push({ category: currentCategory, content });
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
return corrections;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Write corrections as KnowledgeAsset nodes to Memgraph.
|
|
176
|
-
* Uses MERGE on a stable content-hash ID for idempotency.
|
|
177
|
-
* Confidence increases each time the same pattern is reinforced.
|
|
178
|
-
*/
|
|
179
|
-
async function _writeToMemgraph(mgQueryAsync, corrections, sessionId, missionRunId) {
|
|
180
|
-
for (const correction of corrections) {
|
|
181
|
-
const id = 'headroom-learn:' + crypto.createHash('sha256')
|
|
182
|
-
.update(correction.content).digest('hex').slice(0, 16);
|
|
183
|
-
|
|
184
|
-
await mgQueryAsync(`
|
|
185
|
-
MERGE (ka:KnowledgeAsset {id: $id})
|
|
186
|
-
ON CREATE SET
|
|
187
|
-
ka.type = 'headroom-correction',
|
|
188
|
-
ka.category = $category,
|
|
189
|
-
ka.content = $content,
|
|
190
|
-
ka.confidence = 0.65,
|
|
191
|
-
ka.source = 'headroom-learn',
|
|
192
|
-
ka.sessionId = $sessionId,
|
|
193
|
-
ka.missionRunId = $missionRunId,
|
|
194
|
-
ka.createdAt = localdatetime(),
|
|
195
|
-
ka.lastSeenAt = localdatetime()
|
|
196
|
-
ON MATCH SET
|
|
197
|
-
ka.confidence = CASE
|
|
198
|
-
WHEN ka.confidence < 0.95 THEN ka.confidence + 0.05
|
|
199
|
-
ELSE 0.95
|
|
200
|
-
END,
|
|
201
|
-
ka.lastSeenAt = localdatetime(),
|
|
202
|
-
ka.updatedAt = localdatetime(),
|
|
203
|
-
ka.sessionId = $sessionId,
|
|
204
|
-
ka.missionRunId = $missionRunId
|
|
205
|
-
`, {
|
|
206
|
-
id,
|
|
207
|
-
category: correction.category,
|
|
208
|
-
content: correction.content,
|
|
209
|
-
sessionId: sessionId ?? 'unknown',
|
|
210
|
-
missionRunId: missionRunId ?? 'unknown',
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
module.exports = { runBridge };
|