dual-brain 0.2.8 → 0.2.10
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/dual-brain.mjs +208 -42
- package/package.json +9 -2
- package/src/agents/registry.mjs +405 -0
- package/src/collaboration.mjs +545 -0
- package/src/detect.mjs +73 -1
- package/src/dispatch.mjs +47 -5
- package/src/head.mjs +705 -263
- package/src/pipeline.mjs +387 -163
- package/src/profile.mjs +82 -1
- package/src/provider-context.mjs +257 -0
package/src/dispatch.mjs
CHANGED
|
@@ -15,6 +15,7 @@ import { createHash } from 'node:crypto';
|
|
|
15
15
|
import { markHot, markDegraded, markHealthy, recordDispatch } from './health.mjs';
|
|
16
16
|
import { redact } from './redact.mjs';
|
|
17
17
|
import { getFailoverOrder } from './decide.mjs';
|
|
18
|
+
import { getTemplate, renderPrompt, quickRender } from './templates.mjs';
|
|
18
19
|
|
|
19
20
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
20
21
|
const USAGE_DIR = join(__dirname, '..', '.dualbrain', 'usage');
|
|
@@ -675,6 +676,30 @@ function runProcess(cmd, cwd, timeoutMs, env) {
|
|
|
675
676
|
});
|
|
676
677
|
}
|
|
677
678
|
|
|
679
|
+
// ─── Template-based prompt rendering ─────────────────────────────────────────
|
|
680
|
+
|
|
681
|
+
function _renderTemplatedPrompt(prompt, decision, context = {}) {
|
|
682
|
+
const tier = decision.tier ?? 'execute';
|
|
683
|
+
const template = getTemplate(tier);
|
|
684
|
+
if (!template) return prompt;
|
|
685
|
+
|
|
686
|
+
if (decision.contract) {
|
|
687
|
+
const rendered = renderPrompt(tier, decision.contract, context);
|
|
688
|
+
if (rendered.valid) return rendered.prompt;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
const rendered = quickRender(tier, prompt, {
|
|
692
|
+
scope: decision.owns || decision.scope || [],
|
|
693
|
+
files: decision.files || [],
|
|
694
|
+
risk: decision.risk || 'medium',
|
|
695
|
+
criteria: decision.acceptanceCriteria || [],
|
|
696
|
+
nonGoals: decision.nonGoals || [],
|
|
697
|
+
context: decision.taskContext || '',
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
return rendered.valid ? rendered.prompt : prompt;
|
|
701
|
+
}
|
|
702
|
+
|
|
678
703
|
// ─── Dispatch marker ─────────────────────────────────────────────────────────
|
|
679
704
|
// Prepend a marker to every prompt that goes through the official dispatch pipeline.
|
|
680
705
|
// The enforce-tier hook checks for this marker to distinguish legitimate dispatches
|
|
@@ -725,6 +750,11 @@ async function dispatch(input = {}) {
|
|
|
725
750
|
// Safety gate: redact secrets before anything reaches a subprocess or log
|
|
726
751
|
prompt = redact(prompt);
|
|
727
752
|
|
|
753
|
+
// ── Template-based prompt rendering ─────────────────────────────────────────
|
|
754
|
+
// When a tier and/or contract are present, render through templates.mjs for
|
|
755
|
+
// structured, typed prompts. Falls back to raw prompt when no template matches.
|
|
756
|
+
prompt = _renderTemplatedPrompt(prompt, decision);
|
|
757
|
+
|
|
728
758
|
// ── Resume brief injection ───────────────────────────────────────────────────
|
|
729
759
|
// Inject the last session's receipt as context when no situationBrief is already set.
|
|
730
760
|
// This closes the receipt → brief → next session loop automatically.
|
|
@@ -738,15 +768,27 @@ async function dispatch(input = {}) {
|
|
|
738
768
|
}
|
|
739
769
|
} catch { /* non-blocking */ }
|
|
740
770
|
|
|
741
|
-
//
|
|
771
|
+
// Provider-aware continuity fallback: adapts resume format for target provider
|
|
742
772
|
if (!input.situationBrief) {
|
|
743
773
|
try {
|
|
744
|
-
const {
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
|
|
774
|
+
const { buildProviderResumeBrief } = await import('./provider-context.mjs');
|
|
775
|
+
const targetProvider = decision.provider || 'claude';
|
|
776
|
+
const providerBrief = buildProviderResumeBrief(cwd, targetProvider);
|
|
777
|
+
if (providerBrief) {
|
|
778
|
+
input = { ...input, situationBrief: providerBrief };
|
|
748
779
|
}
|
|
749
780
|
} catch { /* non-blocking */ }
|
|
781
|
+
|
|
782
|
+
// Legacy fallback: continuity.mjs handoff (provider-unaware)
|
|
783
|
+
if (!input.situationBrief) {
|
|
784
|
+
try {
|
|
785
|
+
const { buildResumeBrief: buildHandoffBrief } = await import('./continuity.mjs');
|
|
786
|
+
const handoffBrief = buildHandoffBrief(cwd);
|
|
787
|
+
if (handoffBrief) {
|
|
788
|
+
input = { ...input, situationBrief: handoffBrief };
|
|
789
|
+
}
|
|
790
|
+
} catch { /* non-blocking */ }
|
|
791
|
+
}
|
|
750
792
|
}
|
|
751
793
|
}
|
|
752
794
|
// ── End resume brief injection ───────────────────────────────────────────────
|