@xopcai/xopc 0.0.264 → 0.0.265

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 (175) hide show
  1. package/dist/browser-ext/dist/assets/auth-CknJ00ae.js +5844 -0
  2. package/dist/browser-ext/dist/assets/sidepanel-C69P3v9u.css +113 -0
  3. package/dist/browser-ext/dist/background.js +200 -68
  4. package/dist/browser-ext/dist/sidepanel.html +14 -0
  5. package/dist/browser-ext/dist/sidepanel.js +18832 -0
  6. package/dist/browser-ext/manifest.json +20 -20
  7. package/dist/extensions/telegram/xopc.extension.json +1 -1
  8. package/dist/gateway/static/root/assets/{activity-store-BYBCpt7_.js → activity-store-Bhyy6ENz.js} +1 -1
  9. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-D20aBHPJ.js → agent-avatar-dicebear-DRTTU2H3.js} +1 -1
  10. package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-BZvMqNrU.js → agent-defaults-settings-panel-BsXF7LVi.js} +1 -1
  11. package/dist/gateway/static/root/assets/{agents-6foBELwt.js → agents-DpjWfxYx.js} +1 -1
  12. package/dist/gateway/static/root/assets/{api-YbtjDbuG.js → api-B2y7ZjZk.js} +1 -1
  13. package/dist/gateway/static/root/assets/{app-management-settings-panel-4ps1RrDU.js → app-management-settings-panel-33Mc81h2.js} +1 -1
  14. package/dist/gateway/static/root/assets/{appearance-settings-C7nBHnkG.js → appearance-settings-C8JECSiJ.js} +1 -1
  15. package/dist/gateway/static/root/assets/{apps-page-B_g-rL5b.js → apps-page-t64FcxIZ.js} +1 -1
  16. package/dist/gateway/static/root/assets/{archive-plugin-DfKK1iJO.js → archive-plugin-PzZ6SzfY.js} +1 -1
  17. package/dist/gateway/static/root/assets/{attachment-load-BOU1pdnQ.js → attachment-load-OhLL6K__.js} +1 -1
  18. package/dist/gateway/static/root/assets/{automations-page-BePzexAH.js → automations-page-BpAYwuvx.js} +1 -1
  19. package/dist/gateway/static/root/assets/{automations-workspace-7-m5pBEZ.js → automations-workspace-B6aTT5IC.js} +1 -1
  20. package/dist/gateway/static/root/assets/{binary-plugins-Cly8XDyt.js → binary-plugins-B5XpGsHg.js} +1 -1
  21. package/dist/gateway/static/root/assets/{block-editor-DzSu62js.js → block-editor-BgEhoTnN.js} +1 -1
  22. package/dist/gateway/static/root/assets/{browser-automation-inputs-8bHj7Fv2.js → browser-automation-inputs-s5I78Iko.js} +1 -1
  23. package/dist/gateway/static/root/assets/{browser-automations-page-CzL7Aj2k.js → browser-automations-page-CBLunDRH.js} +1 -1
  24. package/dist/gateway/static/root/assets/{browser-settings-page-Dum3mTiz.js → browser-settings-page-CSfpPCuD.js} +1 -1
  25. package/dist/gateway/static/root/assets/{capabilities-settings-panel-Ci6qVS-O.js → capabilities-settings-panel-DftNnICK.js} +2 -2
  26. package/dist/gateway/static/root/assets/{channels-settings-BOGEZnSv.js → channels-settings-CpuWa5qu.js} +1 -1
  27. package/dist/gateway/static/root/assets/{channels-status-swr-BR8oQ_eP.js → channels-status-swr-_OsWr_vS.js} +1 -1
  28. package/dist/gateway/static/root/assets/{chat-terminal-dock-BF9ZXa5Z.js → chat-terminal-dock-mnPsS_GJ.js} +1 -1
  29. package/dist/gateway/static/root/assets/{connectors-page-DBvEvupa.js → connectors-page-D3nHGnNV.js} +1 -1
  30. package/dist/gateway/static/root/assets/{cron-expression-BszT_gjK.js → cron-expression-NcGBvIfe.js} +1 -1
  31. package/dist/gateway/static/root/assets/{date-picker-D2MKBJ0G.js → date-picker-BrVcl7H5.js} +1 -1
  32. package/dist/gateway/static/root/assets/{dependency-picker-DqigBx-G.js → dependency-picker--edd48Y9.js} +1 -1
  33. package/dist/gateway/static/root/assets/{desktop-pet-settings-DMPk4CaK.js → desktop-pet-settings-DWredJwO.js} +1 -1
  34. package/dist/gateway/static/root/assets/{desktop-pet-CapspBGG.js → desktop-pet-vuyyb6I6.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-debug-page-CXLsDtrW.js → extension-debug-page-CtBW4ebj.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-page-B2j87ROw.js → extension-page-CsUrmtW7.js} +1 -1
  37. package/dist/gateway/static/root/assets/{extension-settings-page-t1BApw-u.js → extension-settings-page-BUd31AuK.js} +1 -1
  38. package/dist/gateway/static/root/assets/{files-BArWTfr7.js → files-Be5viEFY.js} +1 -1
  39. package/dist/gateway/static/root/assets/{gateway-config-swr-BFSesvAC.js → gateway-config-swr-OObCK8jM.js} +1 -1
  40. package/dist/gateway/static/root/assets/{gateway-settings-BFAecoGy.js → gateway-settings-C6FQv7op.js} +1 -1
  41. package/dist/gateway/static/root/assets/{gateway-startup-retry-D1giYgI-.js → gateway-startup-retry-D_TxKN_A.js} +1 -1
  42. package/dist/gateway/static/root/assets/{heartbeat-settings-CyD9zDcU.js → heartbeat-settings-CDo1Gjp2.js} +1 -1
  43. package/dist/gateway/static/root/assets/{home-page-uItZMiBE.js → home-page-D3_uvVJY.js} +1 -1
  44. package/dist/gateway/static/root/assets/{image-generation-api-BeB-OPqO.js → image-generation-api-fPU9yGsw.js} +1 -1
  45. package/dist/gateway/static/root/assets/index-Dpu7EKTm.js +74 -0
  46. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-Q_CM-ZCs.js → keyboard-shortcuts-settings-CNVmrCrV.js} +1 -1
  47. package/dist/gateway/static/root/assets/{local-app-workbench-page-DaiBWfOt.js → local-app-workbench-page-B0IVDvs-.js} +1 -1
  48. package/dist/gateway/static/root/assets/{local-apps-page-BALMNCSp.js → local-apps-page-DvkarvLt.js} +1 -1
  49. package/dist/gateway/static/root/assets/{locale-store-RJHYFoq9.js → locale-store-DFQX9kTl.js} +1 -1
  50. package/dist/gateway/static/root/assets/{logs-page-C-TS7oFg.js → logs-page-BknY0vZf.js} +1 -1
  51. package/dist/gateway/static/root/assets/{management-settings-BajkJeYl.js → management-settings-DW6hJINQ.js} +1 -1
  52. package/dist/gateway/static/root/assets/{markdown-view-BxoQJqHx.js → markdown-view-D-5jHRWg.js} +1 -1
  53. package/dist/gateway/static/root/assets/{media-plugins-C_8Hl6KG.js → media-plugins-DYautOWY.js} +1 -1
  54. package/dist/gateway/static/root/assets/{messages-Cf1h5WXB.js → messages-Dj8QuS6e.js} +2 -2
  55. package/dist/gateway/static/root/assets/{mobile-pairing-wizard-CsWIZrZW.js → mobile-pairing-wizard-Dgu03cv-.js} +1 -1
  56. package/dist/gateway/static/root/assets/{note-detail-panel-CCbPg_GP.js → note-detail-panel-Cv9uH6Jq.js} +3 -3
  57. package/dist/gateway/static/root/assets/{notes-page-BdxxcUKa.js → notes-page-DcIAiYJn.js} +1 -1
  58. package/dist/gateway/static/root/assets/{page-tabs-BMYQef5D.js → page-tabs-DF6Q5XvA.js} +1 -1
  59. package/dist/gateway/static/root/assets/{preview-open-alternatives-CKZ-H5hJ.js → preview-open-alternatives-CQJH_jAN.js} +1 -1
  60. package/dist/gateway/static/root/assets/{product-delivery-CV4pDOtG.js → product-delivery-CCC4FJ3Y.js} +1 -1
  61. package/dist/gateway/static/root/assets/{product-open-page-zXJIptXs.js → product-open-page-CIGLPFm7.js} +1 -1
  62. package/dist/gateway/static/root/assets/{project-detail-page-DRhhxhZB.js → project-detail-page-D65JfNSw.js} +1 -1
  63. package/dist/gateway/static/root/assets/{projects-page-Wt7gF3Sx.js → projects-page-BfWF7Sz-.js} +1 -1
  64. package/dist/gateway/static/root/assets/{registry-api-BSfFaxVN.js → registry-api-CsZLatKo.js} +1 -1
  65. package/dist/gateway/static/root/assets/{remote-access-hub-B6de5Zrb.js → remote-access-hub-DA1g4CUD.js} +1 -1
  66. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DcUB5h1f.js → runtime-tools-settings-panel-0-bFSBB8.js} +1 -1
  67. package/dist/gateway/static/root/assets/{schema-form-CquWhe7r.js → schema-form-csGbhTUr.js} +1 -1
  68. package/dist/gateway/static/root/assets/{schemas-BHS7bo06.js → schemas-BaskVM5g.js} +2 -2
  69. package/dist/gateway/static/root/assets/{sessions-page-BNWO2EVF.js → sessions-page-DXPBNhrZ.js} +1 -1
  70. package/dist/gateway/static/root/assets/settings-advanced-gate-DxeZm67O.js +1 -0
  71. package/dist/gateway/static/root/assets/{settings-form-section-DwswAefc.js → settings-form-section-YdrdY8B3.js} +1 -1
  72. package/dist/gateway/static/root/assets/{settings-page-CoAVmjjs.js → settings-page-Cv9waRn3.js} +1 -1
  73. package/dist/gateway/static/root/assets/{setup-status-panel-C9EIcWtH.js → setup-status-panel-DPf-_05Z.js} +1 -1
  74. package/dist/gateway/static/root/assets/{share-preview-page-DWwy_4zh.js → share-preview-page-DBoEHTPn.js} +1 -1
  75. package/dist/gateway/static/root/assets/{shares-settings-CIUvq27-.js → shares-settings-BQP5yNcO.js} +1 -1
  76. package/dist/gateway/static/root/assets/skill-reload-api-B5T8cgjq.js +1 -0
  77. package/dist/gateway/static/root/assets/{skills-page-CqsgVtVh.js → skills-page-D1G8h2Lt.js} +1 -1
  78. package/dist/gateway/static/root/assets/{skills-page.utils-B-N73248.js → skills-page.utils-BNZYTop7.js} +1 -1
  79. package/dist/gateway/static/root/assets/{system-settings-panel-BndjXuBZ.js → system-settings-panel-ByuppbHi.js} +1 -1
  80. package/dist/gateway/static/root/assets/{task-detail-page-CG6Ha9t7.js → task-detail-page-DNmkrF6y.js} +1 -1
  81. package/dist/gateway/static/root/assets/{theme-store-C5ssn64t.js → theme-store-zRT6vaZc.js} +1 -1
  82. package/dist/gateway/static/root/assets/{tunnel-api-BxZdcp3h.js → tunnel-api-CxWGpbwr.js} +1 -1
  83. package/dist/gateway/static/root/assets/{use-autosave-mj9CUuz1.js → use-autosave-B0l4qubU.js} +1 -1
  84. package/dist/gateway/static/root/assets/{user-model-page-DKeHMXI4.js → user-model-page-DMsI6YI7.js} +1 -1
  85. package/dist/gateway/static/root/assets/{voice-api-key-field-CzxVMYlr.js → voice-api-key-field-Bcz7boJg.js} +1 -1
  86. package/dist/gateway/static/root/assets/{voice-config-api-COA0V1cK.js → voice-config-api-DpJEu2RQ.js} +1 -1
  87. package/dist/gateway/static/root/assets/{work-discovery-overlay-DMiPK8dp.js → work-discovery-overlay-Dujq1j_u.js} +1 -1
  88. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-C82OXCJK.js → workflow-run-setup-panel-CxNYbLNB.js} +1 -1
  89. package/dist/gateway/static/root/assets/{workflows-page-HLCUV3rQ.js → workflows-page-Dk1jK2nv.js} +1 -1
  90. package/dist/gateway/static/root/index.html +12 -12
  91. package/dist/package.js +1 -1
  92. package/dist/packages/browser-control-contract/src/index.js +14 -0
  93. package/dist/packages/endpoint-tools-protocol/src/index.js +2 -1
  94. package/dist/packages/gateway-contract/src/browser-page-context.js +43 -0
  95. package/dist/packages/gateway-contract/src/browser-tab-binding.js +29 -0
  96. package/dist/packages/gateway-contract/src/device-pairing.js +18 -2
  97. package/dist/packages/realtime-protocol/src/index.js +1 -0
  98. package/dist/packages/realtime-protocol/src/voice-audio.js +70 -9
  99. package/dist/packages/realtime-protocol/src/voice.js +65 -17
  100. package/dist/src/agent/source-context/browser-page.d.ts +3 -0
  101. package/dist/src/agent/source-context/browser-page.js +39 -0
  102. package/dist/src/agent/source-context/injector.js +9 -2
  103. package/dist/src/agent/source-context/types.d.ts +7 -1
  104. package/dist/src/agent/source-context/types.js +4 -1
  105. package/dist/src/agent/tools/factory.js +9 -1
  106. package/dist/src/browser/automations/schema.d.ts +1 -1
  107. package/dist/src/browser/automations/service.d.ts +1 -1
  108. package/dist/src/browser/native-messaging-host.d.ts +15 -0
  109. package/dist/src/browser/native-messaging-host.js +105 -0
  110. package/dist/src/browser/providers/browser-ext-install.d.ts +17 -1
  111. package/dist/src/browser/providers/browser-ext-install.js +82 -16
  112. package/dist/src/browser/providers/extension.d.ts +4 -0
  113. package/dist/src/browser/providers/extension.js +104 -38
  114. package/dist/src/browser/runtime/browser-runtime.d.ts +2 -1
  115. package/dist/src/browser/runtime/browser-runtime.js +6 -2
  116. package/dist/src/cli/commands/browser.js +13 -3
  117. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  118. package/dist/src/endpoint-tools/runtime.js +2 -0
  119. package/dist/src/gateway/chat-stream/mapper.js +3 -2
  120. package/dist/src/gateway/hono/app.js +19 -3
  121. package/dist/src/gateway/hono/middleware/auth.d.ts +4 -1
  122. package/dist/src/gateway/hono/middleware/auth.js +12 -1
  123. package/dist/src/gateway/hono/routes/browser-install.js +1 -1
  124. package/dist/src/gateway/hono/routes/browser.js +106 -7
  125. package/dist/src/gateway/hono/routes/devices.js +36 -3
  126. package/dist/src/gateway/hono/routes/endpoint-tools.js +15 -0
  127. package/dist/src/gateway/hono/routes/session-input-handler.d.ts +20 -2
  128. package/dist/src/gateway/hono/routes/session-input-handler.js +18 -0
  129. package/dist/src/gateway/hono/routes/sessions.js +12 -6
  130. package/dist/src/gateway/hono/routes/voice.js +5 -4
  131. package/dist/src/gateway/security/gateway-scopes.d.ts +1 -0
  132. package/dist/src/gateway/security/gateway-scopes.js +10 -1
  133. package/dist/src/gateway/security/origin-check.d.ts +1 -0
  134. package/dist/src/gateway/security/origin-check.js +5 -1
  135. package/dist/src/gateway/service/agent-runner.js +2 -1
  136. package/dist/src/gateway/service/session-input-coordinator.d.ts +1 -0
  137. package/dist/src/gateway/service/session-input-coordinator.js +5 -4
  138. package/dist/src/gateway/service.js +12 -11
  139. package/dist/src/realtime/runtime.js +2 -1
  140. package/dist/src/session/client-history.d.ts +4 -1
  141. package/dist/src/session/client-history.js +6 -3
  142. package/dist/src/storage/sqlite/browser-tab-binding-repository.d.ts +7 -0
  143. package/dist/src/storage/sqlite/browser-tab-binding-repository.js +60 -0
  144. package/dist/src/storage/sqlite/device-access-repository.d.ts +3 -1
  145. package/dist/src/storage/sqlite/device-access-repository.js +4 -3
  146. package/dist/src/storage/sqlite/device-pairing-approval.js +6 -4
  147. package/dist/src/storage/sqlite/device-pairing-repository.d.ts +1 -1
  148. package/dist/src/storage/sqlite/index.d.ts +1 -0
  149. package/dist/src/storage/sqlite/index.js +2 -1
  150. package/dist/src/storage/sqlite/migrations/155_browser_extension.sql +120 -0
  151. package/dist/src/storage/sqlite/migrations/156_browser_tab_bindings.sql +14 -0
  152. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  153. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  154. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  155. package/dist/src/storage/sqlite/schema.js +1 -1
  156. package/dist/src/voice/realtime/agentBroker.d.ts +54 -0
  157. package/dist/src/voice/realtime/agentBroker.js +116 -0
  158. package/dist/src/voice/realtime/agentEngine.js +62 -15
  159. package/dist/src/voice/realtime/engine.d.ts +2 -1
  160. package/dist/src/voice/realtime/omniEngine.js +36 -7
  161. package/dist/src/voice/realtime/pcmFrameBuffer.d.ts +6 -0
  162. package/dist/src/voice/realtime/pcmFrameBuffer.js +29 -0
  163. package/dist/src/voice/realtime/runtime.js +97 -21
  164. package/dist/src/voice/realtime/runtime.types.d.ts +5 -21
  165. package/dist/src/voice/realtime/turnPolicy.d.ts +36 -0
  166. package/dist/src/voice/realtime/turnPolicy.js +134 -0
  167. package/package.json +2 -2
  168. package/dist/browser-ext/dist/content.js +0 -191
  169. package/dist/browser-ext/dist/popup.js +0 -32
  170. package/dist/browser-ext/popup.html +0 -37
  171. package/dist/gateway/static/root/assets/index-DQtjs2x3.js +0 -74
  172. package/dist/gateway/static/root/assets/settings-advanced-gate-DK1yTYE4.js +0 -1
  173. package/dist/gateway/static/root/assets/skill-reload-api-DfE3zFVw.js +0 -1
  174. package/dist/src/voice/realtime/conversationTurn.d.ts +0 -20
  175. package/dist/src/voice/realtime/conversationTurn.js +0 -83
@@ -1,4 +1,4 @@
1
- import type { CreateVoiceSessionRequest, VoiceProviderRoute } from '@xopcai/realtime-protocol/voice';
1
+ import type { CreateVoiceSessionRequest, VoiceMode, VoiceProviderRoute } from '@xopcai/realtime-protocol/voice';
2
2
  import type { Config } from '../../config/schema.js';
3
3
  import type { MediaUnderstandingProvider } from '../../media-understanding/types.js';
4
4
  import type { ResolvedSpeechProvider } from '../tts/factory.js';
@@ -6,6 +6,7 @@ import type { TTSConfig } from '../tts/types.js';
6
6
  import type { VoiceConversationContext } from './conversation-context.js';
7
7
  import type { OmniTranscript } from './omniEngine.js';
8
8
  import type { OmniRoute } from './omniRoute.js';
9
+ import type { VoiceAgentBroker } from './agentBroker.js';
9
10
  export interface ResolvedStreamingStt {
10
11
  plugin: MediaUnderstandingProvider & Required<Pick<MediaUnderstandingProvider, 'openAudioStream' | 'streamingAudio'>>;
11
12
  model: string;
@@ -18,7 +19,9 @@ export interface ResolvedStreamingStt {
18
19
  }
19
20
  export interface VoiceTicketClaim {
20
21
  sessionId: string;
22
+ connectionEpoch: number;
21
23
  principalId: string;
24
+ mode?: VoiceMode;
22
25
  request: CreateVoiceSessionRequest;
23
26
  inputMode: 'server_vad';
24
27
  idleTimeoutMs: number;
@@ -37,24 +40,6 @@ export interface ResolvedStreamingTts {
37
40
  config: TTSConfig;
38
41
  route: VoiceProviderRoute;
39
42
  }
40
- interface VoiceAgentEvent {
41
- type: string;
42
- payload?: {
43
- delta?: unknown;
44
- message?: unknown;
45
- status?: unknown;
46
- toolCallId?: unknown;
47
- toolName?: unknown;
48
- requestId?: unknown;
49
- kind?: unknown;
50
- question?: unknown;
51
- choices?: unknown;
52
- suggestedAnswer?: unknown;
53
- version?: unknown;
54
- createdAt?: unknown;
55
- expiresAt?: unknown;
56
- };
57
- }
58
43
  export interface VoiceRealtimeRuntimeOptions {
59
44
  recordOmniTranscript?: (sessionKey: string, callId: string, entry: OmniTranscript, expectedSessionId: string) => Promise<void>;
60
45
  getConversationContext?: (sessionKey: string, expectedSessionId: string) => Promise<VoiceConversationContext>;
@@ -62,7 +47,7 @@ export interface VoiceRealtimeRuntimeOptions {
62
47
  getConfig: () => Config;
63
48
  sessionExists: (sessionKey: string) => Promise<boolean>;
64
49
  sessionBusy: (sessionKey: string) => boolean;
65
- runAgent: (text: string, sessionKey: string, signal: AbortSignal) => AsyncIterable<VoiceAgentEvent>;
50
+ agentBroker: VoiceAgentBroker;
66
51
  recordInterruption: (entry: {
67
52
  sessionKey: string;
68
53
  responseId: string;
@@ -71,4 +56,3 @@ export interface VoiceRealtimeRuntimeOptions {
71
56
  interruptedDuring: 'thinking' | 'speaking';
72
57
  }) => Promise<void>;
73
58
  }
74
- export {};
@@ -0,0 +1,36 @@
1
+ export type TurnDisposition = 'complete' | 'incomplete' | 'backchannel' | 'wait';
2
+ export interface TurnSnapshot {
3
+ text: string;
4
+ silenceDurationMs: number;
5
+ }
6
+ export interface TurnDecision {
7
+ disposition: TurnDisposition;
8
+ confidence: number;
9
+ holdMs: number;
10
+ source: 'provider' | 'semantic' | 'heuristic';
11
+ }
12
+ export interface TurnPolicy {
13
+ decide(snapshot: TurnSnapshot): TurnDecision | Promise<TurnDecision>;
14
+ }
15
+ export declare function hasContinuationHint(text: string): boolean;
16
+ export declare const heuristicTurnPolicy: TurnPolicy;
17
+ /** Accumulates provider utterances and commits only after the selected policy settles. */
18
+ export declare class TurnCoordinator {
19
+ private readonly silenceDurationMs;
20
+ private readonly onCommit;
21
+ private readonly policy;
22
+ private readonly parts;
23
+ private timer;
24
+ private stoppedAt;
25
+ private decisionGeneration;
26
+ constructor(silenceDurationMs: number, onCommit: (text: string, decision: TurnDecision, turnId: string) => void, policy?: TurnPolicy);
27
+ start(id: string): void;
28
+ stop(id: string): void;
29
+ final(id: string, text: string): void;
30
+ reset(): void;
31
+ restore(id: string, text: string): void;
32
+ private text;
33
+ private invalidatePendingDecision;
34
+ private evaluate;
35
+ private schedule;
36
+ }
@@ -0,0 +1,134 @@
1
+ //#region src/voice/realtime/turnPolicy.ts
2
+ const MIN_TURN_SILENCE_MS = 1200;
3
+ const FINAL_SETTLE_MS = 350;
4
+ const INCOMPLETE_HOLD_MS = 1800;
5
+ const DECISION_TIMEOUT_MS = 750;
6
+ const MAX_TRANSCRIPT_CHARACTERS = 32e3;
7
+ const MAX_UTTERANCES = 256;
8
+ function isPromise(value) {
9
+ return typeof value.then === "function";
10
+ }
11
+ function hasContinuationHint(text) {
12
+ const tail = text.trim().replace(/[\s,。!?、;:,.!?;:]+$/u, "");
13
+ return /(?:…|\.\.\.)[\s,。!?,.!?]*$/u.test(text) || /(?:因为|但是|然后|而且|如果|所以|比如|或者|还有|我想|我要|我觉得|帮我|帮我查一下|帮我查查|请帮我|就是说|那个|这个|呃|嗯)$/u.test(tail) || /\b(?:because|but|and|or|if|so|then|um|uh|i want to|i would like to|can you|could you|let me)\s*$/iu.test(tail);
14
+ }
15
+ const heuristicTurnPolicy = { decide({ text }) {
16
+ const incomplete = hasContinuationHint(text);
17
+ return {
18
+ disposition: incomplete ? "incomplete" : "complete",
19
+ confidence: incomplete ? .6 : .5,
20
+ holdMs: incomplete ? INCOMPLETE_HOLD_MS : 0,
21
+ source: "heuristic"
22
+ };
23
+ } };
24
+ /** Accumulates provider utterances and commits only after the selected policy settles. */
25
+ var TurnCoordinator = class {
26
+ parts = /* @__PURE__ */ new Map();
27
+ timer;
28
+ stoppedAt = 0;
29
+ decisionGeneration = 0;
30
+ constructor(silenceDurationMs, onCommit, policy = heuristicTurnPolicy) {
31
+ this.silenceDurationMs = silenceDurationMs;
32
+ this.onCommit = onCommit;
33
+ this.policy = policy;
34
+ }
35
+ start(id) {
36
+ if (this.parts.has(id)) return;
37
+ this.invalidatePendingDecision();
38
+ this.parts.set(id, { stopped: false });
39
+ }
40
+ stop(id) {
41
+ const part = this.parts.get(id);
42
+ if (part?.stopped) return;
43
+ this.parts.set(id, {
44
+ ...part,
45
+ stopped: true
46
+ });
47
+ this.stoppedAt = Date.now();
48
+ this.evaluate();
49
+ }
50
+ final(id, text) {
51
+ const part = this.parts.get(id);
52
+ if (part?.text !== void 0) return;
53
+ if (!part?.stopped) this.stoppedAt = Date.now();
54
+ this.parts.set(id, {
55
+ text: text.trim(),
56
+ stopped: true
57
+ });
58
+ if (this.parts.size > MAX_UTTERANCES || this.text().length > MAX_TRANSCRIPT_CHARACTERS) {
59
+ this.reset();
60
+ throw new Error("Voice turn input limit reached");
61
+ }
62
+ this.evaluate();
63
+ }
64
+ reset() {
65
+ this.invalidatePendingDecision();
66
+ this.parts.clear();
67
+ }
68
+ restore(id, text) {
69
+ this.invalidatePendingDecision();
70
+ this.parts.set(id, {
71
+ text,
72
+ stopped: true
73
+ });
74
+ }
75
+ text() {
76
+ return [...this.parts.values()].map((part) => part.text ?? "").filter(Boolean).join(" ");
77
+ }
78
+ invalidatePendingDecision() {
79
+ this.decisionGeneration += 1;
80
+ clearTimeout(this.timer);
81
+ this.timer = void 0;
82
+ }
83
+ evaluate() {
84
+ this.invalidatePendingDecision();
85
+ if ([...this.parts.values()].some((part) => !part.stopped || part.text === void 0)) return;
86
+ const text = this.text();
87
+ if (!text) {
88
+ this.reset();
89
+ return;
90
+ }
91
+ const generation = this.decisionGeneration;
92
+ const snapshot = {
93
+ text,
94
+ silenceDurationMs: Math.max(0, Date.now() - this.stoppedAt)
95
+ };
96
+ let result;
97
+ try {
98
+ result = this.policy.decide(snapshot);
99
+ } catch {
100
+ this.schedule(text, heuristicTurnPolicy.decide(snapshot), generation);
101
+ return;
102
+ }
103
+ if (isPromise(result)) {
104
+ let settled = false;
105
+ const finish = (decision) => {
106
+ if (settled || generation !== this.decisionGeneration) return;
107
+ settled = true;
108
+ clearTimeout(timeout);
109
+ this.schedule(text, decision, generation);
110
+ };
111
+ const fallback = () => finish(heuristicTurnPolicy.decide(snapshot));
112
+ const timeout = setTimeout(fallback, DECISION_TIMEOUT_MS);
113
+ result.then(finish).catch(fallback);
114
+ return;
115
+ }
116
+ this.schedule(text, result, generation);
117
+ }
118
+ schedule(text, decision, generation) {
119
+ if (generation !== this.decisionGeneration) return;
120
+ const holdMs = Math.max(FINAL_SETTLE_MS, MIN_TURN_SILENCE_MS - this.silenceDurationMs, Number.isFinite(decision.holdMs) ? Math.max(0, decision.holdMs) : 0);
121
+ this.timer = setTimeout(() => {
122
+ if (generation !== this.decisionGeneration) return;
123
+ const turnId = [...this.parts.keys()][0];
124
+ if (decision.disposition === "wait") {
125
+ this.evaluate();
126
+ return;
127
+ }
128
+ this.reset();
129
+ if (decision.disposition !== "backchannel") this.onCommit(text, decision, turnId);
130
+ }, Math.max(FINAL_SETTLE_MS, this.stoppedAt + holdMs - Date.now()));
131
+ }
132
+ };
133
+ //#endregion
134
+ export { TurnCoordinator, hasContinuationHint, heuristicTurnPolicy };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xopcai/xopc",
3
- "version": "0.0.264",
3
+ "version": "0.0.265",
4
4
  "description": "Local-first personal AI assistant with reviewable user understanding, durable tasks, workflows, automations, and local or cloud models.",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -163,9 +163,9 @@
163
163
  "vitepress": "^1.6.4",
164
164
  "vitest": "4.1.5",
165
165
  "vue": "^3.5.35",
166
+ "@xopcai/browser-control-contract": "0.1.0",
166
167
  "@xopcai/endpoint-tools-protocol": "0.0.0",
167
168
  "@xopcai/realtime-client": "0.0.0",
168
- "@xopcai/browser-control-contract": "0.1.0",
169
169
  "@xopcai/realtime-protocol": "0.0.0",
170
170
  "@xopcai/gateway-contract": "0.0.0"
171
171
  },
@@ -1,191 +0,0 @@
1
- const OVERLAY_HOST_ID = "xopc-overlay-host";
2
- let overlayHost = null;
3
- let overlayInner = null;
4
- let completedTimer = null;
5
- function ensureOverlay() {
6
- if (overlayHost?.isConnected && overlayInner) return;
7
- removeOverlay();
8
- const existing = document.getElementById(OVERLAY_HOST_ID);
9
- if (existing) existing.remove();
10
- overlayHost = document.createElement("div");
11
- overlayHost.id = OVERLAY_HOST_ID;
12
- const shadow = overlayHost.attachShadow({ mode: "open" });
13
- const style = document.createElement("style");
14
- style.textContent = `
15
- :host { all: initial; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 2147483647; pointer-events: none; }
16
- .overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; }
17
- .overlay.operating { border: 2px solid rgba(59, 130, 246, 0.6); animation: pulse 2s ease-in-out infinite; }
18
- .overlay.completed { border: 2px solid rgba(34, 197, 94, 0.6); }
19
- .badge { position: fixed; top: 8px; right: 8px; padding: 4px 12px; border-radius: 12px; font: 500 12px -apple-system, sans-serif; color: white; opacity: 0; transform: translateY(-8px); animation: badge-in 0.3s ease forwards; }
20
- .badge.operating { background: rgba(59, 130, 246, 0.85); backdrop-filter: blur(8px); }
21
- .badge.completed { background: rgba(34, 197, 94, 0.85); backdrop-filter: blur(8px); }
22
- .dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: white; margin-right: 6px; vertical-align: middle; }
23
- .badge.operating .dot { animation: dot-pulse 1.2s ease-in-out infinite; }
24
- @keyframes pulse { 0%, 100% { border-color: rgba(59, 130, 246, 0.3); } 50% { border-color: rgba(59, 130, 246, 0.8); } }
25
- @keyframes badge-in { to { opacity: 1; transform: translateY(0); } }
26
- @keyframes dot-pulse { 0%, 100% { opacity: 0.4; transform: scale(0.8); } 50% { opacity: 1; transform: scale(1.2); } }
27
- `;
28
- shadow.appendChild(style);
29
- overlayInner = document.createElement("div");
30
- overlayInner.className = "overlay";
31
- shadow.appendChild(overlayInner);
32
- document.documentElement.appendChild(overlayHost);
33
- }
34
- function removeOverlay() {
35
- if (completedTimer) {
36
- clearTimeout(completedTimer);
37
- completedTimer = null;
38
- }
39
- overlayInner = null;
40
- if (overlayHost) {
41
- overlayHost.remove();
42
- overlayHost = null;
43
- }
44
- document.documentElement.style.cursor = "";
45
- }
46
- function showOverlay(status) {
47
- if (completedTimer) {
48
- clearTimeout(completedTimer);
49
- completedTimer = null;
50
- }
51
- if (status === "hidden") {
52
- removeOverlay();
53
- return;
54
- }
55
- ensureOverlay();
56
- if (!overlayInner) return;
57
- overlayInner.innerHTML = "";
58
- overlayInner.className = `overlay ${status}`;
59
- const badge = document.createElement("div");
60
- badge.className = `badge ${status}`;
61
- const dot = document.createElement("span");
62
- dot.className = "dot";
63
- badge.appendChild(dot);
64
- badge.appendChild(document.createTextNode(status === "operating" ? "🤖 xopc" : "✅ Done"));
65
- overlayInner.appendChild(badge);
66
- document.documentElement.style.cursor = status === "operating" ? "not-allowed" : "";
67
- if (status === "completed") {
68
- completedTimer = setTimeout(removeOverlay, 2e3);
69
- }
70
- }
71
- function showClickRipple(x, y) {
72
- if (!document.getElementById("xopc-ripple-style")) {
73
- const s = document.createElement("style");
74
- s.id = "xopc-ripple-style";
75
- s.textContent = `@keyframes xopc-ripple { 0% { transform: scale(0.3); opacity: 1; } 100% { transform: scale(2.5); opacity: 0; } }`;
76
- document.head.appendChild(s);
77
- }
78
- const el = document.createElement("div");
79
- Object.assign(el.style, {
80
- position: "fixed",
81
- left: `${x - 15}px`,
82
- top: `${y - 15}px`,
83
- width: "30px",
84
- height: "30px",
85
- borderRadius: "50%",
86
- background: "rgba(59, 130, 246, 0.4)",
87
- border: "2px solid rgba(59, 130, 246, 0.7)",
88
- pointerEvents: "none",
89
- zIndex: "2147483646",
90
- animation: "xopc-ripple 0.6s ease-out forwards"
91
- });
92
- document.body.appendChild(el);
93
- setTimeout(() => el.remove(), 600);
94
- }
95
- function showHoverHighlight(selector) {
96
- const element = document.querySelector(selector);
97
- if (!element) return;
98
- const rect = element.getBoundingClientRect();
99
- const el = document.createElement("div");
100
- Object.assign(el.style, {
101
- position: "fixed",
102
- left: `${rect.left - 2}px`,
103
- top: `${rect.top - 2}px`,
104
- width: `${rect.width + 4}px`,
105
- height: `${rect.height + 4}px`,
106
- border: "2px solid rgba(59, 130, 246, 0.6)",
107
- borderRadius: "4px",
108
- background: "rgba(59, 130, 246, 0.08)",
109
- pointerEvents: "none",
110
- zIndex: "2147483645",
111
- transition: "opacity 0.3s ease",
112
- opacity: "1"
113
- });
114
- document.body.appendChild(el);
115
- setTimeout(() => {
116
- el.style.opacity = "0";
117
- setTimeout(() => el.remove(), 300);
118
- }, 800);
119
- }
120
- function showInputFlash(selector) {
121
- const el = document.querySelector(selector);
122
- if (!el) return;
123
- const orig = el.style.backgroundColor;
124
- el.style.transition = "background-color 0.15s ease";
125
- el.style.backgroundColor = "rgba(34, 197, 94, 0.15)";
126
- setTimeout(() => {
127
- el.style.backgroundColor = orig;
128
- }, 300);
129
- }
130
- function showScrollIndicator(direction) {
131
- const el = document.createElement("div");
132
- Object.assign(el.style, {
133
- position: "fixed",
134
- right: "20px",
135
- [direction === "down" ? "bottom" : "top"]: "20px",
136
- width: "36px",
137
- height: "36px",
138
- borderRadius: "50%",
139
- background: "rgba(59, 130, 246, 0.85)",
140
- color: "white",
141
- display: "flex",
142
- alignItems: "center",
143
- justifyContent: "center",
144
- fontSize: "18px",
145
- fontWeight: "bold",
146
- pointerEvents: "none",
147
- zIndex: "2147483646",
148
- opacity: "0",
149
- transform: `translateY(${direction === "down" ? "10px" : "-10px"})`,
150
- transition: "opacity 0.2s ease, transform 0.2s ease"
151
- });
152
- el.textContent = direction === "down" ? "↓" : "↑";
153
- document.body.appendChild(el);
154
- requestAnimationFrame(() => {
155
- el.style.opacity = "1";
156
- el.style.transform = "translateY(0)";
157
- });
158
- setTimeout(() => {
159
- el.style.opacity = "0";
160
- setTimeout(() => el.remove(), 200);
161
- }, 600);
162
- }
163
- chrome.runtime.onMessage.addListener(
164
- (message, _sender, sendResponse) => {
165
- switch (message.type) {
166
- case "content/show-overlay":
167
- showOverlay(message.status);
168
- break;
169
- case "content/hide-overlay":
170
- removeOverlay();
171
- break;
172
- case "content/show-click-ripple":
173
- showClickRipple(message.x, message.y);
174
- break;
175
- case "content/show-hover-highlight":
176
- showHoverHighlight(message.selector);
177
- break;
178
- case "content/show-input-flash":
179
- showInputFlash(message.selector);
180
- break;
181
- case "content/show-scroll-indicator":
182
- showScrollIndicator(message.direction);
183
- break;
184
- default:
185
- sendResponse({ ok: false });
186
- return true;
187
- }
188
- sendResponse({ ok: true });
189
- return true;
190
- }
191
- );
@@ -1,32 +0,0 @@
1
- function updateUI(status) {
2
- const dot = document.getElementById("status-dot");
3
- const label = document.getElementById("status-label");
4
- const urlEl = document.getElementById("server-url");
5
- const connectBtn = document.getElementById("btn-connect");
6
- const disconnectBtn = document.getElementById("btn-disconnect");
7
- if (status.connected) {
8
- dot.className = "dot connected";
9
- label.textContent = "Connected";
10
- connectBtn.classList.add("hidden");
11
- disconnectBtn.classList.remove("hidden");
12
- } else {
13
- dot.className = "dot disconnected";
14
- label.textContent = "Disconnected";
15
- connectBtn.classList.remove("hidden");
16
- disconnectBtn.classList.add("hidden");
17
- }
18
- urlEl.textContent = status.url ?? "ws://127.0.0.1:19820/browser-ext";
19
- }
20
- async function getStatus() {
21
- const response = await chrome.runtime.sendMessage({ type: "popup/get-status" });
22
- updateUI(response);
23
- }
24
- document.getElementById("btn-connect")?.addEventListener("click", async () => {
25
- await chrome.runtime.sendMessage({ type: "popup/connect" });
26
- setTimeout(getStatus, 500);
27
- });
28
- document.getElementById("btn-disconnect")?.addEventListener("click", async () => {
29
- await chrome.runtime.sendMessage({ type: "popup/disconnect" });
30
- setTimeout(getStatus, 200);
31
- });
32
- getStatus();
@@ -1,37 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <style>
6
- * { margin: 0; padding: 0; box-sizing: border-box; }
7
- body { width: 280px; padding: 16px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 13px; color: #1e293b; background: #fff; }
8
- h1 { font-size: 14px; font-weight: 600; margin-bottom: 12px; display: flex; align-items: center; gap: 8px; }
9
- h1 .logo { width: 20px; height: 20px; flex-shrink: 0; }
10
- .status-row { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; padding: 8px 10px; background: #f8fafc; border-radius: 6px; border: 1px solid #e2e8f0; }
11
- .dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
12
- .dot.connected { background: #22c55e; box-shadow: 0 0 4px rgba(34, 197, 94, 0.4); }
13
- .dot.disconnected { background: #ef4444; }
14
- .server-url { font-size: 11px; color: #64748b; margin-top: 4px; word-break: break-all; }
15
- .actions { display: flex; gap: 8px; margin-top: 12px; }
16
- button { flex: 1; padding: 6px 12px; border-radius: 6px; border: 1px solid #e2e8f0; background: #f8fafc; font-size: 12px; cursor: pointer; transition: all 0.15s; }
17
- button:hover { background: #e2e8f0; }
18
- button.primary { background: #3b82f6; color: white; border-color: #3b82f6; }
19
- button.primary:hover { background: #2563eb; }
20
- button.danger { color: #ef4444; }
21
- .hidden { display: none; }
22
- </style>
23
- </head>
24
- <body>
25
- <h1><img class="logo" src="icons/icon-32.png" width="20" height="20" alt="" /> xopc Browser Bridge</h1>
26
- <div class="status-row">
27
- <span id="status-dot" class="dot disconnected"></span>
28
- <span id="status-label">Checking...</span>
29
- </div>
30
- <div class="server-url" id="server-url">ws://127.0.0.1:19820/browser-ext</div>
31
- <div class="actions">
32
- <button id="btn-connect" class="primary hidden">Connect</button>
33
- <button id="btn-disconnect" class="danger hidden">Disconnect</button>
34
- </div>
35
- <script type="module" src="dist/popup.js"></script>
36
- </body>
37
- </html>