@xopcai/xopc 0.0.428 → 0.0.431

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 (63) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{agents-yTsbcxRi.js → agents-Ds7K3D7T.js} +1 -1
  4. package/dist/gateway/static/root/assets/{automation-task-card-w6cKxfaP.js → automation-task-card-DIjI_Y1X.js} +1 -1
  5. package/dist/gateway/static/root/assets/{automations-page-D4LyOLzY.js → automations-page-BQwSCkmd.js} +1 -1
  6. package/dist/gateway/static/root/assets/{automations-workspace-BGyvZZj5.js → automations-workspace-BUFTTjBq.js} +2 -2
  7. package/dist/gateway/static/root/assets/{capabilities-page-DHJLl4RD.js → capabilities-page-B8UgQ4dD.js} +2 -2
  8. package/dist/gateway/static/root/assets/{capabilities-settings-panel-FJ3Vo7Wa.js → capabilities-settings-panel-Bxn8HtEO.js} +2 -2
  9. package/dist/gateway/static/root/assets/{chat-page--dKUIg5W.js → chat-page-e-xCsrdS.js} +1 -1
  10. package/dist/gateway/static/root/assets/{computer-settings-page-Bm70SIjD.js → computer-settings-page-BIHbjBVt.js} +1 -1
  11. package/dist/gateway/static/root/assets/{image-generation-api-BbEnKsK1.js → image-generation-api-CdwYgzhU.js} +1 -1
  12. package/dist/gateway/static/root/assets/{index-ClPeYgQk.js → index-BmUSsdwn.js} +8 -8
  13. package/dist/gateway/static/root/assets/model-selector-YsCHu1Gn.js +1 -0
  14. package/dist/gateway/static/root/assets/{personal-page-CiKdXLeR.js → personal-page-Cvd-v9kT.js} +1 -1
  15. package/dist/gateway/static/root/assets/{phone-model-connection-ucX3Tvl8.js → phone-model-connection-BieYnAJ5.js} +1 -1
  16. package/dist/gateway/static/root/assets/phone-model-picker-Bkex4Sre.js +1 -0
  17. package/dist/gateway/static/root/assets/{phone-settings-page-BSdB2pnW.js → phone-settings-page-D8PuvEuJ.js} +1 -1
  18. package/dist/gateway/static/root/assets/{product-preview-content-CNaJb6Bj.js → product-preview-content-VcvxaO2S.js} +1 -1
  19. package/dist/gateway/static/root/assets/{product-preview-modal-DgwrrpqJ.js → product-preview-modal-Ym6bomTW.js} +2 -2
  20. package/dist/gateway/static/root/assets/{project-detail-page-FReFYUv0.js → project-detail-page-CFbXWvcd.js} +1 -1
  21. package/dist/gateway/static/root/assets/{route-preload-DS4fVFbc.js → route-preload-C-VRKwB1.js} +2 -2
  22. package/dist/gateway/static/root/assets/{sessions-page-C8gwrFHR.js → sessions-page-C2CH-55n.js} +1 -1
  23. package/dist/gateway/static/root/assets/{settings-page-DCQnHCNN.js → settings-page-B6vrcc6f.js} +1 -1
  24. package/dist/gateway/static/root/assets/{setup-interaction-page-B9ACeAvO.js → setup-interaction-page-BDPte58u.js} +1 -1
  25. package/dist/gateway/static/root/assets/{task-detail-page-L5RIelv0.js → task-detail-page-CC3Rzqq9.js} +1 -1
  26. package/dist/gateway/static/root/index.html +6 -6
  27. package/dist/package.js +1 -1
  28. package/dist/src/agent/coding/run-verification.d.ts +1 -0
  29. package/dist/src/agent/coding/run-verification.js +1 -1
  30. package/dist/src/agent/embedded/run-turn.js +12 -4
  31. package/dist/src/agent/service/direct-turn-helpers.js +9 -6
  32. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  33. package/dist/src/computer/model-policy.js +1 -1
  34. package/dist/src/config/models-json.d.ts +6 -0
  35. package/dist/src/config/models-json.js +9 -4
  36. package/dist/src/gateway/global-defaults-admin.js +1 -1
  37. package/dist/src/gateway/hono/routes/models.js +12 -5
  38. package/dist/src/gateway/hono/routes/phone-setup.js +2 -1
  39. package/dist/src/gateway/hono/routes/phone.js +2 -1
  40. package/dist/src/gateway/service/run-gateway-agent.js +1 -2
  41. package/dist/src/gateway/service.js +10 -10
  42. package/dist/src/gateway/xopc-cloud-capability-setup.js +6 -3
  43. package/dist/src/home-intelligence/host.js +2 -2
  44. package/dist/src/observability/runtime.d.ts +2 -0
  45. package/dist/src/observability/runtime.js +12 -1
  46. package/dist/src/phone/contracts.d.ts +2 -0
  47. package/dist/src/phone/contracts.js +1 -0
  48. package/dist/src/phone/model-adapter.d.ts +1 -1
  49. package/dist/src/phone/model-adapter.js +29 -6
  50. package/dist/src/phone/model-policy.d.ts +4 -0
  51. package/dist/src/phone/model-policy.js +30 -0
  52. package/dist/src/phone/structured-model.d.ts +6 -0
  53. package/dist/src/phone/structured-model.js +87 -0
  54. package/dist/src/providers/model-catalog-types.d.ts +1 -1
  55. package/dist/src/providers/model-reference-auditor.js +2 -1
  56. package/dist/src/providers/presentation.js +5 -3
  57. package/dist/src/storage/sqlite/schema.d.ts +2 -2
  58. package/dist/src/storage/sqlite/schema.js +7 -7
  59. package/dist/src/storage/sqlite/upgrades/261_session_input_task_origins.sql +6 -0
  60. package/dist/src/voice/platform-catalog.d.ts +1 -1
  61. package/package.json +1 -1
  62. package/dist/gateway/static/root/assets/model-selector-DJSVr3LM.js +0 -1
  63. package/dist/gateway/static/root/assets/phone-model-picker-CU37sziM.js +0 -1
@@ -88,12 +88,6 @@ import { ChatPreviewService } from "../chat-previews/service.js";
88
88
  import "../chat-previews/index.js";
89
89
  import { configureGatewayHttpProxy } from "../infra/http-proxy-env.js";
90
90
  import "../config/index.js";
91
- import { DiscussionService } from "../discussions/service.js";
92
- import { DiscussionOrganizer } from "../discussions/organizer.js";
93
- import { DiscussionLiveWorker } from "../discussions/live-worker.js";
94
- import { DiscussionOrganizerWorker } from "../discussions/organizer-worker.js";
95
- import { DiscussionSealer } from "../discussions/sealer.js";
96
- import "../discussions/index.js";
97
91
  import { ManagedComposioEventPoller } from "../connectors/composio-managed-events.js";
98
92
  import { startConnectedKnowledgeCoordinator } from "../knowledge/coordinator.js";
99
93
  import "../knowledge/index.js";
@@ -101,8 +95,18 @@ import { PersonalReplyComposer } from "../personal-agent/reply-composer.js";
101
95
  import { ConnectionRecoveryService } from "../connectors/connection-recovery-service.js";
102
96
  import { startConnectorLearningCoordinator } from "../connectors/learning-coordinator.js";
103
97
  import { startConnectorSupervisor } from "../connectors/supervisor.js";
98
+ import { DiscussionService } from "../discussions/service.js";
99
+ import { DiscussionOrganizer } from "../discussions/organizer.js";
100
+ import { DiscussionLiveWorker } from "../discussions/live-worker.js";
101
+ import { DiscussionOrganizerWorker } from "../discussions/organizer-worker.js";
102
+ import { DiscussionSealer } from "../discussions/sealer.js";
103
+ import "../discussions/index.js";
104
104
  import { EndpointToolRuntime } from "../endpoint-tools/runtime.js";
105
105
  import "../endpoint-tools/index.js";
106
+ import { HomeCapabilityPreflightService } from "../home-intelligence/capability-preflight.js";
107
+ import { HomeAdviceGenerator } from "../home-intelligence/generator.js";
108
+ import { HomeIntelligenceHost } from "../home-intelligence/host.js";
109
+ import { HomeSnapshotBuilder } from "../home-intelligence/snapshot.js";
106
110
  import { assertGatewayAuthConfigured, extractToken, resolveGatewayAuth, validateToken } from "./auth.js";
107
111
  import { prepareAutomationAgentSession } from "./automation-agent-session.js";
108
112
  import { deliverAutomationCompletion } from "./automation-completion-delivery.js";
@@ -166,10 +170,6 @@ import { TaskMainUpdateDecisionService } from "../tasks/task-main-update-decisio
166
170
  import { selectTaskMainUpdateAttempt, submitAndConfirmTaskMainUpdate } from "../tasks/task-main-update-input.js";
167
171
  import { checkAppContextAccess, prepareAppContext } from "./service/app-context-access.js";
168
172
  import { applyAutomaticVoiceLanguage, inferProductLanguageFromEnvironment, initializeVoiceDefaults } from "../voice/language-profile.js";
169
- import { HomeAdviceGenerator } from "../home-intelligence/generator.js";
170
- import { HomeIntelligenceHost } from "../home-intelligence/host.js";
171
- import { HomeSnapshotBuilder } from "../home-intelligence/snapshot.js";
172
- import { HomeCapabilityPreflightService } from "../home-intelligence/capability-preflight.js";
173
173
  import crypto from "node:crypto";
174
174
  //#region src/gateway/service.ts
175
175
  init_session_repository();
@@ -2,11 +2,12 @@ import "../agent-config/schema.js";
2
2
  import { init_agent_config } from "../agent-config/index.js";
3
3
  import { AgentCatalogRepository, init_repository } from "../agent-catalog/repository.js";
4
4
  import { ConfigSchema, init_schema } from "../config/schema.js";
5
+ import { phoneModelProfile } from "../phone/model-policy.js";
5
6
  import { init_model_policy, isComputerModel, isDedicatedComputerProfile } from "../computer/model-policy.js";
6
7
  import { voiceSupportsMode } from "../../packages/realtime-protocol/src/voice-catalog.js";
7
8
  import { init_voice } from "../../packages/realtime-protocol/src/voice.js";
8
9
  import { getModelCatalogStore, init_model_catalog_store } from "../providers/model-catalog-store.js";
9
- import { phoneModelProfile } from "../phone/model-adapter.js";
10
+ import "../phone/model-adapter.js";
10
11
  import { compareCatalogModels } from "../providers/model-catalog-ranking.js";
11
12
  import { AgentCatalogService } from "../agent-catalog/service.js";
12
13
  import { prepareUpdateGlobalDefaults } from "./global-defaults-admin.js";
@@ -102,15 +103,17 @@ function completeXopcCloudModelRoutes(models, source) {
102
103
  })).sort((left, right) => compareCatalogModels(left, right))[0];
103
104
  if (computer) next.computerUse ??= route(computer);
104
105
  const preferredPhone = [
106
+ "phone",
105
107
  "phone-doubao",
106
- "phone-gui-plus",
107
- "phone"
108
+ "phone-gui-plus"
108
109
  ].find((id) => source.models.some((model) => model.id === id && model.availability === "available" && phoneModelProfile({
109
110
  ...model,
111
+ provider: source.providerId,
110
112
  api: source.api
111
113
  })));
112
114
  const phone = source.models.filter((model) => model.availability === "available" && phoneModelProfile({
113
115
  ...model,
116
+ provider: source.providerId,
114
117
  api: source.api
115
118
  })).sort((left, right) => compareCatalogModels(left, right, preferredPhone))[0];
116
119
  if (phone) next.phoneUse ??= route(phone);
@@ -1,10 +1,10 @@
1
1
  import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
- import { HomeAdviceBudgetExceededError, isHomeProseLocalized } from "./generator.js";
4
3
  import { homePatternKey } from "./strategy.js";
5
- import { HomeAdvicePolicy } from "./policy.js";
6
4
  import { HomeIntelligenceRepository } from "./repository.js";
7
5
  import { HomeOpportunityApplicationService } from "./application-service.js";
6
+ import { HomeAdvicePolicy } from "./policy.js";
7
+ import { HomeAdviceBudgetExceededError, isHomeProseLocalized } from "./generator.js";
8
8
  import { buildHomeOpportunityNotification } from "./notification.js";
9
9
  import { createHash } from "node:crypto";
10
10
  //#region src/home-intelligence/host.ts
@@ -55,3 +55,5 @@ export declare function testLangfuse(): Promise<boolean>;
55
55
  export declare function closeTracing(): Promise<void>;
56
56
  export declare function traceOperation<T>(name: string, fn: () => Promise<T>, attributes?: Attributes): Promise<T>;
57
57
  export declare function setTraceIdentity(identity: TraceIdentity): void;
58
+ /** Keep the first timing for each milestone on the run root, including fallback runs. */
59
+ export declare function recordTraceLatency(metrics: Record<string, number>): void;
@@ -400,5 +400,16 @@ function setTraceIdentity(identity) {
400
400
  };
401
401
  scope.root?.setAttributes(attrs(scope.identity, "agent"));
402
402
  }
403
+ /** Keep the first timing for each milestone on the run root, including fallback runs. */
404
+ function recordTraceLatency(metrics) {
405
+ const scope = scopes.getStore();
406
+ if (!scope?.root) return;
407
+ scope.metrics ??= /* @__PURE__ */ new Set();
408
+ for (const [name, value] of Object.entries(metrics)) {
409
+ if (!Number.isFinite(value) || value < 0 || scope.metrics.has(name)) continue;
410
+ scope.root.setAttribute(`xopc.latency.${name}`, value);
411
+ scope.metrics.add(name);
412
+ }
413
+ }
403
414
  //#endregion
404
- export { closeTracing, configureTracing, flushTracing, localStore, setTraceIdentity, startObservation, testLangfuse, traceIds, traceOperation, traceRun, traceTools, tracingConfig, tracingStatus };
415
+ export { closeTracing, configureTracing, flushTracing, localStore, recordTraceLatency, setTraceIdentity, startObservation, testLangfuse, traceIds, traceOperation, traceRun, traceTools, tracingConfig, tracingStatus };
@@ -3,6 +3,7 @@ import type { PhonePerformance } from './execution-trace.js';
3
3
  export declare const PhoneProfileSchema: z.ZodEnum<{
4
4
  "autoglm-phone-v1": "autoglm-phone-v1";
5
5
  "structured-phone-v1": "structured-phone-v1";
6
+ "structured-tools-phone-v1": "structured-tools-phone-v1";
6
7
  "doubao-seed-phone-v1": "doubao-seed-phone-v1";
7
8
  "gui-owl-phone-v1": "gui-owl-phone-v1";
8
9
  }>;
@@ -10,6 +11,7 @@ export declare const PhoneModelMetadataSchema: z.ZodObject<{
10
11
  profile: z.ZodEnum<{
11
12
  "autoglm-phone-v1": "autoglm-phone-v1";
12
13
  "structured-phone-v1": "structured-phone-v1";
14
+ "structured-tools-phone-v1": "structured-tools-phone-v1";
13
15
  "doubao-seed-phone-v1": "doubao-seed-phone-v1";
14
16
  "gui-owl-phone-v1": "gui-owl-phone-v1";
15
17
  }>;
@@ -6,6 +6,7 @@ var init_contracts = __esmMin((() => {
6
6
  PhoneProfileSchema = z.enum([
7
7
  "autoglm-phone-v1",
8
8
  "structured-phone-v1",
9
+ "structured-tools-phone-v1",
9
10
  "doubao-seed-phone-v1",
10
11
  "gui-owl-phone-v1"
11
12
  ]);
@@ -2,7 +2,7 @@ import { type PhoneAppAccess } from './app-access.js';
2
2
  import { type PhonePlan, type PhonePlanState } from './planning.js';
3
3
  import type { Config } from '../config/schema.js';
4
4
  import { type PhoneAction, type PhoneApplication, type PhoneDecision, type PhoneObservation, type PhoneCompletion, type PhoneTaskContract, type PhoneRequestedOutput } from './contracts.js';
5
- export declare function phoneModelProfile(model: unknown): "autoglm-phone-v1" | "structured-phone-v1" | "doubao-seed-phone-v1" | "gui-owl-phone-v1";
5
+ export { phoneModelProfile } from './model-policy.js';
6
6
  export declare function validatePhoneModelChange(next: {
7
7
  primary: string;
8
8
  } | null | undefined, previous?: {
@@ -1,8 +1,10 @@
1
- import { PhoneDecisionSchema, PhoneError, PhoneModelMetadataSchema, init_contracts } from "./contracts.js";
1
+ import { PhoneDecisionSchema, PhoneError, init_contracts } from "./contracts.js";
2
+ import { init_model_policy, phoneModelProfile } from "./model-policy.js";
2
3
  import { getApiKey, init_providers, resolveModel } from "../providers/index.js";
3
4
  import { cloudCreditBudgetHeaders, reportCloudCreditError } from "../providers/xopc-cloud-credit-budget.js";
4
5
  import { currentPhoneTrace, measurePhonePhase } from "./execution-trace.js";
5
6
  import { phoneAllowsApp } from "./app-access.js";
7
+ import { predictPhoneToolDecision } from "./structured-model.js";
6
8
  import { readPhoneModelResponse } from "./model-response.js";
7
9
  import { AutoGlmHistory, autoGlmSystemPrompt, buildAutoGlmMessages } from "./autoglm-protocol.js";
8
10
  import { PHONE_PLANNING_RULES, PhonePlanSchema } from "./planning.js";
@@ -11,13 +13,9 @@ import { guiPhonePrompt, parseDoubaoPhone, parseGuiOwlPhone } from "./gui-protoc
11
13
  import { z } from "zod";
12
14
  import { performance } from "node:perf_hooks";
13
15
  //#region src/phone/model-adapter.ts
16
+ init_model_policy();
14
17
  init_providers();
15
18
  init_contracts();
16
- function phoneModelProfile(model) {
17
- const candidate = model;
18
- const metadata = PhoneModelMetadataSchema.safeParse(candidate?.phoneUse);
19
- return metadata.success && candidate?.api === "openai-completions" && candidate.input?.includes("image") ? metadata.data.profile : void 0;
20
- }
21
19
  function validatePhoneModelChange(next, previous) {
22
20
  if (!next || next.primary === previous?.primary || next.primary === "phone-mock/settings-about") return;
23
21
  try {
@@ -507,6 +505,31 @@ async function createPhoneModel(modelRef, config, agentId) {
507
505
  transport: config.phone?.modelResponseMode ?? "json"
508
506
  };
509
507
  try {
508
+ if (profile === "structured-tools-phone-v1") {
509
+ metric.protocol = profile;
510
+ const deadline = AbortSignal.timeout(config.phone?.modelRequestTimeoutMs ?? 45e3);
511
+ try {
512
+ const requestSignal = AbortSignal.any([signal, deadline]);
513
+ requestSignal.throwIfAborted();
514
+ const currentKey = await getApiKey(model.provider, {
515
+ agentId,
516
+ appConfig: config
517
+ });
518
+ if (!currentKey) throw new PhoneError("PHONE_MODEL_KEY_MISSING", "Reconnect the model provider on the Gateway host");
519
+ const modelInput = {
520
+ ...input,
521
+ enhancements: {
522
+ ...input.enhancements,
523
+ hierarchicalPlanning: false
524
+ }
525
+ };
526
+ const instructions = `Operate the authorized Android phone using exactly one phone_decision function call. Coordinates are normalized to 0..1000 on both axes. Ground actions in the current screenshot and native UI. Use typeText only for a focused non-password editable field; mode replace changes existing contents, insert appends. Request needsUser for passwords, login, verification codes, CAPTCHA or an effect outside the authorized goal. Completion is a claim, verified separately by the host. ${input.readOnly ? PHONE_SCREEN_READING_RULES : PHONE_RECOVERY_RULES}${input.readingFields ? " Return the requested readingFields as a compact JSON object encoded in complete.summary, with exactly those keys and visible string values or null. Do not merge fields." : ""}${input.enhancements?.semanticControls ? ` ${PHONE_SEMANTIC_RULES}` : ""}`;
527
+ return await measurePhonePhase("model_request", () => predictPhoneToolDecision(model, currentKey, instructions, buildPhoneModelContext(modelInput), modelInput, requestSignal, metric, started));
528
+ } catch (error) {
529
+ if (!signal.aborted && deadline.aborted) throw new PhoneError("PHONE_MODEL_TIMEOUT", "Phone model request timed out; no action from this request was dispatched", 502);
530
+ throw error;
531
+ }
532
+ }
510
533
  const nativeGui = profile === "doubao-seed-phone-v1" || profile === "gui-owl-phone-v1";
511
534
  const modelInput = profile !== "structured-phone-v1" ? {
512
535
  ...input,
@@ -0,0 +1,4 @@
1
+ /** Native phone protocols stay explicit; ordinary visual tool models use function calls. */
2
+ export declare function phoneModelProfile(model: unknown): "autoglm-phone-v1" | "structured-phone-v1" | "structured-tools-phone-v1" | "doubao-seed-phone-v1" | "gui-owl-phone-v1";
3
+ /** Generic visual models remain eligible for ordinary chat and vision pickers. */
4
+ export declare function isDedicatedPhoneModel(model: unknown): boolean;
@@ -0,0 +1,30 @@
1
+ import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
2
+ import { PhoneModelMetadataSchema, init_contracts } from "./contracts.js";
3
+ import { init_constants } from "../providers/constants.js";
4
+ //#region src/phone/model-policy.ts
5
+ /** Native phone protocols stay explicit; ordinary visual tool models use function calls. */
6
+ function phoneModelProfile(model) {
7
+ if (!model || typeof model !== "object") return void 0;
8
+ const value = model;
9
+ if (!Array.isArray(value.input) || !value.input.includes("image")) return void 0;
10
+ const compatibleApi = value.api === "openai-completions" || value.api === "openai-responses";
11
+ if (value.phoneUse !== void 0) {
12
+ const metadata = PhoneModelMetadataSchema.safeParse(value.phoneUse);
13
+ if (!metadata.success) return void 0;
14
+ return metadata.data.profile === "structured-tools-phone-v1" ? compatibleApi && value.supportsTools !== false ? metadata.data.profile : void 0 : value.api === "openai-completions" ? metadata.data.profile : void 0;
15
+ }
16
+ if (value.provider === "xopc-cloud" || value.baseUrl === "extension://provider-plugin" || value.computerUse || value.supportsTools === false) return void 0;
17
+ return compatibleApi ? "structured-tools-phone-v1" : void 0;
18
+ }
19
+ /** Generic visual models remain eligible for ordinary chat and vision pickers. */
20
+ function isDedicatedPhoneModel(model) {
21
+ const value = model;
22
+ return Boolean(value?.phoneUse && phoneModelProfile(model) && value.phoneUse.profile !== "structured-tools-phone-v1");
23
+ }
24
+ var init_model_policy = __esmMin((() => {
25
+ init_constants();
26
+ init_contracts();
27
+ }));
28
+ //#endregion
29
+ init_model_policy();
30
+ export { init_model_policy, isDedicatedPhoneModel, phoneModelProfile };
@@ -0,0 +1,6 @@
1
+ import { type Api, type Model } from '@earendil-works/pi-ai/compat';
2
+ import { type PhoneDecision } from './contracts.js';
3
+ import type { PhoneModelMetric } from './execution-trace.js';
4
+ import type { PhonePrediction } from './model-adapter.js';
5
+ /** Use the shared provider client for API keys and ChatGPT OAuth without persisting screenshots. */
6
+ export declare function predictPhoneToolDecision(model: Model<Api>, apiKey: string, instructions: string, context: unknown, input: PhonePrediction, signal: AbortSignal, metric: PhoneModelMetric, started: number): Promise<PhoneDecision>;
@@ -0,0 +1,87 @@
1
+ import { PhoneDecisionSchema, PhoneError, init_contracts } from "./contracts.js";
2
+ import { z } from "zod";
3
+ import { completeSimple } from "@earendil-works/pi-ai/compat";
4
+ import { performance } from "node:perf_hooks";
5
+ //#region src/phone/structured-model.ts
6
+ init_contracts();
7
+ const TOOL_NAME = "phone_decision";
8
+ const invalid = () => new PhoneError("PHONE_MODEL_PROTOCOL", "Phone model returned an invalid or incomplete function call; no action was dispatched");
9
+ /** Use the shared provider client for API keys and ChatGPT OAuth without persisting screenshots. */
10
+ async function predictPhoneToolDecision(model, apiKey, instructions, context, input, signal, metric, started) {
11
+ const responses = model.api === "openai-responses";
12
+ const schema = z.object({ decision: input.readOnly ? z.union([PhoneDecisionSchema.options[1], PhoneDecisionSchema.options[2]]) : PhoneDecisionSchema }).strict();
13
+ const tool = {
14
+ name: TOOL_NAME,
15
+ description: "One authorized phone action, completion claim or request for user assistance.",
16
+ parameters: z.toJSONSchema(schema)
17
+ };
18
+ metric.transport = responses ? "responses-stream" : "chat-completions-stream";
19
+ signal.throwIfAborted();
20
+ metric.preparationMs = Math.round(performance.now() - started);
21
+ const result = await completeSimple(model, {
22
+ systemPrompt: instructions,
23
+ messages: [{
24
+ role: "user",
25
+ timestamp: Date.now(),
26
+ content: [{
27
+ type: "text",
28
+ text: JSON.stringify(context)
29
+ }, {
30
+ type: "image",
31
+ data: input.observation.frame.toString("base64"),
32
+ mimeType: "image/png"
33
+ }]
34
+ }],
35
+ tools: [tool]
36
+ }, {
37
+ apiKey,
38
+ maxTokens: Math.min(3e3, model.maxTokens ?? 3e3),
39
+ signal,
40
+ maxRetries: 0,
41
+ transport: "sse",
42
+ cacheRetention: "none",
43
+ onPayload(payload) {
44
+ const body = payload;
45
+ body.parallel_tool_calls = false;
46
+ body.tool_choice = responses ? {
47
+ type: "function",
48
+ name: TOOL_NAME
49
+ } : {
50
+ type: "function",
51
+ function: { name: TOOL_NAME }
52
+ };
53
+ if (responses) {
54
+ body.store = false;
55
+ body.stream = true;
56
+ }
57
+ metric.requestBytes = Buffer.byteLength(JSON.stringify(body));
58
+ },
59
+ onProviderStreamEvent(event) {
60
+ metric.responseBytes = (metric.responseBytes ?? 0) + Buffer.byteLength(JSON.stringify(event));
61
+ if (metric.responseBytes > 2 * 1024 * 1024) throw invalid();
62
+ },
63
+ onResponse(response) {
64
+ metric.httpStatus = response.status;
65
+ metric.firstByteMs = Math.round(performance.now() - started);
66
+ }
67
+ });
68
+ signal.throwIfAborted();
69
+ metric.finishReason = result.stopReason;
70
+ metric.promptTokens = result.usage?.input;
71
+ metric.completionTokens = result.usage?.output;
72
+ metric.totalTokens = result.usage?.totalTokens;
73
+ if (result.stopReason === "length") throw new PhoneError("PHONE_MODEL_TRUNCATED", "Phone model response was truncated; no action was dispatched");
74
+ if (result.stopReason !== "toolUse" || result.errorMessage) throw invalid();
75
+ const calls = result.content.filter((item) => item.type === "toolCall");
76
+ if (calls.length !== 1 || calls[0].name !== TOOL_NAME) throw invalid();
77
+ let decision;
78
+ try {
79
+ decision = schema.parse(calls[0].arguments).decision;
80
+ } catch {
81
+ throw invalid();
82
+ }
83
+ if (decision.kind === "action" && decision.action.type === "tapElement" && !input.enhancements?.semanticControls) throw invalid();
84
+ return decision;
85
+ }
86
+ //#endregion
87
+ export { predictPhoneToolDecision };
@@ -6,7 +6,7 @@ export interface CatalogModel {
6
6
  profile: ComputerProfile;
7
7
  };
8
8
  phoneUse?: {
9
- profile: 'autoglm-phone-v1' | 'structured-phone-v1' | 'doubao-seed-phone-v1' | 'gui-owl-phone-v1';
9
+ profile: 'autoglm-phone-v1' | 'structured-phone-v1' | 'structured-tools-phone-v1' | 'doubao-seed-phone-v1' | 'gui-owl-phone-v1';
10
10
  };
11
11
  id: string;
12
12
  name: string;
@@ -1,9 +1,10 @@
1
1
  import { AgentCatalogRepository, init_repository } from "../agent-catalog/repository.js";
2
2
  import { init_contracts } from "../phone/contracts.js";
3
+ import { phoneModelProfile } from "../phone/model-policy.js";
3
4
  import { init_model_policy, isComputerModel, isDedicatedComputerProfile } from "../computer/model-policy.js";
4
5
  import { getModelCatalogStore, init_model_catalog_store } from "./model-catalog-store.js";
5
6
  import { getModelRegistry, init_model_registry } from "./model-registry.js";
6
- import { phoneModelProfile } from "../phone/model-adapter.js";
7
+ import "../phone/model-adapter.js";
7
8
  import { parseImageGenerationModelRef } from "../agent/image/generation/model-ref.js";
8
9
  import { getImageGenerationProvider } from "../agent/image/generation/provider-registry.js";
9
10
  //#region src/providers/model-reference-auditor.ts
@@ -1,4 +1,5 @@
1
- import { init_model_policy, isDedicatedComputerModel } from "../computer/model-policy.js";
1
+ import { init_model_policy, isDedicatedPhoneModel } from "../phone/model-policy.js";
2
+ import { init_model_policy as init_model_policy$1, isDedicatedComputerModel } from "../computer/model-policy.js";
2
3
  import { getProviderRegistry, init_plugin_registry } from "./plugin-registry.js";
3
4
  import { PROVIDER_META, getAllModels, getAllProviders, getModelsByProvider, getProviderDisplayName, init_providers, isProviderConfigured } from "./index.js";
4
5
  import { getDomesticProviderPreset, getDomesticProviderPresetIds } from "./domestic-presets.js";
@@ -6,6 +7,7 @@ import { getDomesticProviderPreset, getDomesticProviderPresetIds } from "./domes
6
7
  init_providers();
7
8
  init_plugin_registry();
8
9
  init_model_policy();
10
+ init_model_policy$1();
9
11
  const FEATURED_PROVIDER_ORDER = [
10
12
  "xopc-cloud",
11
13
  "qwen-token-plan-cn",
@@ -238,7 +240,7 @@ function modelToCatalogView(model) {
238
240
  ...model.contextWindow ? { contextWindow: model.contextWindow } : {},
239
241
  ...model.maxTokens ? { maxTokens: model.maxTokens } : {},
240
242
  vision: input.includes("image"),
241
- recommended: !("phoneUse" in model && model.phoneUse) && !isDedicatedComputerModel(model) && isRecommendedModel(model.provider, model.id)
243
+ recommended: !isDedicatedPhoneModel(model) && !isDedicatedComputerModel(model) && isRecommendedModel(model.provider, model.id)
242
244
  };
243
245
  }
244
246
  function presetModelToCatalogView(provider, model) {
@@ -266,7 +268,7 @@ function sortModelsForPicker(models) {
266
268
  });
267
269
  }
268
270
  function getRecommendedModelsForProvider(provider, limit = 4) {
269
- const fromRegistry = sortModelsForPicker(getModelsByProvider(provider)).filter((model) => !("phoneUse" in model && model.phoneUse) && !isDedicatedComputerModel(model) && isRecommendedModel(provider, model.id)).slice(0, limit).map(modelToCatalogView);
271
+ const fromRegistry = sortModelsForPicker(getModelsByProvider(provider)).filter((model) => !isDedicatedPhoneModel(model) && !isDedicatedComputerModel(model) && isRecommendedModel(provider, model.id)).slice(0, limit).map(modelToCatalogView);
270
272
  if (fromRegistry.length > 0) return fromRegistry;
271
273
  const preset = getDomesticProviderPreset(provider);
272
274
  if (!preset) return [];
@@ -1,7 +1,7 @@
1
1
  import type { DatabaseSync } from 'node:sqlite';
2
2
  export { SCHEMA_META_SCHEMA_VERSION_KEY, readSchemaVersionForTest, readSchemaVersion, setSchemaVersion, ensureSchemaMetaTable } from './schema-version.js';
3
- export declare const XOPC_DB_SCHEMA_VERSION = 260;
4
- export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 260;
3
+ export declare const XOPC_DB_SCHEMA_VERSION = 261;
4
+ export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 261;
5
5
  export { XOPC_DB_MINIMUM_SCHEMA_VERSION } from './schema-upgrade.js';
6
6
  /** Initialize fresh databases directly; upgrade supported installed databases transactionally. */
7
7
  export declare function ensureXopcDatabaseSchema(db: DatabaseSync): void;
@@ -14,18 +14,18 @@ function ensureXopcDatabaseSchema(db) {
14
14
  const hasMetadata = tables.some((row) => row.name === "schema_meta");
15
15
  if (!hasMetadata && tables.length) throw new Error("An unversioned database cannot be initialized");
16
16
  const version = hasMetadata ? readSchemaVersion(db) : 0;
17
- if (version > 260) throw new DatabaseSchemaTooNewError(version, 260);
17
+ if (version > 261) throw new DatabaseSchemaTooNewError(version, 261);
18
18
  if (version > 0 && version < 202) throw new DatabaseSchemaTooOldError(version, 202);
19
- if (version >= 202 && version < 260) {
20
- upgradeXopcDatabase(db, version, 260);
19
+ if (version >= 202 && version < 261) {
20
+ upgradeXopcDatabase(db, version, 261);
21
21
  return;
22
22
  }
23
- if (version === 260) return;
23
+ if (version === 261) return;
24
24
  if (db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name != 'schema_meta' AND name NOT LIKE 'sqlite_%' LIMIT 1").get()) throw new Error("An unversioned database cannot be initialized");
25
25
  db.exec("BEGIN IMMEDIATE");
26
26
  try {
27
27
  db.exec(readSqliteAsset("schema.sql"));
28
- setSchemaVersion(db, 260);
28
+ setSchemaVersion(db, 261);
29
29
  db.exec("COMMIT");
30
30
  } catch (error) {
31
31
  db.exec("ROLLBACK");
@@ -40,8 +40,8 @@ var init_schema = __esmMin((() => {
40
40
  init_schema_version();
41
41
  init_schema_errors();
42
42
  init_schema_upgrade();
43
- XOPC_DB_SCHEMA_VERSION = 260;
44
- XOPC_DB_BASELINE_SCHEMA_VERSION = 260;
43
+ XOPC_DB_SCHEMA_VERSION = 261;
44
+ XOPC_DB_BASELINE_SCHEMA_VERSION = 261;
45
45
  }));
46
46
  //#endregion
47
47
  init_schema();
@@ -0,0 +1,6 @@
1
+ -- Task dispatch, collaboration, and scheduled inputs previously shared this source.
2
+ -- Normalize durable inputs before startup recovery applies the current origin contract.
3
+ UPDATE session_inputs
4
+ SET origin_json = json_set(origin_json, '$.source', 'task'), version = version + 1
5
+ WHERE json_extract(origin_json, '$.type') = 'system'
6
+ AND json_extract(origin_json, '$.source') = 'workflow';
@@ -43,7 +43,7 @@ export declare function requirePlatformVoiceModel(id: string, mode: VoiceModeCap
43
43
  profile: import("@xopcai/computer-control-contract/src/index.js").ComputerProfile;
44
44
  };
45
45
  phoneUse?: {
46
- profile: "autoglm-phone-v1" | "structured-phone-v1" | "doubao-seed-phone-v1" | "gui-owl-phone-v1";
46
+ profile: "autoglm-phone-v1" | "structured-phone-v1" | "structured-tools-phone-v1" | "doubao-seed-phone-v1" | "gui-owl-phone-v1";
47
47
  };
48
48
  id: string;
49
49
  name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xopcai/xopc",
3
- "version": "0.0.428",
3
+ "version": "0.0.431",
4
4
  "description": "Local-first personal AI with Ada, task delegation, files, projects and automations. Use cloud or local models.",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -1 +0,0 @@
1
- import{i as e}from"./rolldown-runtime-aKtaBQYM.js";import{a as t,n,t as r}from"./vendor-react-BOUWij0V.js";import{l as i,u as ee}from"./chunk-KS7C4IRE-CiwuuyV8.js";import{n as a}from"./locale-store-B7y-RVe8.js";import{t as te}from"./messages-DYDkJvXj.js";import{t as ne}from"./check-DUBLcKHA.js";import{a as re,c as o,d as s,l as c,o as l,u as ie}from"./popover-select-CEMWIrtY.js";import{t as ae}from"./settings-2-D-csvaET.js";import{t as u}from"./cn-0WouolD0.js";import{i as oe}from"./vendor-swr-CdRzEUwF.js";import{n as d,t as se}from"./registry-api-QZEjziDq.js";import{c as ce,s as le}from"./dist-GJ8-1PAx.js";import{t as ue}from"./open-capability-settings-Cvb65hQB.js";import{n as de}from"./interaction-BdhHBbYa.js";import{i as fe,n as pe,r as me}from"./form-field-width-DJw_jULe.js";var he=n();function f(e,t){return e.displayNames?.[t.toLowerCase().startsWith(`zh`)?`zh-CN`:`en`]||(t.toLowerCase().startsWith(`zh`)&&e.id===`xopc-cloud/auto`&&e.name===`Standard`?`标准`:t.toLowerCase().startsWith(`zh`)&&e.id===`xopc-cloud/advanced`&&e.name===`Advanced`?`高级`:e.name)}var p=e(t(),1);function ge(e,t,n){if(t===`computer-use`)return e.filter(e=>!!e.computerUse);let r=e=>e.computerUse?.profile!==void 0&&e.computerUse.dedicated!==!1&&e.computerUse.profile!==`openai-responses-computer-v1`;if(t!==`vision`)return e.filter(e=>!r(e)&&!e.phoneUse||e.id===n);let i=e.filter(e=>e.vision===!0&&!r(e)&&!e.phoneUse);if(!n||i.some(e=>e.id===n))return i;let ee=e.find(e=>e.id===n);if(ee)return[ee,...i];let a=n.indexOf(`/`),te=a>=0?n.slice(0,a):``;return[{id:n,name:(a>=0?n.slice(a+1):n)||n,provider:te||`—`,vision:!1},...i]}var m=r();function h(e){return`${e.id} ${e.name} ${Object.values(e.displayNames??{}).join(` `)} ${e.provider}`.toLowerCase()}var _e=[];function ve(e,t){let n=t.trim().toLowerCase();if(!n)return e;let r=n.split(/\s+/).filter(Boolean);return e.filter(e=>{let t=h(e);return r.every(e=>t.includes(e))})}function g(e){let t=(0,he.c)(100),{value:n,disabled:r,placeholder:l,searchPlaceholder:h,noMatches:g,models:Se,modelsLoading:Ce,modelsError:we,capabilitiesFilter:_,outOfFilterNote:Te,registryEmptyHint:Ee,allowEmpty:De,emptyLabel:Oe,compact:ke,showProviderInTrigger:Ae,contentSide:je,contentAlign:Me,className:Ne,popoverContentClassName:Pe,showProviderSettingsFooter:Fe,settingsFooterLink:Ie,ariaLabel:Le,onChange:v}=e,Re=De!==void 0&&De,ze=Ae===void 0||Ae,Be=je===void 0?`bottom`:je,Ve=Me===void 0?`end`:Me,He=ee(),y=i(),b=a(be),x;t[0]===b?x=t[1]:(x=te(b),t[0]=b,t[1]=x);let Ue=x.chat,[We,S]=(0,p.useState)(!1),[C,w]=(0,p.useState)(``),[Ge,Ke]=(0,p.useState)(null),qe=c(),Je=o(Ge),Ye=ie(qe,Je!==null),T;t[2]!==Ue||t[3]!==Ie||t[4]!==Fe?(T=Ie??(Fe?{label:Ue.modelProviderSettingsLink,path:`/settings/capabilities/models`}:void 0),t[2]=Ue,t[3]=Ie,t[4]=Fe,t[5]=T):T=t[5];let E=T,D;t[6]===Symbol.for(`react.memo_cache_sentinel`)?(D={revalidateOnFocus:!1},t[6]=D):D=t[6];let Xe=oe(Se?null:se,d,D),O=Se??Xe.data??_e,Ze=Se?!!Ce:Xe.isLoading,k=Se?we:Xe.error,A;t[7]===n?A=t[8]:(A=n.trim(),t[7]=n,t[8]=A);let j=A,M;t[9]!==_||t[10]!==O||t[11]!==j?(M=ge(O,_,j),t[9]=_,t[10]=O,t[11]=j,t[12]=M):M=t[12];let N=M,P=_===`computer-use`||N.length>10,Qe=P?C:``,$e;t[13]!==N||t[14]!==Qe?($e=ve(N,Qe),t[13]=N,t[14]=Qe,t[15]=$e):$e=t[15];let F=$e,et;t[16]!==O||t[17]!==N||t[18]!==n?(et=O.find(e=>e.id===n)??N.find(e=>e.id===n),t[16]=O,t[17]=N,t[18]=n,t[19]=et):et=t[19];let I=et,tt;t[20]!==b||t[21]!==l||t[22]!==I||t[23]!==ze||t[24]!==n?(tt=I?ze?`${f(I,b)} (${I.provider})`:f(I,b):n||l,t[20]=b,t[21]=l,t[22]=I,t[23]=ze,t[24]=n,t[25]=tt):tt=t[25];let nt=tt,L=!k&&!!_&&N.length===0,rt=ce,it=r||Ze,R;t[26]!==b||t[27]!==l||t[28]!==I?(R=I?`${f(I,b)} (${I.provider})`:l,t[26]=b,t[27]=l,t[28]=I,t[29]=R):R=t[29];let at=ke&&`py-1.5 text-[13px]`,z;t[30]!==Ne||t[31]!==at?(z=u(pe,`items-center gap-2 rounded-lg border border-edge-subtle bg-surface-panel px-3 py-2 text-left text-sm font-normal text-fg`,de.transition,`hover:border-edge hover:bg-surface-hover/45`,fe,`disabled:cursor-not-allowed disabled:opacity-50`,`dark:border-edge-subtle dark:hover:bg-surface-hover/55`,at,Ne),t[30]=Ne,t[31]=at,t[32]=z):z=t[32];let ot=Ze?`…`:nt,B;t[33]===ot?B=t[34]:(B=(0,m.jsx)(`span`,{className:`min-w-0 truncate`,children:ot}),t[33]=ot,t[34]=B);let st;t[35]===Symbol.for(`react.memo_cache_sentinel`)?(st=(0,m.jsx)(s,{className:`size-4 shrink-0 text-fg-subtle opacity-70`,"aria-hidden":!0}),t[35]=st):st=t[35];let V;t[36]!==Le||t[37]!==it||t[38]!==R||t[39]!==z||t[40]!==B?(V=(0,m.jsx)(le,{asChild:!0,children:(0,m.jsxs)(`button`,{ref:Ke,type:`button`,"aria-label":Le,disabled:it,title:R,className:z,children:[B,st]})}),t[36]=Le,t[37]=it,t[38]=R,t[39]=z,t[40]=B,t[41]=V):V=t[41];let ct=ce,lt=Je??void 0,ut=ce,dt=u(Ye,re,`w-[min(22rem,calc(100vw-2rem))] rounded-xl border border-edge-subtle bg-surface-overlay p-1 shadow-elevated dark:border-edge-subtle`,Pe),H;t[42]!==C||t[43]!==h||t[44]!==P?(H=P?(0,m.jsx)(`input`,{type:`search`,"aria-label":h,className:u(`mb-1 w-full shrink-0 rounded-lg border border-edge-subtle bg-surface-base px-2.5 py-1.5 text-sm text-fg placeholder:text-fg-disabled dark:bg-surface-hover/40`,me),placeholder:h,value:C,onChange:e=>w(e.target.value)}):null,t[42]=C,t[43]=h,t[44]=P,t[45]=H):H=t[45];let U;t[46]!==Re||t[47]!==Oe||t[48]!==v||t[49]!==l||t[50]!==j?(U=Re?(0,m.jsxs)(`button`,{type:`button`,className:u(`flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm text-fg hover:bg-surface-hover`,j===``&&`bg-surface-hover/90 font-medium dark:bg-surface-hover/70`),onClick:()=>{v(``),S(!1),w(``)},children:[(0,m.jsx)(ne,{className:u(`size-4 shrink-0`,j!==``&&`invisible`),"aria-hidden":!0}),(0,m.jsx)(`span`,{className:`min-w-0 truncate text-fg-muted`,children:Oe??l})]}):null,t[46]=Re,t[47]=Oe,t[48]=v,t[49]=l,t[50]=j,t[51]=U):U=t[51];let W;t[52]===k?W=t[53]:(W=k?(0,m.jsx)(`div`,{className:`p-2 text-xs text-red-600 dark:text-red-400`,children:k instanceof Error?k.message:`Failed to load models`}):null,t[52]=k,t[53]=W);let G;t[54]!==k||t[55]!==g||t[56]!==Ee||t[57]!==L?(G=!k&&L?(0,m.jsx)(`div`,{className:`px-2 py-3 text-center text-xs text-fg-muted`,children:Ee??g}):null,t[54]=k,t[55]=g,t[56]=Ee,t[57]=L,t[58]=G):G=t[58];let K;t[59]!==k||t[60]!==F||t[61]!==g||t[62]!==L?(K=!k&&!L&&F.length===0?(0,m.jsx)(`div`,{className:`px-2 py-3 text-center text-xs text-fg-muted`,children:g}):null,t[59]=k,t[60]=F,t[61]=g,t[62]=L,t[63]=K):K=t[63];let ft=_===`vision`?Te:void 0,q;t[64]===v?q=t[65]:(q=e=>{v(e),S(!1),w(``)},t[64]=v,t[65]=q);let J;t[66]!==F||t[67]!==g||t[68]!==h||t[69]!==ft||t[70]!==q||t[71]!==n?(J=(0,m.jsx)(xe,{models:F,value:n,searchPlaceholder:h,noMatches:g,showSearch:!1,outOfFilterNote:ft,onChange:q}),t[66]=F,t[67]=g,t[68]=h,t[69]=ft,t[70]=q,t[71]=n,t[72]=J):J=t[72];let Y;t[73]!==U||t[74]!==W||t[75]!==G||t[76]!==K||t[77]!==J?(Y=(0,m.jsxs)(`div`,{className:`flex min-h-0 max-h-60 flex-1 flex-col overflow-hidden`,children:[U,W,G,K,J]}),t[73]=U,t[74]=W,t[75]=G,t[76]=K,t[77]=J,t[78]=Y):Y=t[78];let X;t[79]!==E||t[80]!==y||t[81]!==He?(X=E?(0,m.jsx)(`div`,{className:`mt-1 shrink-0 border-t border-edge-subtle pt-1`,children:(0,m.jsxs)(`button`,{type:`button`,className:u(`flex w-full items-center gap-2 rounded-md p-2 text-left text-sm font-medium text-accent`,de.transition,`hover:bg-surface-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40`),onClick:()=>{E.path===`/settings/capabilities/models`?ue(y.pathname.startsWith(`/automations`)?`background`:`chat`,`services`,y.pathname.startsWith(`/automations`)?`automation`:void 0):He(E.path,{state:{settingsBackPath:`${y.pathname}${y.search}`}}),S(!1),w(``)},children:[(0,m.jsx)(ae,{className:`size-4 shrink-0 opacity-90`,"aria-hidden":!0}),(0,m.jsx)(`span`,{className:`min-w-0`,children:E.label})]})}):null,t[79]=E,t[80]=y,t[81]=He,t[82]=X):X=t[82];let Z;t[83]!==Ve||t[84]!==Be||t[85]!==ut.Content||t[86]!==dt||t[87]!==H||t[88]!==Y||t[89]!==X?(Z=(0,m.jsxs)(ut.Content,{className:dt,side:Be,sideOffset:4,align:Ve,collisionPadding:8,onOpenAutoFocus:ye,children:[H,Y,X]}),t[83]=Ve,t[84]=Be,t[85]=ut.Content,t[86]=dt,t[87]=H,t[88]=Y,t[89]=X,t[90]=Z):Z=t[90];let Q;t[91]!==ct.Portal||t[92]!==lt||t[93]!==Z?(Q=(0,m.jsx)(ct.Portal,{container:lt,children:Z}),t[91]=ct.Portal,t[92]=lt,t[93]=Z,t[94]=Q):Q=t[94];let $;return t[95]!==We||t[96]!==rt.Root||t[97]!==V||t[98]!==Q?($=(0,m.jsxs)(rt.Root,{open:We,onOpenChange:S,children:[V,Q]}),t[95]=We,t[96]=rt.Root,t[97]=V,t[98]=Q,t[99]=$):$=t[99],$}function ye(e){return e.preventDefault()}function be(e){return e.language}function xe({models:e,value:t,onChange:n,searchPlaceholder:r,noMatches:i,disabled:ee,showSearch:te=e.length>10,outOfFilterNote:re,groupByProvider:o=!1}){let s=a(e=>e.language),[c,ie]=(0,p.useState)(``),ae=ve(e,te?c:``),oe=(0,p.useRef)(null);(0,p.useLayoutEffect)(()=>{c||oe.current?.scrollIntoView?.({block:`nearest`})},[t,c,ae.some(e=>e.id===t)]);let d=new Map;for(let t of e){let e=o?t.provider:``;d.has(e)||d.set(e,[])}for(let e of ae)d.get(o?e.provider:``).push(e);return(0,m.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col`,children:[te&&(0,m.jsx)(`input`,{type:`search`,"aria-label":r,placeholder:r,value:c,onChange:e=>ie(e.target.value),className:u(`mx-1 mb-2 rounded-lg border border-edge bg-surface-base px-3 py-2 text-sm text-fg`,`focus:outline-none focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:shadow-none focus-visible:border-edge`)}),(0,m.jsxs)(`div`,{"data-select-scroll-region":!0,className:u(l,o&&`scroll-pt-8`),children:[ae.length===0&&(0,m.jsx)(`p`,{className:`px-3 py-4 text-sm text-fg-muted`,children:i}),[...d].filter(([,e])=>e.length>0).map(([r,i])=>(0,m.jsxs)(`div`,{role:o?`group`:void 0,"aria-label":o?r:void 0,className:o?`border-t border-edge-subtle pb-1 first:border-t-0`:void 0,children:[o&&(0,m.jsx)(`p`,{className:`sticky top-0 z-10 bg-surface-overlay px-3 pb-1 pt-3 text-xs font-medium text-fg-muted`,children:r}),i.map(r=>(0,m.jsxs)(`button`,{type:`button`,disabled:ee,title:r.id,ref:r.id===t?oe:void 0,"aria-pressed":r.id===t,style:e.length>50?{contentVisibility:`auto`,containIntrinsicSize:`auto 60px`}:void 0,className:u(`flex min-h-11 w-full items-center gap-3 rounded-lg px-3 py-2 text-left text-sm text-fg hover:bg-surface-hover disabled:opacity-50`,de.focusRingPanel,r.id===t&&`bg-surface-hover`),onClick:()=>n(r.id),children:[(0,m.jsxs)(`span`,{className:`min-w-0 flex-1`,children:[(0,m.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,m.jsx)(`span`,{className:`truncate font-medium`,children:f(r,s)}),(r.phoneUse||r.computerUse?.dedicated||r.reasoning)&&(0,m.jsx)(`span`,{className:`shrink-0 text-xs text-fg-muted`,children:r.phoneUse?s===`zh`?`手机控制`:`Phone control`:r.computerUse?.dedicated?s===`zh`?`电脑控制`:`Computer control`:s===`zh`?`推理`:`Reasoning`})]}),!o&&(0,m.jsx)(`span`,{className:`block truncate text-xs text-fg-muted`,children:r.provider}),re&&!r.vision&&r.id===t&&(0,m.jsx)(`span`,{className:`block text-xs text-fg-muted`,children:re})]}),(0,m.jsx)(ne,{className:u(`size-4 shrink-0 text-accent-fg`,r.id!==t&&`invisible`),"aria-hidden":!0})]},r.id))]},r))]})]})}export{g as n,f as r,xe as t};
@@ -1 +0,0 @@
1
- import{n as e,t}from"./vendor-react-BOUWij0V.js";import{i as n,o as r}from"./locale-store-B7y-RVe8.js";import{n as i,r as a}from"./popover-select-CEMWIrtY.js";import{t as o}from"./skeleton-BI5TyibZ.js";import{i as s}from"./vendor-swr-CdRzEUwF.js";var c=e(),l=`phone-mock/settings-about`;async function u(e,t,i=t===void 0?`GET`:`POST`){let a=await r(n(`/api/phone${e}`),{method:i,...t===void 0?{}:{headers:{"Content-Type":`application/json`},body:JSON.stringify(t)}});if(!a.ok)throw Error(a.error?.message??`Phone operation failed`);return a.payload}var d=()=>u(`/settings`),f=()=>u(`/devices`),p=()=>u(`/models`);async function m(e,t){return(await u(e?`/devices/${encodeURIComponent(e)}/apps`:`/apps?serial=${encodeURIComponent(t)}`)).applications}var h=t();function g(e){let t=(0,c.c)(25),{value:n,onChange:r,zh:l,disabled:u}=e,d=u!==void 0&&u,f=s(`phone-models`,p);if(!f.data&&!f.error){let e;return t[0]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,h.jsx)(o,{className:`h-11 w-full rounded-lg`}),t[0]=e):e=t[0],e}let m=l?`手机模型`:`Phone model`,g=d||!!f.error,_;t[1]===r?_=t[2]:(_=e=>r(e.target.value),t[1]=r,t[2]=_);let v=l?`未配置`:`Not configured`,y;t[3]===v?y=t[4]:(y=(0,h.jsx)(a,{value:``,children:v}),t[3]=v,t[4]=y);let b;t[5]!==f.data?.models||t[6]!==n||t[7]!==l?(b=n&&!f.data?.models.some(e=>e.id===n)&&(0,h.jsx)(a,{value:n,disabled:!0,children:l?`当前模型不可用,请重新选择`:`Current model unavailable; choose another`}),t[5]=f.data?.models,t[6]=n,t[7]=l,t[8]=b):b=t[8];let x;t[9]!==f.data?.models||t[10]!==l?(x=f.data?.models.map(e=>(0,h.jsx)(a,{value:e.id,children:`${e.name} · ${e.id.split(`/`)[0]}${e.configured?``:l?` · 待配置凭据`:` · Credentials needed`}`},e.id)),t[9]=f.data?.models,t[10]=l,t[11]=x):x=t[11];let S;t[12]!==m||t[13]!==g||t[14]!==_||t[15]!==y||t[16]!==b||t[17]!==x||t[18]!==n?(S=(0,h.jsxs)(i,{"aria-label":m,value:n,disabled:g,onChange:_,children:[y,b,x]}),t[12]=m,t[13]=g,t[14]=_,t[15]=y,t[16]=b,t[17]=x,t[18]=n,t[19]=S):S=t[19];let C;t[20]===f.error?C=t[21]:(C=f.error&&(0,h.jsx)(`p`,{role:`alert`,className:`text-sm text-danger`,children:String(f.error)}),t[20]=f.error,t[21]=C);let w;return t[22]!==C||t[23]!==S?(w=(0,h.jsxs)(`div`,{className:`space-y-2`,children:[S,C]}),t[22]=C,t[23]=S,t[24]=w):w=t[24],w}function _(e){let t=(0,c.c)(24),{value:n,inherited:r,onChange:i,zh:a}=e,o=a?`手机操作模型`:`Phone Use model`,s;t[0]===o?s=t[1]:(s=(0,h.jsx)(`h4`,{className:`text-sm font-medium text-fg`,children:o}),t[0]=o,t[1]=s);let l=n?n.primary:`${a?`继承全局`:`Inherited`}: ${r?.primary??`—`}`,u;t[2]===l?u=t[3]:(u=(0,h.jsx)(`p`,{className:`mt-1 break-all text-xs text-fg-muted`,children:l}),t[2]=l,t[3]=u);let d;t[4]!==s||t[5]!==u?(d=(0,h.jsxs)(`div`,{children:[s,u]}),t[4]=s,t[5]=u,t[6]=d):d=t[6];let f;t[7]!==r?.primary||t[8]!==i||t[9]!==n?(f=()=>i(n?void 0:{primary:r?.primary??``,fallbacks:[]}),t[7]=r?.primary,t[8]=i,t[9]=n,t[10]=f):f=t[10];let p=n?a?`继承全局`:`Inherit globally`:a?`单独设置`:`Override`,m;t[11]!==f||t[12]!==p?(m=(0,h.jsx)(`button`,{type:`button`,className:`touch-target rounded-lg px-3 py-2 text-sm text-accent-fg hover:bg-surface-hover focus-visible:ring-2 focus-visible:ring-accent`,onClick:f,children:p}),t[11]=f,t[12]=p,t[13]=m):m=t[13];let _;t[14]!==d||t[15]!==m?(_=(0,h.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-3`,children:[d,m]}),t[14]=d,t[15]=m,t[16]=_):_=t[16];let v;t[17]!==i||t[18]!==n||t[19]!==a?(v=n&&(0,h.jsx)(g,{value:n.primary,onChange:e=>i({primary:e,fallbacks:[]}),zh:a}),t[17]=i,t[18]=n,t[19]=a,t[20]=v):v=t[20];let y;return t[21]!==v||t[22]!==_?(y=(0,h.jsxs)(`section`,{className:`space-y-3 rounded-2xl border border-edge bg-surface-base p-4`,children:[_,v]}),t[21]=v,t[22]=_,t[23]=y):y=t[23],y}export{f as a,u as c,m as i,g as n,p as o,l as r,d as s,_ as t};