total-recall-brain 3.43.3 → 3.45.0
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/total-recall.mjs +6 -4
- package/docs/ARCHITECTURE.md +10 -7
- package/docs/reference/cli-reference.md +16 -9
- package/frontend/dist/assets/index-DACh1dhx.js +5613 -0
- package/frontend/dist/index.html +1 -1
- package/frontend/src/App.tsx +2 -2
- package/frontend/src/pages/PluginsPage.tsx +1 -1
- package/package.json +3 -3
- package/scaffold/.agent/skills/plugin/SKILL.md +7 -2
- package/scaffold/.agent/skills/plugin/evals/README.md +5 -0
- package/scaffold/.agent/skills/plugin/evals/cases/01-fabricated-seed.mjs +1 -0
- package/scaffold/.agent/skills/plugin/evals/cases/02-reassuring-default.mjs +1 -0
- package/scaffold/.agent/skills/plugin/evals/cases/03-declared-template.mjs +1 -0
- package/scaffold/.agent/skills/plugin/evals/cases/04-card-collects-credential.mjs +1 -0
- package/scaffold/.agent/skills/plugin/evals/cases/05-host-leak-in-scaffold.mjs +1 -0
- package/scaffold/.agent/skills/plugin/evals/cases/06-correct-plugin.mjs +1 -0
- package/scaffold/.agent/skills/plugin/evals/cases/07-missing-reuse.mjs +11 -0
- package/scaffold/.agent/skills/plugin/evals/cases/08-unresolved-owner-command.mjs +12 -0
- package/scaffold/.agent/skills/plugin/evals/cases/09-missing-brick.mjs +12 -0
- package/scaffold/.agent/skills/plugin/prompts/01-function.md +4 -3
- package/scaffold/.agent/skills/plugin/prompts/08-verify.md +1 -0
- package/scaffold/.agent/skills/plugin/references/surfaces.md +12 -2
- package/scaffold/.agent/skills/plugin/scripts/record.mjs +59 -2
- package/scaffold/.agent/skills/total-recall/plugins/auth/README.md +8 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +15 -14
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-gh-login.mjs +102 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +38 -2
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/login-projections.mjs +157 -13
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-store.mjs +12 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-sync.mjs +161 -17
- package/scaffold/.agent/skills/total-recall/plugins/automations/BRICK.md +7 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/README.md +66 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/adapters.mjs +231 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/capability/index.mjs +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/cli.mjs +121 -0
- package/{src/core/plugin-cron.mjs → scaffold/.agent/skills/total-recall/plugins/automations/cron.mjs} +1 -3
- package/scaffold/.agent/skills/total-recall/plugins/automations/inventory.mjs +88 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/package-lock.json +7661 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/package.json +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/plugin.json +167 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/scheduler.mjs +246 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/DESIGN.md +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/automations.js +2 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/card.js +273 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +57 -5
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report-data.mjs +40 -14
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report.mjs +15 -40
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +15 -14
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +15 -14
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +15 -14
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/git/README.md +112 -0
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/cli.mjs +135 -23
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package-lock.json +17 -16
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package.json +5 -4
- package/scaffold/.agent/skills/total-recall/plugins/git/plugin.json +161 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/context.mjs +94 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/phase3-spec-support.mjs +50 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/runtime.mjs +40 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/spec-support.mjs +51 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/_support.mjs +195 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/auth.mjs +201 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/bisect.mjs +40 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/cli-meta.mjs +159 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit-memory.mjs +30 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit.mjs +46 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/fleet.mjs +156 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/force-push.mjs +58 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/group.mjs +119 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/handoff.mjs +59 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/message.mjs +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/resolve.mjs +60 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/rules.mjs +81 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/secret-firewall.mjs +66 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/undo.mjs +132 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/why.mjs +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/ui/card.js +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +15 -14
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/README.md +59 -10
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/BRICK.md +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +18 -6
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +160 -29
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +15 -14
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +231 -16
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +19 -4
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/adoption.mjs +104 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +101 -16
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/control.mjs +145 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +54 -21
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch-request.mjs +40 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +68 -13
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +55 -11
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +64 -15
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/hermes.mjs +29 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/index.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/included-usage.mjs +249 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/jobs.mjs +84 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +6 -12
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/maintenance.mjs +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/mcp-server.mjs +228 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/pool.mjs +16 -8
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/process-manager.mjs +26 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reasoning.mjs +6 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/remote-operation.mjs +6 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/review.mjs +34 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reviews.mjs +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +28 -12
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/runtime.mjs +28 -6
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/claude.mjs +319 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/codex.mjs +387 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/result.mjs +67 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/resume.mjs +373 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/run.mjs +223 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session-jobs.mjs +72 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/setup.mjs +6 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/termination.mjs +16 -12
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/threads.mjs +32 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/usage-queue.mjs +110 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/verify-ssss.mjs +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +597 -70
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worktree.mjs +76 -9
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/DESIGN.md +5 -5
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +47 -18
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +15 -14
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh-diagnostics.mjs +44 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +120 -113
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +15 -14
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/ssss/assets/scaffold.json +5 -5
- package/scaffold/.agent/skills/total-recall/plugins/ssss/package-lock.json +6 -6
- package/scaffold/.agent/skills/total-recall/plugins/ssss/package.json +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/ssss/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/references/operations.md +1 -1
- package/src/cli/agent.mjs +1 -1
- package/src/cli/command.mjs +41 -2
- package/src/cli/connect.mjs +2 -29
- package/src/cli/context.mjs +1 -0
- package/src/cli/dream.mjs +93 -69
- package/src/cli/harness.mjs +4 -4
- package/src/cli/help.mjs +1 -1
- package/src/cli/ingest-okf.mjs +4 -4
- package/src/cli/init.mjs +0 -25
- package/src/cli/lessons.mjs +51 -0
- package/src/cli/memory-plugin.mjs +1 -1
- package/src/cli/plugin/copies.mjs +64 -14
- package/src/cli/plugin/create.mjs +6 -7
- package/src/cli/plugin/index.mjs +11 -7
- package/src/cli/plugin/install.mjs +10 -15
- package/src/cli/plugin/remove.mjs +1 -1
- package/src/cli/plugin/search.mjs +2 -2
- package/src/cli/preflight.mjs +19 -6
- package/src/cli/proposals.mjs +1 -1
- package/src/cli/recall.mjs +66 -23
- package/src/cli/release.mjs +4 -1
- package/src/cli/remember.mjs +3 -14
- package/src/cli/server.mjs +18 -0
- package/src/cli/startup.mjs +1 -0
- package/src/cli/task.mjs +9 -2
- package/src/cli/update.mjs +10 -3
- package/src/core/access-ledger.mjs +78 -0
- package/src/core/agent-manager.mjs +2 -2
- package/src/core/automatic-brain-updates.mjs +11 -6
- package/src/core/brain-locate.mjs +15 -0
- package/src/core/brain-updates.mjs +42 -12
- package/src/core/claude-hooks.mjs +3 -1
- package/src/core/cleanup-task.mjs +15 -0
- package/src/core/cli-commands.mjs +2 -0
- package/src/core/cli-maintenance.mjs +4 -1
- package/src/core/cli-semantic-index.mjs +23 -3
- package/src/core/context-compactor.mjs +7 -2
- package/src/core/context-compiler.mjs +26 -1
- package/src/core/context-policy.mjs +7 -3
- package/src/core/daemon-control.mjs +22 -0
- package/src/core/daemon-loop.mjs +39 -62
- package/src/core/default-plugins.mjs +1 -1
- package/src/core/dream.mjs +33 -105
- package/src/core/ensure-dependencies.mjs +148 -0
- package/src/core/fast-recall.mjs +2 -2
- package/src/core/field-evaluation.mjs +23 -0
- package/src/core/gate-mode.mjs +1 -1
- package/src/core/global-brain-updates.mjs +1 -1
- package/src/core/harness-hooks.mjs +14 -2
- package/src/core/lesson-hooks.mjs +74 -0
- package/src/core/lesson-memory.mjs +24 -0
- package/src/core/lesson-sessions.mjs +52 -0
- package/src/core/lessons.mjs +96 -0
- package/src/core/mesh-brain-updates.mjs +2 -1
- package/src/core/meta-harness.mjs +2 -2
- package/src/core/optimizer.mjs +66 -165
- package/src/core/plugin-contracts/card.mjs +3 -0
- package/src/core/plugin-loader.mjs +12 -3
- package/src/core/plugin-runtime.mjs +40 -1
- package/src/core/plugin-store.mjs +35 -23
- package/src/core/post-mortem.mjs +52 -36
- package/src/core/preflight-gate.mjs +23 -2
- package/src/core/proposal-applier.mjs +31 -2
- package/src/core/repo-sync.mjs +4 -6
- package/src/core/required-plugins.mjs +26 -4
- package/src/core/rule-length.mjs +23 -0
- package/src/core/runtime.mjs +1 -1
- package/src/core/scheduler.mjs +35 -43
- package/src/core/schema.mjs +1 -2
- package/src/core/search.mjs +4 -3
- package/src/core/server-restart.mjs +128 -7
- package/src/core/server-service.mjs +148 -0
- package/src/core/session-watcher.mjs +19 -1
- package/src/core/skill-gap.mjs +16 -0
- package/src/core/ssss-kernel-bridge.mjs +33 -6
- package/src/core/startup-health.mjs +6 -1
- package/src/core/surface.mjs +68 -57
- package/src/core/task-context.mjs +9 -8
- package/src/core/task-executors.mjs +54 -106
- package/src/core/validated-write.mjs +5 -0
- package/src/core/vault-files.mjs +8 -2
- package/src/core/vault.mjs +1 -1
- package/src/core/vector-field.mjs +134 -192
- package/src/server/index.mjs +3 -1
- package/src/server/invoke-core-child.mjs +24 -0
- package/src/server/invoke-core-operations.mjs +102 -0
- package/src/server/route-manifest.json +0 -56
- package/src/server/routes/brains.mjs +0 -18
- package/src/server/routes/context.mjs +0 -41
- package/src/server/routes/invoke.mjs +26 -1
- package/src/server/routes/ssss.mjs +0 -169
- package/src/server/routes/system.mjs +0 -15
- package/src/server/routes/tasks.mjs +0 -26
- package/src/server/routes/vault.mjs +2 -23
- package/frontend/dist/assets/index-DD4qONvH.js +0 -5607
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +0 -53
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +0 -129
- package/src/core/append-log.mjs +0 -131
- package/src/core/plugin-tasks.mjs +0 -173
- package/templates/onboarding-interview.md +0 -82
- /package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/LICENSE +0 -0
- /package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/ui/DESIGN.md +0 -0
|
@@ -20,11 +20,13 @@
|
|
|
20
20
|
|
|
21
21
|
import fs from 'node:fs';
|
|
22
22
|
import path from 'node:path';
|
|
23
|
-
import {
|
|
24
|
-
import { getEmbedding,
|
|
23
|
+
import { loadNodes } from './vault.mjs';
|
|
24
|
+
import { getEmbedding, loadEmbeddingsIndex } from './embeddings.mjs';
|
|
25
25
|
import { inferMemoryLayer } from './memory-layers.mjs';
|
|
26
26
|
import { logger } from './logger.mjs';
|
|
27
27
|
import { brainDir } from './config.mjs';
|
|
28
|
+
import { readAccess, recordAccess, lastUsedMs, activeDays, activeDaysSince } from './access-ledger.mjs';
|
|
29
|
+
import { atomicWrite } from './vault-files.mjs';
|
|
28
30
|
|
|
29
31
|
const DERIVED_DIR = path.join(brainDir, 'memory-derived');
|
|
30
32
|
|
|
@@ -32,10 +34,6 @@ function fieldPath(derivedDir = DERIVED_DIR) {
|
|
|
32
34
|
return path.join(derivedDir, 'vector-field.json');
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
function queryLogPath(derivedDir = DERIVED_DIR) {
|
|
36
|
-
return path.join(derivedDir, 'query-log.jsonl');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
37
|
const LN2 = Math.log(2);
|
|
40
38
|
|
|
41
39
|
// ─── Half-Life Constants (milliseconds) ─────────────────────────────────────
|
|
@@ -74,12 +72,14 @@ const HALF_LIFE_TABLE = {
|
|
|
74
72
|
*/
|
|
75
73
|
function computeHalfLife(node) {
|
|
76
74
|
// Priority overrides category
|
|
77
|
-
if (node.priority === 'absolute') return Infinity;
|
|
75
|
+
if (node.priority === 'absolute' || HALF_LIFE_TABLE[node.category] === Infinity) return Infinity;
|
|
76
|
+
const configuredDays = Number(node.decay?.half_life_days);
|
|
77
|
+
const categoryHL = configuredDays > 0 ? configuredDays * 86400000 :
|
|
78
|
+
(HALF_LIFE_TABLE[node.category] || HALF_LIFE_TABLE.fact);
|
|
78
79
|
if (node.priority === 'high') {
|
|
79
|
-
const categoryHL = HALF_LIFE_TABLE[node.category] || HALF_LIFE_TABLE.fact;
|
|
80
80
|
return Math.max(categoryHL, HALF_LIFE_TABLE._high);
|
|
81
81
|
}
|
|
82
|
-
return
|
|
82
|
+
return categoryHL;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/**
|
|
@@ -120,7 +120,7 @@ function computeNovelty(accessCount) {
|
|
|
120
120
|
/**
|
|
121
121
|
* Compile the full vector field from the vault.
|
|
122
122
|
*
|
|
123
|
-
* 1.
|
|
123
|
+
* 1. Read existing active-node embeddings → position vectors
|
|
124
124
|
* 2. Compute half-life, trajectory, novelty per point
|
|
125
125
|
* 3. Compute full pairwise covariance → coupling matrix
|
|
126
126
|
* 4. Extract dependency graph
|
|
@@ -128,133 +128,97 @@ function computeNovelty(accessCount) {
|
|
|
128
128
|
*/
|
|
129
129
|
export async function compileField({ vaultDir, derivedDir = DERIVED_DIR } = {}) {
|
|
130
130
|
const startTime = Date.now();
|
|
131
|
-
const allNodes =
|
|
131
|
+
const allNodes = loadNodes(vaultDir || path.join(brainDir, 'memory-vault')).filter(n => n.status === 'active');
|
|
132
132
|
const totalNodes = allNodes.length;
|
|
133
133
|
const now = Date.now();
|
|
134
134
|
|
|
135
135
|
logger.info('vector-field', `Compiling field from ${totalNodes} nodes...`);
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
const queryHistory = loadQueryHistory(derivedDir);
|
|
137
|
+
const ledger = readAccess(derivedDir);
|
|
139
138
|
|
|
140
|
-
// ── Phase 1:
|
|
141
|
-
const existingIndex = loadEmbeddingsIndex(derivedDir);
|
|
139
|
+
// ── Phase 1: Read stored vectors and compute physics ──
|
|
140
|
+
const existingIndex = fs.existsSync(path.join(derivedDir, 'embeddings.db')) ? loadEmbeddingsIndex(derivedDir) : {};
|
|
142
141
|
const points = [];
|
|
143
|
-
const newlyEmbedded = new Map();
|
|
144
|
-
let embedded = 0;
|
|
145
142
|
let cached = 0;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
let embedding;
|
|
157
|
-
if (existingIndex[node.slug]?.embedding) {
|
|
158
|
-
embedding = existingIndex[node.slug].embedding;
|
|
159
|
-
cached++;
|
|
160
|
-
} else {
|
|
161
|
-
try {
|
|
162
|
-
embedding = await getEmbedding(text);
|
|
163
|
-
newlyEmbedded.set(node.slug, embedding);
|
|
164
|
-
embedded++;
|
|
165
|
-
} catch (err) {
|
|
166
|
-
logger.error('vector-field', `Failed to embed ${node.slug}: ${err.message}`);
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// ── Physics computation ──
|
|
172
|
-
const decay = node.decay || {};
|
|
173
|
-
const accessCount = decay.access_count || 0;
|
|
174
|
-
const lastAccessed = node.last_accessed ? new Date(node.last_accessed).getTime() : 0;
|
|
175
|
-
const createdAt = node.created ? new Date(node.created).getTime() : now;
|
|
176
|
-
const ageMs = now - createdAt;
|
|
177
|
-
|
|
178
|
-
// Half-life
|
|
179
|
-
const halfLife = computeHalfLife(node);
|
|
180
|
-
const lambda = decayConstant(halfLife);
|
|
181
|
-
|
|
182
|
-
// Velocity: dR/dt from query log
|
|
183
|
-
const { velocity, trajectory } = computeTrajectory(node.slug, queryHistory, now);
|
|
184
|
-
|
|
185
|
-
// Novelty
|
|
186
|
-
const novelty = computeNovelty(accessCount);
|
|
187
|
-
|
|
188
|
-
// Peak relevance (baseline from importance + priority)
|
|
189
|
-
const importanceWeight = (node.importance || 3) / 5;
|
|
190
|
-
const priorityWeight = node.priority === 'absolute' ? 1.0 : node.priority === 'high' ? 0.8 : node.priority === 'normal' ? 0.5 : 0.3;
|
|
191
|
-
const peakRelevance = Math.max(importanceWeight, priorityWeight);
|
|
192
|
-
|
|
193
|
-
// Time since last access (for decay computation at sample time)
|
|
194
|
-
const timeSinceAccess = lastAccessed > 0 ? now - lastAccessed : ageMs;
|
|
195
|
-
|
|
196
|
-
// Dependencies: extract from node.related field
|
|
197
|
-
const dependencies = node.related ? (Array.isArray(node.related) ? node.related : String(node.related).split(',').map(s => s.trim())) : [];
|
|
198
|
-
|
|
199
|
-
// Precompute L2 norm
|
|
200
|
-
let mag = 0;
|
|
201
|
-
for (let j = 0; j < embedding.length; j++) mag += embedding[j] * embedding[j];
|
|
202
|
-
mag = Math.sqrt(mag);
|
|
203
|
-
|
|
204
|
-
points.push({
|
|
205
|
-
slug: node.slug,
|
|
206
|
-
title: node.title || node.slug,
|
|
207
|
-
category: node.category || 'unknown',
|
|
208
|
-
layer: inferMemoryLayer(node),
|
|
209
|
-
embedding,
|
|
210
|
-
magnitude: mag,
|
|
211
|
-
// ── Physics ──
|
|
212
|
-
half_life: isFinite(halfLife) ? halfLife : null, // null = infinite
|
|
213
|
-
lambda, // decay constant
|
|
214
|
-
peak_relevance: peakRelevance, // R₀
|
|
215
|
-
velocity, // dR/dt
|
|
216
|
-
trajectory, // d²R/dt² (acceleration)
|
|
217
|
-
novelty, // information density
|
|
218
|
-
// ── Metadata ──
|
|
219
|
-
access_count: accessCount,
|
|
220
|
-
last_accessed: lastAccessed,
|
|
221
|
-
created_at: createdAt,
|
|
222
|
-
time_since_access: timeSinceAccess,
|
|
223
|
-
importance: node.importance || 3,
|
|
224
|
-
priority: node.priority || 'normal',
|
|
225
|
-
dependencies,
|
|
226
|
-
body_length: (node.body || node.content || '').length,
|
|
227
|
-
});
|
|
143
|
+
let missing = 0;
|
|
144
|
+
let dimensions = null;
|
|
145
|
+
|
|
146
|
+
for (const node of allNodes) {
|
|
147
|
+
const embedding = existingIndex[node.slug]?.embedding;
|
|
148
|
+
if (!Array.isArray(embedding) || !embedding.length || !embedding.every(Number.isFinite) ||
|
|
149
|
+
!embedding.some(value => value !== 0) || (dimensions !== null && embedding.length !== dimensions)) {
|
|
150
|
+
missing++;
|
|
151
|
+
continue;
|
|
228
152
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
153
|
+
dimensions ??= embedding.length;
|
|
154
|
+
cached++;
|
|
155
|
+
|
|
156
|
+
// ── Physics computation ──
|
|
157
|
+
const { accessCount, lastAccessed, elapsedMs, velocity, trajectory } = accessPhysics(node, ledger, now);
|
|
158
|
+
const createdAt = new Date(node.created || now).getTime();
|
|
159
|
+
const halfLife = computeHalfLife(node);
|
|
160
|
+
const lambda = decayConstant(halfLife);
|
|
161
|
+
|
|
162
|
+
// Novelty
|
|
163
|
+
const novelty = computeNovelty(accessCount);
|
|
164
|
+
|
|
165
|
+
// Peak relevance (baseline from importance + priority)
|
|
166
|
+
const importanceWeight = (node.importance || 3) / 5;
|
|
167
|
+
const priorityWeight = node.priority === 'absolute' ? 1.0 : node.priority === 'high' ? 0.8 : node.priority === 'normal' ? 0.5 : 0.3;
|
|
168
|
+
const peakRelevance = Math.max(importanceWeight, priorityWeight);
|
|
169
|
+
|
|
170
|
+
// Time since last access (for decay computation at sample time)
|
|
171
|
+
const timeSinceAccess = elapsedMs;
|
|
172
|
+
|
|
173
|
+
// Dependencies: extract from node.related field
|
|
174
|
+
const dependencies = node.related ? (Array.isArray(node.related) ? node.related : String(node.related).split(',').map(s => s.trim())) : [];
|
|
175
|
+
|
|
176
|
+
// Precompute L2 norm
|
|
177
|
+
let mag = 0;
|
|
178
|
+
for (let j = 0; j < embedding.length; j++) mag += embedding[j] * embedding[j];
|
|
179
|
+
mag = Math.sqrt(mag);
|
|
180
|
+
|
|
181
|
+
points.push({
|
|
182
|
+
slug: node.slug,
|
|
183
|
+
title: node.title || node.slug,
|
|
184
|
+
category: node.category || 'unknown',
|
|
185
|
+
layer: inferMemoryLayer(node),
|
|
186
|
+
embedding,
|
|
187
|
+
magnitude: mag,
|
|
188
|
+
// ── Physics ──
|
|
189
|
+
half_life: isFinite(halfLife) ? halfLife : null, // null = infinite
|
|
190
|
+
lambda, // decay constant
|
|
191
|
+
peak_relevance: peakRelevance, // R₀
|
|
192
|
+
velocity, // dR/dt
|
|
193
|
+
trajectory, // d²R/dt² (acceleration)
|
|
194
|
+
novelty, // information density
|
|
195
|
+
// ── Metadata ──
|
|
196
|
+
access_count: accessCount,
|
|
197
|
+
last_accessed: lastAccessed,
|
|
198
|
+
created_at: createdAt,
|
|
199
|
+
time_since_access: timeSinceAccess,
|
|
200
|
+
importance: node.importance || 3,
|
|
201
|
+
priority: node.priority || 'normal',
|
|
202
|
+
dependencies,
|
|
203
|
+
body_length: (node.body || node.content || '').length,
|
|
204
|
+
});
|
|
239
205
|
}
|
|
240
206
|
|
|
241
207
|
const embedMs = Date.now() - startTime;
|
|
242
|
-
logger.info('vector-field', `
|
|
208
|
+
logger.info('vector-field', `Loaded ${cached} existing vectors; ${missing} missing or incompatible`);
|
|
243
209
|
|
|
244
210
|
// ── Phase 2: Pairwise covariance matrix ──
|
|
245
211
|
const N = points.length;
|
|
246
212
|
const slugIndex = points.map(p => p.slug);
|
|
247
|
-
const covariance = new
|
|
213
|
+
const covariance = Array.from({ length: N }, () => new Float32Array(N));
|
|
248
214
|
|
|
249
215
|
for (let i = 0; i < N; i++) {
|
|
250
|
-
covariance[i] = new Float32Array(N);
|
|
251
216
|
for (let j = i; j < N; j++) {
|
|
252
217
|
if (i === j) {
|
|
253
218
|
covariance[i][j] = 1.0;
|
|
254
219
|
} else {
|
|
255
220
|
const sim = fastCosine(points[i].embedding, points[j].embedding, points[i].magnitude, points[j].magnitude);
|
|
256
221
|
covariance[i][j] = sim;
|
|
257
|
-
if (!covariance[j]) covariance[j] = new Float32Array(N);
|
|
258
222
|
covariance[j][i] = sim;
|
|
259
223
|
}
|
|
260
224
|
}
|
|
@@ -274,7 +238,8 @@ export async function compileField({ vaultDir, derivedDir = DERIVED_DIR } = {})
|
|
|
274
238
|
meta: {
|
|
275
239
|
total_nodes: totalNodes,
|
|
276
240
|
points_compiled: N,
|
|
277
|
-
embedded_new:
|
|
241
|
+
embedded_new: 0,
|
|
242
|
+
missing_embeddings: missing,
|
|
278
243
|
embedded_cached: cached,
|
|
279
244
|
embed_ms: embedMs,
|
|
280
245
|
covariance_ms: covMs,
|
|
@@ -289,54 +254,51 @@ export async function compileField({ vaultDir, derivedDir = DERIVED_DIR } = {})
|
|
|
289
254
|
|
|
290
255
|
const outPath = fieldPath(derivedDir);
|
|
291
256
|
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
292
|
-
|
|
257
|
+
atomicWrite(outPath, JSON.stringify(field));
|
|
293
258
|
|
|
294
259
|
logger.info('vector-field', `Field compiled: ${N} points, ${N * N} couplings, ${field.meta.infinite_halflife} immortal, ${field.meta.with_dependencies} with deps, ${field.meta.compile_ms}ms`);
|
|
295
260
|
|
|
296
261
|
return field;
|
|
297
262
|
}
|
|
298
263
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
if (!entry.hits?.includes(slug)) continue;
|
|
312
|
-
const age = now - entry.t;
|
|
313
|
-
if (age < WINDOW) w0++;
|
|
314
|
-
else if (age < 2 * WINDOW) w1++;
|
|
315
|
-
else if (age < 3 * WINDOW) w2++;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Velocity = first derivative (recent vs previous)
|
|
319
|
-
const velocity = (w0 - w1) * 0.1;
|
|
320
|
-
|
|
321
|
-
// Trajectory = second derivative (acceleration of velocity)
|
|
322
|
-
const v_prev = (w1 - w2) * 0.1;
|
|
323
|
-
const trajectory = velocity - v_prev; // positive = accelerating, negative = decelerating
|
|
324
|
-
|
|
325
|
-
return {
|
|
326
|
-
velocity: Math.max(-1, Math.min(1, velocity)),
|
|
327
|
-
trajectory: Math.max(-1, Math.min(1, trajectory)),
|
|
328
|
-
};
|
|
264
|
+
/** Read live usage. Old ledgers have no daily counts, so their trend is unknown (zero). */
|
|
265
|
+
function accessPhysics(node, ledger, now) {
|
|
266
|
+
const entry = ledger[node.slug];
|
|
267
|
+
const accessCount = Math.max(0, Number(entry?.count ?? node.access_count ?? node.decay?.access_count) || 0);
|
|
268
|
+
const lastAccessed = lastUsedMs(node, ledger);
|
|
269
|
+
const days = activeDays(ledger).filter(day => day <= new Date(now).toISOString().slice(0, 10));
|
|
270
|
+
const counts = [0, 1, 2].map(offset => Number(entry?.daily_counts?.[days.at(-1 - offset)]) || 0);
|
|
271
|
+
const clamp = value => Math.max(-1, Math.min(1, value));
|
|
272
|
+
return { accessCount, lastAccessed,
|
|
273
|
+
elapsedMs: days.length ? activeDaysSince({ _active_days: days }, lastAccessed) * 86400000 : Math.max(0, now - lastAccessed),
|
|
274
|
+
velocity: clamp((counts[0] - counts[1]) * .1),
|
|
275
|
+
trajectory: clamp((counts[0] - 2 * counts[1] + counts[2]) * .1) };
|
|
329
276
|
}
|
|
330
277
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
278
|
+
/** Re-rank only recall candidates. No provider call or access write. */
|
|
279
|
+
export function rankWithField(results, { field, derivedDir = DERIVED_DIR, now = Date.now() } = {}) {
|
|
280
|
+
if (!field) throw new Error('Vector field not compiled. Run total-recall recall --compile-field first.');
|
|
281
|
+
const ledger = readAccess(derivedDir);
|
|
282
|
+
const indexes = new Map(field.points.map((point, index) => [point.slug, index]));
|
|
283
|
+
const ranked = results.map(result => {
|
|
284
|
+
const index = result.type === 'session' ? undefined : indexes.get(result.slug);
|
|
285
|
+
if (index === undefined) return result;
|
|
286
|
+
const point = field.points[index];
|
|
287
|
+
const physics = accessPhysics({ ...point, last_accessed: point.last_accessed, created: point.created_at }, ledger, now);
|
|
288
|
+
const relevance = effectiveRelevance(point.peak_relevance, point.lambda, physics.elapsedMs, physics.trajectory);
|
|
289
|
+
let coupling = 0, peers = 0;
|
|
290
|
+
for (const candidate of results) {
|
|
291
|
+
const other = candidate.type === 'session' ? undefined : indexes.get(candidate.slug);
|
|
292
|
+
if (other === undefined || other === index) continue;
|
|
293
|
+
coupling += Math.max(0, field.covariance[index][other]) * candidate.score;
|
|
294
|
+
peers++;
|
|
295
|
+
}
|
|
296
|
+
// The base score remains the main signal. Missing vectors and sessions retain their score.
|
|
297
|
+
const score = result.score * (.7 + .3 * relevance) + .05 * physics.velocity +
|
|
298
|
+
.05 * computeNovelty(physics.accessCount) + .15 * (peers ? coupling / peers : 0);
|
|
299
|
+
return { ...result, base_score: result.score, score: round4(score), field_score: round4(score) };
|
|
300
|
+
});
|
|
301
|
+
return ranked.sort((a, b) => b.score - a.score);
|
|
340
302
|
}
|
|
341
303
|
|
|
342
304
|
// ─── Fast Cosine ────────────────────────────────────────────────────────────
|
|
@@ -381,7 +343,14 @@ export async function sampleField({
|
|
|
381
343
|
if (!field) throw new Error('Vector field not compiled. Run compileField() first.');
|
|
382
344
|
}
|
|
383
345
|
|
|
384
|
-
const
|
|
346
|
+
const ledger = readAccess(derivedDir);
|
|
347
|
+
const points = field.points.map(point => {
|
|
348
|
+
const usage = accessPhysics({ ...point, created: point.created_at }, ledger, now);
|
|
349
|
+
return { ...point, velocity: usage.velocity, trajectory: usage.trajectory,
|
|
350
|
+
novelty: computeNovelty(usage.accessCount), access_count: usage.accessCount,
|
|
351
|
+
last_accessed: usage.lastAccessed, time_since_access: usage.elapsedMs };
|
|
352
|
+
});
|
|
353
|
+
const { covariance } = field;
|
|
385
354
|
const N = points.length;
|
|
386
355
|
if (N === 0) return { results: [], query_embedding: [], sample_ms: 0, interference: [] };
|
|
387
356
|
|
|
@@ -401,7 +370,7 @@ export async function sampleField({
|
|
|
401
370
|
const decayedScores = new Float64Array(N);
|
|
402
371
|
for (let i = 0; i < N; i++) {
|
|
403
372
|
const p = points[i];
|
|
404
|
-
const elapsed = p.
|
|
373
|
+
const elapsed = p.time_since_access || 0;
|
|
405
374
|
const decayedRelevance = effectiveRelevance(p.peak_relevance, p.lambda, elapsed, p.trajectory);
|
|
406
375
|
// Blend: 70% similarity, 30% decayed relevance
|
|
407
376
|
decayedScores[i] = (directScores[i] * 0.7) + (decayedRelevance * 0.3);
|
|
@@ -460,7 +429,7 @@ export async function sampleField({
|
|
|
460
429
|
// ── Format results ──
|
|
461
430
|
const results = topResults.map(({ index, score }) => {
|
|
462
431
|
const p = points[index];
|
|
463
|
-
const elapsed = p.
|
|
432
|
+
const elapsed = p.time_since_access || 0;
|
|
464
433
|
const remainingHL = p.lambda > 0 ? Math.round((LN2 / p.lambda) / (24 * 60 * 60 * 1000)) : null;
|
|
465
434
|
return {
|
|
466
435
|
slug: p.slug,
|
|
@@ -485,7 +454,7 @@ export async function sampleField({
|
|
|
485
454
|
});
|
|
486
455
|
|
|
487
456
|
const sampleMs = Date.now() - startTime;
|
|
488
|
-
|
|
457
|
+
recordAccess(derivedDir, results.map(r => r.slug));
|
|
489
458
|
|
|
490
459
|
logger.info('vector-field', `Sampled in ${sampleMs}ms: ${results.length} results, ${depsPulledIn.length} deps pulled, ${interference.length} interference pairs`);
|
|
491
460
|
|
|
@@ -557,52 +526,25 @@ function round4(n) { return Math.round(n * 10000) / 10000; }
|
|
|
557
526
|
// ─── Field Loading ──────────────────────────────────────────────────────────
|
|
558
527
|
|
|
559
528
|
let _cachedField = null;
|
|
560
|
-
let
|
|
529
|
+
let _cachedFieldStamp = null;
|
|
561
530
|
|
|
562
531
|
export function loadField(derivedDir = DERIVED_DIR) {
|
|
563
532
|
try {
|
|
564
533
|
const fp = fieldPath(derivedDir);
|
|
565
534
|
if (!fs.existsSync(fp)) return null;
|
|
566
535
|
const stat = fs.statSync(fp);
|
|
567
|
-
|
|
536
|
+
const stamp = `${path.resolve(fp)}:${stat.mtimeMs}:${stat.ctimeMs}:${stat.size}`;
|
|
537
|
+
if (_cachedField && stamp === _cachedFieldStamp) return _cachedField;
|
|
568
538
|
const raw = JSON.parse(fs.readFileSync(fp, 'utf8'));
|
|
539
|
+
if (!Array.isArray(raw.points) || !Array.isArray(raw.covariance) ||
|
|
540
|
+
raw.covariance.length !== raw.points.length || raw.covariance.some(row =>
|
|
541
|
+
!Array.isArray(row) || row.length !== raw.points.length || !row.every(Number.isFinite))) return null;
|
|
569
542
|
_cachedField = raw;
|
|
570
|
-
|
|
543
|
+
_cachedFieldStamp = stamp;
|
|
571
544
|
return raw;
|
|
572
545
|
} catch { return null; }
|
|
573
546
|
}
|
|
574
547
|
|
|
575
|
-
// ─── Query Logging ──────────────────────────────────────────────────────────
|
|
576
|
-
|
|
577
|
-
function logQuery(query, hitSlugs, derivedDir = DERIVED_DIR) {
|
|
578
|
-
try {
|
|
579
|
-
const logPath = queryLogPath(derivedDir);
|
|
580
|
-
fs.mkdirSync(path.dirname(logPath), { recursive: true });
|
|
581
|
-
fs.appendFileSync(logPath, JSON.stringify({ t: Date.now(), q: query.slice(0, 200), hits: hitSlugs.slice(0, 20) }) + '\n');
|
|
582
|
-
} catch { /* fire and forget */ }
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// ─── Velocity Recomputation ─────────────────────────────────────────────────
|
|
586
|
-
|
|
587
|
-
/**
|
|
588
|
-
* Recompute velocity + trajectory vectors from query log.
|
|
589
|
-
* Call periodically (dream cycle) to update the field in-place.
|
|
590
|
-
*/
|
|
591
|
-
export function recomputeVelocities(field, derivedDir = DERIVED_DIR) {
|
|
592
|
-
if (!field) return field;
|
|
593
|
-
const now = Date.now();
|
|
594
|
-
const history = loadQueryHistory(derivedDir);
|
|
595
|
-
|
|
596
|
-
for (const point of field.points) {
|
|
597
|
-
const { velocity, trajectory } = computeTrajectory(point.slug, history, now);
|
|
598
|
-
point.velocity = velocity;
|
|
599
|
-
point.trajectory = trajectory;
|
|
600
|
-
point.novelty = computeNovelty(point.access_count);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
return field;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
548
|
// ─── Field Statistics ───────────────────────────────────────────────────────
|
|
607
549
|
|
|
608
550
|
export function fieldStats(derivedDir = DERIVED_DIR) {
|
package/src/server/index.mjs
CHANGED
|
@@ -34,7 +34,7 @@ import { readProcessCommand, entryPathHint, shouldHonorPidLock } from '../core/p
|
|
|
34
34
|
import { createCoverageCache } from './health-coverage.mjs';
|
|
35
35
|
import { drainActiveEmbeddings } from './routes/sessions.mjs';
|
|
36
36
|
import { agentDir as configAgentDir, brainDir as configBrainDir, port as configPort, host as configHost, nodeEnv, instructionsFilePath } from '../core/config.mjs';
|
|
37
|
-
import { getDaemonStatus, ensureDaemonRunning } from '../core/daemon-control.mjs';
|
|
37
|
+
import { getDaemonStatus, ensureDaemonRunning, clearStaleDaemonLock } from '../core/daemon-control.mjs';
|
|
38
38
|
import { deploymentHealth } from '../core/running-server.mjs';
|
|
39
39
|
|
|
40
40
|
|
|
@@ -1004,6 +1004,8 @@ if (HOST !== '127.0.0.1' && HOST !== '0.0.0.0') {
|
|
|
1004
1004
|
|
|
1005
1005
|
// Unref the interval so it doesn't block process shutdown
|
|
1006
1006
|
daemonWatchdogInterval.unref();
|
|
1007
|
+
} else if (nodeEnv !== 'test') {
|
|
1008
|
+
clearStaleDaemonLock();
|
|
1007
1009
|
}
|
|
1008
1010
|
|
|
1009
1011
|
// ─── Cloudflare Tunnel Auto-Start ───
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Full-mode owner work shares invoke's process-group and output limits. */
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
console.log = console.info = console.debug = (...args) => console.error(...args);
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
let input = '';
|
|
7
|
+
for await (const chunk of process.stdin) {
|
|
8
|
+
input += chunk;
|
|
9
|
+
if (Buffer.byteLength(input) > 32768) throw new Error('Input limit');
|
|
10
|
+
}
|
|
11
|
+
const { operation, params, brain, registryBrain } = JSON.parse(input);
|
|
12
|
+
// Config and Auth must use the serving brain, not a brain inferred from cwd.
|
|
13
|
+
process.env.AGENT_DIR = path.dirname(path.dirname(brain));
|
|
14
|
+
process.env.TR_SECRETS_BRAIN = brain;
|
|
15
|
+
const { executeCoreOperation } = await import('./invoke-core-operations.mjs');
|
|
16
|
+
const result = await executeCoreOperation(operation, params, { brain, registryBrain });
|
|
17
|
+
process.stdout.write(JSON.stringify({ ok: true, result }));
|
|
18
|
+
} catch (err) {
|
|
19
|
+
// Return curated policy codes only. Never return owner exception text.
|
|
20
|
+
const { InvokeError } = await import('./invoke-execution.mjs');
|
|
21
|
+
process.stdout.write(JSON.stringify({ ok: false, error: err instanceof InvokeError ? err.code : 'operation-failed',
|
|
22
|
+
status: err instanceof InvokeError ? err.status : 502 }));
|
|
23
|
+
process.exitCode = 1;
|
|
24
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/** Owner adapters for the retired one-off remote routes. */
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import matter from '../core/frontmatter.mjs';
|
|
5
|
+
import { readRegisteredProjects } from '../core/brain-registry.mjs';
|
|
6
|
+
import { InvokeError } from './invoke-execution.mjs';
|
|
7
|
+
|
|
8
|
+
export const CORE_OPERATIONS = Object.freeze({
|
|
9
|
+
'context.preview': { scope: 'memory:read', family: 'context' },
|
|
10
|
+
'context.stream': { scope: 'memory:read', family: 'context' },
|
|
11
|
+
'context.flash.health': { scope: 'memory:read', family: 'context' },
|
|
12
|
+
'brains.sync': { scope: 'memory:write', family: 'brains' },
|
|
13
|
+
'tasks.cleanup': { scope: 'tasks:write', family: 'tasks' },
|
|
14
|
+
'tasks.failed': { scope: 'tasks:read', family: 'tasks' },
|
|
15
|
+
'vault.hash': { scope: 'memory:read', family: 'vault' },
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export function validateCoreParams(operation, params) {
|
|
19
|
+
if (params.timeoutMs !== undefined && (!Number.isFinite(Number(params.timeoutMs)) || Number(params.timeoutMs) <= 0)) {
|
|
20
|
+
throw new InvokeError(400, 'invalid-timeout', 'timeoutMs must be positive and finite.');
|
|
21
|
+
}
|
|
22
|
+
const allowed = new Set(['target', 'timeoutMs']);
|
|
23
|
+
if (!['brains.sync', 'context.flash.health'].includes(operation)) allowed.add('brainId');
|
|
24
|
+
if (operation === 'context.stream') for (const key of ['query', 'budget_tokens', 'concurrency', 'min_score']) allowed.add(key);
|
|
25
|
+
if (Object.keys(params).some(key => !allowed.has(key))) throw new InvokeError(400, 'unknown-parameter', 'Unsupported operation parameter.');
|
|
26
|
+
if (params.brainId !== undefined && (typeof params.brainId !== 'string' || !params.brainId || params.brainId.includes(','))) {
|
|
27
|
+
throw new InvokeError(400, 'invalid-brain', 'brainId must select one brain.');
|
|
28
|
+
}
|
|
29
|
+
if (params.brainId !== undefined && !['server', 'global'].includes(params.brainId) &&
|
|
30
|
+
!/^project:[^/\\\0,]+$/.test(params.brainId)) {
|
|
31
|
+
throw new InvokeError(400, 'invalid-brain', 'brainId must select the server or a registered project.');
|
|
32
|
+
}
|
|
33
|
+
if (operation === 'context.stream') {
|
|
34
|
+
if (typeof params.query !== 'string' || !params.query.trim()) throw new InvokeError(400, 'invalid-query', 'query is required.');
|
|
35
|
+
for (const [key, min, max] of [['budget_tokens', 1, 65536], ['concurrency', 1, 4], ['min_score', 0, 100]]) {
|
|
36
|
+
if (params[key] !== undefined && (!Number.isInteger(params[key]) || params[key] < min || params[key] > max)) {
|
|
37
|
+
throw new InvokeError(400, 'invalid-parameter', `Invalid ${key}.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function selectedBrain(brainId, brain, registryBrain) {
|
|
44
|
+
if (brainId === undefined || brainId === 'server' || brainId === 'global') return brain;
|
|
45
|
+
if (brainId.startsWith('project:')) {
|
|
46
|
+
const name = brainId.slice('project:'.length);
|
|
47
|
+
const project = readRegisteredProjects(registryBrain).find(row => row.name === name || row.project_id === name);
|
|
48
|
+
if (project?.brainDir && fs.existsSync(path.join(project.brainDir, 'memory-vault'))) return project.brainDir;
|
|
49
|
+
}
|
|
50
|
+
throw new InvokeError(404, 'brain-unavailable', 'Selected brain is unavailable.');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function executeCoreOperation(operation, params, { brain, registryBrain }) {
|
|
54
|
+
if (!Object.hasOwn(CORE_OPERATIONS, operation)) throw new InvokeError(404, 'unknown-operation', 'Unknown operation.');
|
|
55
|
+
validateCoreParams(operation, params);
|
|
56
|
+
const selected = selectedBrain(params.brainId, brain, registryBrain);
|
|
57
|
+
const vaultDir = path.join(selected, 'memory-vault');
|
|
58
|
+
switch (operation) {
|
|
59
|
+
case 'context.preview': {
|
|
60
|
+
const { previewContext } = await import('../core/context-compiler.mjs');
|
|
61
|
+
return previewContext({ vaultDir });
|
|
62
|
+
}
|
|
63
|
+
case 'context.stream': {
|
|
64
|
+
const { streamParallelContext } = await import('../core/parallel-context.mjs');
|
|
65
|
+
const result = await streamParallelContext({ query: params.query, vaultDir,
|
|
66
|
+
budgetTokens: params.budget_tokens, concurrency: params.concurrency, minScore: params.min_score });
|
|
67
|
+
// Provider diagnostics can contain a credential-bearing URL.
|
|
68
|
+
return { ...result, scored: result.scored.map(({ error, ...row }) => error ? { ...row, error: 'provider-failed' } : row) };
|
|
69
|
+
}
|
|
70
|
+
case 'context.flash.health': {
|
|
71
|
+
const { checkFlashHealth } = await import('../core/parallel-context.mjs');
|
|
72
|
+
const { error, ...result } = await checkFlashHealth();
|
|
73
|
+
return error ? { ...result, error: 'provider-unavailable' } : result;
|
|
74
|
+
}
|
|
75
|
+
case 'brains.sync': {
|
|
76
|
+
const { syncAllRepos } = await import('../core/repo-sync.mjs');
|
|
77
|
+
const result = await syncAllRepos({ registryBrain });
|
|
78
|
+
if (result.repos.some(row => row.errors)) throw new InvokeError(502, 'sync-failed', 'Some repositories failed to sync.');
|
|
79
|
+
return { ok: true, ...result };
|
|
80
|
+
}
|
|
81
|
+
case 'tasks.cleanup': {
|
|
82
|
+
const queue = path.join(selected, 'scheduler/queue');
|
|
83
|
+
let deleted = 0;
|
|
84
|
+
if (fs.existsSync(queue)) for (const entry of fs.readdirSync(queue, { withFileTypes: true })) {
|
|
85
|
+
if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
|
|
86
|
+
const file = path.join(queue, entry.name);
|
|
87
|
+
const { data } = matter(fs.readFileSync(file, 'utf8'));
|
|
88
|
+
if (data.status === 'completed') { fs.unlinkSync(file); deleted++; }
|
|
89
|
+
}
|
|
90
|
+
return { deleted };
|
|
91
|
+
}
|
|
92
|
+
case 'tasks.failed': {
|
|
93
|
+
const { listTasks } = await import('../core/task-envelope.mjs');
|
|
94
|
+
const tasks = listTasks(path.join(selected, 'scheduler/queue'), { status: 'failed' });
|
|
95
|
+
return { total: tasks.length, tasks };
|
|
96
|
+
}
|
|
97
|
+
case 'vault.hash': {
|
|
98
|
+
const file = path.join(selected, 'memory-derived/vault-hash.txt');
|
|
99
|
+
return { vault_hash: fs.existsSync(file) ? fs.readFileSync(file, 'utf8').trim() : null, vault_dir: vaultDir };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|