mirror of
https://github.com/morluto/rea
synced 2026-10-28 06:46:47 -04:00
fix(experiment): align named reference lookup with input contracts
Share displayed method names with direct registration, support explicit multi-name selection and unmatched terms, and cache immutable schema conversions by schema identity. Export input-side schemas so defaulted fields stay optional; verify both actual MCP catalogs and named contracts before repeating the unchanged model task. Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
This commit is contained in:
@@ -214,3 +214,12 @@ The trace reveals two format sources at each shared call, verified against byte
|
||||
and machine evidence. [Costs and source review](trials.md) also record a direct
|
||||
documentation-lookup miss; named discovery needs fixing before rerunning the
|
||||
same task. No general adapter speedup follows from that first pair.
|
||||
|
||||
Named reference lookup now accepts canonical IDs or exact displayed names,
|
||||
including space/comma-separated lists. One presentation map supplies direct
|
||||
registration and documentation names; unmatched terms remain explicit. Selected
|
||||
schemas come from named contracts, use input semantics (defaulted fields stay
|
||||
optional), and are cached immutably by Zod schema identity for the process.
|
||||
Validation stays in the API registry; documentation has its own module. Both
|
||||
actual MCP catalogs and all fourteen reference schemas validate. The unchanged
|
||||
PHI question/oracle will be rerun after this discovery correction.
|
||||
|
||||
@@ -3,6 +3,7 @@ import { z } from "zod";
|
||||
import { KunaFactOwner } from "./provider.mjs";
|
||||
import { FactFailure } from "./fact-error.mjs";
|
||||
import { MAX_BYTE_BATCH_BYTES } from "./limits.mjs";
|
||||
import { createReference } from "./reference.mjs";
|
||||
|
||||
const hex = z.string().regex(/^0x(?:0|[1-9a-f][0-9a-f]{0,15})$/u);
|
||||
const snapshot = z.string().startsWith("snap_");
|
||||
@@ -250,36 +251,6 @@ export const methods = {
|
||||
),
|
||||
};
|
||||
|
||||
// Compact return contracts let code compose before first observing a response.
|
||||
const returns = {
|
||||
bytes:
|
||||
"data:{results:[{ok,address,source:{evidence_id,sha256},hex,returned_bytes,end_address,coverage:{status,requested_bytes,provider_truncated,notes,original_file_mapping,runtime_memory}}|{ok:false,address,error}]}; ranges are [address,end_address). Completeness concerns requested bytes, not the provider truncation flag or adjacent bytes. Complete native hexdump and metadata remain in Evidence.",
|
||||
functions:
|
||||
"data:{source:{evidence_id,sha256},items:[{address,name,aliases,size}],total,next_offset,coverage}; size is an upper bound, or zero if unmeasured. Multiple matching addresses are retained, never selected by catalog order or display name alone.",
|
||||
instructions:
|
||||
"data:{results:[{ok,address,source:{evidence_id,sha256},items:[{address,size,status,bytes?,text?,mnemonic?,operands?}],resume_address,coverage}|{ok:false,address,error}]}; decoded rows describe selected machine bytes, not optimized IR branches. Partial batches retain successful windows.",
|
||||
strings:
|
||||
"data:{source:{evidence_id,sha256},items:[{address,value,string:{encoding,byte_length}}],total,next_offset,coverage}",
|
||||
references:
|
||||
"data:{results:[{ok,address,source:{evidence_id},items:[{source_row,from,to,kind,fromFunction:{address,name}|null,toFunction:{address,name}|null,instruction}],total,next_offset,coverage}|{ok:false,address,error}]}. source_row is the zero-based row in retained native xrefs; Evidence identity plus source_row identifies an occurrence. Coverage preserves examined/excluded/unattributed counts and unknown function scope/discovery. fields projects optional columns; endpoints/kind/source_row stay.",
|
||||
snapshot:
|
||||
"data:{snapshot:string,entry,source:{evidence_id,sha256},facets:{code:{availability},recovered_ir:{availability,error?},operations:{availability,scope?,count?,root_count?,definition_frontier_count?,error?},calls:{availability,count?,by_opcode?:[{opcode,known_target_count,unknown_target_count}],coverage?,error?},values:{availability,scope?,count?,error?}},coverage}. Call counts cover all retained recovered calls, including is_dead rows, not complete machine/runtime calls; target knowledge comes from a non-null native target, independent of opcode. An unavailable facet omits counts; an available empty call facet reports zeros. The complete graph can be unavailable while operations reports a bounded projection count; an unavailable operation facet has unknown/null counts, never a fabricated empty set; code remains readable through the same handle.",
|
||||
operations:
|
||||
"data:{items:[{id,origin_address,block,source_line,kind,statement,definitions,constants,inputs?,output?,opcode?,is_dead?}],total,next_offset,evidence_id,coverage}. Native inputs:[{input_index,value_id,phi_predecessor,phi_predecessor_edge}], output:value_id|null; native constants include value_id/input_index and exact u64 value offset with printed_size_bytes (native width), not arbitrary-width computed values; definitions:[{definition,use,input_index,value_id,phi_predecessor,phi_predecessor_edge}]. Diagnostic definitions use printed occurrence instead. fields projects columns; identity and native is_dead remain. A call-rooted projection reports retained facts only, with unknown CFG and explicit definition frontier coverage.",
|
||||
relations:
|
||||
"data:{items:definition/use edges OR CFG edges{from,to,operation,polarity?,condition_value?,condition_test?} OR controllers{controller,dependent,edge},total,next_offset,evidence_id,coverage,relation_coverage?,unretained_seeds?}. definitions/uses seeds are operation-ID strings; incoming/outgoing/controllers seeds are numeric block IDs. Native def/use edges include input_index,value_id,phi_predecessor; diagnostic edges include occurrence only.",
|
||||
values:
|
||||
"data:{items:[{id,space,offset,size_bytes,is_constant,is_input,is_annotation,definition,type_name}],total,next_offset,evidence_id,coverage}; definitions are operation IDs, offsets are exact hex strings. When the graph is unavailable, values may come from a bounded definition projection or only the call boundary; coverage states that scope and operations/definitions may be unreadable.",
|
||||
calls:
|
||||
"data:{items:[{id,origin_address,block,opcode,spec_available,is_dead,target:{space,offset}|null,target_name,arguments:[{input_index,value_id,storage:{space,offset,size_bytes}|null,storage_authority}],output}],total,next_offset,missing,evidence_id,coverage}. fields selects target/target_name/arguments/output/prototype/stack_offset/target_value_id/is_dead. Values may come from a bounded call-rooted definition projection or call-boundary set when the graph is unavailable; coverage states that scope. Calls include all retained call operations; null facts are unknown, not proof of no arguments or runtime binding. Candidate cross-function links require matching target identity and exact passing/input storage; join/subregister/alias interpretation remains unknown.",
|
||||
blocks:
|
||||
"data:{items:[{id,origin_start,operations,predecessors,successors:[{target,index,polarity,condition_value,condition_test}]}],total,next_offset,evidence_id,coverage}; IDs are local to this snapshot.",
|
||||
slice:
|
||||
"data:{operations:[operation rows as above],coverage:{status,boundaries:[{id,reason}],missing,missing_reasons?,source_scope?,source_issues,runtime_reachability,memory_alias_dependencies},value_frontier?:[{value_id?|operation?,input_index?,reason}],evidence_id}",
|
||||
code: "data:{evidence_id,start_line,language:string|null,text,total_lines,next_offset,coverage}; language is native-reported or unknown. Read/filter full reconstructed text within Code Mode without returning it all.",
|
||||
evidence:
|
||||
"data:{source:{evidence_id,sha256,bytes,arguments},text,total_code_units,next_offset}; source has complete native output, including code before the SSA graph.",
|
||||
};
|
||||
methods.bytes.input = methods.bytes.input.superRefine((value, context) => {
|
||||
// Pattern failures need not prevent object refinements from running in Zod.
|
||||
// Only the named window contract may admit values to exact u64 arithmetic.
|
||||
@@ -426,32 +397,4 @@ export class AtomicSession {
|
||||
}
|
||||
}
|
||||
|
||||
export const reference = (query = "") => {
|
||||
const keys = Object.keys(methods).filter(
|
||||
(key) =>
|
||||
query === "" ||
|
||||
key.includes(query) ||
|
||||
methods[key].description.toLowerCase().includes(query.toLowerCase()),
|
||||
);
|
||||
return {
|
||||
language:
|
||||
"JavaScript async function body; return a JSON value. All rea methods return {ok,context,data} or {ok:false,error}.",
|
||||
methods: keys.map((name) => ({
|
||||
name: `rea.${name}`,
|
||||
description: methods[name].description,
|
||||
input_schema: z.toJSONSchema(methods[name].input),
|
||||
returns: returns[name],
|
||||
example: methods[name].example,
|
||||
})),
|
||||
limits: {
|
||||
execution_heap_mib: 64,
|
||||
max_host_calls: 64,
|
||||
output_utf8_bytes: 32768,
|
||||
},
|
||||
notes: [
|
||||
"No process, filesystem, network, imports or Node globals in the execution environment.",
|
||||
"Select facts inside code, return only what the question needs, and keep real source IDs and unknowns.",
|
||||
"A fresh JavaScript environment is used per execute_javascript call; analysis snapshots live until close_binary.",
|
||||
],
|
||||
};
|
||||
};
|
||||
export const reference = createReference(methods);
|
||||
|
||||
@@ -542,3 +542,33 @@ stages and workspace gates remain unexecuted; the patch is not upstream-ready.
|
||||
No whole-repository typecheck pass is claimed beyond the previously recorded
|
||||
resource limitation. The [PHI-format task/oracle](projection-oracle.md) are fixed
|
||||
before real-model launches. No model benefit is assumed from these smaller facts.
|
||||
|
||||
## Named reference discovery and input schemas
|
||||
|
||||
The first PHI model pair completes correctly but exposes a documentation boundary:
|
||||
direct help displays `facts_*` names while lookup only recognizes raw method
|
||||
substrings. A multi-name query returns no matches and causes a full-reference
|
||||
fallback. Registration and help now share one explicit name map, with canonical
|
||||
identity separate from presentation. Exact lists preserve caller order,
|
||||
deduplicate names and retain unmatched terms. Topic/all-contract lookup remain;
|
||||
ambiguous labels fail instead of choosing by catalog position.
|
||||
|
||||
The real client verifier also catches that plain `z.toJSONSchema` defaults to
|
||||
output semantics, making defaulted `offset`/`limit` falsely required in advertised
|
||||
input schemas. Reference generation now explicitly selects `io:input`. Converted
|
||||
schemas are frozen and weakly cached by schema identity; a replacement schema
|
||||
invalidates the conversion, while description/presentation is read separately.
|
||||
The documentation module removes the adapter rename pass. No target analysis,
|
||||
live-result caching or native-engine change is involved.
|
||||
|
||||
The shared suite passes 55 tests. `verify-discovery.mjs` checks both real SDK
|
||||
catalogs and reference responses without opening a target, compiles every catalog
|
||||
and all fourteen reference schemas with Ajv2020, and validates the same
|
||||
omitted-default requests against named reference/direct contracts. It proves the
|
||||
six-name query matches all requested contracts without a miss. The corrected
|
||||
verifier takes 1.628 family wall / 1.449 CPU seconds, 128.9 MiB kernel peak,
|
||||
cleanup verified and OOM 0. Its original 1.730 / 1.487 seconds, 128.1 MiB failed
|
||||
omitted-default check remains recorded. Six contracts occupy 10,835 Code Mode /
|
||||
10,360 direct bytes, versus 21,746 / 20,699 for all fourteen. Bytes do not prove
|
||||
token or time savings. The same PHI question/oracle are retained for the next
|
||||
actual model pair.
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
// Named documentation selection; adapters supply their exact registered labels.
|
||||
import { z } from "zod";
|
||||
import { FactFailure } from "./fact-error.mjs";
|
||||
import { freezeFacts } from "./fact-reads.mjs";
|
||||
|
||||
// Compact return contracts let code compose before first observing a response.
|
||||
const returns = {
|
||||
bytes:
|
||||
"data:{results:[{ok,address,source:{evidence_id,sha256},hex,returned_bytes,end_address,coverage:{status,requested_bytes,provider_truncated,notes,original_file_mapping,runtime_memory}}|{ok:false,address,error}]}; ranges are [address,end_address). Completeness concerns requested bytes, not the provider truncation flag or adjacent bytes. Complete native hexdump and metadata remain in Evidence.",
|
||||
functions:
|
||||
"data:{source:{evidence_id,sha256},items:[{address,name,aliases,size}],total,next_offset,coverage}; size is an upper bound, or zero if unmeasured. Multiple matching addresses are retained, never selected by catalog order or display name alone.",
|
||||
instructions:
|
||||
"data:{results:[{ok,address,source:{evidence_id,sha256},items:[{address,size,status,bytes?,text?,mnemonic?,operands?}],resume_address,coverage}|{ok:false,address,error}]}; decoded rows describe selected machine bytes, not optimized IR branches. Partial batches retain successful windows.",
|
||||
strings:
|
||||
"data:{source:{evidence_id,sha256},items:[{address,value,string:{encoding,byte_length}}],total,next_offset,coverage}",
|
||||
references:
|
||||
"data:{results:[{ok,address,source:{evidence_id},items:[{source_row,from,to,kind,fromFunction:{address,name}|null,toFunction:{address,name}|null,instruction}],total,next_offset,coverage}|{ok:false,address,error}]}. source_row is the zero-based row in retained native xrefs; Evidence identity plus source_row identifies an occurrence. Coverage preserves examined/excluded/unattributed counts and unknown function scope/discovery. fields projects optional columns; endpoints/kind/source_row stay.",
|
||||
snapshot:
|
||||
"data:{snapshot:string,entry,source:{evidence_id,sha256},facets:{code:{availability},recovered_ir:{availability,error?},operations:{availability,scope?,count?,root_count?,definition_frontier_count?,error?},calls:{availability,count?,by_opcode?:[{opcode,known_target_count,unknown_target_count}],coverage?,error?},values:{availability,scope?,count?,error?}},coverage}. Call counts cover all retained recovered calls, including is_dead rows, not complete machine/runtime calls; target knowledge comes from a non-null native target, independent of opcode. An unavailable facet omits counts; an available empty call facet reports zeros. The complete graph can be unavailable while operations reports a bounded projection count; an unavailable operation facet has unknown/null counts, never a fabricated empty set; code remains readable through the same handle.",
|
||||
operations:
|
||||
"data:{items:[{id,origin_address,block,source_line,kind,statement,definitions,constants,inputs?,output?,opcode?,is_dead?}],total,next_offset,evidence_id,coverage}. Native inputs:[{input_index,value_id,phi_predecessor,phi_predecessor_edge}], output:value_id|null; native constants include value_id/input_index and exact u64 value offset with printed_size_bytes (native width), not arbitrary-width computed values; definitions:[{definition,use,input_index,value_id,phi_predecessor,phi_predecessor_edge}]. Diagnostic definitions use printed occurrence instead. fields projects columns; identity and native is_dead remain. A call-rooted projection reports retained facts only, with unknown CFG and explicit definition frontier coverage.",
|
||||
relations:
|
||||
"data:{items:definition/use edges OR CFG edges{from,to,operation,polarity?,condition_value?,condition_test?} OR controllers{controller,dependent,edge},total,next_offset,evidence_id,coverage,relation_coverage?,unretained_seeds?}. definitions/uses seeds are operation-ID strings; incoming/outgoing/controllers seeds are numeric block IDs. Native def/use edges include input_index,value_id,phi_predecessor; diagnostic edges include occurrence only.",
|
||||
values:
|
||||
"data:{items:[{id,space,offset,size_bytes,is_constant,is_input,is_annotation,definition,type_name}],total,next_offset,evidence_id,coverage}; definitions are operation IDs, offsets are exact hex strings. When the graph is unavailable, values may come from a bounded definition projection or only the call boundary; coverage states that scope and operations/definitions may be unreadable.",
|
||||
calls:
|
||||
"data:{items:[{id,origin_address,block,opcode,spec_available,is_dead,target:{space,offset}|null,target_name,arguments:[{input_index,value_id,storage:{space,offset,size_bytes}|null,storage_authority}],output}],total,next_offset,missing,evidence_id,coverage}. fields selects target/target_name/arguments/output/prototype/stack_offset/target_value_id/is_dead. Values may come from a bounded call-rooted definition projection or call-boundary set when the graph is unavailable; coverage states that scope. Calls include all retained call operations; null facts are unknown, not proof of no arguments or runtime binding. Candidate cross-function links require matching target identity and exact passing/input storage; join/subregister/alias interpretation remains unknown.",
|
||||
blocks:
|
||||
"data:{items:[{id,origin_start,operations,predecessors,successors:[{target,index,polarity,condition_value,condition_test}]}],total,next_offset,evidence_id,coverage}; IDs are local to this snapshot.",
|
||||
slice:
|
||||
"data:{operations:[operation rows as above],coverage:{status,boundaries:[{id,reason}],missing,missing_reasons?,source_scope?,source_issues,runtime_reachability,memory_alias_dependencies},value_frontier?:[{value_id?|operation?,input_index?,reason}],evidence_id}",
|
||||
code: "data:{evidence_id,start_line,language:string|null,text,total_lines,next_offset,coverage}; language is native-reported or unknown. Read/filter full reconstructed text within Code Mode without returning it all.",
|
||||
evidence:
|
||||
"data:{source:{evidence_id,sha256,bytes,arguments},text,total_code_units,next_offset}; source has complete native output, including code before the SSA graph.",
|
||||
};
|
||||
const referenceSchemas = new WeakMap();
|
||||
const schemaFor = (input) => {
|
||||
if (!referenceSchemas.has(input))
|
||||
referenceSchemas.set(
|
||||
input,
|
||||
freezeFacts(z.toJSONSchema(input, { io: "input" })),
|
||||
);
|
||||
return referenceSchemas.get(input);
|
||||
};
|
||||
function referenceNames(methods, names) {
|
||||
const keys = Object.keys(methods),
|
||||
lookup = new Map(keys.map((key) => [key, key])),
|
||||
labels = {};
|
||||
if (Object.keys(names).some((key) => !Object.hasOwn(methods, key)))
|
||||
throw new FactFailure(
|
||||
"invalid_input",
|
||||
"Unknown method in reference presentation",
|
||||
);
|
||||
for (const key of keys) {
|
||||
const label = names[key] ?? `rea.${key}`;
|
||||
if (
|
||||
typeof label !== "string" ||
|
||||
!label ||
|
||||
(lookup.has(label) && lookup.get(label) !== key)
|
||||
)
|
||||
throw new FactFailure(
|
||||
"invalid_input",
|
||||
"Ambiguous or invalid reference method name",
|
||||
);
|
||||
lookup.set(label, key);
|
||||
labels[key] = label;
|
||||
}
|
||||
return { lookup, labels };
|
||||
}
|
||||
function selectReference(methods, query, lookup) {
|
||||
const normalized = query.trim();
|
||||
if (!normalized)
|
||||
return { keys: Object.keys(methods), selection: "all", unmatched: [] };
|
||||
const terms = normalized.split(/[\s,]+/u),
|
||||
named = terms.map((term) => lookup.get(term));
|
||||
if (named.some(Boolean))
|
||||
return {
|
||||
keys: [...new Set(named.filter(Boolean))],
|
||||
selection: "named",
|
||||
unmatched: terms.filter((term) => !lookup.has(term)),
|
||||
};
|
||||
const topic = normalized.toLowerCase();
|
||||
const keys = Object.keys(methods).filter(
|
||||
(key) =>
|
||||
key.includes(topic) ||
|
||||
methods[key].description.toLowerCase().includes(topic),
|
||||
);
|
||||
return { keys, selection: "topic", unmatched: keys.length ? [] : [query] };
|
||||
}
|
||||
export const createReference =
|
||||
(methods) =>
|
||||
(query = "", { names = {}, language, includeExamples = true } = {}) => {
|
||||
const { lookup, labels } = referenceNames(methods, names),
|
||||
selected = selectReference(methods, query, lookup);
|
||||
return {
|
||||
language:
|
||||
language ??
|
||||
"JavaScript async function body; return a JSON value. All rea methods return {ok,context,data} or {ok:false,error}.",
|
||||
methods: selected.keys.map((method) => ({
|
||||
method,
|
||||
name: labels[method],
|
||||
description: methods[method].description,
|
||||
input_schema: schemaFor(methods[method].input),
|
||||
returns: returns[method],
|
||||
...(includeExamples ? { example: methods[method].example } : {}),
|
||||
})),
|
||||
coverage: {
|
||||
declared_methods: Object.keys(methods).length,
|
||||
matched_methods: selected.keys.length,
|
||||
selection: selected.selection,
|
||||
},
|
||||
unmatched_queries: selected.unmatched,
|
||||
limits: {
|
||||
execution_heap_mib: 64,
|
||||
max_host_calls: 64,
|
||||
output_utf8_bytes: 32768,
|
||||
},
|
||||
notes: [
|
||||
"Select exact displayed method names or canonical method IDs, separated by spaces/commas; a topic query searches descriptions. Empty query returns all contracts.",
|
||||
"No process, filesystem, network, imports or Node globals in the execution environment.",
|
||||
"Select facts inside code, return only what the question needs, and keep real source IDs and unknowns.",
|
||||
"A fresh JavaScript environment is used per execute_javascript call; analysis snapshots live until close_binary.",
|
||||
],
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import Ajv2020 from "ajv/dist/2020.js";
|
||||
import { methods, reference } from "./api.mjs";
|
||||
|
||||
test("named reference lists preserve caller order and exact schemas across presentations", () => {
|
||||
const names = Object.fromEntries(
|
||||
Object.keys(methods).map((name) => [name, `facts_${name}`]),
|
||||
);
|
||||
const result = reference(
|
||||
"facts_values, facts_snapshot facts_values unknown",
|
||||
{ names, includeExamples: false },
|
||||
);
|
||||
assert.deepEqual(
|
||||
result.methods.map((m) => m.method),
|
||||
["values", "snapshot"],
|
||||
);
|
||||
assert.deepEqual(
|
||||
result.methods.map((m) => m.name),
|
||||
["facts_values", "facts_snapshot"],
|
||||
);
|
||||
assert.deepEqual(result.unmatched_queries, ["unknown"]);
|
||||
assert.equal(result.coverage.declared_methods, 14);
|
||||
assert.equal(result.coverage.matched_methods, 2);
|
||||
assert.equal(result.coverage.selection, "named");
|
||||
const javascript = reference("rea.values snapshot");
|
||||
assert.deepEqual(
|
||||
javascript.methods.map((m) => m.method),
|
||||
["values", "snapshot"],
|
||||
);
|
||||
assert.deepEqual(
|
||||
result.methods.map((m) => m.input_schema),
|
||||
javascript.methods.map((m) => m.input_schema),
|
||||
);
|
||||
const validate = new Ajv2020({ strict: false }).compile(
|
||||
result.methods[1].input_schema,
|
||||
);
|
||||
assert.equal(validate({ entry: "0xea6d0" }), true);
|
||||
assert.equal(validate({ snapshot: "wrong contract" }), false);
|
||||
const validateValues = new Ajv2020({ strict: false }).compile(
|
||||
result.methods[0].input_schema,
|
||||
);
|
||||
assert.equal(validateValues({ snapshot: "snap_example", ids: ["v1"] }), true);
|
||||
assert.equal(
|
||||
methods.values.input.parse({ snapshot: "snap_example" }).limit,
|
||||
20,
|
||||
);
|
||||
assert.equal(Object.hasOwn(result.methods[0], "example"), false);
|
||||
});
|
||||
|
||||
test("topic/all reference selection and shared schema ownership remain explicit", () => {
|
||||
assert.equal(reference().methods.length, 14);
|
||||
assert.equal(reference("no-such-topic").coverage.matched_methods, 0);
|
||||
assert.ok(
|
||||
reference("memory aliases").methods.some((m) => m.method === "slice"),
|
||||
);
|
||||
const before = reference("snapshot").methods[0];
|
||||
assert.throws(() => {
|
||||
before.input_schema.properties.entry.type = "number";
|
||||
}, TypeError);
|
||||
const after = reference("snapshot").methods[0];
|
||||
assert.equal(after.input_schema.properties.entry.type, "string");
|
||||
assert.throws(
|
||||
() => reference("values", { names: { snapshot: "values" } }),
|
||||
/Ambiguous/,
|
||||
);
|
||||
assert.throws(
|
||||
() => reference("values", { names: { unknown: "facts_values" } }),
|
||||
/Unknown/,
|
||||
);
|
||||
});
|
||||
@@ -7,9 +7,18 @@ import { executeJavaScript } from "./executor.mjs";
|
||||
import { TARGET_CONTENT_RESULT_META } from "../../dist/server/toolResultEncoding.js";
|
||||
import { appendFile } from "node:fs/promises";
|
||||
|
||||
const DIRECT_REFERENCE_LANGUAGE =
|
||||
"Direct MCP calls: use the displayed facts_* tools with the same JSON arguments; results have {ok,context,data}.";
|
||||
const session = new AtomicSession();
|
||||
const createServer = () => {
|
||||
const server = new McpServer({ name: "rea-codemode", version: "0.1.0" });
|
||||
const direct = process.env.REA_ATOMIC_ADAPTER === "direct";
|
||||
const apiNames = Object.fromEntries(
|
||||
Object.keys(methods).map((name) => [
|
||||
name,
|
||||
direct ? `facts_${name}` : `rea.${name}`,
|
||||
]),
|
||||
);
|
||||
const encode = (value) => ({
|
||||
content: [{ type: "text", text: JSON.stringify(value) }],
|
||||
_meta: TARGET_CONTENT_RESULT_META,
|
||||
@@ -64,26 +73,19 @@ const createServer = () => {
|
||||
);
|
||||
register(
|
||||
"reference",
|
||||
"Look up the rea JavaScript API by method name or analyst concept before writing Code Mode calls. Empty query returns the complete API reference.",
|
||||
"Look up analysis contracts by exact displayed method names or canonical method IDs, separated by spaces or commas, or search one analyst concept. Request only needed methods; empty query returns all contracts.",
|
||||
z.strictObject({ query: z.string().default("") }),
|
||||
(input) => {
|
||||
const docs = reference(input.query);
|
||||
if (process.env.REA_ATOMIC_ADAPTER === "direct") {
|
||||
docs.language =
|
||||
"Direct MCP calls: use facts_<method> with the same JSON arguments; results have {ok,context,data}.";
|
||||
docs.methods = docs.methods.map((method) => ({
|
||||
...method,
|
||||
name: method.name.replace("rea.", "facts_"),
|
||||
example: undefined,
|
||||
}));
|
||||
}
|
||||
return docs;
|
||||
},
|
||||
(input) =>
|
||||
reference(input.query, {
|
||||
names: apiNames,
|
||||
includeExamples: !direct,
|
||||
language: direct ? DIRECT_REFERENCE_LANGUAGE : undefined,
|
||||
}),
|
||||
);
|
||||
if (process.env.REA_ATOMIC_ADAPTER === "direct") {
|
||||
if (direct) {
|
||||
for (const [name, contract] of Object.entries(methods))
|
||||
register(
|
||||
`facts_${name}`,
|
||||
apiNames[name],
|
||||
contract.description,
|
||||
contract.input,
|
||||
(input, signal) => session.call(name, input, signal),
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
// Verify actual SDK-converted catalogs and named help without target analysis.
|
||||
import assert from "node:assert/strict";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { resolve } from "node:path";
|
||||
import { Client } from "@modelcontextprotocol/client";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
import Ajv2020 from "ajv/dist/2020.js";
|
||||
import {
|
||||
createVerifierRun,
|
||||
completeVerifierRun,
|
||||
assertVerifierCleanupComplete,
|
||||
} from "../../scripts/lib/verifier-run.mjs";
|
||||
|
||||
const [output] = process.argv.slice(2);
|
||||
const run = createVerifierRun(),
|
||||
results = [],
|
||||
failures = [];
|
||||
for (const direct of [false, true]) {
|
||||
const client = new Client({
|
||||
name: "rea-discovery-verifier",
|
||||
version: "0.1.0",
|
||||
});
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [resolve("experiments/atomic-codemode/server.mjs")],
|
||||
env: { ...process.env, REA_ATOMIC_ADAPTER: direct ? "direct" : "codemode" },
|
||||
stderr: "pipe",
|
||||
});
|
||||
const call = async (query) => {
|
||||
const reply = await client.callTool({
|
||||
name: "reference",
|
||||
arguments: { query },
|
||||
});
|
||||
return JSON.parse(reply.content[0].text);
|
||||
};
|
||||
try {
|
||||
await client.connect(transport);
|
||||
const catalog = await client.listTools();
|
||||
const prefix = direct ? "facts_" : "rea.";
|
||||
const ids = [
|
||||
"snapshot",
|
||||
"operations",
|
||||
"relations",
|
||||
"values",
|
||||
"instructions",
|
||||
"bytes",
|
||||
];
|
||||
const selected = await call(ids.map((name) => prefix + name).join(" "));
|
||||
assert.deepEqual(
|
||||
selected.methods.map((m) => m.method),
|
||||
ids,
|
||||
);
|
||||
assert.deepEqual(selected.unmatched_queries, []);
|
||||
assert.equal(selected.coverage.declared_methods, 14);
|
||||
const ajv = new Ajv2020({ strict: false });
|
||||
for (const tool of catalog.tools) ajv.compile(tool.inputSchema);
|
||||
for (const method of selected.methods) {
|
||||
ajv.compile(method.input_schema);
|
||||
assert.equal(method.name, prefix + method.method);
|
||||
if (direct)
|
||||
assert.ok(catalog.tools.some((tool) => tool.name === method.name));
|
||||
}
|
||||
for (const [name, good, bad] of [
|
||||
["snapshot", { entry: "0xea6d0" }, { snapshot: "snap_other" }],
|
||||
["values", { snapshot: "snap_other", ids: ["v1"] }, { entry: "0xea6d0" }],
|
||||
]) {
|
||||
const method = selected.methods.find((m) => m.method === name);
|
||||
const validators = [ajv.compile(method.input_schema)];
|
||||
if (direct)
|
||||
validators.push(
|
||||
ajv.compile(
|
||||
catalog.tools.find((t) => t.name === method.name).inputSchema,
|
||||
),
|
||||
);
|
||||
for (const validate of validators) {
|
||||
assert.equal(validate(good), true);
|
||||
assert.equal(validate(bad), false);
|
||||
}
|
||||
}
|
||||
const missing = await call(`${prefix}values unknown`);
|
||||
assert.deepEqual(
|
||||
missing.methods.map((m) => m.method),
|
||||
["values"],
|
||||
);
|
||||
assert.deepEqual(missing.unmatched_queries, ["unknown"]);
|
||||
const all = await call("");
|
||||
assert.equal(all.methods.length, 14);
|
||||
for (const method of all.methods) ajv.compile(method.input_schema);
|
||||
assert.ok(JSON.stringify(selected).length < JSON.stringify(all).length);
|
||||
results.push({
|
||||
direct,
|
||||
selected,
|
||||
all_reference_bytes: Buffer.byteLength(JSON.stringify(all)),
|
||||
selected_reference_bytes: Buffer.byteLength(JSON.stringify(selected)),
|
||||
actual_catalog_and_named_schema_checks_passed: true,
|
||||
});
|
||||
} catch (error) {
|
||||
failures.push(error.stack ?? String(error));
|
||||
} finally {
|
||||
await client.close();
|
||||
await transport.close();
|
||||
}
|
||||
}
|
||||
const lifecycle = await completeVerifierRun(run);
|
||||
assertVerifierCleanupComplete(lifecycle.process_lineage);
|
||||
await writeFile(
|
||||
output,
|
||||
JSON.stringify({
|
||||
results,
|
||||
failures,
|
||||
lifecycle,
|
||||
scope:
|
||||
"Static contract/catalog checks; no native target analysis or model cost measurement",
|
||||
}),
|
||||
{ flag: "wx" },
|
||||
);
|
||||
console.log(JSON.stringify({ output, lanes: results.length, failures }));
|
||||
if (failures.length) process.exitCode = 1;
|
||||
Reference in New Issue
Block a user