Compare commits

...
6 changed files with 37 additions and 6 deletions

No files matched your search

+1 -1
View File
@@ -21,7 +21,7 @@ export const workflowAnalysisProfile = (
: operation === "batch_decompile"
? { workflow_revision: 4 }
: operation === "trace_feature"
? { workflow_revision: 4 }
? { workflow_revision: 5 }
: operation === "get_call_graph"
? { workflow_revision: 3 }
: operation === "trace_native_values"
+7
View File
@@ -204,6 +204,10 @@ export const executeComposedWorkflow = async (
);
};
/** Scope of the exact-address reference lookups composed by trace_feature. */
export const TRACE_FEATURE_REFERENCE_LIMITATION =
"Reference queries cover only each matched start address, not interior bytes of strings and procedures. Empty references and truncated: false do not establish complete reference coverage.";
/** Build the shared Evidence record for a successful composed binary workflow. */
export const createWorkflowEvidence = (input: {
readonly target: BinaryTarget | undefined;
@@ -232,6 +236,9 @@ export const createWorkflowEvidence = (input: {
: { evidenceLinks: input.upstreamEvidenceLinks }),
limitations: [
"Derived by an REA composed workflow.",
...(input.operation === "trace_feature"
? [TRACE_FEATURE_REFERENCE_LIMITATION]
: []),
...input.upstreamLimitations,
],
...(input.operation === "batch_decompile"
@@ -1496,7 +1496,7 @@ it("revises the trace_feature workflow profile so earlier results are not replay
expect(
workflowAnalysisProfile(profile, "trace_feature").parameters
.workflow_revision,
).toBe(4);
).toBe(5);
expect(workflowAnalysisProfile(profile, "trace_feature")).not.toEqual(
workflowAnalysisProfile(profile),
+1 -1
View File
@@ -82,7 +82,7 @@ export const ENHANCED_TOOL_CONTRACTS = [
...FUNCTION_WORKFLOW_TOOL_CONTRACTS,
enhanced(
"trace_feature",
"Trace the complete literal query, preserving spaces, through every matching string and procedure and their xrefs. Preserve an explicit unavailable owner for a non-retryable containment capability refusal. Returns observations and residual unknowns without inferring reference kinds; truncated only reports an observed reference cap. For a procedure match, use procedure_references with direction incoming for provider-reported kinds when supported; it covers the procedure body, so compare target addresses when selecting exact-entry references. Select detail summary to retain complete Evidence and page matches, references and unknowns with inspect_analysis_view kind native-feature-trace.",
"Search provider string and procedure inventories for the complete literal query, preserving spaces, then query xrefs at each matched start address. References to interior bytes of strings and procedures are not searched. An empty result or truncated: false does not prove there are no references. Preserve an explicit unavailable owner for a non-retryable containment capability refusal; reference kinds remain unknown. For a procedure match, use procedure_references with direction incoming for provider-reported kinds when supported; it covers the procedure body, so compare target addresses when selecting exact-entry references. Select detail summary to retain complete Evidence and page matches, references and unknowns with inspect_analysis_view kind native-feature-trace.",
enhancedInputSchemas.trace_feature.extend({ detail: analysisDetailSchema }),
),
enhanced(
+10 -2
View File
@@ -14,8 +14,16 @@ export const literalFeatureTraceSchema = z.object({
query: z.string(),
search_mode: z.literal("literal"),
matches: z.array(literalFeatureMatchSchema),
references: z.array(literalFeatureReferenceSchema),
truncated: z.boolean(),
references: z
.array(literalFeatureReferenceSchema)
.describe(
"References to matched start addresses only; references to interior bytes of strings and procedures are not searched. Empty does not establish absence.",
),
truncated: z
.boolean()
.describe(
"True only when a provider reports a reference cap; false does not establish complete reference coverage.",
),
residual_unknowns: z.array(z.string()),
});
@@ -3,6 +3,7 @@ import { afterEach, describe, expect, it } from "vitest";
import type { AnalysisOperationPort } from "../../../src/application/AnalysisProvider.js";
import { createAnalysisExecution } from "../../../src/application/AnalysisProvider.js";
import { EnhancedTools } from "../../../src/application/EnhancedTools.js";
import { TRACE_FEATURE_REFERENCE_LIMITATION } from "../../../src/application/WorkflowEvidence.js";
import {
AnalysisCapabilityUnavailableError,
AnalysisOutputError,
@@ -137,7 +138,10 @@ const trace = async (analysis: AnalysisOperationPort) => {
arguments: { query: "hello" },
});
expect(result.isError).not.toBe(true);
return result.structuredContent as { normalized_result: unknown };
return result.structuredContent as {
normalized_result: unknown;
limitations: string[];
};
};
it("preserves references and explicitly unknown owners without claiming truncation", async () => {
@@ -330,6 +334,18 @@ it("retains incomplete cleanup when cancellation arrives during owner lookup", a
});
describe("trace_feature reference coverage", () => {
it("reports the address-only scope when a match has no references", async () => {
const output = await trace(provider({ emptyXrefsOf: "0x10" }));
expect(output.normalized_result).toEqual(expectedTrace([], [], ["0x10"]));
expect(output.limitations).toContain(TRACE_FEATURE_REFERENCE_LIMITATION);
expect(TRACE_FEATURE_REFERENCE_LIMITATION).toMatch(/start address/);
expect(TRACE_FEATURE_REFERENCE_LIMITATION).toMatch(
/interior bytes of strings and procedures/,
);
expect(TRACE_FEATURE_REFERENCE_LIMITATION).toMatch(/truncated: false/);
});
it("marks the trace truncated for the target whose references the provider capped", async () => {
const output = await trace(
provider({ coverage: { "0x10": true, "0x11": false } }),