Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export default class EntryAbility extends UIAbility {

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
const scenarioId = want.parameters?.['openbitfunDesignPreview'];
// The device-model scenarios are one family: the prefix opens the design
// gallery and the `-dark` suffix locks the colour mode, so the list below
// does not have to grow by one id per state of that feature.
const deviceModelScenario: boolean = String(scenarioId || '').startsWith('device-model');
if (scenarioId === 'history-scroll' || scenarioId === 'durable-timeline' || scenarioId === 'composer-submit' || scenarioId === 'catalog-refresh' || scenarioId === 'catalog-refresh-dark' || scenarioId === 'model-role-selector' || scenarioId === 'model-role-selector-dark' || scenarioId === 'model-role-selector-wide' || scenarioId === 'subagent-process' || scenarioId === 'subagent-process-dark' || scenarioId === 'file-visibility' || scenarioId === 'file-visibility-dark' || scenarioId === 'immersive-bottom' || scenarioId === 'immersive-bottom-dark' || scenarioId === 'immersive-bottom-wide' || scenarioId === 'immersive-bottom-drawer' || scenarioId === 'immersive-bottom-drawer-dark' || scenarioId === 'immersive-bottom-settings' || scenarioId === 'immersive-bottom-settings-dark' || scenarioId === 'immersive-bottom-cover' || scenarioId === 'immersive-bottom-miniapps' || scenarioId === 'immersive-bottom-wide-shell' || scenarioId === 'immersive-bottom-tools' || scenarioId === 'immersive-bottom-tools-editor' || scenarioId === 'immersive-bottom-tools-file-action' || scenarioId === 'immersive-bottom-view-settings' || scenarioId === 'immersive-bottom-connect' || scenarioId === 'composer-overflow' || scenarioId === 'composer-overflow-empty' || scenarioId === 'composer-overflow-dark' || scenarioId === 'composer-overflow-wide' || scenarioId === 'composer-overflow-expanded' || scenarioId === 'composer-overflow-expanded-dark' || scenarioId === 'device-selector' || scenarioId === 'device-selector-dark' || scenarioId === 'welcome-home' || scenarioId === 'connected-conversation' ||
scenarioId === 'streaming-dark' ||
scenarioId === 'reconnecting-wide' || scenarioId === 'narrow-multiline' ||
Expand All @@ -25,16 +29,19 @@ export default class EntryAbility extends UIAbility {
scenarioId === 'copy-rollback-send-fail' || scenarioId === 'copy-rollback-fail' ||
scenarioId === 'copy-rollback-fail-dark' || scenarioId === 'copy-rollback-stale' ||
scenarioId === 'copy-rollback-busy' || scenarioId === 'copy-rollback-unsupported' ||
scenarioId === 'interaction-mailbox' || scenarioId === 'interaction-mailbox-dark') {
scenarioId === 'interaction-mailbox' || scenarioId === 'interaction-mailbox-dark' ||
deviceModelScenario) {
this.initialPage = 'pages/preview/MobileDesignGallery';
this.previewStorage = new LocalStorage();
this.previewStorage.setOrCreate('scenarioId', scenarioId);
AppStorage.setOrCreate('scenarioId', scenarioId);
}
try {
const colorMode = scenarioId === 'cold-start-home-dark' || scenarioId === 'catalog-refresh-dark' || scenarioId === 'model-role-selector-dark' || scenarioId === 'subagent-process-dark' || scenarioId === 'file-visibility-dark' || scenarioId === 'immersive-bottom-dark' || scenarioId === 'immersive-bottom-drawer-dark' || scenarioId === 'immersive-bottom-settings-dark' || scenarioId === 'composer-overflow-dark' || scenarioId === 'composer-overflow-expanded-dark' || scenarioId === 'device-selector-dark' || scenarioId === 'streaming-dark' || scenarioId === 'interaction-mailbox-dark' || scenarioId === 'copy-selection-dark' || scenarioId === 'copy-rollback-dark' || scenarioId === 'copy-rollback-send-dark' || scenarioId === 'copy-rollback-fail-dark'
const colorMode = scenarioId === 'cold-start-home-dark' || scenarioId === 'catalog-refresh-dark' || scenarioId === 'model-role-selector-dark' || scenarioId === 'subagent-process-dark' || scenarioId === 'file-visibility-dark' || scenarioId === 'immersive-bottom-dark' || scenarioId === 'immersive-bottom-drawer-dark' || scenarioId === 'immersive-bottom-settings-dark' || scenarioId === 'composer-overflow-dark' || scenarioId === 'composer-overflow-expanded-dark' || scenarioId === 'device-selector-dark' || scenarioId === 'streaming-dark' || scenarioId === 'interaction-mailbox-dark' || scenarioId === 'copy-selection-dark' || scenarioId === 'copy-rollback-dark' || scenarioId === 'copy-rollback-send-dark' || scenarioId === 'copy-rollback-fail-dark' ||
(deviceModelScenario && String(scenarioId).endsWith('-dark'))
? ConfigurationConstant.ColorMode.COLOR_MODE_DARK
: scenarioId === 'cold-start-home' || scenarioId === 'catalog-refresh' || scenarioId === 'model-role-selector' || scenarioId === 'model-role-selector-wide' || scenarioId === 'subagent-process' || scenarioId === 'file-visibility' || scenarioId === 'immersive-bottom' || scenarioId === 'immersive-bottom-wide' || scenarioId === 'immersive-bottom-drawer' || scenarioId === 'immersive-bottom-settings' || scenarioId === 'immersive-bottom-cover' || scenarioId === 'immersive-bottom-miniapps' || scenarioId === 'immersive-bottom-wide-shell' || scenarioId === 'immersive-bottom-tools' || scenarioId === 'immersive-bottom-tools-editor' || scenarioId === 'immersive-bottom-tools-file-action' || scenarioId === 'immersive-bottom-view-settings' || scenarioId === 'immersive-bottom-connect' || scenarioId === 'composer-overflow' || scenarioId === 'composer-overflow-empty' || scenarioId === 'composer-overflow-wide' || scenarioId === 'composer-overflow-expanded' || scenarioId === 'device-selector' || scenarioId === 'connected-conversation' || scenarioId === 'reconnecting-wide' || scenarioId === 'copy-selection' || scenarioId === 'copy-selection-nav' || scenarioId === 'copy-selection-wide' || scenarioId === 'copy-rollback' || scenarioId === 'copy-rollback-send' || scenarioId === 'copy-rollback-send-fail' || scenarioId === 'copy-rollback-fail' || scenarioId === 'copy-rollback-stale' || scenarioId === 'copy-rollback-busy' || scenarioId === 'copy-rollback-unsupported'
: scenarioId === 'cold-start-home' || scenarioId === 'catalog-refresh' || scenarioId === 'model-role-selector' || scenarioId === 'model-role-selector-wide' || scenarioId === 'subagent-process' || scenarioId === 'file-visibility' || scenarioId === 'immersive-bottom' || scenarioId === 'immersive-bottom-wide' || scenarioId === 'immersive-bottom-drawer' || scenarioId === 'immersive-bottom-settings' || scenarioId === 'immersive-bottom-cover' || scenarioId === 'immersive-bottom-miniapps' || scenarioId === 'immersive-bottom-wide-shell' || scenarioId === 'immersive-bottom-tools' || scenarioId === 'immersive-bottom-tools-editor' || scenarioId === 'immersive-bottom-tools-file-action' || scenarioId === 'immersive-bottom-view-settings' || scenarioId === 'immersive-bottom-connect' || scenarioId === 'composer-overflow' || scenarioId === 'composer-overflow-empty' || scenarioId === 'composer-overflow-wide' || scenarioId === 'composer-overflow-expanded' || scenarioId === 'device-selector' || scenarioId === 'connected-conversation' || scenarioId === 'reconnecting-wide' || scenarioId === 'copy-selection' || scenarioId === 'copy-selection-nav' || scenarioId === 'copy-selection-wide' || scenarioId === 'copy-rollback' || scenarioId === 'copy-rollback-send' || scenarioId === 'copy-rollback-send-fail' || scenarioId === 'copy-rollback-fail' || scenarioId === 'copy-rollback-stale' || scenarioId === 'copy-rollback-busy' || scenarioId === 'copy-rollback-unsupported' ||
(deviceModelScenario && !String(scenarioId).endsWith('-dark'))
? ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT
: ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET;
this.context.getApplicationContext().setColorMode(colorMode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ export interface RemoteCommand {
tool_id?: string;
updated_input?: Object;
model_id?: string;
// set_model_defaults carries the whole object in one write, so all three
// managed slots are always present. A missing key means the same thing as an
// explicit null: no model configured (and, for `fast`, follow the primary).
primary?: string | null;
fast?: string | null;
image_understanding?: string | null;
reason?: string;
mode?: RemotePermissionMode;
answers?: Object;
Expand Down Expand Up @@ -403,6 +409,8 @@ export const REMOTE_CAPABILITY_HARNESS_PROFILES_V1: string = 'harness_profiles_v
export const REMOTE_CAPABILITY_SESSION_ROLLBACK_V1: string = 'session_rollback_v1';
/** The host resolves `workspace_id` in commands and stamps it on its projections. */
export const REMOTE_CAPABILITY_WORKSPACE_ID_REFERENCES_V1: string = 'workspace_id_references_v1';
/** The host reads and writes the device-level model role defaults. */
export const REMOTE_CAPABILITY_DEVICE_MODEL_DEFAULTS_V1: string = 'device_model_defaults_v1';

export interface RemoteModelConfig {
id: string;
Expand Down Expand Up @@ -437,11 +445,21 @@ export interface RemoteReasoningCatalogProjection {
presets?: RemoteReasoningPresetDescriptor[];
}

/**
* Device-level model role defaults, exactly as the host persists them.
*
* All six slots are always projected; only `primary`, `fast` and
* `image_understanding` are writable from the phone. `null` is a real value and
* means "no model configured" (`fast` then follows `primary`), so every reader
* must treat it as such rather than as an absent field.
*/
export interface RemoteDefaultModels {
primary?: string;
fast?: string;
search?: string;
image_understanding?: string;
primary?: string | null;
fast?: string | null;
search?: string | null;
image_understanding?: string | null;
image_generation?: string | null;
speech_recognition?: string | null;
}

export interface RemoteModelCatalog {
Expand All @@ -460,6 +478,18 @@ export interface SetSessionModelResponse extends CommandStatusResponse {
model_id?: string;
}

/**
* `set_model_defaults` acknowledgement.
*
* `default_models` is the reconcile result the host read back, all six slots
* included. A host that only acknowledges the command without the payload
* leaves it undefined, which the client answers with its own authoritative
* re-read instead of guessing.
*/
export interface ModelDefaultsUpdatedResponse extends CommandStatusResponse {
default_models?: RemoteDefaultModels;
}

export interface RemoteToolStatusResponse {
permission_request_id?: string;
id?: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { WorkspaceToolsIntent } from '../state/WorkspaceToolsState';
import { RemotePermissionMode, RemoteSession } from '../../model/RemoteModels';
import { RemoteDefaultModels, RemoteModelCatalog, RemotePermissionMode, RemoteSession } from '../../model/RemoteModels';
import { CloudAccountDevice } from '../../services/CloudAccountClient';
import { ConversationIntent } from './ConversationIntent';
import { AppRoute } from '../navigation/AppRouteContract';
Expand Down Expand Up @@ -87,6 +87,21 @@ export interface SettingsPresentationActions {
readonly cloudSelectDevice: (device: CloudAccountDevice) => Promise<void>;
readonly getPermissionMode: () => Promise<RemotePermissionMode>;
readonly setPermissionMode: (mode: RemotePermissionMode) => Promise<RemotePermissionMode>;
/**
* Device-level model catalog of the connected desktop: read without a session
* id, so the projection carries the defaults that belong to the desktop.
*/
readonly getDeviceModelCatalog: () => Promise<RemoteModelCatalog>;
/**
* Writes the three phone-managed slots as one object. `null` is a value:
* the fast model then follows the primary one and the others are left
* unconfigured. The answer is the desktop's reconciled projection.
*/
readonly setDeviceModelDefaults: (
primary: string | null,
fast: string | null,
imageUnderstanding: string | null
) => Promise<RemoteDefaultModels>;
readonly setLanguage: (language: string) => Promise<void>;
}

Expand Down Expand Up @@ -131,6 +146,18 @@ export function emptyAppRootPresentationActions(): AppRootPresentationActions {
cloudListDevices: async () => [], cloudSelectDevice: async () => {},
getPermissionMode: async () => 'ask',
setPermissionMode: async (mode: RemotePermissionMode) => mode,
getDeviceModelCatalog: async () => ({ version: 0, models: [], default_models: {} }),
setDeviceModelDefaults: async (
primary: string | null,
fast: string | null,
imageUnderstanding: string | null
) => {
return {
primary,
fast,
image_understanding: imageUnderstanding
};
},
setLanguage: async () => {}
},
onConnect: {
Expand Down
Loading
Loading