rea-agents 2.2.0 → 2.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/README.md +57 -10
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +162 -0
- package/dist/application/ArtifactExtraction.js +5 -1
- package/dist/application/ArtifactGraphConstruction.js +3 -15
- package/dist/application/ArtifactInventory/classify.js +5 -1
- package/dist/application/ArtifactInventory/reader.js +2 -0
- package/dist/application/BinarySession.js +9 -2
- package/dist/application/BinarySessionStatus.js +40 -1
- package/dist/application/CapabilityInventory.js +26 -3
- package/dist/application/CommandShimReplay.js +6 -3
- package/dist/application/CompositeProvider.js +5 -2
- package/dist/application/DirectAnalysis.js +1 -20
- package/dist/application/DirectAnalysisStatus.js +83 -0
- package/dist/application/Doctor.js +34 -25
- package/dist/application/DoctorDiagnostics.js +1 -4
- package/dist/application/DoctorProjection.js +43 -0
- package/dist/application/DoctorScope.js +102 -0
- package/dist/application/EvidenceReferenceResolver.js +26 -0
- package/dist/application/JavaScriptApplicationEvidence.js +1 -1
- package/dist/application/JavaScriptApplicationEvidenceGraph.js +10 -1
- package/dist/application/JavaScriptApplicationService.js +3 -2
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +26 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +49 -1
- package/dist/application/JavaScriptArtifactReconstruction.js +7 -0
- package/dist/application/JavaScriptModuleRelationships.js +25 -1
- package/dist/application/JavaScriptReturnShapeProjection.js +111 -0
- package/dist/application/JavaScriptSemanticGraphBuilder.js +294 -0
- package/dist/application/JavaScriptSemanticGraphConstruction.js +144 -0
- package/dist/application/JavaScriptSemanticGraphEvidence.js +86 -0
- package/dist/application/JavaScriptSemanticGraphFlowProjection.js +72 -0
- package/dist/application/JavaScriptSemanticGraphProjection.js +33 -0
- package/dist/application/JavaScriptSemanticTraceService.js +45 -0
- package/dist/application/LoopbackReplay.js +277 -133
- package/dist/application/LoopbackReplayRecorder.js +101 -0
- package/dist/application/PermissionAuthority.js +7 -3
- package/dist/application/ProcessCaptureEnvironment.js +26 -0
- package/dist/application/ProcessCaptureJournal.js +95 -0
- package/dist/application/ProcessCaptureLifecycle.js +25 -13
- package/dist/application/ProcessCheckpoints.js +35 -4
- package/dist/application/ProcessCli.js +26 -4
- package/dist/application/ProcessHarness.js +64 -83
- package/dist/application/ProcessNormalization.js +9 -2
- package/dist/application/ProcessReactiveCoordinator.js +225 -0
- package/dist/application/ProcessReactiveEffects.js +141 -0
- package/dist/application/ProcessReactiveHarness.js +85 -0
- package/dist/application/ProcessReactiveObservations.js +105 -0
- package/dist/application/ProcessSampling.js +2 -0
- package/dist/application/SessionProviderRouter.js +7 -6
- package/dist/application/Setup.js +11 -10
- package/dist/application/SetupPlan.js +1 -1
- package/dist/application/SetupRegistrationEnvironment.js +12 -0
- package/dist/application/TerminalRenderer.js +3 -1
- package/dist/browser/CdpEndpoint.js +37 -4
- package/dist/catalogIdentity.js +16 -7
- package/dist/cli/coreAnalysisCommands.js +34 -2
- package/dist/cli/javascriptApplicationAnalysis.js +19 -0
- package/dist/cli/setupCommands.js +81 -32
- package/dist/cli/utilityCommands.js +10 -2
- package/dist/cliApplicationCommands.js +79 -16
- package/dist/cliCommandNames.js +3 -0
- package/dist/cliElectronCommands.js +23 -33
- package/dist/cliObservationOptions.js +1 -1
- package/dist/cliOutput.js +56 -0
- package/dist/cliProcessCommands.js +32 -1
- package/dist/cliSetup.js +30 -21
- package/dist/contracts/applicationToolContracts.js +54 -7
- package/dist/contracts/applicationWorkflowInputContracts.js +119 -0
- package/dist/contracts/electronToolContracts.js +53 -32
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +32 -9
- package/dist/contracts/processCaptureExample.js +1 -0
- package/dist/contracts/replayMachineExample.js +37 -0
- package/dist/contracts/toolContractExamples.js +2 -0
- package/dist/contracts/toolContracts.js +5 -1
- package/dist/contracts/toolEffects.js +3 -0
- package/dist/contracts/toolOutputSchemaGroups.js +2 -0
- package/dist/contracts/toolOutputSchemaPrimitives.js +43 -0
- package/dist/doctorRuntime.js +41 -0
- package/dist/domain/analysisErrorPresentation.js +5 -3
- package/dist/domain/analysisErrorProjection.js +7 -1
- package/dist/domain/analysisSnapshot.js +2 -0
- package/dist/domain/artifactGraph.js +2 -0
- package/dist/domain/binaryTarget.js +3 -6
- package/dist/domain/changedBehavior.js +5 -4
- package/dist/domain/completionLedgerGeneration.js +190 -0
- package/dist/domain/errors.js +4 -2
- package/dist/domain/evidence.js +2 -0
- package/dist/domain/evidenceCompletionLedger.js +113 -0
- package/dist/domain/hopperStartupFailure.js +9 -0
- package/dist/domain/javascriptApplicationAnalysis.js +44 -2
- package/dist/domain/javascriptExportShapeComparison.js +145 -0
- package/dist/domain/javascriptExportShapeComparisonIdentity.js +11 -0
- package/dist/domain/javascriptExportShapeComparisonSchemas.js +277 -0
- package/dist/domain/javascriptExportShapeSelection.js +132 -0
- package/dist/domain/javascriptExportShapeVariants.js +272 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +2 -2
- package/dist/domain/javascriptSemanticAnalysis.js +17 -36
- package/dist/domain/javascriptSemanticCallResolution.js +129 -0
- package/dist/domain/javascriptSemanticCalls.js +269 -0
- package/dist/domain/javascriptSemanticGraph.js +356 -0
- package/dist/domain/javascriptSemanticGraphSchemas.js +329 -0
- package/dist/domain/javascriptSemanticGraphSerialization.js +23 -0
- package/dist/domain/javascriptSemanticIr.js +14 -1
- package/dist/domain/javascriptSemanticPrimitives.js +36 -0
- package/dist/domain/javascriptSemanticProjection.js +36 -4
- package/dist/domain/javascriptSemanticProvenance.js +29 -0
- package/dist/domain/javascriptSemanticQuery.js +294 -0
- package/dist/domain/javascriptSemanticQueryAssessment.js +74 -0
- package/dist/domain/javascriptSemanticQueryIdentity.js +86 -0
- package/dist/domain/javascriptSemanticQueryRelations.js +15 -0
- package/dist/domain/javascriptSemanticQuerySchemas.js +188 -0
- package/dist/domain/javascriptSemanticReturns.js +187 -0
- package/dist/domain/javascriptSemanticState.js +32 -0
- package/dist/domain/javascriptSemanticTraceSchemas.js +14 -0
- package/dist/domain/javascriptSemanticValues.js +54 -70
- package/dist/domain/processCapture.js +100 -0
- package/dist/domain/processCaptureReactiveValidation.js +175 -0
- package/dist/domain/processCaptureValidation.js +103 -12
- package/dist/domain/processComparison.js +78 -25
- package/dist/domain/processObservation.js +162 -0
- package/dist/domain/processReactiveCheckpointDataflow.js +32 -0
- package/dist/domain/processReactiveMatching.js +210 -0
- package/dist/domain/processReactiveRuntime.js +120 -0
- package/dist/domain/processReactiveScenario.js +361 -0
- package/dist/domain/processReactiveScenarioPreflight.js +120 -0
- package/dist/domain/processReactiveTransition.js +161 -0
- package/dist/domain/processScenario.js +33 -0
- package/dist/domain/processTraceComparison.js +43 -0
- package/dist/domain/processTraceDimensionProjection.js +50 -0
- package/dist/domain/processTraceEvaluation.js +312 -0
- package/dist/domain/processTraceSpecification.js +373 -0
- package/dist/domain/reconstructionVerification.js +5 -4
- package/dist/domain/replayMachine.js +18 -2
- package/dist/domain/replayMachineRun.js +216 -0
- package/dist/domain/replayMachineRuntime.js +88 -9
- package/dist/domain/staticRuntimeCorrelation.js +4 -2
- package/dist/domain/zipPackageFormat.js +20 -0
- package/dist/evaluation/CodexAgentEval.js +24 -0
- package/dist/generatedPackageMetadata.js +1 -1
- package/dist/ghidra/GhidraClient.js +13 -2
- package/dist/ghidra/GhidraDiagnostics.js +1 -0
- package/dist/ghidra/GhidraLauncher.js +7 -3
- package/dist/ghidra/GhidraProvider.js +8 -1
- package/dist/hopper/BridgeLauncher.js +56 -14
- package/dist/hopper/HopperClient.js +23 -10
- package/dist/hopper/HopperDiagnostics.js +14 -0
- package/dist/hopper/HopperProcessDiagnostic.js +14 -0
- package/dist/hopper/HopperProvider.js +8 -1
- package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +72 -0
- package/dist/hopper/LinuxPrivateDisplayProbe.js +217 -0
- package/dist/main/transport.js +2 -0
- package/dist/mcpDoctor.js +310 -0
- package/dist/native/NativeMacOSProvider.js +1 -0
- package/dist/process/ProcessOwnership.js +200 -53
- package/dist/process/ProviderRunLineage.js +21 -0
- package/dist/server/createServer.js +4 -1
- package/dist/server/javascriptApplicationResult.js +22 -1
- package/dist/server/registerApplicationTools/characterization.js +3 -3
- package/dist/server/registerApplicationTools/compareExportShapes.js +37 -0
- package/dist/server/registerApplicationTools/compareVersions.js +12 -6
- package/dist/server/registerApplicationTools/controlledReplay.js +2 -2
- package/dist/server/registerApplicationTools/coverage.js +3 -3
- package/dist/server/registerApplicationTools/helpers.js +37 -25
- package/dist/server/registerApplicationTools/traceFeature.js +9 -5
- package/dist/server/registerApplicationTools/traceSemantics.js +30 -0
- package/dist/server/registerApplicationTools.js +4 -0
- package/dist/server/registerEvidenceResources.js +5 -1
- package/dist/server/registerProcessComparisonTool.js +17 -7
- package/dist/server/registerReplayMachineTool.js +20 -0
- package/dist/server/registerSessionTools.js +12 -7
- package/dist/server/sessionAvailabilityPolicy.js +2 -1
- package/dist/serverIdentity.js +19 -0
- package/package.json +62 -30
- package/scripts/hopper-demo-x11.py +459 -61
- package/scripts/prepack.mjs +55 -0
- package/scripts/prepare.mjs +28 -0
- package/scripts/rea.mjs +26 -5
- package/skills/reverse-engineer-anything/SKILL.md +4 -3
- package/skills/reverse-engineer-anything/references/javascript-applications.md +10 -0
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +2 -2
|
@@ -44,7 +44,7 @@ export const parseProcessEnvironment = (value) => Object.fromEntries(value
|
|
|
44
44
|
return [entry.slice(0, separator), entry.slice(separator + 1)];
|
|
45
45
|
}));
|
|
46
46
|
const systemHost = {
|
|
47
|
-
async
|
|
47
|
+
async listProcesses() {
|
|
48
48
|
if (process.platform === "win32")
|
|
49
49
|
return [];
|
|
50
50
|
const { stdout } = await execFileAsync("ps", [
|
|
@@ -61,8 +61,7 @@ const systemHost = {
|
|
|
61
61
|
processGroupId: Number(match[3]),
|
|
62
62
|
state: match[4] ?? "",
|
|
63
63
|
command: match[5] ?? "",
|
|
64
|
-
}))
|
|
65
|
-
.filter((member) => member.processGroupId === processGroupId);
|
|
64
|
+
}));
|
|
66
65
|
},
|
|
67
66
|
async environment(pid) {
|
|
68
67
|
if (process.platform === "linux") {
|
|
@@ -95,73 +94,140 @@ const systemWindowsProcessTreeHost = {
|
|
|
95
94
|
};
|
|
96
95
|
/** Read the capture run token currently exposed by one live process. */
|
|
97
96
|
export const readProcessRunId = async (pid, host = systemHost) => (await host.environment(pid)).REA_PROCESS_RUN_ID;
|
|
98
|
-
/**
|
|
99
|
-
* Verify run-token ownership before signaling a POSIX process group.
|
|
100
|
-
*
|
|
101
|
-
* Process IDs and group IDs can be reused. REA therefore re-reads every live
|
|
102
|
-
* member's environment immediately before signaling and fails closed if any
|
|
103
|
-
* member cannot be inspected or lacks the per-capture run token.
|
|
104
|
-
*/
|
|
97
|
+
/** Token-validate every rooted POSIX process group before signaling any. */
|
|
105
98
|
export const cleanupOwnedProcessGroup = async (ownership, host = systemHost) => {
|
|
106
|
-
let
|
|
99
|
+
let processes;
|
|
107
100
|
try {
|
|
108
|
-
|
|
101
|
+
processes = liveProcesses(await host.listProcesses());
|
|
109
102
|
}
|
|
110
103
|
catch {
|
|
111
|
-
return { cleaned: false, reason: "process
|
|
104
|
+
return { cleaned: false, reason: "process table could not be inspected" };
|
|
112
105
|
}
|
|
113
|
-
const
|
|
114
|
-
if (
|
|
106
|
+
const plan = await createOwnedCleanupPlan(ownership, processes, host);
|
|
107
|
+
if ("cleaned" in plan)
|
|
108
|
+
return plan;
|
|
109
|
+
let signaled = false;
|
|
110
|
+
for (const processGroupId of plan.signalOrder) {
|
|
111
|
+
const revalidation = await revalidateOwnedProcessGroup(processGroupId, ownership, host);
|
|
112
|
+
if ("cleaned" in revalidation)
|
|
113
|
+
return revalidation;
|
|
114
|
+
if (revalidation.empty)
|
|
115
|
+
continue;
|
|
116
|
+
try {
|
|
117
|
+
host.signalGroup(processGroupId, "SIGKILL");
|
|
118
|
+
signaled = true;
|
|
119
|
+
}
|
|
120
|
+
catch (cause) {
|
|
121
|
+
const code = cause instanceof Error && "code" in cause ? cause.code : undefined;
|
|
122
|
+
if (code !== "ESRCH")
|
|
123
|
+
return { cleaned: false, reason: "owned process group signal failed" };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { cleaned: true, signaled };
|
|
127
|
+
};
|
|
128
|
+
const createOwnedCleanupPlan = async (ownership, processes, host) => {
|
|
129
|
+
const launcher = processes.find(({ pid }) => pid === ownership.leaderPid);
|
|
130
|
+
const rootMembers = processes.filter(({ processGroupId }) => processGroupId === ownership.processGroupId);
|
|
131
|
+
if (launcher === undefined && rootMembers.length === 0)
|
|
115
132
|
return { cleaned: true, signaled: false };
|
|
116
|
-
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
133
|
+
let descendants = [];
|
|
134
|
+
if (launcher !== undefined) {
|
|
135
|
+
const identityFailure = launcherIdentityFailure(launcher, ownership);
|
|
136
|
+
if (identityFailure !== null)
|
|
137
|
+
return { cleaned: false, reason: identityFailure };
|
|
138
|
+
descendants = descendantsOf(launcher.pid, processes);
|
|
139
|
+
}
|
|
140
|
+
const descendantPids = new Set(descendants.map(({ pid }) => pid));
|
|
141
|
+
const processGroupIds = new Set([ownership.processGroupId]);
|
|
142
|
+
for (const descendant of descendants)
|
|
143
|
+
processGroupIds.add(descendant.processGroupId);
|
|
144
|
+
for (const processGroupId of processGroupIds) {
|
|
145
|
+
if (processGroupId === ownership.processGroupId)
|
|
146
|
+
continue;
|
|
147
|
+
const groupLeader = processes.find(({ pid, processGroupId: observedGroupId }) => pid === processGroupId && observedGroupId === processGroupId);
|
|
148
|
+
if (groupLeader === undefined || !descendantPids.has(groupLeader.pid))
|
|
126
149
|
return {
|
|
127
150
|
cleaned: false,
|
|
128
|
-
reason: "
|
|
151
|
+
reason: "descendant process-group leader identity could not be verified",
|
|
129
152
|
};
|
|
130
153
|
}
|
|
154
|
+
const liveMembers = processes.filter(({ processGroupId }) => processGroupIds.has(processGroupId));
|
|
155
|
+
const failures = await processOwnershipFailures(liveMembers, ownership.runId, host);
|
|
156
|
+
if (failures.length > 0)
|
|
157
|
+
return cleanupValidationFailure(failures);
|
|
158
|
+
return {
|
|
159
|
+
signalOrder: [ownership.processGroupId].concat([...processGroupIds]
|
|
160
|
+
.filter((processGroupId) => processGroupId !== ownership.processGroupId)
|
|
161
|
+
.sort((left, right) => left - right)),
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
const launcherIdentityFailure = (launcher, ownership) => {
|
|
165
|
+
if (launcher.processGroupId !== ownership.processGroupId)
|
|
166
|
+
return "owned launcher process-group identity did not match";
|
|
167
|
+
if (ownership.expectedParentPid !== undefined &&
|
|
168
|
+
launcher.parentPid !== ownership.expectedParentPid)
|
|
169
|
+
return "owned launcher parent identity did not match";
|
|
170
|
+
if (ownership.expectedCommand !== undefined &&
|
|
171
|
+
!commandMatches(launcher.command, ownership.expectedCommand))
|
|
172
|
+
return "owned launcher command identity did not match";
|
|
173
|
+
return null;
|
|
174
|
+
};
|
|
175
|
+
const revalidateOwnedProcessGroup = async (processGroupId, ownership, host) => {
|
|
176
|
+
let members;
|
|
177
|
+
try {
|
|
178
|
+
members = liveProcesses(await host.listProcesses()).filter(({ processGroupId: observedGroupId }) => observedGroupId === processGroupId);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return {
|
|
182
|
+
cleaned: false,
|
|
183
|
+
reason: "process ownership could not be revalidated",
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const failures = await processOwnershipFailures(members, ownership.runId, host);
|
|
187
|
+
if (failures.length > 0)
|
|
188
|
+
return cleanupValidationFailure(failures);
|
|
189
|
+
if (processGroupId === ownership.processGroupId) {
|
|
190
|
+
const launcher = members.find(({ pid }) => pid === ownership.leaderPid);
|
|
191
|
+
if (launcher !== undefined) {
|
|
192
|
+
const identityFailure = launcherIdentityFailure(launcher, ownership);
|
|
193
|
+
if (identityFailure !== null)
|
|
194
|
+
return { cleaned: false, reason: identityFailure };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (processGroupId !== ownership.processGroupId &&
|
|
198
|
+
members.length > 0 &&
|
|
199
|
+
!members.some(({ pid }) => pid === processGroupId))
|
|
200
|
+
return {
|
|
201
|
+
cleaned: false,
|
|
202
|
+
reason: "descendant process-group leader identity could not be revalidated",
|
|
203
|
+
};
|
|
204
|
+
return { empty: members.length === 0 };
|
|
205
|
+
};
|
|
206
|
+
const cleanupValidationFailure = (failures) => ({
|
|
207
|
+
cleaned: false,
|
|
208
|
+
reason: failures.some(({ reason }) => reason === "run-token-mismatch")
|
|
209
|
+
? "process tree contains an unowned or PID-reused process"
|
|
210
|
+
: "process ownership could not be revalidated",
|
|
211
|
+
failures,
|
|
212
|
+
});
|
|
213
|
+
const processOwnershipFailures = async (members, runId, host) => {
|
|
131
214
|
const failures = [];
|
|
132
|
-
for (const member of
|
|
215
|
+
for (const member of members) {
|
|
133
216
|
try {
|
|
134
|
-
|
|
135
|
-
if (environment.REA_PROCESS_RUN_ID !== ownership.runId)
|
|
217
|
+
if ((await host.environment(member.pid)).REA_PROCESS_RUN_ID !== runId)
|
|
136
218
|
failures.push({ pid: member.pid, reason: "run-token-mismatch" });
|
|
137
219
|
}
|
|
138
220
|
catch {
|
|
139
221
|
failures.push({ pid: member.pid, reason: "environment-unreadable" });
|
|
140
222
|
}
|
|
141
223
|
}
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
cleaned: false,
|
|
145
|
-
reason: failures.some(({ reason }) => reason === "run-token-mismatch")
|
|
146
|
-
? "process group contains an unowned or PID-reused process"
|
|
147
|
-
: "process ownership could not be revalidated",
|
|
148
|
-
failures,
|
|
149
|
-
};
|
|
150
|
-
try {
|
|
151
|
-
host.signalGroup(ownership.processGroupId, "SIGKILL");
|
|
152
|
-
}
|
|
153
|
-
catch (cause) {
|
|
154
|
-
const code = cause instanceof Error && "code" in cause ? cause.code : undefined;
|
|
155
|
-
if (code !== "ESRCH")
|
|
156
|
-
return { cleaned: false, reason: "owned process group signal failed" };
|
|
157
|
-
}
|
|
158
|
-
return { cleaned: true, signaled: true };
|
|
224
|
+
return failures;
|
|
159
225
|
};
|
|
160
226
|
/** Observe one group without signaling it, failing closed on identity doubt. */
|
|
161
227
|
export const observeOwnedProcessGroup = async (ownership, host = systemHost) => {
|
|
162
228
|
let members;
|
|
163
229
|
try {
|
|
164
|
-
members = await host.
|
|
230
|
+
members = (await host.listProcesses()).filter(({ processGroupId }) => processGroupId === ownership.processGroupId);
|
|
165
231
|
}
|
|
166
232
|
catch {
|
|
167
233
|
return {
|
|
@@ -169,7 +235,7 @@ export const observeOwnedProcessGroup = async (ownership, host = systemHost) =>
|
|
|
169
235
|
reason: "process group could not be inspected",
|
|
170
236
|
};
|
|
171
237
|
}
|
|
172
|
-
const liveMembers =
|
|
238
|
+
const liveMembers = liveProcesses(members);
|
|
173
239
|
if (liveMembers.length === 0)
|
|
174
240
|
return { state: "empty" };
|
|
175
241
|
for (const member of liveMembers) {
|
|
@@ -190,10 +256,91 @@ export const observeOwnedProcessGroup = async (ownership, host = systemHost) =>
|
|
|
190
256
|
}
|
|
191
257
|
return { state: "alive" };
|
|
192
258
|
};
|
|
193
|
-
|
|
259
|
+
/**
|
|
260
|
+
* Record the live launcher and descendant lineage after run-token validation.
|
|
261
|
+
*
|
|
262
|
+
* The observation is intentionally point-in-time. A verified empty descendant
|
|
263
|
+
* list means no descendants were live during this observation, not that the
|
|
264
|
+
* run never created a short-lived child.
|
|
265
|
+
*/
|
|
266
|
+
export const observeOwnedProcessLineage = async (ownership, host = systemHost) => {
|
|
267
|
+
let processes;
|
|
268
|
+
try {
|
|
269
|
+
processes = liveProcesses(await host.listProcesses());
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
return unavailableLineage(ownership, "process table could not be inspected");
|
|
273
|
+
}
|
|
274
|
+
const launcher = processes.find(({ pid }) => pid === ownership.leaderPid);
|
|
275
|
+
if (launcher === undefined)
|
|
276
|
+
return unavailableLineage(ownership, "owned launcher is not live");
|
|
277
|
+
if (launcher.processGroupId !== ownership.processGroupId)
|
|
278
|
+
return unavailableLineage(ownership, "owned launcher process-group identity did not match");
|
|
279
|
+
if (ownership.expectedParentPid !== undefined &&
|
|
280
|
+
launcher.parentPid !== ownership.expectedParentPid)
|
|
281
|
+
return unavailableLineage(ownership, "owned launcher parent identity did not match");
|
|
282
|
+
if (ownership.expectedCommand !== undefined &&
|
|
283
|
+
!commandMatches(launcher.command, ownership.expectedCommand))
|
|
284
|
+
return unavailableLineage(ownership, "owned launcher command identity did not match");
|
|
285
|
+
const descendants = descendantsOf(launcher.pid, processes);
|
|
286
|
+
for (const member of [launcher, ...descendants]) {
|
|
287
|
+
try {
|
|
288
|
+
if ((await host.environment(member.pid)).REA_PROCESS_RUN_ID !==
|
|
289
|
+
ownership.runId)
|
|
290
|
+
return unavailableLineage(ownership, "process lineage contains an unowned or PID-reused process");
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return unavailableLineage(ownership, "process ownership could not be revalidated");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
status: "verified",
|
|
298
|
+
observedAt: new Date().toISOString(),
|
|
299
|
+
lineage: {
|
|
300
|
+
schemaVersion: 1,
|
|
301
|
+
runId: ownership.runId,
|
|
302
|
+
launcherPid: launcher.pid,
|
|
303
|
+
launcherParentPid: launcher.parentPid,
|
|
304
|
+
processGroupId: launcher.processGroupId,
|
|
305
|
+
descendants: descendants
|
|
306
|
+
.sort((left, right) => left.pid - right.pid)
|
|
307
|
+
.map(({ pid, parentPid, processGroupId }) => ({
|
|
308
|
+
pid,
|
|
309
|
+
parentPid,
|
|
310
|
+
processGroupId,
|
|
311
|
+
})),
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
};
|
|
315
|
+
const descendantsOf = (launcherPid, processes) => {
|
|
316
|
+
const childrenByParent = new Map();
|
|
317
|
+
for (const process of processes) {
|
|
318
|
+
const siblings = childrenByParent.get(process.parentPid) ?? [];
|
|
319
|
+
siblings.push(process);
|
|
320
|
+
childrenByParent.set(process.parentPid, siblings);
|
|
321
|
+
}
|
|
322
|
+
const descendants = [];
|
|
323
|
+
const pending = [...(childrenByParent.get(launcherPid) ?? [])];
|
|
324
|
+
const visited = new Set([launcherPid]);
|
|
325
|
+
for (const process of pending) {
|
|
326
|
+
if (visited.has(process.pid))
|
|
327
|
+
continue;
|
|
328
|
+
visited.add(process.pid);
|
|
329
|
+
descendants.push(process);
|
|
330
|
+
pending.push(...(childrenByParent.get(process.pid) ?? []));
|
|
331
|
+
}
|
|
332
|
+
return descendants;
|
|
333
|
+
};
|
|
334
|
+
const unavailableLineage = (ownership, reason) => ({
|
|
335
|
+
status: "unavailable",
|
|
336
|
+
observedAt: new Date().toISOString(),
|
|
337
|
+
runId: ownership.runId,
|
|
338
|
+
launcherPid: ownership.leaderPid,
|
|
339
|
+
processGroupId: ownership.processGroupId,
|
|
340
|
+
reason,
|
|
341
|
+
});
|
|
342
|
+
const liveProcesses = (members) => members.filter(({ state }) => !state.startsWith("Z"));
|
|
194
343
|
const commandMatches = (actual, expected) => {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return (normalizedActual === normalizedExpected ||
|
|
198
|
-
normalizedActual.startsWith(`${normalizedExpected} `));
|
|
344
|
+
return (actual.trim() === expected.trim() ||
|
|
345
|
+
actual.trim().startsWith(`${expected.trim()} `));
|
|
199
346
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { observeOwnedProcessLineage } from "./ProcessOwnership.js";
|
|
2
|
+
/** Retain the latest truthful process-lineage observation for one client. */
|
|
3
|
+
export class ProviderRunLineage {
|
|
4
|
+
#observation = null;
|
|
5
|
+
/** Clear stale lineage before a new launch attempt. */
|
|
6
|
+
reset() {
|
|
7
|
+
this.#observation = null;
|
|
8
|
+
}
|
|
9
|
+
/** Observe a token-owned launcher without signaling it. */
|
|
10
|
+
async observe(launch) {
|
|
11
|
+
if (launch?.ownership === undefined)
|
|
12
|
+
return;
|
|
13
|
+
this.#observation = await observeOwnedProcessLineage(launch.ownership);
|
|
14
|
+
}
|
|
15
|
+
/** Return a detached copy suitable for caller-visible status. */
|
|
16
|
+
snapshot() {
|
|
17
|
+
return this.#observation === null
|
|
18
|
+
? null
|
|
19
|
+
: structuredClone(this.#observation);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -15,7 +15,7 @@ import { createServerIdentity } from "../serverIdentity.js";
|
|
|
15
15
|
import { registerBrowserTools } from "./registerBrowserTools.js";
|
|
16
16
|
import { registerElectronTools } from "./registerElectronTools.js";
|
|
17
17
|
import { registerApplicationTools } from "./registerApplicationTools.js";
|
|
18
|
-
const TARGET_FREE_INSTRUCTIONS = "REA analyzes shipped artifacts
|
|
18
|
+
const TARGET_FREE_INSTRUCTIONS = "REA analyzes shipped artifacts. Route: ASAR/JavaScript -> analyze_javascript_application (configured root required); archive/package -> inventory_artifact; managed PE/CLI -> inspect_managed_artifact; browser/Electron runtime -> list_browser_targets/list_electron_targets; native binary/database -> open_binary, then binary_overview. Check binary_session for policy. Start with summaries and cite Evidence IDs. Never repeat identical analysis or read full Evidence without a specific need.";
|
|
19
19
|
const ACTIVE_TARGET_INSTRUCTIONS = "REA analyzes the active reverse-engineering target. Start native analysis with binary_overview, then narrow with analyze_function, literal search, callers, callees, and xrefs. Prefer summary views, never repeat an identical call, and read full Evidence only when the task requires it.";
|
|
20
20
|
import { LinuxJavaScriptReplayRunner } from "../replay/LinuxJavaScriptReplayRunner.js";
|
|
21
21
|
import { SystemJavaScriptReplayHost } from "../replay/SystemJavaScriptReplayHost.js";
|
|
@@ -205,6 +205,9 @@ const registerObservationTools = ({ server, session, options, logger, permission
|
|
|
205
205
|
logger,
|
|
206
206
|
recordEvidence,
|
|
207
207
|
recordEvidenceWithUnknown,
|
|
208
|
+
evidenceLookup: session === undefined
|
|
209
|
+
? undefined
|
|
210
|
+
: (evidenceId) => session.evidenceById(evidenceId),
|
|
208
211
|
evidenceFilePolicy: options.evidenceFilePolicy ?? {
|
|
209
212
|
roots: [],
|
|
210
213
|
maxBytes: 1,
|
|
@@ -46,7 +46,7 @@ export const summarizeJavaScriptApplicationEvidence = (evidence) => {
|
|
|
46
46
|
location: observation.evidence.location,
|
|
47
47
|
};
|
|
48
48
|
});
|
|
49
|
-
const
|
|
49
|
+
const summary = result.schema_version === 1 ? omitGraph(result) : omitGraphs(result);
|
|
50
50
|
const unknowns = new Set([
|
|
51
51
|
...result.limitations,
|
|
52
52
|
...result.graph.limitations,
|
|
@@ -59,6 +59,19 @@ export const summarizeJavaScriptApplicationEvidence = (evidence) => {
|
|
|
59
59
|
unknowns.add(`Application graph coverage is ${result.graph.coverage.status}; omitted facts remain unknown.`);
|
|
60
60
|
return javascriptApplicationAnalysisSummarySchema.parse({
|
|
61
61
|
...summary,
|
|
62
|
+
...(result.schema_version === 1
|
|
63
|
+
? {}
|
|
64
|
+
: {
|
|
65
|
+
semantic_graph: {
|
|
66
|
+
graph_id: result.semantic_graph.graph_id,
|
|
67
|
+
nodes: result.semantic_graph.nodes.length,
|
|
68
|
+
relations: result.semantic_graph.relations.length,
|
|
69
|
+
unknown_frontiers: result.semantic_graph.unknowns.length,
|
|
70
|
+
fingerprints: result.semantic_graph.fingerprints.length,
|
|
71
|
+
coverage: result.semantic_graph.coverage.status,
|
|
72
|
+
query_tool: "trace_javascript_semantics",
|
|
73
|
+
},
|
|
74
|
+
}),
|
|
62
75
|
unknowns: [...unknowns],
|
|
63
76
|
graph: {
|
|
64
77
|
graph_id: result.graph.graph_id,
|
|
@@ -83,3 +96,11 @@ export const summarizeJavaScriptApplicationEvidence = (evidence) => {
|
|
|
83
96
|
},
|
|
84
97
|
});
|
|
85
98
|
};
|
|
99
|
+
const omitGraph = (result) => {
|
|
100
|
+
const { graph: _graph, ...summary } = result;
|
|
101
|
+
return summary;
|
|
102
|
+
};
|
|
103
|
+
const omitGraphs = (result) => {
|
|
104
|
+
const { graph: _graph, semantic_graph: _semanticGraph, ...summary } = result;
|
|
105
|
+
return summary;
|
|
106
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { executeNodeCharacterization, prepareNodeCharacterization, } from "../../application/NodeRuntimeCharacterizationService.js";
|
|
2
|
-
import {
|
|
2
|
+
import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
|
|
3
3
|
import { nodeCharacterizationExecutionInputSchema, nodeCharacterizationExecutionOutputSchema, nodeCharacterizationPreparationInputSchema, nodeCharacterizationPreparationOutputSchema, } from "../../domain/nodeRuntimeCharacterization.js";
|
|
4
4
|
import { parseEvidence } from "../../domain/evidence.js";
|
|
5
5
|
import { logToolExecution } from "../toolLogging.js";
|
|
@@ -8,8 +8,8 @@ import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
|
|
|
8
8
|
import { safeParseToolInput } from "../toolInputValidation.js";
|
|
9
9
|
import { mcpProgressReporter } from "../mcpProgress.js";
|
|
10
10
|
import { recordSources } from "./helpers.js";
|
|
11
|
-
const prepareContract =
|
|
12
|
-
const executeContract =
|
|
11
|
+
const prepareContract = applicationToolContract("prepare_node_characterization");
|
|
12
|
+
const executeContract = applicationToolContract("execute_node_characterization");
|
|
13
13
|
/** Register node runtime characterization prepare and execute tools. */
|
|
14
14
|
export const registerCharacterizationTools = (server, options) => {
|
|
15
15
|
server.registerTool(prepareContract.name, toolRegistrationOptions(prepareContract), async (input, context) => {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { resolveCompareJavaScriptExportShapesRequestValidated } from "../../application/ApplicationWorkflowEvidenceResolver.js";
|
|
2
|
+
import { compareJavaScriptExportShapesEvidenceValidated } from "../../application/JavaScriptApplicationWorkflowService.js";
|
|
3
|
+
import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
|
|
4
|
+
import { compareJavaScriptExportShapesRequestSchema } from "../../contracts/applicationWorkflowInputContracts.js";
|
|
5
|
+
import { javaScriptExportShapeComparisonResultSchema } from "../../domain/javascriptExportShapeComparisonSchemas.js";
|
|
6
|
+
import { safeParseToolInput } from "../toolInputValidation.js";
|
|
7
|
+
import { logToolExecution } from "../toolLogging.js";
|
|
8
|
+
import { toCallToolResult } from "../toolResult.js";
|
|
9
|
+
import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
|
|
10
|
+
import { recordResult, recordSources } from "./helpers.js";
|
|
11
|
+
const contract = applicationToolContract("compare_javascript_export_shapes");
|
|
12
|
+
/** Register the execution-free exact JavaScript export-shape comparison. */
|
|
13
|
+
export const registerCompareJavaScriptExportShapesTool = (server, options) => {
|
|
14
|
+
server.registerTool(contract.name, toolRegistrationOptions(contract), async (input) => {
|
|
15
|
+
const parsedInput = safeParseToolInput(compareJavaScriptExportShapesRequestSchema, input, contract.name);
|
|
16
|
+
if (!parsedInput.ok)
|
|
17
|
+
return toCallToolResult(parsedInput, contract);
|
|
18
|
+
const resolved = resolveCompareJavaScriptExportShapesRequestValidated(parsedInput.value, options.evidenceLookup);
|
|
19
|
+
if (!resolved.ok)
|
|
20
|
+
return toCallToolResult(resolved, contract);
|
|
21
|
+
const parsed = resolved.value;
|
|
22
|
+
const result = await logToolExecution(options.logger, contract.name, () => Promise.resolve(compareJavaScriptExportShapesEvidenceValidated(parsed)));
|
|
23
|
+
if (!result.ok)
|
|
24
|
+
return toCallToolResult(result, contract);
|
|
25
|
+
const recorded = recordSources(options.recordEvidence, [
|
|
26
|
+
parsed.left,
|
|
27
|
+
parsed.right,
|
|
28
|
+
]);
|
|
29
|
+
if (!recorded.ok)
|
|
30
|
+
return toCallToolResult(recorded, contract);
|
|
31
|
+
const comparison = javaScriptExportShapeComparisonResultSchema.parse(result.value.normalized_result);
|
|
32
|
+
const unknown = comparison.summary.unknown > 0;
|
|
33
|
+
return recordResult(options, contract, result.value, parsed.unknown_registry_approved === true && unknown
|
|
34
|
+
? "javascript-export-shape"
|
|
35
|
+
: undefined);
|
|
36
|
+
});
|
|
37
|
+
};
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import { compareApplicationVersionsEvidenceValidated } from "../../application/JavaScriptApplicationWorkflowService.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { resolveCompareApplicationVersionsRequestValidated } from "../../application/ApplicationWorkflowEvidenceResolver.js";
|
|
3
|
+
import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
|
|
4
|
+
import { compareApplicationVersionsRequestSchema } from "../../contracts/applicationWorkflowInputContracts.js";
|
|
4
5
|
import { applicationVersionComparisonResultSchema } from "../../domain/javascriptApplicationVersionComparisonSchemas.js";
|
|
5
6
|
import { logToolExecution } from "../toolLogging.js";
|
|
6
7
|
import { toCallToolResult } from "../toolResult.js";
|
|
7
8
|
import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
|
|
8
9
|
import { safeParseToolInput } from "../toolInputValidation.js";
|
|
9
10
|
import { recordResult, recordSources } from "./helpers.js";
|
|
10
|
-
const compareContract =
|
|
11
|
+
const compareContract = applicationToolContract("compare_application_versions");
|
|
11
12
|
/** Register the provider-neutral JavaScript version comparison tool. */
|
|
12
13
|
export const registerCompareApplicationVersionsTool = (server, options) => {
|
|
13
14
|
server.registerTool(compareContract.name, toolRegistrationOptions(compareContract), async (input) => {
|
|
14
|
-
const parsedInput = safeParseToolInput(
|
|
15
|
+
const parsedInput = safeParseToolInput(compareApplicationVersionsRequestSchema, input, compareContract.name);
|
|
15
16
|
if (!parsedInput.ok)
|
|
16
17
|
return toCallToolResult(parsedInput, compareContract);
|
|
17
|
-
const
|
|
18
|
+
const resolved = resolveCompareApplicationVersionsRequestValidated(parsedInput.value, options.evidenceLookup);
|
|
19
|
+
if (!resolved.ok)
|
|
20
|
+
return toCallToolResult(resolved, compareContract);
|
|
21
|
+
const parsed = resolved.value;
|
|
18
22
|
const result = await logToolExecution(options.logger, compareContract.name, () => Promise.resolve(compareApplicationVersionsEvidenceValidated(parsed)));
|
|
19
23
|
if (!result.ok)
|
|
20
24
|
return toCallToolResult(result, compareContract);
|
|
@@ -29,6 +33,8 @@ export const registerCompareApplicationVersionsTool = (server, options) => {
|
|
|
29
33
|
return toCallToolResult(recorded, compareContract);
|
|
30
34
|
const comparison = applicationVersionComparisonResultSchema.parse(result.value.normalized_result);
|
|
31
35
|
const unknown = comparison.summary.unknown > 0;
|
|
32
|
-
return recordResult(options, compareContract, result.value, parsed.unknown_registry_approved === true && unknown
|
|
36
|
+
return recordResult(options, compareContract, result.value, parsed.unknown_registry_approved === true && unknown
|
|
37
|
+
? "application-version-comparison"
|
|
38
|
+
: undefined);
|
|
33
39
|
});
|
|
34
40
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { runControlledReplayValidated } from "../../application/JavaScriptReplayService.js";
|
|
2
|
-
import {
|
|
2
|
+
import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
|
|
3
3
|
import { controlledReplayInputSchema, controlledReplayOutputSchema, } from "../../domain/javascriptReplay.js";
|
|
4
4
|
import { parseEvidence } from "../../domain/evidence.js";
|
|
5
5
|
import { logToolExecution } from "../toolLogging.js";
|
|
@@ -8,7 +8,7 @@ import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
|
|
|
8
8
|
import { safeParseToolInput } from "../toolInputValidation.js";
|
|
9
9
|
import { mcpProgressReporter } from "../mcpProgress.js";
|
|
10
10
|
import { recordSources } from "./helpers.js";
|
|
11
|
-
const replayContract =
|
|
11
|
+
const replayContract = applicationToolContract("run_controlled_replay");
|
|
12
12
|
/** Register the controlled JavaScript replay tool. */
|
|
13
13
|
export const registerControlledReplayTool = (server, options) => {
|
|
14
14
|
server.registerTool(replayContract.name, toolRegistrationOptions(replayContract), async (input, context) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { commitReconstructionCoverage, queryReconstructionCoverage, } from "../../application/ReconstructionCoverageService.js";
|
|
2
2
|
import { readReconstructionCoverageWorkspace } from "../../application/ReconstructionCoverageWorkspaceStore.js";
|
|
3
3
|
import { authorizeFileReadWithDeferredWrite, authorizeRootPermission, } from "../../application/DeferredFileAuthorization.js";
|
|
4
|
-
import {
|
|
4
|
+
import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
|
|
5
5
|
import { reconstructionCoverageCommitInputSchema, reconstructionCoverageQueryInputSchema, } from "../../application/ReconstructionCoverageService.js";
|
|
6
6
|
import { reconstructionClosureResultSchema } from "../../domain/reconstructionCoverage.js";
|
|
7
7
|
import { AnalysisCapabilityUnavailableError, EvidenceIntegrityError, } from "../../domain/errors.js";
|
|
@@ -11,8 +11,8 @@ import { toCallToolResult } from "../toolResult.js";
|
|
|
11
11
|
import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
|
|
12
12
|
import { safeParseToolInput } from "../toolInputValidation.js";
|
|
13
13
|
import { coverageWorkspaceUri } from "./helpers.js";
|
|
14
|
-
const commitContract =
|
|
15
|
-
const queryContract =
|
|
14
|
+
const commitContract = applicationToolContract("commit_reconstruction_coverage");
|
|
15
|
+
const queryContract = applicationToolContract("query_reconstruction_coverage");
|
|
16
16
|
/** Register reconstruction coverage commit and query tools. */
|
|
17
17
|
export const registerCoverageTools = (server, options) => {
|
|
18
18
|
registerCommitTool(server, options);
|
|
@@ -9,34 +9,46 @@ export const recordSources = (recordEvidence, sources) => {
|
|
|
9
9
|
}
|
|
10
10
|
return { ok: true, value: null };
|
|
11
11
|
};
|
|
12
|
-
export const recordResult = (options, contract, evidence,
|
|
13
|
-
const recorded =
|
|
14
|
-
? options.
|
|
15
|
-
|
|
16
|
-
question: "Which application entities remain unmatched or ambiguous across these versions?",
|
|
17
|
-
severity: "medium",
|
|
18
|
-
domain: "application-version-comparison",
|
|
19
|
-
supporting_evidence_ids: [evidence.evidence_id],
|
|
20
|
-
contradicting_evidence_ids: [],
|
|
21
|
-
required_authority: "shipped-artifact",
|
|
22
|
-
required_confidence: "observed",
|
|
23
|
-
required_environment: null,
|
|
24
|
-
recommended_probes: [
|
|
25
|
-
{
|
|
26
|
-
operation: "analyze_javascript_application",
|
|
27
|
-
rationale: "Repeat static reconstruction with complete artifacts and source maps when available.",
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
operation: "reconcile_javascript_runtime",
|
|
31
|
-
rationale: "Add approved passive runtime Evidence without promoting it to static fact.",
|
|
32
|
-
},
|
|
33
|
-
],
|
|
34
|
-
relationships: [],
|
|
35
|
-
})
|
|
36
|
-
: options.recordEvidence?.(evidence);
|
|
12
|
+
export const recordResult = (options, contract, evidence, unknownKind) => {
|
|
13
|
+
const recorded = unknownKind === undefined
|
|
14
|
+
? options.recordEvidence?.(evidence)
|
|
15
|
+
: options.recordEvidenceWithUnknown?.(evidence, unknownRegistration(evidence, unknownKind));
|
|
37
16
|
if (recorded !== undefined && !recorded.ok)
|
|
38
17
|
return toCallToolResult(recorded, contract);
|
|
39
18
|
return toCallToolResult({ ok: true, value: evidence }, contract, {
|
|
40
19
|
evidenceResourcesAvailable: recorded !== undefined,
|
|
41
20
|
});
|
|
42
21
|
};
|
|
22
|
+
const unknownRegistration = (evidence, kind) => ({
|
|
23
|
+
approved: true,
|
|
24
|
+
question: kind === "application-version-comparison"
|
|
25
|
+
? "Which application entities remain unmatched or ambiguous across these versions?"
|
|
26
|
+
: "Which selected JavaScript export return shapes remain dynamic, incomplete, or ambiguously paired?",
|
|
27
|
+
severity: "medium",
|
|
28
|
+
domain: kind,
|
|
29
|
+
supporting_evidence_ids: [evidence.evidence_id],
|
|
30
|
+
contradicting_evidence_ids: [],
|
|
31
|
+
required_authority: "shipped-artifact",
|
|
32
|
+
required_confidence: "observed",
|
|
33
|
+
required_environment: null,
|
|
34
|
+
recommended_probes: [
|
|
35
|
+
{
|
|
36
|
+
operation: "analyze_javascript_application",
|
|
37
|
+
rationale: "Repeat static reconstruction with complete artifacts and source maps when available.",
|
|
38
|
+
},
|
|
39
|
+
...(kind === "application-version-comparison"
|
|
40
|
+
? [
|
|
41
|
+
{
|
|
42
|
+
operation: "reconcile_javascript_runtime",
|
|
43
|
+
rationale: "Add approved passive runtime Evidence without promoting it to static fact.",
|
|
44
|
+
},
|
|
45
|
+
]
|
|
46
|
+
: [
|
|
47
|
+
{
|
|
48
|
+
operation: "run_controlled_replay",
|
|
49
|
+
rationale: "Validate exact approved module behavior separately when runtime semantics are required.",
|
|
50
|
+
},
|
|
51
|
+
]),
|
|
52
|
+
],
|
|
53
|
+
relationships: [],
|
|
54
|
+
});
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { traceApplicationFeatureEvidenceValidated } from "../../application/JavaScriptApplicationWorkflowService.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { resolveTraceApplicationFeatureRequestValidated } from "../../application/ApplicationWorkflowEvidenceResolver.js";
|
|
3
|
+
import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
|
|
4
|
+
import { traceApplicationFeatureRequestSchema } from "../../contracts/applicationWorkflowInputContracts.js";
|
|
4
5
|
import { logToolExecution } from "../toolLogging.js";
|
|
5
6
|
import { toCallToolResult } from "../toolResult.js";
|
|
6
7
|
import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
|
|
7
8
|
import { safeParseToolInput } from "../toolInputValidation.js";
|
|
8
9
|
import { recordResult, recordSources } from "./helpers.js";
|
|
9
|
-
const traceContract =
|
|
10
|
+
const traceContract = applicationToolContract("trace_application_feature");
|
|
10
11
|
/** Register the provider-neutral JavaScript feature trace tool. */
|
|
11
12
|
export const registerTraceFeatureTool = (server, options) => {
|
|
12
13
|
server.registerTool(traceContract.name, toolRegistrationOptions(traceContract), async (input) => {
|
|
13
|
-
const parsedInput = safeParseToolInput(
|
|
14
|
+
const parsedInput = safeParseToolInput(traceApplicationFeatureRequestSchema, input, traceContract.name);
|
|
14
15
|
if (!parsedInput.ok)
|
|
15
16
|
return toCallToolResult(parsedInput, traceContract);
|
|
16
|
-
const
|
|
17
|
+
const resolved = resolveTraceApplicationFeatureRequestValidated(parsedInput.value, options.evidenceLookup);
|
|
18
|
+
if (!resolved.ok)
|
|
19
|
+
return toCallToolResult(resolved, traceContract);
|
|
20
|
+
const parsed = resolved.value;
|
|
17
21
|
const result = await logToolExecution(options.logger, traceContract.name, () => Promise.resolve(traceApplicationFeatureEvidenceValidated(parsed)));
|
|
18
22
|
if (!result.ok)
|
|
19
23
|
return toCallToolResult(result, traceContract);
|