Repository navigation
feat!: per-call observe level; no state in observations - #62
Draft
cryptotavares wants to merge 2 commits into
Draft
cryptotavares wants to merge 2 commits into
cryptotavares wants to merge 2 commits into
Conversation
Callers can now choose the observation they get back, and observations no
longer carry app state (which was wrong on mobile and slow on browser).
- Every tool input accepts `observe: 'none' | 'diff' | 'full'` (shared
observeLevelSchema, added to toolSchemas with withObserve()). The daemon
reads it and strips it before the tool runs. Defaults: diff for mutating
tools and run_steps, none for readonly/discovery/cdp. `full` returns the
whole (option-collapsed) tree without diffing and becomes the new diff
baseline.
- CLI: global `--observe <none|diff|full>` flag, added to POST /tool/*,
/launch and /cleanup bodies.
- run_steps: `includeObservations` is removed. The batch observation uses the
top-level `observe`, else the last executed step's `observe`, else diff.
- Observations no longer include `state`. The getExtensionState/getAppState
calls and the 500 ms unknown-screen recheck loop are gone from the
observation path. Use get_state for state.
- The mobile driver's getAppState reports only what it knows:
{ isLoaded, extensionId, source: 'stub' }. `source: 'stub'` marks partial
state; fields not read from the app are omitted.
BREAKING CHANGE: `run_steps.includeObservations` is removed and now fails
validation with a hint to use `observe` (the 'failures' mode is gone).
MobilePlatformDriver.getAppState() now returns only
{ isLoaded, extensionId, source: 'stub' } (no isUnlocked, currentScreen,
currentUrl or null wallet fields). Observations no
longer contain `state`; StepRecordObservation has no `state` field and
createDefaultObservation(testIds, nodes) drops its state parameter.
ExtensionState fields other than isLoaded and extensionId are optional and a
`source?: 'stub'` field is added. shouldCollectObservations and
shouldIncludeObservationsInResponse are replaced by resolveObserveLevel.
toolSchemas entries are now intersections that include `observe`.
cryptotavares
added this pull request to stack #63
October 23, 2026 20:59
Collaborator
Author
|
Fixed CodeQL js/unvalidated-dynamic-method-call in 1bbfc70 (check the registry entry is a function before calling it). |
cryptotavares
force-pushed
the
cryptotavares/mmcli-p1-06-observe-level-no-state
branch
from
October 24, 2026 01:32
1bbfc70 to
2ee766a
Compare
CodeQL (js/unvalidated-dynamic-method-call) flagged the tool call because the tool name comes from the request path. Look the tool up only when the registry has the name, and check the entry is a function.
cryptotavares
force-pushed
the
cryptotavares/mmcli-p1-06-observe-level-no-state
branch
from
October 24, 2026 01:35
2ee766a to
9f88039
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Two observation problems, fixed together because they touch the same code (tickets P1-06 and P1-07):
run_stepshad its ownincludeObservationsswitch.state, which was wrong on mobile and slow on browser. On mobile it was a stub with invented values (isUnlocked: true,currentScreen: "unknown", nulls). On browser it cost agetExtensionStatecall plus a 500 ms re-check loop whenever the screen wasunknown.Changes:
observe: 'none' | 'diff' | 'full'on every tool input. It is one sharedobserveLevelSchema, added to everytoolSchemasentry withwithObserve(). The daemon reads it and strips it (omitObserve) before the tool runs, so consumers'sessionManager.launch()never sees it.difffor mutating tools andrun_steps,nonefor readonly, discovery andcdp.fullreturns the whole (option-collapsed) tree without diffing, and becomes the new diff baseline.--observe <level>flag, accepted anywhere on the command line. It is added to POST/tool/*,/launchand/cleanupbodies.run_steps:includeObservationsis removed and now fails validation with a hint to useobserve. The single batch observation uses the top-levelobserve, else the last executed step'sobserve, elsediff.statein observations.getExtensionState/getAppStateand the 500 ms re-check are gone from the observation path. Useget_statefor state.getAppState()returns only what it knows:{ isLoaded, extensionId, source: 'stub' }.ExtensionStatefields other thanisLoadedandextensionIdare optional, andsource: 'stub'marks partial state.cdp --observe full, which may change the screen, also waits.Part of the mm CLI v2 effort: tickets
P1-06+P1-07(Phase 1), combined into one PR.Stacked on #61 (review that first).
Breaking changes
run_steps.includeObservationsis removed. Sending it is a validation error (includeObservations was removed; use observe). The'failures'mode is gone.state:StepRecordObservationhas nostatefield;createDefaultObservation(testIds, nodes)drops its first parameter.ExtensionState:currentUrl,isUnlocked,currentScreen,accountAddress,networkName,chainIdandbalanceare optional. New field:source?: 'stub'.MobilePlatformDriver.getAppState()returns{ isLoaded, extensionId, source: 'stub' }only.shouldCollectObservationsandshouldIncludeObservationsInResponseare replaced byresolveObserveLevel(category, input, toolResult).toolSchemasentries are now intersections that includeobserve. The individual*InputSchemaexports are unchanged.Consumer impact
test/e2e/playwright/llm-workflow/ExtensionStatewith required fields still type-checks against the looser core type, and nothing readsobservations.stateorincludeObservations. Jest: 144 passed.tests/llm-workflow/isUnlocked/currentScreen/null fields: with no session it reports{isLoaded:false, extensionId:'', source:'stub'}, and it omitscurrentScreenwhen no known testId matches. Jest: 232 passed.cryptotavares/mm-cli-v2-mobile35fc72cc609Follow-up, not in this PR: on mobile, core
get_statecalls the driver directly, so the Mobile capability's screen detection only runs on browser. Phase 2mm state(P2-09) covers this.Proof
Tested with a yalc build of this branch in the Extension (e2e build, headless) and Mobile (dev client on iOS sim and Android emulator), on wallet home after unlock. Before = #61 tip; its proof is reused, except the Extension click bench, which was run on both builds back to back.
Same command at each level (after; bytes / totalMs):
nonedifffullclicka home tab)wait-for wallet-screen)click tab-bar-item-Wallet)click --observe noneobservations,observationMs0, on all 3 platformsget-state --observe fulltestIds+a11yonly: nostate, nodiffrun-steps, top-levelobserve: none/ last stepobserve: fulldiff(all 3 platforms)run-stepswithincludeObservationsincludeObservations was removed; use observeobservations.statein any of the 55 after outputsget-state, before → afterisUnlocked: true, currentScreen: "unknown", currentUrl: "", 4 × null→{isLoaded, extensionId, source: "stub"}observationMsp50 (bench, 5 runs, back to back)stateblock)click-unlock(unlock → home)observationMsunknown)The bench gain is small because on home the screen is already known, so the 500 ms re-check rarely ran. Most of the remaining ~550 ms is the animation wait plus the a11y snapshot. P1-10 targets the animation wait.
Transcripts (ext)
Unit tests
New or changed:
resolveObserveLevel: category defaults, an explicit level wins, an invalid level is ignored,run_stepslast executed step and failure fallback.clickwithnone;get_statewithfull/diff;cdpwithfullwaits for animations;observeis not passed to the handler;/cleanupbody.getExtensionStateduring observation (spy).getAppStateshape.withObserve/omitObserve,includeObservationsrejected, and the CLI--observeparsing and injection.