Store them with the REPL cell and the address insight, as a JSON
column each, and bind the decode view to them. Older rows and wire
cells without the field decode as none.
Add a machine abstraction to LumaCore, with QEMU as its first
backend: templates mirroring frida-core's test guests, a QMP client,
and a display that maps QEMU's surface over D-Bus rather than
copying frames.
Leave readiness to the user: drive the guest, mark it ready, and come
back to that snapshot later. Keep the machine in the project, so
booting it again resumes where it was marked. Hand the stub to
addBareboneDevice, so it joins the picker beside the other devices.
Show the display while booting, and afterwards in a Machines panel,
which the side panel now models as an enum to share its slot.
A comment is a last resort, and these were not one: section banners,
doc headers naming what the declaration below already names, and
notes saying in prose what the next line says in code.
What is left is what the code cannot show -- an ordering that matters,
a platform quirk, why the obvious way fails.
A compile error reads in words with a dot at the failing spot.
Evaluate splits from evaluate-and-inspect, the snippet spins while
it runs, and print it, format and browse sit on GT's keys.
Open any class in a browser folded into the inspector: its methods,
class-side ones too, a method saved from the editor, a sent method
opened in place, and a wrench offering fixes for an undeclared name.
Each value opens in a pane on the gutter, beside the page rather
than under it. Panes drag, close and walk, a crowded tab strip
scrolls, and the split and its divider stay where they were put.
Boot the VM against a bundled image, evaluate from a notebook cell
or a playground snippet, and keep what each evaluation produced.
The image is staged on every build; the framework release is pinned.
Give instruments that have hooks a disclosure chevron and persist
the per-instrument collapsed state in the session UI state. Toggle
the module and thread groups on a full header-row click. Pad the
module view so its tabs clear the top-left edge.
Drop the modules/threads tabs; show both as collapsible sidebar
groups mirroring the hooks UX: a handful of top picks plus a
browse popover for the rest. Default threads to collapsed.
Revert the eager re-analysis of persisted modules on attach; keep
disassembly analysis lazy and add a per-module Analyze action and
status indicator instead.
Share one sidebar-row and one browser-popover component across
hooks, modules, and threads in both the SwiftUI and GTK frontends.
Create-table-if-not-exists never alters a table that already
exists, so a shipped database lacks the REPL, seek, and styled
notebook columns and fails on save. Add each one if missing.
Carry the styled text on NotebookEntry alongside the plain
details, capture it when pinning an r2 cell, and render it with
color in both notebook frontends.
Snapshot the r2 seek after each r2 eval, storing it on
SessionUIState as an AddressAnchor so it re-bases across runs.
Resolve and restore it during the session warm-up, which now
also runs when only a seek (no analysis) was saved.
Replay every module that has a persisted analysis row into the
fresh r2 core once modules are enumerated, restoring last
session's warm state without blocking attach. Replays are cheap
re-basing; modules lacking persisted analysis stay lazy.
Carry a display/insert split through completion so command
suggestions show r2's help text via the native secondary title,
and complete command arguments against eval vars and flagspaces
in addition to flags. Recall the language alongside the code when
cycling history, and keep r2 output's leading whitespace intact.
Persist the REPL draft and active language per session in
SessionUIState so they survive reopening the project. Defer the
input field's first-responder change out of the view update.
Make the REPL prompt a clickable mode toggle between JavaScript
and r2; ":", ":r2" and ":js" switch from the keyboard. Carry the
language on REPLCell and REPLResult, route r2 input through the
session disassembler, and render output as a JSInspectValue tree
for JSON commands or ANSI-parsed styled text otherwise.
Console widgets get an appendImage / respond.image API on the
agent. read_widget_state surfaces image entries as attachments
scaled to at most max_dimension pixels (default 1568) on the
longest edge. Anthropic nests image blocks inside tool_result;
OpenAI gets a synthetic follow-up user message. The host scales
via CoreImage on macOS and GdkPixbuf on GTK.
Engine.symbolDisplay returns a richer SymbolDisplay (overlay,
source, raw, primary) and persists every live symbolicate
result into module_symbol (sessionID, modulePath, function-entry
offset → name). Offline rendering falls through to that table
first, then to enclosingFunction from the persisted block
ranges, then to module+offset. ProcessNode delegates trace
block annotation back through this path via
onResolveBlockNames so the same names show up on detached
traces.
Split ModuleAnalysis persistence into module_analysis,
module_analysis_function and module_analysis_block tables.
Harvest blocks via afbj and use them to answer enclosingFunction
offline. R2Hex accepts both 0x-prefixed strings and raw integers,
since cfg.json.num=hex doesn't cover afbj. When the cache is
present but blocks are missing, replay anyway unless we're online
and can re-analyze.
Replace the auto-derived title with userTitle (nil = auto)
and a parentInsightID link. Display name is userTitle, else
<parent.title>+0x<delta>, else sub_<offset> if at a function
start, else module+offset. Sidebar context menu Rename… and
an LLM rename_insight tool both go through Engine.renameInsight,
which broadcasts via the existing updateInsight op and pushes
the new name into r2 as a flag (plus afn at function starts).
getOrCreateInsight kicks off a background pass that links a
new insight to its enclosing function-start insight, and
adopts siblings inside its region when the new insight is
itself the function start. The same adoption runs when a
fresh ModuleAnalysis newly reveals a function start under an
existing insight.
Identify a process instance by a deterministic identity returned
from the agent (per-OS, with a Process.mainModule + pid fallback)
and stash it on ProcessSession.processInfo. Rekey memory_page
into memory_page_module (module UUID + offset) so cached bytes
survive ASLR, and memory_page_anon (process identity + address)
for JIT / heap regions that only survive within one process
instance. Purge stale anon pages on every fresh attach.
Add updateProcessInfo, updateModuleAnalysis and updateInsight
SessionOps plus a side-channel publish-memory-page op and a
+fetch-memory-page request. The driver write-throughs every
page it fetches; non-drivers pull on miss and persist locally.
Module-analysis rows flow the same way so non-drivers can key
their cache by module UUID. The lab-join snapshot now carries
processInfo and module_analyses too.
Refresh actions on the insight detail view: Reread Bytes drops
the pages overlapping the visible range; Reanalyze Module drops
the module's analysis and pages, re-running r2 on next access.
The macOS detail view now reads the insight live from
insightsBySession via observation; GTK forwards .insightUpdated
into the detail and reschedules a refresh when the resolved
address changes.
Decouple Disassembler from ProcessNode via MemoryReader and
ModuleIntrospector protocols, and add a per-session page store
that r2 reads flow through. The disassembler now survives
detach; insights render from cache, with live reads
re-populating it on reattach.
Persist per-session module analysis (executable ranges + known
functions, keyed by module name + LC_UUID) so on-demand
analysis runs once per binary and rebases on reattach. The
agent gains enumerateModuleRanges and getModuleIdentity; the
store gains a module_analysis table.
Disassembler queries $FB/$FE for the request address; when it
hits a function start, fetch via pDJ bounded to the function's
byte range. Otherwise fall back to pdJ N. Filter pdJ
decoration entries (xrefs, fcn headers, var summaries) that
share the addr of an instruction.
DisassemblyPage carries a scope (.function/.span) so the
SwiftUI and GTK insight views can disable loadMore when the
page is function-bounded. The disassemble AI tool surfaces the
scope too, so the model knows whether it has the whole
function or just an N-instruction span.
Go-to-Function-Start now opens (or selects) the insight rooted
at the function start, which then renders via the bounded
path.
Engine: deleteAddressNoteMessage drops the row from the store
and enqueues an address_note_message_remove op. Collab apply
mirrors the existing edit path. AddressNoteChange grows a
messageRemoved case.
GTK: right-click on a user message offers Edit and Delete.
Edit swaps the body label for an inline editor; Save commits
via editUserMessage. Delete confirms first, then removes the
row from the popover and via the engine.
requestAIReply runs against an ambient mission per session,
billing token usage and recording the call as a MissionAction
so the user can review the chain in the mission view. System
prompt seeds the model with disasm + pdc around the anchor.
Threads anchored to an address, each with a chain of messages
(user/assistant/system). Notes carry editors[], messages carry
a single immutable author. rebuildAddressAnnotations folds note
counts into the gutter so the UI can render breadcrumbs.
- Store source files as named CustomInstrumentFile rows per def
- Render sidebar with disclosure groups
- Drive editor via Monaco project models with activePath
- Validate Add/Rename/Delete dialogs and submit on Enter
- Fork installed hook packs into editable custom instruments
- Reload instances on def change; clear stale compat reasons live
- Focus editor on selection (native + Monaco)
Stamp PRAGMA user_version on fresh databases; on subsequent
opens, throw if the stored version does not match the current
constant. Pre-launch this means bumping the version when
schema changes and recreating dev projects, instead of
crashing inside GRDB on a missing column.
Each instrument carries an InstrumentCompatibility (platforms,
osIDs, archs; nil set means any). Hook pack manifests and custom
defs declare it; tracer and codeshare default to universal.
System parameters are queried lazily via SystemParametersCache,
keyed by device id and invalidated when devices disappear.
Restoring an incompatible instrument now marks it with a reason
on its InstrumentRef instead of attaching.
EngineRegistry caches one Engine per working-project URL.
MainWindowView.init does a pure lookup, so SwiftUI's
view-tree re-evaluations cannot open extra ProjectStores
that race the live one's BEGIN IMMEDIATE and crash via
try!.
UI-state persistence (collapse flag, sidebar selection,
session detail section, etc.) lives on Engine for GTK to
adopt. SQLite has a 5s busy timeout. Engine open failures
surface as an error view.
TargetPicker is host @State injected via environment.
Global instrument-UI defaults register at app startup;
per-engine instrument and local-notifier hookups live in
Engine extensions.
Mission defaults (provider, model, budgets, thinking, effort)
and provider base URLs move from the project document to
LumaAppState. Opening a project on another machine no longer
drags in someone else's model choice or localhost URL.
The dialog loads models asynchronously, shows loading and
error states inside a stable "Model" row, debounces typed
API keys with a length floor, and persists the key on the
first successful fetch.
Base URL field surfaces only for providers that opt in via
capability (local). Reasoning effort dropdown surfaces for
Claude Code; the extended-thinking toggle is hidden when
the provider doesn't support it.
Fetch models at runtime where supported (OpenAI, local
OpenAI-compatible). Static lists for Anthropic and Claude
Code; the OpenAI list was already drifting.
Add reasoningEffortOptions capability. Claude Code exposes
[auto, low, medium, high, xhigh, max] and routes the choice
to its --effort flag. LLMTurnRequest, Mission, the executor
and the mission schema all carry reasoningEffort end to end.
suggestedModels is now async/throws and takes apiKey and
baseURL so dynamic providers can authenticate and target a
caller-supplied endpoint.
InstrumentWidget (graph or list, with a persistence enum) lives
on CustomInstrumentDef and HookPackManifest. Runtime widget data
serializes through a widget_state table keyed by instance id and
widget id; the def itself round-trips a widgets_json column on
custom_instrument_def.
Whole-row saves let any writer clobber fields it didn't
own. The executor saved its in-memory Mission whenever the
status moved, wiping the title or pending user text that a
parallel task had just written. Add a closure-based
ProjectStore.updateMission(id:) that fetches, mutates, and
saves inside one db.write transaction. The executor now
writes only status and token counters; the title pass
writes title; appendMissionUserMessage writes
pendingUserText; the drain reads-and-resets atomically.
A new title column carries a short label generated in
parallel with mission start. Each LLMProviderDescriptor
declares a summarizationModelID so the pass runs on the
mission's own backend (Haiku for Anthropic and Claude
Code, gpt-5-mini for OpenAI, llama3.2:3b locally). The
instruction sits in the user message to dodge Claude
Code's prompt layering and asks for two to four punchy
words. Sidebar shows title or goal; header keeps the
full goal under the title.
Reorder the provider registry so Claude Code (subprocess)
shows up at the top of the New Mission picker, and update
the ProjectUIState defaults to match — fresh projects pick
"claude-code" + "default" instead of Anthropic Sonnet.
Provider, model, token budgets, and thinking config persist
on the project's UI state, so opening New Mission a second
time pre-fills the previous choices instead of resetting.
The API key continues to live in the credential store.
A pending_user_text column on mission stores any text the
user types into the new MissionInputBar. The executor drains
it at the start of each iteration, folding it into the next
synthesized user turn. Send queues; Send Now interrupts the
current turn (when running) before resuming.
LLM agent for user-stated RE goals: propose-and-approve over the
existing RPC, evidence-grounded findings, collab via the op-outbox
pattern. Anthropic / OpenAI / local-OAI providers. r2ai used for
explain_function when the plugin is loaded; falls back to the
mission's own provider otherwise.
Engine drives device.enableSpawnGating() per armed-session intent;
matched spawns re-attach via the existing attach path and misses
resume immediately. Engagement intent is in-memory only so launches
don't auto-engage gating for sessions armed in a previous run.
ProcessSession gains armingState and lastArmPattern, mirrored in
the SQLite schema. RuntimeEvent.Source.spawnGating carries the
captured/released outcome with wire encoding.
Lab protocol adds update-arming as a SessionOp variant; armed-only
sessions announce themselves with pid 0 and phase detached so peers
can see them before any capture.
Engine.onUserNotification surfaces gating-enable failures using the
clean Frida.Error message.