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
package/src/core/surface.mjs
CHANGED
|
@@ -496,13 +496,8 @@ function injectDirectives(fileContent, rulesBlock) {
|
|
|
496
496
|
return prependDirectives(authored, rulesBlock);
|
|
497
497
|
}
|
|
498
498
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
if (!content.includes('total-recall/SKILL.md')) {
|
|
502
|
-
content = baseline + '\n\n' + content;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
return prependDirectives(content, rulesBlock);
|
|
499
|
+
// The managed block carries the start-here instructions; authored text stays as written.
|
|
500
|
+
return prependDirectives(fileContent.trimEnd(), rulesBlock);
|
|
506
501
|
}
|
|
507
502
|
|
|
508
503
|
function prependDirectives(authored, rulesBlock) {
|
|
@@ -516,14 +511,28 @@ function prependDirectives(authored, rulesBlock) {
|
|
|
516
511
|
/**
|
|
517
512
|
* Write or update a platform instruction shim with the pointer and active rules.
|
|
518
513
|
*/
|
|
514
|
+
/**
|
|
515
|
+
* The whole managed part of every client entry file (ON-DEMAND-CONTEXT-01). It is static: no rule
|
|
516
|
+
* bodies, CLI list, plugin table or command list, so a rule or plugin change rewrites no repository.
|
|
517
|
+
* The rules for a task are compiled when the agent acknowledges preflight, and commands are looked
|
|
518
|
+
* up when needed.
|
|
519
|
+
*/
|
|
520
|
+
export const ENTRY_BLOCK = `# Total Recall: start here
|
|
521
|
+
|
|
522
|
+
This repository uses Total Recall. Its rules are not copied into this file: they are compiled for your task when you acknowledge preflight.
|
|
523
|
+
|
|
524
|
+
1. Read \`.agent/skills/total-recall/SKILL.md\`.
|
|
525
|
+
2. Run \`total-recall tr-preflight --ack --task "<task>" --action read,edit,test\` and read everything it prints. Those are the rules for this task. Until it passes, working tools are denied.
|
|
526
|
+
3. Route the task through Metaharness with \`total-recall meta-harness route "<task>" --json\` and follow the route.
|
|
527
|
+
4. Look commands up only when you need them: \`total-recall --help\`, \`total-recall <namespace> --help\`, \`total-recall plugin library\`.`;
|
|
528
|
+
|
|
519
529
|
async function writeShim(shimPath, skillsDir, nodes = [], { vaultDir, derivedDir, force = false } = {}) {
|
|
520
530
|
const shimDir = path.dirname(shimPath);
|
|
521
|
-
const rulesBlock =
|
|
522
|
-
const baseline = '# 🔴 MANDATORY FIRST STEP ON EVERY TURN: TOTAL RECALL FIRST\n1. Run `total-recall --help` (or `node bin/total-recall.mjs --help`) to discover all tools, plugins, and commands.\n2. Read and follow .agent/skills/total-recall/SKILL.md before doing ANYTHING.\n3. Run `total-recall context "<task>"` and `total-recall recall "<topic>" --local` before any decision or action.\nTotal Recall is the mandatory working memory and toolset across all tasks and harnesses. Never skip this.\n';
|
|
531
|
+
const rulesBlock = ENTRY_BLOCK;
|
|
523
532
|
const mdcHeader = shimPath.endsWith('.mdc')
|
|
524
|
-
? '---\ndescription: "Total Recall —
|
|
533
|
+
? '---\ndescription: "Total Recall — start here."\nglobs:\nalwaysApply: true\n---\n\n'
|
|
525
534
|
: '';
|
|
526
|
-
const fullContent = prependDirectives(
|
|
535
|
+
const fullContent = prependDirectives(mdcHeader, rulesBlock);
|
|
527
536
|
|
|
528
537
|
try {
|
|
529
538
|
const normShim = path.normalize(shimPath);
|
|
@@ -620,7 +629,7 @@ const CLIENT_SHIMS = {
|
|
|
620
629
|
'hermes-agent': ['AGENTS.md'],
|
|
621
630
|
dsh: ['AGENTS.md'],
|
|
622
631
|
'deepseek-harness': ['AGENTS.md'],
|
|
623
|
-
openclaw: ['
|
|
632
|
+
openclaw: ['AGENTS.md']
|
|
624
633
|
};
|
|
625
634
|
|
|
626
635
|
/**
|
|
@@ -739,13 +748,13 @@ async function compilePointers(instructionsFile, skillsDir, nodes = [], { vaultD
|
|
|
739
748
|
*/
|
|
740
749
|
/**
|
|
741
750
|
* The rule nodes a project's instruction surfaces are built from: the global
|
|
742
|
-
* brain's rules plus the project's own. A project
|
|
751
|
+
* brain's rules plus the project's own. A project rule with the same slug
|
|
743
752
|
* wins, so a repo can override a global rule; everything else in the global
|
|
744
753
|
* vault stays search-only. Without this every repo carried only its own vault,
|
|
745
754
|
* and a rule saved with `remember --global` reached no repo at all.
|
|
746
755
|
*/
|
|
747
756
|
export function mergeGlobalRuleNodes(projectNodes, globalNodes) {
|
|
748
|
-
const own = new Set(projectNodes.map((n) => n.slug));
|
|
757
|
+
const own = new Set(projectNodes.filter((n) => RULE_CATEGORIES.has(n.category)).map((n) => n.slug));
|
|
749
758
|
const inherited = globalNodes
|
|
750
759
|
.filter((n) => RULE_CATEGORIES.has(n.category) && !own.has(n.slug))
|
|
751
760
|
.map((n) => ({ ...n, _layer: 'global' }));
|
|
@@ -772,46 +781,50 @@ export async function compileSurface({ vaultDir, skillsDir, derivedDir, instruct
|
|
|
772
781
|
? mergeGlobalRuleNodes(nodes, getNodes(globalVault))
|
|
773
782
|
: nodes;
|
|
774
783
|
|
|
775
|
-
// ──
|
|
776
|
-
//
|
|
777
|
-
//
|
|
784
|
+
// ── Each output has its own trigger (ON-DEMAND-CONTEXT-01) ──
|
|
785
|
+
// Entry files are static: they change only with the entry block or the client list.
|
|
786
|
+
// Indexes change only with this vault. The CLI index changes only with this brain's
|
|
787
|
+
// plugin catalog. A plugin or global-rule change therefore rewrites no repository's
|
|
788
|
+
// entry files and starts no embeddings pass; rules reach agents at preflight.
|
|
778
789
|
const hashFile = path.join(derivedDir, 'vault-hash.txt');
|
|
779
790
|
const currentHash = computeVaultHash(vaultDir);
|
|
780
|
-
const globalHashFile = path.join(derivedDir, 'global-rules-hash.txt');
|
|
781
|
-
const globalHash = globalVault && fs.existsSync(globalVault) ? computeVaultHash(globalVault) : '';
|
|
782
|
-
// Commands and skills feed the surface too; a change to either must recompile
|
|
783
|
-
// even when no memory node changed.
|
|
784
791
|
const inputsHashFile = path.join(derivedDir, 'surface-inputs-hash.txt');
|
|
792
|
+
const cliHashFile = path.join(derivedDir, 'cli-index-hash.txt');
|
|
785
793
|
const files = instructionFiles(instructionsFile, skillsDir);
|
|
786
794
|
const surfaceDir = path.dirname(instructionsFile);
|
|
787
795
|
const artifactsFile = path.join(derivedDir, 'surface-artifacts.json');
|
|
788
|
-
const inputsHash =
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
};
|
|
804
|
-
}
|
|
796
|
+
const inputsHash = crypto.createHash('sha256').update(ENTRY_BLOCK).update(JSON.stringify(files.map(file => path.relative(surfaceDir, file)))).digest('hex');
|
|
797
|
+
const stored = file => (fs.existsSync(file) ? fs.readFileSync(file, 'utf8').trim() : null);
|
|
798
|
+
const entryCurrent = !force && stored(inputsHashFile) === inputsHash && instructionArtifactsMatch(files, surfaceDir, artifactsFile);
|
|
799
|
+
const vaultCurrent = !force && stored(hashFile) === currentHash;
|
|
800
|
+
const cliCurrent = !force && stored(cliHashFile) === catalog.fingerprint;
|
|
801
|
+
|
|
802
|
+
if (entryCurrent && vaultCurrent && cliCurrent) {
|
|
803
|
+
return {
|
|
804
|
+
nodesProcessed: nodes.length,
|
|
805
|
+
skillsInjected: 0,
|
|
806
|
+
semanticIndexed: 0,
|
|
807
|
+
semanticUnavailable: false,
|
|
808
|
+
skipped: true,
|
|
809
|
+
reason: 'vault-hash-unchanged'
|
|
810
|
+
};
|
|
805
811
|
}
|
|
806
812
|
|
|
807
|
-
// 1. Write pointer and active rules to all instruction shims
|
|
808
|
-
const skillsInjected = await compilePointers(instructionsFile, skillsDir, ruleNodes, { vaultDir, derivedDir, force, files });
|
|
809
|
-
|
|
810
|
-
// 2. Build derived indexes (powers semantic search API)
|
|
811
813
|
if (!fs.existsSync(derivedDir)) {
|
|
812
814
|
fs.mkdirSync(derivedDir, { recursive: true });
|
|
813
815
|
}
|
|
814
816
|
|
|
817
|
+
// 1. Entry files, only when the entry block or client list changed (or a file drifted).
|
|
818
|
+
const skillsInjected = entryCurrent ? 0 : await compilePointers(instructionsFile, skillsDir, ruleNodes, { vaultDir, derivedDir, force, files });
|
|
819
|
+
if (!entryCurrent) {
|
|
820
|
+
atomicWrite(inputsHashFile, inputsHash);
|
|
821
|
+
atomicWrite(artifactsFile, JSON.stringify({ version: 1, files: files.map(file => describeInstruction(file, surfaceDir)) }, null, 2));
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// 2. Derived indexes, only when this vault changed.
|
|
825
|
+
let semanticResult = { indexed: 0, skipped: nodes.length, unavailable: false };
|
|
826
|
+
if (!vaultCurrent) {
|
|
827
|
+
|
|
815
828
|
const graphIndex = nodes.map(n => ({
|
|
816
829
|
slug: n.slug,
|
|
817
830
|
title: n.title,
|
|
@@ -844,7 +857,7 @@ export async function compileSurface({ vaultDir, skillsDir, derivedDir, instruct
|
|
|
844
857
|
// caller could detect any of it.
|
|
845
858
|
// Awaiting is cheap in steady state: buildEmbeddingsIndex skips nodes whose
|
|
846
859
|
// content hash is unchanged, so only genuinely new or edited nodes cost a call.
|
|
847
|
-
|
|
860
|
+
semanticResult = { indexed: 0, skipped: nodes.length, unavailable: true };
|
|
848
861
|
if (!semantic || process.env.TR_EMBEDDINGS_DISABLED === '1') {
|
|
849
862
|
logger.info('surface', 'Semantic embeddings explicitly disabled; local indexes updated.', { derivedDir });
|
|
850
863
|
} else try {
|
|
@@ -869,9 +882,18 @@ export async function compileSurface({ vaultDir, skillsDir, derivedDir, instruct
|
|
|
869
882
|
semanticResult = { indexed: 0, skipped: nodes.length, unavailable: true, error: err.message };
|
|
870
883
|
}
|
|
871
884
|
|
|
872
|
-
//
|
|
873
|
-
let cliIndexResult = { built: 0, skipped: 0, failed: 0 };
|
|
885
|
+
// 4. Generate Obsidian Canvas
|
|
874
886
|
try {
|
|
887
|
+
generateCanvas(nodes, vaultDir);
|
|
888
|
+
} catch { /* non-fatal */ }
|
|
889
|
+
|
|
890
|
+
atomicWrite(hashFile, currentHash);
|
|
891
|
+
writeProjectionManifest(derivedDir, currentHash);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// 3. The CLI semantic index, only when this brain's plugin catalog changed.
|
|
895
|
+
let cliIndexResult = { built: 0, skipped: 0, failed: 0 };
|
|
896
|
+
if (!cliCurrent) try {
|
|
875
897
|
const { buildCliSemanticIndex } = await import('./cli-semantic-index.mjs');
|
|
876
898
|
const projectRoot = skillsDir ? path.dirname(path.dirname(skillsDir)) : process.cwd();
|
|
877
899
|
cliIndexResult = await buildCliSemanticIndex({
|
|
@@ -884,22 +906,11 @@ export async function compileSurface({ vaultDir, skillsDir, derivedDir, instruct
|
|
|
884
906
|
if (cliIndexResult.built > 0) {
|
|
885
907
|
logger.info('surface', `CLI semantic index: ${cliIndexResult.built} indexed, ${cliIndexResult.skipped} skipped.`, { derivedDir });
|
|
886
908
|
}
|
|
909
|
+
atomicWrite(cliHashFile, catalog.fingerprint);
|
|
887
910
|
} catch (err) {
|
|
888
911
|
logger.warn('surface', `CLI semantic index build non-fatal warning: ${err.message}`, { derivedDir });
|
|
889
912
|
}
|
|
890
913
|
|
|
891
|
-
// 4. Generate Obsidian Canvas
|
|
892
|
-
try {
|
|
893
|
-
generateCanvas(nodes, vaultDir);
|
|
894
|
-
} catch { /* non-fatal */ }
|
|
895
|
-
|
|
896
|
-
// 5. Write vault hash + projection manifest
|
|
897
|
-
atomicWrite(hashFile, currentHash);
|
|
898
|
-
atomicWrite(globalHashFile, globalHash);
|
|
899
|
-
atomicWrite(inputsHashFile, inputsHash);
|
|
900
|
-
atomicWrite(artifactsFile, JSON.stringify({ version: 1, files: files.map(file => describeInstruction(file, surfaceDir)) }, null, 2));
|
|
901
|
-
writeProjectionManifest(derivedDir, currentHash);
|
|
902
|
-
|
|
903
914
|
return {
|
|
904
915
|
nodesProcessed: nodes.length,
|
|
905
916
|
skillsInjected,
|
|
@@ -9,7 +9,9 @@ import fs from 'node:fs';
|
|
|
9
9
|
const hash = text => crypto.createHash('sha256').update(String(text)).digest('hex');
|
|
10
10
|
const STOP = new Set('a an and are as at be before by can do for from how i in is it of on or our should so that the their this to use we what when which with you'.split(' '));
|
|
11
11
|
export const taskTerms = task => [...new Set((String(task).toLowerCase().match(/[\p{L}\p{N}_-]{3,}/gu) || []).filter(term => !STOP.has(term)))];
|
|
12
|
-
|
|
12
|
+
// Task terms contain only letters, digits, underscores and hyphens. Hyphens also delimit source-reference segments.
|
|
13
|
+
const taskMatchers = task => taskTerms(task).map(term => new RegExp(`(?<![\\p{L}\\p{N}_])${term}(?![\\p{L}\\p{N}_])`, 'iu'));
|
|
14
|
+
const overlap = (matchers, text) => matchers.filter(matcher => matcher.test(text)).length / Math.max(1, matchers.length);
|
|
13
15
|
export const summarizeRetrieval = summary => summary ? { mode: summary.mode, selected: summary.selected,
|
|
14
16
|
candidates: summary.candidates || 0, providers: (summary.coverage || []).map(row => row.provider),
|
|
15
17
|
...(summary.duplicatePassages ? { duplicatePassages: summary.duplicatePassages } : {}),
|
|
@@ -18,11 +20,11 @@ export const summarizeRetrieval = summary => summary ? { mode: summary.mode, sel
|
|
|
18
20
|
|
|
19
21
|
/** Preserve the best matching line and bounded neighboring text, including a match late in a long body. */
|
|
20
22
|
export function extractTaskPassage(text, task, { maxChars = 1000, anchorLine } = {}) {
|
|
21
|
-
const lines = String(text || '').split('\n'),
|
|
23
|
+
const lines = String(text || '').split('\n'), matchers = taskMatchers(task);
|
|
22
24
|
let anchor = Number.isInteger(anchorLine) ? Math.max(0, Math.min(lines.length - 1, anchorLine - 1)) : 0;
|
|
23
25
|
if (!Number.isInteger(anchorLine)) {
|
|
24
26
|
let best = -1;
|
|
25
|
-
lines.forEach((line, index) => { const score = overlap(
|
|
27
|
+
lines.forEach((line, index) => { const score = overlap(matchers, line); if (score > best) { best = score; anchor = index; } });
|
|
26
28
|
}
|
|
27
29
|
let start = Math.max(0, anchor - 2), end = Math.min(lines.length, anchor + 3);
|
|
28
30
|
while (lines.slice(start, end).join('\n').length > maxChars && (start < anchor || end > anchor + 1)) {
|
|
@@ -32,8 +34,7 @@ export function extractTaskPassage(text, task, { maxChars = 1000, anchorLine } =
|
|
|
32
34
|
let passage = lines.slice(start, end).join('\n');
|
|
33
35
|
let startColumn = 1;
|
|
34
36
|
if (passage.length > maxChars) {
|
|
35
|
-
const
|
|
36
|
-
const hits = terms.map(term => lower.indexOf(term)).filter(index => index >= 0);
|
|
37
|
+
const hits = matchers.map(matcher => passage.search(matcher)).filter(index => index >= 0);
|
|
37
38
|
const focus = hits.length ? Math.min(...hits) : 0;
|
|
38
39
|
const offset = Math.max(0, Math.min(passage.length - maxChars, focus - Math.floor(maxChars / 3)));
|
|
39
40
|
passage = passage.slice(offset, offset + maxChars); startColumn += offset;
|
|
@@ -50,8 +51,8 @@ function renderEvidence(item) {
|
|
|
50
51
|
export async function retrieveTaskKnowledge({ query, nodes = [], derivedDir, indexScopes,
|
|
51
52
|
projectRoot = process.cwd(), actions = [], semantic = true, budget = 1200, limit = 6,
|
|
52
53
|
embedding = getEmbedding, loadIndex = loadEmbeddingsIndex, providers = retrievePluginContext } = {}) {
|
|
53
|
-
const
|
|
54
|
-
if (!
|
|
54
|
+
const matchers = taskMatchers(query);
|
|
55
|
+
if (!matchers.length) return { contributions: [], summary: { mode: 'none', selected: 0, diagnostics: [], coverage: [] }, queryVector: null };
|
|
55
56
|
const diagnostics = [], indexes = new Map();
|
|
56
57
|
const scopes = indexScopes || (derivedDir ? [{ layer: '*', derivedDir }] : []);
|
|
57
58
|
for (const scope of scopes) {
|
|
@@ -77,7 +78,7 @@ export async function retrieveTaskKnowledge({ query, nodes = [], derivedDir, ind
|
|
|
77
78
|
if (stale) staleVectors++;
|
|
78
79
|
const valid = !stale && Array.isArray(entry?.embedding) && Array.isArray(queryVector) && entry.embedding.length === queryVector.length;
|
|
79
80
|
if (valid) vectors++;
|
|
80
|
-
const lexical = overlap(
|
|
81
|
+
const lexical = overlap(matchers, `${node.title || ''}\n${body}`);
|
|
81
82
|
const similarity = valid ? cosineSimilarity(queryVector, entry.embedding) : 0;
|
|
82
83
|
const score = valid ? Math.max(0, similarity) * .75 + lexical * .25 : lexical * .7;
|
|
83
84
|
return { node, layer, body, score, relevant: lexical > 0 || similarity >= .35 };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { loadPluginModule as __loadOwnedPlugin } from './plugin-runtime.mjs';
|
|
2
1
|
/**
|
|
3
2
|
* Executor registry for daemon tasks.
|
|
4
3
|
*
|
|
@@ -41,43 +40,18 @@ function policyCheck(task) {
|
|
|
41
40
|
|
|
42
41
|
async function runDream(task, ctx) {
|
|
43
42
|
const { runDreamCycle } = await import('./dream.mjs');
|
|
44
|
-
await runDreamCycle({
|
|
43
|
+
const result = await runDreamCycle({
|
|
45
44
|
vaultDir: ctx.vaultDir,
|
|
46
45
|
skillsDir: ctx.skillsDir,
|
|
47
46
|
derivedDir: ctx.derivedDir,
|
|
48
47
|
conflictsDir: ctx.conflictsDir,
|
|
49
48
|
instructionsFile: ctx.instructionsFile,
|
|
49
|
+
runtimeConfig: ctx.runtimeConfig,
|
|
50
50
|
});
|
|
51
|
+
if (result?.status !== 'success') return { success: false, error: result?.errors?.join('; ') || 'unsupported', executor: 'dream' };
|
|
51
52
|
return { success: true, output: 'Dream cycle complete', executor: 'dream' };
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
async function runSessionIngest(task, ctx) {
|
|
55
|
-
const { scanAndIngest } = await import('./session-watcher.mjs');
|
|
56
|
-
const result = scanAndIngest(ctx.sessionsDir);
|
|
57
|
-
return {
|
|
58
|
-
success: true,
|
|
59
|
-
output: `Ingested ${result.ingested} new sessions`,
|
|
60
|
-
executor: 'session-ingest',
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async function runSurfaceCompile(task, ctx) {
|
|
65
|
-
const { compileSurface } = await import('./surface.mjs');
|
|
66
|
-
await compileSurface({
|
|
67
|
-
vaultDir: ctx.vaultDir,
|
|
68
|
-
skillsDir: ctx.skillsDir,
|
|
69
|
-
derivedDir: ctx.derivedDir,
|
|
70
|
-
instructionsFile: ctx.instructionsFile,
|
|
71
|
-
});
|
|
72
|
-
return { success: true, output: 'Surface recompiled', executor: 'surface-compile' };
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async function runPrune(task, ctx) {
|
|
76
|
-
const { autoPruneStorage } = await import('./dream.mjs');
|
|
77
|
-
autoPruneStorage(ctx.brainDir, ctx.vaultDir, ctx.conflictsDir);
|
|
78
|
-
return { success: true, output: 'Storage prune complete', executor: 'prune' };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
55
|
async function runCustom(task, ctx) {
|
|
82
56
|
const land = task.result?.land || 'inbox';
|
|
83
57
|
const intent = task.intent || task.body || task.reason || task.slug;
|
|
@@ -89,8 +63,8 @@ async function runCustom(task, ctx) {
|
|
|
89
63
|
message: `Custom task (log only): ${task.slug} — ${intent}`,
|
|
90
64
|
});
|
|
91
65
|
return {
|
|
92
|
-
success:
|
|
93
|
-
|
|
66
|
+
success: false,
|
|
67
|
+
error: 'unsupported',
|
|
94
68
|
executor: 'custom',
|
|
95
69
|
};
|
|
96
70
|
}
|
|
@@ -207,7 +181,7 @@ async function runLegacyCategoryDispatch(task, ctx) {
|
|
|
207
181
|
}
|
|
208
182
|
|
|
209
183
|
if (category === 'system2-deliberation') {
|
|
210
|
-
if (process.env.TR_POWER_EXECUTORS !== '1' && !task._research_id) {
|
|
184
|
+
if (process.env.TR_POWER_EXECUTORS !== '1' && !task._research_id && !(task.created_by === 'automations' && task.payload?.scheduledIdle)) {
|
|
211
185
|
return {
|
|
212
186
|
success: false,
|
|
213
187
|
error: 'system2-deliberation requires TR_POWER_EXECUTORS=1 (or research-queue phase)',
|
|
@@ -221,6 +195,7 @@ async function runLegacyCategoryDispatch(task, ctx) {
|
|
|
221
195
|
inboxDir: path.join(BRAIN_DIR, 'memory-inbox', 'pending'),
|
|
222
196
|
runtimeConfig,
|
|
223
197
|
});
|
|
198
|
+
if (result.error || result.success === false) return { success: false, error: result.error || 'Inference failed', executor: 'system2' };
|
|
224
199
|
return {
|
|
225
200
|
success: true,
|
|
226
201
|
output: `Inference complete: ${result.conclusions?.length || 0} conclusions`,
|
|
@@ -253,7 +228,9 @@ async function runLegacyCategoryDispatch(task, ctx) {
|
|
|
253
228
|
vaultDir: VAULT_DIR,
|
|
254
229
|
inboxDir: path.join(BRAIN_DIR, 'memory-inbox', 'pending'),
|
|
255
230
|
runtimeConfig,
|
|
231
|
+
fromTurn: 1,
|
|
256
232
|
});
|
|
233
|
+
if (result.error) return { success: false, error: result.error, executor: 'post-mortem' };
|
|
257
234
|
return {
|
|
258
235
|
success: true,
|
|
259
236
|
output: `Post-mortem: ${result.nodesCreated || 0} nodes`,
|
|
@@ -280,6 +257,7 @@ async function runLegacyCategoryDispatch(task, ctx) {
|
|
|
280
257
|
inboxDir: path.join(BRAIN_DIR, 'memory-inbox', 'pending'),
|
|
281
258
|
runtimeConfig,
|
|
282
259
|
});
|
|
260
|
+
if (result.error || result.success === false) return { success: false, error: result.error || 'Clarity review failed', executor: 'clarity' };
|
|
283
261
|
return {
|
|
284
262
|
success: true,
|
|
285
263
|
output: `Clarity review: rewrote=${result.rewrote}`,
|
|
@@ -287,17 +265,8 @@ async function runLegacyCategoryDispatch(task, ctx) {
|
|
|
287
265
|
};
|
|
288
266
|
}
|
|
289
267
|
if (String(task.slug || '').startsWith('memory-compaction-')) {
|
|
290
|
-
const {
|
|
291
|
-
|
|
292
|
-
vaultDir: VAULT_DIR,
|
|
293
|
-
inboxDir: path.join(BRAIN_DIR, 'memory-inbox', 'pending'),
|
|
294
|
-
runtimeConfig,
|
|
295
|
-
});
|
|
296
|
-
return {
|
|
297
|
-
success: true,
|
|
298
|
-
output: `Compaction: ${result.consolidatedCount || 0} nodes`,
|
|
299
|
-
executor: 'compaction',
|
|
300
|
-
};
|
|
268
|
+
const { fileCleanupProposals } = await import('./cleanup-task.mjs');
|
|
269
|
+
return fileCleanupProposals(VAULT_DIR);
|
|
301
270
|
}
|
|
302
271
|
// generic maintenance: lease vacuum
|
|
303
272
|
try {
|
|
@@ -324,10 +293,10 @@ async function runLegacyCategoryDispatch(task, ctx) {
|
|
|
324
293
|
return { success: true, output: 'Maintenance complete', executor: 'maintenance' };
|
|
325
294
|
}
|
|
326
295
|
|
|
327
|
-
if (category === '
|
|
296
|
+
if (category === 'self-evaluation' || category === 'skill-engineering') {
|
|
328
297
|
return {
|
|
329
|
-
success:
|
|
330
|
-
|
|
298
|
+
success: false,
|
|
299
|
+
error: 'unsupported',
|
|
331
300
|
executor: category,
|
|
332
301
|
};
|
|
333
302
|
}
|
|
@@ -335,7 +304,7 @@ async function runLegacyCategoryDispatch(task, ctx) {
|
|
|
335
304
|
// Unknown — fail loud
|
|
336
305
|
return {
|
|
337
306
|
success: false,
|
|
338
|
-
error:
|
|
307
|
+
error: 'unsupported',
|
|
339
308
|
executor: 'unknown',
|
|
340
309
|
};
|
|
341
310
|
}
|
|
@@ -375,7 +344,7 @@ async function runResearchLegacy(task, ctx) {
|
|
|
375
344
|
// researched — so it is now a no-op (RESEARCH_SYSTEM2).
|
|
376
345
|
const topic = String(task.target || '').trim();
|
|
377
346
|
if (!topic || topic === 'global') {
|
|
378
|
-
return { success:
|
|
347
|
+
return { success: false, error: 'unsupported', executor: 'research' };
|
|
379
348
|
}
|
|
380
349
|
|
|
381
350
|
// Prefer deep multi-source research when this came from the research queue
|
|
@@ -514,7 +483,7 @@ async function runResearchLegacy(task, ctx) {
|
|
|
514
483
|
quarantineDir: path.join(BRAIN_DIR, 'memory-inbox', 'quarantine'),
|
|
515
484
|
});
|
|
516
485
|
if (result.skipped) {
|
|
517
|
-
return { success:
|
|
486
|
+
return { success: false, error: 'unsupported', executor: 'research' };
|
|
518
487
|
}
|
|
519
488
|
return {
|
|
520
489
|
success: true,
|
|
@@ -525,7 +494,7 @@ async function runResearchLegacy(task, ctx) {
|
|
|
525
494
|
|
|
526
495
|
return {
|
|
527
496
|
success: false,
|
|
528
|
-
error:
|
|
497
|
+
error: 'unsupported',
|
|
529
498
|
executor: 'research',
|
|
530
499
|
};
|
|
531
500
|
}
|
|
@@ -571,47 +540,39 @@ function findLatestResearchDraftSlug(inboxDir, topic) {
|
|
|
571
540
|
|
|
572
541
|
// ─── Registry ───────────────────────────────────────────────────────────────────
|
|
573
542
|
|
|
574
|
-
async function
|
|
575
|
-
const {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const { runSecretsExportAll } = await __loadOwnedPlugin('auth', 'secrets-rotate');
|
|
581
|
-
return runSecretsExportAll(ctx);
|
|
543
|
+
async function runSkillGap(task, ctx) {
|
|
544
|
+
const { requestSkillGap } = await import('./skill-gap.mjs');
|
|
545
|
+
const topic = task.payload?.topic || task.target || /topic "([^"]+)"/.exec(task.reason || '')?.[1];
|
|
546
|
+
const session = task.source_session || task.origin?.session_id;
|
|
547
|
+
return requestSkillGap(topic, task.payload?.evidence ||
|
|
548
|
+
(session ? `session://${session}` : null), { projectRoot: ctx.projectRoot || process.cwd() });
|
|
582
549
|
}
|
|
583
550
|
|
|
584
|
-
async function
|
|
585
|
-
|
|
586
|
-
const
|
|
587
|
-
if (!
|
|
588
|
-
|
|
551
|
+
async function runLessonReview(task, ctx) {
|
|
552
|
+
const { runPostMortem } = await import('./post-mortem.mjs');
|
|
553
|
+
const sessionPath = task.payload?.sessionPath || (task.target && path.join(ctx.sessionsDir, task.target));
|
|
554
|
+
if (!sessionPath) return { success: false, error: 'Lesson review requires a session transcript and turn range' };
|
|
555
|
+
const result = await runPostMortem(sessionPath, {
|
|
556
|
+
vaultDir: ctx.vaultDir, inboxDir: path.join(ctx.brainDir, 'memory-inbox', 'pending'), runtimeConfig: ctx.runtimeConfig,
|
|
557
|
+
// Scheduled snapshots already contain exactly the requested range.
|
|
558
|
+
fromTurn: task.payload?.sessionPath ? 1 : task.payload?.fromTurn,
|
|
559
|
+
toTurn: task.payload?.sessionPath ? Infinity : task.payload?.toTurn,
|
|
560
|
+
sourceRef: task.payload?.sessionId ? `${task.payload.sessionId}/turns/${task.payload.fromTurn}-${task.payload.toTurn}` : task.slug,
|
|
561
|
+
hold: task.payload?.hold === true,
|
|
562
|
+
});
|
|
563
|
+
if (result.error) return { success: false, error: result.error, executor: 'lesson-review' };
|
|
564
|
+
if (task.slug?.startsWith('lesson-review-')) {
|
|
565
|
+
const dir = path.join(ctx.brainDir, 'lesson-reviews');
|
|
566
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
567
|
+
atomicWrite(path.join(dir, `${task.slug}.done`), new Date().toISOString());
|
|
589
568
|
}
|
|
590
|
-
|
|
591
|
-
const assist = await getBrowserRotateAssist(ctx.brainDir, key);
|
|
592
|
-
return {
|
|
593
|
-
success: true,
|
|
594
|
-
output: `Browser rotation assist ready for ${key}. Console: ${assist.console_url || 'n/a'}. Use browser tools with human 2FA; then secret rotate --export-env.`,
|
|
595
|
-
executor: 'secrets-rotation-assist',
|
|
596
|
-
assist: {
|
|
597
|
-
key: assist.key,
|
|
598
|
-
console_url: assist.console_url,
|
|
599
|
-
docs_url: assist.docs_url,
|
|
600
|
-
overdue: assist.overdue,
|
|
601
|
-
// prompt available to agent; do not echo secrets
|
|
602
|
-
prompt: assist.prompt,
|
|
603
|
-
},
|
|
604
|
-
};
|
|
569
|
+
return { success: true, output: `Lesson review: ${result.patterns?.length || 0} patterns, ${result.facts?.length || 0} facts, ${result.skill_gaps?.length || 0} skill gaps, ${result.violations?.length || 0} violations`, executor: 'lesson-review' };
|
|
605
570
|
}
|
|
606
571
|
|
|
607
572
|
const EXECUTORS = {
|
|
608
|
-
'
|
|
609
|
-
'
|
|
610
|
-
'secrets-rotation-assist': { id: 'secrets-rotation-assist', run: runSecretsRotationAssist },
|
|
573
|
+
'lesson-review': { id: 'lesson-review', run: runLessonReview },
|
|
574
|
+
'skill-manager': { id: 'skill-manager', run: runSkillGap },
|
|
611
575
|
dream: { id: 'dream', run: runDream },
|
|
612
|
-
'session-ingest': { id: 'session-ingest', run: runSessionIngest },
|
|
613
|
-
'surface-compile': { id: 'surface-compile', run: runSurfaceCompile },
|
|
614
|
-
prune: { id: 'prune', run: runPrune },
|
|
615
576
|
custom: { id: 'custom', run: runCustom },
|
|
616
577
|
research: { id: 'research', run: runResearchLegacy },
|
|
617
578
|
legacy: { id: 'legacy', run: runLegacyDispatch },
|
|
@@ -623,31 +584,16 @@ const EXECUTORS = {
|
|
|
623
584
|
export function resolveExecutor(task) {
|
|
624
585
|
const t = normalizeTask(task);
|
|
625
586
|
|
|
626
|
-
if (t.
|
|
627
|
-
|
|
628
|
-
|
|
587
|
+
if (t.category === 'skill-engineering') return EXECUTORS['skill-manager'];
|
|
588
|
+
if (t.category === 'lesson-review' || t.category === 'self-evaluation') return EXECUTORS['lesson-review'];
|
|
589
|
+
|
|
590
|
+
// An explicit unsupported executor must never fall through to kind/category.
|
|
591
|
+
if (t.executor) return typeof t.executor === 'string' && Object.hasOwn(EXECUTORS, t.executor) ? EXECUTORS[t.executor] : null;
|
|
629
592
|
|
|
630
593
|
// System / named slugs
|
|
631
594
|
if (t.slug === 'dream' || String(t.slug || '').startsWith('dream-') || t.executor === 'dream') {
|
|
632
595
|
return EXECUTORS.dream;
|
|
633
596
|
}
|
|
634
|
-
if (String(t.slug || '').startsWith('session-ingest')) return EXECUTORS['session-ingest'];
|
|
635
|
-
if (String(t.slug || '').startsWith('surface-compile')) return EXECUTORS['surface-compile'];
|
|
636
|
-
if (String(t.slug || '').startsWith('prune-') || t.slug === 'prune') return EXECUTORS.prune;
|
|
637
|
-
if (String(t.slug || '').startsWith('secret-rotate-') || t.category === 'secrets-rotation') {
|
|
638
|
-
return EXECUTORS['secrets-rotation-assist'];
|
|
639
|
-
}
|
|
640
|
-
if (String(t.slug || '').startsWith('secrets-rotation-check') || t.executor === 'secrets-rotation-check') {
|
|
641
|
-
return EXECUTORS['secrets-rotation-check'];
|
|
642
|
-
}
|
|
643
|
-
if (String(t.slug || '').startsWith('secrets-export') || t.executor === 'secrets-export-env') {
|
|
644
|
-
return EXECUTORS['secrets-export-env'];
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
if (t.kind === 'custom' || t.category === 'custom' || t.executor === 'custom') {
|
|
648
|
-
return EXECUTORS.custom;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
597
|
// Known legacy categories still registered
|
|
652
598
|
const legacyCategories = new Set([
|
|
653
599
|
'conscious-enforcement',
|
|
@@ -658,13 +604,14 @@ export function resolveExecutor(task) {
|
|
|
658
604
|
'proactive-research',
|
|
659
605
|
'skill-engineering',
|
|
660
606
|
'self-evaluation',
|
|
661
|
-
'exploration',
|
|
662
607
|
]);
|
|
663
608
|
|
|
664
609
|
if (legacyCategories.has(t.category) || t._research_id || t._is_remote_vault_sync) {
|
|
665
610
|
return EXECUTORS.legacy;
|
|
666
611
|
}
|
|
667
612
|
|
|
613
|
+
if (t.category === 'custom' || (t.kind === 'custom' && !t.category)) return EXECUTORS.custom;
|
|
614
|
+
|
|
668
615
|
if (t.kind === 'research') return EXECUTORS.research;
|
|
669
616
|
if (t.kind === 'system' || t.kind === 'maintenance' || t.kind === 'memory') {
|
|
670
617
|
return EXECUTORS.legacy;
|
|
@@ -691,11 +638,12 @@ export async function dispatchTask(task, ctx) {
|
|
|
691
638
|
`Unknown task executor for slug=${normalized.slug} ` +
|
|
692
639
|
`category=${normalized.category} kind=${normalized.kind} executor=${normalized.executor}`;
|
|
693
640
|
logger.info({ subsystem: 'task-executors', message: msg });
|
|
694
|
-
return { success: false, error:
|
|
641
|
+
return { success: false, error: 'unsupported', executor: 'unknown' };
|
|
695
642
|
}
|
|
696
643
|
|
|
697
644
|
try {
|
|
698
645
|
const result = await executor.run(normalized, ctx);
|
|
646
|
+
if (!result || typeof result.success !== 'boolean') return { success: false, error: 'unsupported', executor: executor.id };
|
|
699
647
|
return { ...result, executor: result.executor || executor.id };
|
|
700
648
|
} catch (err) {
|
|
701
649
|
return {
|
|
@@ -15,6 +15,8 @@ import { processOperationAsync } from './operation-validator.mjs';
|
|
|
15
15
|
import { safeStringify, isSafeVaultName } from './vault.mjs';
|
|
16
16
|
import { invalidate } from './vault-cache.mjs';
|
|
17
17
|
|
|
18
|
+
export const supportsExpectedBeforeHash = true;
|
|
19
|
+
|
|
18
20
|
/**
|
|
19
21
|
* Normalize a vault node so package universal frontmatter + TR memory v2 pass.
|
|
20
22
|
*/
|
|
@@ -101,6 +103,7 @@ function buildEnvelope(node, options = {}) {
|
|
|
101
103
|
const {
|
|
102
104
|
workspaceId = 'default',
|
|
103
105
|
idempotencyKey,
|
|
106
|
+
expectedBeforeHash,
|
|
104
107
|
dryRun = false,
|
|
105
108
|
path: pathOverride,
|
|
106
109
|
} = options;
|
|
@@ -124,6 +127,7 @@ function buildEnvelope(node, options = {}) {
|
|
|
124
127
|
path: vfsPath,
|
|
125
128
|
workspace_id: workspaceId,
|
|
126
129
|
content,
|
|
130
|
+
...(expectedBeforeHash === undefined ? {} : { expected_before_hash: expectedBeforeHash }),
|
|
127
131
|
dry_run: !!dryRun,
|
|
128
132
|
actor: { role: options.agentRole || 'system' },
|
|
129
133
|
};
|
|
@@ -152,6 +156,7 @@ export async function writeNodeValidatedAsync(node, vaultDir, options = {}) {
|
|
|
152
156
|
agentRole,
|
|
153
157
|
leaseStore,
|
|
154
158
|
eventLogDir,
|
|
159
|
+
allowLong: options.allowLong === true,
|
|
155
160
|
});
|
|
156
161
|
|
|
157
162
|
if (result.success && !dryRun) {
|
package/src/core/vault-files.mjs
CHANGED
|
@@ -8,6 +8,12 @@ export function isSafeVaultName(name) {
|
|
|
8
8
|
|
|
9
9
|
export function atomicWrite(filePath, content) {
|
|
10
10
|
const tmpPath = `${filePath}.tmp.${process.pid}`;
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
try {
|
|
12
|
+
fs.writeFileSync(tmpPath, content, 'utf8');
|
|
13
|
+
fs.renameSync(tmpPath, filePath);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
// A leftover copy is untracked and unignored, so a commit of everything would publish it.
|
|
16
|
+
fs.rmSync(tmpPath, { force: true });
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
13
19
|
}
|
package/src/core/vault.mjs
CHANGED
|
@@ -33,7 +33,7 @@ function stripUndefined(obj) {
|
|
|
33
33
|
* @returns {string} The serialized markdown with frontmatter
|
|
34
34
|
*/
|
|
35
35
|
export function safeStringify(content, data) {
|
|
36
|
-
return matter.stringify(content ?? '', stripUndefined(data ?? {}));
|
|
36
|
+
return matter.stringify({ content: content ?? '', data: stripUndefined(data ?? {}) });
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|