@opengeni/core 3.0.1 → 3.1.2
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/README.md +4 -4
- package/dist/access/index.d.ts +131 -7
- package/dist/access/service-initiator.d.ts +3 -0
- package/dist/application/archived-session-imports.d.ts +6 -0
- package/dist/application/composer-submit.d.ts +1 -1
- package/dist/application/connect-operation.d.ts +6 -0
- package/dist/application/external-workspace-members.d.ts +13 -1
- package/dist/application/modal-native-live-declaration-join.d.ts +37 -0
- package/dist/application/modal-native-original-configuration.d.ts +79 -0
- package/dist/application/modal-native-worker-host-transport.d.ts +297 -0
- package/dist/application/new-session-drafts.d.ts +29 -2
- package/dist/application/organization-privacy.d.ts +57 -0
- package/dist/application/prepared-mcp-connection.d.ts +31 -0
- package/dist/application/sandbox-recovery.d.ts +58 -0
- package/dist/application/session-commands.d.ts +3 -0
- package/dist/application/session-tenancy.d.ts +13 -4
- package/dist/billing/agent-run-admission.d.ts +21 -0
- package/dist/billing/limits.d.ts +27 -1
- package/dist/billing/model-call-admission.d.ts +34 -0
- package/dist/billing/model-usage-settlement.d.ts +112 -0
- package/dist/canonical-human-identities.js +1 -1
- package/dist/{chunk-VCZFFEPB.js → chunk-7T2SZ7BI.js} +16 -2
- package/dist/chunk-7T2SZ7BI.js.map +1 -0
- package/dist/{chunk-ZYCNLYGH.js → chunk-OMJFIIWK.js} +178 -12
- package/dist/chunk-OMJFIIWK.js.map +1 -0
- package/dist/{chunk-H7X52RI2.js → chunk-QI2M2B2K.js} +83 -8
- package/dist/chunk-QI2M2B2K.js.map +1 -0
- package/dist/{chunk-OF65T3PM.js → chunk-TTWTXQB2.js} +94 -6
- package/dist/chunk-TTWTXQB2.js.map +1 -0
- package/dist/codex-model-availability.d.ts +55 -0
- package/dist/default-session-model.d.ts +192 -0
- package/dist/dependencies.d.ts +41 -7
- package/dist/developer-setup-credential-ceiling.d.ts +10 -0
- package/dist/domain/agent-config-resolution.d.ts +49 -0
- package/dist/domain/automations.d.ts +8 -1
- package/dist/domain/capabilities.d.ts +112 -8
- package/dist/domain/connection-account-selection-error.d.ts +8 -0
- package/dist/domain/connector-tool-permissions.d.ts +62 -0
- package/dist/domain/direct-model-provider.d.ts +3 -0
- package/dist/domain/editable-artifacts/agent-application.d.ts +10 -1
- package/dist/domain/editable-artifacts/durable-export.d.ts +22 -1
- package/dist/domain/editable-artifacts/ports.d.ts +10 -0
- package/dist/domain/editable-artifacts/service.d.ts +3 -1
- package/dist/domain/editable-artifacts/types.d.ts +1 -1
- package/dist/domain/encryption-key.d.ts +12 -0
- package/dist/domain/insights-timing.d.ts +16 -0
- package/dist/domain/insights-usage.d.ts +27 -0
- package/dist/domain/insights.d.ts +21 -3
- package/dist/domain/knowledge-files.d.ts +1 -1
- package/dist/domain/knowledge-preparation.d.ts +10 -0
- package/dist/domain/knowledge-preparation.test.d.ts +1 -0
- package/dist/domain/knowledge-search.d.ts +15 -76
- package/dist/domain/mcp-account-bindings.d.ts +17 -0
- package/dist/domain/mcp-account-routes.d.ts +21 -0
- package/dist/domain/native-mcp-connection-admission.d.ts +3 -0
- package/dist/domain/organization-integration-catalog.d.ts +15 -0
- package/dist/domain/personal-connection-delegations.d.ts +73 -12
- package/dist/domain/pr-review.d.ts +121 -6
- package/dist/domain/realtime-voice-billing.d.ts +103 -0
- package/dist/domain/resources.d.ts +17 -1
- package/dist/domain/scheduled-task-access.d.ts +265 -0
- package/dist/domain/scheduled-task-account-audit.d.ts +27 -0
- package/dist/domain/scheduled-task-audience.d.ts +70 -0
- package/dist/domain/scheduled-tasks.d.ts +61 -18
- package/dist/domain/session-connection-accounts.d.ts +53 -0
- package/dist/domain/session-file-access.d.ts +4 -0
- package/dist/domain/session-native-connection-replacement.d.ts +14 -0
- package/dist/domain/session-start-timing.d.ts +7 -0
- package/dist/domain/session-tool-policy.d.ts +102 -4
- package/dist/domain/sessions.d.ts +179 -15
- package/dist/domain/site-audience.d.ts +52 -0
- package/dist/domain/skills.d.ts +29 -1
- package/dist/domain/slack-bot.d.ts +46 -1
- package/dist/domain/slack-publication-secret-safety.d.ts +1 -1
- package/dist/domain/vaults/index.d.ts +7 -0
- package/dist/domain/vaults/mapping.d.ts +25 -0
- package/dist/domain/vaults/provider-registry.d.ts +72 -0
- package/dist/domain/vaults/provider-sync.d.ts +176 -0
- package/dist/domain/vaults/provider-writes.d.ts +57 -0
- package/dist/domain/vaults/secret-resolver.d.ts +34 -0
- package/dist/domain/vaults/types.d.ts +24 -0
- package/dist/domain/vaults/vault-service.d.ts +35 -0
- package/dist/domain/voice-input-billing.d.ts +77 -0
- package/dist/domain/web-search-billing.d.ts +51 -0
- package/dist/domain/workspace-credits.d.ts +7 -0
- package/dist/editable-artifact-live/application.d.ts +2 -1
- package/dist/editable-artifact-live/server.d.ts +5 -1
- package/dist/editable-artifact-live/ticket.d.ts +2 -1
- package/dist/editable-artifact-live/types.d.ts +9 -0
- package/dist/editable-artifact-live.js +2 -2
- package/dist/editable-artifacts.js +10 -2
- package/dist/goal-admission.d.ts +627 -0
- package/dist/host-session-usage-limit.d.ts +15 -0
- package/dist/index.d.ts +43 -8
- package/dist/index.js +22682 -11743
- package/dist/index.js.map +1 -1
- package/dist/managed-session.d.ts +110 -7
- package/dist/model-call.d.ts +58 -0
- package/dist/model-catalog.d.ts +29 -3
- package/dist/model-provider-credentials.d.ts +43 -0
- package/dist/product-usage-metrics.d.ts +30 -0
- package/dist/rigs/index.d.ts +1 -13
- package/dist/sandbox/fleet.d.ts +58 -4
- package/dist/sandbox/routing.d.ts +42 -2
- package/dist/sandbox/runtime-settings.d.ts +27 -17
- package/dist/session-authorization.d.ts +10 -0
- package/dist/session-usage-limit-metrics.d.ts +60 -0
- package/dist/session-usage-limits.d.ts +89 -0
- package/dist/site-agent-clamp.d.ts +71 -0
- package/dist/site-session-origin.d.ts +39 -2
- package/dist/transcription.d.ts +120 -3
- package/dist/turn-surface.d.ts +20 -0
- package/dist/user-presence.d.ts +32 -0
- package/dist/workflow-wake-contract.d.ts +13 -1
- package/package.json +15 -22
- package/src/access/index.ts +961 -38
- package/src/access/service-initiator.ts +50 -0
- package/src/application/api-integration-servers.ts +9 -0
- package/src/application/archived-session-imports.ts +161 -0
- package/src/application/composer-submit.ts +3 -2
- package/src/application/connect-authority.ts +3 -3
- package/src/application/connect-operation.ts +37 -3
- package/src/application/external-continuation.ts +20 -3
- package/src/application/external-link-work-admission.ts +3 -0
- package/src/application/external-workspace-members.ts +51 -3
- package/src/application/modal-native-live-declaration-join.ts +196 -0
- package/src/application/modal-native-original-configuration.ts +261 -0
- package/src/application/modal-native-worker-host-transport.ts +263 -0
- package/src/application/new-session-drafts.ts +172 -55
- package/src/application/organization-privacy.ts +161 -0
- package/src/application/prepared-mcp-connection.ts +133 -0
- package/src/application/sandbox-recovery.ts +78 -0
- package/src/application/session-commands.ts +73 -1
- package/src/application/session-mcp-credential-rotation.ts +61 -18
- package/src/application/session-tenancy.ts +262 -44
- package/src/application/user-resource-grants.ts +1 -29
- package/src/billing/agent-run-admission.ts +117 -0
- package/src/billing/limits.ts +138 -14
- package/src/billing/model-call-admission.ts +118 -0
- package/src/billing/model-usage-settlement.ts +472 -0
- package/src/codex-model-availability.ts +264 -0
- package/src/default-session-model.ts +692 -0
- package/src/dependencies.ts +48 -6
- package/src/developer-setup-credential-ceiling.ts +60 -0
- package/src/domain/agent-config-resolution.ts +148 -0
- package/src/domain/automations.ts +76 -2
- package/src/domain/capabilities.ts +830 -181
- package/src/domain/connection-account-selection-error.ts +19 -0
- package/src/domain/connector-tool-permissions.ts +572 -0
- package/src/domain/direct-model-provider.ts +62 -0
- package/src/domain/editable-artifacts/agent-application.ts +44 -6
- package/src/domain/editable-artifacts/durable-export.ts +100 -5
- package/src/domain/editable-artifacts/in-memory.ts +6 -0
- package/src/domain/editable-artifacts/ports.ts +11 -0
- package/src/domain/editable-artifacts/service.ts +20 -0
- package/src/domain/editable-artifacts/types.ts +1 -1
- package/src/domain/encryption-key.ts +26 -0
- package/src/domain/file-owner.ts +16 -1
- package/src/domain/github-action-policies.ts +1 -1
- package/src/domain/github-skill-source.ts +3 -1
- package/src/domain/insights-timing.ts +95 -0
- package/src/domain/insights-usage.ts +67 -0
- package/src/domain/insights.ts +167 -43
- package/src/domain/knowledge-files.ts +10 -3
- package/src/domain/knowledge-messages.ts +1 -0
- package/src/domain/knowledge-preparation.test.ts +276 -0
- package/src/domain/knowledge-preparation.ts +147 -0
- package/src/domain/knowledge-search.test.ts +617 -5
- package/src/domain/knowledge-search.ts +240 -23
- package/src/domain/mcp-account-bindings.ts +169 -0
- package/src/domain/mcp-account-routes.ts +152 -0
- package/src/domain/native-mcp-connection-admission.ts +14 -0
- package/src/domain/organization-integration-catalog.ts +88 -0
- package/src/domain/personal-connection-delegations.ts +399 -257
- package/src/domain/pr-review.ts +24 -8
- package/src/domain/realtime-voice-billing.ts +425 -0
- package/src/domain/resources.ts +50 -4
- package/src/domain/scheduled-task-access.ts +1089 -0
- package/src/domain/scheduled-task-account-audit.ts +90 -0
- package/src/domain/scheduled-task-audience.ts +249 -0
- package/src/domain/scheduled-tasks.ts +1313 -374
- package/src/domain/session-connection-accounts.ts +239 -0
- package/src/domain/session-file-access.ts +47 -0
- package/src/domain/session-native-connection-replacement.ts +64 -0
- package/src/domain/session-start-timing.ts +60 -0
- package/src/domain/session-tool-policy.ts +489 -14
- package/src/domain/sessions.ts +2254 -717
- package/src/domain/site-audience.ts +130 -0
- package/src/domain/skills.ts +9 -0
- package/src/domain/slack-bot.ts +141 -4
- package/src/domain/slack-publication-secret-safety.ts +1 -1
- package/src/domain/vaults/index.ts +19 -0
- package/src/domain/vaults/mapping.ts +190 -0
- package/src/domain/vaults/provider-registry.ts +201 -0
- package/src/domain/vaults/provider-sync.ts +531 -0
- package/src/domain/vaults/provider-writes.ts +288 -0
- package/src/domain/vaults/secret-resolver.ts +464 -0
- package/src/domain/vaults/types.ts +26 -0
- package/src/domain/vaults/vault-service.ts +1316 -0
- package/src/domain/voice-input-billing.ts +330 -0
- package/src/domain/web-search-billing.ts +169 -0
- package/src/domain/workspace-credits.ts +13 -0
- package/src/editable-artifact-live/application.ts +8 -1
- package/src/editable-artifact-live/server.ts +154 -6
- package/src/editable-artifact-live/ticket.ts +86 -1
- package/src/editable-artifact-live/types.ts +10 -0
- package/src/goal-admission.ts +220 -0
- package/src/host-session-usage-limit.ts +25 -0
- package/src/index.ts +75 -11
- package/src/managed-session.ts +137 -6
- package/src/model-call.ts +79 -0
- package/src/model-catalog.ts +345 -91
- package/src/model-provider-credentials.ts +394 -0
- package/src/product-usage-metrics.ts +101 -0
- package/src/rigs/index.ts +23 -111
- package/src/sandbox/fleet.ts +253 -28
- package/src/sandbox/routing.ts +204 -43
- package/src/sandbox/runtime-settings.ts +159 -85
- package/src/session-authorization.ts +172 -23
- package/src/session-usage-limit-metrics.ts +95 -0
- package/src/session-usage-limits.ts +415 -0
- package/src/site-agent-clamp.ts +226 -0
- package/src/site-session-origin.ts +84 -5
- package/src/transcription.ts +123 -4
- package/src/turn-surface.ts +47 -0
- package/src/user-presence.ts +134 -0
- package/src/workflow-wake-contract.ts +14 -1
- package/dist/application/host-mcp-owner.d.ts +0 -6
- package/dist/application/host-mcp-resolvers.d.ts +0 -13
- package/dist/chunk-5WZYFL45.js +0 -266
- package/dist/chunk-5WZYFL45.js.map +0 -1
- package/dist/chunk-H7X52RI2.js.map +0 -1
- package/dist/chunk-OF65T3PM.js.map +0 -1
- package/dist/chunk-VCZFFEPB.js.map +0 -1
- package/dist/chunk-ZYCNLYGH.js.map +0 -1
- package/dist/domain/connection-authority.d.ts +0 -97
- package/dist/domain/connection-authority.js +0 -169
- package/dist/domain/connection-authority.js.map +0 -1
- package/dist/domain/environments.d.ts +0 -33
- package/dist/domain/host-mcp-authority-source-admission.d.ts +0 -10
- package/dist/domain/host-mcp-task-admission.d.ts +0 -18
- package/dist/domain/packs.d.ts +0 -59
- package/dist/domain/product-integration-pack.d.ts +0 -44
- package/dist/domain/product-integration-skill.gen.d.ts +0 -5
- package/dist/remote-mcp-credentials.d.ts +0 -23
- package/dist/remote-mcp-credentials.js +0 -11
- package/dist/remote-mcp-credentials.js.map +0 -1
- package/src/application/host-mcp-owner.ts +0 -41
- package/src/application/host-mcp-resolvers.ts +0 -84
- package/src/domain/connection-authority.ts +0 -329
- package/src/domain/environments.ts +0 -120
- package/src/domain/host-mcp-authority-source-admission.ts +0 -25
- package/src/domain/host-mcp-task-admission.ts +0 -121
- package/src/domain/packs.ts +0 -699
- package/src/domain/product-integration-pack.ts +0 -42
- package/src/domain/product-integration-skill.gen.ts +0 -52
- package/src/remote-mcp-credentials.ts +0 -361
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { types } from "node:util";
|
|
3
|
+
import type { Settings } from "@opengeni/config";
|
|
4
|
+
import {
|
|
5
|
+
ModalNativeConfigurationCaptureRefV2,
|
|
6
|
+
ModalNativeOriginalScopeV2,
|
|
7
|
+
ModalNativeProofGrantRefV2,
|
|
8
|
+
} from "@opengeni/contracts/modal-native-proof-v2";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
|
|
11
|
+
// Private transport only. This module is not an ordinary access-token entry,
|
|
12
|
+
// native acquisition issuer, custody authorizer, public registrar or effect CAS.
|
|
13
|
+
// A successful MAC authenticates this host transport, NOT the named human,
|
|
14
|
+
// live origin, grant, configuration, provider response or physical settlement.
|
|
15
|
+
const PREFIX = "ogmnp2_";
|
|
16
|
+
const SUBKEY_DOMAIN = "opengeni:modal-native-worker-host:transport:subkey:v2";
|
|
17
|
+
const MESSAGE_DOMAIN = "opengeni:modal-native-worker-host:transport:message:v2\0";
|
|
18
|
+
const TRANSPORT_TTL_SECONDS = 60;
|
|
19
|
+
const MAX_BODY_BYTES = 4 * 1024 * 1024;
|
|
20
|
+
const MAX_ENVELOPE_BYTES = 16_384;
|
|
21
|
+
const typedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype);
|
|
22
|
+
const nativeByteLength = Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteLength")!.get!;
|
|
23
|
+
const nativeByteOffset = Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteOffset")!.get!;
|
|
24
|
+
const nativeBuffer = Object.getOwnPropertyDescriptor(typedArrayPrototype, "buffer")!.get!;
|
|
25
|
+
const uuid = z.string().uuid();
|
|
26
|
+
const revision = z.number().int().positive().safe();
|
|
27
|
+
const base = {
|
|
28
|
+
protocol: z.literal("opengeni-modal-native-worker-host-proof"),
|
|
29
|
+
version: z.literal(2),
|
|
30
|
+
principalKind: z.literal("worker_host"),
|
|
31
|
+
purpose: z.literal("original-native-custody-proof"),
|
|
32
|
+
scope: ModalNativeOriginalScopeV2,
|
|
33
|
+
configurationCaptureRef: ModalNativeConfigurationCaptureRefV2,
|
|
34
|
+
proofGrantRef: ModalNativeProofGrantRefV2,
|
|
35
|
+
requestId: uuid,
|
|
36
|
+
};
|
|
37
|
+
const acquisition = z
|
|
38
|
+
.object({
|
|
39
|
+
claimId: uuid,
|
|
40
|
+
nonce: uuid,
|
|
41
|
+
claimRevision: revision,
|
|
42
|
+
recordRevision: revision,
|
|
43
|
+
captureId: uuid,
|
|
44
|
+
acquisitionId: uuid,
|
|
45
|
+
})
|
|
46
|
+
.strict();
|
|
47
|
+
const intent = z.discriminatedUnion("action", [
|
|
48
|
+
z.object({ ...base, action: z.literal("declare_preparation") }).strict(),
|
|
49
|
+
z
|
|
50
|
+
.object({ ...base, action: z.literal("claim_namespace_read"), recordRevision: revision })
|
|
51
|
+
.strict(),
|
|
52
|
+
z
|
|
53
|
+
.object({ ...base, action: z.literal("claim_original_command_read"), recordRevision: revision })
|
|
54
|
+
.strict(),
|
|
55
|
+
z.object({ ...base, action: z.literal("capture_namespace_binding"), acquisition }).strict(),
|
|
56
|
+
z.object({ ...base, action: z.literal("capture_original_page"), acquisition }).strict(),
|
|
57
|
+
z.object({ ...base, action: z.literal("reconcile_same_capture"), acquisition }).strict(),
|
|
58
|
+
z.object({ ...base, action: z.literal("settle_original_acquisition"), acquisition }).strict(),
|
|
59
|
+
]);
|
|
60
|
+
const timing = z
|
|
61
|
+
.object({
|
|
62
|
+
bodySha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
63
|
+
issuedAt: z.number().int().nonnegative().safe(),
|
|
64
|
+
expiresAt: z.number().int().positive().safe(),
|
|
65
|
+
})
|
|
66
|
+
.strict();
|
|
67
|
+
type Intent = z.infer<typeof intent>;
|
|
68
|
+
export type NativeWorkerHostTransportDescriptionV2 = Intent & z.infer<typeof timing>;
|
|
69
|
+
|
|
70
|
+
declare const transportBrand: unique symbol;
|
|
71
|
+
export type NativeWorkerHostTransport = Readonly<{ [transportBrand]: true }>;
|
|
72
|
+
declare const verifiedBrand: unique symbol;
|
|
73
|
+
export type VerifiedNativeWorkerHostTransport = Readonly<{ [verifiedBrand]: true }>;
|
|
74
|
+
const transports = new WeakMap<NativeWorkerHostTransport, Buffer>();
|
|
75
|
+
const verified = new WeakMap<
|
|
76
|
+
VerifiedNativeWorkerHostTransport,
|
|
77
|
+
{
|
|
78
|
+
transport: NativeWorkerHostTransport;
|
|
79
|
+
description: NativeWorkerHostTransportDescriptionV2;
|
|
80
|
+
}
|
|
81
|
+
>();
|
|
82
|
+
|
|
83
|
+
/** Inspect before schema parsing; getters/Proxies must not execute inside the
|
|
84
|
+
* signing boundary. This is JSON correlation, never an authority constructor. */
|
|
85
|
+
function ownJson(value: unknown, depth = 0): void {
|
|
86
|
+
if (depth > 12) throw new Error("Unsupported native host transport input");
|
|
87
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return;
|
|
88
|
+
if (typeof value === "number" && Number.isFinite(value) && !Object.is(value, -0)) return;
|
|
89
|
+
if (typeof value !== "object" || types.isProxy(value))
|
|
90
|
+
throw new Error("Unsupported native host transport input");
|
|
91
|
+
const array = Array.isArray(value);
|
|
92
|
+
const prototype = Object.getPrototypeOf(value);
|
|
93
|
+
if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null)
|
|
94
|
+
throw new Error("Unsupported native host transport input");
|
|
95
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
96
|
+
const keys = Reflect.ownKeys(descriptors).filter((key) => !(array && key === "length"));
|
|
97
|
+
if (array && keys.length !== (value as unknown[]).length)
|
|
98
|
+
throw new Error("Unsupported native host transport input");
|
|
99
|
+
for (const key of keys) {
|
|
100
|
+
if (typeof key !== "string" || (array && !/^(0|[1-9][0-9]*)$/u.test(key)))
|
|
101
|
+
throw new Error("Unsupported native host transport input");
|
|
102
|
+
const descriptor = descriptors[key]!;
|
|
103
|
+
if (!descriptor.enumerable || !("value" in descriptor))
|
|
104
|
+
throw new Error("Unsupported native host transport input");
|
|
105
|
+
ownJson(descriptor.value, depth + 1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function bodyHash(body: Uint8Array): string {
|
|
110
|
+
if (types.isProxy(body) || !types.isUint8Array(body))
|
|
111
|
+
throw new Error("Unsupported native host transport body");
|
|
112
|
+
// Native getters ignore caller-owned byteLength/buffer/offset overrides. Reject
|
|
113
|
+
// shared backing storage: its bytes could change concurrently during the copy.
|
|
114
|
+
const length = nativeByteLength.call(body) as number;
|
|
115
|
+
const backing = nativeBuffer.call(body) as ArrayBuffer;
|
|
116
|
+
if (length > MAX_BODY_BYTES || !types.isArrayBuffer(backing))
|
|
117
|
+
throw new Error("Unsupported native host transport body");
|
|
118
|
+
const snapshot = new Uint8Array(length);
|
|
119
|
+
try {
|
|
120
|
+
snapshot.set(new Uint8Array(backing, nativeByteOffset.call(body) as number, length));
|
|
121
|
+
return createHash("sha256").update(snapshot).digest("hex");
|
|
122
|
+
} catch {
|
|
123
|
+
throw new Error("Unsupported native host transport body");
|
|
124
|
+
} finally {
|
|
125
|
+
snapshot.fill(0);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function keyFor(transport: NativeWorkerHostTransport): Buffer {
|
|
130
|
+
const key = transports.get(transport);
|
|
131
|
+
if (!key) throw new Error("Native host transport is unavailable");
|
|
132
|
+
return key;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function mac(key: Buffer, encoded: string): Buffer {
|
|
136
|
+
return createHmac("sha256", key).update(MESSAGE_DOMAIN).update(PREFIX).update(encoded).digest();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Host startup only. Deliberately DOES NOT call resolveFirstPartyDelegationSecret:
|
|
140
|
+
* neither configured access-key substitution nor the public local/test fallback
|
|
141
|
+
* can select this new purpose root. Missing explicit deployment secret is OFF.
|
|
142
|
+
* No variable lookup, provider request, config mutation or persistence occurs. */
|
|
143
|
+
export function configuredNativeWorkerHostTransport(
|
|
144
|
+
settings: Pick<Settings, "delegationSecret">,
|
|
145
|
+
): NativeWorkerHostTransport | null {
|
|
146
|
+
if (!settings || typeof settings !== "object" || types.isProxy(settings)) return null;
|
|
147
|
+
const descriptor = Object.getOwnPropertyDescriptor(settings, "delegationSecret");
|
|
148
|
+
if (!descriptor || !("value" in descriptor) || typeof descriptor.value !== "string") return null;
|
|
149
|
+
const secret = descriptor.value.trim();
|
|
150
|
+
if (!secret) return null;
|
|
151
|
+
const transient = Buffer.from(secret, "utf8");
|
|
152
|
+
try {
|
|
153
|
+
const key = createHmac("sha256", transient).update(SUBKEY_DOMAIN).digest();
|
|
154
|
+
const handle = Object.freeze(Object.create(null)) as NativeWorkerHostTransport;
|
|
155
|
+
transports.set(handle, key);
|
|
156
|
+
return handle;
|
|
157
|
+
} finally {
|
|
158
|
+
transient.fill(0);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Release only this transport's derived-key copy, not a config/grant/custody
|
|
163
|
+
* lifecycle or observation slot. Outstanding native I/O needs its own join. */
|
|
164
|
+
export function disposeNativeWorkerHostTransport(transport: NativeWorkerHostTransport): void {
|
|
165
|
+
const key = transports.get(transport);
|
|
166
|
+
if (key) key.fill(0);
|
|
167
|
+
transports.delete(transport);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Private worker/core transport serialization. The caller MUST first establish
|
|
171
|
+
* canonical original-host admission. This helper cannot establish that admission
|
|
172
|
+
* and is intentionally not exported from the core package's public entrypoints. */
|
|
173
|
+
export function signNativeWorkerHostTransport(
|
|
174
|
+
transport: NativeWorkerHostTransport,
|
|
175
|
+
input: Intent,
|
|
176
|
+
body: Uint8Array,
|
|
177
|
+
nowSeconds = Math.floor(Date.now() / 1000),
|
|
178
|
+
): string {
|
|
179
|
+
const key = keyFor(transport);
|
|
180
|
+
ownJson(input);
|
|
181
|
+
const parsed = intent.safeParse(input);
|
|
182
|
+
const clock = z.number().int().nonnegative().safe().safeParse(nowSeconds);
|
|
183
|
+
if (!parsed.success || !clock.success) throw new Error("Unsupported native host transport input");
|
|
184
|
+
const timed = timing.safeParse({
|
|
185
|
+
bodySha256: bodyHash(body),
|
|
186
|
+
issuedAt: clock.data,
|
|
187
|
+
expiresAt: clock.data + TRANSPORT_TTL_SECONDS,
|
|
188
|
+
});
|
|
189
|
+
if (!timed.success) throw new Error("Unsupported native host transport input");
|
|
190
|
+
const description: NativeWorkerHostTransportDescriptionV2 = {
|
|
191
|
+
...parsed.data,
|
|
192
|
+
...timed.data,
|
|
193
|
+
};
|
|
194
|
+
const encoded = Buffer.from(JSON.stringify(description), "utf8").toString("base64url");
|
|
195
|
+
if (PREFIX.length + encoded.length + 1 + 43 > MAX_ENVELOPE_BYTES)
|
|
196
|
+
throw new Error("Unsupported native host transport input");
|
|
197
|
+
return `${PREFIX}${encoded}.${mac(key, encoded).toString("base64url")}`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Only host transport authenticity is verified. A later narrow authorizer must
|
|
201
|
+
* independently source-join original live/maintenance authority, configuration,
|
|
202
|
+
* grant, claim, acquisition, body fields/cursors and exact action-specific CAS. */
|
|
203
|
+
export function verifyNativeWorkerHostTransport(
|
|
204
|
+
transport: NativeWorkerHostTransport,
|
|
205
|
+
envelope: string,
|
|
206
|
+
body: Uint8Array,
|
|
207
|
+
nowSeconds = Math.floor(Date.now() / 1000),
|
|
208
|
+
): VerifiedNativeWorkerHostTransport | null {
|
|
209
|
+
const key = keyFor(transport);
|
|
210
|
+
if (
|
|
211
|
+
typeof envelope !== "string" ||
|
|
212
|
+
envelope.length > MAX_ENVELOPE_BYTES ||
|
|
213
|
+
!envelope.startsWith(PREFIX)
|
|
214
|
+
)
|
|
215
|
+
return null;
|
|
216
|
+
const pieces = envelope.slice(PREFIX.length).split(".");
|
|
217
|
+
if (pieces.length !== 2) return null;
|
|
218
|
+
const [encoded, signature] = pieces as [string, string];
|
|
219
|
+
if (!/^[A-Za-z0-9_-]+$/u.test(encoded) || !/^[A-Za-z0-9_-]{43}$/u.test(signature)) return null;
|
|
220
|
+
const received = Buffer.from(signature, "base64url");
|
|
221
|
+
if (received.length !== 32 || received.toString("base64url") !== signature) return null;
|
|
222
|
+
if (!timingSafeEqual(received, mac(key, encoded))) return null;
|
|
223
|
+
try {
|
|
224
|
+
const decoded = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8")) as Record<
|
|
225
|
+
string,
|
|
226
|
+
unknown
|
|
227
|
+
>;
|
|
228
|
+
const { bodySha256, issuedAt, expiresAt, ...rawIntent } = decoded;
|
|
229
|
+
const description = {
|
|
230
|
+
...intent.parse(rawIntent),
|
|
231
|
+
...timing.parse({ bodySha256, issuedAt, expiresAt }),
|
|
232
|
+
};
|
|
233
|
+
if (
|
|
234
|
+
!Number.isSafeInteger(nowSeconds) ||
|
|
235
|
+
nowSeconds < 0 ||
|
|
236
|
+
description.issuedAt > nowSeconds ||
|
|
237
|
+
description.expiresAt <= nowSeconds ||
|
|
238
|
+
description.expiresAt - description.issuedAt !== TRANSPORT_TTL_SECONDS ||
|
|
239
|
+
description.bodySha256 !== bodyHash(body) ||
|
|
240
|
+
Buffer.from(JSON.stringify(description), "utf8").toString("base64url") !== encoded
|
|
241
|
+
)
|
|
242
|
+
return null;
|
|
243
|
+
const proof = Object.freeze(Object.create(null)) as VerifiedNativeWorkerHostTransport;
|
|
244
|
+
verified.set(proof, { transport, description });
|
|
245
|
+
return proof;
|
|
246
|
+
} catch {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Request-local inspection of a previously verified MAC, NOT current admission.
|
|
252
|
+
* The narrow host authorizer must still recheck expiry and current authority/CAS.
|
|
253
|
+
* Copying a description, token or ref cannot mint grant or native-I/O authority. */
|
|
254
|
+
export function nativeWorkerHostTransportDescription(
|
|
255
|
+
transport: NativeWorkerHostTransport,
|
|
256
|
+
proof: VerifiedNativeWorkerHostTransport,
|
|
257
|
+
): NativeWorkerHostTransportDescriptionV2 {
|
|
258
|
+
keyFor(transport);
|
|
259
|
+
const record = verified.get(proof);
|
|
260
|
+
if (!record || record.transport !== transport)
|
|
261
|
+
throw new Error("Native host transport proof is unavailable");
|
|
262
|
+
return structuredClone(record.description);
|
|
263
|
+
}
|
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
getEnrollment,
|
|
12
12
|
getRig,
|
|
13
13
|
getSandbox,
|
|
14
|
-
getVariableSet,
|
|
15
14
|
NewSessionDraftAccessError,
|
|
15
|
+
newSessionDraftModelProvided,
|
|
16
16
|
newSessionDraftSelectedProjectChannelId,
|
|
17
17
|
newSessionDraftToolsProvided,
|
|
18
18
|
newSessionSelectionHistory,
|
|
@@ -31,12 +31,14 @@ import {
|
|
|
31
31
|
validateGitHubRepositorySelection,
|
|
32
32
|
validateToolRefs,
|
|
33
33
|
} from "../domain/resources";
|
|
34
|
-
import {
|
|
35
|
-
hasPermission,
|
|
36
|
-
externalAttributionForAuthorization,
|
|
37
|
-
type AccessGrantAuthorization,
|
|
38
|
-
} from "../access";
|
|
34
|
+
import { hasVerifiedOwningUserAuthorization, type AccessGrantAuthorization } from "../access";
|
|
39
35
|
import { assertConfiguredModel, assertWorkspaceModelPolicyAllows } from "../domain/sessions";
|
|
36
|
+
import {
|
|
37
|
+
resolveCallerWorkspaceModelSelections,
|
|
38
|
+
resolveDefaultSessionModel,
|
|
39
|
+
workspaceCatalogListsModel,
|
|
40
|
+
} from "../default-session-model";
|
|
41
|
+
import { canonicalizeConfiguredModelId, type Settings } from "@opengeni/config";
|
|
40
42
|
|
|
41
43
|
type NewSessionDraftDependencies = Pick<AppDependencies, "settings" | "db" | "objectStorage">;
|
|
42
44
|
|
|
@@ -58,6 +60,9 @@ function mapNewSessionDraft(
|
|
|
58
60
|
model: row.model,
|
|
59
61
|
reasoningEffort: row.reasoningEffort,
|
|
60
62
|
latencyMode: row.latencyMode,
|
|
63
|
+
...(newSessionDraftModelProvided(row) !== undefined
|
|
64
|
+
? { modelProvided: newSessionDraftModelProvided(row) }
|
|
65
|
+
: {}),
|
|
61
66
|
...(selectedProjectChannelId !== undefined ? { selectedProjectChannelId } : {}),
|
|
62
67
|
options: publicNewSessionDraftOptions(row),
|
|
63
68
|
selectionHistory: newSessionSelectionHistory(row),
|
|
@@ -65,15 +70,108 @@ function mapNewSessionDraft(
|
|
|
65
70
|
});
|
|
66
71
|
}
|
|
67
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Whether a stored draft's model policy was the person's choice. A row written
|
|
75
|
+
* before the marker existed (or by an older client) counts as a choice unless
|
|
76
|
+
* it is exactly the deployment default policy (model, reasoning, standard
|
|
77
|
+
* speed), which is what an untouched composer used to save.
|
|
78
|
+
*/
|
|
79
|
+
export function draftModelProvided(
|
|
80
|
+
settings: Settings,
|
|
81
|
+
draft: Pick<NewSessionDraftValue, "model" | "reasoningEffort" | "latencyMode" | "modelProvided">,
|
|
82
|
+
): boolean {
|
|
83
|
+
if (draft.modelProvided !== undefined) return draft.modelProvided;
|
|
84
|
+
return !(
|
|
85
|
+
canonicalizeConfiguredModelId(settings, draft.model) ===
|
|
86
|
+
canonicalizeConfiguredModelId(settings, settings.openaiModel) &&
|
|
87
|
+
draft.reasoningEffort === settings.openaiReasoningEffort &&
|
|
88
|
+
draft.latencyMode === "standard"
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** The resolved new-chat default for this actor and workspace. */
|
|
93
|
+
async function actorDefaultModel(
|
|
94
|
+
deps: Pick<NewSessionDraftDependencies, "db" | "settings">,
|
|
95
|
+
grant: AccessGrant,
|
|
96
|
+
workspaceId: string,
|
|
97
|
+
) {
|
|
98
|
+
return await resolveDefaultSessionModel(deps.db, deps.settings, {
|
|
99
|
+
accountId: grant.accountId,
|
|
100
|
+
workspaceId,
|
|
101
|
+
subjectId: grant.subjectId,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* False only when this workspace's catalog demonstrably no longer lists the
|
|
107
|
+
* model. A catalog read failure is not proof of removal, so it keeps the
|
|
108
|
+
* saved choice.
|
|
109
|
+
*/
|
|
110
|
+
async function workspaceCatalogMayHaveModel(
|
|
111
|
+
deps: Pick<NewSessionDraftDependencies, "db" | "settings">,
|
|
112
|
+
grant: AccessGrant,
|
|
113
|
+
workspaceId: string,
|
|
114
|
+
modelId: string,
|
|
115
|
+
): Promise<boolean> {
|
|
116
|
+
try {
|
|
117
|
+
const selections = await resolveCallerWorkspaceModelSelections(deps.db, deps.settings, {
|
|
118
|
+
accountId: grant.accountId,
|
|
119
|
+
workspaceId,
|
|
120
|
+
subjectId: grant.subjectId,
|
|
121
|
+
});
|
|
122
|
+
return workspaceCatalogListsModel(
|
|
123
|
+
selections,
|
|
124
|
+
canonicalizeConfiguredModelId(deps.settings, modelId),
|
|
125
|
+
);
|
|
126
|
+
} catch {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
type NewSessionDraftReadOptions = {
|
|
132
|
+
/**
|
|
133
|
+
* Project a draft that follows the default onto today's resolved default.
|
|
134
|
+
* Callers that only reuse a chosen model (Slack defaults) skip the
|
|
135
|
+
* resolution; session creation resolves the default itself.
|
|
136
|
+
*/
|
|
137
|
+
projectDefaultModel?: boolean;
|
|
138
|
+
};
|
|
139
|
+
|
|
68
140
|
async function hydrateNewSessionDraft(
|
|
69
141
|
deps: Pick<NewSessionDraftDependencies, "db" | "settings">,
|
|
70
142
|
grant: AccessGrant,
|
|
71
143
|
workspaceId: string,
|
|
72
144
|
row: Awaited<ReturnType<typeof getNewSessionDraftInTransaction>>,
|
|
145
|
+
readOptions: NewSessionDraftReadOptions = {},
|
|
73
146
|
): Promise<NewSessionDraftValue | null> {
|
|
74
147
|
if (!row) return null;
|
|
75
|
-
const
|
|
76
|
-
if (!
|
|
148
|
+
const stored = mapNewSessionDraft(row);
|
|
149
|
+
if (!stored) return null;
|
|
150
|
+
// A draft that follows the default is projected onto today's default, so a
|
|
151
|
+
// later subscription connect or credit purchase replaces an untouched free
|
|
152
|
+
// model. The stored row is unchanged until the person saves again.
|
|
153
|
+
let modelProvided = draftModelProvided(deps.settings, stored);
|
|
154
|
+
if (
|
|
155
|
+
modelProvided &&
|
|
156
|
+
readOptions.projectDefaultModel !== false &&
|
|
157
|
+
!(await workspaceCatalogMayHaveModel(deps, grant, workspaceId, stored.model))
|
|
158
|
+
) {
|
|
159
|
+
// The saved choice left this workspace's catalog (a removed provider or a
|
|
160
|
+
// retired model). A new chat must not start on a model that cannot run, so
|
|
161
|
+
// the draft follows today's default until the person picks again. A model
|
|
162
|
+
// that is listed but temporarily unavailable keeps the person's choice.
|
|
163
|
+
modelProvided = false;
|
|
164
|
+
}
|
|
165
|
+
let mapped: NewSessionDraftValue = { ...stored, modelProvided };
|
|
166
|
+
if (!modelProvided && readOptions.projectDefaultModel !== false) {
|
|
167
|
+
const resolved = await actorDefaultModel(deps, grant, workspaceId);
|
|
168
|
+
mapped = {
|
|
169
|
+
...mapped,
|
|
170
|
+
model: resolved.model,
|
|
171
|
+
reasoningEffort: resolved.reasoningEffort,
|
|
172
|
+
latencyMode: resolved.model === stored.model ? stored.latencyMode : "standard",
|
|
173
|
+
};
|
|
174
|
+
}
|
|
77
175
|
const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(
|
|
78
176
|
deps.db,
|
|
79
177
|
workspaceId,
|
|
@@ -112,35 +210,11 @@ async function hydrateNewSessionDraft(
|
|
|
112
210
|
}
|
|
113
211
|
|
|
114
212
|
const options = { ...mapped.options };
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
hasPermission(grant.permissions, "variable-sets:attach") &&
|
|
119
|
-
hasPermission(grant.permissions, "variable-sets:use") &&
|
|
120
|
-
(
|
|
121
|
-
await Promise.all(
|
|
122
|
-
variableSetIds.map((variableSetId) =>
|
|
123
|
-
getVariableSet(
|
|
124
|
-
deps.db,
|
|
125
|
-
{ accountId: grant.accountId, workspaceId, subjectId: grant.subjectId },
|
|
126
|
-
variableSetId,
|
|
127
|
-
),
|
|
128
|
-
),
|
|
129
|
-
)
|
|
130
|
-
).every(Boolean);
|
|
131
|
-
if (selectionsAuthorized) {
|
|
132
|
-
options.variableSetIds = variableSetIds;
|
|
133
|
-
options.variableSetId = variableSetIds[variableSetIds.length - 1];
|
|
134
|
-
} else {
|
|
135
|
-
delete options.variableSetIds;
|
|
136
|
-
delete options.variableSetId;
|
|
137
|
-
}
|
|
138
|
-
} else {
|
|
139
|
-
delete options.variableSetIds;
|
|
140
|
-
delete options.variableSetId;
|
|
141
|
-
}
|
|
213
|
+
// Variable Sets were replaced by vaults: stale draft selections are dropped.
|
|
214
|
+
delete options.variableSetIds;
|
|
215
|
+
delete options.variableSetId;
|
|
142
216
|
if (options.rigId) {
|
|
143
|
-
const rig = await getRig(deps.db, workspaceId, options.rigId);
|
|
217
|
+
const rig = await getRig(deps.db, { ...grant, workspaceId }, options.rigId);
|
|
144
218
|
if (!rig?.activeVersion) delete options.rigId;
|
|
145
219
|
}
|
|
146
220
|
if (options.targetSandboxId) {
|
|
@@ -190,6 +264,7 @@ async function getActorNewSessionDraftInFileScope(
|
|
|
190
264
|
deps: Pick<NewSessionDraftDependencies, "settings" | "db">,
|
|
191
265
|
grant: AccessGrant,
|
|
192
266
|
workspaceId: string,
|
|
267
|
+
readOptions: NewSessionDraftReadOptions = {},
|
|
193
268
|
): Promise<NewSessionDraftValue> {
|
|
194
269
|
const row = await withWorkspaceSubjectRls(deps.db, workspaceId, grant.subjectId, (scoped) =>
|
|
195
270
|
getNewSessionDraftInTransaction(scoped, {
|
|
@@ -197,21 +272,26 @@ async function getActorNewSessionDraftInFileScope(
|
|
|
197
272
|
subjectId: grant.subjectId,
|
|
198
273
|
}),
|
|
199
274
|
);
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
275
|
+
const hydrated = await hydrateNewSessionDraft(deps, grant, workspaceId, row, readOptions);
|
|
276
|
+
if (hydrated) return hydrated;
|
|
277
|
+
const resolved =
|
|
278
|
+
readOptions.projectDefaultModel === false
|
|
279
|
+
? { model: deps.settings.openaiModel, reasoningEffort: deps.settings.openaiReasoningEffort }
|
|
280
|
+
: await actorDefaultModel(deps, grant, workspaceId);
|
|
281
|
+
return {
|
|
282
|
+
revision: 0,
|
|
283
|
+
text: "",
|
|
284
|
+
resources: [],
|
|
285
|
+
tools: [],
|
|
286
|
+
toolsProvided: false,
|
|
287
|
+
model: resolved.model,
|
|
288
|
+
reasoningEffort: resolved.reasoningEffort,
|
|
289
|
+
latencyMode: "standard",
|
|
290
|
+
modelProvided: false,
|
|
291
|
+
options: {},
|
|
292
|
+
selectionHistory: { projects: [] },
|
|
293
|
+
updatedAt: null,
|
|
294
|
+
};
|
|
215
295
|
}
|
|
216
296
|
|
|
217
297
|
/**
|
|
@@ -283,6 +363,7 @@ async function saveActorNewSessionDraftInFileScope(
|
|
|
283
363
|
model: input.model,
|
|
284
364
|
reasoningEffort: input.reasoningEffort,
|
|
285
365
|
latencyMode: input.latencyMode,
|
|
366
|
+
...(input.modelProvided !== undefined ? { modelProvided: input.modelProvided } : {}),
|
|
286
367
|
...(input.selectedProjectChannelId !== undefined
|
|
287
368
|
? { selectedProjectChannelId: input.selectedProjectChannelId }
|
|
288
369
|
: {}),
|
|
@@ -295,14 +376,18 @@ async function saveActorNewSessionDraftInFileScope(
|
|
|
295
376
|
// A managed human's own personal workspace has no membership row at
|
|
296
377
|
// all, so the human-removal fence above must fall back to the
|
|
297
378
|
// organization-membership pointer for them — and only for the
|
|
298
|
-
//
|
|
379
|
+
// verified owning-user authorization for this exact grant.
|
|
299
380
|
personalWorkspaceOwnerException:
|
|
300
381
|
canonicalManagedHumanSession ||
|
|
301
|
-
|
|
382
|
+
(externalAuthorization?.grant === grant &&
|
|
383
|
+
hasVerifiedOwningUserAuthorization(externalAuthorization)),
|
|
302
384
|
}),
|
|
303
385
|
),
|
|
304
386
|
);
|
|
305
|
-
|
|
387
|
+
// Report the same model-policy marker a read of this row reports, so the
|
|
388
|
+
// save response and the next GET agree for old and new clients alike.
|
|
389
|
+
const mapped = mapNewSessionDraft(saved)!;
|
|
390
|
+
return { ...mapped, modelProvided: draftModelProvided(deps.settings, mapped) };
|
|
306
391
|
} catch (error) {
|
|
307
392
|
if (error instanceof NewSessionDraftAccessError) {
|
|
308
393
|
throw new HTTPException(403, { message: error.message });
|
|
@@ -316,12 +401,13 @@ export async function getActorNewSessionDraft(
|
|
|
316
401
|
grant: AccessGrant,
|
|
317
402
|
workspaceId: string,
|
|
318
403
|
authorization?: AccessGrantAuthorization,
|
|
404
|
+
readOptions: NewSessionDraftReadOptions = {},
|
|
319
405
|
): Promise<NewSessionDraftValue> {
|
|
320
406
|
const actor = authorization
|
|
321
407
|
? await fileOwnerContextForAccess(deps, authorization, "sessions:read")
|
|
322
408
|
: { subjectId: grant.subjectId, privateFileOwnerSubjectId: null };
|
|
323
409
|
return withSessionRlsActorContext(actor, () =>
|
|
324
|
-
getActorNewSessionDraftInFileScope(deps, grant, workspaceId),
|
|
410
|
+
getActorNewSessionDraftInFileScope(deps, grant, workspaceId, readOptions),
|
|
325
411
|
);
|
|
326
412
|
}
|
|
327
413
|
export async function saveActorNewSessionDraft(
|
|
@@ -333,3 +419,34 @@ export async function saveActorNewSessionDraft(
|
|
|
333
419
|
: { subjectId: grant.subjectId, privateFileOwnerSubjectId: null };
|
|
334
420
|
return withSessionRlsActorContext(actor, () => saveActorNewSessionDraftInFileScope(...args));
|
|
335
421
|
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* The model policy the person explicitly chose in the website composer, or an
|
|
425
|
+
* empty object when their draft follows the default.
|
|
426
|
+
*
|
|
427
|
+
* Only the model policy is reused. Connectors, repositories, Variable Sets,
|
|
428
|
+
* Sandbox Environment and compute target in the draft are the person's last
|
|
429
|
+
* explicit narrowing of one website chat, so surfaces that start work without
|
|
430
|
+
* the composer (Slack) resolve those from workspace defaults instead of
|
|
431
|
+
* silently inheriting that narrowing.
|
|
432
|
+
*/
|
|
433
|
+
export async function getActorNewSessionModelChoice(
|
|
434
|
+
deps: Parameters<typeof getActorNewSessionDraft>[0],
|
|
435
|
+
grant: AccessGrant,
|
|
436
|
+
workspaceId: string,
|
|
437
|
+
): Promise<
|
|
438
|
+
Pick<NewSessionDraftValue, "model" | "reasoningEffort" | "latencyMode"> | Record<string, never>
|
|
439
|
+
> {
|
|
440
|
+
// A draft that follows the default is not projected here; session creation
|
|
441
|
+
// resolves the default itself, once.
|
|
442
|
+
const draft = await getActorNewSessionDraft(deps, grant, workspaceId, undefined, {
|
|
443
|
+
projectDefaultModel: false,
|
|
444
|
+
});
|
|
445
|
+
return draft.revision > 0 && draft.modelProvided === true
|
|
446
|
+
? {
|
|
447
|
+
model: draft.model,
|
|
448
|
+
reasoningEffort: draft.reasoningEffort,
|
|
449
|
+
latencyMode: draft.latencyMode,
|
|
450
|
+
}
|
|
451
|
+
: {};
|
|
452
|
+
}
|