@eir-labs/coltrane 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +85 -0
- package/agents/anticipation-mapper.json +34 -0
- package/agents/claim-amender.json +34 -0
- package/agents/claim-architect.json +34 -0
- package/agents/claim-rewriter.json +32 -0
- package/agents/delivery-finalizer.json +40 -0
- package/agents/diamond-cutter.json +33 -0
- package/agents/disclosure-analyst.json +26 -0
- package/agents/domain-explorer.json +37 -0
- package/agents/e2e-runner.json +34 -0
- package/agents/e2e-suite-judge.json +26 -0
- package/agents/event-clusterer.json +29 -0
- package/agents/novelty-searcher.json +40 -0
- package/agents/patent-browser-scout.json +49 -0
- package/agents/patent-examiner.json +36 -0
- package/agents/phase_agents/claim-rewriter.md +44 -0
- package/agents/phase_agents/delivery-finalizer.md +30 -0
- package/agents/phase_agents/diamond-cutter.md +47 -0
- package/agents/phase_agents/domain-explorer.md +27 -0
- package/agents/phase_agents/novelty-searcher.md +50 -0
- package/agents/phase_agents/problem-definer.md +32 -0
- package/agents/phase_agents/solution-developer.md +26 -0
- package/agents/phase_agents/verdict-judger.md +70 -0
- package/agents/players/audience-modeler.md +40 -0
- package/agents/players/chain-audit-keeper.md +41 -0
- package/agents/players/illumination-reviewer.md +42 -0
- package/agents/players/methodology-cadence-keeper.md +42 -0
- package/agents/players/substrate-edge-keeper.md +45 -0
- package/agents/prior-art-scout.json +45 -0
- package/agents/problem-definer.json +35 -0
- package/agents/seeds/steve_seed_a.json +12 -0
- package/agents/seeds/steve_seed_b.json +12 -0
- package/agents/seeds/steve_seed_c.json +12 -0
- package/agents/seeds/steve_seed_d.json +12 -0
- package/agents/sensor.json +22 -0
- package/agents/solution-developer.json +35 -0
- package/agents/source-walker.json +38 -0
- package/agents/spec-drafter.json +31 -0
- package/agents/sub_thread_soft_judge.json +26 -0
- package/agents/sub_thread_trace_sensor.json +22 -0
- package/agents/summarizer.json +25 -0
- package/agents/synthesis-writer.json +29 -0
- package/agents/triage-judge.json +39 -0
- package/agents/user_flow_judge.json +26 -0
- package/agents/user_flow_sensor.json +22 -0
- package/agents/verdict-judger.json +39 -0
- package/core_types/artifact.json +17 -0
- package/core_types/interpretation.json +16 -0
- package/core_types/judgment.json +16 -0
- package/core_types/plan.json +16 -0
- package/core_types/signal.json +16 -0
- package/core_types/verdict.json +27 -0
- package/dist/src/access_grant.d.ts +46 -0
- package/dist/src/access_grant.js +76 -0
- package/dist/src/access_grant.js.map +1 -0
- package/dist/src/acoustics.d.ts +9 -0
- package/dist/src/acoustics.js +65 -0
- package/dist/src/acoustics.js.map +1 -0
- package/dist/src/agent_profile.d.ts +36 -0
- package/dist/src/agent_profile.js +57 -0
- package/dist/src/agent_profile.js.map +1 -0
- package/dist/src/bifrost_invoker.d.ts +33 -0
- package/dist/src/bifrost_invoker.js +85 -0
- package/dist/src/bifrost_invoker.js.map +1 -0
- package/dist/src/canonical_core_types.d.ts +2 -0
- package/dist/src/canonical_core_types.js +111 -0
- package/dist/src/canonical_core_types.js.map +1 -0
- package/dist/src/canonical_form.d.ts +55 -0
- package/dist/src/canonical_form.js +93 -0
- package/dist/src/canonical_form.js.map +1 -0
- package/dist/src/charter.d.ts +37 -0
- package/dist/src/charter.js +86 -0
- package/dist/src/charter.js.map +1 -0
- package/dist/src/circle_of_fifths.d.ts +27 -0
- package/dist/src/circle_of_fifths.js +78 -0
- package/dist/src/circle_of_fifths.js.map +1 -0
- package/dist/src/claude_invoker.d.ts +102 -0
- package/dist/src/claude_invoker.js +760 -0
- package/dist/src/claude_invoker.js.map +1 -0
- package/dist/src/coltrane_profile.d.ts +87 -0
- package/dist/src/coltrane_profile.js +132 -0
- package/dist/src/coltrane_profile.js.map +1 -0
- package/dist/src/composition.d.ts +57 -0
- package/dist/src/composition.js +447 -0
- package/dist/src/composition.js.map +1 -0
- package/dist/src/core_types.d.ts +6 -0
- package/dist/src/core_types.js +25 -0
- package/dist/src/core_types.js.map +1 -0
- package/dist/src/document_factory.d.ts +89 -0
- package/dist/src/document_factory.js +176 -0
- package/dist/src/document_factory.js.map +1 -0
- package/dist/src/genome_schema.d.ts +512 -0
- package/dist/src/genome_schema.js +168 -0
- package/dist/src/genome_schema.js.map +1 -0
- package/dist/src/genome_writer.d.ts +67 -0
- package/dist/src/genome_writer.js +179 -0
- package/dist/src/genome_writer.js.map +1 -0
- package/dist/src/gig_song.d.ts +29 -0
- package/dist/src/gig_song.js +56 -0
- package/dist/src/gig_song.js.map +1 -0
- package/dist/src/gig_tracker.d.ts +54 -0
- package/dist/src/gig_tracker.js +109 -0
- package/dist/src/gig_tracker.js.map +1 -0
- package/dist/src/harmonic_validation.d.ts +11 -0
- package/dist/src/harmonic_validation.js +35 -0
- package/dist/src/harmonic_validation.js.map +1 -0
- package/dist/src/hooks.d.ts +27 -0
- package/dist/src/hooks.js +2 -0
- package/dist/src/hooks.js.map +1 -0
- package/dist/src/index.d.ts +36 -0
- package/dist/src/index.js +37 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/jsong.d.ts +47 -0
- package/dist/src/jsong.js +130 -0
- package/dist/src/jsong.js.map +1 -0
- package/dist/src/judges/user_flow_judge.d.ts +89 -0
- package/dist/src/judges/user_flow_judge.js +200 -0
- package/dist/src/judges/user_flow_judge.js.map +1 -0
- package/dist/src/learner.d.ts +37 -0
- package/dist/src/learner.js +30 -0
- package/dist/src/learner.js.map +1 -0
- package/dist/src/ledger.d.ts +225 -0
- package/dist/src/ledger.js +306 -0
- package/dist/src/ledger.js.map +1 -0
- package/dist/src/loader.d.ts +87 -0
- package/dist/src/loader.js +504 -0
- package/dist/src/loader.js.map +1 -0
- package/dist/src/mcp.d.ts +23 -0
- package/dist/src/mcp.js +125 -0
- package/dist/src/mcp.js.map +1 -0
- package/dist/src/modulation_path.d.ts +42 -0
- package/dist/src/modulation_path.js +60 -0
- package/dist/src/modulation_path.js.map +1 -0
- package/dist/src/output_validation.d.ts +19 -0
- package/dist/src/output_validation.js +51 -0
- package/dist/src/output_validation.js.map +1 -0
- package/dist/src/outputs.d.ts +125 -0
- package/dist/src/outputs.js +452 -0
- package/dist/src/outputs.js.map +1 -0
- package/dist/src/overtones.d.ts +42 -0
- package/dist/src/overtones.js +98 -0
- package/dist/src/overtones.js.map +1 -0
- package/dist/src/player_to_claude_code.d.ts +17 -0
- package/dist/src/player_to_claude_code.js +141 -0
- package/dist/src/player_to_claude_code.js.map +1 -0
- package/dist/src/playwright_cage.d.ts +28 -0
- package/dist/src/playwright_cage.js +47 -0
- package/dist/src/playwright_cage.js.map +1 -0
- package/dist/src/polyphony.d.ts +46 -0
- package/dist/src/polyphony.js +91 -0
- package/dist/src/polyphony.js.map +1 -0
- package/dist/src/portfolio.d.ts +52 -0
- package/dist/src/portfolio.js +186 -0
- package/dist/src/portfolio.js.map +1 -0
- package/dist/src/pricing.d.ts +23 -0
- package/dist/src/pricing.js +16 -0
- package/dist/src/pricing.js.map +1 -0
- package/dist/src/registry.d.ts +46 -0
- package/dist/src/registry.js +199 -0
- package/dist/src/registry.js.map +1 -0
- package/dist/src/runtime.d.ts +304 -0
- package/dist/src/runtime.js +1099 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/server.d.ts +88 -0
- package/dist/src/server.js +1692 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/server_entry.d.ts +2 -0
- package/dist/src/server_entry.js +40 -0
- package/dist/src/server_entry.js.map +1 -0
- package/dist/src/server_relay.d.ts +82 -0
- package/dist/src/server_relay.js +341 -0
- package/dist/src/server_relay.js.map +1 -0
- package/dist/src/simulate.d.ts +60 -0
- package/dist/src/simulate.js +72 -0
- package/dist/src/simulate.js.map +1 -0
- package/dist/src/skill_runner.mjs +34 -0
- package/dist/src/skill_subprocess.d.ts +65 -0
- package/dist/src/skill_subprocess.js +132 -0
- package/dist/src/skill_subprocess.js.map +1 -0
- package/dist/src/skills.d.ts +85 -0
- package/dist/src/skills.js +258 -0
- package/dist/src/skills.js.map +1 -0
- package/dist/src/subthread_recorder.d.ts +68 -0
- package/dist/src/subthread_recorder.js +166 -0
- package/dist/src/subthread_recorder.js.map +1 -0
- package/dist/src/tensor_read.d.ts +38 -0
- package/dist/src/tensor_read.js +60 -0
- package/dist/src/tensor_read.js.map +1 -0
- package/dist/src/test_honest_broker.d.ts +34 -0
- package/dist/src/test_honest_broker.js +155 -0
- package/dist/src/test_honest_broker.js.map +1 -0
- package/dist/src/test_topology.d.ts +22 -0
- package/dist/src/test_topology.js +137 -0
- package/dist/src/test_topology.js.map +1 -0
- package/dist/src/tones.d.ts +20 -0
- package/dist/src/tones.js +81 -0
- package/dist/src/tones.js.map +1 -0
- package/dist/src/tool_providers.d.ts +45 -0
- package/dist/src/tool_providers.js +104 -0
- package/dist/src/tool_providers.js.map +1 -0
- package/dist/src/type_versioning.d.ts +23 -0
- package/dist/src/type_versioning.js +31 -0
- package/dist/src/type_versioning.js.map +1 -0
- package/dist/src/version.d.ts +28 -0
- package/dist/src/version.js +86 -0
- package/dist/src/version.js.map +1 -0
- package/domain_types/claim-draft.json +34 -0
- package/domain_types/coverage-report.json +31 -0
- package/domain_types/draft-agent-profile.json +31 -0
- package/domain_types/draft-domain-type.json +28 -0
- package/domain_types/draft-standard.json +46 -0
- package/domain_types/e2e-observation.json +32 -0
- package/domain_types/e2e-report.json +30 -0
- package/domain_types/e2e-target.json +26 -0
- package/domain_types/e2e-verdict.json +26 -0
- package/domain_types/examine-round-record.json +40 -0
- package/domain_types/examiner-rejection.json +28 -0
- package/domain_types/failure-modes.json +31 -0
- package/domain_types/invention-analysis.json +34 -0
- package/domain_types/invention-spec.json +34 -0
- package/domain_types/novelty-analysis.json +28 -0
- package/domain_types/novelty-verdict.json +35 -0
- package/domain_types/parsed-conversation-trace.json +38 -0
- package/domain_types/pattern-extraction.json +62 -0
- package/domain_types/prior-art-hit.json +50 -0
- package/domain_types/project-charter.json +71 -0
- package/domain_types/provisional-draft.json +34 -0
- package/domain_types/raw-note.json +19 -0
- package/domain_types/repo-survey.json +28 -0
- package/domain_types/seeding-verdict.json +70 -0
- package/domain_types/soft-verdict.json +55 -0
- package/domain_types/summary.json +19 -0
- package/domain_types/triage-verdict.json +59 -0
- package/domain_types/user-flow-transcript.json +43 -0
- package/domain_types/user-flow-verdict.json +60 -0
- package/domain_types/verdict-record.json +39 -0
- package/package.json +82 -0
- package/skills/citation-verify/fixtures/basic.json +14 -0
- package/skills/citation-verify/meta.json +10 -0
- package/skills/citation-verify/skill.mjs +26 -0
- package/skills/claim-element-decompose/fixtures/basic.json +11 -0
- package/skills/claim-element-decompose/meta.json +10 -0
- package/skills/claim-element-decompose/skill.mjs +14 -0
- package/skills/diamond-cutting-discipline/fixtures/basic.json +13 -0
- package/skills/diamond-cutting-discipline/meta.json +19 -0
- package/skills/diamond-cutting-discipline/skill.md +46 -0
- package/skills/element-mapping-matrix/fixtures/basic.json +13 -0
- package/skills/element-mapping-matrix/meta.json +10 -0
- package/skills/element-mapping-matrix/skill.mjs +20 -0
- package/skills/number-adder/fixtures/basic.json +9 -0
- package/skills/number-adder/meta.json +10 -0
- package/skills/number-adder/skill.mjs +12 -0
- package/skills/patent-fetch/fixtures/parse.json +18 -0
- package/skills/patent-fetch/meta.json +11 -0
- package/skills/patent-fetch/skill.mjs +34 -0
- package/skills/query-expand/fixtures/basic.json +14 -0
- package/skills/query-expand/meta.json +10 -0
- package/skills/query-expand/skill.mjs +29 -0
- package/skills/run-vitest-band/fixtures/basic.json +9 -0
- package/skills/run-vitest-band/meta.json +18 -0
- package/skills/run-vitest-band/skill.mjs +22 -0
- package/skills/statutory-checklist/fixtures/basic.json +16 -0
- package/skills/statutory-checklist/meta.json +10 -0
- package/skills/statutory-checklist/skill.mjs +25 -0
- package/skills/summarize-tight/fixtures/basic.json +13 -0
- package/skills/summarize-tight/meta.json +20 -0
- package/skills/summarize-tight/skill.md +1 -0
- package/skills/verdict-gate/fixtures/basic.json +9 -0
- package/skills/verdict-gate/meta.json +10 -0
- package/skills/verdict-gate/skill.mjs +82 -0
- package/standards/e2e-suite-v0.json +44 -0
- package/standards/patent-triage-v0.json +98 -0
- package/standards/patent-triage-v1.json +84 -0
- package/standards/seed-from-local-repos-v0.json +115 -0
- package/standards/sub_thread_invocation.json +50 -0
- package/standards/summarize.json +40 -0
- package/standards/synthesis-walk-v0.json +108 -0
- package/standards/user_flow_correctness.json +57 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface DivergenceReport {
|
|
2
|
+
path: string;
|
|
3
|
+
primary: string;
|
|
4
|
+
secondary: string;
|
|
5
|
+
reason: string;
|
|
6
|
+
}
|
|
7
|
+
export interface HonestBrokerComparison<T> {
|
|
8
|
+
primary: T;
|
|
9
|
+
secondary: T;
|
|
10
|
+
agreement: boolean;
|
|
11
|
+
divergence: DivergenceReport | null;
|
|
12
|
+
}
|
|
13
|
+
export type Comparator<T> = (a: T, b: T) => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Walk two JSON-serialisable values in parallel. Returns null when structurally
|
|
16
|
+
* equal, or a DivergenceReport pinpointing the first mismatch. Arrays are
|
|
17
|
+
* length-checked then compared positionally; objects are key-set-checked then
|
|
18
|
+
* compared by key. Primitives use strict equality (NaN is treated as unequal).
|
|
19
|
+
*/
|
|
20
|
+
export declare function findDivergence(a: unknown, b: unknown, path?: string): DivergenceReport | null;
|
|
21
|
+
/**
|
|
22
|
+
* Run two independent code paths that should produce the same result under the
|
|
23
|
+
* same contract; compare structurally. The comparator defaults to deep
|
|
24
|
+
* structural equality on JSON-serialisable objects. When a custom comparator is
|
|
25
|
+
* supplied, the DivergenceReport will still be produced via findDivergence so
|
|
26
|
+
* the failure is locatable.
|
|
27
|
+
*/
|
|
28
|
+
export declare function compareHonestBroker<T>(primary: () => Promise<T> | T, secondary: () => Promise<T> | T, comparator?: Comparator<T>): Promise<HonestBrokerComparison<T>>;
|
|
29
|
+
/**
|
|
30
|
+
* Returns a new object with the named keys removed RECURSIVELY at any depth.
|
|
31
|
+
* Used to scrub volatile fields (uuids, timestamps, derived fingerprints)
|
|
32
|
+
* before comparison so two structurally-equivalent runs match.
|
|
33
|
+
*/
|
|
34
|
+
export declare function scrubKeys<T>(value: T, keys: readonly string[]): T;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Independent re-measurement layer. For a given contract, run the primary code
|
|
2
|
+
// path and a secondary code path that exercises the SAME contract via a different
|
|
3
|
+
// route, then compare. Agreement = the contract holds across both paths; a
|
|
4
|
+
// divergence is an honest finding — either the public API does work the raw path
|
|
5
|
+
// skips, or the underlying schema is under-specified.
|
|
6
|
+
//
|
|
7
|
+
// Used by tests under tests/honest_broker/. The comparator scrubs known volatile
|
|
8
|
+
// keys (uuids, timestamps, fingerprints derived from uuids) so two structurally
|
|
9
|
+
// equivalent runs match; surviving differences are real.
|
|
10
|
+
const MAX_VALUE_LEN = 200;
|
|
11
|
+
function truncate(v) {
|
|
12
|
+
let s;
|
|
13
|
+
try {
|
|
14
|
+
s = typeof v === "string" ? v : JSON.stringify(v);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
s = String(v);
|
|
18
|
+
}
|
|
19
|
+
if (s.length > MAX_VALUE_LEN)
|
|
20
|
+
s = s.slice(0, MAX_VALUE_LEN) + "…";
|
|
21
|
+
return s;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Walk two JSON-serialisable values in parallel. Returns null when structurally
|
|
25
|
+
* equal, or a DivergenceReport pinpointing the first mismatch. Arrays are
|
|
26
|
+
* length-checked then compared positionally; objects are key-set-checked then
|
|
27
|
+
* compared by key. Primitives use strict equality (NaN is treated as unequal).
|
|
28
|
+
*/
|
|
29
|
+
export function findDivergence(a, b, path = "$") {
|
|
30
|
+
if (a === b)
|
|
31
|
+
return null;
|
|
32
|
+
// both null/undefined catch-all
|
|
33
|
+
if (a === null || b === null || a === undefined || b === undefined) {
|
|
34
|
+
return {
|
|
35
|
+
path,
|
|
36
|
+
primary: truncate(a),
|
|
37
|
+
secondary: truncate(b),
|
|
38
|
+
reason: `null/undefined on one side`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const ta = typeof a;
|
|
42
|
+
const tb = typeof b;
|
|
43
|
+
if (ta !== tb) {
|
|
44
|
+
return {
|
|
45
|
+
path,
|
|
46
|
+
primary: truncate(a),
|
|
47
|
+
secondary: truncate(b),
|
|
48
|
+
reason: `type mismatch: ${ta} vs ${tb}`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (ta !== "object") {
|
|
52
|
+
return {
|
|
53
|
+
path,
|
|
54
|
+
primary: truncate(a),
|
|
55
|
+
secondary: truncate(b),
|
|
56
|
+
reason: `value mismatch (${ta})`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const aArr = Array.isArray(a);
|
|
60
|
+
const bArr = Array.isArray(b);
|
|
61
|
+
if (aArr !== bArr) {
|
|
62
|
+
return {
|
|
63
|
+
path,
|
|
64
|
+
primary: truncate(a),
|
|
65
|
+
secondary: truncate(b),
|
|
66
|
+
reason: `one side is array, other is object`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (aArr && bArr) {
|
|
70
|
+
const arrA = a;
|
|
71
|
+
const arrB = b;
|
|
72
|
+
if (arrA.length !== arrB.length) {
|
|
73
|
+
return {
|
|
74
|
+
path,
|
|
75
|
+
primary: `length=${arrA.length}`,
|
|
76
|
+
secondary: `length=${arrB.length}`,
|
|
77
|
+
reason: `array length mismatch`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
for (let i = 0; i < arrA.length; i++) {
|
|
81
|
+
const d = findDivergence(arrA[i], arrB[i], `${path}[${i}]`);
|
|
82
|
+
if (d)
|
|
83
|
+
return d;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const objA = a;
|
|
88
|
+
const objB = b;
|
|
89
|
+
const keysA = Object.keys(objA).sort();
|
|
90
|
+
const keysB = Object.keys(objB).sort();
|
|
91
|
+
if (keysA.length !== keysB.length || keysA.some((k, i) => k !== keysB[i])) {
|
|
92
|
+
const onlyA = keysA.filter((k) => !keysB.includes(k));
|
|
93
|
+
const onlyB = keysB.filter((k) => !keysA.includes(k));
|
|
94
|
+
return {
|
|
95
|
+
path,
|
|
96
|
+
primary: `keys: ${keysA.join(",")}`,
|
|
97
|
+
secondary: `keys: ${keysB.join(",")}`,
|
|
98
|
+
reason: `key-set mismatch (only-primary: [${onlyA.join(",")}], only-secondary: [${onlyB.join(",")}])`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
for (const k of keysA) {
|
|
102
|
+
const d = findDivergence(objA[k], objB[k], `${path}.${k}`);
|
|
103
|
+
if (d)
|
|
104
|
+
return d;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Run two independent code paths that should produce the same result under the
|
|
110
|
+
* same contract; compare structurally. The comparator defaults to deep
|
|
111
|
+
* structural equality on JSON-serialisable objects. When a custom comparator is
|
|
112
|
+
* supplied, the DivergenceReport will still be produced via findDivergence so
|
|
113
|
+
* the failure is locatable.
|
|
114
|
+
*/
|
|
115
|
+
export async function compareHonestBroker(primary, secondary, comparator) {
|
|
116
|
+
const primaryResult = await primary();
|
|
117
|
+
const secondaryResult = await secondary();
|
|
118
|
+
const cmp = comparator ?? (defaultDeepEqual);
|
|
119
|
+
const agreement = cmp(primaryResult, secondaryResult);
|
|
120
|
+
const divergence = agreement
|
|
121
|
+
? null
|
|
122
|
+
: findDivergence(primaryResult, secondaryResult);
|
|
123
|
+
return {
|
|
124
|
+
primary: primaryResult,
|
|
125
|
+
secondary: secondaryResult,
|
|
126
|
+
agreement,
|
|
127
|
+
divergence,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function defaultDeepEqual(a, b) {
|
|
131
|
+
return findDivergence(a, b) === null;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Returns a new object with the named keys removed RECURSIVELY at any depth.
|
|
135
|
+
* Used to scrub volatile fields (uuids, timestamps, derived fingerprints)
|
|
136
|
+
* before comparison so two structurally-equivalent runs match.
|
|
137
|
+
*/
|
|
138
|
+
export function scrubKeys(value, keys) {
|
|
139
|
+
const skip = new Set(keys);
|
|
140
|
+
function walk(v) {
|
|
141
|
+
if (v === null || typeof v !== "object")
|
|
142
|
+
return v;
|
|
143
|
+
if (Array.isArray(v))
|
|
144
|
+
return v.map(walk);
|
|
145
|
+
const out = {};
|
|
146
|
+
for (const [k, val] of Object.entries(v)) {
|
|
147
|
+
if (skip.has(k))
|
|
148
|
+
continue;
|
|
149
|
+
out[k] = walk(val);
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
return walk(value);
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=test_honest_broker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test_honest_broker.js","sourceRoot":"","sources":["../../src/test_honest_broker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type ContextSizeClass = "small" | "medium" | "large" | "overflow";
|
|
2
|
+
export type PersonaClass = "backend-eng" | "oss-maintainer" | "security-eng" | "fresh-contributor" | "non-engineer-tash-shape";
|
|
3
|
+
export interface SubThreadState {
|
|
4
|
+
parent_turn: number;
|
|
5
|
+
child_turn: number;
|
|
6
|
+
context_size_class: ContextSizeClass;
|
|
7
|
+
persona_class: PersonaClass;
|
|
8
|
+
}
|
|
9
|
+
export interface Transition {
|
|
10
|
+
from: SubThreadState;
|
|
11
|
+
to: SubThreadState;
|
|
12
|
+
kind: "spawn" | "resume" | "context-overflow" | "persona-switch";
|
|
13
|
+
}
|
|
14
|
+
export declare function buildStateSpace(maxParentTurns: number, maxChildTurns: number): SubThreadState[];
|
|
15
|
+
export declare function equivalenceClasses(states: SubThreadState[]): SubThreadState[][];
|
|
16
|
+
export declare function selectRepresentatives(classes: SubThreadState[][]): SubThreadState[];
|
|
17
|
+
export declare function coverageReport(allClasses: SubThreadState[][], testedStates: SubThreadState[]): {
|
|
18
|
+
covered: number;
|
|
19
|
+
total: number;
|
|
20
|
+
missing: SubThreadState[][];
|
|
21
|
+
};
|
|
22
|
+
export declare function adjacentTransitions(states: SubThreadState[]): Transition[];
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Topology-based test-coverage helper for sub-thread state-space tests.
|
|
2
|
+
// Saves compute by collapsing the full (parent_turn x child_turn x context_size x persona)
|
|
3
|
+
// product into equivalence classes, then picking 1-2 representatives per class
|
|
4
|
+
// instead of running every multi-turn flow exhaustively.
|
|
5
|
+
const PERSONA_CLASSES = [
|
|
6
|
+
"backend-eng",
|
|
7
|
+
"oss-maintainer",
|
|
8
|
+
"security-eng",
|
|
9
|
+
"fresh-contributor",
|
|
10
|
+
"non-engineer-tash-shape",
|
|
11
|
+
];
|
|
12
|
+
// Map child_turn -> context_size_class by accumulation bound.
|
|
13
|
+
// Bands cumulative tokens by integer turn count; overflow at the last threshold.
|
|
14
|
+
function contextSizeForTurn(childTurn) {
|
|
15
|
+
if (childTurn <= 2)
|
|
16
|
+
return "small";
|
|
17
|
+
if (childTurn <= 5)
|
|
18
|
+
return "medium";
|
|
19
|
+
if (childTurn <= 8)
|
|
20
|
+
return "large";
|
|
21
|
+
return "overflow";
|
|
22
|
+
}
|
|
23
|
+
// Enumerate all reachable sub-thread states up to the given bounds.
|
|
24
|
+
// A state is reachable if there is some parent_turn in [0, maxParentTurns]
|
|
25
|
+
// and child_turn in [0, maxChildTurns] paired with a persona; context_size_class
|
|
26
|
+
// follows from child_turn under the accumulation model above.
|
|
27
|
+
export function buildStateSpace(maxParentTurns, maxChildTurns) {
|
|
28
|
+
if (maxParentTurns < 0 || maxChildTurns < 0) {
|
|
29
|
+
throw new Error(`buildStateSpace bounds must be non-negative; got parent=${maxParentTurns}, child=${maxChildTurns}`);
|
|
30
|
+
}
|
|
31
|
+
const states = [];
|
|
32
|
+
for (let p = 0; p <= maxParentTurns; p++) {
|
|
33
|
+
for (let c = 0; c <= maxChildTurns; c++) {
|
|
34
|
+
const ctx = contextSizeForTurn(c);
|
|
35
|
+
for (const persona of PERSONA_CLASSES) {
|
|
36
|
+
states.push({
|
|
37
|
+
parent_turn: p,
|
|
38
|
+
child_turn: c,
|
|
39
|
+
context_size_class: ctx,
|
|
40
|
+
persona_class: persona,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return states;
|
|
46
|
+
}
|
|
47
|
+
// Two states are equivalent modulo (context_size_class, child_turn % 3, persona_class).
|
|
48
|
+
function classKey(s) {
|
|
49
|
+
return `${s.context_size_class}|${s.child_turn % 3}|${s.persona_class}`;
|
|
50
|
+
}
|
|
51
|
+
export function equivalenceClasses(states) {
|
|
52
|
+
const buckets = new Map();
|
|
53
|
+
for (const s of states) {
|
|
54
|
+
const k = classKey(s);
|
|
55
|
+
const existing = buckets.get(k);
|
|
56
|
+
if (existing) {
|
|
57
|
+
existing.push(s);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
buckets.set(k, [s]);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Stable iteration order by insertion.
|
|
64
|
+
return Array.from(buckets.values());
|
|
65
|
+
}
|
|
66
|
+
// Pick 1-2 representatives per class.
|
|
67
|
+
// Centroid: the median-child_turn member of the class.
|
|
68
|
+
// Boundary: the maximum-parent_turn member.
|
|
69
|
+
// If the class is singleton, return just the centroid.
|
|
70
|
+
export function selectRepresentatives(classes) {
|
|
71
|
+
const reps = [];
|
|
72
|
+
for (const cls of classes) {
|
|
73
|
+
if (cls.length === 0)
|
|
74
|
+
continue;
|
|
75
|
+
const sorted = [...cls].sort((a, b) => a.child_turn - b.child_turn);
|
|
76
|
+
const centroidIdx = Math.floor(sorted.length / 2);
|
|
77
|
+
const centroid = sorted[centroidIdx];
|
|
78
|
+
if (!centroid)
|
|
79
|
+
continue;
|
|
80
|
+
reps.push(centroid);
|
|
81
|
+
if (cls.length > 1) {
|
|
82
|
+
const boundary = [...cls].sort((a, b) => b.parent_turn - a.parent_turn)[0];
|
|
83
|
+
if (boundary && !sameState(boundary, centroid)) {
|
|
84
|
+
reps.push(boundary);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return reps;
|
|
89
|
+
}
|
|
90
|
+
function sameState(a, b) {
|
|
91
|
+
return (a.parent_turn === b.parent_turn &&
|
|
92
|
+
a.child_turn === b.child_turn &&
|
|
93
|
+
a.context_size_class === b.context_size_class &&
|
|
94
|
+
a.persona_class === b.persona_class);
|
|
95
|
+
}
|
|
96
|
+
// Coverage report: which classes are exercised by `testedStates`, which are missed.
|
|
97
|
+
export function coverageReport(allClasses, testedStates) {
|
|
98
|
+
const testedKeys = new Set();
|
|
99
|
+
for (const s of testedStates)
|
|
100
|
+
testedKeys.add(classKey(s));
|
|
101
|
+
const missing = [];
|
|
102
|
+
let covered = 0;
|
|
103
|
+
for (const cls of allClasses) {
|
|
104
|
+
const first = cls[0];
|
|
105
|
+
if (!first)
|
|
106
|
+
continue;
|
|
107
|
+
if (testedKeys.has(classKey(first))) {
|
|
108
|
+
covered += 1;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
missing.push(cls);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return { covered, total: allClasses.length, missing };
|
|
115
|
+
}
|
|
116
|
+
// Optional convenience: enumerate transitions between adjacent states.
|
|
117
|
+
// Not required by the public spec, but cheap to expose for downstream graph use.
|
|
118
|
+
export function adjacentTransitions(states) {
|
|
119
|
+
const ts = [];
|
|
120
|
+
for (let i = 0; i < states.length - 1; i++) {
|
|
121
|
+
const from = states[i];
|
|
122
|
+
const to = states[i + 1];
|
|
123
|
+
if (!from || !to)
|
|
124
|
+
continue;
|
|
125
|
+
let kind = "resume";
|
|
126
|
+
if (from.persona_class !== to.persona_class)
|
|
127
|
+
kind = "persona-switch";
|
|
128
|
+
else if (from.context_size_class !== "overflow" &&
|
|
129
|
+
to.context_size_class === "overflow")
|
|
130
|
+
kind = "context-overflow";
|
|
131
|
+
else if (to.child_turn === 0)
|
|
132
|
+
kind = "spawn";
|
|
133
|
+
ts.push({ from, to, kind });
|
|
134
|
+
}
|
|
135
|
+
return ts;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=test_topology.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test_topology.js","sourceRoot":"","sources":["../../src/test_topology.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const TONES: readonly ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
|
|
2
|
+
export declare const N_TONES = 12;
|
|
3
|
+
export type Tone = number;
|
|
4
|
+
export type Chord = readonly Tone[];
|
|
5
|
+
export declare function toneName(t: Tone): string;
|
|
6
|
+
export declare function intervalClass(a: Tone, b: Tone): number;
|
|
7
|
+
export declare function intervalConsonance(a: Tone, b: Tone): number;
|
|
8
|
+
export type Arity = "silent" | "monotonic" | "bitonic" | "tritonic" | "tetratonic" | "pentatonic" | "polyphonic";
|
|
9
|
+
export declare function classifyArity(toneCount: number): Arity;
|
|
10
|
+
export declare function chordArity(chord: Chord): Arity;
|
|
11
|
+
export declare function chordConsonance(chord: Chord): number;
|
|
12
|
+
export declare function chordTension(chord: Chord): number;
|
|
13
|
+
export interface Resolution {
|
|
14
|
+
resolves: boolean;
|
|
15
|
+
tonic: Tone | null;
|
|
16
|
+
finalConsonance: number;
|
|
17
|
+
tensionProfile: number[];
|
|
18
|
+
tensionDropped: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare function resolve(progression: readonly Chord[]): Resolution;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Song-substrate CORE — the 12 chromatic tones, additive chords, arity (the
|
|
2
|
+
// monotonic…polyphonic primitives = tone-count), interval consonance, and
|
|
3
|
+
// resolution. This is ONE channel's read (the song). Per the directive: it returns
|
|
4
|
+
// STRUCTURE, never a scalar "truth" — the song is one vantage of three
|
|
5
|
+
// (score · output · song); triangulation across them lives above this module.
|
|
6
|
+
// Nothing here collapses to pass/fail; it emits the song-read's shape for the
|
|
7
|
+
// tensor to couple against the other channels.
|
|
8
|
+
export const TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
|
|
9
|
+
export const N_TONES = 12;
|
|
10
|
+
export function toneName(t) {
|
|
11
|
+
return TONES[((t % N_TONES) + N_TONES) % N_TONES];
|
|
12
|
+
}
|
|
13
|
+
// Interval class (0..6) = the symmetric semitone distance. Consonance is grounded
|
|
14
|
+
// in just-intonation ratios: unison/octave most consonant, tritone least.
|
|
15
|
+
export function intervalClass(a, b) {
|
|
16
|
+
const d = Math.abs(a - b) % N_TONES;
|
|
17
|
+
return Math.min(d, N_TONES - d);
|
|
18
|
+
}
|
|
19
|
+
const IC_CONSONANCE = {
|
|
20
|
+
0: 1.0, // unison / octave (2:1)
|
|
21
|
+
5: 0.9, // perfect fifth / fourth (3:2, 4:3)
|
|
22
|
+
4: 0.7, // major third / minor sixth (5:4)
|
|
23
|
+
3: 0.6, // minor third / major sixth (6:5)
|
|
24
|
+
2: 0.35, // major second / minor seventh
|
|
25
|
+
1: 0.1, // minor second / major seventh
|
|
26
|
+
6: 0.05, // tritone
|
|
27
|
+
};
|
|
28
|
+
export function intervalConsonance(a, b) {
|
|
29
|
+
return IC_CONSONANCE[intervalClass(a, b)] ?? 0;
|
|
30
|
+
}
|
|
31
|
+
export function classifyArity(toneCount) {
|
|
32
|
+
switch (toneCount) {
|
|
33
|
+
case 0: return "silent";
|
|
34
|
+
case 1: return "monotonic";
|
|
35
|
+
case 2: return "bitonic";
|
|
36
|
+
case 3: return "tritonic";
|
|
37
|
+
case 4: return "tetratonic";
|
|
38
|
+
case 5: return "pentatonic";
|
|
39
|
+
default: return "polyphonic";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function chordArity(chord) {
|
|
43
|
+
return classifyArity(new Set(chord.map((t) => ((t % N_TONES) + N_TONES) % N_TONES)).size);
|
|
44
|
+
}
|
|
45
|
+
// Chord consonance = the mean of pairwise interval consonances (additive over
|
|
46
|
+
// pairs). A lone tone is maximally consonant (nothing clashes); silence is null.
|
|
47
|
+
export function chordConsonance(chord) {
|
|
48
|
+
const ts = [...new Set(chord.map((t) => ((t % N_TONES) + N_TONES) % N_TONES))];
|
|
49
|
+
if (ts.length <= 1)
|
|
50
|
+
return 1.0;
|
|
51
|
+
let sum = 0, pairs = 0;
|
|
52
|
+
for (let i = 0; i < ts.length; i++) {
|
|
53
|
+
for (let j = i + 1; j < ts.length; j++) {
|
|
54
|
+
sum += intervalConsonance(ts[i], ts[j]);
|
|
55
|
+
pairs++;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return sum / pairs;
|
|
59
|
+
}
|
|
60
|
+
// Tension = the complement of consonance. A dissonant chord wants to resolve.
|
|
61
|
+
export function chordTension(chord) {
|
|
62
|
+
return 1 - chordConsonance(chord);
|
|
63
|
+
}
|
|
64
|
+
const RESOLVE_CONSONANCE = 0.6; // a final chord at/above this is "at rest"
|
|
65
|
+
export function resolve(progression) {
|
|
66
|
+
const tensionProfile = progression.map(chordTension);
|
|
67
|
+
if (progression.length === 0) {
|
|
68
|
+
return { resolves: false, tonic: null, finalConsonance: 0, tensionProfile, tensionDropped: false };
|
|
69
|
+
}
|
|
70
|
+
const last = progression[progression.length - 1];
|
|
71
|
+
const finalConsonance = chordConsonance(last);
|
|
72
|
+
const tensionDropped = progression.length >= 2
|
|
73
|
+
? tensionProfile[tensionProfile.length - 2] > tensionProfile[tensionProfile.length - 1]
|
|
74
|
+
: true; // a lone consonant chord is trivially at rest
|
|
75
|
+
const tonic = last.length ? [...last].sort((a, b) => a - b)[0] : null;
|
|
76
|
+
return {
|
|
77
|
+
resolves: finalConsonance >= RESOLVE_CONSONANCE && tensionDropped,
|
|
78
|
+
tonic, finalConsonance, tensionProfile, tensionDropped,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=tones.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tones.js","sourceRoot":"","sources":["../../src/tones.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** How a granted tool name resolves to something runnable. */
|
|
2
|
+
export interface ToolProvider {
|
|
3
|
+
tool: string;
|
|
4
|
+
kind: "mcp" | "in_house";
|
|
5
|
+
/** the MCP server slug that hosts this tool (multiple tools may share one server). For kind "mcp"
|
|
6
|
+
* it's the external server; for kind "in_house" it's the engine's own server (#204), through which
|
|
7
|
+
* the tool is bridged and under whose mcp__<server>__ prefix it is advertised. */
|
|
8
|
+
server?: string;
|
|
9
|
+
/** for kind "mcp": the server's --mcp-config entry (command/args/env/url). */
|
|
10
|
+
config?: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
export type ToolProviderRegistry = ReadonlyMap<string, ToolProvider>;
|
|
13
|
+
/** The engine's own MCP server slug — the key the repo's .mcp.json ships it under, and the prefix
|
|
14
|
+
* its tools are advertised behind (mcp__coltrane__<tool>). In-house engine tools are bridged through
|
|
15
|
+
* this server, so an in-house grant resolves to it (#204). */
|
|
16
|
+
export declare const ENGINE_MCP_SERVER = "coltrane";
|
|
17
|
+
/** A scoped grant like `Bash(npx vitest run:*)` resolves on its base tool name (`Bash`). */
|
|
18
|
+
export declare function toolBaseName(grant: string): string;
|
|
19
|
+
export interface ResolvedGrants {
|
|
20
|
+
/** MCP server slug → its --mcp-config entry, deduped across all grants that share a server. */
|
|
21
|
+
mcpServers: Record<string, unknown>;
|
|
22
|
+
/** in-house runtime tools the spawn should expose. */
|
|
23
|
+
inHouse: string[];
|
|
24
|
+
/** grants with no resolvable provider — dead names. Dispatch must refuse these. */
|
|
25
|
+
unknown: string[];
|
|
26
|
+
/** the grants as the SPAWN will see them, to feed --allowedTools (NOT the raw allowed_tools).
|
|
27
|
+
* Host-builtins and `mcp__<server>__*` grants pass through unchanged; a registry tool that
|
|
28
|
+
* resolves to an MCP server — INCLUDING an in-house engine tool bridged through the engine's own
|
|
29
|
+
* server — is namespaced to `mcp__<server>__<tool>`, because that is the name the server actually
|
|
30
|
+
* advertises. A bare slug (e.g. `output_write`) in --allowedTools matches nothing and the agent
|
|
31
|
+
* can't seal — #204. An in-house tool with no engine-server config keeps its bare name (no regression). */
|
|
32
|
+
effectiveAllowed: string[];
|
|
33
|
+
}
|
|
34
|
+
/** Claude Code MCP tools follow the `mcp__<server>__<tool>` naming convention. Extract the server
|
|
35
|
+
* slug so an MCP grant resolves to its server by convention — no per-tool registration needed. */
|
|
36
|
+
export declare function mcpServerOf(grant: string): string | null;
|
|
37
|
+
/** Resolve an agent's allowed_tools to providers. In order: a host-builtin passes (no server); an
|
|
38
|
+
* explicit registry entry wins; an `mcp__<server>__*` grant resolves by convention to that server's
|
|
39
|
+
* config from `mcpServerConfigs` (deployment-registered; coltrane ships its own); everything else —
|
|
40
|
+
* including an MCP grant for an unconfigured server — is `unknown` (a dead name). */
|
|
41
|
+
export declare function resolveToolGrants(allowed: readonly string[], registry: ToolProviderRegistry, mcpServerConfigs?: Readonly<Record<string, unknown>>): ResolvedGrants;
|
|
42
|
+
/** Fail closed: an agent that grants an unresolvable tool must not be dispatched — a granted tool
|
|
43
|
+
* with no provider is indistinguishable from a real one until the agent tries (and fails) to call
|
|
44
|
+
* it. Raising here at dispatch surfaces it as a clear error instead of a confabulated answer. */
|
|
45
|
+
export declare function assertToolGrantsResolvable(agentSlug: string, allowed: readonly string[], registry: ToolProviderRegistry, mcpServerConfigs?: Readonly<Record<string, unknown>>): ResolvedGrants;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Tool-grant → provider resolution (#185). An agent's `allowed_tools` is a set of grants, each of
|
|
2
|
+
// which resolves to one of three provider classes:
|
|
3
|
+
// - host-builtin (Read/Write/Edit/Bash/Glob/Grep/WebFetch/WebSearch/…): provided by the Claude
|
|
4
|
+
// runtime, governed by --allowedTools + code_tool_access. No MCP server needed.
|
|
5
|
+
// - mcp: provided by an MCP server (e.g. a browser server) that must be wired into the spawn's
|
|
6
|
+
// --mcp-config. The OSS engine ships this RESOLUTION machinery; a deployment registers the
|
|
7
|
+
// actual server config (the server itself is deployment-provided).
|
|
8
|
+
// - in_house: a runtime primitive (e.g. output_write) the engine provides directly.
|
|
9
|
+
// A grant that resolves to NONE of these is a DEAD NAME — the prompt lists a tool the spawn can't
|
|
10
|
+
// provide, so the model confabulates. resolveToolGrants surfaces those as `unknown` so dispatch can
|
|
11
|
+
// fail closed instead of launching a spawn that advertises tools it cannot back.
|
|
12
|
+
// Host-builtin Claude Code tools — provided by the runtime, not an MCP server. Grants are governed
|
|
13
|
+
// by --allowedTools + code_tool_access; they never need a provider entry and are never "unknown".
|
|
14
|
+
const HOST_BUILTINS = new Set([
|
|
15
|
+
"Read", "Write", "Edit", "MultiEdit", "NotebookEdit",
|
|
16
|
+
"Bash", "BashOutput", "KillShell",
|
|
17
|
+
"Glob", "Grep", "LS",
|
|
18
|
+
"WebFetch", "WebSearch",
|
|
19
|
+
"Task", "TodoWrite",
|
|
20
|
+
]);
|
|
21
|
+
/** The engine's own MCP server slug — the key the repo's .mcp.json ships it under, and the prefix
|
|
22
|
+
* its tools are advertised behind (mcp__coltrane__<tool>). In-house engine tools are bridged through
|
|
23
|
+
* this server, so an in-house grant resolves to it (#204). */
|
|
24
|
+
export const ENGINE_MCP_SERVER = "coltrane";
|
|
25
|
+
/** A scoped grant like `Bash(npx vitest run:*)` resolves on its base tool name (`Bash`). */
|
|
26
|
+
export function toolBaseName(grant) {
|
|
27
|
+
const paren = grant.indexOf("(");
|
|
28
|
+
return (paren >= 0 ? grant.slice(0, paren) : grant).trim();
|
|
29
|
+
}
|
|
30
|
+
/** Claude Code MCP tools follow the `mcp__<server>__<tool>` naming convention. Extract the server
|
|
31
|
+
* slug so an MCP grant resolves to its server by convention — no per-tool registration needed. */
|
|
32
|
+
export function mcpServerOf(grant) {
|
|
33
|
+
const m = toolBaseName(grant).match(/^mcp__(.+?)__/);
|
|
34
|
+
return m ? m[1] : null;
|
|
35
|
+
}
|
|
36
|
+
/** Resolve an agent's allowed_tools to providers. In order: a host-builtin passes (no server); an
|
|
37
|
+
* explicit registry entry wins; an `mcp__<server>__*` grant resolves by convention to that server's
|
|
38
|
+
* config from `mcpServerConfigs` (deployment-registered; coltrane ships its own); everything else —
|
|
39
|
+
* including an MCP grant for an unconfigured server — is `unknown` (a dead name). */
|
|
40
|
+
export function resolveToolGrants(allowed, registry, mcpServerConfigs = {}) {
|
|
41
|
+
const mcpServers = {};
|
|
42
|
+
const inHouse = [];
|
|
43
|
+
const unknown = [];
|
|
44
|
+
const effectiveAllowed = [];
|
|
45
|
+
for (const grant of allowed) {
|
|
46
|
+
const base = toolBaseName(grant);
|
|
47
|
+
if (HOST_BUILTINS.has(base)) {
|
|
48
|
+
effectiveAllowed.push(grant); // builtins pass through with their scoping intact
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const prov = registry.get(base);
|
|
52
|
+
if (prov) {
|
|
53
|
+
if (prov.kind === "mcp" && prov.server) {
|
|
54
|
+
if (prov.config !== undefined)
|
|
55
|
+
mcpServers[prov.server] = prov.config;
|
|
56
|
+
effectiveAllowed.push(`mcp__${prov.server}__${base}`); // the name the server advertises
|
|
57
|
+
}
|
|
58
|
+
else if (prov.kind === "in_house") {
|
|
59
|
+
inHouse.push(base);
|
|
60
|
+
// #204 — an in-house engine tool is bridged through the engine's OWN MCP server. Resolving
|
|
61
|
+
// it as a "runtime primitive" left the server out of the spawn's mcp-config AND advertised
|
|
62
|
+
// the bare slug, which the server's namespaced surface never matched — so the agent could
|
|
63
|
+
// never call it (drafts emitted as text, nothing sealed). Wire the engine server in and name
|
|
64
|
+
// the tool the way the server exposes it. No engine-server config → keep the bare name.
|
|
65
|
+
if (prov.server && Object.prototype.hasOwnProperty.call(mcpServerConfigs, prov.server)) {
|
|
66
|
+
mcpServers[prov.server] = mcpServerConfigs[prov.server];
|
|
67
|
+
effectiveAllowed.push(`mcp__${prov.server}__${base}`);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
effectiveAllowed.push(base);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
effectiveAllowed.push(grant);
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const server = mcpServerOf(base);
|
|
79
|
+
if (server) {
|
|
80
|
+
if (Object.prototype.hasOwnProperty.call(mcpServerConfigs, server)) {
|
|
81
|
+
mcpServers[server] = mcpServerConfigs[server];
|
|
82
|
+
effectiveAllowed.push(grant); // already in mcp__<server>__<tool> form
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
unknown.push(grant); // an MCP tool whose server has no registered config — a dead name
|
|
86
|
+
}
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
unknown.push(grant);
|
|
90
|
+
}
|
|
91
|
+
return { mcpServers, inHouse, unknown, effectiveAllowed };
|
|
92
|
+
}
|
|
93
|
+
/** Fail closed: an agent that grants an unresolvable tool must not be dispatched — a granted tool
|
|
94
|
+
* with no provider is indistinguishable from a real one until the agent tries (and fails) to call
|
|
95
|
+
* it. Raising here at dispatch surfaces it as a clear error instead of a confabulated answer. */
|
|
96
|
+
export function assertToolGrantsResolvable(agentSlug, allowed, registry, mcpServerConfigs = {}) {
|
|
97
|
+
const resolved = resolveToolGrants(allowed, registry, mcpServerConfigs);
|
|
98
|
+
if (resolved.unknown.length > 0) {
|
|
99
|
+
throw new Error(`agent "${agentSlug}" grants unresolvable tool(s) [${resolved.unknown.join(", ")}] — no provider registered ` +
|
|
100
|
+
`(a granted tool with no provider is a dead name; register a provider or remove the grant)`);
|
|
101
|
+
}
|
|
102
|
+
return resolved;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=tool_providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool_providers.js","sourceRoot":"","sources":["../../src/tool_providers.ts"],"names":[],"mappings":"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"}
|