@bitkyc08/opencodex 2.90.0 → 2.92.0
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/bin/ocx.mjs +37 -3
- package/gui/dist/assets/App-DMps4OLk.css +1 -0
- package/gui/dist/assets/{App-DgxkE6Md.js → App-sUf_8afJ.js} +18 -18
- package/gui/dist/assets/{Tray-DbTNc_88.js → Tray-BSsxznDm.js} +1 -1
- package/gui/dist/assets/index-BlFE_VB_.js +94 -0
- package/gui/dist/assets/quota-summary-BRlbXgw0.js +1 -0
- package/gui/dist/index.html +1 -1
- package/gui/dist/provider-icons/workbuddy-color.svg +29 -0
- package/package.json +4 -3
- package/src/adapters/anthropic.ts +2 -5
- package/src/adapters/claude-cli/usage-admission.ts +14 -0
- package/src/adapters/google-vertex-endpoint.ts +9 -1
- package/src/adapters/google-vertex-key-project.ts +223 -0
- package/src/adapters/google.ts +13 -3
- package/src/adapters/openai-chat/tool-name-registry.ts +3 -1
- package/src/adapters/openai-chat.ts +2 -2
- package/src/adapters/openai-responses/passthrough.ts +2 -1
- package/src/adapters/openai-responses/request-strips.ts +92 -0
- package/src/adapters/openai-responses/transport.ts +6 -0
- package/src/adapters/registry.ts +5 -0
- package/src/adapters/workbuddy-schema.ts +61 -0
- package/src/adapters/workbuddy.ts +68 -0
- package/src/bridge/response-json.ts +1 -1
- package/src/bridge/sse.ts +1 -1
- package/src/chat/inbound.ts +20 -2
- package/src/chat/outbound.ts +47 -8
- package/src/chat/reasoning-details.ts +137 -0
- package/src/cli/capabilities-agents-routing.ts +53 -3
- package/src/cli/capabilities-base.ts +15 -2
- package/src/cli/capabilities-observe-system.ts +4 -4
- package/src/cli/codex-cli-update.ts +8 -0
- package/src/cli/codex-shim-autorestore.ts +5 -0
- package/src/cli/combo-result.ts +6 -0
- package/src/cli/combo-stats.ts +3 -0
- package/src/cli/combo.ts +66 -42
- package/src/cli/dispatch.ts +30 -0
- package/src/cli/fallback.ts +127 -0
- package/src/cli/help.ts +1 -0
- package/src/cli/index.ts +1 -2
- package/src/cli/launcher-context.ts +49 -2
- package/src/cli/link-apply-restart.ts +72 -0
- package/src/cli/link-child.ts +6 -2
- package/src/cli/link-cleanup.ts +95 -0
- package/src/cli/link-enrollment.ts +4 -0
- package/src/cli/link.ts +123 -35
- package/src/cli/provider.ts +46 -3
- package/src/cli/registry.ts +13 -1
- package/src/cli/restart-failure.ts +4 -0
- package/src/cli/runtime-api.ts +1 -1
- package/src/cli/status-codex-settings.ts +56 -0
- package/src/cli/status-fallback.ts +29 -0
- package/src/cli/status-install.ts +52 -0
- package/src/cli/status.ts +20 -1
- package/src/cli/system-restart-client.ts +32 -1
- package/src/cli/update-restart-child.ts +4 -3
- package/src/cli/update-restart-home.ts +20 -8
- package/src/cli/usage-compare.ts +68 -12
- package/src/client/connect.ts +82 -6
- package/src/client/home-unreachable.ts +16 -0
- package/src/client/hub-relay.ts +8 -2
- package/src/client/link-home.ts +119 -6
- package/src/client/link-ingress.ts +1 -1
- package/src/client/link-parity-reporter.ts +107 -0
- package/src/client/link-readiness.ts +97 -0
- package/src/client/link-relay-transport.ts +84 -9
- package/src/client/link-relay.ts +98 -15
- package/src/client/link-tunnel.ts +45 -18
- package/src/client/machine-listener.ts +103 -3
- package/src/client/runtime-restart.ts +127 -0
- package/src/client/runtime.ts +155 -25
- package/src/codex/account-store.ts +17 -3
- package/src/codex/app-server-restart-service.ts +6 -5
- package/src/codex/auth-context.ts +10 -6
- package/src/codex/catalog/effort.ts +9 -2
- package/src/codex/catalog/gather-capture.ts +4 -2
- package/src/codex/catalog/parsing.ts +4 -0
- package/src/codex/catalog/provider-models.ts +14 -8
- package/src/codex/chatgpt-refresh-failure.ts +6 -2
- package/src/codex/cli-install-provenance.ts +418 -0
- package/src/codex/cli-installation-targets.ts +5 -1
- package/src/codex/cli-update-plan.ts +43 -16
- package/src/codex/desktop-app/picker-freshness.ts +10 -8
- package/src/codex/desktop-compatibility/usage-activation.ts +2 -1
- package/src/codex/desktop-compatibility/usage-controller.ts +7 -1
- package/src/codex/desktop-compatibility/windows-key-protection.ts +18 -6
- package/src/codex/pool-refresh-backoff.ts +3 -1
- package/src/codex/quota-history.ts +7 -0
- package/src/codex/quota.ts +10 -0
- package/src/codex/subagent-model-fallback.ts +24 -1
- package/src/codex/user-identity.ts +35 -6
- package/src/codex/windows-current-sid.ts +162 -0
- package/src/combos/index.ts +1 -0
- package/src/combos/jev-quota-activation.ts +5 -1
- package/src/combos/jev-quota-route.ts +1 -1
- package/src/combos/jev-quota.ts +9 -1
- package/src/combos/jev-session-pin-config.ts +51 -0
- package/src/combos/jev.ts +6 -0
- package/src/combos/resolve.ts +12 -0
- package/src/combos/types.ts +19 -0
- package/src/config/diagnostics.ts +5 -0
- package/src/config/load-degrade.ts +2 -0
- package/src/config/provider-validation.ts +7 -0
- package/src/config/schema/config-schema.ts +3 -0
- package/src/config/schema/fallback-policy.ts +40 -0
- package/src/config/schema/leaf-validators.ts +1 -0
- package/src/generated/compatibility-version.json +375 -195
- package/src/lib/authorization-scheme.ts +95 -0
- package/src/lib/decision-quota-sink.ts +10 -0
- package/src/lib/provider-client-headers.ts +1 -0
- package/src/lib/redact.ts +2 -2
- package/src/lib/system-restart-contract.ts +24 -0
- package/src/lib/test-home-guard.ts +78 -11
- package/src/lib/translator-budget.ts +4 -4
- package/src/lib/upstream-retry.ts +4 -1
- package/src/link/child-parity.ts +124 -0
- package/src/link/codex-settings.ts +37 -0
- package/src/link/compatibility.ts +119 -0
- package/src/link/diagnostics.ts +193 -0
- package/src/link/management-routes.ts +2 -0
- package/src/link/peer-report.ts +100 -0
- package/src/link/relay-auth.ts +6 -0
- package/src/link/remote-platform.ts +78 -0
- package/src/link/ssh-argv.ts +19 -0
- package/src/link/status-projection.ts +63 -2
- package/src/link/supervisor.ts +45 -12
- package/src/link/tunnel-history.ts +109 -0
- package/src/link/tunnel-state.ts +80 -17
- package/src/messaging/remote-files.ts +24 -1
- package/src/oauth/generic-account-failover.ts +91 -7
- package/src/oauth/index.ts +16 -1
- package/src/oauth/kimi.ts +2 -0
- package/src/oauth/model-discovery-auth.ts +5 -0
- package/src/oauth/nous.ts +4 -1
- package/src/oauth/store.ts +6 -2
- package/src/oauth/token-guardian.ts +7 -0
- package/src/oauth/types.ts +9 -0
- package/src/oauth/workbuddy.ts +139 -0
- package/src/protocols/codecs/chat.ts +3 -3
- package/src/protocols/encoders/adapter-events.ts +26 -2
- package/src/protocols/encoders/chat.ts +55 -3
- package/src/providers/antigravity-quota-identity.ts +37 -0
- package/src/providers/base-url-choices.ts +8 -5
- package/src/providers/derive.ts +23 -3
- package/src/providers/mistral-transport.ts +37 -4
- package/src/providers/model-discovery.ts +2 -0
- package/src/providers/model-rename-fields.ts +1 -0
- package/src/providers/quota/account-cache.ts +3 -2
- package/src/providers/quota/antigravity.ts +27 -22
- package/src/providers/quota/report-cache.ts +13 -4
- package/src/providers/quota/vendor-probes-key.ts +60 -0
- package/src/providers/quota/workbuddy.ts +83 -0
- package/src/providers/quota-decision-antigravity.ts +64 -0
- package/src/providers/quota-decision-publication.ts +11 -0
- package/src/providers/quota-decision-snapshot.ts +21 -4
- package/src/providers/quota-types.ts +2 -0
- package/src/providers/quota.ts +3 -1
- package/src/providers/registry/entries-core.ts +9 -0
- package/src/providers/registry/entries-extended.ts +20 -7
- package/src/providers/registry/model-ids.ts +3 -0
- package/src/providers/registry/model-seeds.ts +27 -28
- package/src/providers/registry/types.ts +19 -1
- package/src/providers/resolved-model-policy.ts +4 -4
- package/src/providers/routed-config.ts +15 -0
- package/src/providers/stale-model-roster-migration.ts +83 -15
- package/src/providers/workbuddy-credit-balance.ts +81 -0
- package/src/providers/workbuddy-models.ts +37 -0
- package/src/providers/workbuddy.ts +60 -0
- package/src/quota-pool/assemble.ts +326 -0
- package/src/quota-pool/contract.ts +58 -0
- package/src/quota-pool/identity.ts +203 -0
- package/src/remote/features.ts +74 -0
- package/src/remote/protocol.ts +2 -0
- package/src/remote/served-by.ts +57 -0
- package/src/responses/parser.ts +7 -0
- package/src/responses/reasoning-envelope.ts +12 -0
- package/src/responses/reasoning-replay-cache.ts +18 -14
- package/src/router.ts +2 -0
- package/src/routing/analytics.ts +2 -3
- package/src/routing/fallback-policy.ts +290 -0
- package/src/routing/fallback-state.ts +179 -0
- package/src/server/audio-client.ts +2 -1
- package/src/server/audio-dictation.ts +2 -1
- package/src/server/audio-upstream.ts +2 -1
- package/src/server/auth-cors.ts +7 -5
- package/src/server/background-lifecycle.ts +4 -0
- package/src/server/chat-completions.ts +22 -2
- package/src/server/chat-native-reasoning.ts +10 -3
- package/src/server/chat-native.ts +13 -2
- package/src/server/claude-messages.ts +3 -0
- package/src/server/gui-session.ts +2 -1
- package/src/server/index/link-listener.ts +11 -1
- package/src/server/index/link-management.ts +47 -0
- package/src/server/index/link-relay-sessions.ts +20 -7
- package/src/server/index/optional-listeners.ts +11 -2
- package/src/server/index/serve-options.ts +19 -2
- package/src/server/live.ts +5 -0
- package/src/server/local-management-read-client.ts +3 -1
- package/src/server/management/combo-routes.ts +10 -4
- package/src/server/management/context.ts +1 -1
- package/src/server/management/fallback-routes.ts +75 -0
- package/src/server/management/link-enrollment.ts +21 -3
- package/src/server/management/link-route-state.ts +2 -0
- package/src/server/management/link-routes.ts +16 -2
- package/src/server/management/provider-routes.ts +1 -1
- package/src/server/management/quota-pool-routes.ts +24 -0
- package/src/server/management/route-registry.ts +3 -0
- package/src/server/management-api.ts +16 -0
- package/src/server/management-auth.ts +2 -1
- package/src/server/port-reclaim.ts +51 -9
- package/src/server/request-log-conversation.ts +49 -0
- package/src/server/request-log.ts +2 -0
- package/src/server/responses/agent-task-recovery.ts +1 -1
- package/src/server/responses/anthropic-thinking-recovery.ts +1 -0
- package/src/server/responses/compact.ts +12 -4
- package/src/server/responses/core-auth.ts +11 -1
- package/src/server/responses/core-combo.ts +125 -32
- package/src/server/responses/core-lifetime.ts +2 -2
- package/src/server/responses/core-normalize.ts +6 -3
- package/src/server/responses/core-opaque-recovery.ts +1 -0
- package/src/server/responses/core-options.ts +5 -0
- package/src/server/responses/core-replay.ts +2 -1
- package/src/server/responses/encrypted-payload.ts +3 -3
- package/src/server/responses/jev-combo-choice.ts +39 -0
- package/src/server/responses/jev-session-pin.ts +203 -0
- package/src/server/responses/main-hard-lock-routing.ts +54 -2
- package/src/server/responses/passthrough-delivery.ts +4 -0
- package/src/server/responses/policy-fallback.ts +44 -1
- package/src/server/responses/request-prepare.ts +19 -6
- package/src/server/responses/request-transport.ts +25 -6
- package/src/server/responses/run-turn-execution.ts +2 -4
- package/src/server/responses/sidecar-execution.ts +2 -4
- package/src/server/responses-phase-inference.ts +267 -0
- package/src/server/restart-replacement.ts +50 -2
- package/src/server/sse-payload-rewrite.ts +14 -1
- package/src/server/ws-bridge.ts +5 -0
- package/src/service/cli.ts +25 -2
- package/src/service/definition.ts +20 -8
- package/src/service/diagnostics.ts +20 -6
- package/src/service/status-json.ts +116 -0
- package/src/types/config.ts +13 -0
- package/src/types/fallback.ts +38 -0
- package/src/types/provider.ts +9 -1
- package/src/types/request.ts +1 -0
- package/src/types/wire.ts +1 -0
- package/src/update/codex-cli-update-launch-policy.d.mts +9 -0
- package/src/update/codex-cli-update-launch-policy.mjs +30 -0
- package/src/usage/jev-stats.ts +54 -14
- package/gui/dist/assets/App-dnkJ85te.css +0 -1
- package/gui/dist/assets/index-TmaHa54u.js +0 -94
- package/gui/dist/assets/quota-summary-B16x5DZZ.js +0 -1
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { diagnoseService, type ServiceDiagnostic } from "./diagnostics";
|
|
2
|
+
import { desktopLoginStatusSummary, diagnoseDesktopStartup } from "./desktop-startup";
|
|
3
|
+
import { confirmServiceServing } from "./health";
|
|
4
|
+
import { ownershipMutationLeaseStatusLine } from "./ownership-mutation-lease.mjs";
|
|
5
|
+
import { LABEL, TASK, serviceLogPath, serviceStatePaths } from "./state";
|
|
6
|
+
import { WINSW_SERVICE_ID } from "../lib/winsw";
|
|
7
|
+
import { runPassiveRead } from "../lib/passive-read";
|
|
8
|
+
|
|
9
|
+
/** The service manager holding the registration; `none` when nothing is registered. */
|
|
10
|
+
export type ServiceStatusBackend = "launchd" | "systemd" | "schtasks" | "winsw" | "none";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* `ocx service status --json`. Additive within `schemaVersion: 1`.
|
|
14
|
+
*
|
|
15
|
+
* `running` is the service manager's own verdict — the value `ocx status --json` already
|
|
16
|
+
* reports as `startup.serviceRunning` — and `serving` says separately whether a proxy
|
|
17
|
+
* answers on the installed port, because a loaded job that never bound is the failure the
|
|
18
|
+
* text report exists to show.
|
|
19
|
+
*/
|
|
20
|
+
export interface ServiceStatusJson {
|
|
21
|
+
schemaVersion: 1;
|
|
22
|
+
platform: NodeJS.Platform;
|
|
23
|
+
supported: boolean;
|
|
24
|
+
backend: ServiceStatusBackend;
|
|
25
|
+
label: string | null;
|
|
26
|
+
installed: boolean;
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
running: boolean;
|
|
29
|
+
viable: boolean;
|
|
30
|
+
stale: boolean;
|
|
31
|
+
conflict: boolean;
|
|
32
|
+
serving: { probed: boolean; ok: boolean; port: number | null };
|
|
33
|
+
summary: string;
|
|
34
|
+
logPath: string;
|
|
35
|
+
ownershipLease: string | null;
|
|
36
|
+
desktopLogin: string | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ServiceStatusJsonDeps {
|
|
40
|
+
platform?: NodeJS.Platform;
|
|
41
|
+
diagnose?: () => ServiceDiagnostic;
|
|
42
|
+
serving?: () => Promise<{ ok: boolean; port: number }>;
|
|
43
|
+
logPath?: () => string;
|
|
44
|
+
leaseLine?: () => string | null;
|
|
45
|
+
desktopLogin?: () => string | null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function serviceStatusBackend(diag: ServiceDiagnostic): ServiceStatusBackend {
|
|
49
|
+
if (!diag.supported || !diag.installed) return "none";
|
|
50
|
+
switch (diag.backend) {
|
|
51
|
+
case "launchd": return "launchd";
|
|
52
|
+
case "systemd": return "systemd";
|
|
53
|
+
case "scheduler": return "schtasks";
|
|
54
|
+
case "native": return "winsw";
|
|
55
|
+
default: return "none";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const BACKEND_LABEL: Record<ServiceStatusBackend, string | null> = {
|
|
60
|
+
launchd: LABEL,
|
|
61
|
+
systemd: `${TASK}.service`,
|
|
62
|
+
schtasks: TASK,
|
|
63
|
+
winsw: WINSW_SERVICE_ID,
|
|
64
|
+
none: null,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Passive by construction: it performs the reads `ocx service status` already performs
|
|
69
|
+
* (manager diagnosis, the loopback serving probe only when a registration exists, the
|
|
70
|
+
* ownership-lease and Desktop login lines) and nothing else. The caller answers before
|
|
71
|
+
* `platformOps()` is resolved, so no install, start, stop or repair path is reachable.
|
|
72
|
+
* The whole collection runs in a passive-read scope: the serving probe resolves the port
|
|
73
|
+
* through loadConfig(), which outside that scope hardens config modes and backs up a
|
|
74
|
+
* malformed config — writes an inventory read must not make.
|
|
75
|
+
*/
|
|
76
|
+
export function collectServiceStatusJson(deps: ServiceStatusJsonDeps = {}): Promise<ServiceStatusJson> {
|
|
77
|
+
return runPassiveRead(() => collectPassively(deps));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function collectPassively(deps: ServiceStatusJsonDeps): Promise<ServiceStatusJson> {
|
|
81
|
+
const diag = (deps.diagnose ?? diagnoseService)();
|
|
82
|
+
const backend = serviceStatusBackend(diag);
|
|
83
|
+
let serving: ServiceStatusJson["serving"] = { probed: false, ok: false, port: null };
|
|
84
|
+
if (diag.installed) {
|
|
85
|
+
// A probe that throws (config load, port resolution) must not cost the caller the whole
|
|
86
|
+
// document; it is reported as probed, not serving, with no port.
|
|
87
|
+
try {
|
|
88
|
+
const result = await (deps.serving ?? (() => confirmServiceServing({ timeoutMs: 1_500 })))();
|
|
89
|
+
serving = { probed: true, ok: result.ok, port: result.port };
|
|
90
|
+
} catch {
|
|
91
|
+
serving = { probed: true, ok: false, port: null };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const desktopLogin = deps.desktopLogin ?? (() => {
|
|
95
|
+
const desktop = diagnoseDesktopStartup();
|
|
96
|
+
return desktop ? desktopLoginStatusSummary(desktop) : null;
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
schemaVersion: 1,
|
|
100
|
+
platform: deps.platform ?? process.platform,
|
|
101
|
+
supported: diag.supported,
|
|
102
|
+
backend,
|
|
103
|
+
label: BACKEND_LABEL[backend],
|
|
104
|
+
installed: diag.installed,
|
|
105
|
+
enabled: diag.enabled,
|
|
106
|
+
running: diag.running,
|
|
107
|
+
viable: diag.viable,
|
|
108
|
+
stale: diag.stale,
|
|
109
|
+
conflict: diag.conflict,
|
|
110
|
+
serving,
|
|
111
|
+
summary: diag.summary,
|
|
112
|
+
logPath: (deps.logPath ?? serviceLogPath)(),
|
|
113
|
+
ownershipLease: (deps.leaseLine ?? (() => ownershipMutationLeaseStatusLine(serviceStatePaths())))() ?? null,
|
|
114
|
+
desktopLogin: desktopLogin(),
|
|
115
|
+
};
|
|
116
|
+
}
|
package/src/types/config.ts
CHANGED
|
@@ -2,7 +2,9 @@ import type { OcxProviderConfig } from "./provider";
|
|
|
2
2
|
import type { CodexAccount } from "./accounts";
|
|
3
3
|
import type { AnthropicAccountPoolConfig, OcxAccountPoolRotationStrategy } from "./anthropic-account-pool";
|
|
4
4
|
import type { AnthropicInstanceId } from "../providers/anthropic-instance-id";
|
|
5
|
+
import type { OcxFallbackConfig } from "./fallback";
|
|
5
6
|
export type { AnthropicAccountPoolConfig, AnthropicModelRoute, OcxAccountPoolRotationStrategy, OcxAccountPoolQuotaWindow } from "./anthropic-account-pool";
|
|
7
|
+
export type { OcxFallbackConfig, OcxFallbackDeviceConfig, OcxFallbackKind, OcxFallbackSwitches } from "./fallback";
|
|
6
8
|
|
|
7
9
|
/** Public inference API exposure. Responses and Chat Completions are always served. */
|
|
8
10
|
export interface OcxApiSurfacesConfig {
|
|
@@ -1361,6 +1363,11 @@ export interface OcxConfig {
|
|
|
1361
1363
|
tokenGuardian?: OcxTokenGuardianConfig;
|
|
1362
1364
|
/** Additional exact origins allowed for CORS (e.g. HTTPS or chrome-extension://<id>). Loopback origins are always allowed. */
|
|
1363
1365
|
corsAllowOrigins?: string[];
|
|
1366
|
+
/**
|
|
1367
|
+
* OCX-11 fallback off: refuse model or provider fallback per scope, and record per-device local
|
|
1368
|
+
* fallback permission. Absent keeps every chain as it was. See src/routing/fallback-policy.ts.
|
|
1369
|
+
*/
|
|
1370
|
+
fallback?: OcxFallbackConfig;
|
|
1364
1371
|
}
|
|
1365
1372
|
|
|
1366
1373
|
export type OcxComboStrategy = "failover" | "round-robin" | "random" | "least-used" | "reset-window" | "jev";
|
|
@@ -1489,6 +1496,12 @@ export interface OcxComboConfig {
|
|
|
1489
1496
|
decisionQuotaSignals?: boolean | null;
|
|
1490
1497
|
/** Partial 0..100 tier overrides; defaults limited 70/nearlyExhausted 90. null clears. */
|
|
1491
1498
|
decisionQuotaTiers?: import("../combos/jev-quota-config").JevQuotaTiers | null;
|
|
1499
|
+
/**
|
|
1500
|
+
* `strategy: "jev"` only, default off: keep an explicit client session on the target JEV chose
|
|
1501
|
+
* for it while the session stays active, so the provider prompt cache stays warm (#7525).
|
|
1502
|
+
* `ttlMs` is the idle window after the last successful turn (60000..86400000). null clears.
|
|
1503
|
+
*/
|
|
1504
|
+
sessionPin?: import("../combos/jev-session-pin-config").JevSessionPinConfig | null;
|
|
1492
1505
|
}
|
|
1493
1506
|
|
|
1494
1507
|
export type OcxRoutingUnknownEvidenceMode = "allow" | "penalize" | "exclude";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OCX-11 fallback policy (`fallback` in config.json). Every leaf is optional and absence keeps
|
|
3
|
+
* today's behavior: model and provider fallback allowed, local fallback not granted.
|
|
4
|
+
*
|
|
5
|
+
* Contract and precedence: `src/routing/fallback-policy.ts` and `structure/fallback-policy.md`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** A fallback kind this policy governs. Intra-pool account or key rotation is not one of them. */
|
|
9
|
+
export type OcxFallbackKind = "model" | "provider";
|
|
10
|
+
|
|
11
|
+
export interface OcxFallbackSwitches {
|
|
12
|
+
/** false refuses a hop to another model of the same provider. */
|
|
13
|
+
model?: boolean;
|
|
14
|
+
/** false refuses a hop to another provider. */
|
|
15
|
+
provider?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface OcxFallbackDeviceConfig extends OcxFallbackSwitches {
|
|
19
|
+
/**
|
|
20
|
+
* Permission only: true tells GOAT it may move this device's work to the device's own
|
|
21
|
+
* providers while the Home is unreachable. OpenCodex itself never serves a Child or Hub client
|
|
22
|
+
* from local providers; absent or false means no permission.
|
|
23
|
+
*/
|
|
24
|
+
localFallback?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface OcxFallbackConfig extends OcxFallbackSwitches {
|
|
28
|
+
/**
|
|
29
|
+
* Keyed by the provider a hop leaves: the resolved provider of the target that just failed or
|
|
30
|
+
* is known unavailable. In combo [a, b, c] with providers.b.provider false, a to b is allowed and
|
|
31
|
+
* b to c is refused.
|
|
32
|
+
*/
|
|
33
|
+
providers?: Record<string, OcxFallbackSwitches>;
|
|
34
|
+
/** Keyed by an exact public selector: combo/<id>, policy/<id>, provider/model or an alias. */
|
|
35
|
+
routes?: Record<string, OcxFallbackSwitches>;
|
|
36
|
+
/** Keyed by a Remote Link id (lnk_…) or a Hub-client data key id. Read on the Home. */
|
|
37
|
+
devices?: Record<string, OcxFallbackDeviceConfig>;
|
|
38
|
+
}
|
package/src/types/provider.ts
CHANGED
|
@@ -871,6 +871,14 @@ export interface OcxProviderConfig {
|
|
|
871
871
|
* SSE/JSON; raw inspection state remains authoritative.
|
|
872
872
|
*/
|
|
873
873
|
responsesSnapshotRepair?: boolean;
|
|
874
|
+
/**
|
|
875
|
+
* Infer the commentary/final_answer phase on assistant messages for Responses passthrough
|
|
876
|
+
* providers whose upstream omits it (e.g. third-party gateways). Codex desktop collapses
|
|
877
|
+
* commentary into progress rows; without this every narration renders fully expanded.
|
|
878
|
+
* Disabled by default. Applies in the client-facing SSE rewrite chain, including
|
|
879
|
+
* buffered JSON reconstructed from upstream SSE; ordinary upstream JSON is unchanged.
|
|
880
|
+
*/
|
|
881
|
+
messagePhaseInference?: boolean;
|
|
874
882
|
/**
|
|
875
883
|
* Opt-in hosted-web-search bridge for this KEY-auth Responses passthrough provider (#3761).
|
|
876
884
|
* Absent or disabled leaves the passthrough byte-identical to today.
|
|
@@ -1116,7 +1124,7 @@ export interface OcxProviderConfig {
|
|
|
1116
1124
|
googleMode?: "ai-studio" | "vertex" | "cloud-code-assist";
|
|
1117
1125
|
/** Google tool-schema compatibility policy. Omitted preserves compatible report-only behavior. */
|
|
1118
1126
|
googleToolSchemaPolicy?: "compatible" | "reject-lossy";
|
|
1119
|
-
/** Vertex AI GCP project id. API keys use it to select the project endpoint; ADC falls back to GOOGLE_CLOUD_PROJECT / GCLOUD_PROJECT env. */
|
|
1127
|
+
/** Vertex AI GCP project id. API keys use it to select the project endpoint; "auto" resolves each API key's own project. ADC falls back to GOOGLE_CLOUD_PROJECT / GCLOUD_PROJECT env. */
|
|
1120
1128
|
project?: string;
|
|
1121
1129
|
/** Vertex AI location, e.g. "us-central1" or "global". API-key project endpoints default to "global"; ADC falls back to GOOGLE_CLOUD_LOCATION env. */
|
|
1122
1130
|
location?: string;
|
package/src/types/request.ts
CHANGED
|
@@ -148,6 +148,7 @@ export interface OcxParsedRequest {
|
|
|
148
148
|
_kiroAuthContext?: Pick<KiroOAuthMetadata, "profileArn" | "apiRegion" | "ssoRegion" | "authType">;
|
|
149
149
|
/** Account-scoped Zed user identity paired with the long-lived access token. */
|
|
150
150
|
_zedAuthContext?: { userId: string };
|
|
151
|
+
_workbuddyAuthContext?: import("../oauth/types").WorkBuddyOAuthMetadata;
|
|
151
152
|
/** Provider-private continuation metadata resolved from the Responses previous_response_id chain. */
|
|
152
153
|
_providerContinuation?: OcxProviderContinuationState;
|
|
153
154
|
/** Persisted continuation considered only after the final physical route is known. */
|
package/src/types/wire.ts
CHANGED
|
@@ -101,6 +101,7 @@ function normalizedEndpoint(value: unknown): string | undefined {
|
|
|
101
101
|
* URL. The empty prefix covers every model id.
|
|
102
102
|
*/
|
|
103
103
|
const CREDENTIAL_PINNED_ADAPTERS: Readonly<Record<string, Readonly<Record<string, string>>>> = Object.freeze({
|
|
104
|
+
"workbuddy-ai": Object.freeze({ "": "workbuddy-ai" }),
|
|
104
105
|
"dsh-account": Object.freeze({ "": "dsh-account" }),
|
|
105
106
|
});
|
|
106
107
|
|
|
@@ -16,3 +16,12 @@ export const CODEX_CLI_VERSION_MANAGER_ROOT_ENV_SLOTS: readonly [
|
|
|
16
16
|
"VOLTA_HOME",
|
|
17
17
|
];
|
|
18
18
|
export function isCodexCliUpdateInspectionArgv(argv: readonly string[]): boolean;
|
|
19
|
+
export const NODE_LAUNCH_CONTEXT_MAX_CHARS: number;
|
|
20
|
+
export function codexCliInspectionLookupSeeds(
|
|
21
|
+
platform: string,
|
|
22
|
+
home: string | null,
|
|
23
|
+
nodePath: string | null,
|
|
24
|
+
serializedContextChars: number,
|
|
25
|
+
): { home: string | null; nodePath: string | null };
|
|
26
|
+
export const CODEX_CLI_TRANSPORT_ENV_KEYS: readonly string[];
|
|
27
|
+
export function captureCodexCliTransportEnv(env: Readonly<NodeJS.ProcessEnv>): Record<string, string>;
|
|
@@ -16,6 +16,16 @@ export const CODEX_CLI_VERSION_MANAGER_ROOT_ENV_SLOTS = Object.freeze([
|
|
|
16
16
|
"VOLTA_HOME",
|
|
17
17
|
]);
|
|
18
18
|
|
|
19
|
+
export const CODEX_CLI_TRANSPORT_ENV_KEYS = Object.freeze([
|
|
20
|
+
"http_proxy", "https_proxy", "all_proxy", "no_proxy", "node_extra_ca_certs",
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
/** Preserve spelling, aliases and empty values before Bun loads project dotenv. */
|
|
24
|
+
export function captureCodexCliTransportEnv(env) {
|
|
25
|
+
return Object.fromEntries(Object.entries(env).filter(([name, value]) =>
|
|
26
|
+
CODEX_CLI_TRANSPORT_ENV_KEYS.includes(name.toLowerCase()) && typeof value === "string"));
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
/**
|
|
20
30
|
* Detect the read-only Codex CLI updater inspection namespace before Bun loads.
|
|
21
31
|
* Keep this exact and argument-position based: malformed actions still inherit
|
|
@@ -28,3 +38,23 @@ export function isCodexCliUpdateInspectionArgv(argv) {
|
|
|
28
38
|
const args = argv.slice(2).filter(value => !value.startsWith("--ocx-internal-launch-proof="));
|
|
29
39
|
return args[0] === "system" && args[1] === "codex-cli-update";
|
|
30
40
|
}
|
|
41
|
+
|
|
42
|
+
/** Serialized OCX_NODE_LAUNCH_CONTEXT ceiling; src/cli/launcher-context.ts refuses above it. */
|
|
43
|
+
export const NODE_LAUNCH_CONTEXT_MAX_CHARS = 64 * 1024;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The HOME/NODE_PATH seeds the update inspection's modeled Node lookup needs,
|
|
47
|
+
* bounded against the launch-context budget. They are POSIX-only: a Windows
|
|
48
|
+
* inspection never models the global lookup, so the seeds are not sent at all
|
|
49
|
+
* (and their env copies stay inherited for the child's own use). If carrying
|
|
50
|
+
* them would push the serialized context past the reader's limit they are
|
|
51
|
+
* dropped instead of taking the whole snapshot down with them — absent seeds
|
|
52
|
+
* leave only the global lane unverifiable, which fails the content
|
|
53
|
+
* observation closed while the selection context survives.
|
|
54
|
+
*/
|
|
55
|
+
export function codexCliInspectionLookupSeeds(platform, home, nodePath, serializedContextChars) {
|
|
56
|
+
if (platform === "win32" || serializedContextChars > NODE_LAUNCH_CONTEXT_MAX_CHARS) {
|
|
57
|
+
return { home: null, nodePath: null };
|
|
58
|
+
}
|
|
59
|
+
return { home: home ?? null, nodePath: nodePath ?? null };
|
|
60
|
+
}
|
package/src/usage/jev-stats.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeJevQuotaSummary, type JevQuotaDecisionSummary } from "../combos/jev-quota-config";
|
|
2
|
+
import { JEV_SESSION_PIN_REASONS, type JevSessionPinReason } from "../combos/jev-session-pin-config";
|
|
2
3
|
import type { OcxComboDefaultEffort } from "../types";
|
|
3
4
|
import type { PersistedUsageEntry } from "./log";
|
|
4
5
|
import { usageDisplayTotalTokens } from "./totals";
|
|
@@ -48,6 +49,10 @@ export interface PersistedJevDecisionV1 {
|
|
|
48
49
|
outputTokens: number;
|
|
49
50
|
totalTokens: number;
|
|
50
51
|
};
|
|
52
|
+
/** The turn reused its session pin (#7525); no decision request was sent for it. */
|
|
53
|
+
pinned?: true;
|
|
54
|
+
/** `session` with `pinned`; otherwise why an existing session pin was released. */
|
|
55
|
+
pinReason?: JevSessionPinReason;
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
export interface JevStatsModelRow {
|
|
@@ -88,6 +93,8 @@ export interface JevStatsResponse {
|
|
|
88
93
|
decisions: number;
|
|
89
94
|
appliedDecisions: number;
|
|
90
95
|
failOpenDecisions: number;
|
|
96
|
+
/** Requests that reused a session pin; they are not decisions and add only physical attempts. */
|
|
97
|
+
pinnedRequests: number;
|
|
91
98
|
successfulRequests: number;
|
|
92
99
|
requestsWithModelFallback: number;
|
|
93
100
|
modelAttempts: number;
|
|
@@ -188,6 +195,13 @@ export function normalizePersistedJevDecision(value: unknown): PersistedJevDecis
|
|
|
188
195
|
const backend = value.backend === "typesafe" || value.backend === "systemone" || value.backend === "model"
|
|
189
196
|
? value.backend
|
|
190
197
|
: undefined;
|
|
198
|
+
const pinReason = typeof value.pinReason === "string"
|
|
199
|
+
&& (JEV_SESSION_PIN_REASONS as readonly string[]).includes(value.pinReason)
|
|
200
|
+
? value.pinReason as JevSessionPinReason
|
|
201
|
+
: undefined;
|
|
202
|
+
// A pinned turn always says so with `session`; any other pairing is not a record this writes.
|
|
203
|
+
const pinned = value.pinned === true && pinReason === "session";
|
|
204
|
+
if ((value.pinned === true || pinReason === "session") && !pinned) return undefined;
|
|
191
205
|
return {
|
|
192
206
|
version: 1,
|
|
193
207
|
comboId,
|
|
@@ -203,6 +217,8 @@ export function normalizePersistedJevDecision(value: unknown): PersistedJevDecis
|
|
|
203
217
|
...(confidence !== undefined ? { confidence } : {}),
|
|
204
218
|
...(chosenProbability !== undefined ? { chosenProbability } : {}),
|
|
205
219
|
...(usage ? { usage } : {}),
|
|
220
|
+
...(pinned ? { pinned: true as const } : {}),
|
|
221
|
+
...(pinReason ? { pinReason } : {}),
|
|
206
222
|
};
|
|
207
223
|
}
|
|
208
224
|
|
|
@@ -210,6 +226,24 @@ interface MutableModelRow extends Omit<JevStatsModelRow, "efforts"> {
|
|
|
210
226
|
effortCounts: Map<OcxComboDefaultEffort | null, number>;
|
|
211
227
|
}
|
|
212
228
|
|
|
229
|
+
interface SentAttempt {
|
|
230
|
+
attempt: NonNullable<PersistedUsageEntry["attempts"]>[number];
|
|
231
|
+
provider: string;
|
|
232
|
+
model: string;
|
|
233
|
+
sendCount: number;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Attempts that physically sent, with bounded identities; zero-send rows never count. */
|
|
237
|
+
function sentAttempts(entry: PersistedUsageEntry): SentAttempt[] {
|
|
238
|
+
return (entry.attempts ?? []).flatMap(attempt => {
|
|
239
|
+
const sendCount = boundedCount(attempt.sendCount) ?? 0;
|
|
240
|
+
if (sendCount === 0) return [];
|
|
241
|
+
const provider = boundedIdentity(attempt.provider, MAX_TARGET_IDENTITY_CHARS);
|
|
242
|
+
const model = boundedIdentity(attempt.model, MAX_TARGET_IDENTITY_CHARS);
|
|
243
|
+
return provider && model ? [{ attempt, provider, model, sendCount }] : [];
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
213
247
|
interface JevStatsAccumulatorOptions {
|
|
214
248
|
comboId?: string | null;
|
|
215
249
|
since?: number | null;
|
|
@@ -291,6 +325,7 @@ class StreamingJevStatsAccumulator implements JevStatsAccumulator {
|
|
|
291
325
|
private snapshotWindowStart: number | null = null;
|
|
292
326
|
private snapshotWindowEnd: number | null = null;
|
|
293
327
|
private decisions = 0;
|
|
328
|
+
private pinnedRequests = 0;
|
|
294
329
|
private appliedDecisions = 0;
|
|
295
330
|
private successfulRequests = 0;
|
|
296
331
|
private requestsWithModelFallback = 0;
|
|
@@ -358,6 +393,21 @@ class StreamingJevStatsAccumulator implements JevStatsAccumulator {
|
|
|
358
393
|
|| (this.until !== undefined && entry.timestamp > this.until)) return;
|
|
359
394
|
const decision = normalizePersistedJevDecision(entry.jevDecision);
|
|
360
395
|
if (!decision || (this.comboId !== null && decision.comboId !== this.comboId)) return;
|
|
396
|
+
const attempts = sentAttempts(entry);
|
|
397
|
+
// Request-level outcomes count every JEV-routed request, pinned or decided.
|
|
398
|
+
if (entry.status >= 200 && entry.status < 400) {
|
|
399
|
+
this.successfulRequests = saturatingAdd(this.successfulRequests, 1);
|
|
400
|
+
}
|
|
401
|
+
if (attempts.some(({ provider, model }) => provider !== decision.selected.provider
|
|
402
|
+
|| model !== decision.selected.model)) {
|
|
403
|
+
this.requestsWithModelFallback = saturatingAdd(this.requestsWithModelFallback, 1);
|
|
404
|
+
}
|
|
405
|
+
this.addAttempts(attempts);
|
|
406
|
+
if (decision.pinned) {
|
|
407
|
+
// A session-pinned turn sent no decision request, so it stays out of every decision total.
|
|
408
|
+
this.pinnedRequests = saturatingAdd(this.pinnedRequests, 1);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
361
411
|
|
|
362
412
|
const backend = decision.backend ?? "unknown";
|
|
363
413
|
const backendRow = this.backends.get(backend)
|
|
@@ -369,9 +419,6 @@ class StreamingJevStatsAccumulator implements JevStatsAccumulator {
|
|
|
369
419
|
this.averageLatencyMs = nextMean(this.averageLatencyMs, this.decisions, decision.latencyMs);
|
|
370
420
|
this.decisions = saturatingAdd(this.decisions, 1);
|
|
371
421
|
if (decision.gate === "apply") this.appliedDecisions = saturatingAdd(this.appliedDecisions, 1);
|
|
372
|
-
if (entry.status >= 200 && entry.status < 400) {
|
|
373
|
-
this.successfulRequests = saturatingAdd(this.successfulRequests, 1);
|
|
374
|
-
}
|
|
375
422
|
this.gates.set(decision.gate, saturatingAdd(this.gates.get(decision.gate) ?? 0, 1));
|
|
376
423
|
if (decision.confidence !== undefined) {
|
|
377
424
|
this.confidenceTotal = saturatingAdd(this.confidenceTotal, decision.confidence);
|
|
@@ -396,18 +443,9 @@ class StreamingJevStatsAccumulator implements JevStatsAccumulator {
|
|
|
396
443
|
decision.selected.effort,
|
|
397
444
|
saturatingAdd(selected.effortCounts.get(decision.selected.effort) ?? 0, 1),
|
|
398
445
|
);
|
|
446
|
+
}
|
|
399
447
|
|
|
400
|
-
|
|
401
|
-
const sendCount = boundedCount(attempt.sendCount) ?? 0;
|
|
402
|
-
if (sendCount === 0) return [];
|
|
403
|
-
const provider = boundedIdentity(attempt.provider, MAX_TARGET_IDENTITY_CHARS);
|
|
404
|
-
const model = boundedIdentity(attempt.model, MAX_TARGET_IDENTITY_CHARS);
|
|
405
|
-
return provider && model ? [{ attempt, provider, model, sendCount }] : [];
|
|
406
|
-
});
|
|
407
|
-
if (attempts.some(({ provider, model }) => provider !== decision.selected.provider
|
|
408
|
-
|| model !== decision.selected.model)) {
|
|
409
|
-
this.requestsWithModelFallback = saturatingAdd(this.requestsWithModelFallback, 1);
|
|
410
|
-
}
|
|
448
|
+
private addAttempts(attempts: readonly SentAttempt[]): void {
|
|
411
449
|
for (const { attempt, provider, model: modelId, sendCount } of attempts) {
|
|
412
450
|
const model = this.rowFor(provider, modelId);
|
|
413
451
|
model.attempts = saturatingAdd(model.attempts, sendCount);
|
|
@@ -449,6 +487,7 @@ class StreamingJevStatsAccumulator implements JevStatsAccumulator {
|
|
|
449
487
|
cloned.snapshotWindowStart = this.snapshotWindowStart;
|
|
450
488
|
cloned.snapshotWindowEnd = this.snapshotWindowEnd;
|
|
451
489
|
cloned.decisions = this.decisions;
|
|
490
|
+
cloned.pinnedRequests = this.pinnedRequests;
|
|
452
491
|
cloned.appliedDecisions = this.appliedDecisions;
|
|
453
492
|
cloned.successfulRequests = this.successfulRequests;
|
|
454
493
|
cloned.requestsWithModelFallback = this.requestsWithModelFallback;
|
|
@@ -492,6 +531,7 @@ class StreamingJevStatsAccumulator implements JevStatsAccumulator {
|
|
|
492
531
|
decisions: this.decisions,
|
|
493
532
|
appliedDecisions: this.appliedDecisions,
|
|
494
533
|
failOpenDecisions: this.decisions - this.appliedDecisions,
|
|
534
|
+
pinnedRequests: this.pinnedRequests,
|
|
495
535
|
successfulRequests: this.successfulRequests,
|
|
496
536
|
requestsWithModelFallback: this.requestsWithModelFallback,
|
|
497
537
|
modelAttempts: this.modelAttempts,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.pws-completion-test{align-items:start;gap:var(--space-2);margin-top:var(--space-4);grid-template-columns:minmax(0,1fr) auto;display:grid}.pws-completion-test>h4,.pws-completion-result{grid-column:1/-1}.pws-completion-test>.custom-select{min-width:0}.pws-completion-result{overflow-wrap:anywhere}@media (width<=640px){.pws-completion-test{grid-template-columns:minmax(0,1fr)}}.codex-account-controls{flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:12px;min-width:0;display:flex}.codex-account-auto-switch{flex:none;align-items:center;gap:7px;min-width:0;display:flex}.codex-account-auto-switch-label{font-size:var(--text-label);color:var(--muted);font-weight:var(--weight-medium);white-space:nowrap}.codex-account-auto-switch-input-wrap{align-items:stretch;display:inline-flex;position:relative}.codex-account-auto-switch-input{width:76px;height:30px;font-size:var(--text-label);text-align:right;padding:4px 38px 4px 8px}.codex-account-auto-switch-unit{color:var(--muted);font-size:var(--text-label);pointer-events:none;position:absolute;top:50%;right:25px;transform:translateY(-50%)}.codex-account-auto-switch-input-wrap .ocx-stepper{width:22px}.codex-account-auto-switch-toggle{transform-origin:50%;margin-inline:-3px;transform:scale(.8)}.quota-row--codex-credits .quota-val{white-space:nowrap}.quota-compact:has(>.quota-row--codex-credits){grid-template-columns:minmax(34px,max-content) max-content minmax(34px,max-content) minmax(38px,max-content) minmax(58px,1fr) minmax(30px,max-content);column-gap:8px}.quota-compact:has(>.quota-row--codex-credits)>*{grid-column:1/-1}.quota-compact:has(>.quota-row--codex-credits)>.quota-row:not(.quota-row--credits){grid-template-columns:subgrid}.codex-auth-credits-toggle{align-items:center;gap:var(--space-2);white-space:nowrap;display:inline-flex}.codex-auth-credits-toggle__label{font-size:var(--text-label);color:var(--muted)}.codex-credit-spend{align-items:center;gap:var(--space-2);white-space:nowrap;display:inline-flex}.codex-credit-spend .toggle[aria-pressed=mixed] .toggle-knob{transform:translate(8px,-50%)}.codex-account-credits{align-items:center;gap:7px;display:inline-flex}.codex-account-credits__label{font-size:var(--text-label);color:var(--muted);font-weight:var(--weight-medium);white-space:nowrap}.codex-account-credits__toggle{transform-origin:50%;margin-inline:-3px;transform:scale(.8)}.anthropic-pool-card__native-messages{align-items:flex-start;gap:var(--space-2);margin-top:var(--space-3);cursor:pointer;display:flex}.anthropic-pool-card__native-messages>input{margin:var(--space-0-5) 0 0;accent-color:var(--accent)}.anthropic-pool-card__native-messages>input:focus-visible{outline:var(--space-0-5) solid var(--accent);outline-offset:var(--space-0-5)}.anthropic-pool-card__native-messages-copy{gap:var(--space-1);max-width:var(--prose-measure);display:grid}.anthropic-pool-card__native-messages:has(input:disabled){cursor:default}.anthropic-pool-card__save-warning{margin:var(--space-2) var(--space-4);color:var(--amber);font-size:var(--text-label);line-height:var(--leading-body)}.kiro-device-dialog{max-width:var(--prose-measure);border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--surface);width:100%;color:var(--text);padding:var(--space-6)}.kiro-device-dialog::backdrop{background:var(--glass-rail)}.kiro-login-choices{gap:var(--space-2);margin-block:var(--space-4);display:grid}.kiro-login-choices .btn{min-height:var(--control-touch);justify-content:flex-start}.kiro-device-code{align-items:center;gap:var(--space-3);margin-block:var(--space-4);flex-wrap:wrap;display:flex}.kiro-device-code-value{font-family:var(--font-code);font-size:var(--text-title);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-wide)}.kiro-device-destination{overflow-wrap:anywhere;-webkit-user-select:all;user-select:all}.kiro-device-actions{justify-content:flex-end;gap:var(--space-2);margin-top:var(--space-4);display:flex}.model-price-dialog{flex-direction:column;max-height:calc(100dvh - 16vh);display:flex}.model-price-dialog>.modal-head,.model-price-dialog>.modal-actions{flex:none}.model-price-body{overscroll-behavior:contain;flex:auto;min-height:0;padding:2px;overflow-y:auto}.model-price-body .input{margin-bottom:6px}.model-price-body>.small{text-wrap:balance}.model-price-rates-grid{gap:var(--space-1-5) var(--space-3);grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.model-price-rate-field{min-width:0}.model-price-rate-field .field-label{margin-bottom:var(--space-1)}.model-price-rate-field .input{margin-bottom:0}.model-price-mode{border:0;min-width:0;margin:12px 0 0;padding:0}.model-price-mode>legend{margin-bottom:5px;padding:0}.model-price-mode-option{cursor:pointer;align-items:flex-start;gap:8px;padding:6px 0;display:flex}.model-price-mode-option input{accent-color:var(--accent);margin-top:3px}.model-price-mode-option>span{gap:2px;min-width:0;display:grid}.model-price-band{border-top:1px solid var(--border);margin-top:12px;padding-top:12px}.model-price-band-title{font-weight:var(--weight-medium);margin-bottom:2px}.model-price-custom-controls{gap:var(--space-2);grid-template-columns:minmax(0,1fr) minmax(0,1fr);display:grid}.model-price-custom-controls .field-label{min-height:2.5em}.model-price-dialog .model-display-name-error{margin-top:8px}@media (width<=760px){.model-price-mode-option{min-height:44px}}@media (width<=560px){.model-price-rates-grid{grid-template-columns:1fr}.model-price-custom-controls{grid-template-columns:1fr;gap:4px}.model-price-custom-controls .field-label{min-height:0}}.advisor-editor-row{align-items:center;gap:var(--space-3);margin:var(--space-2) 0;display:flex}.advisor-editor-label{min-width:11rem}.advisor-editor-row>input,.advisor-editor-row>select{min-width:0;max-width:100%}.advisor-activity-table th,.advisor-activity-table td{white-space:nowrap}@media (width<=760px){.advisor-editor-row{align-items:flex-start;gap:var(--space-1);flex-direction:column}.advisor-editor-label{min-width:0}.advisor-editor-row>input,.advisor-editor-row>select{width:100%;min-height:44px}}.logs-stack-end.logs-decode-rate{white-space:normal;display:flex}.logs-decode-rate>span:first-child{white-space:nowrap}.logs-decode-rate .text-caption{font-family:var(--font-ui);overflow-wrap:break-word}.log-detail-grid.log-detail-performance-grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.log-detail-performance-grid>span{overflow-wrap:break-word;min-width:0}.logs-decode-basis-hint{grid-column:1/-1}.main-inner:has(.claude-page .claudecode-workspace-shell){max-width:980px}.claude-panel-lead{color:var(--muted);font-size:var(--text-label);line-height:var(--leading-body);max-width:var(--prose-measure);margin:0}.claudecode-connection-card{margin-bottom:var(--space-4)}.claudecode-workspace-shell .setting-controls>.custom-select{text-align:right}.claude-page .claude-status-bar{z-index:8;box-shadow:var(--shadow-sm);flex-wrap:wrap;padding:6px 8px 6px 14px;position:sticky;top:12px}.claude-page .claude-status-bar>.claude-status-health{margin-left:0}.claude-status-actions{align-items:center;gap:var(--space-3);margin-left:auto;display:flex}.claude-desktop-roles{margin-bottom:var(--space-4);overflow:hidden}.claude-desktop-section-title{font-size:var(--text-control);font-weight:var(--weight-semibold);line-height:var(--leading-ui);color:var(--text);align-items:baseline;gap:8px;margin:0;display:flex}.claude-desktop-section-title .count{color:var(--muted);font-weight:var(--weight-medium);font-family:var(--font-code);font-size:var(--text-label)}.claude-desktop-roles>.claude-desktop-section-title{padding:12px 16px 4px}.claude-desktop-roles-scope{color:var(--muted);font-size:var(--text-label);margin:0;padding:0 16px 8px}.claude-desktop-roles .setting-controls>.custom-select{text-align:right}.claude-desktop-roles .setting-label{flex:12rem}.claude-desktop-roles .setting-controls{flex:0 auto;min-width:0;max-width:min(60%,28rem)}.claude-desktop-roles .setting-controls>.custom-select,.claude-desktop-roles .select-trigger{max-width:100%}.claude-desktop-roles .select-trigger>span:first-child{text-overflow:ellipsis;min-width:0;overflow:hidden}.claude-desktop-roles .select-trigger>svg{flex:none}.claude-role-option{vertical-align:bottom;min-width:0;max-width:100%;display:inline-flex}.claude-role-option__route{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.claude-role-option__status{white-space:pre;color:var(--muted);flex:none}@media (width<=760px){.claude-desktop-roles .setting-controls{max-width:100%}.claude-desktop-roles .setting-controls>.custom-select,.claude-desktop-roles .select-trigger{width:100%}.claude-desktop-roles .select-trigger{justify-content:space-between}}.claude-desktop-list{gap:var(--space-2);margin-bottom:var(--space-4);flex-direction:column;display:flex}.claude-desktop-list-head{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;display:flex}.claude-desktop-list-hint{color:var(--muted);font-size:var(--text-label);margin:0}.claude-desktop-list-rows{margin:0;padding:0;list-style:none;overflow:hidden}.claude-desktop-list-row{border-top:1px solid var(--border-soft,var(--border));align-items:center;gap:10px;min-width:0;padding:8px 14px;display:flex}.claude-desktop-list-row:first-child{border-top:none}.claude-desktop-list-names{flex:1;align-items:baseline;gap:8px;min-width:0;display:flex}.claude-desktop-list-names strong,.claude-desktop-list-names code{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.claude-desktop-list-names strong{font-weight:var(--weight-medium)}.claude-desktop-list-names code{color:var(--muted);font-size:11px}.claude-row-quick{color:var(--accent)}.claude-desktop-advanced{margin-bottom:var(--space-4);border:1px solid var(--border);border-radius:var(--radius);background:var(--surface)}.claude-desktop-advanced>summary{cursor:pointer;flex-direction:column;gap:8px;padding:12px 14px;list-style:none;display:flex}.claude-desktop-advanced>summary::-webkit-details-marker{display:none}.claude-desktop-advanced-title{font-weight:var(--weight-semibold);color:var(--text);align-items:center;gap:6px;display:flex}.claude-desktop-advanced-chips{flex-wrap:wrap;gap:6px;display:flex}.claude-desktop-advanced-chip{border-radius:var(--radius-pill);background:var(--raised);max-width:100%;color:var(--muted);font-size:var(--text-label);align-items:baseline;gap:6px;padding:2px 10px;display:inline-flex}.claude-desktop-advanced-chip b{color:var(--text);font-weight:var(--weight-medium)}.claude-desktop-advanced-chip code{text-overflow:ellipsis;white-space:nowrap;max-width:220px;font-size:11px;overflow:hidden}.claude-desktop-advanced-count{font-family:var(--font-code);color:var(--faint);font-size:11px}.claude-desktop-advanced-desc{color:var(--muted);font-size:var(--text-label);margin:0;padding:0 14px 10px}.claude-desktop-advanced>.ocx-group-stack{padding:0 10px 10px}.remote-link-page{gap:var(--space-4);display:grid}.remote-link-page .page-head{align-items:flex-start}.remote-link-page .page-sub{max-width:var(--prose-measure);margin:0}.remote-link-panel{gap:var(--space-4);display:grid}.remote-link-off-preview{min-height:260px;position:relative;overflow:hidden}.remote-link-off-preview:after{content:"";pointer-events:none;-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);background:color-mix(in srgb, var(--bg) 24%, transparent);position:absolute;inset:0}.remote-link-preview-content{gap:var(--space-3);filter:grayscale();opacity:.64;-webkit-user-select:none;user-select:none;display:grid}.remote-link-preview-row{justify-content:space-between;align-items:center;gap:var(--space-3);padding:var(--space-3);border:1px solid var(--border-soft);border-radius:var(--radius-sm);background:var(--raised);display:flex}.remote-link-preview-lines{gap:var(--space-2);width:min(55%,300px);display:grid}.remote-link-preview-lines span{border-radius:var(--radius-pill);background:var(--border);height:8px;display:block}.remote-link-switch-row{z-index:1;justify-content:space-between;align-items:center;gap:var(--space-4);display:flex;position:relative}.remote-link-switch{width:var(--control-touch);height:var(--control-md);border:1px solid var(--border);border-radius:var(--radius-pill);background:var(--toggle-off-bg);cursor:pointer;padding:3px}.remote-link-switch:after{content:"";background:var(--toggle-dot-color);width:24px;height:24px;transition:transform var(--motion-fast) ease;border-radius:50%;display:block}.remote-link-switch[aria-checked=true]{background:var(--toggle-on-bg)}.remote-link-switch[aria-checked=true]:after{transform:translate(16px)}.remote-link-switch:focus-visible,.remote-link-role-card:focus-visible,.remote-link-sheet button:focus-visible,.remote-link-sheet a:focus-visible,.remote-link-row button:focus-visible{outline:2px solid var(--accent-ring);outline-offset:2px}.remote-link-role-grid{gap:var(--space-3);grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.remote-link-role-card{min-height:132px;padding:var(--space-4);border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--raised);color:var(--text);text-align:left;cursor:pointer}.remote-link-role-card:hover{background:var(--raised-hover)}.remote-link-role-card[aria-disabled=true]{color:var(--muted);cursor:not-allowed;opacity:.72}.remote-link-role-card[aria-disabled=true]:hover{background:var(--raised)}.remote-link-role-card[aria-checked=true]{border-color:var(--text);box-shadow:0 0 0 1px var(--text)}.remote-link-role-card strong,.remote-link-role-card span{display:block}.remote-link-role-card span{margin-top:var(--space-2);color:var(--muted);font-size:var(--text-label)}.remote-link-toolbar{justify-content:space-between;align-items:center;gap:var(--space-3);display:flex}.remote-link-status{align-items:center;gap:var(--space-2);color:var(--muted);font-size:var(--text-label);display:inline-flex}.remote-link-status:before{content:"";background:var(--faint);border-radius:50%;flex:none;width:8px;height:8px}.remote-link-status[data-state=connected]:before{background:var(--green)}.remote-link-status[data-state=reconnecting]:before{background:var(--amber)}.remote-link-status[data-state=failed]:before{background:var(--red)}.remote-link-status[data-state=connecting]:before{background:var(--amber)}.remote-link-children{gap:var(--space-2);display:grid}.remote-link-row{gap:var(--space-3);padding:var(--space-3);border:1px solid var(--border-soft);border-radius:var(--radius-sm);background:var(--raised);grid-template-columns:minmax(0,1fr) auto;align-items:center;display:grid}.remote-link-row-main{gap:var(--space-2);min-width:0;display:grid}.remote-link-row-meta{gap:var(--space-3);color:var(--muted);font-size:var(--text-caption);flex-wrap:wrap;display:flex}.remote-link-row .btn{min-height:var(--control-touch)}.remote-link-error{color:var(--red)}.remote-link-hint{margin-top:var(--space-1);color:var(--muted);font-size:var(--text-label);overflow-wrap:anywhere;display:block}.remote-link-info{color:var(--muted);font-size:var(--text-label)}.remote-link-restart{gap:var(--space-2);padding:var(--space-4);border:1px solid var(--border-soft);border-radius:var(--radius-sm);background:var(--raised);display:grid}.remote-link-restart p{color:var(--muted);margin:0}.remote-link-fingerprint{padding:var(--space-3);overflow-wrap:anywhere;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--raised);font-family:var(--font-code);font-size:var(--text-label);margin:0}.remote-link-workspace-card{justify-content:space-between;align-items:center;gap:var(--space-4);display:flex}.remote-link-workspace-card p{margin:var(--space-1) 0 0;color:var(--muted);font-size:var(--text-label)}.remote-link-sheet{width:min(620px, calc(100vw - var(--space-4)));max-height:min(760px, calc(100dvh - var(--space-4)));padding:var(--space-6);border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--bg);color:var(--text);box-shadow:var(--shadow);margin:auto;overflow:auto}.remote-link-sheet::backdrop{background:color-mix(in srgb, var(--text) 24%, transparent)}.remote-link-sheet-head{justify-content:space-between;align-items:flex-start;gap:var(--space-3);display:flex}.remote-link-sheet-head h3{font-size:var(--text-title);margin:0}.remote-link-sheet-body{gap:var(--space-4);margin-top:var(--space-5);display:grid}.remote-link-sheet pre{white-space:pre-wrap;overflow-wrap:anywhere;font-family:var(--font-code);font-size:var(--text-label)}.remote-link-candidates{gap:var(--space-2);display:grid}.remote-link-candidate{justify-content:space-between;align-items:center;gap:var(--space-3);min-height:var(--control-touch);padding:var(--space-2) var(--space-3);border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--raised);color:var(--text);cursor:pointer;text-align:left;display:flex}.remote-link-candidate:hover{background:var(--raised-hover)}.remote-link-form{gap:var(--space-2);display:grid}.remote-link-form .field-label{margin-bottom:0}.remote-link-sheet-actions{justify-content:flex-end;gap:var(--space-2);flex-wrap:wrap;display:flex}.remote-link-confirm-dialog{width:min(460px, calc(100vw - var(--space-4)));padding:var(--space-5);border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);color:var(--text);box-shadow:var(--shadow);margin:auto}.remote-link-confirm-dialog::backdrop{background:color-mix(in srgb, var(--text) 24%, transparent)}.remote-link-confirm-dialog h3{font-size:var(--text-subtitle);margin:0}.remote-link-confirm-dialog p{color:var(--muted)}@media (width<=620px){.remote-link-role-grid{grid-template-columns:1fr}.remote-link-workspace-card,.remote-link-switch-row{flex-direction:column;align-items:stretch}.remote-link-switch{align-self:flex-start}.remote-link-sheet{width:calc(100vw - var(--space-2));border-radius:var(--radius-lg) var(--radius-lg) 0 0;max-height:88dvh;margin:auto auto 0}}@media (prefers-reduced-motion:reduce){.remote-link-page *,.remote-link-page :before,.remote-link-page :after{scroll-behavior:auto!important;transition:none!important;animation:none!important}}.quota-summary-bar{z-index:var(--z-sticky);border-bottom:1px solid var(--border);background:var(--bg);min-height:34px;font-size:var(--text-label);align-items:center;gap:12px;padding:4px 16px;display:flex;position:sticky;top:0}.quota-summary-scroller{flex:1;align-items:center;gap:2px;min-width:0;display:flex}.quota-summary-list{overscroll-behavior-x:contain;scrollbar-width:none;flex-wrap:nowrap;flex:auto;align-items:center;gap:4px;min-width:0;margin:0;padding:3px;list-style:none;display:flex;overflow:auto hidden}.quota-summary-list::-webkit-scrollbar{display:none}.quota-summary-item{flex:none}.quota-summary-scroll{border:1px solid var(--border);background:var(--surface);width:22px;height:22px;color:var(--text);font:inherit;cursor:pointer;border-radius:999px;flex:none;place-items:center;padding:0;font-weight:600;line-height:1;display:inline-grid}.quota-summary-scroll:hover:not([aria-disabled=true]){background:var(--bg)}.quota-summary-scroll[aria-disabled=true]{color:var(--muted);opacity:.45;cursor:default}.quota-summary-scroll:focus-visible{outline:2px solid var(--text);outline-offset:1px}.quota-summary-chip{color:var(--text);font:inherit;white-space:nowrap;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:999px;align-items:center;gap:6px;padding:3px 8px;text-decoration:none;display:inline-flex}.quota-summary-chip:hover,.quota-summary-chip--open{border-color:var(--border)}.quota-summary-chip:focus-visible{outline:2px solid var(--text);outline-offset:1px}.quota-summary-name{color:var(--muted)}.quota-summary-pct{font-variant-numeric:tabular-nums;font-weight:600}.quota-summary-flag{width:14px;height:14px;color:var(--bg);font-size:var(--text-micro);border-radius:50%;place-items:center;font-weight:700;display:inline-grid}.quota-summary-item--warn .quota-summary-chip{background:var(--amber-soft)}.quota-summary-item--warn .quota-summary-pct{color:var(--amber)}.quota-summary-item--warn .quota-summary-flag{background:var(--amber)}.quota-summary-item--critical .quota-summary-chip{border-color:var(--red);background:var(--red-soft)}.quota-summary-item--critical .quota-summary-pct{color:var(--red)}.quota-summary-item--critical .quota-summary-flag{background:var(--red)}.quota-summary-popover{top:calc(var(--qs-pop-top,0) * 1px);left:calc(var(--qs-pop-left,0) * 1px);z-index:var(--z-popover);border:1px solid var(--border);background:var(--surface);border-radius:8px;min-width:260px;max-width:calc(100% - 16px);padding:10px 12px;position:fixed;box-shadow:0 8px 24px #00000024}.quota-summary-popover:before{content:"";left:calc(var(--qs-bridge-left,0) * 1px - 1px);right:calc(var(--qs-bridge-right,0) * 1px - 1px);height:calc((var(--qs-pop-gap,4) + 1) * 1px);position:absolute;bottom:100%}.quota-summary-popover-head{justify-content:space-between;align-items:center;gap:12px;margin-bottom:6px;display:flex}.quota-summary-badge{font-size:var(--text-caption);border-radius:999px;padding:1px 6px}.quota-summary-badge--warn{color:var(--amber);background:var(--amber-soft)}.quota-summary-badge--critical{color:var(--red);background:var(--red-soft)}.quota-summary-table{border-collapse:collapse;width:100%}.quota-summary-table th,.quota-summary-table td{text-align:left;white-space:nowrap;padding:3px 0;font-weight:400}.quota-summary-table td.quota-summary-row-pct{font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap;padding:3px 12px;font-weight:600}.quota-summary-row-reset{color:var(--muted);white-space:nowrap}.quota-summary-table th[scope=row]{white-space:normal;overflow-wrap:anywhere}.quota-summary-row--warn .quota-summary-row-pct{color:var(--amber)}.quota-summary-row--critical .quota-summary-row-pct{color:var(--red)}.quota-summary-popover-foot{color:var(--muted);font-size:var(--text-caption);flex-wrap:wrap;justify-content:space-between;gap:4px 12px;margin-top:6px;display:flex}.quota-summary-updated{color:var(--muted);font-size:var(--text-caption);white-space:nowrap;flex-shrink:0}.quota-summary-popover-link{color:var(--text);text-underline-offset:2px;text-decoration:underline}.quota-summary-popover-link:focus-visible{outline:2px solid var(--text);outline-offset:1px}.quota-summary-updated--stale{color:var(--amber)}.main:has(>.quota-summary-bar):has(>.main-inner--combos .combos-workspace-shell){flex-direction:column;height:100dvh;display:flex}.main:has(>.quota-summary-bar)>.main-inner.main-inner--combos:has(.combos-workspace-shell){flex:auto;height:auto;min-height:0}@media (width<=760px){.quota-summary-bar{padding:4px 10px;position:static}.quota-summary-popover{top:calc(var(--qs-pop-top,0) * 1px);min-width:0;left:8px;right:8px}.main:has(>.quota-summary-bar):has(>.main-inner--combos .combos-workspace-shell){height:100%;min-height:0}}.section-switcher{border:1px solid var(--border);border-radius:var(--radius-pill);background:var(--surface);flex-wrap:wrap;gap:2px;max-width:100%;margin:0 0 16px;padding:2px;display:inline-flex}.section-switcher-btn{appearance:none;color:var(--muted);border-radius:var(--radius-pill);cursor:pointer;font:inherit;font-size:var(--text-control);white-space:nowrap;background:0 0;border:none;align-items:center;padding:5px 14px;display:inline-flex}.section-switcher-btn:hover{color:var(--text)}.section-switcher-btn[aria-current=page]{background:var(--raised);color:var(--text);font-weight:var(--weight-semibold)}.section-switcher-btn:focus-visible{outline:2px solid var(--accent-ring);outline-offset:1px}@media (width<=760px){.section-switcher{display:flex}.section-switcher-btn{min-height:var(--control-touch);flex:auto;justify-content:center}}.app{--titlebar-h:40px;--tl-inset:80px;--chrome-clear:52px}.app--macos{--chrome-clear:124px;--sidebar-column:max(232px, calc(var(--tl-inset) + 52px))}.sidebar-top{z-index:calc(var(--z-overlay,30) + 1);width:232px;height:var(--titlebar-h,40px);border-bottom:1px solid var(--border);background:var(--glass-rail);-webkit-backdrop-filter:var(--glass-blur);-webkit-user-select:none;user-select:none;align-items:center;padding:0 10px 0 14px;display:flex;position:fixed;top:0;left:0}.app--macos .sidebar-top{width:var(--sidebar-column);padding-left:var(--tl-inset,80px)}.sidebar{padding-top:calc(var(--titlebar-h,40px) + 18px)}.sidebar-collapse{border-radius:var(--radius-sm);width:28px;height:28px;color:var(--muted);cursor:pointer;transition:background var(--motion-fast), color var(--motion-fast);background:0 0;border:none;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.sidebar-collapse:hover{background:var(--accent-soft);color:var(--text)}.sidebar-collapse:focus-visible{outline:2px solid var(--accent-ring);outline-offset:2px}.sidebar-collapse svg{width:18px;height:18px}.main-top{z-index:var(--z-sticky);height:var(--titlebar-h,40px);background:var(--bg);border-bottom:1px solid var(--border);-webkit-user-select:none;user-select:none;align-items:stretch;display:flex;position:sticky;top:0}.main-top>.quota-summary-bar{border-bottom:none;flex:auto;min-width:0;min-height:0;padding:0 16px;position:static}.main:has(>.main-top):has(>.main-inner--combos .combos-workspace-shell){flex-direction:column;height:100dvh;display:flex}.main:has(>.main-top)>.main-inner.main-inner--combos:has(.combos-workspace-shell){flex:auto;height:auto;min-height:0}@media (width>=761px){.app{transition:grid-template-columns .16s}.app--macos.app--reduced-zoom{transition:none}.app--macos:not(.app--nav-collapsed){grid-template-columns:var(--sidebar-column) minmax(0, 1fr)}.app.app--nav-collapsed{grid-template-columns:0 1fr}.app--nav-collapsed .sidebar{display:none}.app--nav-collapsed .main{grid-column:2}.app--nav-collapsed .sidebar-top{-webkit-backdrop-filter:none;background:0 0;border-bottom:none;width:auto;margin:0;padding:0 12px}.app--macos.app--nav-collapsed .sidebar-top{padding-left:var(--tl-inset,80px)}.app--nav-collapsed .main-top,.app--nav-collapsed .main>.quota-summary-bar{padding-left:var(--chrome-clear,52px)}}@media (width<=760px){.sidebar-top{display:none}.main-top{z-index:auto;position:static}.main:has(>.main-top):has(>.main-inner--combos .combos-workspace-shell){height:100%;min-height:0}.app--macos .mobile-topbar{padding-left:var(--tl-inset,80px)}.app--macos .sidebar.open{padding-top:calc(var(--titlebar-h,40px) + 18px)}}@media (width<=320px){.app--macos .mobile-topbar{flex-wrap:wrap}.app--macos .mobile-topbar-actions{width:calc(100% + var(--tl-inset,80px));margin-left:calc(-1 * var(--tl-inset,80px));justify-content:flex-end}}
|