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.
Files changed (59) hide show
  1. package/dist/agents/adapters/ide-compat.d.ts +3 -3
  2. package/dist/agents/adapters/ide-compat.js +51 -1
  3. package/dist/agents/adapters/mcp/definitions.js +4 -1
  4. package/dist/agents/adapters/mcp/dispatch.js +66 -4
  5. package/dist/agents/adapters/mcp/handlers/memory.js +44 -5
  6. package/dist/agents/adapters/mcp/handlers/orchestration.js +91 -0
  7. package/dist/agents/adapters/mcp/runHandler.js +3 -0
  8. package/dist/agents/adapters/mcp/util/detect-caller.js +21 -0
  9. package/dist/agents/adapters/mcp.js +1 -1
  10. package/dist/agents/adapters.d.ts +10 -1
  11. package/dist/agents/adapters.js +21 -0
  12. package/dist/agents/core/types.d.ts +1 -1
  13. package/dist/cli/hook.d.ts +4 -6
  14. package/dist/cli/hook.js +6 -8
  15. package/dist/cli/install-wizard.js +5 -1
  16. package/dist/cli.js +181 -15
  17. package/dist/core/types.d.ts +1 -1
  18. package/dist/dashboard/app/styles/board.css +85 -1
  19. package/dist/dashboard/app/styles/runtime.css +148 -0
  20. package/dist/dashboard/app/styles/workforce.css +28 -0
  21. package/dist/dashboard/app/views/board.js +56 -0
  22. package/dist/dashboard/app/views/memory.js +71 -10
  23. package/dist/dashboard/app/views/runtime.js +138 -4
  24. package/dist/dashboard/app/views/workforce.js +11 -4
  25. package/dist/dashboard/routes/events.js +3 -0
  26. package/dist/dashboard/selectors/operate-selector.js +5 -0
  27. package/dist/dashboard/selectors/runs-selector.js +5 -0
  28. package/dist/dashboard/server.js +6 -0
  29. package/dist/dashboard/types.d.ts +4 -0
  30. package/dist/engines/client-bootstrap.d.ts +5 -1
  31. package/dist/engines/client-bootstrap.js +105 -10
  32. package/dist/engines/client-registry.js +51 -0
  33. package/dist/engines/event-bus.d.ts +20 -2
  34. package/dist/engines/feature-registry.js +1 -0
  35. package/dist/engines/instruction-gateway.d.ts +9 -0
  36. package/dist/engines/instruction-gateway.js +113 -4
  37. package/dist/engines/memory/types.d.ts +28 -0
  38. package/dist/engines/memory-bridge.d.ts +1 -1
  39. package/dist/engines/memory-bridge.js +1 -1
  40. package/dist/engines/memory.d.ts +5 -0
  41. package/dist/engines/memory.js +144 -12
  42. package/dist/engines/orchestrator/decision-spine.d.ts +26 -0
  43. package/dist/engines/orchestrator/decision-spine.js +145 -6
  44. package/dist/engines/orchestrator/funnel.js +8 -1
  45. package/dist/engines/orchestrator/scoring.d.ts +1 -1
  46. package/dist/engines/orchestrator/scoring.js +24 -2
  47. package/dist/engines/orchestrator.d.ts +3 -0
  48. package/dist/engines/orchestrator.js +73 -13
  49. package/dist/engines/peer-connectors.d.ts +1 -1
  50. package/dist/engines/peer-connectors.js +9 -2
  51. package/dist/engines/runtime-registry.d.ts +9 -0
  52. package/dist/index.js +9 -0
  53. package/dist/install/state-locator.d.ts +1 -1
  54. package/dist/install/state-locator.js +3 -0
  55. package/dist/synapse/bootstrap.js +3 -0
  56. package/dist/synapse/mandate/pipeline.js +52 -5
  57. package/dist/synapse/sorties/runner.js +32 -0
  58. package/dist/synapse/types.d.ts +27 -0
  59. package/package.json +1 -1
@@ -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: this.getSnapshot(id),
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
- computeQmdScore(item) {
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
- const recency = item.qmdRecency ?? Math.max(0, 1 - ageDays / 30);
2850
- const frequency = item.qmdFrequency ?? Math.min(1, item.accessCount / 20);
2851
- const relevance = item.qmdRelevance ?? (1 - item.entropy);
2852
- return 0.3 * recency + 0.3 * frequency + 0.2 * relevance + 0.2 * item.trust;
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: reviewMode !== 'none' || (task.verifyCommands ?? []).length > 0,
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 totalTokens = Number(readingPlan?.totalEstimatedTokens ?? run.tokenTelemetry?.forwardedTokens ?? 0);
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: Math.min(1200, Math.max(200, Math.ceil(String(task.goal ?? '').length / 4))),
188
- memory: Math.min(2400, Math.max(300, (run.backendEvidence?.memory?.recalled?.length ?? 0) * 300)),
189
- repo: Math.max(0, totalTokens),
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
- reserve: Math.max(500, Math.ceil(totalTokens * 0.1)),
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
- nexusEventBus.emit('orchestrator.funnel.stage', {
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, _selectedFiles: string[], reason: string): RuntimeSourceAwareTokenBudgetSnapshot;
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(/,| then | and then | after that | followed by | also /i)
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, _selectedFiles, reason) {
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;