@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.
- package/dist/browser-ext/dist/assets/auth-CknJ00ae.js +5844 -0
- package/dist/browser-ext/dist/assets/sidepanel-C69P3v9u.css +113 -0
- package/dist/browser-ext/dist/background.js +200 -68
- package/dist/browser-ext/dist/sidepanel.html +14 -0
- package/dist/browser-ext/dist/sidepanel.js +18832 -0
- package/dist/browser-ext/manifest.json +20 -20
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-BYBCpt7_.js → activity-store-Bhyy6ENz.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-D20aBHPJ.js → agent-avatar-dicebear-DRTTU2H3.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-BZvMqNrU.js → agent-defaults-settings-panel-BsXF7LVi.js} +1 -1
- package/dist/gateway/static/root/assets/{agents-6foBELwt.js → agents-DpjWfxYx.js} +1 -1
- package/dist/gateway/static/root/assets/{api-YbtjDbuG.js → api-B2y7ZjZk.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-4ps1RrDU.js → app-management-settings-panel-33Mc81h2.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-C7nBHnkG.js → appearance-settings-C8JECSiJ.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-B_g-rL5b.js → apps-page-t64FcxIZ.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-DfKK1iJO.js → archive-plugin-PzZ6SzfY.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-BOU1pdnQ.js → attachment-load-OhLL6K__.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-BePzexAH.js → automations-page-BpAYwuvx.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-workspace-7-m5pBEZ.js → automations-workspace-B6aTT5IC.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-Cly8XDyt.js → binary-plugins-B5XpGsHg.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-DzSu62js.js → block-editor-BgEhoTnN.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automation-inputs-8bHj7Fv2.js → browser-automation-inputs-s5I78Iko.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-CzL7Aj2k.js → browser-automations-page-CBLunDRH.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-Dum3mTiz.js → browser-settings-page-CSfpPCuD.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-Ci6qVS-O.js → capabilities-settings-panel-DftNnICK.js} +2 -2
- package/dist/gateway/static/root/assets/{channels-settings-BOGEZnSv.js → channels-settings-CpuWa5qu.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-status-swr-BR8oQ_eP.js → channels-status-swr-_OsWr_vS.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-BF9ZXa5Z.js → chat-terminal-dock-mnPsS_GJ.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-DBvEvupa.js → connectors-page-D3nHGnNV.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-expression-BszT_gjK.js → cron-expression-NcGBvIfe.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-D2MKBJ0G.js → date-picker-BrVcl7H5.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-DqigBx-G.js → dependency-picker--edd48Y9.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-DMPk4CaK.js → desktop-pet-settings-DWredJwO.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-CapspBGG.js → desktop-pet-vuyyb6I6.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-CXLsDtrW.js → extension-debug-page-CtBW4ebj.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-B2j87ROw.js → extension-page-CsUrmtW7.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-t1BApw-u.js → extension-settings-page-BUd31AuK.js} +1 -1
- package/dist/gateway/static/root/assets/{files-BArWTfr7.js → files-Be5viEFY.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-BFSesvAC.js → gateway-config-swr-OObCK8jM.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-BFAecoGy.js → gateway-settings-C6FQv7op.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-D1giYgI-.js → gateway-startup-retry-D_TxKN_A.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-CyD9zDcU.js → heartbeat-settings-CDo1Gjp2.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-uItZMiBE.js → home-page-D3_uvVJY.js} +1 -1
- package/dist/gateway/static/root/assets/{image-generation-api-BeB-OPqO.js → image-generation-api-fPU9yGsw.js} +1 -1
- package/dist/gateway/static/root/assets/index-Dpu7EKTm.js +74 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-Q_CM-ZCs.js → keyboard-shortcuts-settings-CNVmrCrV.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-DaiBWfOt.js → local-app-workbench-page-B0IVDvs-.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-BALMNCSp.js → local-apps-page-DvkarvLt.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-RJHYFoq9.js → locale-store-DFQX9kTl.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-C-TS7oFg.js → logs-page-BknY0vZf.js} +1 -1
- package/dist/gateway/static/root/assets/{management-settings-BajkJeYl.js → management-settings-DW6hJINQ.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-view-BxoQJqHx.js → markdown-view-D-5jHRWg.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-C_8Hl6KG.js → media-plugins-DYautOWY.js} +1 -1
- package/dist/gateway/static/root/assets/{messages-Cf1h5WXB.js → messages-Dj8QuS6e.js} +2 -2
- package/dist/gateway/static/root/assets/{mobile-pairing-wizard-CsWIZrZW.js → mobile-pairing-wizard-Dgu03cv-.js} +1 -1
- package/dist/gateway/static/root/assets/{note-detail-panel-CCbPg_GP.js → note-detail-panel-Cv9uH6Jq.js} +3 -3
- package/dist/gateway/static/root/assets/{notes-page-BdxxcUKa.js → notes-page-DcIAiYJn.js} +1 -1
- package/dist/gateway/static/root/assets/{page-tabs-BMYQef5D.js → page-tabs-DF6Q5XvA.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-CKZ-H5hJ.js → preview-open-alternatives-CQJH_jAN.js} +1 -1
- package/dist/gateway/static/root/assets/{product-delivery-CV4pDOtG.js → product-delivery-CCC4FJ3Y.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-zXJIptXs.js → product-open-page-CIGLPFm7.js} +1 -1
- package/dist/gateway/static/root/assets/{project-detail-page-DRhhxhZB.js → project-detail-page-D65JfNSw.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-Wt7gF3Sx.js → projects-page-BfWF7Sz-.js} +1 -1
- package/dist/gateway/static/root/assets/{registry-api-BSfFaxVN.js → registry-api-CsZLatKo.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-B6de5Zrb.js → remote-access-hub-DA1g4CUD.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DcUB5h1f.js → runtime-tools-settings-panel-0-bFSBB8.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-CquWhe7r.js → schema-form-csGbhTUr.js} +1 -1
- package/dist/gateway/static/root/assets/{schemas-BHS7bo06.js → schemas-BaskVM5g.js} +2 -2
- package/dist/gateway/static/root/assets/{sessions-page-BNWO2EVF.js → sessions-page-DXPBNhrZ.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DxeZm67O.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-DwswAefc.js → settings-form-section-YdrdY8B3.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-CoAVmjjs.js → settings-page-Cv9waRn3.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-C9EIcWtH.js → setup-status-panel-DPf-_05Z.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-DWwy_4zh.js → share-preview-page-DBoEHTPn.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-CIUvq27-.js → shares-settings-BQP5yNcO.js} +1 -1
- package/dist/gateway/static/root/assets/skill-reload-api-B5T8cgjq.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-CqsgVtVh.js → skills-page-D1G8h2Lt.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page.utils-B-N73248.js → skills-page.utils-BNZYTop7.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-BndjXuBZ.js → system-settings-panel-ByuppbHi.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-CG6Ha9t7.js → task-detail-page-DNmkrF6y.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-C5ssn64t.js → theme-store-zRT6vaZc.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-BxZdcp3h.js → tunnel-api-CxWGpbwr.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-mj9CUuz1.js → use-autosave-B0l4qubU.js} +1 -1
- package/dist/gateway/static/root/assets/{user-model-page-DKeHMXI4.js → user-model-page-DMsI6YI7.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-CzxVMYlr.js → voice-api-key-field-Bcz7boJg.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-COA0V1cK.js → voice-config-api-DpJEu2RQ.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-DMiPK8dp.js → work-discovery-overlay-Dujq1j_u.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-C82OXCJK.js → workflow-run-setup-panel-CxNYbLNB.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-HLCUV3rQ.js → workflows-page-Dk1jK2nv.js} +1 -1
- package/dist/gateway/static/root/index.html +12 -12
- package/dist/package.js +1 -1
- package/dist/packages/browser-control-contract/src/index.js +14 -0
- package/dist/packages/endpoint-tools-protocol/src/index.js +2 -1
- package/dist/packages/gateway-contract/src/browser-page-context.js +43 -0
- package/dist/packages/gateway-contract/src/browser-tab-binding.js +29 -0
- package/dist/packages/gateway-contract/src/device-pairing.js +18 -2
- package/dist/packages/realtime-protocol/src/index.js +1 -0
- package/dist/packages/realtime-protocol/src/voice-audio.js +70 -9
- package/dist/packages/realtime-protocol/src/voice.js +65 -17
- package/dist/src/agent/source-context/browser-page.d.ts +3 -0
- package/dist/src/agent/source-context/browser-page.js +39 -0
- package/dist/src/agent/source-context/injector.js +9 -2
- package/dist/src/agent/source-context/types.d.ts +7 -1
- package/dist/src/agent/source-context/types.js +4 -1
- package/dist/src/agent/tools/factory.js +9 -1
- package/dist/src/browser/automations/schema.d.ts +1 -1
- package/dist/src/browser/automations/service.d.ts +1 -1
- package/dist/src/browser/native-messaging-host.d.ts +15 -0
- package/dist/src/browser/native-messaging-host.js +105 -0
- package/dist/src/browser/providers/browser-ext-install.d.ts +17 -1
- package/dist/src/browser/providers/browser-ext-install.js +82 -16
- package/dist/src/browser/providers/extension.d.ts +4 -0
- package/dist/src/browser/providers/extension.js +104 -38
- package/dist/src/browser/runtime/browser-runtime.d.ts +2 -1
- package/dist/src/browser/runtime/browser-runtime.js +6 -2
- package/dist/src/cli/commands/browser.js +13 -3
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/endpoint-tools/runtime.js +2 -0
- package/dist/src/gateway/chat-stream/mapper.js +3 -2
- package/dist/src/gateway/hono/app.js +19 -3
- package/dist/src/gateway/hono/middleware/auth.d.ts +4 -1
- package/dist/src/gateway/hono/middleware/auth.js +12 -1
- package/dist/src/gateway/hono/routes/browser-install.js +1 -1
- package/dist/src/gateway/hono/routes/browser.js +106 -7
- package/dist/src/gateway/hono/routes/devices.js +36 -3
- package/dist/src/gateway/hono/routes/endpoint-tools.js +15 -0
- package/dist/src/gateway/hono/routes/session-input-handler.d.ts +20 -2
- package/dist/src/gateway/hono/routes/session-input-handler.js +18 -0
- package/dist/src/gateway/hono/routes/sessions.js +12 -6
- package/dist/src/gateway/hono/routes/voice.js +5 -4
- package/dist/src/gateway/security/gateway-scopes.d.ts +1 -0
- package/dist/src/gateway/security/gateway-scopes.js +10 -1
- package/dist/src/gateway/security/origin-check.d.ts +1 -0
- package/dist/src/gateway/security/origin-check.js +5 -1
- package/dist/src/gateway/service/agent-runner.js +2 -1
- package/dist/src/gateway/service/session-input-coordinator.d.ts +1 -0
- package/dist/src/gateway/service/session-input-coordinator.js +5 -4
- package/dist/src/gateway/service.js +12 -11
- package/dist/src/realtime/runtime.js +2 -1
- package/dist/src/session/client-history.d.ts +4 -1
- package/dist/src/session/client-history.js +6 -3
- package/dist/src/storage/sqlite/browser-tab-binding-repository.d.ts +7 -0
- package/dist/src/storage/sqlite/browser-tab-binding-repository.js +60 -0
- package/dist/src/storage/sqlite/device-access-repository.d.ts +3 -1
- package/dist/src/storage/sqlite/device-access-repository.js +4 -3
- package/dist/src/storage/sqlite/device-pairing-approval.js +6 -4
- package/dist/src/storage/sqlite/device-pairing-repository.d.ts +1 -1
- package/dist/src/storage/sqlite/index.d.ts +1 -0
- package/dist/src/storage/sqlite/index.js +2 -1
- package/dist/src/storage/sqlite/migrations/155_browser_extension.sql +120 -0
- package/dist/src/storage/sqlite/migrations/156_browser_tab_bindings.sql +14 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/voice/realtime/agentBroker.d.ts +54 -0
- package/dist/src/voice/realtime/agentBroker.js +116 -0
- package/dist/src/voice/realtime/agentEngine.js +62 -15
- package/dist/src/voice/realtime/engine.d.ts +2 -1
- package/dist/src/voice/realtime/omniEngine.js +36 -7
- package/dist/src/voice/realtime/pcmFrameBuffer.d.ts +6 -0
- package/dist/src/voice/realtime/pcmFrameBuffer.js +29 -0
- package/dist/src/voice/realtime/runtime.js +97 -21
- package/dist/src/voice/realtime/runtime.types.d.ts +5 -21
- package/dist/src/voice/realtime/turnPolicy.d.ts +36 -0
- package/dist/src/voice/realtime/turnPolicy.js +134 -0
- package/package.json +2 -2
- package/dist/browser-ext/dist/content.js +0 -191
- package/dist/browser-ext/dist/popup.js +0 -32
- package/dist/browser-ext/popup.html +0 -37
- package/dist/gateway/static/root/assets/index-DQtjs2x3.js +0 -74
- package/dist/gateway/static/root/assets/settings-advanced-gate-DK1yTYE4.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DfE3zFVw.js +0 -1
- package/dist/src/voice/realtime/conversationTurn.d.ts +0 -20
- package/dist/src/voice/realtime/conversationTurn.js +0 -83
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { logAuthEvent } from "../../../utils/logger/audit.js";
|
|
2
2
|
import { createLogger } from "../../../utils/logger/index.js";
|
|
3
3
|
import { init_logger } from "../../../utils/logger.js";
|
|
4
|
-
import { authenticateDeviceAccessToken } from "../../../storage/sqlite/device-access-repository.js";
|
|
4
|
+
import { authenticateDeviceAccessToken, getDevice } from "../../../storage/sqlite/device-access-repository.js";
|
|
5
5
|
import { safeEqualSecret } from "../../security/secret-equal.js";
|
|
6
6
|
import { resolveClientIpFromRequest } from "../../client-ip.js";
|
|
7
7
|
import { authPolicyConfig, buckets, resolveAuthRateLimit } from "../../rate-limit/buckets.js";
|
|
@@ -9,6 +9,7 @@ import { getClientIpFromHeaders } from "../../security/loopback.js";
|
|
|
9
9
|
import { resolveAuthTracking } from "../../rate-limit/auth-policy.js";
|
|
10
10
|
import { isAuthRateLimitGloballyDisabled } from "../../rate-limit/env-flags.js";
|
|
11
11
|
import "../../rate-limit/index.js";
|
|
12
|
+
import { isChromeExtensionOrigin } from "../../security/origin-check.js";
|
|
12
13
|
import { authorizeTrustedProxy } from "../../trusted-proxy.js";
|
|
13
14
|
import { setGatewayPrincipal } from "../../security/gateway-principal.js";
|
|
14
15
|
import { createMiddleware } from "hono/factory";
|
|
@@ -208,7 +209,12 @@ function auth(config) {
|
|
|
208
209
|
clientIp
|
|
209
210
|
}, "Credential in query string rejected: use Authorization header for this endpoint");
|
|
210
211
|
const providedCredential = authHeader || queryToken;
|
|
212
|
+
const browserExtensionOrigin = isChromeExtensionOrigin(origin) ? origin.toLowerCase() : void 0;
|
|
211
213
|
if (providedCredential && validateCredential(providedCredential, expectedCredential)) {
|
|
214
|
+
if (browserExtensionOrigin) return c.json({
|
|
215
|
+
error: "Forbidden",
|
|
216
|
+
code: "device_credential_required"
|
|
217
|
+
}, 403);
|
|
212
218
|
recordSuccess(rl);
|
|
213
219
|
setGatewayPrincipal(c, {
|
|
214
220
|
kind: "owner",
|
|
@@ -220,6 +226,11 @@ function auth(config) {
|
|
|
220
226
|
}
|
|
221
227
|
const deviceIdentity = providedCredential?.startsWith("xopc_at_") ? authenticateDeviceAccessToken(providedCredential) : void 0;
|
|
222
228
|
if (deviceIdentity) {
|
|
229
|
+
const device = getDevice(deviceIdentity.deviceId);
|
|
230
|
+
if (browserExtensionOrigin && (device?.platform !== "chrome" || `chrome-extension://${device.extensionId}` !== browserExtensionOrigin)) return c.json({
|
|
231
|
+
error: "Forbidden",
|
|
232
|
+
code: "extension_origin_mismatch"
|
|
233
|
+
}, 403);
|
|
223
234
|
recordSuccess(rl);
|
|
224
235
|
setGatewayPrincipal(c, {
|
|
225
236
|
kind: "device",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createGatewayRouteLogger } from "../lib/route-logger.js";
|
|
2
1
|
import { acquireBrowserInstallLock, cancelBrowserInstall } from "../../../browser/install-lock.js";
|
|
2
|
+
import { createGatewayRouteLogger } from "../lib/route-logger.js";
|
|
3
3
|
import { runPlaywrightChromiumInstallWithProgress } from "../../../browser/providers/playwright-install.js";
|
|
4
4
|
import { streamSSE } from "hono/streaming";
|
|
5
5
|
//#region src/gateway/hono/routes/browser-install.ts
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import { deleteBrowserTabBinding, getBrowserTabBinding, setBrowserTabBinding } from "../../../storage/sqlite/browser-tab-binding-repository.js";
|
|
2
|
+
import { browserTabBindingRequestSchema } from "../../../../packages/gateway-contract/src/browser-tab-binding.js";
|
|
3
|
+
import { getDevice } from "../../../storage/sqlite/device-access-repository.js";
|
|
1
4
|
import { decideBrowserApproval, listBrowserApprovals } from "../../../browser/policy/approval-store.js";
|
|
2
5
|
import { createBrowserDriver } from "../../../browser/drivers/create-driver.js";
|
|
3
6
|
import { checkBrowserReadiness } from "../../../browser/readiness.js";
|
|
4
7
|
import { isLoopbackClientIp } from "../../security/loopback.js";
|
|
8
|
+
import { getGatewayPrincipal } from "../../security/gateway-principal.js";
|
|
5
9
|
import { getConnInfo } from "@hono/node-server/conninfo";
|
|
6
10
|
//#region src/gateway/hono/routes/browser.ts
|
|
7
11
|
/** Browser Control status, approvals, and driver diagnostics. */
|
|
@@ -48,6 +52,8 @@ async function extensionStatus() {
|
|
|
48
52
|
};
|
|
49
53
|
}
|
|
50
54
|
function isLocalOwnerRequest(c, service) {
|
|
55
|
+
const principal = getGatewayPrincipal(c);
|
|
56
|
+
if (principal.kind !== "owner" && principal.kind !== "trusted-proxy") return false;
|
|
51
57
|
if (service.currentConfig.gateway?.bind === "loopback") return true;
|
|
52
58
|
try {
|
|
53
59
|
return isLoopbackClientIp(getConnInfo(c).remote.address);
|
|
@@ -55,8 +61,99 @@ function isLocalOwnerRequest(c, service) {
|
|
|
55
61
|
return false;
|
|
56
62
|
}
|
|
57
63
|
}
|
|
64
|
+
function chromeDevicePrincipal(c) {
|
|
65
|
+
const principal = getGatewayPrincipal(c);
|
|
66
|
+
if (principal.kind !== "device" || !principal.deviceId) return void 0;
|
|
67
|
+
const device = getDevice(principal.deviceId);
|
|
68
|
+
return device?.platform === "chrome" && device.revokedAt === void 0 ? principal : void 0;
|
|
69
|
+
}
|
|
70
|
+
function canManageBrowserSession(c, service, sessionKey) {
|
|
71
|
+
const principal = chromeDevicePrincipal(c);
|
|
72
|
+
if (principal) return getBrowserTabBinding(sessionKey)?.principalId === principal.deviceId;
|
|
73
|
+
return isLocalOwnerRequest(c, service);
|
|
74
|
+
}
|
|
58
75
|
function registerBrowserRoutes(authenticated, deps) {
|
|
59
76
|
const { service, strictRateLimitMiddleware } = deps;
|
|
77
|
+
authenticated.get("/api/browser/tab-bindings/:sessionKey", (c) => {
|
|
78
|
+
const sessionKey = c.req.param("sessionKey").trim();
|
|
79
|
+
const binding = getBrowserTabBinding(sessionKey);
|
|
80
|
+
if (!binding) return c.json({
|
|
81
|
+
ok: false,
|
|
82
|
+
error: {
|
|
83
|
+
code: "NOT_FOUND",
|
|
84
|
+
message: "Tab binding not found"
|
|
85
|
+
}
|
|
86
|
+
}, 404);
|
|
87
|
+
if (!canManageBrowserSession(c, service, sessionKey)) return c.json({
|
|
88
|
+
ok: false,
|
|
89
|
+
error: {
|
|
90
|
+
code: "FORBIDDEN",
|
|
91
|
+
message: "Tab binding belongs to another device"
|
|
92
|
+
}
|
|
93
|
+
}, 403);
|
|
94
|
+
return c.json({
|
|
95
|
+
ok: true,
|
|
96
|
+
payload: binding
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
authenticated.put("/api/browser/tab-bindings/:sessionKey", async (c) => {
|
|
100
|
+
const principal = chromeDevicePrincipal(c);
|
|
101
|
+
if (!principal?.deviceId) return c.json({
|
|
102
|
+
ok: false,
|
|
103
|
+
error: {
|
|
104
|
+
code: "FORBIDDEN",
|
|
105
|
+
message: "Chrome device access required"
|
|
106
|
+
}
|
|
107
|
+
}, 403);
|
|
108
|
+
const parsed = browserTabBindingRequestSchema.safeParse(await c.req.json().catch(() => null));
|
|
109
|
+
if (!parsed.success) return c.json({
|
|
110
|
+
ok: false,
|
|
111
|
+
error: {
|
|
112
|
+
code: "BAD_REQUEST",
|
|
113
|
+
message: "Invalid tab binding"
|
|
114
|
+
}
|
|
115
|
+
}, 400);
|
|
116
|
+
const endpoint = service.endpointTools.registry.get(parsed.data.endpointId);
|
|
117
|
+
if (!service.endpointTools.registry.verifyTurnClaim(parsed.data.endpointId, parsed.data.turnToken) || endpoint?.kind !== "browser" || endpoint.principalId !== principal.deviceId) return c.json({
|
|
118
|
+
ok: false,
|
|
119
|
+
error: {
|
|
120
|
+
code: "INVALID_ENDPOINT",
|
|
121
|
+
message: "Browser endpoint is not active"
|
|
122
|
+
}
|
|
123
|
+
}, 401);
|
|
124
|
+
const sessionKey = c.req.param("sessionKey").trim();
|
|
125
|
+
if (!sessionKey || !await service.sessions.getSession(sessionKey)) return c.json({
|
|
126
|
+
ok: false,
|
|
127
|
+
error: {
|
|
128
|
+
code: "NOT_FOUND",
|
|
129
|
+
message: "Session not found"
|
|
130
|
+
}
|
|
131
|
+
}, 404);
|
|
132
|
+
const { turnToken: _turnToken, ...bindingInput } = parsed.data;
|
|
133
|
+
const binding = setBrowserTabBinding({
|
|
134
|
+
...bindingInput,
|
|
135
|
+
sessionKey,
|
|
136
|
+
principalId: principal.deviceId
|
|
137
|
+
});
|
|
138
|
+
return c.json({
|
|
139
|
+
ok: true,
|
|
140
|
+
payload: binding
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
authenticated.delete("/api/browser/tab-bindings/:sessionKey", (c) => {
|
|
144
|
+
const sessionKey = c.req.param("sessionKey").trim();
|
|
145
|
+
if (!canManageBrowserSession(c, service, sessionKey)) return c.json({
|
|
146
|
+
ok: false,
|
|
147
|
+
error: {
|
|
148
|
+
code: "FORBIDDEN",
|
|
149
|
+
message: "Tab binding belongs to another device"
|
|
150
|
+
}
|
|
151
|
+
}, 403);
|
|
152
|
+
return c.json({
|
|
153
|
+
ok: true,
|
|
154
|
+
payload: { removed: deleteBrowserTabBinding(sessionKey) }
|
|
155
|
+
});
|
|
156
|
+
});
|
|
60
157
|
authenticated.get("/api/browser/extension-status", async (c) => {
|
|
61
158
|
try {
|
|
62
159
|
return c.json(await extensionStatus());
|
|
@@ -169,33 +266,35 @@ function registerBrowserRoutes(authenticated, deps) {
|
|
|
169
266
|
}
|
|
170
267
|
});
|
|
171
268
|
authenticated.get("/api/browser/approvals", (c) => {
|
|
172
|
-
|
|
269
|
+
const sessionKey = c.req.query("sessionKey");
|
|
270
|
+
if (!isLocalOwnerRequest(c, service) && (!sessionKey || !canManageBrowserSession(c, service, sessionKey))) return c.json({
|
|
173
271
|
ok: false,
|
|
174
272
|
error: "Local owner access required."
|
|
175
273
|
}, 403);
|
|
176
274
|
return c.json({
|
|
177
275
|
ok: true,
|
|
178
|
-
approvals: listBrowserApprovals(
|
|
276
|
+
approvals: listBrowserApprovals(sessionKey)
|
|
179
277
|
});
|
|
180
278
|
});
|
|
181
279
|
authenticated.post("/api/browser/approvals/respond", strictRateLimitMiddleware, async (c) => {
|
|
182
|
-
|
|
280
|
+
const body = await c.req.json().catch(() => null);
|
|
281
|
+
const approval = typeof body?.id === "string" ? listBrowserApprovals().find((candidate) => candidate.id === body.id) : void 0;
|
|
282
|
+
if (!isLocalOwnerRequest(c, service) && (!approval || !canManageBrowserSession(c, service, approval.sessionKey))) return c.json({
|
|
183
283
|
ok: false,
|
|
184
284
|
error: "Local owner access required."
|
|
185
285
|
}, 403);
|
|
186
|
-
const body = await c.req.json().catch(() => null);
|
|
187
286
|
if (typeof body?.id !== "string" || body.decision !== "approved" && body.decision !== "denied") return c.json({
|
|
188
287
|
ok: false,
|
|
189
288
|
error: "id and decision are required."
|
|
190
289
|
}, 400);
|
|
191
|
-
const
|
|
192
|
-
if (!
|
|
290
|
+
const decided = decideBrowserApproval(body.id, body.decision);
|
|
291
|
+
if (!decided) return c.json({
|
|
193
292
|
ok: false,
|
|
194
293
|
error: "Approval not found."
|
|
195
294
|
}, 404);
|
|
196
295
|
return c.json({
|
|
197
296
|
ok: true,
|
|
198
|
-
approval
|
|
297
|
+
approval: decided
|
|
199
298
|
});
|
|
200
299
|
});
|
|
201
300
|
authenticated.get("/api/browser/playwright/doctor", async (c) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { runSqliteWriteTransaction } from "../../../storage/sqlite/transaction.js";
|
|
2
|
-
import { DEFAULT_MOBILE_SCOPES } from "../../security/gateway-scopes.js";
|
|
2
|
+
import { DEFAULT_BROWSER_EXTENSION_SCOPES, DEFAULT_MOBILE_SCOPES } from "../../security/gateway-scopes.js";
|
|
3
3
|
import { createDevice, issueDeviceTokenPair, listDevices, revokeDevice, rotateDeviceRefreshToken } from "../../../storage/sqlite/device-access-repository.js";
|
|
4
4
|
import { getGatewayIdentityPublicKeyRaw, getOrCreateGatewayIdentity, signGatewayPayload } from "../../../storage/sqlite/gateway-identity-repository.js";
|
|
5
5
|
import { consumeDevicePairingToken, createDevicePairingSetup, isDevicePairingSetupActive } from "../../../storage/sqlite/device-pairing-repository.js";
|
|
@@ -21,7 +21,12 @@ const pairingExchangeSchema = z.strictObject({
|
|
|
21
21
|
pairingToken: z.string().min(1).max(256),
|
|
22
22
|
device: z.strictObject({
|
|
23
23
|
displayName: z.string().trim().min(1).max(80),
|
|
24
|
-
platform: z.enum([
|
|
24
|
+
platform: z.enum([
|
|
25
|
+
"ios",
|
|
26
|
+
"android",
|
|
27
|
+
"chrome"
|
|
28
|
+
]),
|
|
29
|
+
extensionId: z.string().regex(/^[a-p]{32}$/).optional(),
|
|
25
30
|
publicKeyJwk: z.strictObject({
|
|
26
31
|
kty: z.literal("EC"),
|
|
27
32
|
crv: z.literal("P-256"),
|
|
@@ -29,6 +34,17 @@ const pairingExchangeSchema = z.strictObject({
|
|
|
29
34
|
y: z.string().min(40).max(64)
|
|
30
35
|
})
|
|
31
36
|
})
|
|
37
|
+
}).superRefine((value, context) => {
|
|
38
|
+
if (value.device.platform === "chrome" && !value.device.extensionId) context.addIssue({
|
|
39
|
+
code: "custom",
|
|
40
|
+
path: ["device", "extensionId"],
|
|
41
|
+
message: "Chrome devices require an extension id"
|
|
42
|
+
});
|
|
43
|
+
if (value.device.platform !== "chrome" && value.device.extensionId) context.addIssue({
|
|
44
|
+
code: "custom",
|
|
45
|
+
path: ["device", "extensionId"],
|
|
46
|
+
message: "Only Chrome devices may set an extension id"
|
|
47
|
+
});
|
|
32
48
|
});
|
|
33
49
|
const pairingProbeSchema = z.strictObject({ pairingId: z.string().uuid() });
|
|
34
50
|
function pairingLinkPayload(input) {
|
|
@@ -100,7 +116,8 @@ function registerDeviceAuthPublicRoutes(app) {
|
|
|
100
116
|
displayName: parsed.data.device.displayName,
|
|
101
117
|
platform: parsed.data.device.platform,
|
|
102
118
|
publicKeyJwk: parsed.data.device.publicKeyJwk,
|
|
103
|
-
|
|
119
|
+
extensionId: parsed.data.device.extensionId,
|
|
120
|
+
scopes: parsed.data.device.platform === "chrome" ? DEFAULT_BROWSER_EXTENSION_SCOPES : DEFAULT_MOBILE_SCOPES
|
|
104
121
|
});
|
|
105
122
|
return {
|
|
106
123
|
success: true,
|
|
@@ -207,6 +224,22 @@ function registerDeviceRoutes(authenticated, deps) {
|
|
|
207
224
|
}
|
|
208
225
|
}, 404);
|
|
209
226
|
});
|
|
227
|
+
authenticated.delete("/api/devices/me", (c) => {
|
|
228
|
+
const principal = getGatewayPrincipal(c);
|
|
229
|
+
if (principal.kind !== "device" || !principal.deviceId) return c.json({
|
|
230
|
+
ok: false,
|
|
231
|
+
error: {
|
|
232
|
+
code: "DEVICE_REQUIRED",
|
|
233
|
+
message: "Device access required"
|
|
234
|
+
}
|
|
235
|
+
}, 403);
|
|
236
|
+
const revoked = revokeDevice(principal.deviceId);
|
|
237
|
+
if (revoked) deps.service.realtime.disconnectPrincipal(principal.deviceId);
|
|
238
|
+
return c.json({
|
|
239
|
+
ok: true,
|
|
240
|
+
revoked
|
|
241
|
+
});
|
|
242
|
+
});
|
|
210
243
|
authenticated.delete("/api/devices/:deviceId", (c) => {
|
|
211
244
|
const deviceId = c.req.param("deviceId");
|
|
212
245
|
const revoked = revokeDevice(deviceId);
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { getDevice } from "../../../storage/sqlite/device-access-repository.js";
|
|
1
2
|
import { endpointPrincipalRegistrationSchema, endpointSessionBindingRequestSchema } from "../../../../packages/endpoint-tools-protocol/src/index.js";
|
|
2
3
|
import { createEndpointPrincipal, getEndpointPrincipal, listEndpointPrincipals, revokeEndpointPrincipal } from "../../../storage/sqlite/endpoint-principal-repository.js";
|
|
3
4
|
import { listEndpointToolInvocationAudits } from "../../../storage/sqlite/endpoint-tool-invocation-repository.js";
|
|
4
5
|
import "../../../storage/sqlite/index.js";
|
|
5
6
|
import { parseEndpointPublicKey } from "../../../endpoint-tools/auth.js";
|
|
6
7
|
import { ENDPOINT_UPLOAD_MAX_BYTES, EndpointUploadError } from "../../../endpoint-tools/upload-service.js";
|
|
8
|
+
import { getGatewayPrincipal } from "../../security/gateway-principal.js";
|
|
7
9
|
//#region src/gateway/hono/routes/endpoint-tools.ts
|
|
8
10
|
async function readBoundedBody(body, maxBytes) {
|
|
9
11
|
if (!body) return new Uint8Array();
|
|
@@ -54,6 +56,19 @@ function registerEndpointToolRoutes(authenticated, deps) {
|
|
|
54
56
|
message: "Invalid endpoint principal"
|
|
55
57
|
}
|
|
56
58
|
}, 400);
|
|
59
|
+
const gatewayPrincipal = getGatewayPrincipal(c);
|
|
60
|
+
if (gatewayPrincipal.kind === "device") {
|
|
61
|
+
const device = gatewayPrincipal.deviceId ? getDevice(gatewayPrincipal.deviceId) : void 0;
|
|
62
|
+
const ownsPrincipal = parsed.data.principalId === gatewayPrincipal.deviceId;
|
|
63
|
+
const compatibleKind = device?.platform === "chrome" ? parsed.data.kind === "browser" && parsed.data.platform === "chrome" : parsed.data.kind === "mobile";
|
|
64
|
+
if (!ownsPrincipal || !compatibleKind) return c.json({
|
|
65
|
+
ok: false,
|
|
66
|
+
error: {
|
|
67
|
+
code: "FORBIDDEN",
|
|
68
|
+
message: "Endpoint identity does not match this device"
|
|
69
|
+
}
|
|
70
|
+
}, 403);
|
|
71
|
+
}
|
|
57
72
|
try {
|
|
58
73
|
parseEndpointPublicKey(parsed.data.publicKey);
|
|
59
74
|
} catch {
|
|
@@ -18,6 +18,12 @@ export declare function submitSessionInput(c: Context, deps: AuthenticatedRouteD
|
|
|
18
18
|
code: string;
|
|
19
19
|
message: string;
|
|
20
20
|
};
|
|
21
|
+
}, 403, "json">) | (Response & import("hono").TypedResponse<{
|
|
22
|
+
ok: false;
|
|
23
|
+
error: {
|
|
24
|
+
code: string;
|
|
25
|
+
message: string;
|
|
26
|
+
};
|
|
21
27
|
}, 409, "json">) | (Response & import("hono").TypedResponse<{
|
|
22
28
|
ok: false;
|
|
23
29
|
error: {
|
|
@@ -63,9 +69,12 @@ export declare function submitSessionInput(c: Context, deps: AuthenticatedRouteD
|
|
|
63
69
|
title: string;
|
|
64
70
|
tokenEstimate?: number;
|
|
65
71
|
truncated?: boolean;
|
|
72
|
+
url?: string;
|
|
73
|
+
capturedAt?: number;
|
|
74
|
+
documentId?: string;
|
|
66
75
|
}[];
|
|
67
76
|
contextSnapshots?: {
|
|
68
|
-
kind: import("../../../agent/source-context/types.js").SessionSourceBinding["kind"];
|
|
77
|
+
kind: import("../../../agent/source-context/types.js").SessionSourceBinding["kind"] | "browser_page";
|
|
69
78
|
sourceId: string;
|
|
70
79
|
version: string;
|
|
71
80
|
title: string;
|
|
@@ -77,6 +86,9 @@ export declare function submitSessionInput(c: Context, deps: AuthenticatedRouteD
|
|
|
77
86
|
}[];
|
|
78
87
|
tokenEstimate?: number;
|
|
79
88
|
truncated?: boolean;
|
|
89
|
+
url?: string;
|
|
90
|
+
capturedAt?: number;
|
|
91
|
+
documentId?: string;
|
|
80
92
|
}[];
|
|
81
93
|
thinking?: string;
|
|
82
94
|
origin: {
|
|
@@ -163,9 +175,12 @@ export declare function replaceLatestSessionTurn(c: Context, deps: Authenticated
|
|
|
163
175
|
title: string;
|
|
164
176
|
tokenEstimate?: number;
|
|
165
177
|
truncated?: boolean;
|
|
178
|
+
url?: string;
|
|
179
|
+
capturedAt?: number;
|
|
180
|
+
documentId?: string;
|
|
166
181
|
}[];
|
|
167
182
|
contextSnapshots?: {
|
|
168
|
-
kind: import("../../../agent/source-context/types.js").SessionSourceBinding["kind"];
|
|
183
|
+
kind: import("../../../agent/source-context/types.js").SessionSourceBinding["kind"] | "browser_page";
|
|
169
184
|
sourceId: string;
|
|
170
185
|
version: string;
|
|
171
186
|
title: string;
|
|
@@ -177,6 +192,9 @@ export declare function replaceLatestSessionTurn(c: Context, deps: Authenticated
|
|
|
177
192
|
}[];
|
|
178
193
|
tokenEstimate?: number;
|
|
179
194
|
truncated?: boolean;
|
|
195
|
+
url?: string;
|
|
196
|
+
capturedAt?: number;
|
|
197
|
+
documentId?: string;
|
|
180
198
|
}[];
|
|
181
199
|
thinking?: string;
|
|
182
200
|
origin: {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { browserPageContextsInputSchema } from "../../../../packages/gateway-contract/src/browser-page-context.js";
|
|
1
2
|
import { endpointTurnClaimSchema } from "../../../../packages/endpoint-tools-protocol/src/index.js";
|
|
2
3
|
import { init_providers, resolveModel } from "../../../providers/index.js";
|
|
3
4
|
import { parseTurnContextRefs } from "../../../agent/source-context/types.js";
|
|
4
5
|
import { getModelThinking } from "../../../providers/model-thinking.js";
|
|
5
6
|
import { validateWebchatAttachments, validateWebchatContent } from "../../chat-limits.js";
|
|
6
7
|
import { withModelConfigLock } from "../../../session/model-config-lock.js";
|
|
8
|
+
import { browserPageContextToAgentContext } from "../../../agent/source-context/browser-page.js";
|
|
7
9
|
//#region src/gateway/hono/routes/session-input-handler.ts
|
|
8
10
|
init_providers();
|
|
9
11
|
const MAX_TURN_CONTEXTS = 5;
|
|
@@ -25,6 +27,14 @@ async function submitSessionInput(c, deps, sessionKey) {
|
|
|
25
27
|
message: `contextRefs must contain at most ${MAX_TURN_CONTEXTS} valid notes`
|
|
26
28
|
}
|
|
27
29
|
}, 400);
|
|
30
|
+
const browserContexts = browserPageContextsInputSchema.safeParse(body.browserContexts ?? []);
|
|
31
|
+
if (!browserContexts.success) return c.json({
|
|
32
|
+
ok: false,
|
|
33
|
+
error: {
|
|
34
|
+
code: "BAD_REQUEST",
|
|
35
|
+
message: "Invalid browser page context"
|
|
36
|
+
}
|
|
37
|
+
}, 400);
|
|
28
38
|
const content = typeof body.content === "string" ? body.content : "";
|
|
29
39
|
const contentError = validateWebchatContent(content);
|
|
30
40
|
if (contentError) return c.json({
|
|
@@ -65,6 +75,13 @@ async function submitSessionInput(c, deps, sessionKey) {
|
|
|
65
75
|
message: "Endpoint connection is not active"
|
|
66
76
|
}
|
|
67
77
|
}, 401);
|
|
78
|
+
if (browserContexts.data.length && deps.service.endpointTools.registry.get(origin.data.endpointId)?.kind !== "browser") return c.json({
|
|
79
|
+
ok: false,
|
|
80
|
+
error: {
|
|
81
|
+
code: "FORBIDDEN",
|
|
82
|
+
message: "Browser context requires a browser endpoint"
|
|
83
|
+
}
|
|
84
|
+
}, 403);
|
|
68
85
|
if (body.expectedSessionId !== void 0 && (typeof body.expectedSessionId !== "string" || !body.expectedSessionId || body.expectedSessionId.length > 128)) return c.json({
|
|
69
86
|
ok: false,
|
|
70
87
|
error: {
|
|
@@ -115,6 +132,7 @@ async function submitSessionInput(c, deps, sessionKey) {
|
|
|
115
132
|
content,
|
|
116
133
|
attachments,
|
|
117
134
|
contextRefs,
|
|
135
|
+
sourceContexts: browserContexts.data.map(browserPageContextToAgentContext),
|
|
118
136
|
thinking: selection?.thinkingLevel ?? (typeof body.thinking === "string" ? body.thinking : void 0),
|
|
119
137
|
origin: {
|
|
120
138
|
type: "endpoint",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { deleteBrowserTabBinding } from "../../../storage/sqlite/browser-tab-binding-repository.js";
|
|
1
2
|
import { buildSessionKey, init_session_key } from "../../../routing/session-key.js";
|
|
2
3
|
import { resolveProjectAgentId } from "../../../projects/project-agent.js";
|
|
3
4
|
import "../../../projects/index.js";
|
|
@@ -245,6 +246,7 @@ function registerSessionsRoutes(authenticated, deps) {
|
|
|
245
246
|
}, 400);
|
|
246
247
|
}
|
|
247
248
|
}
|
|
249
|
+
if (body.createdSurface === "browser_extension") await service.sessions.patch(sessionKey, { customData: { createdSurface: "browser_extension" } });
|
|
248
250
|
const session = await service.sessions.getSession(sessionKey);
|
|
249
251
|
const agentConfig = channel === "webchat" ? await service.sessions.getFixedAgentConfig(sessionKey) : void 0;
|
|
250
252
|
return c.json({
|
|
@@ -657,6 +659,7 @@ function registerSessionsRoutes(authenticated, deps) {
|
|
|
657
659
|
ok: false,
|
|
658
660
|
error: result.error
|
|
659
661
|
}, 404);
|
|
662
|
+
deleteBrowserTabBinding(key);
|
|
660
663
|
if (requestedProjectId !== void 0) try {
|
|
661
664
|
if (requestedProjectId) service.projects.attachSession(key, requestedProjectId);
|
|
662
665
|
else service.projects.detachSession(key);
|
|
@@ -832,12 +835,15 @@ function registerSessionsRoutes(authenticated, deps) {
|
|
|
832
835
|
error: "Stop the active session run before deleting its environment"
|
|
833
836
|
}, 409);
|
|
834
837
|
const result = await service.sessions.delete(key);
|
|
835
|
-
if (result.deleted)
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
838
|
+
if (result.deleted) {
|
|
839
|
+
deleteBrowserTabBinding(key);
|
|
840
|
+
await environments.release(key).catch((error) => {
|
|
841
|
+
log.warn({
|
|
842
|
+
err: error,
|
|
843
|
+
sessionKey: key
|
|
844
|
+
}, "Session deleted but execution environment cleanup failed");
|
|
845
|
+
});
|
|
846
|
+
}
|
|
841
847
|
return c.json(result);
|
|
842
848
|
});
|
|
843
849
|
authenticated.post("/api/sessions/:key/archive", async (c) => {
|
|
@@ -133,12 +133,13 @@ function registerVoiceRoutes(authenticated, deps) {
|
|
|
133
133
|
enabled,
|
|
134
134
|
capabilities: {
|
|
135
135
|
dictation: availability(Boolean(stt)),
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
assistant: availability(Boolean(stt && tts)),
|
|
137
|
+
natural: availability(Boolean(omni)),
|
|
138
138
|
languages: ["zh", "en"],
|
|
139
|
-
bargeIn: config.voice?.realtime?.bargeIn ?? true
|
|
139
|
+
bargeIn: config.voice?.realtime?.bargeIn ?? true,
|
|
140
|
+
mediaTransports: ["websocket-pcm"]
|
|
140
141
|
},
|
|
141
|
-
|
|
142
|
+
defaultMode: config.voice?.realtime?.defaultEngine === "omni" ? "natural" : "assistant",
|
|
142
143
|
omni: omni?.route ?? null,
|
|
143
144
|
stt: stt?.route ?? null,
|
|
144
145
|
tts: tts ? {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare const GATEWAY_SCOPES: readonly ["gateway.status", "agents.read", "agents.run", "sessions.read", "sessions.write", "workspace.read", "workspace.write", "tasks.read", "tasks.write", "automations.read", "automations.write", "notifications.self", "device.self", "gateway.admin"];
|
|
2
2
|
export type GatewayScope = typeof GATEWAY_SCOPES[number];
|
|
3
3
|
export declare const DEFAULT_MOBILE_SCOPES: readonly GatewayScope[];
|
|
4
|
+
export declare const DEFAULT_BROWSER_EXTENSION_SCOPES: readonly GatewayScope[];
|
|
4
5
|
export declare function isGatewayScope(value: unknown): value is GatewayScope;
|
|
5
6
|
export declare function parseGatewayScopes(value: string): GatewayScope[];
|
|
6
7
|
export declare function requiredGatewayScope(method: string, path: string): GatewayScope;
|
|
@@ -31,6 +31,14 @@ const DEFAULT_MOBILE_SCOPES = [
|
|
|
31
31
|
"notifications.self",
|
|
32
32
|
"device.self"
|
|
33
33
|
];
|
|
34
|
+
const DEFAULT_BROWSER_EXTENSION_SCOPES = [
|
|
35
|
+
"gateway.status",
|
|
36
|
+
"agents.read",
|
|
37
|
+
"agents.run",
|
|
38
|
+
"sessions.read",
|
|
39
|
+
"sessions.write",
|
|
40
|
+
"device.self"
|
|
41
|
+
];
|
|
34
42
|
function isGatewayScope(value) {
|
|
35
43
|
return typeof value === "string" && KNOWN_GATEWAY_SCOPES.has(value);
|
|
36
44
|
}
|
|
@@ -48,6 +56,7 @@ function requiredGatewayScope(method, path) {
|
|
|
48
56
|
if (path === "/api/device-auth/refresh" || path === "/api/devices/me") return "device.self";
|
|
49
57
|
if (path.startsWith("/api/devices/me/push")) return "notifications.self";
|
|
50
58
|
if (path.startsWith("/api/endpoint-tools")) return "device.self";
|
|
59
|
+
if (path.startsWith("/api/browser/tab-bindings") || path.startsWith("/api/browser/approvals")) return methodScope(method, "sessions.read", "sessions.write");
|
|
51
60
|
if (method === "GET" && path === "/api/connectors/approvals") return "sessions.read";
|
|
52
61
|
if (method === "POST" && path === "/api/connectors/approvals/respond") return "sessions.write";
|
|
53
62
|
if (path === "/api/agent" || path.startsWith("/api/agent/")) return "agents.run";
|
|
@@ -66,4 +75,4 @@ function hasGatewayScope(granted, required) {
|
|
|
66
75
|
return granted.includes("gateway.admin") || granted.includes(required);
|
|
67
76
|
}
|
|
68
77
|
//#endregion
|
|
69
|
-
export { DEFAULT_MOBILE_SCOPES, GATEWAY_SCOPES, hasGatewayScope, isGatewayScope, parseGatewayScopes, requiredGatewayScope };
|
|
78
|
+
export { DEFAULT_BROWSER_EXTENSION_SCOPES, DEFAULT_MOBILE_SCOPES, GATEWAY_SCOPES, hasGatewayScope, isGatewayScope, parseGatewayScopes, requiredGatewayScope };
|
|
@@ -5,6 +5,10 @@ const LOOPBACK_HOSTNAMES = new Set([
|
|
|
5
5
|
"::1",
|
|
6
6
|
"[::1]"
|
|
7
7
|
]);
|
|
8
|
+
const CHROME_EXTENSION_ORIGIN = /^chrome-extension:\/\/[a-p]{32}$/;
|
|
9
|
+
function isChromeExtensionOrigin(origin) {
|
|
10
|
+
return CHROME_EXTENSION_ORIGIN.test((origin ?? "").trim().toLowerCase());
|
|
11
|
+
}
|
|
8
12
|
function isLoopbackHost(hostname) {
|
|
9
13
|
return LOOPBACK_HOSTNAMES.has(hostname.toLowerCase());
|
|
10
14
|
}
|
|
@@ -55,4 +59,4 @@ function checkBrowserOrigin(params) {
|
|
|
55
59
|
};
|
|
56
60
|
}
|
|
57
61
|
//#endregion
|
|
58
|
-
export { checkBrowserOrigin };
|
|
62
|
+
export { checkBrowserOrigin, isChromeExtensionOrigin };
|
|
@@ -50,7 +50,8 @@ var GatewayAgentRunner = class {
|
|
|
50
50
|
const generator = this.runAgent(input.content, "webchat", input.sessionKey, input.origin, input.attachments, input.thinking, {
|
|
51
51
|
runId: input.runId,
|
|
52
52
|
taskRunId: input.taskRunId,
|
|
53
|
-
sourceContexts: input.sourceContexts
|
|
53
|
+
sourceContexts: input.sourceContexts,
|
|
54
|
+
...input.origin.type === "channel" && input.origin.channel === "voice" ? { presentation: "voice" } : {}
|
|
54
55
|
});
|
|
55
56
|
let result;
|
|
56
57
|
while (true) {
|
|
@@ -7,6 +7,7 @@ import { cancelConnectionObjective, consumeConnectionResume, invalidateConnectio
|
|
|
7
7
|
import "../../storage/sqlite/index.js";
|
|
8
8
|
import { summarizeSourceContext } from "../../agent/source-context/types.js";
|
|
9
9
|
import { deleteMediaUrisNoLongerReferenced } from "../../media/session-references.js";
|
|
10
|
+
import { fitSourceContextsToBudget } from "../../agent/source-context/budget.js";
|
|
10
11
|
import crypto from "node:crypto";
|
|
11
12
|
//#region src/gateway/service/session-input-coordinator.ts
|
|
12
13
|
init_logger();
|
|
@@ -70,7 +71,7 @@ var SessionInputCoordinator = class {
|
|
|
70
71
|
const clientMessageId = input.clientMessageId.trim();
|
|
71
72
|
const content = input.content.trim();
|
|
72
73
|
const targetTurnId = input.targetTurnId.trim();
|
|
73
|
-
if (!sessionKey || !clientMessageId || !targetTurnId || !content && !input.attachments?.length) return {
|
|
74
|
+
if (!sessionKey || !clientMessageId || !targetTurnId || !content && !input.attachments?.length && !input.sourceContexts?.length) return {
|
|
74
75
|
ok: false,
|
|
75
76
|
code: "BAD_REQUEST"
|
|
76
77
|
};
|
|
@@ -89,7 +90,7 @@ var SessionInputCoordinator = class {
|
|
|
89
90
|
const attachments = await this.deps.prepareAttachments(sessionKey, input.attachments);
|
|
90
91
|
let sourceContexts;
|
|
91
92
|
try {
|
|
92
|
-
sourceContexts = await this.deps.prepareContexts(input.contextRefs);
|
|
93
|
+
sourceContexts = fitSourceContextsToBudget([...await this.deps.prepareContexts(input.contextRefs) ?? [], ...input.sourceContexts ?? []]);
|
|
93
94
|
} catch (err) {
|
|
94
95
|
log.warn({
|
|
95
96
|
err,
|
|
@@ -137,7 +138,7 @@ var SessionInputCoordinator = class {
|
|
|
137
138
|
const sessionKey = input.sessionKey;
|
|
138
139
|
const clientMessageId = input.clientMessageId.trim();
|
|
139
140
|
const content = input.content.trim();
|
|
140
|
-
if (!sessionKey || !clientMessageId || !content && !input.attachments?.length) return {
|
|
141
|
+
if (!sessionKey || !clientMessageId || !content && !input.attachments?.length && !input.sourceContexts?.length) return {
|
|
141
142
|
ok: false,
|
|
142
143
|
code: "BAD_REQUEST"
|
|
143
144
|
};
|
|
@@ -158,7 +159,7 @@ var SessionInputCoordinator = class {
|
|
|
158
159
|
const attachments = await this.deps.prepareAttachments(sessionKey, input.attachments);
|
|
159
160
|
let sourceContexts;
|
|
160
161
|
try {
|
|
161
|
-
sourceContexts = await this.deps.prepareContexts(input.contextRefs);
|
|
162
|
+
sourceContexts = fitSourceContextsToBudget([...await this.deps.prepareContexts(input.contextRefs) ?? [], ...input.sourceContexts ?? []]);
|
|
162
163
|
} catch (err) {
|
|
163
164
|
log.warn({
|
|
164
165
|
err,
|