nexus-prime 7.9.21 → 7.9.23
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/dist/agents/adapters/ide-compat.d.ts +3 -3
- package/dist/agents/adapters/ide-compat.js +51 -1
- package/dist/agents/adapters/mcp/definitions.js +4 -1
- package/dist/agents/adapters/mcp/dispatch.js +66 -4
- package/dist/agents/adapters/mcp/handlers/memory.js +44 -5
- package/dist/agents/adapters/mcp/handlers/orchestration.js +91 -0
- package/dist/agents/adapters/mcp/runHandler.js +3 -0
- package/dist/agents/adapters/mcp/util/detect-caller.js +21 -0
- package/dist/agents/adapters/mcp.js +1 -1
- package/dist/agents/adapters.d.ts +10 -1
- package/dist/agents/adapters.js +21 -0
- package/dist/agents/core/types.d.ts +1 -1
- package/dist/cli/hook.d.ts +4 -6
- package/dist/cli/hook.js +6 -8
- package/dist/cli/install-wizard.js +5 -1
- package/dist/cli.js +181 -15
- package/dist/core/types.d.ts +1 -1
- package/dist/dashboard/app/styles/board.css +85 -1
- package/dist/dashboard/app/styles/runtime.css +148 -0
- package/dist/dashboard/app/styles/workforce.css +28 -0
- package/dist/dashboard/app/views/board.js +56 -0
- package/dist/dashboard/app/views/memory.js +71 -10
- package/dist/dashboard/app/views/runtime.js +138 -4
- package/dist/dashboard/app/views/workforce.js +11 -4
- package/dist/dashboard/routes/events.js +3 -0
- package/dist/dashboard/selectors/operate-selector.js +5 -0
- package/dist/dashboard/selectors/runs-selector.js +5 -0
- package/dist/dashboard/server.js +6 -0
- package/dist/dashboard/types.d.ts +4 -0
- package/dist/engines/client-bootstrap.d.ts +5 -1
- package/dist/engines/client-bootstrap.js +105 -10
- package/dist/engines/client-registry.js +51 -0
- package/dist/engines/event-bus.d.ts +20 -2
- package/dist/engines/feature-registry.js +1 -0
- package/dist/engines/instruction-gateway.d.ts +9 -0
- package/dist/engines/instruction-gateway.js +113 -4
- package/dist/engines/memory/types.d.ts +28 -0
- package/dist/engines/memory-bridge.d.ts +1 -1
- package/dist/engines/memory-bridge.js +1 -1
- package/dist/engines/memory.d.ts +5 -0
- package/dist/engines/memory.js +144 -12
- package/dist/engines/orchestrator/decision-spine.d.ts +26 -0
- package/dist/engines/orchestrator/decision-spine.js +145 -6
- package/dist/engines/orchestrator/funnel.js +8 -1
- package/dist/engines/orchestrator/scoring.d.ts +1 -1
- package/dist/engines/orchestrator/scoring.js +24 -2
- package/dist/engines/orchestrator.d.ts +3 -0
- package/dist/engines/orchestrator.js +73 -13
- package/dist/engines/peer-connectors.d.ts +1 -1
- package/dist/engines/peer-connectors.js +9 -2
- package/dist/engines/runtime-registry.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/install/state-locator.d.ts +1 -1
- package/dist/install/state-locator.js +3 -0
- package/dist/synapse/bootstrap.js +3 -0
- package/dist/synapse/mandate/pipeline.js +52 -5
- package/dist/synapse/sorties/runner.js +32 -0
- package/dist/synapse/types.d.ts +27 -0
- package/package.json +1 -1
package/dist/engines/memory.js
CHANGED
|
@@ -27,6 +27,7 @@ import { createMemoryContentFingerprint } from './memory-fingerprint.js';
|
|
|
27
27
|
import { resolveCurrentRepoId, warmRepoId, resolveCurrentProjectId, resolvePreferredStateRoot, openMemoryDatabase, } from './memory/schema.js';
|
|
28
28
|
import { safeParseTags, safeParseObject, dedupeStrings, isOperatorVisibleTag, seedDemoMemoriesOnFirstBoot } from './memory/store.js';
|
|
29
29
|
import { dedupeReferences, dedupeLinks, sanitizeFileName } from './memory/recall.js';
|
|
30
|
+
import { QMD_WEIGHTS } from './memory/entropy-decay.js';
|
|
30
31
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
31
32
|
// Types — extracted to memory/types.ts (Phase 1 of v7.x memory split)
|
|
32
33
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -1788,19 +1789,32 @@ export class MemoryEngine {
|
|
|
1788
1789
|
let scope = existing.scope;
|
|
1789
1790
|
let state = existing.state;
|
|
1790
1791
|
let tags = [...existing.tags];
|
|
1792
|
+
const qmd = this.qmdComponents(existing);
|
|
1793
|
+
let entropy = existing.entropy;
|
|
1794
|
+
let qmdRecency = qmd.recency;
|
|
1795
|
+
let qmdFrequency = qmd.frequency;
|
|
1796
|
+
let qmdRelevance = qmd.relevance;
|
|
1791
1797
|
if (reaction === 'positive') {
|
|
1792
1798
|
priority = Math.min(1, priority + 0.06 * Math.max(rating, 0));
|
|
1793
1799
|
trust = Math.min(1, trust + 0.12 * Math.max(rating, 0));
|
|
1800
|
+
entropy = Math.max(0, entropy - 0.08 * Math.max(rating, 0));
|
|
1801
|
+
qmdFrequency = Math.min(1, qmdFrequency + 0.08);
|
|
1802
|
+
qmdRelevance = Math.min(1, qmdRelevance + 0.1 * Math.max(rating, 0));
|
|
1794
1803
|
}
|
|
1795
1804
|
else if (reaction === 'negative') {
|
|
1796
1805
|
priority = Math.max(0.05, priority + 0.08 * Math.min(rating, 0));
|
|
1797
1806
|
trust = Math.max(0.05, trust + 0.18 * Math.min(rating, 0));
|
|
1807
|
+
entropy = Math.min(1, entropy + 0.12 * Math.abs(Math.min(rating, 0)));
|
|
1808
|
+
qmdRelevance = Math.max(0, qmdRelevance - 0.12 * Math.abs(Math.min(rating, 0)));
|
|
1798
1809
|
}
|
|
1799
1810
|
else if (reaction === 'promote') {
|
|
1800
1811
|
priority = Math.min(1, Math.max(priority, 0.9));
|
|
1801
1812
|
trust = Math.min(1, Math.max(trust, 0.82));
|
|
1802
1813
|
scope = 'promoted';
|
|
1803
1814
|
tags = dedupeStrings([...tags.filter((tag) => tag !== '#quarantine'), '#promoted']);
|
|
1815
|
+
entropy = Math.max(0, entropy - 0.18);
|
|
1816
|
+
qmdFrequency = Math.min(1, qmdFrequency + 0.18);
|
|
1817
|
+
qmdRelevance = Math.min(1, Math.max(qmdRelevance, 0.82));
|
|
1804
1818
|
}
|
|
1805
1819
|
else if (reaction === 'demote') {
|
|
1806
1820
|
priority = Math.max(0.1, priority - 0.08);
|
|
@@ -1808,11 +1822,17 @@ export class MemoryEngine {
|
|
|
1808
1822
|
if (scope === 'promoted')
|
|
1809
1823
|
scope = 'project';
|
|
1810
1824
|
tags = tags.filter((tag) => tag !== '#promoted');
|
|
1825
|
+
entropy = Math.min(1, entropy + 0.1);
|
|
1826
|
+
qmdFrequency = Math.max(0, qmdFrequency - 0.08);
|
|
1827
|
+
qmdRelevance = Math.max(0, qmdRelevance - 0.12);
|
|
1811
1828
|
}
|
|
1812
1829
|
else if (reaction === 'quarantine') {
|
|
1813
1830
|
state = 'quarantined';
|
|
1814
1831
|
tags = dedupeStrings([...tags, '#quarantine']);
|
|
1832
|
+
entropy = Math.min(1, entropy + 0.18);
|
|
1833
|
+
qmdRelevance = Math.max(0, qmdRelevance - 0.2);
|
|
1815
1834
|
}
|
|
1835
|
+
qmdRecency = Math.min(1, qmdRecency + 0.05);
|
|
1816
1836
|
const nextExpiresAt = state === 'active'
|
|
1817
1837
|
? existing.expiresAt
|
|
1818
1838
|
: state === 'quarantined'
|
|
@@ -1834,9 +1854,9 @@ export class MemoryEngine {
|
|
|
1834
1854
|
this.withSqliteRetry('submitMemoryFeedback', () => {
|
|
1835
1855
|
this.db.prepare(`
|
|
1836
1856
|
UPDATE memories
|
|
1837
|
-
SET priority = ?, trust = ?, scope = ?, state = ?, tags = ?, provenance_json = ?, expires_at = ?, purge_at = ?
|
|
1857
|
+
SET priority = ?, trust = ?, scope = ?, state = ?, tags = ?, entropy = ?, qmd_recency = ?, qmd_frequency = ?, qmd_relevance = ?, provenance_json = ?, expires_at = ?, purge_at = ?
|
|
1838
1858
|
WHERE id = ?
|
|
1839
|
-
`).run(priority, trust, scope, state, JSON.stringify(dedupeStrings(tags)), JSON.stringify(provenance), nextExpiresAt ?? null, nextPurgeAt ?? null, id);
|
|
1859
|
+
`).run(priority, trust, scope, state, JSON.stringify(dedupeStrings(tags)), entropy, qmdRecency, qmdFrequency, qmdRelevance, JSON.stringify(provenance), nextExpiresAt ?? null, nextPurgeAt ?? null, id);
|
|
1840
1860
|
});
|
|
1841
1861
|
const historyEntry = this.appendHistory(id, 'feedback', `Memory feedback recorded: ${reaction}.`, {
|
|
1842
1862
|
reaction,
|
|
@@ -1844,17 +1864,24 @@ export class MemoryEngine {
|
|
|
1844
1864
|
note: input.note ?? null,
|
|
1845
1865
|
priority,
|
|
1846
1866
|
trust,
|
|
1867
|
+
entropy,
|
|
1868
|
+
qmdRecency,
|
|
1869
|
+
qmdFrequency,
|
|
1870
|
+
qmdRelevance,
|
|
1847
1871
|
scope,
|
|
1848
1872
|
state,
|
|
1849
1873
|
}, input.provenance ?? provenance);
|
|
1850
1874
|
this.markVaultDirty(id);
|
|
1851
1875
|
this.syncVault(true);
|
|
1876
|
+
const item = this.getSnapshot(id);
|
|
1852
1877
|
return {
|
|
1853
1878
|
accepted: true,
|
|
1854
1879
|
historyEntry,
|
|
1855
|
-
item
|
|
1880
|
+
item,
|
|
1856
1881
|
trust,
|
|
1857
1882
|
priority,
|
|
1883
|
+
decay: item?.decay,
|
|
1884
|
+
reinforcement: item?.reinforcement,
|
|
1858
1885
|
};
|
|
1859
1886
|
}
|
|
1860
1887
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -2024,15 +2051,20 @@ export class MemoryEngine {
|
|
|
2024
2051
|
this.db.prepare(`
|
|
2025
2052
|
UPDATE memories
|
|
2026
2053
|
SET entropy = MAX(0.0, MIN(1.0, entropy + ?)),
|
|
2027
|
-
priority = priority *
|
|
2054
|
+
priority = priority * ?,
|
|
2055
|
+
qmd_recency = MAX(0.0, MIN(1.0, COALESCE(qmd_recency, 1.0) - (? * 0.5))),
|
|
2056
|
+
qmd_frequency = MAX(0.0, MIN(1.0, COALESCE(qmd_frequency, MIN(1.0, CAST(access_count AS REAL) / 20.0)) * (1.0 - ?))),
|
|
2057
|
+
qmd_relevance = MAX(0.0, MIN(1.0, COALESCE(qmd_relevance, 1.0 - entropy) - (? * 0.35)))
|
|
2028
2058
|
WHERE tier = ?
|
|
2029
|
-
`).run(rate, tier === 'cortex' ? 1.0 : priorityRetention, tier);
|
|
2059
|
+
`).run(rate, tier === 'cortex' ? 1.0 : priorityRetention, rate, rate, rate, tier);
|
|
2030
2060
|
// Apply access-frequency retention: frequently accessed memories decay slower
|
|
2031
2061
|
this.db.prepare(`
|
|
2032
2062
|
UPDATE memories
|
|
2033
|
-
SET entropy = MAX(0.0, MIN(1.0, entropy - (? / (1.0 + CAST(access_count AS REAL) * 0.1))))
|
|
2063
|
+
SET entropy = MAX(0.0, MIN(1.0, entropy - (? / (1.0 + CAST(access_count AS REAL) * 0.1)))),
|
|
2064
|
+
qmd_frequency = MAX(0.0, MIN(1.0, COALESCE(qmd_frequency, 0.0) + (? / 2.0))),
|
|
2065
|
+
qmd_relevance = MAX(0.0, MIN(1.0, COALESCE(qmd_relevance, 1.0 - entropy) + (? / 3.0)))
|
|
2034
2066
|
WHERE tier = ? AND access_count > 0
|
|
2035
|
-
`).run(rate, tier);
|
|
2067
|
+
`).run(rate, rate, rate, tier);
|
|
2036
2068
|
}
|
|
2037
2069
|
// Force flush high entropy items (this will promote/demote based on priority)
|
|
2038
2070
|
this.consolidate();
|
|
@@ -2495,6 +2527,9 @@ export class MemoryEngine {
|
|
|
2495
2527
|
`freshnessScore: ${snapshot.freshnessScore}`,
|
|
2496
2528
|
`trustScore: ${snapshot.trustScore}`,
|
|
2497
2529
|
`entropyScore: ${snapshot.entropyScore}`,
|
|
2530
|
+
`decayState: ${snapshot.decay.state}`,
|
|
2531
|
+
`decayScore: ${snapshot.decay.score}`,
|
|
2532
|
+
`reinforcementScore: ${snapshot.reinforcement.score}`,
|
|
2498
2533
|
`timestamp: ${new Date(item.timestamp).toISOString()}`,
|
|
2499
2534
|
item.expiresAt ? `expiresAt: ${new Date(item.expiresAt).toISOString()}` : 'expiresAt:',
|
|
2500
2535
|
item.supersedes ? `supersedes: ${item.supersedes}` : 'supersedes:',
|
|
@@ -2509,6 +2544,11 @@ export class MemoryEngine {
|
|
|
2509
2544
|
`- source: ${item.provenance.source}`,
|
|
2510
2545
|
...(item.provenance.references.length > 0 ? item.provenance.references.map((reference) => `- ref: ${reference}`) : ['- ref: none']),
|
|
2511
2546
|
'',
|
|
2547
|
+
'## Lifecycle',
|
|
2548
|
+
`- decay: ${snapshot.decay.state} (${snapshot.decay.score.toFixed(3)})`,
|
|
2549
|
+
`- next action: ${snapshot.decay.nextAction}`,
|
|
2550
|
+
`- reinforcement: ${snapshot.reinforcement.score.toFixed(3)} from ${snapshot.reinforcement.feedbackCount} feedback events and ${snapshot.reinforcement.accessCount} recalls`,
|
|
2551
|
+
'',
|
|
2512
2552
|
'## Related Runtime Objects',
|
|
2513
2553
|
related || '- none',
|
|
2514
2554
|
'',
|
|
@@ -2843,13 +2883,103 @@ export class MemoryEngine {
|
|
|
2843
2883
|
`).all(entropyLimit, limit);
|
|
2844
2884
|
return rows.map((row) => this.rowToItem(row));
|
|
2845
2885
|
}
|
|
2846
|
-
|
|
2886
|
+
clamp01(value) {
|
|
2887
|
+
if (!Number.isFinite(value))
|
|
2888
|
+
return 0;
|
|
2889
|
+
return Math.max(0, Math.min(1, value));
|
|
2890
|
+
}
|
|
2891
|
+
qmdComponents(item) {
|
|
2847
2892
|
const now = Date.now();
|
|
2848
2893
|
const ageDays = (now - item.timestamp) / 86_400_000;
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2894
|
+
return {
|
|
2895
|
+
recency: this.clamp01(item.qmdRecency ?? Math.max(0, 1 - ageDays / 30)),
|
|
2896
|
+
frequency: this.clamp01(item.qmdFrequency ?? Math.min(1, item.accessCount / 20)),
|
|
2897
|
+
relevance: this.clamp01(item.qmdRelevance ?? (1 - item.entropy)),
|
|
2898
|
+
};
|
|
2899
|
+
}
|
|
2900
|
+
computeQmdScore(item) {
|
|
2901
|
+
const qmd = this.qmdComponents(item);
|
|
2902
|
+
const qmdWeightTotal = QMD_WEIGHTS.recency + QMD_WEIGHTS.frequency + QMD_WEIGHTS.relevance;
|
|
2903
|
+
const trustWeight = Math.max(0, 1 - qmdWeightTotal);
|
|
2904
|
+
return this.clamp01((QMD_WEIGHTS.recency * qmd.recency)
|
|
2905
|
+
+ (QMD_WEIGHTS.frequency * qmd.frequency)
|
|
2906
|
+
+ (QMD_WEIGHTS.relevance * qmd.relevance)
|
|
2907
|
+
+ (trustWeight * item.trust));
|
|
2908
|
+
}
|
|
2909
|
+
getFeedbackStats(memoryId) {
|
|
2910
|
+
const row = this.db.prepare(`
|
|
2911
|
+
SELECT COUNT(*) as count, MAX(timestamp) as lastAt
|
|
2912
|
+
FROM memory_history
|
|
2913
|
+
WHERE memory_id = ? AND event_type = 'feedback'
|
|
2914
|
+
`).get(memoryId);
|
|
2915
|
+
return {
|
|
2916
|
+
count: Number(row?.count ?? 0),
|
|
2917
|
+
lastAt: typeof row?.lastAt === 'number' ? row.lastAt : undefined,
|
|
2918
|
+
};
|
|
2919
|
+
}
|
|
2920
|
+
buildDecayProfile(item) {
|
|
2921
|
+
const ageDays = Math.max(0, (Date.now() - item.timestamp) / 86_400_000);
|
|
2922
|
+
const retentionHint = item.provenance.retentionHint ?? 'default';
|
|
2923
|
+
const halfLifeDays = retentionHint === 'forever'
|
|
2924
|
+
? 365
|
|
2925
|
+
: retentionHint === 'long'
|
|
2926
|
+
? 180
|
|
2927
|
+
: retentionHint === 'short'
|
|
2928
|
+
? 30
|
|
2929
|
+
: retentionHint === 'session'
|
|
2930
|
+
? 3
|
|
2931
|
+
: 90;
|
|
2932
|
+
const qmd = this.qmdComponents(item);
|
|
2933
|
+
const ageDecay = this.clamp01(1 - Math.pow(0.5, ageDays / Math.max(halfLifeDays, 1)));
|
|
2934
|
+
const accessRetention = Math.min(0.2, qmd.frequency * 0.2);
|
|
2935
|
+
const score = this.clamp01((item.entropy * 0.55) + (ageDecay * 0.3) + ((1 - qmd.relevance) * 0.15) - accessRetention);
|
|
2936
|
+
const state = score >= 0.75 ? 'retiring' : score >= 0.5 ? 'stale' : score >= 0.25 ? 'fading' : 'fresh';
|
|
2937
|
+
const nextAction = state === 'fresh'
|
|
2938
|
+
? 'keep'
|
|
2939
|
+
: state === 'fading'
|
|
2940
|
+
? 'reinforce'
|
|
2941
|
+
: state === 'stale'
|
|
2942
|
+
? 'review'
|
|
2943
|
+
: 'archive';
|
|
2944
|
+
const reasons = [
|
|
2945
|
+
item.entropy >= 0.8 ? `entropy ${item.entropy.toFixed(2)} is high` : `entropy ${item.entropy.toFixed(2)}`,
|
|
2946
|
+
ageDays >= halfLifeDays ? `age ${Math.round(ageDays)}d passed half-life ${halfLifeDays}d` : `age ${Math.round(ageDays)}d within half-life ${halfLifeDays}d`,
|
|
2947
|
+
item.accessCount > 0 ? `${item.accessCount} recalls slow decay` : 'no recall reinforcement yet',
|
|
2948
|
+
`retention ${retentionHint}`,
|
|
2949
|
+
];
|
|
2950
|
+
return {
|
|
2951
|
+
score,
|
|
2952
|
+
state,
|
|
2953
|
+
ageDays,
|
|
2954
|
+
halfLifeDays,
|
|
2955
|
+
retentionHint,
|
|
2956
|
+
nextAction,
|
|
2957
|
+
reasons,
|
|
2958
|
+
};
|
|
2959
|
+
}
|
|
2960
|
+
buildReinforcementProfile(item) {
|
|
2961
|
+
const qmd = this.qmdComponents(item);
|
|
2962
|
+
const feedback = this.getFeedbackStats(item.id);
|
|
2963
|
+
const feedbackScore = this.clamp01(feedback.count / 5);
|
|
2964
|
+
const score = this.clamp01((qmd.frequency * 0.3)
|
|
2965
|
+
+ (qmd.relevance * 0.25)
|
|
2966
|
+
+ (item.trust * 0.25)
|
|
2967
|
+
+ (feedbackScore * 0.2));
|
|
2968
|
+
const reasons = [
|
|
2969
|
+
item.accessCount > 0 ? `${item.accessCount} recalls` : 'not recalled yet',
|
|
2970
|
+
feedback.count > 0 ? `${feedback.count} feedback events` : 'no feedback yet',
|
|
2971
|
+
item.trust >= 0.8 ? 'high trust' : `trust ${item.trust.toFixed(2)}`,
|
|
2972
|
+
qmd.relevance >= 0.7 ? 'high QMD relevance' : `QMD relevance ${qmd.relevance.toFixed(2)}`,
|
|
2973
|
+
];
|
|
2974
|
+
return {
|
|
2975
|
+
score,
|
|
2976
|
+
accessCount: item.accessCount,
|
|
2977
|
+
trust: item.trust,
|
|
2978
|
+
feedbackCount: feedback.count,
|
|
2979
|
+
lastFeedbackAt: feedback.lastAt,
|
|
2980
|
+
qmd,
|
|
2981
|
+
reasons,
|
|
2982
|
+
};
|
|
2853
2983
|
}
|
|
2854
2984
|
applyRecallRerankerHint(score, item, hint) {
|
|
2855
2985
|
if (!hint || hint === 'balanced') {
|
|
@@ -2912,6 +3042,8 @@ export class MemoryEngine {
|
|
|
2912
3042
|
freshnessScore: this.freshnessScore(item),
|
|
2913
3043
|
trustScore: item.trust,
|
|
2914
3044
|
entropyScore: item.entropy,
|
|
3045
|
+
decay: this.buildDecayProfile(item),
|
|
3046
|
+
reinforcement: this.buildReinforcementProfile(item),
|
|
2915
3047
|
provenance: item.provenance,
|
|
2916
3048
|
expiresAt: item.expiresAt,
|
|
2917
3049
|
purgeAt: item.purgeAt,
|
|
@@ -76,7 +76,17 @@ export interface SourceBudgets {
|
|
|
76
76
|
graph: number;
|
|
77
77
|
runtime: number;
|
|
78
78
|
skills: number;
|
|
79
|
+
reasoning: number;
|
|
80
|
+
codeBlocks: number;
|
|
81
|
+
verification: number;
|
|
79
82
|
reserve: number;
|
|
83
|
+
codeBlockPolicy: {
|
|
84
|
+
detectedBlocks: number;
|
|
85
|
+
templateBlocks: number;
|
|
86
|
+
reservedTokens: number;
|
|
87
|
+
languages: string[];
|
|
88
|
+
templates: string[];
|
|
89
|
+
};
|
|
80
90
|
}
|
|
81
91
|
export interface RiskGate {
|
|
82
92
|
gate: string;
|
|
@@ -98,6 +108,22 @@ export interface ExecutionPolicy {
|
|
|
98
108
|
reviewMode: 'none' | 'self-check' | 'reviewer' | 'escalated-review';
|
|
99
109
|
escalationPolicy: string[];
|
|
100
110
|
reasons: string[];
|
|
111
|
+
agentFlow: {
|
|
112
|
+
enabled: true;
|
|
113
|
+
boardSource: 'nexus-runtime';
|
|
114
|
+
maxTaskCostUsd: number;
|
|
115
|
+
hardStopUsd: number;
|
|
116
|
+
crashRecovery: string;
|
|
117
|
+
dispatchPolicy: string;
|
|
118
|
+
deterministicGates: string[];
|
|
119
|
+
stages: Array<{
|
|
120
|
+
stage: 'research' | 'build' | 'review' | 'test' | 'integrate' | 'done';
|
|
121
|
+
ownerRole: 'planner' | 'coder' | 'researcher' | 'reviewer' | 'verifier' | 'integrator';
|
|
122
|
+
gate: string;
|
|
123
|
+
modelTier: ModelTier;
|
|
124
|
+
budgetShare: number;
|
|
125
|
+
}>;
|
|
126
|
+
};
|
|
101
127
|
}
|
|
102
128
|
export interface SelectionProvenance {
|
|
103
129
|
targetType: 'file' | 'memory' | 'skill' | 'workflow' | 'crew' | 'specialist' | 'model' | 'hook' | 'automation';
|
|
@@ -42,12 +42,22 @@ function inferRisk(task) {
|
|
|
42
42
|
...(task.manualOverrides ?? []),
|
|
43
43
|
...(task.actions ?? []).map(actionName),
|
|
44
44
|
].join(' ').toLowerCase();
|
|
45
|
+
const intent = normalizeIntent(task.intent);
|
|
46
|
+
const readOnlyIntent = intent === 'inspect' || intent === 'plan';
|
|
47
|
+
const controlPlaneCore = /(orchestrat|synapse|runtime|scheduler|workflow|mcp|model routing|hiring|selection|control plane|worker|dispatch|queue|queued|scheduled)/i.test(text);
|
|
48
|
+
const mutationIntent = /\b(fix|patch|change|modify|implement|build|create|harden|refactor|rewrite|repair|improve)\b/i.test(text);
|
|
45
49
|
if (/(delete|destroy|drop table|rm\s+-rf|force-push|production|credential|secret|billing|payment)/i.test(text)) {
|
|
46
50
|
return 'critical';
|
|
47
51
|
}
|
|
52
|
+
if (!readOnlyIntent && controlPlaneCore && mutationIntent) {
|
|
53
|
+
return 'high';
|
|
54
|
+
}
|
|
48
55
|
if ((task.files ?? []).length > 8 || Number(task.workers ?? 1) > 3 || /(release|migration|auth|security)/i.test(text)) {
|
|
49
56
|
return 'high';
|
|
50
57
|
}
|
|
58
|
+
if (!readOnlyIntent && (mutationIntent || /(bug|broken|not working|doesn.t work|scheduled|queued)/i.test(text))) {
|
|
59
|
+
return 'medium';
|
|
60
|
+
}
|
|
51
61
|
if ((task.files ?? []).length > 0 || (task.actions ?? []).length > 0)
|
|
52
62
|
return 'medium';
|
|
53
63
|
return 'low';
|
|
@@ -100,6 +110,86 @@ export function inferModelRoute(input) {
|
|
|
100
110
|
function normalizeTools(values) {
|
|
101
111
|
return unique(values.flatMap((value) => Array.isArray(value) ? value : [value]), 30);
|
|
102
112
|
}
|
|
113
|
+
function resolveCostGuardrailUsd(task, run, risk) {
|
|
114
|
+
const explicit = Number(task.budgetCapUsd ?? task.maxCostUsd ?? run.budgetCapUsd ?? run.maxCostUsd);
|
|
115
|
+
if (Number.isFinite(explicit) && explicit > 0)
|
|
116
|
+
return Math.min(50, Math.max(0.25, explicit));
|
|
117
|
+
if (risk === 'critical')
|
|
118
|
+
return 20;
|
|
119
|
+
if (risk === 'high')
|
|
120
|
+
return 10;
|
|
121
|
+
if (risk === 'medium')
|
|
122
|
+
return 3;
|
|
123
|
+
return 1;
|
|
124
|
+
}
|
|
125
|
+
function buildAgentFlowPolicy(input) {
|
|
126
|
+
const budget = resolveCostGuardrailUsd(input.task, input.run, input.risk);
|
|
127
|
+
const reviewerTier = input.modelRoute.reviewerTier ?? input.modelRoute.workerTier;
|
|
128
|
+
const stages = [
|
|
129
|
+
{
|
|
130
|
+
stage: 'research',
|
|
131
|
+
ownerRole: 'planner',
|
|
132
|
+
gate: 'request brief, memory recall, and candidate context selected',
|
|
133
|
+
modelTier: reviewerTier,
|
|
134
|
+
budgetShare: 0.12,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
stage: 'build',
|
|
138
|
+
ownerRole: 'coder',
|
|
139
|
+
gate: 'owned files only, small diff, no unrelated churn',
|
|
140
|
+
modelTier: input.modelRoute.workerTier,
|
|
141
|
+
budgetShare: input.workerCount > 1 ? 0.44 : 0.5,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
stage: 'review',
|
|
145
|
+
ownerRole: input.reviewMode === 'none' ? 'verifier' : 'reviewer',
|
|
146
|
+
gate: input.reviewMode === 'none'
|
|
147
|
+
? 'self-check only for low-risk advisory work'
|
|
148
|
+
: 'adversarial review must identify risks before pass',
|
|
149
|
+
modelTier: reviewerTier,
|
|
150
|
+
budgetShare: input.reviewMode === 'none' ? 0.08 : 0.16,
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
stage: 'test',
|
|
154
|
+
ownerRole: 'verifier',
|
|
155
|
+
gate: input.verificationRequired ? 'targeted tests/build/lint required' : 'verification optional unless files changed',
|
|
156
|
+
modelTier: reviewerTier,
|
|
157
|
+
budgetShare: 0.14,
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
stage: 'integrate',
|
|
161
|
+
ownerRole: 'integrator',
|
|
162
|
+
gate: input.workerCount > 1 ? 'merge independent packets and check conflicts' : 'summarize single-lane result',
|
|
163
|
+
modelTier: reviewerTier,
|
|
164
|
+
budgetShare: 0.1,
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
stage: 'done',
|
|
168
|
+
ownerRole: 'verifier',
|
|
169
|
+
gate: 'record verification, memory write-back, and residual risk',
|
|
170
|
+
modelTier: reviewerTier,
|
|
171
|
+
budgetShare: 0.08,
|
|
172
|
+
},
|
|
173
|
+
];
|
|
174
|
+
return {
|
|
175
|
+
enabled: true,
|
|
176
|
+
boardSource: 'nexus-runtime',
|
|
177
|
+
maxTaskCostUsd: budget,
|
|
178
|
+
hardStopUsd: Math.max(budget, Math.min(50, budget * 2)),
|
|
179
|
+
crashRecovery: 'Persist run state, decision-spine artifacts, and worker lanes so the next sweep can resume.',
|
|
180
|
+
dispatchPolicy: input.workerCount > 1
|
|
181
|
+
? 'bounded parallel lanes with one owner per file and deterministic gates before AI review'
|
|
182
|
+
: 'single worker lane with planner and verifier checkpoints',
|
|
183
|
+
deterministicGates: unique([
|
|
184
|
+
'build',
|
|
185
|
+
'lint',
|
|
186
|
+
'test',
|
|
187
|
+
input.reviewMode !== 'none' ? 'adversarial-review' : '',
|
|
188
|
+
input.workerCount > 1 ? 'integration-conflict-check' : '',
|
|
189
|
+
], 8),
|
|
190
|
+
stages,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
103
193
|
export function buildExecutionPolicy(input) {
|
|
104
194
|
const task = input.task ?? {};
|
|
105
195
|
const run = input.run ?? {};
|
|
@@ -123,6 +213,7 @@ export function buildExecutionPolicy(input) {
|
|
|
123
213
|
const topology = String(task.topology ?? planner.swarmDecision?.mode ?? 'auto');
|
|
124
214
|
const coderTier = modelRoute.workerTier;
|
|
125
215
|
const reviewerTier = modelRoute.reviewerTier ?? modelRoute.workerTier;
|
|
216
|
+
const verificationRequired = reviewMode !== 'none' || (task.verifyCommands ?? []).length > 0;
|
|
126
217
|
return {
|
|
127
218
|
topology,
|
|
128
219
|
workerCount,
|
|
@@ -135,7 +226,7 @@ export function buildExecutionPolicy(input) {
|
|
|
135
226
|
{ role: 'coder', modelTier: coderTier, authority: tools.some((tool) => /write|append|replace|run_command/.test(tool)) ? 'write' : 'read', reviewRequired: reviewMode !== 'none' },
|
|
136
227
|
{ role: 'verifier', modelTier: reviewerTier, authority: 'verify', reviewRequired: false },
|
|
137
228
|
],
|
|
138
|
-
verificationRequired
|
|
229
|
+
verificationRequired,
|
|
139
230
|
reviewMode,
|
|
140
231
|
escalationPolicy: modelRoute.escalationPolicy,
|
|
141
232
|
reasons: unique([
|
|
@@ -145,6 +236,15 @@ export function buildExecutionPolicy(input) {
|
|
|
145
236
|
workerCount > 1 ? 'Multiple workers require bounded parallelism and verifier coverage.' : '',
|
|
146
237
|
(task.verifyCommands ?? []).length > 0 ? 'Explicit verification commands were provided.' : '',
|
|
147
238
|
], 8),
|
|
239
|
+
agentFlow: buildAgentFlowPolicy({
|
|
240
|
+
task,
|
|
241
|
+
run,
|
|
242
|
+
risk,
|
|
243
|
+
workerCount,
|
|
244
|
+
modelRoute,
|
|
245
|
+
reviewMode,
|
|
246
|
+
verificationRequired,
|
|
247
|
+
}),
|
|
148
248
|
};
|
|
149
249
|
}
|
|
150
250
|
export function buildRequestBrief(input) {
|
|
@@ -180,17 +280,56 @@ export function buildRequestBrief(input) {
|
|
|
180
280
|
};
|
|
181
281
|
}
|
|
182
282
|
function buildBudgets(run, task, readingPlan) {
|
|
183
|
-
const
|
|
283
|
+
const repoTokens = Number(readingPlan?.totalEstimatedTokens ?? run.tokenTelemetry?.forwardedTokens ?? 0);
|
|
284
|
+
const promptText = String(task.goal ?? '');
|
|
285
|
+
const detectedBlocks = Math.floor((promptText.match(/```/g) ?? []).length / 2);
|
|
286
|
+
const languages = unique([...promptText.matchAll(/```([a-z0-9_-]+)/gi)].map((match) => match[1]), 8);
|
|
287
|
+
const wantsCodeBlocks = detectedBlocks > 0 || /(code blocks?|template|patch|diff|snippet|implementation|coding quality|work quality)/i.test(promptText);
|
|
288
|
+
const templateBlocks = wantsCodeBlocks ? 4 : 2;
|
|
289
|
+
const codeBlockTokens = Math.min(2400, Math.max(360, detectedBlocks * 320 + templateBlocks * 220));
|
|
184
290
|
const skillTokens = Math.min(1600, Math.max(400, (run.activeSkills?.length ?? 0) * 220 + (run.activeWorkflows?.length ?? 0) * 180));
|
|
291
|
+
const promptTokens = Math.min(1600, Math.max(200, Math.ceil(promptText.length / 4)));
|
|
292
|
+
const memoryTokens = Math.min(2400, Math.max(300, (run.backendEvidence?.memory?.recalled?.length ?? 0) * 300));
|
|
293
|
+
const graphTokens = run.knowledgeFabric ? 800 : 0;
|
|
294
|
+
const runtimeTokens = run.executionLedger ? 600 : 0;
|
|
295
|
+
const risk = run.requestBrief?.risk ?? inferRisk(task);
|
|
296
|
+
const reasoningTokens = risk === 'critical' ? 1800 : risk === 'high' ? 1400 : risk === 'medium' ? 900 : 500;
|
|
297
|
+
const verificationTokens = Math.min(1600, Math.max(260, (task.verifyCommands?.length ?? run.workerManifests?.length ?? 1) * 180));
|
|
298
|
+
const reserve = Math.max(500, Math.ceil((repoTokens + memoryTokens + codeBlockTokens) * 0.1));
|
|
299
|
+
const totalTokens = repoTokens
|
|
300
|
+
+ promptTokens
|
|
301
|
+
+ memoryTokens
|
|
302
|
+
+ graphTokens
|
|
303
|
+
+ runtimeTokens
|
|
304
|
+
+ skillTokens
|
|
305
|
+
+ reasoningTokens
|
|
306
|
+
+ codeBlockTokens
|
|
307
|
+
+ verificationTokens
|
|
308
|
+
+ reserve;
|
|
185
309
|
return {
|
|
186
310
|
totalTokens,
|
|
187
|
-
prompt:
|
|
188
|
-
memory:
|
|
189
|
-
repo: Math.max(0,
|
|
311
|
+
prompt: promptTokens,
|
|
312
|
+
memory: memoryTokens,
|
|
313
|
+
repo: Math.max(0, repoTokens),
|
|
190
314
|
graph: run.knowledgeFabric ? 800 : 0,
|
|
191
315
|
runtime: run.executionLedger ? 600 : 0,
|
|
192
316
|
skills: skillTokens,
|
|
193
|
-
|
|
317
|
+
reasoning: reasoningTokens,
|
|
318
|
+
codeBlocks: codeBlockTokens,
|
|
319
|
+
verification: verificationTokens,
|
|
320
|
+
reserve,
|
|
321
|
+
codeBlockPolicy: {
|
|
322
|
+
detectedBlocks,
|
|
323
|
+
templateBlocks,
|
|
324
|
+
reservedTokens: codeBlockTokens,
|
|
325
|
+
languages: languages.length ? languages : ['markdown', 'text'],
|
|
326
|
+
templates: [
|
|
327
|
+
'work-packet',
|
|
328
|
+
'implementation-notes',
|
|
329
|
+
'verification-output',
|
|
330
|
+
'memory-writeback',
|
|
331
|
+
].slice(0, templateBlocks),
|
|
332
|
+
},
|
|
194
333
|
};
|
|
195
334
|
}
|
|
196
335
|
export function buildSelectionPlan(input) {
|
|
@@ -75,8 +75,15 @@ export function categoryFilter(items, intent, kind) {
|
|
|
75
75
|
* the command-bar strip progress indicator.
|
|
76
76
|
*/
|
|
77
77
|
export function emitFunnelStage(payload) {
|
|
78
|
-
|
|
78
|
+
const eventPayload = {
|
|
79
79
|
...payload,
|
|
80
80
|
at: payload.at ?? new Date().toISOString(),
|
|
81
|
+
};
|
|
82
|
+
const handle = setImmediate(() => {
|
|
83
|
+
try {
|
|
84
|
+
nexusEventBus.emit('orchestrator.funnel.stage', eventPayload);
|
|
85
|
+
}
|
|
86
|
+
catch { /* funnel telemetry must not block orchestration */ }
|
|
81
87
|
});
|
|
88
|
+
handle.unref?.();
|
|
82
89
|
}
|
|
@@ -23,7 +23,7 @@ export declare function decideWorkers(requestedWorkers: number | undefined, plan
|
|
|
23
23
|
* (previously read from `this.sessionState.repeatedFailures`).
|
|
24
24
|
*/
|
|
25
25
|
export declare function determineMode(intent: AutonomyIntent, phaseCount: number, workers: number, repeatedFailures: number): RuntimeOrchestrationSnapshot['mode'];
|
|
26
|
-
export declare function toSourceAwareTokenBudget(knowledgeFabric: KnowledgeFabricBundle,
|
|
26
|
+
export declare function toSourceAwareTokenBudget(knowledgeFabric: KnowledgeFabricBundle, selectedFiles: string[], reason: string, task?: string): RuntimeSourceAwareTokenBudgetSnapshot;
|
|
27
27
|
export declare function toRagCandidateStatus(knowledgeFabric: KnowledgeFabricBundle): SessionBootstrapResult['ragCandidateStatus'];
|
|
28
28
|
export declare function toRagUsageSummary(knowledgeFabric: KnowledgeFabricBundle, usage: {
|
|
29
29
|
usedInPlanner: boolean;
|
|
@@ -81,7 +81,7 @@ export function decomposeTask(task) {
|
|
|
81
81
|
.filter(Boolean)
|
|
82
82
|
.join(' ');
|
|
83
83
|
const subtasks = normalized
|
|
84
|
-
.split(
|
|
84
|
+
.split(/\s*(?:[.;,]|\band\s+then\b|\bthen\b|\bafter\s+that\b|\bfollowed\s+by\b|\balso\b)\s*/i)
|
|
85
85
|
.map((segment) => segment.trim())
|
|
86
86
|
.filter(Boolean);
|
|
87
87
|
return subtasks.length > 0 ? subtasks : [task];
|
|
@@ -325,7 +325,13 @@ export function determineMode(intent, phaseCount, workers, repeatedFailures) {
|
|
|
325
325
|
return 'single-pass';
|
|
326
326
|
}
|
|
327
327
|
// ─── Token budget / RAG ────────────────────────────────────────────────────────
|
|
328
|
-
export function toSourceAwareTokenBudget(knowledgeFabric,
|
|
328
|
+
export function toSourceAwareTokenBudget(knowledgeFabric, selectedFiles, reason, task = '') {
|
|
329
|
+
const detectedBlocks = Math.floor((task.match(/```/g) ?? []).length / 2);
|
|
330
|
+
const requested = detectedBlocks > 0 || /(code blocks?|template|patch|diff|snippet|implementation|verification|memory write)/i.test(task);
|
|
331
|
+
const templates = requested
|
|
332
|
+
? ['work-packet', 'implementation-notes', 'verification-output', 'memory-writeback']
|
|
333
|
+
: ['verification-output', 'memory-writeback'];
|
|
334
|
+
const languages = [...new Set([...task.matchAll(/```([a-z0-9_-]+)/gi)].map((match) => match[1]))].slice(0, 8);
|
|
329
335
|
return {
|
|
330
336
|
applied: true,
|
|
331
337
|
reason,
|
|
@@ -334,6 +340,22 @@ export function toSourceAwareTokenBudget(knowledgeFabric, _selectedFiles, reason
|
|
|
334
340
|
byStage: knowledgeFabric.tokenBudget.byStage,
|
|
335
341
|
dropped: knowledgeFabric.tokenBudget.dropped,
|
|
336
342
|
dominantSource: knowledgeFabric.sourceMix.dominantSource,
|
|
343
|
+
codeBlocks: {
|
|
344
|
+
requested,
|
|
345
|
+
detectedBlocks,
|
|
346
|
+
reservedTokens: Math.min(2400, Math.max(360, detectedBlocks * 320 + templates.length * 220)),
|
|
347
|
+
templates,
|
|
348
|
+
languages: languages.length ? languages : ['markdown', 'text'],
|
|
349
|
+
},
|
|
350
|
+
qualityGates: [
|
|
351
|
+
'quote selected files and symbols before reading broadly',
|
|
352
|
+
'use code blocks for work packets, patches, verification, and memory write-back',
|
|
353
|
+
selectedFiles.length > 2 ? 'run token optimization before reading selected files' : 'keep single-file reads direct and cheap',
|
|
354
|
+
],
|
|
355
|
+
recommendations: [
|
|
356
|
+
'Route exploration to cheap lanes, keep implementation and integration on the primary coding model.',
|
|
357
|
+
'Treat no-diff mutate runs as advisory inspected outcomes unless a binding promised a patch.',
|
|
358
|
+
],
|
|
337
359
|
};
|
|
338
360
|
}
|
|
339
361
|
export function toRagCandidateStatus(knowledgeFabric) {
|
|
@@ -48,6 +48,8 @@ export declare class OrchestratorEngine {
|
|
|
48
48
|
private workflowCatalogCache?;
|
|
49
49
|
private hookCatalogCache?;
|
|
50
50
|
private automationCatalogCache?;
|
|
51
|
+
private clientSnapshotCache?;
|
|
52
|
+
private readonly CLIENT_SNAPSHOT_CACHE_TTL_MS;
|
|
51
53
|
private readonly specialistCatalogItems;
|
|
52
54
|
private readonly crewCatalogItems;
|
|
53
55
|
private crGraphClient?;
|
|
@@ -137,6 +139,7 @@ export declare class OrchestratorEngine {
|
|
|
137
139
|
private pruneStaleAutonomySessions;
|
|
138
140
|
private resolvePrimaryClient;
|
|
139
141
|
private listDetectedClients;
|
|
142
|
+
private getClientSnapshot;
|
|
140
143
|
private detectCurrentClientFromEnv;
|
|
141
144
|
private toClientSnapshot;
|
|
142
145
|
private toRuntimeOrchestrationSnapshot;
|