0xray 4.0.21 → 4.0.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/.grok-plugin/plugin.json +1 -1
- package/.opencode/skills/hermes-agent/SKILL.md +1 -1
- package/.opencode/skills/orchestrator/SKILL.md +28 -6
- package/.xray/features.json +1 -1
- package/AGENTS-consumer.md +11 -11
- package/AGENTS.md +8 -8
- package/README.md +27 -27
- package/SKILLS.md +1 -1
- package/dist/AGENTS.md +8 -8
- package/dist/CHANGELOG.md +98 -0
- package/dist/README.md +27 -27
- package/dist/SKILLS.md +1 -1
- package/dist/governance/governance-core.d.ts +2 -1
- package/dist/governance/governance-core.js +20 -11
- package/dist/governance/governance-service.js +9 -7
- package/dist/inference/inference-accumulator.js +36 -4
- package/dist/inference/inference-cycle.d.ts +12 -0
- package/dist/inference/inference-cycle.js +180 -21
- package/dist/inference/inference-proposal-generator.js +148 -5
- package/dist/inference/session-capture.d.ts +4 -0
- package/dist/integrations/cursor/README.md +1 -1
- package/dist/integrations/cursor/hooks/cursor-hook-utils.js +19 -0
- package/dist/integrations/cursor/hooks/pre-tool-use.js +2 -1
- package/dist/integrations/governance/index.d.ts +5 -0
- package/dist/integrations/governance/index.js +22 -2
- package/dist/integrations/governance/types.d.ts +11 -0
- package/dist/integrations/governance/types.js +16 -0
- package/dist/integrations/hooks/station-hook-runtime.cjs +90 -30
- package/dist/integrations/hooks/station-hook-runtime.mjs +1 -0
- package/dist/integrations/hooks/station-memory-ingest.mjs +4 -2
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/dist/mcps/governance.server.d.ts +2 -0
- package/dist/mcps/governance.server.js +12 -1
- package/dist/memory-routing/index.d.ts +1 -1
- package/dist/memory-routing/index.js +1 -1
- package/dist/memory-routing/lesson-pickup.d.ts +2 -0
- package/dist/memory-routing/lesson-pickup.js +57 -0
- package/dist/memory-routing/provider-loader.d.ts +8 -1
- package/dist/memory-routing/provider-loader.js +49 -1
- package/dist/memory-routing/provider-registry.d.ts +7 -2
- package/dist/memory-routing/provider-registry.js +26 -6
- package/dist/memory-routing/record-lesson.d.ts +16 -0
- package/dist/memory-routing/record-lesson.js +26 -0
- package/dist/memory-routing/types.d.ts +15 -0
- package/dist/nucleus/thin-dispatch.d.ts +2 -0
- package/dist/nucleus/thin-dispatch.js +14 -4
- package/dist/processors/implementations/agents-md-validation-processor.js +1 -1
- package/dist/skills/hermes-agent/SKILL.md +1 -1
- package/dist/skills/orchestrator/SKILL.md +28 -6
- package/package.json +2 -2
- package/scripts/foundry/CHANGELOG.md +2 -0
- package/scripts/foundry/README.md +2 -2
- package/scripts/foundry/cli.mjs +1 -1
- package/scripts/foundry/inspect.mjs +160 -5
- package/scripts/foundry/plant/skills/inspect/SKILL.md +2 -0
- package/scripts/foundry/reconcile-version.mjs +23 -1
- package/scripts/foundry/release.mjs +1 -1
- package/scripts/foundry/validate-release-docs.mjs +24 -20
- package/scripts/foundry/version-manager.mjs +180 -7
- package/scripts/mjs/test-consumer-readiness.mjs +10 -3
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/src/mcps/governance.server.ts +12 -1
- package/src/skills/hermes-agent/SKILL.md +1 -1
- package/src/skills/orchestrator/SKILL.md +28 -6
- package/vendor/@0xray/repertoire/data/stack-overlay.json +156 -3
- package/vendor/@0xray/repertoire/data/subject-overlay.json +3 -3
- package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts +4 -0
- package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts.map +1 -1
- package/vendor/@0xray/repertoire/dist/RepertoireService.js +7 -6
- package/vendor/@0xray/repertoire/dist/RepertoireService.js.map +1 -1
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts.map +1 -1
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js +10 -11
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js.map +1 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts.map +1 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js +5 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js.map +1 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts.map +1 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js +16 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js.map +1 -1
- package/vendor/@0xray/repertoire/dist/paths.d.ts +3 -3
- package/vendor/@0xray/repertoire/dist/paths.d.ts.map +1 -1
- package/vendor/@0xray/repertoire/dist/paths.js +66 -4
- package/vendor/@0xray/repertoire/dist/paths.js.map +1 -1
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts +11 -0
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts.map +1 -1
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js +2 -0
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js.map +1 -1
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts +54 -4
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts.map +1 -1
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js +375 -20
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js.map +1 -1
- package/vendor/@0xray/repertoire/dist/types.d.ts +21 -0
- package/vendor/@0xray/repertoire/dist/types.d.ts.map +1 -1
- package/xray/agents_template.md +10 -5
- package/xray/features.json +1 -1
- package/dist/mcps/shared/prompt-security-validator.d.ts +0 -44
- package/dist/mcps/shared/prompt-security-validator.js +0 -148
- package/dist/mcps/shared/security-scanner.d.ts +0 -119
- package/dist/mcps/shared/security-scanner.js +0 -429
- package/dist/scripts/activate-kernel-pipeline.d.ts +0 -7
- package/dist/scripts/activate-kernel-pipeline.js +0 -101
- package/src/mcps/shared/prompt-security-validator.ts +0 -199
- package/src/mcps/shared/security-scanner.ts +0 -599
|
@@ -39,6 +39,11 @@ export declare class InferenceGovernanceIntegration extends BaseIntegration {
|
|
|
39
39
|
* Check if governance is enabled and available
|
|
40
40
|
*/
|
|
41
41
|
isAvailable(): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Start the Dynamo client when a caller required external governance
|
|
44
|
+
* and boot skipped it because inference_governance.enabled is false.
|
|
45
|
+
*/
|
|
46
|
+
ensureDynamoClient(): Promise<void>;
|
|
42
47
|
/**
|
|
43
48
|
* Check a single inference proposal via the appropriate governance tool
|
|
44
49
|
*
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @since 2026-05-11
|
|
8
8
|
*/
|
|
9
9
|
import { BaseIntegration } from '../base/index.js';
|
|
10
|
-
import { DEFAULT_GOVERNANCE_CONFIG } from './types.js';
|
|
10
|
+
import { DEFAULT_GOVERNANCE_CONFIG, resolveDynamoBaseUrl } from './types.js';
|
|
11
11
|
import { GovernanceClient } from './governance-client.js';
|
|
12
12
|
import { frameworkLogger } from '../../core/framework-logger.js';
|
|
13
13
|
import { featuresConfigLoader } from '../../core/features-config.js';
|
|
@@ -84,7 +84,27 @@ export class InferenceGovernanceIntegration extends BaseIntegration {
|
|
|
84
84
|
* Check if governance is enabled and available
|
|
85
85
|
*/
|
|
86
86
|
isAvailable() {
|
|
87
|
-
return this.
|
|
87
|
+
return this.client !== null && this.status === 'initialized';
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Start the Dynamo client when a caller required external governance
|
|
91
|
+
* and boot skipped it because inference_governance.enabled is false.
|
|
92
|
+
*/
|
|
93
|
+
async ensureDynamoClient() {
|
|
94
|
+
if (this.status !== 'initialized') {
|
|
95
|
+
await this.initialize();
|
|
96
|
+
}
|
|
97
|
+
if (this.client !== null) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const endpoint = resolveDynamoBaseUrl(this.configData.endpointUrl);
|
|
101
|
+
this.client = new GovernanceClient({
|
|
102
|
+
baseUrl: endpoint,
|
|
103
|
+
timeoutMs: this.configData.requestTimeoutMs,
|
|
104
|
+
});
|
|
105
|
+
await this.log('info', 'Dynamo client armed for required external governance', {
|
|
106
|
+
endpoint,
|
|
107
|
+
});
|
|
88
108
|
}
|
|
89
109
|
/**
|
|
90
110
|
* Check a single inference proposal via the appropriate governance tool
|
|
@@ -56,6 +56,17 @@ export interface GovernanceIntegrationConfig {
|
|
|
56
56
|
voteWeightMultiplier: number;
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Public Dynamo Solar SSOT. Used only when a caller requires external
|
|
61
|
+
* governance and neither features nor GOVERNANCE_ENDPOINT names a host.
|
|
62
|
+
* The client posts to /call_connected_tool on this origin.
|
|
63
|
+
*/
|
|
64
|
+
export declare const PUBLIC_DYNAMO_SSOT = "https://mcp-production-80e2.up.railway.app";
|
|
65
|
+
/**
|
|
66
|
+
* Origin for POST /call_connected_tool.
|
|
67
|
+
* Preference: configured endpoint, then GOVERNANCE_ENDPOINT, then the public SSOT.
|
|
68
|
+
*/
|
|
69
|
+
export declare function resolveDynamoBaseUrl(configured: string): string;
|
|
59
70
|
/**
|
|
60
71
|
* Default governance configuration
|
|
61
72
|
*/
|
|
@@ -6,6 +6,22 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @since 2026-05-11
|
|
8
8
|
*/
|
|
9
|
+
/**
|
|
10
|
+
* Public Dynamo Solar SSOT. Used only when a caller requires external
|
|
11
|
+
* governance and neither features nor GOVERNANCE_ENDPOINT names a host.
|
|
12
|
+
* The client posts to /call_connected_tool on this origin.
|
|
13
|
+
*/
|
|
14
|
+
export const PUBLIC_DYNAMO_SSOT = 'https://mcp-production-80e2.up.railway.app';
|
|
15
|
+
/**
|
|
16
|
+
* Origin for POST /call_connected_tool.
|
|
17
|
+
* Preference: configured endpoint, then GOVERNANCE_ENDPOINT, then the public SSOT.
|
|
18
|
+
*/
|
|
19
|
+
export function resolveDynamoBaseUrl(configured) {
|
|
20
|
+
const fromConfig = configured.trim();
|
|
21
|
+
const fromEnv = (process.env.GOVERNANCE_ENDPOINT || '').trim();
|
|
22
|
+
const raw = fromConfig || fromEnv || PUBLIC_DYNAMO_SSOT;
|
|
23
|
+
return raw.replace(/\/governance\/?$/, '').replace(/\/$/, '');
|
|
24
|
+
}
|
|
9
25
|
/**
|
|
10
26
|
* Default governance configuration
|
|
11
27
|
*/
|
|
@@ -395,6 +395,30 @@ function pruneKeywordDest(root) {
|
|
|
395
395
|
return { removed, kept: kept.length };
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
+
const STACK_LAW_FIELDS = [
|
|
399
|
+
"definition",
|
|
400
|
+
"tags",
|
|
401
|
+
"priority",
|
|
402
|
+
"evaluation_criteria",
|
|
403
|
+
"validation_experiment",
|
|
404
|
+
"example_inference_snippet",
|
|
405
|
+
"implementation_notes",
|
|
406
|
+
];
|
|
407
|
+
|
|
408
|
+
/** Stack text cadences onto an existing project law. Observation stats stay. */
|
|
409
|
+
function refreshStackLaw(existing, signal) {
|
|
410
|
+
let dirty = false;
|
|
411
|
+
for (const field of STACK_LAW_FIELDS) {
|
|
412
|
+
if (signal[field] == null) continue;
|
|
413
|
+
const next = signal[field];
|
|
414
|
+
const prev = existing[field];
|
|
415
|
+
if (JSON.stringify(prev) === JSON.stringify(next)) continue;
|
|
416
|
+
existing[field] = next;
|
|
417
|
+
dirty = true;
|
|
418
|
+
}
|
|
419
|
+
return dirty;
|
|
420
|
+
}
|
|
421
|
+
|
|
398
422
|
function mergeMissingSignals(destPath, incoming) {
|
|
399
423
|
if (!incoming.length || !existsSync(destPath)) return 0;
|
|
400
424
|
let data;
|
|
@@ -404,24 +428,35 @@ function mergeMissingSignals(destPath, incoming) {
|
|
|
404
428
|
return 0;
|
|
405
429
|
}
|
|
406
430
|
if (!Array.isArray(data.signals)) return 0;
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
431
|
+
const byName = new Map();
|
|
432
|
+
for (const signal of data.signals) {
|
|
433
|
+
const name = String((signal && signal.name) || "").trim();
|
|
434
|
+
if (name) byName.set(name, signal);
|
|
435
|
+
}
|
|
436
|
+
let changed = 0;
|
|
411
437
|
for (const signal of incoming) {
|
|
412
438
|
const name = String(signal.name).trim();
|
|
413
|
-
if (!name
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
439
|
+
if (!name) continue;
|
|
440
|
+
const existing = byName.get(name);
|
|
441
|
+
if (!existing) {
|
|
442
|
+
const created = {
|
|
443
|
+
...signal,
|
|
444
|
+
name,
|
|
445
|
+
tags: Array.isArray(signal.tags) ? signal.tags : [],
|
|
446
|
+
definition: typeof signal.definition === "string" ? signal.definition : name,
|
|
447
|
+
};
|
|
448
|
+
data.signals.push(created);
|
|
449
|
+
byName.set(name, created);
|
|
450
|
+
changed += 1;
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
if (refreshStackLaw(existing, signal)) changed += 1;
|
|
454
|
+
}
|
|
455
|
+
if (changed) {
|
|
456
|
+
data.last_updated = new Date().toISOString();
|
|
457
|
+
writeFileSync(destPath, `${JSON.stringify(data, null, 2)}\n`);
|
|
422
458
|
}
|
|
423
|
-
|
|
424
|
-
return added;
|
|
459
|
+
return changed;
|
|
425
460
|
}
|
|
426
461
|
|
|
427
462
|
/** Hydrate project dest from seed + stack. Subject repo-* stay off dest. */
|
|
@@ -661,6 +696,7 @@ function maybeCaptureSessionOnHeadMove(root) {
|
|
|
661
696
|
from: stamp && stamp.head ? stamp.head : commits[commits.length - 1] ? commits[commits.length - 1].hash : git.head,
|
|
662
697
|
to: git.head,
|
|
663
698
|
};
|
|
699
|
+
const patterns = patternsFromGit(root, commits, span);
|
|
664
700
|
const session = {
|
|
665
701
|
sessionId,
|
|
666
702
|
timestamp: new Date().toISOString(),
|
|
@@ -669,8 +705,8 @@ function maybeCaptureSessionOnHeadMove(root) {
|
|
|
669
705
|
approaches,
|
|
670
706
|
wrongTurns: [],
|
|
671
707
|
solutions: [],
|
|
672
|
-
patterns
|
|
673
|
-
matched_primitives:
|
|
708
|
+
patterns,
|
|
709
|
+
matched_primitives: preferLawHits(patterns.map((row) => row && row.name)),
|
|
674
710
|
metrics: { commits: commits.length },
|
|
675
711
|
};
|
|
676
712
|
const outDir = join(root, "docs", "inference");
|
|
@@ -754,8 +790,16 @@ function growDestOnWake(root) {
|
|
|
754
790
|
const line = String(out).trim().split("\n").filter(Boolean).at(-1) || "{}";
|
|
755
791
|
const parsed = JSON.parse(line);
|
|
756
792
|
const pruned = pruneKeywordDest(root);
|
|
757
|
-
|
|
758
|
-
return {
|
|
793
|
+
const destCount = countCuratedSignals(destSignalsPath(root)) || 0;
|
|
794
|
+
if (!parsed || typeof parsed !== "object") return { pruned: pruned.removed, destCount };
|
|
795
|
+
const heated = Array.isArray(parsed.heated) ? parsed.heated.length : 0;
|
|
796
|
+
return {
|
|
797
|
+
...parsed,
|
|
798
|
+
observed: heated,
|
|
799
|
+
pruned: pruned.removed,
|
|
800
|
+
destCount,
|
|
801
|
+
after: destCount,
|
|
802
|
+
};
|
|
759
803
|
} catch {
|
|
760
804
|
return null;
|
|
761
805
|
}
|
|
@@ -811,6 +855,28 @@ function retainCompactFields(existing, extra = {}) {
|
|
|
811
855
|
return kept;
|
|
812
856
|
}
|
|
813
857
|
|
|
858
|
+
/** Persist heated length. A number stays a number. An array is a count. Never drop a number to 0. */
|
|
859
|
+
function workingGrowSnapshot(grow) {
|
|
860
|
+
if (!grow || typeof grow !== "object") return null;
|
|
861
|
+
if (typeof grow.after !== "number") {
|
|
862
|
+
return typeof grow.skipped === "string" ? grow.skipped : null;
|
|
863
|
+
}
|
|
864
|
+
const observed =
|
|
865
|
+
typeof grow.observed === "number"
|
|
866
|
+
? grow.observed
|
|
867
|
+
: Array.isArray(grow.observed)
|
|
868
|
+
? grow.observed.length
|
|
869
|
+
: Array.isArray(grow.heated)
|
|
870
|
+
? grow.heated.length
|
|
871
|
+
: 0;
|
|
872
|
+
return {
|
|
873
|
+
before: grow.before,
|
|
874
|
+
after: grow.after,
|
|
875
|
+
imported: grow.imported,
|
|
876
|
+
observed,
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
|
|
814
880
|
function formatWorkingLine(working) {
|
|
815
881
|
if (!working || typeof working !== "object") return null;
|
|
816
882
|
const matched = stationSafeSignals(working.matchedSignals);
|
|
@@ -826,7 +892,7 @@ function buildRepertoireResume(root) {
|
|
|
826
892
|
const modulePath = resolveRepertoireProviderModule(root);
|
|
827
893
|
const mr = readMemoryRoutingConfig(root);
|
|
828
894
|
if (!modulePath) {
|
|
829
|
-
return "Repertoire:
|
|
895
|
+
return "Repertoire: module unresolved";
|
|
830
896
|
}
|
|
831
897
|
const destPath = destSignalsPath(root);
|
|
832
898
|
let signalsPath = existsSync(destPath) ? destPath : mr.config && mr.config.signalsPath;
|
|
@@ -932,15 +998,8 @@ function applyStationHeat(root, host, extra = {}, existing = {}) {
|
|
|
932
998
|
if (pickup) workingSnapshot.pickup = pickup;
|
|
933
999
|
if (matchText) workingSnapshot.matchText = matchText;
|
|
934
1000
|
if (captured) workingSnapshot.sessionCapture = captured;
|
|
935
|
-
|
|
936
|
-
if (
|
|
937
|
-
workingSnapshot.grow = {
|
|
938
|
-
before: grow.before,
|
|
939
|
-
after: grow.after,
|
|
940
|
-
imported: grow.imported,
|
|
941
|
-
observed: Array.isArray(grow.observed) ? grow.observed.length : 0,
|
|
942
|
-
};
|
|
943
|
-
}
|
|
1001
|
+
const growReceipt = workingGrowSnapshot(grow);
|
|
1002
|
+
if (growReceipt) workingSnapshot.grow = growReceipt;
|
|
944
1003
|
if (matchedSignals.length) workingSnapshot.matchedSignals = matchedSignals.slice(0, 8);
|
|
945
1004
|
const opProcNames = readOpProcNames(root);
|
|
946
1005
|
if (opProcNames.length) workingSnapshot.opProcNames = opProcNames;
|
|
@@ -1108,7 +1167,7 @@ function formatStationMarkdown(fields) {
|
|
|
1108
1167
|
} else {
|
|
1109
1168
|
lines.push("Git: n/a");
|
|
1110
1169
|
}
|
|
1111
|
-
lines.push(fields.repertoireResume || "Repertoire:
|
|
1170
|
+
lines.push(fields.repertoireResume || "Repertoire: module unresolved");
|
|
1112
1171
|
if (fields.workingLine) {
|
|
1113
1172
|
lines.push(fields.workingLine);
|
|
1114
1173
|
}
|
|
@@ -1165,6 +1224,7 @@ module.exports = {
|
|
|
1165
1224
|
readNotesPickup,
|
|
1166
1225
|
maybeCaptureSessionOnHeadMove,
|
|
1167
1226
|
formatWorkingLine,
|
|
1227
|
+
workingGrowSnapshot,
|
|
1168
1228
|
applyStationHeat,
|
|
1169
1229
|
isStockTicket,
|
|
1170
1230
|
readStationTicketField,
|
|
@@ -28,6 +28,7 @@ export const patternsFromGit = impl.patternsFromGit;
|
|
|
28
28
|
export const readNotesPickup = impl.readNotesPickup;
|
|
29
29
|
export const maybeCaptureSessionOnHeadMove = impl.maybeCaptureSessionOnHeadMove;
|
|
30
30
|
export const formatWorkingLine = impl.formatWorkingLine;
|
|
31
|
+
export const workingGrowSnapshot = impl.workingGrowSnapshot;
|
|
31
32
|
export const applyStationHeat = impl.applyStationHeat;
|
|
32
33
|
export const isStockTicket = impl.isStockTicket;
|
|
33
34
|
export const readStationTicketField = impl.readStationTicketField;
|
|
@@ -128,13 +128,15 @@ async function growDest(projectRoot) {
|
|
|
128
128
|
sources: [...(kernel.sources || []), ...cleanup.sources],
|
|
129
129
|
});
|
|
130
130
|
const after = service.signalsManager.load().signals.length;
|
|
131
|
+
const heatedNames = Array.isArray(diary.heated) ? diary.heated : [];
|
|
131
132
|
process.stdout.write(
|
|
132
133
|
`${JSON.stringify({
|
|
133
134
|
before,
|
|
134
135
|
after,
|
|
135
136
|
imported: sessions.imported,
|
|
136
|
-
observed:
|
|
137
|
-
heated:
|
|
137
|
+
observed: heatedNames.length,
|
|
138
|
+
heated: heatedNames,
|
|
139
|
+
destCount: after,
|
|
138
140
|
})}\n`,
|
|
139
141
|
);
|
|
140
142
|
return;
|
|
@@ -20,6 +20,8 @@ declare class GovernanceServer extends XrayKnowledgeSkillBase {
|
|
|
20
20
|
protected setupToolHandlers(): void;
|
|
21
21
|
/** Align tool default with features.json inference_governance.enabled (P0.2). */
|
|
22
22
|
private resolveRequireExternal;
|
|
23
|
+
/** Boot stays flag-gated. A required external call starts the client on demand. */
|
|
24
|
+
private armRequiredDynamo;
|
|
23
25
|
private handleGovernProposals;
|
|
24
26
|
private handleGovernReflection;
|
|
25
27
|
private handleGetActiveCodex;
|
|
@@ -196,8 +196,19 @@ class GovernanceServer extends XrayKnowledgeSkillBase {
|
|
|
196
196
|
return false;
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
|
+
/** Boot stays flag-gated. A required external call starts the client on demand. */
|
|
200
|
+
async armRequiredDynamo(requireExternal) {
|
|
201
|
+
if (!requireExternal)
|
|
202
|
+
return;
|
|
203
|
+
const integration = await initializeGovernanceIntegration();
|
|
204
|
+
if (!integration.isAvailable()) {
|
|
205
|
+
await integration.ensureDynamoClient();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
199
208
|
async handleGovernProposals(args) {
|
|
200
209
|
const service = getGovernanceService();
|
|
210
|
+
const requireExternal = this.resolveRequireExternal(args.options?.require_external);
|
|
211
|
+
await this.armRequiredDynamo(requireExternal);
|
|
201
212
|
const request = {
|
|
202
213
|
proposals: args.proposals.map((p, i) => ({
|
|
203
214
|
id: p.id || `prop-${Date.now()}-${i}`,
|
|
@@ -210,7 +221,7 @@ class GovernanceServer extends XrayKnowledgeSkillBase {
|
|
|
210
221
|
})),
|
|
211
222
|
context: args.context || {},
|
|
212
223
|
options: {
|
|
213
|
-
requireExternalDynamo:
|
|
224
|
+
requireExternalDynamo: requireExternal,
|
|
214
225
|
},
|
|
215
226
|
};
|
|
216
227
|
frameworkLogger.log("governance-mcp", "delegating-to-governance-service", "info", {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { MemoryRoutingProvider, MemoryRoutingContext, MemoryInheritedContext, MemoryAgentCapability, MemoryOrchestrationTask, MemoryThinDispatchResult, MemoryRoutingConfig, OrchestratorFeedbackEntry, } from './types.js';
|
|
2
|
-
export { getMemoryRoutingProvider, getMemoryRoutingProviderSync, getMemoryRoutingConfig, resetMemoryRoutingProvider, initializeMemoryRouting, } from './provider-registry.js';
|
|
2
|
+
export { getMemoryRoutingProvider, getMemoryRoutingProviderSync, ensureMemoryRoutingProviderSync, getMemoryRoutingConfig, resetMemoryRoutingProvider, initializeMemoryRouting, } from './provider-registry.js';
|
|
3
3
|
export { NullMemoryRoutingProvider, createMemoryRoutingProvider } from './null-provider.js';
|
|
4
4
|
export { loadMemoryRoutingProvider, isLeftoverMemoryRoutingOff, isExplicitMemoryRoutingOptOut, resolveLeftoverEnabledConfig, } from './provider-loader.js';
|
|
5
5
|
export { validateMemoryRoutingConfig } from './validate-config.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { getMemoryRoutingProvider, getMemoryRoutingProviderSync, getMemoryRoutingConfig, resetMemoryRoutingProvider, initializeMemoryRouting, } from './provider-registry.js';
|
|
1
|
+
export { getMemoryRoutingProvider, getMemoryRoutingProviderSync, ensureMemoryRoutingProviderSync, getMemoryRoutingConfig, resetMemoryRoutingProvider, initializeMemoryRouting, } from './provider-registry.js';
|
|
2
2
|
export { NullMemoryRoutingProvider, createMemoryRoutingProvider } from './null-provider.js';
|
|
3
3
|
export { loadMemoryRoutingProvider, isLeftoverMemoryRoutingOff, isExplicitMemoryRoutingOptOut, resolveLeftoverEnabledConfig, } from './provider-loader.js';
|
|
4
4
|
export { validateMemoryRoutingConfig } from './validate-config.js';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
const START = "<!-- lessons -->";
|
|
4
|
+
const END = "<!-- /lessons -->";
|
|
5
|
+
const DEST = path.join(".xray", "state", "repertoire", "curated_signals.json");
|
|
6
|
+
const FLOOR = 0.55;
|
|
7
|
+
function aboveFloor(value) {
|
|
8
|
+
return typeof value === "number" && value > FLOOR && Math.abs(value - FLOOR) > 1e-4;
|
|
9
|
+
}
|
|
10
|
+
function lessonBlock(signals) {
|
|
11
|
+
const lines = ["## Lessons"];
|
|
12
|
+
const graded = signals.filter((signal) => typeof signal.name === "string"
|
|
13
|
+
&& aboveFloor(signal.observation_stats?.avg_confidence)
|
|
14
|
+
&& (signal.lessons?.length ?? 0) > 0);
|
|
15
|
+
if (graded.length === 0) {
|
|
16
|
+
lines.push("No graded lines above the floor.");
|
|
17
|
+
return lines.join("\n");
|
|
18
|
+
}
|
|
19
|
+
for (const signal of graded) {
|
|
20
|
+
lines.push(`### ${signal.name}`);
|
|
21
|
+
if (typeof signal.definition === "string" && signal.definition.length > 0) {
|
|
22
|
+
lines.push(signal.definition);
|
|
23
|
+
}
|
|
24
|
+
for (const lesson of signal.lessons ?? []) {
|
|
25
|
+
const text = lesson.text.length > 0 ? lesson.text : "graded";
|
|
26
|
+
lines.push(`- ${lesson.decision} \`${lesson.taskId}\` — ${text}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return lines.join("\n");
|
|
30
|
+
}
|
|
31
|
+
/** Refresh the Station lessons section from dest. Leaves the rest of the card alone. */
|
|
32
|
+
export function writeLessonPickup(projectRoot) {
|
|
33
|
+
const stationPath = path.join(projectRoot, ".xray", "state", "STATION.md");
|
|
34
|
+
if (!fs.existsSync(stationPath))
|
|
35
|
+
return;
|
|
36
|
+
const destPath = path.join(projectRoot, DEST);
|
|
37
|
+
let signals = [];
|
|
38
|
+
if (fs.existsSync(destPath)) {
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(fs.readFileSync(destPath, "utf-8"));
|
|
41
|
+
if (parsed && typeof parsed === "object" && "signals" in parsed && Array.isArray(parsed.signals)) {
|
|
42
|
+
signals = parsed.signals;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
signals = [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const section = `${START}\n${lessonBlock(signals)}\n${END}`;
|
|
50
|
+
const current = fs.readFileSync(stationPath, "utf-8");
|
|
51
|
+
const pattern = /<!-- lessons -->[\s\S]*?<!-- \/lessons -->/;
|
|
52
|
+
const next = pattern.test(current)
|
|
53
|
+
? current.replace(pattern, section)
|
|
54
|
+
: `${current.trimEnd()}\n\n${section}\n`;
|
|
55
|
+
if (next !== current)
|
|
56
|
+
fs.writeFileSync(stationPath, next);
|
|
57
|
+
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { MemoryRoutingConfig, MemoryRoutingProvider } from './types.js';
|
|
2
2
|
export declare function isExplicitMemoryRoutingOptOut(config: MemoryRoutingConfig): boolean;
|
|
3
|
-
/** Leftover shipped default: enabled false + provider null. Not an explicit opt-out.
|
|
3
|
+
/** Leftover shipped default: enabled false + provider null. Not an explicit opt-out.
|
|
4
|
+
* resolveLeftoverEnabledConfig migrates old tarballs when the organ is on disk.
|
|
5
|
+
* Explicit opt-out is enabled: false + provider: repertoire. */
|
|
4
6
|
export declare function isLeftoverMemoryRoutingOff(config: MemoryRoutingConfig): boolean;
|
|
5
7
|
export declare function resolveLeftoverEnabledConfig(config: MemoryRoutingConfig, cwd: string): MemoryRoutingConfig | null;
|
|
8
|
+
/**
|
|
9
|
+
* First route cannot wait on the async import. Node loads this ESM with require.
|
|
10
|
+
* Returns null when routing is off or the module cannot be constructed.
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadMemoryRoutingProviderSync(config: MemoryRoutingConfig, cwd?: string): MemoryRoutingProvider | null;
|
|
6
13
|
export declare function loadMemoryRoutingProvider(config: MemoryRoutingConfig, cwd?: string): Promise<MemoryRoutingProvider>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
2
3
|
import { resolve, dirname, join } from 'node:path';
|
|
3
4
|
import { pathToFileURL } from 'node:url';
|
|
4
5
|
import { frameworkLogger } from '../core/framework-logger.js';
|
|
@@ -19,6 +20,7 @@ function resolveUnavailableReason(provider) {
|
|
|
19
20
|
return null;
|
|
20
21
|
}
|
|
21
22
|
}
|
|
23
|
+
const require = createRequire(import.meta.url);
|
|
22
24
|
const REPERTOIRE_CANDIDATE_PATHS = [
|
|
23
25
|
'node_modules/@0xray/repertoire/dist/provider/memory-routing-provider.js',
|
|
24
26
|
'vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js',
|
|
@@ -59,7 +61,9 @@ function defaultPathForProvider(provider, cwd) {
|
|
|
59
61
|
export function isExplicitMemoryRoutingOptOut(config) {
|
|
60
62
|
return config.enabled === false && config.provider === 'repertoire';
|
|
61
63
|
}
|
|
62
|
-
/** Leftover shipped default: enabled false + provider null. Not an explicit opt-out.
|
|
64
|
+
/** Leftover shipped default: enabled false + provider null. Not an explicit opt-out.
|
|
65
|
+
* resolveLeftoverEnabledConfig migrates old tarballs when the organ is on disk.
|
|
66
|
+
* Explicit opt-out is enabled: false + provider: repertoire. */
|
|
63
67
|
export function isLeftoverMemoryRoutingOff(config) {
|
|
64
68
|
if (config.enabled === true)
|
|
65
69
|
return false;
|
|
@@ -97,6 +101,50 @@ export function resolveLeftoverEnabledConfig(config, cwd) {
|
|
|
97
101
|
enabled.config = config.config;
|
|
98
102
|
return enabled;
|
|
99
103
|
}
|
|
104
|
+
function providerFromModule(mod, modulePath, effective) {
|
|
105
|
+
const factory = mod.createMemoryRoutingProvider ?? mod.default?.createMemoryRoutingProvider;
|
|
106
|
+
if (typeof factory !== 'function') {
|
|
107
|
+
throw new Error(`Module ${modulePath} must export createMemoryRoutingProvider()`);
|
|
108
|
+
}
|
|
109
|
+
const provider = factory((effective.config ?? {}));
|
|
110
|
+
if (!provider?.id || typeof provider.isAvailable !== 'function') {
|
|
111
|
+
throw new Error(`Invalid memory routing provider from ${modulePath}`);
|
|
112
|
+
}
|
|
113
|
+
return provider;
|
|
114
|
+
}
|
|
115
|
+
function resolveEffectiveProvider(config, cwd) {
|
|
116
|
+
const validation = validateMemoryRoutingConfig(config);
|
|
117
|
+
if (!validation.valid)
|
|
118
|
+
return null;
|
|
119
|
+
const leftover = resolveLeftoverEnabledConfig(validation.normalized, cwd);
|
|
120
|
+
const effective = leftover ?? validation.normalized;
|
|
121
|
+
if (!effective.enabled || effective.provider === 'null')
|
|
122
|
+
return null;
|
|
123
|
+
const modulePath = resolveModulePath(effective.module_path, cwd) ??
|
|
124
|
+
defaultPathForProvider(effective.provider, cwd);
|
|
125
|
+
if (!modulePath)
|
|
126
|
+
return null;
|
|
127
|
+
return { effective, modulePath };
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* First route cannot wait on the async import. Node loads this ESM with require.
|
|
131
|
+
* Returns null when routing is off or the module cannot be constructed.
|
|
132
|
+
*/
|
|
133
|
+
export function loadMemoryRoutingProviderSync(config, cwd = process.cwd()) {
|
|
134
|
+
const resolved = resolveEffectiveProvider(config, cwd);
|
|
135
|
+
if (!resolved)
|
|
136
|
+
return null;
|
|
137
|
+
try {
|
|
138
|
+
const mod = require(resolved.modulePath);
|
|
139
|
+
const provider = providerFromModule(mod, resolved.modulePath, resolved.effective);
|
|
140
|
+
if (!provider.isAvailable())
|
|
141
|
+
return null;
|
|
142
|
+
return provider;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
100
148
|
export async function loadMemoryRoutingProvider(config, cwd = process.cwd()) {
|
|
101
149
|
const validation = validateMemoryRoutingConfig(config);
|
|
102
150
|
if (!validation.valid) {
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type { MemoryRoutingConfig, MemoryRoutingProvider } from './types.js';
|
|
2
2
|
export declare function getMemoryRoutingConfig(): MemoryRoutingConfig;
|
|
3
3
|
export declare function getMemoryRoutingProvider(forceReload?: boolean): Promise<MemoryRoutingProvider>;
|
|
4
|
-
/** Synchronous accessor — returns shared null singleton until
|
|
4
|
+
/** Synchronous accessor — returns shared null singleton until a load completes. */
|
|
5
5
|
export declare function getMemoryRoutingProviderSync(): MemoryRoutingProvider;
|
|
6
|
+
/**
|
|
7
|
+
* The first scoreAndRoute call loads the organ before it routes.
|
|
8
|
+
* A later sync read sees the same provider. Off or a missing module stays null.
|
|
9
|
+
*/
|
|
10
|
+
export declare function ensureMemoryRoutingProviderSync(): MemoryRoutingProvider;
|
|
6
11
|
export declare function resetMemoryRoutingProvider(): void;
|
|
7
|
-
/** Kick off
|
|
12
|
+
/** Kick off load at module init. Sync require first so a later route does not require() an ESM module while its dynamic import is still in flight. */
|
|
8
13
|
export declare function initializeMemoryRouting(): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { featuresConfigLoader } from '../core/features-config.js';
|
|
2
2
|
import { NullMemoryRoutingProvider } from './null-provider.js';
|
|
3
|
-
import { loadMemoryRoutingProvider, resolveLeftoverEnabledConfig } from './provider-loader.js';
|
|
3
|
+
import { loadMemoryRoutingProvider, loadMemoryRoutingProviderSync, resolveLeftoverEnabledConfig, } from './provider-loader.js';
|
|
4
4
|
import { validateMemoryRoutingConfig } from './validate-config.js';
|
|
5
5
|
let cachedProvider = null;
|
|
6
6
|
let loadPromise = null;
|
|
@@ -12,30 +12,50 @@ export function getMemoryRoutingConfig() {
|
|
|
12
12
|
validation.normalized);
|
|
13
13
|
}
|
|
14
14
|
export async function getMemoryRoutingProvider(forceReload = false) {
|
|
15
|
-
if (!forceReload && cachedProvider)
|
|
15
|
+
if (!forceReload && cachedProvider && cachedProvider.id !== 'null')
|
|
16
16
|
return cachedProvider;
|
|
17
17
|
if (!forceReload && loadPromise)
|
|
18
18
|
return loadPromise;
|
|
19
19
|
const config = getMemoryRoutingConfig();
|
|
20
20
|
loadPromise = loadMemoryRoutingProvider(config).then((provider) => {
|
|
21
|
+
if (provider.id === 'null' && cachedProvider && cachedProvider.id !== 'null') {
|
|
22
|
+
loadPromise = null;
|
|
23
|
+
return cachedProvider;
|
|
24
|
+
}
|
|
21
25
|
cachedProvider = provider;
|
|
22
26
|
loadPromise = null;
|
|
23
27
|
return provider;
|
|
24
28
|
});
|
|
25
29
|
return loadPromise;
|
|
26
30
|
}
|
|
27
|
-
/** Synchronous accessor — returns shared null singleton until
|
|
31
|
+
/** Synchronous accessor — returns shared null singleton until a load completes. */
|
|
28
32
|
export function getMemoryRoutingProviderSync() {
|
|
29
33
|
return cachedProvider ?? nullProviderSingleton;
|
|
30
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* The first scoreAndRoute call loads the organ before it routes.
|
|
37
|
+
* A later sync read sees the same provider. Off or a missing module stays null.
|
|
38
|
+
*/
|
|
39
|
+
export function ensureMemoryRoutingProviderSync() {
|
|
40
|
+
if (cachedProvider && cachedProvider.id !== 'null')
|
|
41
|
+
return cachedProvider;
|
|
42
|
+
const config = getMemoryRoutingConfig();
|
|
43
|
+
if (!config.enabled)
|
|
44
|
+
return cachedProvider ?? nullProviderSingleton;
|
|
45
|
+
const loaded = loadMemoryRoutingProviderSync(config);
|
|
46
|
+
if (!loaded || loaded.id === 'null')
|
|
47
|
+
return cachedProvider ?? nullProviderSingleton;
|
|
48
|
+
cachedProvider = loaded;
|
|
49
|
+
return loaded;
|
|
50
|
+
}
|
|
31
51
|
export function resetMemoryRoutingProvider() {
|
|
32
52
|
cachedProvider = null;
|
|
33
53
|
loadPromise = null;
|
|
34
54
|
}
|
|
35
|
-
/** Kick off
|
|
55
|
+
/** Kick off load at module init. Sync require first so a later route does not require() an ESM module while its dynamic import is still in flight. */
|
|
36
56
|
export function initializeMemoryRouting() {
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
57
|
+
const loaded = ensureMemoryRoutingProviderSync();
|
|
58
|
+
if (loaded.id === 'null' && getMemoryRoutingConfig().enabled) {
|
|
39
59
|
void getMemoryRoutingProvider();
|
|
40
60
|
}
|
|
41
61
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface LessonInput {
|
|
2
|
+
operation: string;
|
|
3
|
+
success: boolean;
|
|
4
|
+
taskId: string;
|
|
5
|
+
assignedAgent: string;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
/** Signals the finished task already named. When set, text matching is not used. */
|
|
8
|
+
signals?: string[];
|
|
9
|
+
/** Approaches, solutions, or the wrong turn from the sessions that named the law. */
|
|
10
|
+
lesson?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A finished task names the signals its text actually matched and says
|
|
14
|
+
* whether the outcome was good. That is the one-tenth step. No match, no write.
|
|
15
|
+
*/
|
|
16
|
+
export declare function recordLesson(input: LessonInput): string[];
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ensureMemoryRoutingProviderSync } from "./provider-registry.js";
|
|
2
|
+
/**
|
|
3
|
+
* A finished task names the signals its text actually matched and says
|
|
4
|
+
* whether the outcome was good. That is the one-tenth step. No match, no write.
|
|
5
|
+
*/
|
|
6
|
+
export function recordLesson(input) {
|
|
7
|
+
const provider = ensureMemoryRoutingProviderSync();
|
|
8
|
+
if (provider.id === "null" || !provider.ingestFeedback)
|
|
9
|
+
return [];
|
|
10
|
+
const named = input.signals ?? provider.buildRoutingContext(input.operation).matchedSignals;
|
|
11
|
+
const names = [...new Set(named.filter((name) => name.length > 0))];
|
|
12
|
+
if (names.length === 0)
|
|
13
|
+
return [];
|
|
14
|
+
provider.ingestFeedback({
|
|
15
|
+
timestamp: new Date().toISOString(),
|
|
16
|
+
sessionId: input.sessionId,
|
|
17
|
+
taskId: input.taskId,
|
|
18
|
+
assignedAgent: input.assignedAgent,
|
|
19
|
+
memorySignals: names,
|
|
20
|
+
complexity: 0,
|
|
21
|
+
success: input.success,
|
|
22
|
+
durationMs: 0,
|
|
23
|
+
lesson: input.lesson ?? "",
|
|
24
|
+
});
|
|
25
|
+
return names;
|
|
26
|
+
}
|