@byok-sdk/keys 0.4.3 → 0.5.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/README.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @byok-sdk/keys
2
2
 
3
+ Pi launcher configuration in the unpublished 0.5.0 candidate is explicit.
4
+ Set `pi_model` on `ProviderRegistry.configure` for profiles used by Pi:
5
+
6
+ ```ts
7
+ const pi_model = {
8
+ contextWindow: 1_000_000,
9
+ maxTokens: 131_072,
10
+ reasoning: true,
11
+ thinkingLevel: 'low',
12
+ thinkingLevelMap: {
13
+ off: null, minimal: null, low: 'low', medium: null,
14
+ high: 'high', xhigh: null, max: 'max',
15
+ },
16
+ compat: {
17
+ supportsStore: false, supportsDeveloperRole: false,
18
+ supportsReasoningEffort: true, supportsUsageInStreaming: true,
19
+ maxTokensField: 'max_tokens', thinkingFormat: 'zai', zaiToolStream: true,
20
+ },
21
+ } satisfies PiModelConfig;
22
+ ```
23
+
24
+ These illustrate declared GLM-5.3-Flash/Pi model settings, not Host input budgets
25
+ or automatic defaults. Import `PiModelConfig` from this package. The bounded
26
+ `PiModelConfigSchema` rejects unknown fields, incomplete level maps and
27
+ unsupported selected levels; it accepts no identity, URL, header or secret.
28
+ Use the exact provider's authoritative configuration. Missing `pi_model`
29
+ permits direct provider transports but rejects Pi admission and launch.
30
+ Configuration changes alter the profile hash and registry revision, fencing
31
+ stale tasks before credential access. The launcher projects the selected
32
+ thinking level through its own argv, not delegated overrides.
33
+
34
+ The SQLite profile schema changes in this candidate. Existing stores are
35
+ rejected and preserved: explicitly provision a separate current-schema store
36
+ after reviewing the old configuration. Do not delete an old store or infer its
37
+ missing model settings. No live-store conversion is performed by the SDK.
38
+
3
39
  Key-based BYOK: a validated provider profile, credential-backed auth headers, and
4
40
  direct transports to OpenAI-compatible and Anthropic providers.
5
41
 
@@ -2,7 +2,7 @@
2
2
  import { spawn } from 'child_process';
3
3
  import { promises, mkdirSync, existsSync, chmodSync } from 'fs';
4
4
  import os from 'os';
5
- import path2, { dirname } from 'path';
5
+ import path2, { isAbsolute, dirname } from 'path';
6
6
  import { z } from 'zod';
7
7
  import { createHash } from 'crypto';
8
8
  import { createRequire } from 'module';
@@ -193,6 +193,54 @@ function isPrivateNetworkLiteral(hostname) {
193
193
  }
194
194
  return parts[0] === 10 || parts[0] === 127 || parts[0] === 169 && parts[1] === 254 || parts[0] === 172 && (parts[1] ?? 0) >= 16 && (parts[1] ?? 0) <= 31 || parts[0] === 192 && parts[1] === 168;
195
195
  }
196
+ var PI_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
197
+ var effort = z.string().min(1).max(64).regex(/^[a-zA-Z0-9_-]+$/u).nullable();
198
+ var PiModelConfigSchema = z.object({
199
+ contextWindow: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
200
+ maxTokens: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
201
+ reasoning: z.boolean(),
202
+ thinkingLevel: z.enum(PI_THINKING_LEVELS),
203
+ thinkingLevelMap: z.object({
204
+ off: effort,
205
+ minimal: effort,
206
+ low: effort,
207
+ medium: effort,
208
+ high: effort,
209
+ xhigh: effort,
210
+ max: effort
211
+ }).strict(),
212
+ compat: z.object({
213
+ supportsStore: z.boolean().optional(),
214
+ supportsDeveloperRole: z.boolean().optional(),
215
+ supportsReasoningEffort: z.boolean().optional(),
216
+ supportsUsageInStreaming: z.boolean().optional(),
217
+ maxTokensField: z.enum(["max_completion_tokens", "max_tokens"]).optional(),
218
+ thinkingFormat: z.enum([
219
+ "openai",
220
+ "openrouter",
221
+ "deepseek",
222
+ "together",
223
+ "baseten",
224
+ "zai",
225
+ "qwen",
226
+ "chat-template",
227
+ "qwen-chat-template",
228
+ "string-thinking",
229
+ "ant-ling"
230
+ ]).optional(),
231
+ zaiToolStream: z.boolean().optional()
232
+ }).strict()
233
+ }).strict().superRefine((config, ctx) => {
234
+ if (config.maxTokens > config.contextWindow) {
235
+ ctx.addIssue({ code: "custom", path: ["maxTokens"], message: "Pi maximum output cannot exceed its context window" });
236
+ }
237
+ if (config.reasoning && config.thinkingLevelMap[config.thinkingLevel] === null) {
238
+ ctx.addIssue({ code: "custom", path: ["thinkingLevel"], message: "Pi thinking level is not supported by the declared model" });
239
+ }
240
+ if (!config.reasoning && config.thinkingLevel !== "off") {
241
+ ctx.addIssue({ code: "custom", path: ["thinkingLevel"], message: "Non-reasoning Pi models require thinking off" });
242
+ }
243
+ });
196
244
 
197
245
  // src/provider-profile.ts
198
246
  var PROVIDER_PROFILE_REF_PATTERN = /^[a-z0-9]+(?:[-_][a-z0-9]+)*$/u;
@@ -251,6 +299,8 @@ var ModelProviderProfileSchema = z.object({
251
299
  enabled: z.boolean(),
252
300
  kind: z.literal("model"),
253
301
  model: boundedString("model", 160),
302
+ // Direct transports do not use Pi; Pi admission requires this explicit configuration.
303
+ pi_model: PiModelConfigSchema.optional(),
254
304
  profile_ref: ProviderProfileRefSchema,
255
305
  provider_kind: z.enum(MODEL_PROVIDER_KINDS),
256
306
  updated_at: isoTimestamp("updated_at")
@@ -309,6 +359,7 @@ function exactProviderProfileBinding(profileInput, requiredCapabilities = profil
309
359
  capabilities: normalizedCapabilities,
310
360
  kind: profile.kind,
311
361
  model: profile.model,
362
+ ...profile.pi_model === void 0 ? {} : { pi_model: profile.pi_model },
312
363
  profile_ref: profile.profile_ref,
313
364
  provider_kind: profile.provider_kind
314
365
  });
@@ -539,13 +590,12 @@ function assertKeychainPath(keychainPath) {
539
590
  }
540
591
  return keychainPath;
541
592
  }
542
-
543
- // src/pi-provider-projection.ts
544
593
  var PI_PROJECTED_KEY_ENV = "PI_PROVIDER_API_KEY";
545
594
  function piProjectionProviderId(profileRef) {
546
595
  return `byok-sdk-${profileRef}`;
547
596
  }
548
597
  function buildPiProviderProjection(profile) {
598
+ const { thinkingLevel: _, ...modelSettings } = requirePiModelConfig(profile);
549
599
  const projectedProviderId = piProjectionProviderId(profile.profile_ref);
550
600
  return {
551
601
  providers: {
@@ -556,6 +606,7 @@ function buildPiProviderProjection(profile) {
556
606
  ...profile.auth_mode === "bearer" ? { authHeader: true } : {},
557
607
  models: [
558
608
  {
609
+ ...modelSettings,
559
610
  id: profile.model,
560
611
  name: profile.display_name,
561
612
  input: [
@@ -569,10 +620,27 @@ function buildPiProviderProjection(profile) {
569
620
  };
570
621
  }
571
622
  function buildPiProviderArgs(profile, delegatedArgs) {
623
+ const config = requirePiModelConfig(profile);
624
+ if (delegatedArgs.length > 128) throw new Error("Pi launcher delegated argument limit exceeded");
572
625
  let modeCount = 0;
626
+ let extensionCount = 0;
627
+ const singleFlags = /* @__PURE__ */ new Set();
573
628
  for (let index = 0; index < delegatedArgs.length; index += 1) {
574
629
  const flag = delegatedArgs[index];
630
+ if (typeof flag !== "string" || /[\u0000\r\n]/u.test(flag)) throw new Error("Pi launcher argument must be single-line");
631
+ if (flag !== "--extension") {
632
+ if (singleFlags.has(flag)) throw new Error(`Pi launcher duplicate argument ${flag}`);
633
+ singleFlags.add(flag);
634
+ }
575
635
  if (flag === "--no-tools") continue;
636
+ if (flag === "--extension") {
637
+ const value = delegatedArgs[++index];
638
+ if (typeof value !== "string" || !isAbsolute(value) || /[\u0000\r\n]/u.test(value)) {
639
+ throw new Error("Pi launcher --extension requires an absolute single-line path");
640
+ }
641
+ if (++extensionCount > 16) throw new Error("Pi launcher extension limit exceeded");
642
+ continue;
643
+ }
576
644
  if (flag === "--mode") {
577
645
  modeCount += 1;
578
646
  const value = delegatedArgs[index + 1];
@@ -582,7 +650,7 @@ function buildPiProviderArgs(profile, delegatedArgs) {
582
650
  }
583
651
  if (flag === "--session" || flag === "--tools" || flag === "--exclude-tools") {
584
652
  const value = delegatedArgs[index + 1];
585
- if (!value || value.startsWith("--")) {
653
+ if (!value || value.startsWith("--") || /[\u0000\r\n]/u.test(value)) {
586
654
  throw new Error(`${flag} requires a value`);
587
655
  }
588
656
  index += 1;
@@ -591,14 +659,23 @@ function buildPiProviderArgs(profile, delegatedArgs) {
591
659
  throw new Error(`Pi launcher does not allow delegated argument ${flag ?? "<missing>"}`);
592
660
  }
593
661
  if (modeCount !== 1) throw new Error("Pi launcher requires exactly one --mode rpc");
662
+ if (singleFlags.has("--no-tools") && singleFlags.has("--tools")) {
663
+ throw new Error("Pi launcher cannot combine --no-tools and --tools");
664
+ }
594
665
  return [
595
666
  ...delegatedArgs,
596
667
  "--provider",
597
668
  piProjectionProviderId(profile.profile_ref),
598
669
  "--model",
599
- profile.model
670
+ profile.model,
671
+ "--thinking",
672
+ config.thinkingLevel
600
673
  ];
601
674
  }
675
+ function requirePiModelConfig(profile) {
676
+ if (profile.pi_model === void 0) throw new Error("Pi execution requires explicit pi_model configuration");
677
+ return PiModelConfigSchema.parse(profile.pi_model);
678
+ }
602
679
 
603
680
  // src/pi-provider-launcher-core.ts
604
681
  var PI_CHILD_BASE_ENV_NAMES = [
@@ -637,6 +714,7 @@ function parsePiProviderLauncherOptions(args) {
637
714
  const piArgs = separator < 0 ? [] : args.slice(separator + 1);
638
715
  const allowedFlags = /* @__PURE__ */ new Set([
639
716
  "--pi-bin",
717
+ "--pi-entry",
640
718
  "--profile-db",
641
719
  "--provider",
642
720
  "--model",
@@ -725,7 +803,12 @@ function parsePiProviderLauncherOptions(args) {
725
803
  requiredCapabilities: parsedCapabilities.data
726
804
  };
727
805
  }
806
+ const piEntry = values.get("--pi-entry");
807
+ if (piEntry !== void 0 && (!path2.isAbsolute(piEntry) || /[\u0000\r\n]/u.test(piEntry))) {
808
+ throw new Error("--pi-entry requires an absolute single-line path");
809
+ }
728
810
  return {
811
+ ...piEntry === void 0 ? {} : { piEntry },
729
812
  piBin: required("--pi-bin"),
730
813
  profileDbPath,
731
814
  profileRef: profileRef.data,
@@ -770,11 +853,18 @@ function buildPiProviderChildEnvironment(options) {
770
853
  const isPrefixed = platform === "win32" ? platformName.startsWith("LC_") || platformName.startsWith("XDG_") : name.startsWith("LC_") || name.startsWith("XDG_");
771
854
  if (isExact || isPrefixed) result[name] = value;
772
855
  }
856
+ const mcpPath = options.ambient.BYOK_PI_MCP_CONFIG_PATH;
857
+ if (mcpPath !== void 0) {
858
+ if (!path2.isAbsolute(mcpPath) || /[\u0000\r\n]/u.test(mcpPath)) throw new Error("BYOK_PI_MCP_CONFIG_PATH must be an absolute single-line path");
859
+ result.BYOK_PI_MCP_CONFIG_PATH = mcpPath;
860
+ }
861
+ const permissionMode = options.ambient.BYOK_PI_PERMISSION_MODE;
862
+ if (permissionMode !== void 0) {
863
+ if (permissionMode !== "auto" && permissionMode !== "readonly") throw new Error("BYOK_PI_PERMISSION_MODE must be auto or readonly");
864
+ result.BYOK_PI_PERMISSION_MODE = permissionMode;
865
+ }
773
866
  result.PI_CODING_AGENT_DIR = options.projectionDir;
774
867
  result.PI_CODING_AGENT_SESSION_DIR = options.sessionDir;
775
- if (options.secret !== void 0) {
776
- result[PI_PROJECTED_KEY_ENV] = options.secret;
777
- }
778
868
  return result;
779
869
  }
780
870
  async function ensurePiSessionDirectory(sessionDir) {
@@ -878,6 +968,7 @@ CREATE TABLE IF NOT EXISTS provider_profile (
878
968
  base_url TEXT NOT NULL,
879
969
  auth_mode TEXT NOT NULL CHECK (auth_mode IN (${sqlList(PROVIDER_AUTH_MODES)})),
880
970
  model TEXT NOT NULL,
971
+ pi_model TEXT,
881
972
  capabilities TEXT NOT NULL,
882
973
  enabled INTEGER NOT NULL CHECK (enabled IN (0, 1)),
883
974
  created_at TEXT NOT NULL,
@@ -896,7 +987,7 @@ function assertProviderProfileSchemaIsCurrent(database, path4) {
896
987
  if (normalizeTableDdl(stored) === normalizeTableDdl(SCHEMA)) return;
897
988
  throw new ByokKeysError(
898
989
  "PROVIDER_STORE_SCHEMA_STALE",
899
- `Provider profile store at ${path4} was created by a different @byok-sdk/keys schema; recreate the store file to continue`
990
+ `Provider profile store at ${path4} was created by a different @byok-sdk/keys schema; preserve this store and explicitly provision a separate current-schema store before continuing`
900
991
  );
901
992
  }
902
993
  var ENABLED_INDEX = `
@@ -978,8 +1069,8 @@ var SqliteProviderProfileStore = class {
978
1069
  this.#database.prepare(
979
1070
  `INSERT INTO provider_profile (
980
1071
  profile_ref, provider_kind, kind, adapter, display_name, base_url,
981
- auth_mode, model, capabilities, enabled, created_at, updated_at
982
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1072
+ auth_mode, model, pi_model, capabilities, enabled, created_at, updated_at
1073
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
983
1074
  ON CONFLICT(profile_ref) DO UPDATE SET
984
1075
  provider_kind = excluded.provider_kind,
985
1076
  adapter = excluded.adapter,
@@ -987,6 +1078,7 @@ var SqliteProviderProfileStore = class {
987
1078
  base_url = excluded.base_url,
988
1079
  auth_mode = excluded.auth_mode,
989
1080
  model = excluded.model,
1081
+ pi_model = excluded.pi_model,
990
1082
  capabilities = excluded.capabilities,
991
1083
  enabled = excluded.enabled,
992
1084
  updated_at = excluded.updated_at`
@@ -999,6 +1091,7 @@ var SqliteProviderProfileStore = class {
999
1091
  validated.base_url,
1000
1092
  validated.auth_mode,
1001
1093
  validated.model,
1094
+ validated.pi_model === void 0 ? null : JSON.stringify(validated.pi_model),
1002
1095
  JSON.stringify(validated.capabilities),
1003
1096
  validated.enabled ? 1 : 0,
1004
1097
  validated.created_at,
@@ -1037,6 +1130,7 @@ function parseRow(row) {
1037
1130
  }
1038
1131
  return parseModelProviderProfile({
1039
1132
  ...row,
1133
+ pi_model: row.pi_model === null ? void 0 : JSON.parse(row.pi_model),
1040
1134
  capabilities,
1041
1135
  enabled: row.enabled === 1
1042
1136
  });
@@ -1357,31 +1451,32 @@ async function run(options) {
1357
1451
  if (options.expectedBinding !== void 0) {
1358
1452
  assertExactProviderProfileBinding(profile, options.expectedBinding);
1359
1453
  }
1454
+ const projection = buildPiProviderProjection(profile);
1360
1455
  if (options.validateOnly) return 0;
1361
- const secret = await resolvePiProviderSecret(
1362
- profile,
1363
- () => createSecretStore(
1364
- options.secretServicePrefix,
1365
- options.macosKeychainPath
1366
- )
1367
- );
1456
+ const childArgs = buildPiProviderArgs(profile, options.piArgs);
1368
1457
  projectionDir = await promises.mkdtemp(path2.join(os.tmpdir(), "byok-pi-provider-"));
1369
1458
  await promises.chmod(projectionDir, 448).catch(() => {
1370
1459
  });
1371
1460
  await ensurePiSessionDirectory(options.sessionDir);
1461
+ const childEnv = buildPiProviderChildEnvironment({
1462
+ ambient: process.env,
1463
+ projectionDir,
1464
+ sessionDir: options.sessionDir,
1465
+ secret: void 0
1466
+ });
1467
+ const secret = await resolvePiProviderSecret(
1468
+ profile,
1469
+ () => createSecretStore(options.secretServicePrefix, options.macosKeychainPath)
1470
+ );
1471
+ if (secret !== void 0) childEnv[PI_PROJECTED_KEY_ENV] = secret;
1372
1472
  await promises.writeFile(
1373
1473
  path2.join(projectionDir, "models.json"),
1374
- `${JSON.stringify(buildPiProviderProjection(profile))}
1474
+ `${JSON.stringify(projection)}
1375
1475
  `,
1376
1476
  { mode: 384 }
1377
1477
  );
1378
- const child = spawn(options.piBin, buildPiProviderArgs(profile, options.piArgs), {
1379
- env: buildPiProviderChildEnvironment({
1380
- ambient: process.env,
1381
- projectionDir,
1382
- sessionDir: options.sessionDir,
1383
- secret
1384
- }),
1478
+ const child = spawn(options.piBin, [...options.piEntry === void 0 ? [] : [options.piEntry], ...childArgs], {
1479
+ env: childEnv,
1385
1480
  stdio: "inherit"
1386
1481
  });
1387
1482
  const forward = (signal) => {