@eyeauras/insight-cli 0.0.0-stage → 0.2.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.
Files changed (174) hide show
  1. package/.cli-package-build.json +1 -0
  2. package/LICENSE +21 -0
  3. package/README.md +60 -2
  4. package/SKILL.md +58 -0
  5. package/dist/src/administration-results.d.ts +1 -0
  6. package/dist/src/administration-results.js +84 -0
  7. package/dist/src/administration.d.ts +14 -0
  8. package/dist/src/administration.js +134 -0
  9. package/dist/src/archive-contract.d.ts +6 -0
  10. package/dist/src/archive-contract.js +108 -0
  11. package/dist/src/archive.d.ts +12 -0
  12. package/dist/src/archive.js +116 -0
  13. package/dist/src/arguments.d.ts +14 -0
  14. package/dist/src/arguments.js +62 -0
  15. package/dist/src/artifacts.d.ts +17 -0
  16. package/dist/src/artifacts.js +115 -0
  17. package/dist/src/bin.d.ts +2 -0
  18. package/dist/src/bin.js +4 -0
  19. package/dist/src/cli.d.ts +10 -0
  20. package/dist/src/cli.js +419 -0
  21. package/dist/src/comment-effects.d.ts +3 -0
  22. package/dist/src/comment-effects.js +55 -0
  23. package/dist/src/contexts.d.ts +115 -0
  24. package/dist/src/contexts.js +1078 -0
  25. package/dist/src/coordinator.d.ts +41 -0
  26. package/dist/src/coordinator.js +160 -0
  27. package/dist/src/cross-results.d.ts +14 -0
  28. package/dist/src/cross-results.js +71 -0
  29. package/dist/src/cross.d.ts +3 -0
  30. package/dist/src/cross.js +43 -0
  31. package/dist/src/data-effects.d.ts +3 -0
  32. package/dist/src/data-effects.js +103 -0
  33. package/dist/src/debugger-results.d.ts +4 -0
  34. package/dist/src/debugger-results.js +151 -0
  35. package/dist/src/debugger-supervisor.d.ts +1 -0
  36. package/dist/src/debugger-supervisor.js +136 -0
  37. package/dist/src/debugger.d.ts +21 -0
  38. package/dist/src/debugger.js +267 -0
  39. package/dist/src/documentation-effects.d.ts +5 -0
  40. package/dist/src/documentation-effects.js +56 -0
  41. package/dist/src/extensions.d.ts +64 -0
  42. package/dist/src/extensions.js +289 -0
  43. package/dist/src/function-effects.d.ts +4 -0
  44. package/dist/src/function-effects.js +112 -0
  45. package/dist/src/ghidra.d.ts +50 -0
  46. package/dist/src/ghidra.js +468 -0
  47. package/dist/src/index.d.ts +2 -0
  48. package/dist/src/index.js +1 -0
  49. package/dist/src/initial-model.d.ts +6 -0
  50. package/dist/src/initial-model.js +85 -0
  51. package/dist/src/json.d.ts +2 -0
  52. package/dist/src/json.js +8 -0
  53. package/dist/src/lifecycle.d.ts +49 -0
  54. package/dist/src/lifecycle.js +154 -0
  55. package/dist/src/memory-inputs.d.ts +8 -0
  56. package/dist/src/memory-inputs.js +56 -0
  57. package/dist/src/merge.d.ts +29 -0
  58. package/dist/src/merge.js +66 -0
  59. package/dist/src/model.d.ts +74 -0
  60. package/dist/src/model.js +67 -0
  61. package/dist/src/mutation-effects.d.ts +4 -0
  62. package/dist/src/mutation-effects.js +321 -0
  63. package/dist/src/mutation-inputs.d.ts +2 -0
  64. package/dist/src/mutation-inputs.js +71 -0
  65. package/dist/src/mutations.d.ts +4 -0
  66. package/dist/src/mutations.js +53 -0
  67. package/dist/src/native-inputs.d.ts +28 -0
  68. package/dist/src/native-inputs.js +182 -0
  69. package/dist/src/native-queries.d.ts +2 -0
  70. package/dist/src/native-queries.js +29 -0
  71. package/dist/src/projects.d.ts +31 -0
  72. package/dist/src/projects.js +301 -0
  73. package/dist/src/prototype-cli.d.ts +8 -0
  74. package/dist/src/prototype-cli.js +155 -0
  75. package/dist/src/queries.d.ts +5 -0
  76. package/dist/src/queries.js +84 -0
  77. package/dist/src/query-policy.d.ts +6 -0
  78. package/dist/src/query-policy.js +72 -0
  79. package/dist/src/query-results.d.ts +2 -0
  80. package/dist/src/query-results.js +224 -0
  81. package/dist/src/research.d.ts +184 -0
  82. package/dist/src/research.js +808 -0
  83. package/dist/src/runtime-archives.d.ts +5 -0
  84. package/dist/src/runtime-archives.js +127 -0
  85. package/dist/src/runtime-catalog.d.ts +18 -0
  86. package/dist/src/runtime-catalog.js +27 -0
  87. package/dist/src/runtime-manager.d.ts +101 -0
  88. package/dist/src/runtime-manager.js +444 -0
  89. package/dist/src/runtime-selection.d.ts +13 -0
  90. package/dist/src/runtime-selection.js +25 -0
  91. package/dist/src/saved-materials.d.ts +12 -0
  92. package/dist/src/saved-materials.js +196 -0
  93. package/dist/src/scripts.d.ts +19 -0
  94. package/dist/src/scripts.js +79 -0
  95. package/dist/src/sdk-contracts.d.ts +13 -0
  96. package/dist/src/sdk-contracts.js +37 -0
  97. package/dist/src/sdk-files.d.ts +29 -0
  98. package/dist/src/sdk-files.js +249 -0
  99. package/dist/src/sdk.d.ts +4 -0
  100. package/dist/src/sdk.js +125 -0
  101. package/dist/src/server.d.ts +46 -0
  102. package/dist/src/server.js +460 -0
  103. package/dist/src/store.d.ts +33 -0
  104. package/dist/src/store.js +178 -0
  105. package/dist/src/sync-policy.d.ts +22 -0
  106. package/dist/src/sync-policy.js +123 -0
  107. package/dist/src/transport.d.ts +23 -0
  108. package/dist/src/transport.js +135 -0
  109. package/dist/src/ui-results.d.ts +2 -0
  110. package/dist/src/ui-results.js +80 -0
  111. package/dist/src/ui.d.ts +17 -0
  112. package/dist/src/ui.js +88 -0
  113. package/dist/src/utility.d.ts +14 -0
  114. package/dist/src/utility.js +43 -0
  115. package/dist/src/version-tracking.d.ts +57 -0
  116. package/dist/src/version-tracking.js +200 -0
  117. package/dist/src/workspace.d.ts +199 -0
  118. package/dist/src/workspace.js +564 -0
  119. package/package.json +47 -4
  120. package/references/InsightRepositoryProbe.java +55 -0
  121. package/references/administration.md +34 -0
  122. package/references/archive.md +32 -0
  123. package/references/artifacts.md +47 -0
  124. package/references/debugger.md +40 -0
  125. package/references/extensions-and-sync.md +124 -0
  126. package/references/managed-runtime.md +116 -0
  127. package/references/mutations.json +1424 -0
  128. package/references/native/InsightApplyDocumentation.java +48 -0
  129. package/references/native/InsightArchivePayloads.java +36 -0
  130. package/references/native/InsightBlockTransfer.java +57 -0
  131. package/references/native/InsightEmulation.java +93 -0
  132. package/references/native/InsightExtensions.java +36 -0
  133. package/references/native/InsightFunctionDatabases.java +98 -0
  134. package/references/native/InsightGuiMain.java +32 -0
  135. package/references/native/InsightHeadlessMain.java +10 -0
  136. package/references/native/InsightInitialImport.java +42 -0
  137. package/references/native/InsightInitialJournal.java +48 -0
  138. package/references/native/InsightInitialPublication.java +142 -0
  139. package/references/native/InsightKeytool.java +16 -0
  140. package/references/native/InsightNativeReceipt.java +31 -0
  141. package/references/native/InsightPlugins.java +34 -0
  142. package/references/native/InsightProjectFiles.java +92 -0
  143. package/references/native/InsightPublicationRecovery.java +63 -0
  144. package/references/native/InsightPythonWheels.java +31 -0
  145. package/references/native/InsightResearchQueries.java +188 -0
  146. package/references/native/InsightRuntimeDoctor.java +33 -0
  147. package/references/native/InsightSdkFiles.java +142 -0
  148. package/references/native/InsightSdkScript.java +31 -0
  149. package/references/native/InsightSdkTools.java +338 -0
  150. package/references/native/InsightSdkTypes.java +132 -0
  151. package/references/native/InsightServerAdministration.java +82 -0
  152. package/references/native/InsightServerInit.java +62 -0
  153. package/references/native/InsightServerMain.java +43 -0
  154. package/references/native/InsightServerProbe.java +29 -0
  155. package/references/native/InsightSnapshotFields.java +68 -0
  156. package/references/native/InsightSnapshotInspect.java +189 -0
  157. package/references/native/InsightSnapshotPublisher.java +123 -0
  158. package/references/native/InsightStartDebugger.java +28 -0
  159. package/references/native/InsightStopDebugger.java +20 -0
  160. package/references/native/InsightTraceInspect.java +24 -0
  161. package/references/native/InsightTraceState.java +130 -0
  162. package/references/native/InsightTraceTools.java +114 -0
  163. package/references/native/InsightVersionTracking.java +91 -0
  164. package/references/native-workflows.md +112 -0
  165. package/references/queries.json +1761 -0
  166. package/references/query-semantics.md +96 -0
  167. package/references/recovery.md +53 -0
  168. package/references/research.md +101 -0
  169. package/references/setup.md +49 -0
  170. package/runtime/GhidraMCP-7.0.0.jar +0 -0
  171. package/runtime/GhidraMCP-LICENSE +202 -0
  172. package/runtime/GhidraMCP-NOTICE +10 -0
  173. package/runtime/provenance.json +11 -0
  174. package/skills/insight-re/SKILL.md +108 -0
@@ -0,0 +1,130 @@
1
+ import com.google.gson.*;
2
+ import ghidra.app.script.GhidraScript;
3
+ import ghidra.app.services.DebuggerTraceManagerService;
4
+ import ghidra.app.services.DebuggerTargetService;
5
+ import ghidra.app.services.DebuggerLogicalBreakpointService;
6
+ import ghidra.app.services.DebuggerStaticMappingService;
7
+ import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
8
+ import ghidra.trace.model.Trace;
9
+ import ghidra.framework.plugintool.PluginTool;
10
+ import java.nio.file.*;
11
+ import java.nio.charset.StandardCharsets;
12
+ import java.util.*;
13
+ import javax.swing.SwingUtilities;
14
+ import ghidra.program.model.address.AddressSet;
15
+ import ghidra.trace.model.memory.TraceMemoryState;
16
+
17
+ /** Save/restore trace databases independently of the program database and live execution. */
18
+ public class InsightTraceState extends GhidraScript {
19
+ static String string(JsonObject p,String key){return p.get(key).getAsString();}
20
+ static void bind(Trace trace,JsonObject p,boolean create){
21
+ var options=trace.getOptions("Insight");String target=options.getString("Target",null),context=options.getString("Context",null);
22
+ if(target==null&&context==null&&create){int tx=trace.startTransaction("Bind owned trace");try{
23
+ options.setString("Target",string(p,"target"));options.setString("Context",string(p,"context"));options.setString("Trace ID","trace_"+UUID.randomUUID());
24
+ }finally{trace.endTransaction(tx,true);}}
25
+ else if(!string(p,"target").equals(target)||!string(p,"context").equals(context))throw new IllegalArgumentException("Trace belongs to another context");
26
+ }
27
+ void relocateMappings(Trace trace,PluginTool tool)throws Exception{
28
+ var url=ProgramURLUtils.getUrlFromProgram(currentProgram);if(url==null)throw new IllegalStateException("Selected static program has no project URL");
29
+ var options=trace.getOptions("Insight");String previous=options.getString("Static Program URL",null);
30
+ if(previous==null||previous.equals(url.toExternalForm()))return;
31
+ var mappings=trace.getStaticMappingManager();int tx=trace.startTransaction("Relocate owned static mappings");boolean commit=false;
32
+ try{
33
+ for(var mapping:List.copyOf(mappings.getAllEntries()))if(previous.equals(mapping.getStaticProgramURL().toExternalForm())){
34
+ var range=mapping.getTraceAddressRange();var lifespan=mapping.getLifespan();String address=mapping.getStaticAddress();
35
+ mapping.delete();mappings.add(range,lifespan,url,address);
36
+ }
37
+ options.setString("Static Program URL",url.toExternalForm());commit=true;
38
+ }finally{trace.endTransaction(tx,commit);}
39
+ var service=tool.getService(DebuggerStaticMappingService.class);if(service==null)throw new IllegalStateException("Static mapping service unavailable");
40
+ service.changesSettled().get(30,java.util.concurrent.TimeUnit.SECONDS);
41
+ }
42
+ @Override public void run()throws Exception{
43
+ var p=JsonParser.parseString(new String(Base64.getDecoder().decode(getScriptArgs()[0]),StandardCharsets.UTF_8)).getAsJsonObject();
44
+ var project=state.getProject();DebuggerTraceManagerService manager=null;PluginTool selected=null;
45
+ for(PluginTool tool:project.getToolManager().getRunningTools()){
46
+ var candidate=tool.getService(DebuggerTraceManagerService.class);
47
+ if(candidate!=null&&(manager==null||!candidate.getOpenTraces().isEmpty())){manager=candidate;selected=tool;}
48
+ }
49
+ if(manager==null)throw new IllegalStateException("Debugger services unavailable");
50
+ var data=project.getProjectData();String operation=string(p,"operation");
51
+ if(Set.of("write_memory","write_register","timeline","objects","object_values","schedule").contains(operation)){
52
+ var trace=manager.getCurrentTrace();if(trace==null)throw new IllegalStateException("No selected trace");bind(trace,p,false);
53
+ Object value=insight.InsightTraceTools.run(trace,manager,selected,p,monitor);
54
+ String encoded=new GsonBuilder().serializeNulls().create().toJson(value);if(encoded.length()>524288)throw new IllegalArgumentException("Trace result exceeds 512 KiB");
55
+ println("INSIGHT_TRACE_STATE "+encoded);return;
56
+ }
57
+ if(operation.equals("breakpoints")){
58
+ var trace=manager.getCurrentTrace();if(trace==null)throw new IllegalStateException("No selected trace");bind(trace,p,false);
59
+ var service=selected.getService(DebuggerLogicalBreakpointService.class);if(service==null)throw new IllegalStateException("Breakpoint service unavailable");
60
+ List<Object> rows=new ArrayList<>();long snap=manager.getCurrentSnap();
61
+ for(var entry:service.getBreakpoints(trace).entrySet())for(var breakpoint:entry.getValue()){
62
+ rows.add(Map.of("address",entry.getKey().toString(),"name",breakpoint.getName(),"kinds",breakpoint.getKinds().stream().map(Enum::name).toList(),"state",breakpoint.computeState().name(),
63
+ "trace_location_count",breakpoint.getTraceBreakpoints(trace).stream().filter(location->!location.isDeleted()&&location.isValid(snap)).count()));
64
+ }
65
+ println("INSIGHT_TRACE_STATE "+new Gson().toJson(Map.of("items",rows)));return;
66
+ }
67
+ if(operation.equals("read_memory")){
68
+ var trace=manager.getCurrentTrace();if(trace==null)throw new IllegalStateException("No selected trace");bind(trace,p,false);
69
+ var at=trace.getBaseAddressFactory().getAddress(string(p,"address"));int size=p.get("size").getAsInt();if(at==null||size<1||size>4096)throw new IllegalArgumentException();
70
+ var targets=selected.getService(DebuggerTargetService.class);var target=targets==null?null:targets.getTarget(trace);
71
+ if(target!=null&&target.isValid())target.readMemory(new AddressSet(at,at.add(size-1)),monitor);
72
+ long snap=manager.getCurrentSnap();for(int i=0;i<size;i++)if(trace.getMemoryManager().getState(snap,at.add(i))!=TraceMemoryState.KNOWN)throw new IllegalStateException("Requested trace memory is not known");
73
+ println("INSIGHT_TRACE_STATE "+new Gson().toJson(Map.of("state","known")));return;
74
+ }
75
+ if(operation.equals("bind_new")){
76
+ var trace=manager.getCurrentTrace();if(trace==null||!trace.getName().equals(string(p,"name"))||trace.getOptions("Insight").getString("Trace ID",null)!=null)throw new IllegalStateException("New launch trace was not established");
77
+ bind(trace,p,true);var url=ProgramURLUtils.getUrlFromProgram(currentProgram);if(url==null)throw new IllegalStateException("Selected static program has no project URL");
78
+ int tx=trace.startTransaction("Bind owned static program");try{trace.getOptions("Insight").setString("Static Program URL",url.toExternalForm());trace.getOptions("Insight").setString("Owned Context Root",string(p,"owned_root"));}finally{trace.endTransaction(tx,true);}
79
+ println("INSIGHT_TRACE_STATE "+new Gson().toJson(Map.of("state","bound")));return;
80
+ }
81
+ if(operation.equals("focus")){
82
+ var thread=manager.getCurrentThread();var object=thread==null?null:thread.getObject();
83
+ if(object==null){var candidates=manager.getCurrentTrace().getObjectManager().getAllObjects().filter(item->item.getSchema().getName().toString().equals("Process")).toList();
84
+ if(candidates.size()!=1)throw new IllegalStateException("Debugger process selection is ambiguous");object=candidates.get(0);}
85
+ var m=manager;final var selectedObject=object;Runnable focus=()->m.activateObject(selectedObject);
86
+ if(SwingUtilities.isEventDispatchThread())focus.run();else SwingUtilities.invokeAndWait(focus);
87
+ println("INSIGHT_TRACE_STATE "+new Gson().toJson(Map.of("state","focused")));return;
88
+ }
89
+ if(operation.equals("restore")){
90
+ var folder=data.getRootFolder().getFolder("__insight_traces");if(folder==null)folder=data.getRootFolder().createFolder("__insight_traces");
91
+ for(var entry:p.getAsJsonArray("traces")){
92
+ var saved=entry.getAsJsonObject();String id=string(saved,"id");if(!id.matches("trace_[a-f0-9-]{36}"))throw new IllegalArgumentException();
93
+ boolean present=false;for(var trace:manager.getOpenTraces())if(id.equals(trace.getOptions("Insight").getString("Trace ID",null)))present=true;
94
+ if(present)continue;
95
+ var file=folder.getFile(id);if(file==null)file=folder.createFile(id,Path.of(string(saved,"input")).toFile(),monitor);
96
+ Trace trace=manager.openTrace(file,-1);bind(trace,p,false);
97
+ if(!id.equals(trace.getOptions("Insight").getString("Trace ID",null)))throw new IllegalArgumentException("Restored trace identity changed");
98
+ relocateMappings(trace,selected);
99
+ if(p.has("active")&&id.equals(string(p,"active"))){final var m=manager;Runnable activate=()->{
100
+ m.activateTrace(trace);m.activateSnap(Long.parseLong(string(saved,"snap")));
101
+ if(saved.has("thread")){var thread=trace.getThreadManager().getThread(Long.parseLong(string(saved,"thread")));if(thread!=null)m.activateThread(thread);}
102
+ };if(SwingUtilities.isEventDispatchThread())activate.run();else SwingUtilities.invokeAndWait(activate);}
103
+ }
104
+ println("INSIGHT_TRACE_STATE "+new Gson().toJson(Map.of("state","restored")));return;
105
+ }
106
+ if(operation.equals("select")){
107
+ String id=string(p,"id");for(var trace:manager.getOpenTraces()){
108
+ bind(trace,p,false);if(id.equals(trace.getOptions("Insight").getString("Trace ID",null))){final var m=manager;
109
+ if(SwingUtilities.isEventDispatchThread())m.activateTrace(trace);else SwingUtilities.invokeAndWait(()->m.activateTrace(trace));
110
+ println("INSIGHT_TRACE_STATE "+new Gson().toJson(Map.of("state","selected","id",id)));return;}
111
+ }
112
+ throw new IllegalArgumentException("Trace not found");
113
+ }
114
+ if(!operation.equals("checkpoint"))throw new IllegalArgumentException();
115
+ Path root=Path.of(string(p,"output")).toRealPath();List<Object> traces=new ArrayList<>();String active=null;
116
+ if(manager.getOpenTraces().size()>64)throw new IllegalArgumentException("Trace count exceeds 64");
117
+ for(var trace:manager.getOpenTraces()){
118
+ bind(trace,p,false);String id=trace.getOptions("Insight").getString("Trace ID",null);if(id==null||!id.matches("trace_[a-f0-9-]{36}"))throw new IllegalArgumentException();
119
+ Path output=root.resolve(id+".gzf");if(Files.exists(output))throw new IllegalArgumentException();
120
+ var coordinates=manager.getCurrentFor(trace);trace.saveToPackedFile(output.toFile(),monitor);
121
+ Map<String,Object> row=new LinkedHashMap<>();row.put("id",id);row.put("name",trace.getName());row.put("snap",Long.toString(coordinates.getSnap()));
122
+ row.put("modules",trace.getModuleManager().getAllModules().size());row.put("threads",trace.getThreadManager().getAllThreads().size());
123
+ if(coordinates.getThread()!=null)row.put("thread",Long.toString(coordinates.getThread().getKey()));
124
+ var targets=selected.getService(DebuggerTargetService.class);var live=targets==null?null:targets.getTarget(trace);row.put("connected",live!=null&&live.isValid());
125
+ traces.add(row);if(manager.getCurrentTrace()==trace)active=id;
126
+ }
127
+ Map<String,Object> result=new LinkedHashMap<>();result.put("traces",traces);if(active!=null)result.put("active",active);
128
+ println("INSIGHT_TRACE_STATE "+new Gson().toJson(result));
129
+ }
130
+ }
@@ -0,0 +1,114 @@
1
+ package insight;
2
+
3
+ import com.google.gson.*;
4
+ import ghidra.app.services.*;
5
+ import ghidra.debug.api.target.Target;
6
+ import ghidra.framework.plugintool.PluginTool;
7
+ import ghidra.program.model.address.*;
8
+ import ghidra.program.model.lang.*;
9
+ import ghidra.trace.model.*;
10
+ import ghidra.trace.model.memory.*;
11
+ import ghidra.trace.model.target.*;
12
+ import ghidra.trace.model.target.path.KeyPath;
13
+ import ghidra.trace.model.time.schedule.TraceSchedule;
14
+ import ghidra.trace.model.time.TraceSnapshot;
15
+ import ghidra.util.task.TaskMonitor;
16
+ import java.util.*;
17
+ import java.math.BigInteger;
18
+ import java.nio.ByteBuffer;
19
+ import java.util.concurrent.TimeUnit;
20
+
21
+ /** Typed trace inspection and guarded live-target writes. Trace cache writes never substitute for target writes. */
22
+ public class InsightTraceTools {
23
+ static String text(JsonObject p,String key){return p.has(key)?p.get(key).getAsString():"";}
24
+ static int bound(JsonObject p,String key,int fallback,int maximum){int n=p.has(key)?p.get(key).getAsInt():fallback;if(n<1||n>maximum)throw new IllegalArgumentException("Invalid trace work bound");return n;}
25
+ static void require(boolean value,String message){if(!value)throw new IllegalStateException(message);}
26
+ static byte[] hex(String text){if(!text.matches("(?:[a-f0-9A-F]{2}){1,4096}"))throw new IllegalArgumentException("Expected 1–4096 complete byte pairs");return HexFormat.of().parseHex(text);}
27
+ static BigInteger integer(String text){if(!text.matches("(?:0[xX])?[a-f0-9A-F]{1,128}"))throw new IllegalArgumentException("Expected unsigned hex register value");return new BigInteger(text.replaceFirst("(?i)^0x",""),16);}
28
+ static Object value(Object value){
29
+ if(value instanceof TraceObject object)return Map.of("kind","object","object_id",Long.toString(object.getKey()),"path",object.getCanonicalPath().toString(),"schema",object.getSchema().getName().toString());
30
+ if(value instanceof Address at)return Map.of("kind","address","space",at.getAddressSpace().getName(),"address",at.toString());
31
+ if(value instanceof AddressRange range)return Map.of("kind","address_range","start",range.getMinAddress().toString(),"end",range.getMaxAddress().toString());
32
+ if(value instanceof Number)return Map.of("kind","number","value",value.toString());
33
+ if(value instanceof Boolean)return Map.of("kind","boolean","value",value);
34
+ if(value instanceof String string)return Map.of("kind","string","value",string.substring(0,Math.min(4096,string.length())),"complete",string.length()<=4096);
35
+ if(value instanceof byte[] bytes)return Map.of("kind","bytes","hex",HexFormat.of().formatHex(Arrays.copyOf(bytes,Math.min(bytes.length,4096))),"size",bytes.length,"complete",bytes.length<=4096);
36
+ return Map.of("kind","unsupported","java_type",value==null?"null":value.getClass().getSimpleName());
37
+ }
38
+ static Target live(Trace trace,DebuggerTraceManagerService manager,PluginTool tool){
39
+ var targets=tool.getService(DebuggerTargetService.class);Target target=targets==null?null:targets.getTarget(trace);
40
+ require(target!=null&&target.isValid(),"Selected trace is disconnected");var coords=manager.getCurrentFor(trace);
41
+ require(coords.getTime().isSnapOnly()&&coords.getSnap()==target.getSnap(),"Write requires the current live snapshot, not historical/emulated time");
42
+ require(coords.getThread()!=null&&target.getThreadExecutionState(coords.getThread())==TraceExecutionState.STOPPED,"Write requires a selected stopped thread");return target;
43
+ }
44
+ static byte[] memory(Trace trace,Target target,Address at,int size,TaskMonitor monitor)throws Exception{
45
+ target.readMemoryAsync(new AddressSet(at,at.add(size-1)),monitor).get(30,TimeUnit.SECONDS);long snap=target.getSnap();
46
+ var memory=trace.getMemoryManager();for(int i=0;i<size;i++)require(memory.getState(snap,at.add(i))==TraceMemoryState.KNOWN,"Fresh target memory is not known");
47
+ ByteBuffer bytes=ByteBuffer.allocate(size);require(memory.getBytes(snap,at,bytes)==size,"Fresh target memory is incomplete");return bytes.array();
48
+ }
49
+ public static Object run(Trace trace,DebuggerTraceManagerService manager,PluginTool tool,JsonObject p,TaskMonitor monitor)throws Exception{
50
+ boolean[] targetEffect={false};try{Object result=operation(trace,manager,tool,p,monitor,targetEffect);JsonElement json=new Gson().toJsonTree(result);
51
+ String original=trace.getOptions("Insight").getString("Owned Context Root",text(p,"owned_root"));return logical(json,original,text(p,"owned_root"));}
52
+ catch(Exception failure){String operation=text(p,"operation");if((operation.equals("write_memory")||operation.equals("write_register"))&&!targetEffect[0]){
53
+ String diagnostic=failure.getStackTrace().length>0&&failure.getStackTrace()[0].getClassName().equals("insight.InsightTraceTools")?Objects.toString(failure.getMessage(),failure.getClass().getSimpleName()):failure.getClass().getSimpleName();
54
+ return Map.of("status","rejected","target_effect_started",false,"reason","native_live_precondition_changed","diagnostic",diagnostic);}
55
+ throw failure;}
56
+ }
57
+ static JsonElement logical(JsonElement value,String original,String current){
58
+ if(value.isJsonPrimitive()&&value.getAsJsonPrimitive().isString()){String text=value.getAsString();if(!original.isEmpty())text=text.replace(original,"@target");if(!current.isEmpty())text=text.replace(current,"@target");return new JsonPrimitive(text);}
59
+ if(value.isJsonArray()){JsonArray result=new JsonArray();for(JsonElement child:value.getAsJsonArray())result.add(logical(child,original,current));return result;}
60
+ if(value.isJsonObject()){JsonObject result=new JsonObject();for(var entry:value.getAsJsonObject().entrySet())result.add(entry.getKey(),logical(entry.getValue(),original,current));return result;}return value;
61
+ }
62
+ static Object operation(Trace trace,DebuggerTraceManagerService manager,PluginTool tool,JsonObject p,TaskMonitor monitor,boolean[] targetEffect)throws Exception{
63
+ String operation=text(p,"operation");long snap=p.has("snap")?Long.parseLong(text(p,"snap")):manager.getCurrentSnap();
64
+ if(operation.equals("write_memory")){
65
+ Target target=live(trace,manager,tool);byte[] expected=hex(text(p,"expect_hex")),wanted=hex(text(p,"bytes_hex"));if(expected.length!=wanted.length)throw new IllegalArgumentException("Expected/replacement lengths differ");
66
+ Address at=trace.getBaseAddressFactory().getAddress(text(p,"address"));if(at==null)throw new IllegalArgumentException("Invalid trace address");
67
+ require(Arrays.equals(memory(trace,target,at,expected.length,monitor),expected),"Live memory byte precondition changed");
68
+ targetEffect[0]=true;target.writeMemoryAsync(at,wanted).get(30,TimeUnit.SECONDS);require(Arrays.equals(memory(trace,target,at,wanted.length,monitor),wanted),"Fresh live memory readback differs");
69
+ return Map.of("status","written","address",at.toString(),"size",wanted.length,"bytes_hex",HexFormat.of().formatHex(wanted),"verified_from","live_target_refresh","snap",Long.toString(target.getSnap()));
70
+ }
71
+ if(operation.equals("write_register")){
72
+ Target target=live(trace,manager,tool);var coords=manager.getCurrentFor(trace);var platform=coords.getPlatform();Register register=platform.getLanguage().getRegister(text(p,"register"));
73
+ if(register==null)throw new IllegalArgumentException("Unknown platform register");BigInteger expected=integer(text(p,"expect_value")),wanted=integer(text(p,"value"));
74
+ if(expected.bitLength()>register.getBitLength()||wanted.bitLength()>register.getBitLength())throw new IllegalArgumentException("Register value exceeds its exact width");
75
+ target.readRegistersAsync(platform,coords.getThread(),coords.getFrame(),Set.of(register)).get(30,TimeUnit.SECONDS);
76
+ var space=trace.getMemoryManager().getMemoryRegisterSpace(coords.getThread(),coords.getFrame(),false);require(space!=null,"Selected register bank is absent");
77
+ var observed=space.getViewValue(platform,target.getSnap(),register);require(observed!=null&&observed.hasValue()&&expected.equals(observed.getUnsignedValue()),"Live register precondition changed");
78
+ targetEffect[0]=true;target.writeRegisterAsync(platform,coords.getThread(),coords.getFrame(),new RegisterValue(register,wanted)).get(30,TimeUnit.SECONDS);
79
+ target.readRegistersAsync(platform,coords.getThread(),coords.getFrame(),Set.of(register)).get(30,TimeUnit.SECONDS);observed=space.getViewValue(platform,target.getSnap(),register);
80
+ require(observed!=null&&observed.hasValue()&&wanted.equals(observed.getUnsignedValue()),"Fresh live register readback differs");
81
+ return Map.of("status","written","register",register.getName(),"value","0x"+wanted.toString(16),"bits",register.getBitLength(),"verified_from","live_target_refresh","snap",Long.toString(target.getSnap()));
82
+ }
83
+ if(operation.equals("timeline")){
84
+ long before=p.has("before_snap")?Long.parseLong(text(p,"before_snap")):Long.MAX_VALUE;int limit=bound(p,"limit",100,1000);List<Object> rows=new ArrayList<>();boolean complete=false;
85
+ for(int i=0;i<limit;i++){monitor.checkCancelled();TraceSnapshot snapshot=trace.getTimeManager().getMostRecentSnapshot(before);if(snapshot==null){complete=true;break;}
86
+ Map<String,Object> row=new LinkedHashMap<>();row.put("snap",Long.toString(snapshot.getKey()));row.put("description",snapshot.getDescription());row.put("schedule",snapshot.getScheduleString());row.put("real_time",Long.toString(snapshot.getRealTime()));
87
+ row.put("event_thread",snapshot.getEventThread()==null?null:Long.toString(snapshot.getEventThread().getKey()));rows.add(row);
88
+ if(snapshot.getKey()==Long.MIN_VALUE){complete=true;break;}before=snapshot.getKey()-1;}
89
+ Map<String,Object> out=new LinkedHashMap<>();out.put("items",rows);out.put("complete",complete);if(!complete)out.put("next_before_snap",Long.toString(before));return out;
90
+ }
91
+ if(operation.equals("objects")){
92
+ int limit=bound(p,"limit",100,1000),budget=bound(p,"max_scan",10000,200000),visited=0;List<Object> rows=new ArrayList<>();String prefix=text(p,"path_prefix").replace("@target",trace.getOptions("Insight").getString("Owned Context Root",text(p,"owned_root")));
93
+ try(var stream=trace.getObjectManager().getAllObjects()){var iterator=stream.iterator();
94
+ while(iterator.hasNext()&&rows.size()<limit&&visited<budget){monitor.checkCancelled();TraceObject object=iterator.next();visited++;
95
+ if(object.isAlive(snap)&&object.getCanonicalPath().toString().startsWith(prefix))rows.add(Map.of("object_id",Long.toString(object.getKey()),"path",object.getCanonicalPath().toString(),"schema",object.getSchema().getName().toString(),"life",object.getLife().toString()));}
96
+ return Map.of("snap",Long.toString(snap),"items",rows,"visited",visited,"complete",!iterator.hasNext());}
97
+ }
98
+ if(operation.equals("object_values")){
99
+ TraceObject object=trace.getObjectManager().getObjectById(Long.parseLong(text(p,"object_id")));if(object==null)throw new IllegalArgumentException("Trace object ID is absent");
100
+ String key=text(p,"key");long from=p.has("from_snap")?Long.parseLong(text(p,"from_snap")):snap,to=p.has("to_snap")?Long.parseLong(text(p,"to_snap")):from;
101
+ if(to<from||BigInteger.valueOf(to).subtract(BigInteger.valueOf(from)).compareTo(BigInteger.valueOf(999))>0)throw new IllegalArgumentException("Value history range is 1–1000 snaps");
102
+ List<Object> rows=new ArrayList<>();String previous="";
103
+ for(long current=from;;current++){monitor.checkCancelled();TraceObjectValue entry=object.getValue(current,key);
104
+ if(entry!=null&&!entry.getLifespan().toString().equals(previous)){rows.add(Map.of("key",key,"lifespan",entry.getLifespan().toString(),"value",value(entry.getValue())));previous=entry.getLifespan().toString();}
105
+ if(current==to)break;}
106
+ return Map.of("object_id",Long.toString(object.getKey()),"path",object.getCanonicalPath().toString(),"key",key,"from_snap",Long.toString(from),"to_snap",Long.toString(to),"items",rows,"complete",true);
107
+ }
108
+ if(operation.equals("schedule")){
109
+ TraceSchedule schedule=TraceSchedule.parse(text(p,"schedule"));schedule.validate(trace);
110
+ return Map.of("schedule",schedule.toString(),"base_snap",Long.toString(schedule.getSnap()),"ticks",Long.toString(schedule.totalTickCount()),"patches",Long.toString(schedule.totalPatchCount()),"snap_only",schedule.isSnapOnly(),"validated",true,"executed",false);
111
+ }
112
+ throw new IllegalArgumentException("Unknown typed trace operation");
113
+ }
114
+ }
@@ -0,0 +1,91 @@
1
+ import com.google.gson.*;
2
+ import ghidra.GhidraApplicationLayout;
3
+ import ghidra.framework.*;
4
+ import ghidra.framework.model.*;
5
+ import ghidra.base.project.GhidraProject;
6
+ import ghidra.program.model.listing.*;
7
+ import ghidra.program.model.address.*;
8
+ import ghidra.feature.vt.api.db.VTSessionDB;
9
+ import ghidra.feature.vt.api.main.*;
10
+ import ghidra.feature.vt.api.correlator.program.*;
11
+ import ghidra.util.task.*;
12
+ import java.util.*;
13
+ import java.util.concurrent.TimeUnit;
14
+ import java.nio.file.*;
15
+ import java.nio.channels.FileChannel;
16
+
17
+ /** Persistent native VT session with owned source/destination programs, never caller GPR paths. */
18
+ class InsightVersionTracking {
19
+ static String text(JsonObject p,String key){return p.has(key)?p.get(key).getAsString():"";}
20
+ static Map<String,Object> match(VTMatch value){var association=value.getAssociation();return Map.of("set_id",value.getMatchSet().getID(),
21
+ "source",value.getSourceAddress().toString(),"destination",value.getDestinationAddress().toString(),
22
+ "similarity",value.getSimilarityScore().getScore(),"confidence",value.getConfidenceScore().getScore(),
23
+ "status",association.getStatus().toString(),"type",association.getType().toString(),"markup_status",association.getMarkupStatus().toString());}
24
+ static Object matches(VTSessionDB session,JsonObject p){
25
+ int limit=p.has("limit")?p.get("limit").getAsInt():100,offset=p.has("offset")?p.get("offset").getAsInt():0,total=0;List<Object> rows=new ArrayList<>(),sets=new ArrayList<>();Set<Address> sourceMatches=new HashSet<>(),destinationMatches=new HashSet<>();
26
+ for(var set:session.getMatchSets()){
27
+ sets.add(Map.of("id",set.getID(),"correlator",set.getProgramCorrelatorInfo().getName(),"count",set.getMatchCount()));
28
+ for(var value:set.getMatches()){sourceMatches.add(value.getSourceAddress());destinationMatches.add(value.getDestinationAddress());if(total++<offset)continue;if(rows.size()<limit)rows.add(match(value));}}
29
+ return Map.of("match_sets",sets,"items",rows,"total",total,"offset",offset,"limit",limit,"complete",offset+rows.size()>=total,
30
+ "unmatched_source",unmatched(session.getSourceProgram(),sourceMatches),"unmatched_destination",unmatched(session.getDestinationProgram(),destinationMatches),
31
+ "score_semantics","Native VT scores are correlator-specific, not calibrated probabilities. Unmatched means no association from these configured correlators, not proof of no equivalent function.");
32
+ }
33
+ static Object unmatched(Program program,Set<Address> matched){List<Object> rows=new ArrayList<>();int total=0;var iterator=program.getFunctionManager().getFunctions(true);
34
+ while(iterator.hasNext()){Function function=iterator.next();if(matched.contains(function.getEntryPoint()))continue;if(total++<1000)rows.add(Map.of("name",function.getName(),"address",function.getEntryPoint().toString()));}
35
+ return Map.of("items",rows,"total",total,"complete",total<=1000);}
36
+ static VTAssociation association(VTSessionDB session,JsonObject p){
37
+ Address from=session.getSourceProgram().getAddressFactory().getAddress(text(p,"source_address")),to=session.getDestinationProgram().getAddressFactory().getAddress(text(p,"destination_address"));
38
+ if(from==null||to==null)throw new IllegalArgumentException("Invalid VT association addresses");
39
+ for(var set:session.getMatchSets())for(var value:set.getMatches())if(value.getSourceAddress().equals(from)&&value.getDestinationAddress().equals(to))return value.getAssociation();
40
+ throw new IllegalArgumentException("VT association is not in this session");
41
+ }
42
+ static Object markup(VTAssociation association,TaskMonitor monitor)throws Exception{
43
+ List<Object> rows=new ArrayList<>();for(var item:association.getMarkupItems(monitor)){
44
+ if(rows.size()>=1000)throw new IllegalArgumentException("Markup inventory exceeds 1000 items");Map<String,Object> row=new LinkedHashMap<>();
45
+ row.put("type",item.getMarkupType().getDisplayName());row.put("source",item.getSourceAddress().toString());row.put("destination",item.getDestinationAddress()==null?null:item.getDestinationAddress().toString());
46
+ row.put("source_value",Objects.toString(item.getSourceValue(),null));row.put("destination_value",Objects.toString(item.getCurrentDestinationValue(),null));
47
+ row.put("status",item.getStatus().toString());row.put("can_apply",item.canApply());rows.add(row);}
48
+ return Map.of("items",rows,"association_status",association.getStatus().toString(),"read_only",true);
49
+ }
50
+ static Object run(JsonObject p)throws Exception{
51
+ String operation=text(p,"operation"),root=text(p,"root");Object consumer=new Object();GhidraProject project=null;VTSessionDB session=null;Program source=null,destination=null;
52
+ TimeoutTaskMonitor monitor=TimeoutTaskMonitor.timeoutIn(60,TimeUnit.SECONDS);
53
+ try{
54
+ project=operation.equals("create")?GhidraProject.createProject(root,"project",false):GhidraProject.openProject(root,"project",false);
55
+ var data=project.getProjectData();
56
+ if(operation.equals("create")){
57
+ DomainFile first=data.getRootFolder().createFile("Source",Path.of(text(p,"source_snapshot")).toFile(),monitor);
58
+ DomainFile second=data.getRootFolder().createFile("Destination",Path.of(text(p,"destination_snapshot")).toFile(),monitor);
59
+ source=(Program)first.getDomainObject(consumer,true,false,monitor);destination=(Program)second.getDomainObject(consumer,true,false,monitor);
60
+ if(source.getFunctionManager().getFunctionCount()>10000||destination.getFunctionManager().getFunctionCount()>10000)throw new IllegalArgumentException("VT workflow accepts at most 10000 functions per input");
61
+ session=new VTSessionDB("OwnedVersionTracking",source,destination,consumer);int transaction=session.startTransaction("Correlate owned versions");boolean committed=false;
62
+ try{
63
+ var exact=new ExactMatchBytesProgramCorrelatorFactory();var options=exact.createDefaultOptions();options.setInt(ExactMatchBytesProgramCorrelatorFactory.FUNCTION_MINIMUM_SIZE,1);
64
+ exact.createCorrelator(source,source.getMemory(),destination,destination.getMemory(),options).correlate(session,monitor);
65
+ var duplicates=new DuplicateFunctionMatchProgramCorrelatorFactory();duplicates.createCorrelator(source,source.getMemory(),destination,destination.getMemory(),duplicates.createDefaultOptions()).correlate(session,monitor);
66
+ committed=true;
67
+ }finally{session.endTransaction(transaction,committed);}
68
+ data.getRootFolder().createFile("Session",session,monitor);
69
+ }else{
70
+ DomainFile file=data.getFile("/Session");if(file==null)throw new IllegalArgumentException("Native VT session is absent");session=(VTSessionDB)file.getDomainObject(consumer,true,false,monitor);
71
+ }
72
+ Object result;
73
+ if(operation.equals("create")||operation.equals("matches"))result=matches(session,p);
74
+ else if(operation.equals("markup"))result=markup(association(session,p),monitor);
75
+ else if(operation.equals("decide")){
76
+ VTAssociation value=association(session,p);String decision=text(p,"decision");int transaction=session.startTransaction("Explicit match decision");boolean committed=false,closed=false;
77
+ try{switch(decision){case "accepted":value.setAccepted();break;case "rejected":value.setRejected();break;case "available":value.clearStatus();break;default:throw new IllegalArgumentException("Unknown match decision");}committed=true;}
78
+ catch(Exception rejected){closed=true;session.endTransaction(transaction,false);return Map.of("state","rejected","reason","native_association_constraint");}
79
+ finally{if(!closed)session.endTransaction(transaction,committed);}
80
+ session.save("Explicit facade match decision",monitor);result=Map.of("source",value.getSourceAddress().toString(),"destination",value.getDestinationAddress().toString(),"status",value.getStatus().toString());
81
+ }else throw new IllegalArgumentException("Unknown VT operation");
82
+ return result;
83
+ }finally{monitor.finished();if(session!=null)session.release(consumer);if(source!=null)source.release(consumer);if(destination!=null)destination.release(consumer);if(project!=null)project.close();}
84
+ }
85
+ public static void main(String[] args){var output=System.out;System.setOut(System.err);
86
+ try{Application.initializeApplication(new GhidraApplicationLayout(),new HeadlessGhidraApplicationConfiguration());JsonObject p=JsonParser.parseString(Files.readString(Path.of(args[0]))).getAsJsonObject();Object value=run(p);
87
+ InsightNativeReceipt.write(p,value);
88
+ output.println(new GsonBuilder().serializeNulls().create().toJson(Map.of("value",value)));System.exit(0);
89
+ }catch(Throwable failure){failure.printStackTrace(System.err);output.println("{\"error\":\"native_version_tracking_failed\"}");System.exit(1);}
90
+ }
91
+ }
@@ -0,0 +1,112 @@
1
+ # Selected native SDK workflows
2
+
3
+ These commands extend the pinned MCP catalog. All program calls select both `--session` and
4
+ `--target`; API callers use the same Factory command paths. Use generated help for flags.
5
+ Programs remain private until explicit `ghidra merge`. Every new program edit is a complex
6
+ change: saved-state verification is required, and a diverged shared base retains a conflict.
7
+ `refresh` does not silently overwrite either complex version. Program edits require Destructive
8
+ permission. File/database creation and VT decisions require Update; reads require ReadOnly.
9
+
10
+ ## Compare and edit a private program
11
+
12
+ `ghidra changes compare --other-target <id> --other-session <id>` compares immutable saved
13
+ contexts with native ProgramDiff. Choose `--categories '["bytes","comments","symbols"]'`;
14
+ other supported categories are code_units, functions, references, context and properties.
15
+ Results retain address ranges and completion flags. This does not run a universal DB merger.
16
+
17
+ `ghidra memory patch-bytes --address 0x180000000 --expect-hex 9090 --bytes-hex 9091`
18
+ requires equal lengths of 1–4096 bytes and clears overlapping listing. `assembly preview` takes
19
+ `--instructions '["NOP"]'`; `assembly apply` also requires exact `--expect-hex`. Assembly uses
20
+ the selected program's language and processor context. It does not rebuild executable files.
21
+
22
+ `memory block-state --name <block>` returns current layout and a full digest for initialized
23
+ blocks up to 16 MiB. `split-block` and `move-block` require that digest, size and original start;
24
+ `join-blocks` requires both sizes/digests and compatible adjacent blocks. No uninitialized or
25
+ oversized-block edit is implied. `processor-context read|set|clear` addresses a named register
26
+ and a range of at most 65536 bytes. Read distinguishes explicit state from compiler defaults.
27
+
28
+ `namespaces list|create|update|delete` supports ordinary namespaces/classes; deletion requires
29
+ an empty container. `externals` supplies create/update/delete-library and create/update/delete-location.
30
+ Deletion refuses retained dependencies. It does not recursively erase referenced program state.
31
+
32
+ `decompiler ir --address <function> --simplification normalize --max-ops 1000` returns typed SSA
33
+ operations, varnodes, address spaces, sizes and block IDs. Options are known Boolean keys:
34
+ eliminate_unreachable, respect_read_only, infer_constant_pointers, split_structures, split_arrays.
35
+ Native timeout is 1–120 seconds. `completed` describes decompilation; `ir_complete` separately
36
+ reports a truncated operation list. The result is a bounded view, not a serialized HighFunction graph.
37
+
38
+ ## Loader, exporter and type archives
39
+
40
+ `ghidra formats loaders --input <file>` consumes immutable bytes and lists actual installed
41
+ loader IDs, supported language/compiler specifications and native option definitions.
42
+ `ghidra open <file> --session new --loader BinaryLoader --language x86:LE:64:default
43
+ --loader-options '{"Base Address":"0x10000","File Offset":"0x2","Length":"0x6"}'`
44
+ selects a loader explicitly. Primitive options, native address and HexLong options are converted
45
+ and validated; unsupported complex option classes fail explicitly. One program belongs to a target.
46
+
47
+ `formats exporters` lists compatible native exporters and their options. `formats export
48
+ --exporter 'Raw Bytes' --ranges '[{"start":"0x10001","end":"0x10003"}]'
49
+ --output-directory <new-directory>` requires a format supporting restricted address sets.
50
+ All output/companion files are hashed and retained. Existing destinations are not overwritten.
51
+ Native completion receipts permit a repeated request ID to recover completed bytes without
52
+ another export. An incomplete/uncertain export cannot be replayed blindly.
53
+
54
+ `types define-layout --path /Owned/Record --packing 1 --fields
55
+ '[{"name":"flags","type":"uint","bit_size":3},{"name":"value","type":"ushort"}]'`
56
+ uses native structures and bitfields. Packing/alignment accept -1, zero or powers of two through
57
+ 64; explicit byte offsets require packing -1. There are at most 256 fields. `types layout` returns
58
+ native source archive/universal IDs and bounded field/setting records. `types set-settings --path
59
+ /Owned/Number --settings '{"Format":1}'` uses actual definition names and native enum choices;
60
+ null clears an explicit setting. Inventory the type first; definitions differ between native types.
61
+
62
+ `types archive-export --types '["/Owned/Record"]' --output-directory <new-directory>` creates a
63
+ real GDT. `types archive-import --input <file.gdt> --types '["/Owned/Record"]'` consumes an
64
+ immutable archive up to 64 MiB and checks saved layout/source identity. A conflicting existing
65
+ layout is rejected. `types create-struct --replace-placeholder` replaces only an empty structure.
66
+
67
+ ## Version porting and library identification
68
+
69
+ `version-tracking create --source-target <id> --source-session <id>` creates a persistent native
70
+ VT session inside the destination research scope. The returned tracking ID owns the exact saved
71
+ source/destination snapshots. `list`, `matches --tracking <id>` and `markup --tracking <id>
72
+ --source-address <address> --destination-address <address>` retain native correlator/score/markup
73
+ provenance. Exact function bytes and duplicate instruction correlators are selected; instruction
74
+ matching can ignore operands. Ambiguity is retained. Unmatched means no association in these
75
+ selected policies, not proof of no equivalent function. Inputs are limited to 10000 functions;
76
+ correlation has a 60-second monitor budget, match pages have at most 1000 rows.
77
+
78
+ `decide` explicitly accepts, rejects or resets an association; native one-to-one constraints apply.
79
+ Decisions survive owner replacement. Markup is inspected before ordinary facade edits transfer
80
+ selected changes. No automatic markup application/publication is performed. Context-changed flags
81
+ warn when current programs differ from the VT snapshots. Unknown decisions block further decisions
82
+ on that object until reconciliation; the same request ID never blindly repeats a native effect.
83
+
84
+ `libraries build-fid --library <name> --library-version <version> --output-directory <new-directory>`
85
+ creates a genuine native FID database. `libraries fid-query --database <file.fidb> --function <entry>
86
+ --threshold 1` reports native scores, library/version/variant, database digest and no-match semantics.
87
+ `build-bsim --library <name> --output-directory <new-directory>` creates embedded native BSim/H2;
88
+ `bsim-query --database <database.mv.db> --function <entry> --similarity 0.9` reports signature version,
89
+ similarity and significance. Required SDK components are supplied by the pinned Ghidra install;
90
+ there is no external database service to provision for these local workflows. Inputs are bounded to
91
+ 1000 functions, output is bounded, and scores are not calibrated probabilities. Query databases
92
+ are consumed into private immutable storage; the caller's file is unchanged.
93
+
94
+ ## Live target and retained trace
95
+
96
+ `debugger write-memory --address <dynamic-address> --expect-hex <bytes> --bytes-hex <bytes>`
97
+ and `write-register --register <name> --expect-value <hex> --value <hex>` require a stopped,
98
+ selected live target at its current live time. They refresh from the target before and after the
99
+ write. Preconditions are read-before-write checks, not atomic compare-exchange. Register width
100
+ is exact. These live effects do not enter program publication. A lost write response remains
101
+ uncertain and is never replayed, even when trace bytes subsequently show the requested value.
102
+ The owned copy of Ghidra 12.1.2's Python adapter corrects its SBProcess memory-write call; the
103
+ installed SDK/wheel remains unchanged and runtime fingerprint checks still apply.
104
+
105
+ `debugger timeline --limit 100` returns snapshot keys/times/schedules and continuation.
106
+ `objects --limit 100 --max-scan 10000` returns persistent object IDs, schema, path and lifespan
107
+ at a selected snap. `object-values --object-id <id> --key <exact-key>` reads one key, optionally
108
+ over at most 1000 snapshot keys. Values preserve address/object-reference types and 64-bit integers;
109
+ unknown Java value kinds are explicit. Object iteration is budgeted, paths replace the owned
110
+ staging root with `@target`, and IDs remain scoped to the selected trace. `schedule --schedule <time>`
111
+ parses/validates native schedules and reports `executed: false`. It does not execute emulation.
112
+ Saved trace queries remain available after disconnect; live execution is never resurrected.