Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 53 additions & 5 deletions docs/mobilecore-dual-app-qa.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ MobileCode is the control center. It owns model routing, user consent, Phone Use
- memory/storage preflight and typed failure code;
- installed models, device recommendations, and decode/latency metrics;
- model load, unload, and switch controls using public `model_id` and optional
public `projector_id` only.
public `projector_id` only;
- typed verified-Omni status plus a dedicated load control that refuses partial
or unverified pairs and rechecks the active `/health` runtime, artifact
verification and pinned digests, and requested image/audio capability after
loading.

The v2 client reads health before and after the models/metrics/recommendations
bundle. If MobileCore changes the active runtime during that window, MobileCode
Expand All @@ -26,7 +30,20 @@ IDs containing path separators or control characters are rejected before a
request is sent, and a switch is successful only when `/health` reports the
exact requested public model ID.

The TuiMa control sheet exposes that state inside MobileCode. Image and audio entry points are capability-gated. Selected media is held only in memory, sent only to the local MobileCore endpoint, removed after the request, and excluded from saved chat turns and ActionEvidence. A local-only media request fails closed instead of falling back to cloud inference.
The TuiMa control sheet exposes that state inside MobileCode. When MobileCore
reports a complete verified Omni pair that is not active, the sheet offers an
explicit local activation control. Image and audio entry points remain hidden
until the loaded runtime's `/health` response advertises the corresponding
capability. If an already-selected attachment survives an external model
change, MobileCode may reactivate that verified pair, then repeats the runtime
capability check before sending any media. Selected media is held only in
memory, sent only to the local MobileCore endpoint, removed after the request,
and excluded from saved chat turns and ActionEvidence. A local-only media
request fails closed instead of falling back to cloud inference.

This control path proves protocol enforcement and fail-closed selection; it is
not evidence that the large Qwen2.5-Omni artifacts have run successfully on a
physical phone. That remains a separate gate below.

Adaptive routing currently applies these rules:

Expand Down Expand Up @@ -191,6 +208,31 @@ request. Logcat contained no MobileCode crash, ANR, OOM, or SIGABRT marker. The
local APK is Android Debug-signed and is therefore QA evidence, not the
production-signed GitHub Release asset.

### Verified Omni activation control refresh

On 2026-08-07, a clean `pureDebug` MobileCode v0.1.77 (`67`) APK containing
the typed Omni status and dedicated verified-pair activation path was built,
installed, and cold-launched on the Android 16 ARM64 emulator. The APK SHA-256
was `3833b98f95c113454658d471e9824bb66d2f83c9117c03c48d192a86a47a3c11`.
Cold launch completed in 2,694 ms, the app remained resumed, and filtered logcat
contained no MobileCode fatal exception, ANR, OOM, or SIGABRT marker.

MobileCore 0.1.4-rc4 exposed protocol v2 and loaded the real local
`qwen2.5-0.5b-instruct-q4_k_m` runtime by public model ID. After a visible
service restart and control-sheet retry, MobileCode displayed the exact active
model, `llama.cpp`, CPU backend, `Q4_K_M`, 462 MB peak runtime memory, ready
preflight, three installed models, and explicit `image no · audio no`
capabilities. The stopped-service interval was surfaced as the typed
`service_unavailable` state instead of stale controls.

The same emulator lacked both pinned Qwen2.5-Omni artifacts, and its preflight
reported insufficient memory and storage for that pair. MobileCode therefore
did not render the verified-Omni activation control and did not expose image or
audio attachment entry points. This proves real dual-app capability gating and
the unverified/insufficient-resource refusal path; the successful activation
path remains contract-tested until a capable physical device with both verified
artifacts is available.

### Withdrawn v0.1.76 Android artifact

The official APK described below was withdrawn on 2026-08-07 after post-build
Expand Down Expand Up @@ -354,13 +396,15 @@ Raw screenshots and sanitized logcat remain under the ignored `.qa-artifacts/` d

## Verification

- The complete MobileCode Flutter suite passed 571 tests after the Client v2,
- The complete MobileCode Flutter suite passed 577 tests after the Client v2,
adaptive-routing, pressure-switch, proactive-offline, one-task cloud approval,
protocol-handshake, cancellation, and bounded-output follow-ups.
- The focused MobileCore client/adaptive-policy/approval suite passed 37 tests
- The focused MobileCore client/adaptive-policy/approval suite passed 43 tests
covering coherent
runtime snapshots, exact switch confirmation, public projector IDs,
path-like ID rejection, projector metadata, and image-capability parsing.
path-like ID rejection, projector metadata, image-capability parsing,
verified-Omni activation, partial-pair rejection, post-load pinned-digest
matching, and audio capability confirmation.
- The focused adaptive-policy coverage includes privacy/offline
fail-closed routing, cloud-consent gating, constrained context/model choice,
one-task approval expiry semantics, and multimodal capability retention under
Expand All @@ -373,6 +417,10 @@ Raw screenshots and sanitized logcat remain under the ignored `.qa-artifacts/` d
- A clean local Android arm64 `pureRelease` build passed and its manifest version was
verified as `0.1.76+66`; this local build is validation evidence only and is
not the stable-signed GitHub Release asset.
- A later `pureDebug` v0.1.77 (`67`) APK with verified-Omni activation control
also built, installed, cold-launched, and rendered the real MobileCore control
state on the Android 16 ARM64 emulator. It is debug-signed QA evidence, not a
release asset.
- The real emulator UI displayed the one-task cloud approval card before a
complex cloud request. Declining routed to MobileCore, and the subsequent
local timeout remained fail-closed instead of opening the cloud provider.
Expand Down
89 changes: 88 additions & 1 deletion mobile_agent/lib/screens/home_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14065,6 +14065,9 @@ class _ChatPanelState extends State<_ChatPanel> {

Future<_MobileCoreControlData> _loadMobileCoreControlData() async {
final snapshot = await _tuimaProviderService.runtimeSnapshot();
final omniStatus = await _tuimaProviderService
.omniStatus()
.catchError((_) => const MobileCoreOmniStatus());
if (mounted) setState(() => _tuimaHealth = snapshot.health);
if (snapshot.health.state == TuimaConnectionState.unavailable) {
throw MobileCoreProviderException(
Expand All @@ -14078,6 +14081,7 @@ class _ChatPanelState extends State<_ChatPanel> {
models: snapshot.models,
metrics: snapshot.metrics,
recommendations: snapshot.recommendations,
omniStatus: omniStatus,
);
}

Expand All @@ -14087,6 +14091,7 @@ class _ChatPanelState extends State<_ChatPanel> {
required DateTime startedAt,
required bool success,
Object? error,
Map<String, Object?> safeMetadata = const {},
}) async {
final safeFailure = error is MobileCoreProviderException
? error.code
Expand All @@ -14111,6 +14116,7 @@ class _ChatPanelState extends State<_ChatPanel> {
'inferenceLocation': 'on_device',
'operation': operation,
'modelId': modelId,
...safeMetadata,
'redactionApplied': true,
},
));
Expand Down Expand Up @@ -14182,6 +14188,41 @@ class _ChatPanelState extends State<_ChatPanel> {
}
}

Future<TuimaHealth> _activateVerifiedOmni({
MobileCoreAttachmentKind? requiredCapability,
int contextLength = 4096,
}) async {
final startedAt = DateTime.now();
try {
final result = await _tuimaProviderService.loadVerifiedOmni(
requiredCapability: requiredCapability,
contextLength: contextLength,
);
if (mounted) setState(() => _tuimaHealth = result.health);
await _recordMobileCoreControl(
operation: requiredCapability == null
? 'activate_verified_omni'
: 'activate_verified_omni_for_${requiredCapability.name}',
modelId: result.status.modelId,
startedAt: startedAt,
success: true,
safeMetadata: result.status.evidenceMetadata,
);
return result.health;
} on Object catch (error) {
await _recordMobileCoreControl(
operation: requiredCapability == null
? 'activate_verified_omni'
: 'activate_verified_omni_for_${requiredCapability.name}',
modelId: 'verified-omni-pair',
startedAt: startedAt,
success: false,
error: error,
);
rethrow;
}
}

Future<void> _openMobileCoreControlSheet() async {
var loader = _loadMobileCoreControlData();
await showModalBottomSheet<void>(
Expand Down Expand Up @@ -14303,6 +14344,26 @@ class _ChatPanelState extends State<_ChatPanel> {
label: const Text('Unload active model'),
),
],
if (data.omniStatus.loadable &&
!(data.health.canInfer &&
data.health.mainArtifact.verified &&
data.health.projectorArtifact.verified &&
data.health.runtime.contains('libmtmd'))) ...[
const SizedBox(height: 10),
Text(
'A verified local Omni pair is installed. Media controls remain hidden until the runtime capability check passes.',
style:
const TextStyle(color: _muted, fontSize: 12),
),
const SizedBox(height: 8),
FilledButton.icon(
onPressed: () => unawaited(run(() async {
await _activateVerifiedOmni();
})),
icon: const Icon(Icons.auto_awesome_outlined),
label: const Text('Activate verified Omni'),
),
],
],
),
),
Expand Down Expand Up @@ -14422,9 +14483,33 @@ class _ChatPanelState extends State<_ChatPanel> {
measuredDecodeTokensPerSecond: measuredSpeedFor(health),
);
var currentHealth = health;
if (attachment != null &&
!currentHealth.capabilities.supports(attachment.kind)) {
final omniStatus = await _tuimaProviderService
.omniStatus()
.catchError((_) => const MobileCoreOmniStatus());
if (MobileCoreAdaptivePolicy.shouldActivateVerifiedOmni(
health: currentHealth,
omniStatus: omniStatus,
attachmentKind: attachment.kind,
)) {
currentHealth = await _activateVerifiedOmni(
requiredCapability: attachment.kind,
contextLength: decision.contextLength,
);
decision = MobileCoreAdaptivePolicy.decideForTask(
health: currentHealth,
recommendations: recommendations,
telemetry: telemetry,
task: taskSignals,
attachmentKind: attachment.kind,
measuredDecodeTokensPerSecond: measuredSpeedFor(currentHealth),
);
}
}
if (MobileCoreAdaptivePolicy.shouldSwitchToRecommendedModel(
decision: decision,
activeModelId: health.activeModel,
activeModelId: currentHealth.activeModel,
attachmentKind: attachment?.kind,
)) {
currentHealth = await _tuimaProviderService.loadModel(
Expand Down Expand Up @@ -21093,12 +21178,14 @@ class _MobileCoreControlData {
this.models = const [],
this.metrics = const MobileCoreMetrics(),
this.recommendations = const MobileCoreRecommendations(),
this.omniStatus = const MobileCoreOmniStatus(),
});

final TuimaHealth health;
final List<MobileCoreModel> models;
final MobileCoreMetrics metrics;
final MobileCoreRecommendations recommendations;
final MobileCoreOmniStatus omniStatus;
}

class _MobileCoreRuntimeDecision {
Expand Down
18 changes: 18 additions & 0 deletions mobile_agent/lib/services/mobilecore_adaptive_policy.dart
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,24 @@ class MobileCoreAdaptivePolicy {
recommended != activeModelId;
}

/// A selected local attachment may reactivate the pinned Omni runtime when
/// another model became active after the attachment was chosen. The status
/// only proves artifact readiness; the client must still verify the active
/// `/health` capability after loading before sending media bytes.
static bool shouldActivateVerifiedOmni({
required TuimaHealth health,
required MobileCoreOmniStatus omniStatus,
required MobileCoreAttachmentKind attachmentKind,
}) {
final pinnedOmniActive = health.canInfer &&
health.mainArtifact.verified &&
health.projectorArtifact.verified &&
health.runtime.contains('libmtmd');
return omniStatus.loadable &&
!pinnedOmniActive &&
!health.capabilities.supports(attachmentKind);
}

static MobileCorePolicyDecision decide({
required TuimaHealth health,
required MobileCoreRecommendations recommendations,
Expand Down
Loading
Loading