@opengeni/config 0.5.0 → 0.6.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/dist/index.d.ts +47 -9
- package/dist/index.js +138 -23
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +192 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "OpenGeni runtime configuration: settings resolution, deployment knobs, and config validation shared across the server packages.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"prepublishOnly": "bash ../../scripts/prepublish-guard"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@opengeni/codex": "^0.2.
|
|
37
|
-
"@opengeni/contracts": "^0.
|
|
36
|
+
"@opengeni/codex": "^0.2.5",
|
|
37
|
+
"@opengeni/contracts": "^0.15.0",
|
|
38
38
|
"zod": "^4.2.1"
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|
package/src/index.ts
CHANGED
|
@@ -90,7 +90,7 @@ export const DEFAULT_AGENT_INSTRUCTIONS = [
|
|
|
90
90
|
"You are an OpenGeni workspace agent.",
|
|
91
91
|
"Follow the user's task and any enabled pack or skill instructions for the current role.",
|
|
92
92
|
"Work inside the sandbox workspace and use filesystem and shell tools when useful.",
|
|
93
|
-
"Repository resources are mounted under repos/<owner>/<repo
|
|
93
|
+
"Repository resources are mounted under repos/<host>/<owner>/<repo> unless the session specifies another collision-free mount path.",
|
|
94
94
|
"File resources are mounted under files/<file-id>/ unless the session specifies another mount path.",
|
|
95
95
|
"Attached files are mounted read-only; copy them before modifying.",
|
|
96
96
|
"Bundled skills are under .agents/ and can include infrastructure, marketing, or other role-specific guidance.",
|
|
@@ -103,14 +103,59 @@ export const DEFAULT_AGENT_INSTRUCTIONS = [
|
|
|
103
103
|
|
|
104
104
|
export const McpServerConnectionRefSchema = z
|
|
105
105
|
.object({
|
|
106
|
-
|
|
106
|
+
// Standalone ids are UUIDs; embedded hosts may use any stable opaque id.
|
|
107
|
+
connectionId: z.string().min(1).optional(),
|
|
108
|
+
provider: z.string().min(1).max(128).optional(),
|
|
107
109
|
providerDomain: z.string().min(1),
|
|
108
110
|
kind: z.enum(["oauth2", "api_key", "app_install", "delegated"]).optional(),
|
|
109
111
|
scopes: z.array(z.string().min(1)).optional(),
|
|
110
112
|
resource: z.string().min(1).optional(),
|
|
113
|
+
selectedResources: z
|
|
114
|
+
.array(
|
|
115
|
+
z
|
|
116
|
+
.object({
|
|
117
|
+
id: z.string().min(1).max(512),
|
|
118
|
+
kind: z.literal("repository"),
|
|
119
|
+
})
|
|
120
|
+
.strict(),
|
|
121
|
+
)
|
|
122
|
+
.min(1)
|
|
123
|
+
.max(256)
|
|
124
|
+
.superRefine((resources, context) => {
|
|
125
|
+
const seen = new Set<string>();
|
|
126
|
+
for (const [index, resource] of resources.entries()) {
|
|
127
|
+
const key = `${resource.kind}\0${resource.id}`;
|
|
128
|
+
if (seen.has(key)) {
|
|
129
|
+
context.addIssue({
|
|
130
|
+
code: "custom",
|
|
131
|
+
message: "selectedResources must not contain duplicates",
|
|
132
|
+
path: [index],
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
seen.add(key);
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
.optional(),
|
|
111
139
|
subjectScope: z.enum(["workspace", "subject"]).optional(),
|
|
112
140
|
})
|
|
113
|
-
.strict()
|
|
141
|
+
.strict()
|
|
142
|
+
.superRefine((reference, context) => {
|
|
143
|
+
if (!reference.selectedResources) return;
|
|
144
|
+
if (!reference.connectionId) {
|
|
145
|
+
context.addIssue({
|
|
146
|
+
code: "custom",
|
|
147
|
+
message: "selectedResources requires connectionId",
|
|
148
|
+
path: ["connectionId"],
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (!reference.provider) {
|
|
152
|
+
context.addIssue({
|
|
153
|
+
code: "custom",
|
|
154
|
+
message: "selectedResources requires provider",
|
|
155
|
+
path: ["provider"],
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
});
|
|
114
159
|
export type McpServerConnectionRef = z.infer<typeof McpServerConnectionRefSchema>;
|
|
115
160
|
|
|
116
161
|
const SettingsSchema = z.object({
|
|
@@ -125,7 +170,7 @@ const SettingsSchema = z.object({
|
|
|
125
170
|
// topology. Default "" → standalone: no search_path scoping, server default
|
|
126
171
|
// (`public`). When set (e.g. "opengeni"), the db handle + the managed-auth
|
|
127
172
|
// pool send `search_path = "<dbSchema>","opengeni_private","public"` so every
|
|
128
|
-
// query resolves into the dedicated schema with NO query rewrite (
|
|
173
|
+
// query resolves into the dedicated schema with NO query rewrite (schema-isolation contract F1).
|
|
129
174
|
dbSchema: z.string().default(""),
|
|
130
175
|
// Step I (§7.7). RLS posture. "force" (default) = today's FORCE-RLS via the
|
|
131
176
|
// non-owner `opengeni_app` role. "scoped" = the embedded owner-role path (the
|
|
@@ -135,6 +180,12 @@ const SettingsSchema = z.object({
|
|
|
135
180
|
temporalHost: z.string().default("127.0.0.1:7233"),
|
|
136
181
|
temporalNamespace: z.string().default("default"),
|
|
137
182
|
temporalTaskQueue: z.string().default("opengeni-runs-ts"),
|
|
183
|
+
temporalTlsEnabled: EnvBoolean.default(false),
|
|
184
|
+
temporalApiKey: z.string().optional(),
|
|
185
|
+
temporalTlsServerName: z.string().optional(),
|
|
186
|
+
temporalTlsRootCaCertificateBase64: z.string().optional(),
|
|
187
|
+
temporalTlsClientCertificateBase64: z.string().optional(),
|
|
188
|
+
temporalTlsClientPrivateKeyBase64: z.string().optional(),
|
|
138
189
|
startupDependencyRetryAttempts: z.coerce.number().int().positive().default(30),
|
|
139
190
|
startupDependencyRetryInitialDelayMs: z.coerce.number().int().positive().default(1000),
|
|
140
191
|
startupDependencyRetryMaxDelayMs: z.coerce.number().int().positive().default(5000),
|
|
@@ -157,11 +208,11 @@ const SettingsSchema = z.object({
|
|
|
157
208
|
staticEntitlementsJson: z.string().default("{}"),
|
|
158
209
|
staticUsageLimitsJson: z.string().default("{}"),
|
|
159
210
|
delegationSecret: z.string().optional(),
|
|
160
|
-
//
|
|
211
|
+
// sandbox workspace scoped stream-token HMAC secret (sandbox contract §C.3 / stream-token availability contract).
|
|
161
212
|
// When unset, the API falls back to `delegationSecret` (the same HMAC envelope
|
|
162
213
|
// family, `ogs_` vs `ogd_` prefix). REQUIRED-WHEN-DESKTOP, but the absence of
|
|
163
214
|
// BOTH while sandboxDesktopEnabled=true is a GRACEFUL DEGRADE (DesktopStream
|
|
164
|
-
// transport:null + a loud boot warning), NOT a hard boot-fail (
|
|
215
|
+
// transport:null + a loud boot warning), NOT a hard boot-fail (stream-token availability contract).
|
|
165
216
|
streamTokenSecret: z.string().optional(),
|
|
166
217
|
// The desktop input plane (raw stream:control writes) is OFF in v1: even a
|
|
167
218
|
// holder of stream:control gets 403 until this flips. Keeps stream:control a
|
|
@@ -169,6 +220,13 @@ const SettingsSchema = z.object({
|
|
|
169
220
|
streamControlEnabled: EnvBoolean.default(false),
|
|
170
221
|
toolspaceEnabled: EnvBoolean.default(false),
|
|
171
222
|
toolspaceMaxCallsPerTurn: z.coerce.number().int().positive().default(200),
|
|
223
|
+
// Optional release-coherent bootstrap hint for custom rigs/connected machines
|
|
224
|
+
// that do not carry the stock-image ogtool binary. Exact stable versions only:
|
|
225
|
+
// the agent must never guess a tag or silently install `latest`.
|
|
226
|
+
ogtoolPackageSpec: z
|
|
227
|
+
.string()
|
|
228
|
+
.regex(/^@opengeni\/ogtool@(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/u)
|
|
229
|
+
.optional(),
|
|
172
230
|
environmentsEncryptionKey: z.string().optional(),
|
|
173
231
|
integrationsEnabled: EnvBoolean.default(false),
|
|
174
232
|
integrationsStateSecret: z.string().optional(),
|
|
@@ -181,10 +239,6 @@ const SettingsSchema = z.object({
|
|
|
181
239
|
// it then acts as a hard ceiling that per-goal overrides can only lower.
|
|
182
240
|
goalMaxAutoContinuations: z.coerce.number().int().positive().optional(),
|
|
183
241
|
goalNoProgressLimit: z.coerce.number().int().positive().default(3),
|
|
184
|
-
// Temporary safety valve: spawned child sessions still run and retain their
|
|
185
|
-
// own durable events/goals, but terminal child episodes do not create parent
|
|
186
|
-
// system updates or inference work unless explicitly enabled.
|
|
187
|
-
childCompletionParentWakeEnabled: EnvBoolean.default(false),
|
|
188
242
|
// Per-segment ceiling on agent loop turns (model calls) within a single
|
|
189
243
|
// session turn. Effectively unbounded by default for the same reason as
|
|
190
244
|
// above; the graceful max-turns valve (idle + goal continuation, never a
|
|
@@ -220,6 +274,10 @@ const SettingsSchema = z.object({
|
|
|
220
274
|
// Model-catalog auto-compact limit. When present it is clamped to
|
|
221
275
|
// 90% of the raw window, matching Codex core's auto_compact_token_limit().
|
|
222
276
|
contextAutoCompactThresholdTokens: z.coerce.number().int().positive().optional(),
|
|
277
|
+
// Provider-neutral fallback for canonical model-facing tool-result text.
|
|
278
|
+
// The current stable Codex catalog policy is 10k tokens; the truncator adds
|
|
279
|
+
// Codex's 1.2x JSON serialization allowance when applying it.
|
|
280
|
+
modelToolOutputTruncationTokens: z.coerce.number().int().positive().default(10_000),
|
|
223
281
|
authRequired: EnvBoolean.default(false),
|
|
224
282
|
accessKey: z.string().optional(),
|
|
225
283
|
authAllowHealth: EnvBoolean.default(true),
|
|
@@ -253,15 +311,11 @@ const SettingsSchema = z.object({
|
|
|
253
311
|
// tool that BM25-discloses only the matching connectors. Default OFF — a codex
|
|
254
312
|
// turn is byte-for-byte unchanged until enabled. OPENGENI_CODEX_TOOL_SEARCH_ENABLED
|
|
255
313
|
codexToolSearchEnabled: EnvBoolean.default(false),
|
|
256
|
-
//
|
|
314
|
+
// credential allocator atomic, workspace-local credential allocation. Default OFF is a
|
|
257
315
|
// deliberate rolling-deploy fence: migrate + roll every worker first, then
|
|
258
316
|
// enable. Turning it off restores the legacy sticky selector without a schema
|
|
259
317
|
// rollback; the additive lease table/cursor columns become inert.
|
|
260
318
|
codexCredentialLeasingEnabled: EnvBoolean.default(false),
|
|
261
|
-
// Multi-account P3 (auto-rotation): an account is "near exhaustion" — ineligible to be
|
|
262
|
-
// rotated TO — when EITHER usage window (5h/weekly) is at/over this percent. Default 90 to
|
|
263
|
-
// match the UI danger flip (UsageBar danger at pct >= 90). OPENGENI_CODEX_ROTATION_NEAR_EXHAUSTION_PCT.
|
|
264
|
-
codexRotationNearExhaustionPct: z.coerce.number().int().min(1).max(100).default(90),
|
|
265
319
|
openaiReasoningEffort: ReasoningEffort.default("low"),
|
|
266
320
|
openaiAllowedReasoningEfforts: z.string().default("low,medium,high,xhigh"),
|
|
267
321
|
openaiResponsesTransport: z.enum(["http", "websocket"]).default("http"),
|
|
@@ -402,7 +456,7 @@ const SettingsSchema = z.object({
|
|
|
402
456
|
// recordingMaxSeconds is the ffmpeg -t hard ceiling (bounds a multi-day turn).
|
|
403
457
|
recordingEnabled: EnvBoolean.default(true),
|
|
404
458
|
recordingDefaultCodec: z.enum(["h264-mp4", "vp9-webm"]).default("h264-mp4"),
|
|
405
|
-
// Workbench v2 turn-end workspace capture
|
|
459
|
+
// Workbench v2 turn-end workspace capture. When on, the turn
|
|
406
460
|
// activity probes the box's changed files off the live box at turn end and
|
|
407
461
|
// persists a capture revision (blobs in @opengeni/storage) so the workbench
|
|
408
462
|
// paints cold/offline sessions with zero machine round-trips. Best-effort and
|
|
@@ -481,7 +535,7 @@ const SettingsSchema = z.object({
|
|
|
481
535
|
// 404 (invisible — the surface does not exist for this deployment) and the
|
|
482
536
|
// selfhosted backend is inert; boot is unaffected. EnvBoolean (NOT
|
|
483
537
|
// z.coerce.boolean(), which coerces "false" -> true). Flipped per-environment via
|
|
484
|
-
// the deploy-staging IaC secret/configmap pattern
|
|
538
|
+
// the deploy-staging IaC secret/configmap pattern.
|
|
485
539
|
sandboxSelfhostedEnabled: EnvBoolean.default(false),
|
|
486
540
|
// Gates the op-stream (streaming exec) transport to Connected Machines. The
|
|
487
541
|
// runner must ALSO advertise Capabilities.op_stream; default off, and legacy
|
|
@@ -501,7 +555,7 @@ const SettingsSchema = z.object({
|
|
|
501
555
|
selfhostedNatsUrl: z.string().optional(),
|
|
502
556
|
selfhostedRelayUrl: z.string().optional(),
|
|
503
557
|
// The HMAC secret the control plane signs the agent's relay PRODUCER token with
|
|
504
|
-
// (the `ogr_` envelope threaded into EnrollmentCredentials.relayToken; M8b/
|
|
558
|
+
// (the `ogr_` envelope threaded into EnrollmentCredentials.relayToken; M8b/design
|
|
505
559
|
// §10.5). The relay verifies the producer token with the SAME secret. Optional:
|
|
506
560
|
// when ABSENT the poll returns an empty relayToken (graceful degrade — the stream
|
|
507
561
|
// plane is simply unavailable until configured). Falls back to streamTokenSecret /
|
|
@@ -511,7 +565,7 @@ const SettingsSchema = z.object({
|
|
|
511
565
|
// The minisign PUBLIC key the agent pins for self-update verification (handed to
|
|
512
566
|
// the agent in EnrollmentCredentials; the SECRET key lives only in CI).
|
|
513
567
|
agentUpdatePublicKey: z.string().optional(),
|
|
514
|
-
// --- NATS auth-callout tenancy boundary (bring-your-own-compute M-AUTH;
|
|
568
|
+
// --- NATS auth-callout tenancy boundary (bring-your-own-compute M-AUTH; design
|
|
515
569
|
// §10.1 NATS Accounts per workspace + §17 the isolation smoke) -------------
|
|
516
570
|
// nats-server is configured with AUTH CALLOUT: an external agent connects
|
|
517
571
|
// presenting its `oge_` enrollment bearer as the connect auth-token; the server
|
|
@@ -705,6 +759,19 @@ const SettingsSchema = z.object({
|
|
|
705
759
|
|
|
706
760
|
export type Settings = z.infer<typeof SettingsSchema>;
|
|
707
761
|
export type McpServerConfig = Settings["mcpServers"][number];
|
|
762
|
+
export type TemporalTlsConnectionConfig = {
|
|
763
|
+
serverNameOverride?: string;
|
|
764
|
+
serverRootCACertificate?: Uint8Array;
|
|
765
|
+
clientCertPair?: {
|
|
766
|
+
crt: Uint8Array;
|
|
767
|
+
key: Uint8Array;
|
|
768
|
+
};
|
|
769
|
+
};
|
|
770
|
+
export type TemporalConnectionOptions = {
|
|
771
|
+
address: string;
|
|
772
|
+
tls?: true | TemporalTlsConnectionConfig;
|
|
773
|
+
apiKey?: string;
|
|
774
|
+
};
|
|
708
775
|
export type ModelPricing = {
|
|
709
776
|
inputMicrosPerMillionTokens: number;
|
|
710
777
|
cachedInputMicrosPerMillionTokens?: number | undefined;
|
|
@@ -754,6 +821,9 @@ const RegistryModelSchema = z.object({
|
|
|
754
821
|
contextWindowTokens: z.number().int().positive().optional(),
|
|
755
822
|
effectiveContextWindowTokens: z.number().int().positive().optional(),
|
|
756
823
|
autoCompactTokenLimit: z.number().int().positive().optional(),
|
|
824
|
+
// Canonical model-facing function/tool-result policy. The runtime applies
|
|
825
|
+
// the same 1.2x serialization allowance as Codex when materializing output.
|
|
826
|
+
toolOutputTruncationTokens: z.number().int().positive().optional(),
|
|
757
827
|
reasoningEffort: z.boolean().optional(), // model accepts a reasoning-effort control
|
|
758
828
|
hostedWebSearch: z.boolean().optional(), // provider executes the hosted web_search tool for this model
|
|
759
829
|
pricing: ModelPricingSchema.optional(),
|
|
@@ -812,6 +882,7 @@ export interface ConfiguredModel {
|
|
|
812
882
|
contextWindowTokens?: number | undefined;
|
|
813
883
|
effectiveContextWindowTokens?: number | undefined;
|
|
814
884
|
autoCompactTokenLimit?: number | undefined;
|
|
885
|
+
toolOutputTruncationTokens?: number | undefined;
|
|
815
886
|
reasoningEffort: boolean;
|
|
816
887
|
hostedWebSearch: boolean;
|
|
817
888
|
}
|
|
@@ -975,6 +1046,14 @@ export function getSettings(): Settings {
|
|
|
975
1046
|
temporalHost: optional("OPENGENI_TEMPORAL_HOST"),
|
|
976
1047
|
temporalNamespace: optional("OPENGENI_TEMPORAL_NAMESPACE"),
|
|
977
1048
|
temporalTaskQueue: optional("OPENGENI_TEMPORAL_TASK_QUEUE"),
|
|
1049
|
+
temporalTlsEnabled: optional("OPENGENI_TEMPORAL_TLS_ENABLED"),
|
|
1050
|
+
temporalApiKey: optional("OPENGENI_TEMPORAL_API_KEY"),
|
|
1051
|
+
temporalTlsServerName: optional("OPENGENI_TEMPORAL_TLS_SERVER_NAME"),
|
|
1052
|
+
temporalTlsRootCaCertificateBase64: optional(
|
|
1053
|
+
"OPENGENI_TEMPORAL_TLS_ROOT_CA_CERTIFICATE_BASE64",
|
|
1054
|
+
),
|
|
1055
|
+
temporalTlsClientCertificateBase64: optional("OPENGENI_TEMPORAL_TLS_CLIENT_CERTIFICATE_BASE64"),
|
|
1056
|
+
temporalTlsClientPrivateKeyBase64: optional("OPENGENI_TEMPORAL_TLS_CLIENT_PRIVATE_KEY_BASE64"),
|
|
978
1057
|
startupDependencyRetryAttempts: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_ATTEMPTS"),
|
|
979
1058
|
startupDependencyRetryInitialDelayMs: optional(
|
|
980
1059
|
"OPENGENI_STARTUP_DEPENDENCY_RETRY_INITIAL_DELAY_MS",
|
|
@@ -999,6 +1078,7 @@ export function getSettings(): Settings {
|
|
|
999
1078
|
streamControlEnabled: optional("OPENGENI_STREAM_CONTROL_ENABLED"),
|
|
1000
1079
|
toolspaceEnabled: optional("OPENGENI_TOOLSPACE_ENABLED"),
|
|
1001
1080
|
toolspaceMaxCallsPerTurn: optional("OPENGENI_TOOLSPACE_MAX_CALLS_PER_TURN"),
|
|
1081
|
+
ogtoolPackageSpec: optional("OPENGENI_OGTOOL_PACKAGE_SPEC"),
|
|
1002
1082
|
environmentsEncryptionKey: optional("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"),
|
|
1003
1083
|
integrationsEnabled: optional("OPENGENI_INTEGRATIONS_ENABLED"),
|
|
1004
1084
|
integrationsStateSecret: optional("OPENGENI_INTEGRATIONS_STATE_SECRET"),
|
|
@@ -1008,13 +1088,13 @@ export function getSettings(): Settings {
|
|
|
1008
1088
|
integrationsOauthClientsJson: optional("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON"),
|
|
1009
1089
|
goalMaxAutoContinuations: optional("OPENGENI_GOAL_MAX_AUTO_CONTINUATIONS"),
|
|
1010
1090
|
goalNoProgressLimit: optional("OPENGENI_GOAL_NO_PROGRESS_LIMIT"),
|
|
1011
|
-
childCompletionParentWakeEnabled: optional("OPENGENI_CHILD_COMPLETION_PARENT_WAKE_ENABLED"),
|
|
1012
1091
|
agentMaxModelCallsPerTurn: optional("OPENGENI_AGENT_MAX_MODEL_CALLS_PER_TURN"),
|
|
1013
1092
|
contextWindowTokens: optional("OPENGENI_CONTEXT_WINDOW_TOKENS"),
|
|
1014
1093
|
contextEffectiveWindowTokens: optional("OPENGENI_CONTEXT_EFFECTIVE_WINDOW_TOKENS"),
|
|
1015
1094
|
contextCompactionThresholdRatio: optional("OPENGENI_COMPACTION_THRESHOLD_RATIO"),
|
|
1016
1095
|
contextReservedOutputTokens: optional("OPENGENI_CONTEXT_RESERVED_OUTPUT_TOKENS"),
|
|
1017
1096
|
contextAutoCompactThresholdTokens: optional("OPENGENI_CONTEXT_AUTO_COMPACT_THRESHOLD_TOKENS"),
|
|
1097
|
+
modelToolOutputTruncationTokens: optional("OPENGENI_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS"),
|
|
1018
1098
|
authRequired: optional("OPENGENI_AUTH_REQUIRED"),
|
|
1019
1099
|
accessKey: optional("OPENGENI_ACCESS_KEY"),
|
|
1020
1100
|
authAllowHealth: optional("OPENGENI_AUTH_ALLOW_HEALTH"),
|
|
@@ -1035,7 +1115,6 @@ export function getSettings(): Settings {
|
|
|
1035
1115
|
codexToolSearchEnabled: optional("OPENGENI_CODEX_TOOL_SEARCH_ENABLED"),
|
|
1036
1116
|
codexCredentialLeasingEnabled: optional("OPENGENI_CODEX_CREDENTIAL_LEASING_ENABLED"),
|
|
1037
1117
|
codexProductSku: optional("OPENGENI_CODEX_PRODUCT_SKU"),
|
|
1038
|
-
codexRotationNearExhaustionPct: optional("OPENGENI_CODEX_ROTATION_NEAR_EXHAUSTION_PCT"),
|
|
1039
1118
|
openaiReasoningEffort: optional("OPENGENI_OPENAI_REASONING_EFFORT"),
|
|
1040
1119
|
openaiAllowedReasoningEfforts: optional("OPENGENI_OPENAI_ALLOWED_REASONING_EFFORTS"),
|
|
1041
1120
|
openaiResponsesTransport: optional("OPENGENI_OPENAI_RESPONSES_TRANSPORT"),
|
|
@@ -1373,6 +1452,7 @@ export function configuredModels(settings: Settings): ConfiguredModel[] {
|
|
|
1373
1452
|
providerLabel: builtinLabel,
|
|
1374
1453
|
api: "responses" as const,
|
|
1375
1454
|
contextWindowTokens: settings.contextWindowTokens,
|
|
1455
|
+
toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens,
|
|
1376
1456
|
reasoningEffort: true,
|
|
1377
1457
|
hostedWebSearch: settings.webSearchEnabled,
|
|
1378
1458
|
}));
|
|
@@ -1394,6 +1474,9 @@ export function configuredModels(settings: Settings): ConfiguredModel[] {
|
|
|
1394
1474
|
...(model.autoCompactTokenLimit === undefined
|
|
1395
1475
|
? {}
|
|
1396
1476
|
: { autoCompactTokenLimit: model.autoCompactTokenLimit }),
|
|
1477
|
+
...(model.toolOutputTruncationTokens === undefined
|
|
1478
|
+
? {}
|
|
1479
|
+
: { toolOutputTruncationTokens: model.toolOutputTruncationTokens }),
|
|
1397
1480
|
reasoningEffort: model.reasoningEffort ?? false,
|
|
1398
1481
|
hostedWebSearch: model.hostedWebSearch ?? false,
|
|
1399
1482
|
});
|
|
@@ -1490,7 +1573,10 @@ export function settingsWithResolvedModelContext(
|
|
|
1490
1573
|
settings: Settings,
|
|
1491
1574
|
model: Pick<
|
|
1492
1575
|
ConfiguredModel,
|
|
1493
|
-
|
|
1576
|
+
| "contextWindowTokens"
|
|
1577
|
+
| "effectiveContextWindowTokens"
|
|
1578
|
+
| "autoCompactTokenLimit"
|
|
1579
|
+
| "toolOutputTruncationTokens"
|
|
1494
1580
|
>,
|
|
1495
1581
|
): Settings {
|
|
1496
1582
|
const contextWindowTokens = model.contextWindowTokens ?? settings.contextWindowTokens;
|
|
@@ -1508,6 +1594,9 @@ export function settingsWithResolvedModelContext(
|
|
|
1508
1594
|
...(model.autoCompactTokenLimit === undefined
|
|
1509
1595
|
? {}
|
|
1510
1596
|
: { contextAutoCompactThresholdTokens: model.autoCompactTokenLimit }),
|
|
1597
|
+
...(model.toolOutputTruncationTokens === undefined
|
|
1598
|
+
? {}
|
|
1599
|
+
: { modelToolOutputTruncationTokens: model.toolOutputTruncationTokens }),
|
|
1511
1600
|
};
|
|
1512
1601
|
}
|
|
1513
1602
|
|
|
@@ -1578,6 +1667,77 @@ export function environmentsEncryptionKeyBytes(settings: Settings): Uint8Array |
|
|
|
1578
1667
|
return new Uint8Array(decoded);
|
|
1579
1668
|
}
|
|
1580
1669
|
|
|
1670
|
+
/**
|
|
1671
|
+
* Build one structurally compatible connection policy for both
|
|
1672
|
+
* `@temporalio/client` and `@temporalio/worker`. An API key or any custom TLS
|
|
1673
|
+
* material enables TLS automatically; the explicit flag covers server-auth TLS
|
|
1674
|
+
* without credentials. Secret values are never included in validation errors.
|
|
1675
|
+
*/
|
|
1676
|
+
export function temporalConnectionOptions(settings: Settings): TemporalConnectionOptions {
|
|
1677
|
+
const apiKey = settings.temporalApiKey?.trim() || undefined;
|
|
1678
|
+
const serverNameOverride = settings.temporalTlsServerName?.trim() || undefined;
|
|
1679
|
+
const rootCa = decodeTemporalTlsMaterial(
|
|
1680
|
+
settings.temporalTlsRootCaCertificateBase64,
|
|
1681
|
+
"OPENGENI_TEMPORAL_TLS_ROOT_CA_CERTIFICATE_BASE64",
|
|
1682
|
+
);
|
|
1683
|
+
const clientCertificate = decodeTemporalTlsMaterial(
|
|
1684
|
+
settings.temporalTlsClientCertificateBase64,
|
|
1685
|
+
"OPENGENI_TEMPORAL_TLS_CLIENT_CERTIFICATE_BASE64",
|
|
1686
|
+
);
|
|
1687
|
+
const clientPrivateKey = decodeTemporalTlsMaterial(
|
|
1688
|
+
settings.temporalTlsClientPrivateKeyBase64,
|
|
1689
|
+
"OPENGENI_TEMPORAL_TLS_CLIENT_PRIVATE_KEY_BASE64",
|
|
1690
|
+
);
|
|
1691
|
+
|
|
1692
|
+
if (Boolean(clientCertificate) !== Boolean(clientPrivateKey)) {
|
|
1693
|
+
throw new Error(
|
|
1694
|
+
"OPENGENI_TEMPORAL_TLS_CLIENT_CERTIFICATE_BASE64 and " +
|
|
1695
|
+
"OPENGENI_TEMPORAL_TLS_CLIENT_PRIVATE_KEY_BASE64 must both be set or both omitted",
|
|
1696
|
+
);
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
const tls: TemporalTlsConnectionConfig = {};
|
|
1700
|
+
if (serverNameOverride) {
|
|
1701
|
+
tls.serverNameOverride = serverNameOverride;
|
|
1702
|
+
}
|
|
1703
|
+
if (rootCa) {
|
|
1704
|
+
tls.serverRootCACertificate = rootCa;
|
|
1705
|
+
}
|
|
1706
|
+
if (clientCertificate && clientPrivateKey) {
|
|
1707
|
+
tls.clientCertPair = { crt: clientCertificate, key: clientPrivateKey };
|
|
1708
|
+
}
|
|
1709
|
+
const hasCustomTls = Object.keys(tls).length > 0;
|
|
1710
|
+
const tlsEnabled = settings.temporalTlsEnabled || Boolean(apiKey) || hasCustomTls;
|
|
1711
|
+
|
|
1712
|
+
return {
|
|
1713
|
+
address: settings.temporalHost,
|
|
1714
|
+
...(tlsEnabled ? { tls: hasCustomTls ? tls : true } : {}),
|
|
1715
|
+
...(apiKey ? { apiKey } : {}),
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
function decodeTemporalTlsMaterial(
|
|
1720
|
+
value: string | undefined,
|
|
1721
|
+
settingName: string,
|
|
1722
|
+
): Uint8Array | undefined {
|
|
1723
|
+
// RFC 2045 base64 commonly arrives wrapped at 76 columns. Kubernetes
|
|
1724
|
+
// stringData and external secret stores preserve those line breaks, so
|
|
1725
|
+
// normalize whitespace before applying the strict alphabet/canonical check.
|
|
1726
|
+
const encoded = value?.replace(/\s/g, "");
|
|
1727
|
+
if (!encoded) {
|
|
1728
|
+
return undefined;
|
|
1729
|
+
}
|
|
1730
|
+
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(encoded) || encoded.length % 4 === 1) {
|
|
1731
|
+
throw new Error(`${settingName} must contain valid base64`);
|
|
1732
|
+
}
|
|
1733
|
+
const decoded = Buffer.from(encoded, "base64");
|
|
1734
|
+
const canonical = decoded.toString("base64").replace(/=+$/, "");
|
|
1735
|
+
if (decoded.length === 0 || canonical !== encoded.replace(/=+$/, "")) {
|
|
1736
|
+
throw new Error(`${settingName} must contain valid base64`);
|
|
1737
|
+
}
|
|
1738
|
+
return new Uint8Array(decoded);
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1581
1741
|
/**
|
|
1582
1742
|
* The connection `search_path` for OpenGeni's db handles + the managed-auth pool
|
|
1583
1743
|
* (Step I, §7.8 runtime half). Returns `undefined` when `dbSchema` is unset
|
|
@@ -1585,7 +1745,7 @@ export function environmentsEncryptionKeyBytes(settings: Settings): Uint8Array |
|
|
|
1585
1745
|
* default (`public`) applies — byte-for-byte today's behavior. When `dbSchema`
|
|
1586
1746
|
* is set (embedded), returns `"<schema>,opengeni_private,public"` — `public`
|
|
1587
1747
|
* stays LAST so `gen_random_uuid()` (pgcrypto) and the `vector` type still
|
|
1588
|
-
* resolve (the
|
|
1748
|
+
* resolve (the schema-isolation contract live footgun). `opengeni_private` is on the path so the
|
|
1589
1749
|
* RLS GUC-reader helpers resolve when referenced unqualified.
|
|
1590
1750
|
*/
|
|
1591
1751
|
export function dbSearchPath(settings: Pick<Settings, "dbSchema">): string | undefined {
|
|
@@ -1688,6 +1848,9 @@ export function stableSandboxEnvironmentForRun(
|
|
|
1688
1848
|
}
|
|
1689
1849
|
if (settings.toolspaceEnabled) {
|
|
1690
1850
|
environment.OPENGENI_TOOLSPACE_TOKEN_FILE ??= `${environment.HOME ?? descriptor.workspaceRoot}/.opengeni/toolspace-token`;
|
|
1851
|
+
if (settings.ogtoolPackageSpec) {
|
|
1852
|
+
environment.OPENGENI_OGTOOL_PACKAGE_SPEC ??= settings.ogtoolPackageSpec;
|
|
1853
|
+
}
|
|
1691
1854
|
if (options.workspaceId) {
|
|
1692
1855
|
environment.OPENGENI_TOOLSPACE_URL ??= firstPartyMcpWorkspaceUrl(
|
|
1693
1856
|
settings,
|
|
@@ -2201,6 +2364,7 @@ function firstPartyDocumentsMcpServerUrl(mcpUrl: string): string {
|
|
|
2201
2364
|
}
|
|
2202
2365
|
|
|
2203
2366
|
function validateSettings(settings: Settings): void {
|
|
2367
|
+
temporalConnectionOptions(settings);
|
|
2204
2368
|
if (settings.toolspaceEnabled && !settings.delegationSecret) {
|
|
2205
2369
|
throw new Error("OPENGENI_DELEGATION_SECRET is required when OPENGENI_TOOLSPACE_ENABLED=true");
|
|
2206
2370
|
}
|
|
@@ -2512,7 +2676,7 @@ function validateSettings(settings: Settings): void {
|
|
|
2512
2676
|
);
|
|
2513
2677
|
}
|
|
2514
2678
|
}
|
|
2515
|
-
// --- stream-token secret: required-when-desktop, but GRACEFULLY DEGRADE (
|
|
2679
|
+
// --- stream-token secret: required-when-desktop, but GRACEFULLY DEGRADE (stream-token availability contract) ---
|
|
2516
2680
|
// The desktop pixel plane needs an HMAC secret to mint scoped stream tokens.
|
|
2517
2681
|
// It is REQUIRED when desktop is enabled — but per OD-8 a missing secret is NOT
|
|
2518
2682
|
// a hard boot-fail: we emit a LOUD warning and the deployment ships with
|
|
@@ -2559,7 +2723,7 @@ function validateSettings(settings: Settings): void {
|
|
|
2559
2723
|
}
|
|
2560
2724
|
|
|
2561
2725
|
/**
|
|
2562
|
-
* Resolve the secret used to sign/verify scoped stream tokens (
|
|
2726
|
+
* Resolve the secret used to sign/verify scoped stream tokens (sandbox contract
|
|
2563
2727
|
* §C.3). Falls back to `delegationSecret` (the same HMAC envelope family —
|
|
2564
2728
|
* `ogs_` vs `ogd_` prefix) so a deployment that already carries a delegation
|
|
2565
2729
|
* secret does not need a second one. Returns undefined when neither is set,
|
|
@@ -2576,7 +2740,7 @@ export function resolveStreamTokenSecret(settings: Settings): string | undefined
|
|
|
2576
2740
|
|
|
2577
2741
|
/**
|
|
2578
2742
|
* True iff the desktop pixel plane must GRACEFULLY DEGRADE because desktop is
|
|
2579
|
-
* enabled but no stream-token secret is resolvable (
|
|
2743
|
+
* enabled but no stream-token secret is resolvable (stream-token availability contract). When true,
|
|
2580
2744
|
* negotiateCapabilities forces DesktopStream.transport:null.
|
|
2581
2745
|
*/
|
|
2582
2746
|
export function streamTokenDegraded(settings: Settings): boolean {
|
|
@@ -2585,7 +2749,7 @@ export function streamTokenDegraded(settings: Settings): boolean {
|
|
|
2585
2749
|
|
|
2586
2750
|
/**
|
|
2587
2751
|
* Resolve the secret the control plane signs the enrollment bearer credential
|
|
2588
|
-
* with (the `oge_` envelope the agent presents back — M5
|
|
2752
|
+
* with (the `oge_` envelope the agent presents back — M5). Falls
|
|
2589
2753
|
* back to `delegationSecret` (the same HMAC envelope family) so a deployment that
|
|
2590
2754
|
* already carries a delegation secret needs no second one. Returns undefined when
|
|
2591
2755
|
* neither is set; when selfhosted is enabled but this is undefined, the poll route
|
|
@@ -2603,7 +2767,7 @@ export function resolveEnrollmentSigningSecret(settings: Settings): string | und
|
|
|
2603
2767
|
|
|
2604
2768
|
/**
|
|
2605
2769
|
* Resolve the HMAC secret the control plane signs the agent's relay PRODUCER token
|
|
2606
|
-
* with (the `ogr_` envelope; M8b
|
|
2770
|
+
* with (the `ogr_` envelope; M8b). The RELAY verifies the producer
|
|
2607
2771
|
* token with the SAME secret (injected into the relay via env). Prefers an explicit
|
|
2608
2772
|
* `selfhostedRelayTokenSecret`, then the `streamTokenSecret` (the relay already
|
|
2609
2773
|
* needs that one to verify the viewer's `ogs_` token, so a single secret can back
|