mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 11:13:21 +00:00
refactor(contracts): key the tool effect audit by public tool name (#10579)
TOOL_EFFECT_AUDIT was typed Record<string, EffectAudit>, so a tool missing from the audit or a misspelled entry was found only by a runtime throw or the key-equality unit test. Check the audit with satisfies Record<PublicToolName, EffectAudit>, where PublicToolName is the union of TOOL_CONTRACTS names. A missing, extra or misspelled entry is now a compile error. String lookups for callers that hold an unchecked name keep their runtime refusal. Delete the unit test that compared audit keys with contract names; the type now owns that check.
This commit is contained in:
1 parent
81c115b4c3
commit
764203cf4a
2 files changed
+20
-15
No files matched your search
@@ -22,12 +22,6 @@ describe("tool effects", () => {
|
||||
mutatesTarget: false,
|
||||
});
|
||||
});
|
||||
it("audits every public tool exactly once", () => {
|
||||
expect(Object.keys(TOOL_EFFECTS).sort()).toEqual(
|
||||
TOOL_CONTRACTS.map(({ name }) => name).sort(),
|
||||
);
|
||||
});
|
||||
|
||||
it("marks foreign execution and device mutation as destructive", () => {
|
||||
const destructive = TOOL_CONTRACTS.filter(
|
||||
({ effects }) => effects.executesForeignCode || effects.mutatesDevice,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { TOOL_CONTRACTS } from "./toolContracts.js";
|
||||
|
||||
/** Possible effects across a tool's supported inputs, including temporary work. */
|
||||
export interface ToolEffects {
|
||||
readonly mutatesTarget: boolean;
|
||||
@@ -75,8 +77,11 @@ const providerUiNetworkEvidence = providerEffects(evidence, {
|
||||
changesUiState: true,
|
||||
});
|
||||
|
||||
/** Every public tool name; the audit below must list each one exactly once. */
|
||||
type PublicToolName = (typeof TOOL_CONTRACTS)[number]["name"];
|
||||
|
||||
/** Explicit effect audit for every public tool. */
|
||||
const TOOL_EFFECT_AUDIT: Readonly<Record<string, EffectAudit>> = {
|
||||
const TOOL_EFFECT_AUDIT = {
|
||||
inspect_analysis_settings: effects(),
|
||||
update_analysis_settings: effects({ mutatesSession: true }),
|
||||
list_cutter_sessions: effects({ accessesNetwork: true }),
|
||||
@@ -911,7 +916,11 @@ const TOOL_EFFECT_AUDIT: Readonly<Record<string, EffectAudit>> = {
|
||||
record_unknown: effects({ mutatesSession: true, idempotent: false }),
|
||||
update_unknown: effects({ mutatesSession: true, idempotent: false }),
|
||||
verify_unknown_resolution: effects(),
|
||||
};
|
||||
} satisfies Readonly<Record<PublicToolName, EffectAudit>>;
|
||||
|
||||
/** Lookup by an unchecked name; a missing entry is reported by the caller. */
|
||||
const auditedEffects: Readonly<Record<string, EffectAudit | undefined>> =
|
||||
TOOL_EFFECT_AUDIT;
|
||||
|
||||
const sessionEffects = (audit: EffectAudit): ToolEffects =>
|
||||
"session" in audit ? audit.session : audit;
|
||||
@@ -919,17 +928,19 @@ const sessionEffects = (audit: EffectAudit): ToolEffects =>
|
||||
/** Possible effects across every supported provider profile. */
|
||||
export const TOOL_EFFECTS: Readonly<Record<string, ToolEffects>> =
|
||||
Object.fromEntries(
|
||||
Object.entries(TOOL_EFFECT_AUDIT).map(([name, audit]) => [
|
||||
name,
|
||||
"session" in audit
|
||||
? effects({ ...audit.session, ...audit.global })
|
||||
: audit,
|
||||
]),
|
||||
Object.entries(TOOL_EFFECT_AUDIT).map(
|
||||
([name, audit]: [string, EffectAudit]) => [
|
||||
name,
|
||||
"session" in audit
|
||||
? effects({ ...audit.session, ...audit.global })
|
||||
: audit,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
/** Effects owned by the tool before composing the selected provider. */
|
||||
export const toolSessionEffects = (name: string): ToolEffects => {
|
||||
const audited = TOOL_EFFECT_AUDIT[name];
|
||||
const audited = auditedEffects[name];
|
||||
if (audited === undefined)
|
||||
throw new Error(`Missing effect audit for ${name}`);
|
||||
return sessionEffects(audited);
|
||||
|
||||
Reference in new issue
Block a user