@byok-sdk/keys 0.4.3 → 0.6.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 is explicit, from the published 0.5.0 onward.
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
 
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
+ import path2, { dirname, isAbsolute } from 'path';
2
3
  import { spawn } from 'child_process';
3
- import { promises, mkdirSync, existsSync, chmodSync } from 'fs';
4
- import os from 'os';
5
- import path2, { dirname } from 'path';
6
4
  import { z } from 'zod';
7
5
  import { createHash } from 'crypto';
6
+ import { promises, mkdirSync, existsSync, chmodSync } from 'fs';
7
+ import { parseImplementationSpawnBinding, assertImplementationSpawnBinding, projectKeysPiInheritedEnvironment } from '@byok-sdk/implementation-identity';
8
8
  import { createRequire } from 'module';
9
9
 
10
10
  // src/errors.ts
@@ -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,13 @@ 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";
594
+ var PI_LAUNCHER_RUNTIME_ENTRIES = ["pi-rpc", "pi-prepared"];
545
595
  function piProjectionProviderId(profileRef) {
546
596
  return `byok-sdk-${profileRef}`;
547
597
  }
548
598
  function buildPiProviderProjection(profile) {
599
+ const { thinkingLevel: _, ...modelSettings } = requirePiModelConfig(profile);
549
600
  const projectedProviderId = piProjectionProviderId(profile.profile_ref);
550
601
  return {
551
602
  providers: {
@@ -556,6 +607,7 @@ function buildPiProviderProjection(profile) {
556
607
  ...profile.auth_mode === "bearer" ? { authHeader: true } : {},
557
608
  models: [
558
609
  {
610
+ ...modelSettings,
559
611
  id: profile.model,
560
612
  name: profile.display_name,
561
613
  input: [
@@ -569,10 +621,34 @@ function buildPiProviderProjection(profile) {
569
621
  };
570
622
  }
571
623
  function buildPiProviderArgs(profile, delegatedArgs) {
624
+ const config = requirePiModelConfig(profile);
625
+ if (delegatedArgs.length > 128) throw new Error("Pi launcher delegated argument limit exceeded");
572
626
  let modeCount = 0;
627
+ let extensionCount = 0;
628
+ const singleFlags = /* @__PURE__ */ new Set();
573
629
  for (let index = 0; index < delegatedArgs.length; index += 1) {
574
630
  const flag = delegatedArgs[index];
575
- if (flag === "--no-tools") continue;
631
+ if (typeof flag !== "string" || /[\u0000\r\n]/u.test(flag)) throw new Error("Pi launcher argument must be single-line");
632
+ if (flag !== "--extension") {
633
+ if (singleFlags.has(flag)) throw new Error(`Pi launcher duplicate argument ${flag}`);
634
+ singleFlags.add(flag);
635
+ }
636
+ if (flag === "--no-tools" || flag === "--no-skills" || flag === "--no-extensions") continue;
637
+ if (flag === "--config") {
638
+ const value = delegatedArgs[++index];
639
+ if (typeof value !== "string" || !isAbsolute(value) || /[\u0000\r\n]/u.test(value)) {
640
+ throw new Error("Pi launcher --config requires an absolute single-line path");
641
+ }
642
+ continue;
643
+ }
644
+ if (flag === "--extension") {
645
+ const value = delegatedArgs[++index];
646
+ if (typeof value !== "string" || !isAbsolute(value) || /[\u0000\r\n]/u.test(value)) {
647
+ throw new Error("Pi launcher --extension requires an absolute single-line path");
648
+ }
649
+ if (++extensionCount > 16) throw new Error("Pi launcher extension limit exceeded");
650
+ continue;
651
+ }
576
652
  if (flag === "--mode") {
577
653
  modeCount += 1;
578
654
  const value = delegatedArgs[index + 1];
@@ -582,7 +658,7 @@ function buildPiProviderArgs(profile, delegatedArgs) {
582
658
  }
583
659
  if (flag === "--session" || flag === "--tools" || flag === "--exclude-tools") {
584
660
  const value = delegatedArgs[index + 1];
585
- if (!value || value.startsWith("--")) {
661
+ if (!value || value.startsWith("--") || /[\u0000\r\n]/u.test(value)) {
586
662
  throw new Error(`${flag} requires a value`);
587
663
  }
588
664
  index += 1;
@@ -591,52 +667,47 @@ function buildPiProviderArgs(profile, delegatedArgs) {
591
667
  throw new Error(`Pi launcher does not allow delegated argument ${flag ?? "<missing>"}`);
592
668
  }
593
669
  if (modeCount !== 1) throw new Error("Pi launcher requires exactly one --mode rpc");
670
+ if (singleFlags.has("--no-tools") && singleFlags.has("--tools")) {
671
+ throw new Error("Pi launcher cannot combine --no-tools and --tools");
672
+ }
594
673
  return [
595
674
  ...delegatedArgs,
596
675
  "--provider",
597
676
  piProjectionProviderId(profile.profile_ref),
598
677
  "--model",
599
- profile.model
678
+ profile.model,
679
+ "--thinking",
680
+ config.thinkingLevel
600
681
  ];
601
682
  }
683
+ function buildPiPreparedArgs(delegatedArgs) {
684
+ if (delegatedArgs.length !== 2 || delegatedArgs[0] !== "--config") {
685
+ throw new Error("Pi prepared launcher requires exactly --config <path>");
686
+ }
687
+ const value = delegatedArgs[1];
688
+ if (typeof value !== "string" || !isAbsolute(value) || /[\u0000\r\n]/u.test(value)) {
689
+ throw new Error("Pi launcher --config requires an absolute single-line path");
690
+ }
691
+ return [...delegatedArgs];
692
+ }
693
+ function requirePiModelConfig(profile) {
694
+ if (profile.pi_model === void 0) throw new Error("Pi execution requires explicit pi_model configuration");
695
+ return PiModelConfigSchema.parse(profile.pi_model);
696
+ }
602
697
 
603
698
  // src/pi-provider-launcher-core.ts
604
- var PI_CHILD_BASE_ENV_NAMES = [
605
- "PATH",
606
- "HOME",
607
- "USERPROFILE",
608
- "TMPDIR",
609
- "TEMP",
610
- "TMP",
611
- "LANG",
612
- "TZ",
613
- "TERM",
614
- "SHELL",
615
- "HTTP_PROXY",
616
- "HTTPS_PROXY",
617
- "NO_PROXY",
618
- "ALL_PROXY",
619
- "http_proxy",
620
- "https_proxy",
621
- "no_proxy",
622
- "all_proxy"
623
- ];
624
- var PI_CHILD_WINDOWS_ENV_NAMES = [
625
- "SystemRoot",
626
- "COMSPEC",
627
- "PATHEXT",
628
- "windir",
629
- "SYSTEMDRIVE",
630
- "PROGRAMFILES",
631
- "APPDATA",
632
- "LOCALAPPDATA"
633
- ];
634
699
  function parsePiProviderLauncherOptions(args) {
635
700
  const separator = args.indexOf("--");
636
701
  const ownArgs = separator < 0 ? args : args.slice(0, separator);
637
702
  const piArgs = separator < 0 ? [] : args.slice(separator + 1);
638
703
  const allowedFlags = /* @__PURE__ */ new Set([
639
704
  "--pi-bin",
705
+ "--pi-entry",
706
+ "--launch-binding",
707
+ "--pi-cwd",
708
+ "--pi-fixed-args",
709
+ "--pi-config-digest",
710
+ "--runtime-entry",
640
711
  "--profile-db",
641
712
  "--provider",
642
713
  "--model",
@@ -667,6 +738,11 @@ function parsePiProviderLauncherOptions(args) {
667
738
  if (value === void 0) throw new Error(`${flag} requires a value`);
668
739
  return value;
669
740
  };
741
+ const rawRuntimeEntry = required("--runtime-entry");
742
+ if (!PI_LAUNCHER_RUNTIME_ENTRIES.includes(rawRuntimeEntry)) {
743
+ throw new Error(`--runtime-entry must be one of [${PI_LAUNCHER_RUNTIME_ENTRIES.join(", ")}]`);
744
+ }
745
+ const runtimeEntry = rawRuntimeEntry;
670
746
  const rawProfileRef = required("--provider");
671
747
  const profileRef = ProviderProfileRefSchema.safeParse(rawProfileRef);
672
748
  if (!profileRef.success) {
@@ -725,8 +801,53 @@ function parsePiProviderLauncherOptions(args) {
725
801
  requiredCapabilities: parsedCapabilities.data
726
802
  };
727
803
  }
804
+ const piEntry = values.get("--pi-entry");
805
+ if (piEntry !== void 0 && (!path2.isAbsolute(piEntry) || /[\u0000\r\n]/u.test(piEntry))) {
806
+ throw new Error("--pi-entry requires an absolute single-line path");
807
+ }
808
+ let launchBinding;
809
+ let piCwd;
810
+ let piFixedArgs;
811
+ if (!validateOnly || ["--launch-binding", "--pi-cwd", "--pi-fixed-args"].some((flag) => values.has(flag))) {
812
+ let rawBinding;
813
+ let rawFixedArgs;
814
+ try {
815
+ rawBinding = JSON.parse(required("--launch-binding"));
816
+ } catch {
817
+ throw new Error("--launch-binding requires a valid JSON binding");
818
+ }
819
+ launchBinding = parseImplementationSpawnBinding(rawBinding);
820
+ if (launchBinding === void 0) throw new Error("invalid implementation spawn binding");
821
+ piCwd = required("--pi-cwd");
822
+ try {
823
+ rawFixedArgs = JSON.parse(required("--pi-fixed-args"));
824
+ } catch {
825
+ throw new Error("--pi-fixed-args requires a JSON array");
826
+ }
827
+ if (!Array.isArray(rawFixedArgs) || rawFixedArgs.some((arg) => typeof arg !== "string")) {
828
+ throw new Error("--pi-fixed-args requires a JSON array of strings");
829
+ }
830
+ piFixedArgs = rawFixedArgs;
831
+ if (launchBinding.command !== required("--pi-bin") || launchBinding.entry !== piEntry || launchBinding.cwd !== piCwd || JSON.stringify(launchBinding.fixedArgv) !== JSON.stringify(piFixedArgs)) {
832
+ throw new Error("launcher arguments differ from implementation spawn binding");
833
+ }
834
+ if (launchBinding.envCommitments.PI_CODING_AGENT_SESSION_DIR !== sessionDir || launchBinding.envCommitments.PI_CODING_AGENT_DIR === void 0) {
835
+ throw new Error("launcher session/projection directories must match binding commitments");
836
+ }
837
+ }
838
+ const piConfigDigest = values.get("--pi-config-digest");
839
+ if ((!validateOnly || piConfigDigest !== void 0) && !/^[0-9a-f]{64}$/u.test(piConfigDigest ?? "")) {
840
+ throw new Error("--pi-config-digest requires 64 lowercase hexadecimal characters");
841
+ }
842
+ if (piArgs.some((arg) => arg === "--config-digest" || arg.startsWith("--config-digest="))) {
843
+ throw new Error("delegated --config-digest override is forbidden");
844
+ }
728
845
  return {
846
+ ...piConfigDigest === void 0 ? {} : { piConfigDigest },
847
+ ...piEntry === void 0 ? {} : { piEntry },
848
+ runtimeEntry,
729
849
  piBin: required("--pi-bin"),
850
+ ...launchBinding === void 0 ? {} : { launchBinding, piCwd, piFixedArgs },
730
851
  profileDbPath,
731
852
  profileRef: profileRef.data,
732
853
  modelId,
@@ -738,6 +859,20 @@ function parsePiProviderLauncherOptions(args) {
738
859
  piArgs
739
860
  };
740
861
  }
862
+ function assertPiPreparedProviderProfile(profile) {
863
+ if (profile.adapter === "anthropic") {
864
+ throw new ByokKeysError(
865
+ "PROVIDER_PROFILE_INVALID",
866
+ `${profile.profile_ref} speaks the anthropic adapter; the prepared runtime entry compiles openai-completions only`
867
+ );
868
+ }
869
+ if (profile.auth_mode === "none") {
870
+ throw new ByokKeysError(
871
+ "PROVIDER_PROFILE_INVALID",
872
+ `${profile.profile_ref} declares auth_mode "none"; the prepared runtime entry requires a provider credential`
873
+ );
874
+ }
875
+ }
741
876
  async function resolvePiProviderSecret(profile, createStore) {
742
877
  if (profile.auth_mode === "none") return void 0;
743
878
  const secrets = createStore();
@@ -757,26 +892,139 @@ async function resolvePiProviderSecret(profile, createStore) {
757
892
  return secret;
758
893
  }
759
894
  function buildPiProviderChildEnvironment(options) {
760
- const platform = options.platform ?? process.platform;
761
- const exactNames = new Set([
762
- ...PI_CHILD_BASE_ENV_NAMES,
763
- ...platform === "win32" ? PI_CHILD_WINDOWS_ENV_NAMES : []
764
- ].map((name) => platform === "win32" ? name.toUpperCase() : name));
765
- const result = {};
766
- for (const [name, value] of Object.entries(options.ambient)) {
767
- if (value === void 0) continue;
768
- const platformName = platform === "win32" ? name.toUpperCase() : name;
769
- const isExact = exactNames.has(platformName);
770
- const isPrefixed = platform === "win32" ? platformName.startsWith("LC_") || platformName.startsWith("XDG_") : name.startsWith("LC_") || name.startsWith("XDG_");
771
- if (isExact || isPrefixed) result[name] = value;
772
- }
773
- result.PI_CODING_AGENT_DIR = options.projectionDir;
774
- result.PI_CODING_AGENT_SESSION_DIR = options.sessionDir;
775
- if (options.secret !== void 0) {
776
- result[PI_PROJECTED_KEY_ENV] = options.secret;
777
- }
895
+ const binding = parseImplementationSpawnBinding(options.binding);
896
+ if (binding === void 0) throw new Error("invalid implementation spawn binding");
897
+ if (binding.envCommitments.PI_CODING_AGENT_SESSION_DIR !== options.sessionDir || binding.envCommitments.PI_CODING_AGENT_DIR === void 0) {
898
+ throw new Error("launcher session/projection directories must match binding commitments");
899
+ }
900
+ const result = {
901
+ ...projectKeysPiInheritedEnvironment(options.ambient, options.platform),
902
+ ...binding.envCommitments
903
+ };
778
904
  return result;
779
905
  }
906
+ var WINDOWS_PROJECTION_ACL_SCRIPT = String.raw`
907
+ $ErrorActionPreference = 'Stop'
908
+ try {
909
+ $request = [Console]::In.ReadToEnd() | ConvertFrom-Json
910
+ $acl = Get-Acl -LiteralPath ([string]$request.path)
911
+ $sidType = [System.Security.Principal.SecurityIdentifier]
912
+ $rules = @($acl.Access | ForEach-Object {
913
+ $sid = if ($_.IdentityReference -is [System.Security.Principal.SecurityIdentifier]) { $_.IdentityReference.Value } else { try { $_.IdentityReference.Translate($sidType).Value } catch { $null } }
914
+ [ordered]@{
915
+ sid = $sid
916
+ allow = $_.AccessControlType -eq [System.Security.AccessControl.AccessControlType]::Allow
917
+ fullControl = ($_.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::FullControl) -eq [System.Security.AccessControl.FileSystemRights]::FullControl
918
+ inherits = ($_.InheritanceFlags -band 3) -eq 3 -and $_.PropagationFlags -eq [System.Security.AccessControl.PropagationFlags]::None
919
+ }
920
+ })
921
+ [ordered]@{
922
+ reparsePoint = ((Get-Item -LiteralPath ([string]$request.path) -Force).Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0
923
+ owner = $acl.GetOwner($sidType).Value
924
+ currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
925
+ protected = $acl.AreAccessRulesProtected
926
+ rules = $rules
927
+ } | ConvertTo-Json -Depth 4 -Compress
928
+ } catch {
929
+ [Console]::Error.WriteLine('Pi projection ACL query failed')
930
+ exit 1
931
+ }
932
+ `;
933
+ async function assertWindowsPiProjectionAcl(directory, options = {}) {
934
+ const systemRoot = options.systemRoot ?? process.env.SystemRoot;
935
+ if (systemRoot === void 0 || !path2.win32.isAbsolute(systemRoot) || /[\u0000\r\n]/u.test(systemRoot)) {
936
+ throw new Error("Windows SystemRoot must be an absolute path");
937
+ }
938
+ const result = await (options.run ?? runCommand)(
939
+ path2.win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe"),
940
+ ["-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(WINDOWS_PROJECTION_ACL_SCRIPT, "utf16le").toString("base64")],
941
+ JSON.stringify({ path: directory })
942
+ );
943
+ if (result.exitCode !== 0) throw new Error("Pi projection ACL query failed");
944
+ let acl;
945
+ try {
946
+ acl = JSON.parse(result.stdout);
947
+ } catch {
948
+ throw new Error("pi_projection_acl_invalid_json");
949
+ }
950
+ const recordObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
951
+ if (!recordObject(acl) || Object.keys(acl).sort().join(",") !== "currentUser,owner,protected,reparsePoint,rules" || typeof acl.owner !== "string" || !/^S-1-[0-9-]+$/u.test(acl.owner) || typeof acl.currentUser !== "string" || !/^S-1-[0-9-]+$/u.test(acl.currentUser) || typeof acl.protected !== "boolean" || typeof acl.reparsePoint !== "boolean" || !Array.isArray(acl.rules)) {
952
+ throw new Error("pi_projection_acl_invalid_shape");
953
+ }
954
+ if (acl.reparsePoint) throw new Error("pi_projection_reparse_point: Pi projection path must be a non-symlink directory");
955
+ if (acl.owner !== acl.currentUser) throw new Error("pi_projection_owner_mismatch: Pi projection directory must be owned by the current user");
956
+ if (!acl.protected) throw new Error("pi_projection_acl_unprotected");
957
+ const principals = /* @__PURE__ */ new Set([acl.owner, "S-1-5-18", "S-1-5-32-544"]);
958
+ let ownerControl = false;
959
+ for (const rule of acl.rules) {
960
+ if (!recordObject(rule) || Object.keys(rule).sort().join(",") !== "allow,fullControl,inherits,sid" || typeof rule.sid !== "string" || !/^S-1-[0-9-]+$/u.test(rule.sid) || typeof rule.allow !== "boolean" || typeof rule.fullControl !== "boolean" || typeof rule.inherits !== "boolean") {
961
+ throw new Error("pi_projection_acl_invalid_ace");
962
+ }
963
+ if (!principals.has(rule.sid) || !rule.allow) throw new Error("pi_projection_acl_unauthorized_ace");
964
+ if (rule.sid === acl.owner && rule.fullControl && rule.inherits) ownerControl = true;
965
+ }
966
+ if (!ownerControl) throw new Error("pi_projection_acl_owner_access_missing");
967
+ }
968
+ async function assertPiProjectionDirectory(projectionDir, expectedDir) {
969
+ if (projectionDir !== expectedDir || !path2.isAbsolute(projectionDir) || path2.normalize(projectionDir) !== projectionDir) {
970
+ throw new Error("pi_projection_path_mismatch: Pi projection directory differs from committed path");
971
+ }
972
+ const stat = await promises.lstat(projectionDir);
973
+ if (stat.isSymbolicLink() || !stat.isDirectory()) throw new Error("pi_projection_not_directory_or_symlink: Pi projection path must be a non-symlink directory");
974
+ if (await promises.realpath(projectionDir) !== projectionDir) throw new Error("pi_projection_path_not_canonical: Pi projection directory parents must be canonical, without symlinks");
975
+ if (process.platform === "win32") {
976
+ await assertWindowsPiProjectionAcl(projectionDir);
977
+ } else {
978
+ if (stat.uid !== process.getuid()) throw new Error("pi_projection_owner_mismatch: Pi projection directory must be owned by the current uid");
979
+ if ((stat.mode & 4095) !== 448) throw new Error("pi_projection_mode_mismatch: Pi projection directory must have mode 0700");
980
+ }
981
+ if ((await promises.readdir(projectionDir)).length !== 0) throw new Error("pi_projection_not_empty: Pi projection directory must be empty");
982
+ }
983
+ async function startPiProvider(profile, options, dependencies) {
984
+ const binding = options.launchBinding;
985
+ if (options.validateOnly || binding === void 0 || options.piCwd === void 0 || options.piFixedArgs === void 0 || !/^[0-9a-f]{64}$/u.test(options.piConfigDigest ?? "")) {
986
+ throw new Error("Pi launch requires an explicit spawn binding, cwd and fixed args");
987
+ }
988
+ const projection = buildPiProviderProjection(profile);
989
+ const delegated = options.runtimeEntry === "pi-prepared" ? buildPiPreparedArgs(options.piArgs) : buildPiProviderArgs(profile, options.piArgs);
990
+ const env = buildPiProviderChildEnvironment({
991
+ ambient: dependencies.ambient,
992
+ binding,
993
+ sessionDir: options.sessionDir});
994
+ const actual = { command: options.piBin, entry: options.piEntry, fixedArgv: [...options.piFixedArgs], cwd: options.piCwd, env };
995
+ await assertImplementationSpawnBinding(binding, actual);
996
+ const projectionDir = env.PI_CODING_AGENT_DIR;
997
+ await assertPiProjectionDirectory(projectionDir, binding.envCommitments.PI_CODING_AGENT_DIR);
998
+ await ensurePiSessionDirectory(options.sessionDir);
999
+ const modelsPath = path2.join(projectionDir, "models.json");
1000
+ let created = false;
1001
+ const cleanup = async () => {
1002
+ if (created) {
1003
+ await promises.unlink(modelsPath);
1004
+ created = false;
1005
+ }
1006
+ };
1007
+ try {
1008
+ const file = await promises.open(modelsPath, "wx", 384);
1009
+ created = true;
1010
+ try {
1011
+ await file.writeFile(`${JSON.stringify(projection)}
1012
+ `);
1013
+ } finally {
1014
+ await file.close();
1015
+ }
1016
+ const secret = await resolvePiProviderSecret(profile, dependencies.createSecretStore);
1017
+ if (secret !== void 0) env[PI_PROJECTED_KEY_ENV] = secret;
1018
+ const childArgs = [...actual.entry === void 0 ? [] : [actual.entry], ...actual.fixedArgv, `--config-digest=${options.piConfigDigest}`, ...delegated];
1019
+ const spawnChild = dependencies.spawn ?? spawn;
1020
+ await assertImplementationSpawnBinding(binding, actual);
1021
+ const child = spawnChild(actual.command, childArgs, { env, cwd: actual.cwd, stdio: "inherit" });
1022
+ return { child, cleanup };
1023
+ } catch (error) {
1024
+ await cleanup();
1025
+ throw error;
1026
+ }
1027
+ }
780
1028
  async function ensurePiSessionDirectory(sessionDir) {
781
1029
  const firstCreated = await promises.mkdir(sessionDir, { recursive: true, mode: 448 });
782
1030
  if (firstCreated !== void 0) {
@@ -823,19 +1071,19 @@ function loadSqliteModule() {
823
1071
  );
824
1072
  }
825
1073
  }
826
- function openSqliteDatabase(path4, options, faults) {
1074
+ function openSqliteDatabase(path3, options, faults) {
827
1075
  const { DatabaseSync } = loadSqliteModule();
828
1076
  const readOnly = options?.readOnly === true;
829
- if (path4 !== ":memory:" && !readOnly) {
830
- mkdirSync(dirname(path4), { mode: SECURE_DIR_MODE, recursive: true });
1077
+ if (path3 !== ":memory:" && !readOnly) {
1078
+ mkdirSync(dirname(path3), { mode: SECURE_DIR_MODE, recursive: true });
831
1079
  }
832
- const database = new DatabaseSync(path4, {
1080
+ const database = new DatabaseSync(path3, {
833
1081
  timeout: DEFAULT_BUSY_TIMEOUT_MS,
834
1082
  ...options
835
1083
  });
836
1084
  try {
837
1085
  faults?.onStep?.("after-open");
838
- if (path4 !== ":memory:" && !readOnly) {
1086
+ if (path3 !== ":memory:" && !readOnly) {
839
1087
  database.exec("PRAGMA journal_mode = WAL");
840
1088
  faults?.onStep?.("after-wal");
841
1089
  database.exec("PRAGMA synchronous = FULL");
@@ -878,6 +1126,7 @@ CREATE TABLE IF NOT EXISTS provider_profile (
878
1126
  base_url TEXT NOT NULL,
879
1127
  auth_mode TEXT NOT NULL CHECK (auth_mode IN (${sqlList(PROVIDER_AUTH_MODES)})),
880
1128
  model TEXT NOT NULL,
1129
+ pi_model TEXT,
881
1130
  capabilities TEXT NOT NULL,
882
1131
  enabled INTEGER NOT NULL CHECK (enabled IN (0, 1)),
883
1132
  created_at TEXT NOT NULL,
@@ -887,7 +1136,7 @@ CREATE TABLE IF NOT EXISTS provider_profile (
887
1136
  function normalizeTableDdl(sql) {
888
1137
  return sql.replace(/\bIF\s+NOT\s+EXISTS\b/giu, "").replace(/\s+/gu, " ").trim().replace(/;$/u, "").trim();
889
1138
  }
890
- function assertProviderProfileSchemaIsCurrent(database, path4) {
1139
+ function assertProviderProfileSchemaIsCurrent(database, path3) {
891
1140
  const row = database.prepare(
892
1141
  "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'provider_profile'"
893
1142
  ).get();
@@ -896,7 +1145,7 @@ function assertProviderProfileSchemaIsCurrent(database, path4) {
896
1145
  if (normalizeTableDdl(stored) === normalizeTableDdl(SCHEMA)) return;
897
1146
  throw new ByokKeysError(
898
1147
  "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`
1148
+ `Provider profile store at ${path3} was created by a different @byok-sdk/keys schema; preserve this store and explicitly provision a separate current-schema store before continuing`
900
1149
  );
901
1150
  }
902
1151
  var ENABLED_INDEX = `
@@ -978,8 +1227,8 @@ var SqliteProviderProfileStore = class {
978
1227
  this.#database.prepare(
979
1228
  `INSERT INTO provider_profile (
980
1229
  profile_ref, provider_kind, kind, adapter, display_name, base_url,
981
- auth_mode, model, capabilities, enabled, created_at, updated_at
982
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1230
+ auth_mode, model, pi_model, capabilities, enabled, created_at, updated_at
1231
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
983
1232
  ON CONFLICT(profile_ref) DO UPDATE SET
984
1233
  provider_kind = excluded.provider_kind,
985
1234
  adapter = excluded.adapter,
@@ -987,6 +1236,7 @@ var SqliteProviderProfileStore = class {
987
1236
  base_url = excluded.base_url,
988
1237
  auth_mode = excluded.auth_mode,
989
1238
  model = excluded.model,
1239
+ pi_model = excluded.pi_model,
990
1240
  capabilities = excluded.capabilities,
991
1241
  enabled = excluded.enabled,
992
1242
  updated_at = excluded.updated_at`
@@ -999,6 +1249,7 @@ var SqliteProviderProfileStore = class {
999
1249
  validated.base_url,
1000
1250
  validated.auth_mode,
1001
1251
  validated.model,
1252
+ validated.pi_model === void 0 ? null : JSON.stringify(validated.pi_model),
1002
1253
  JSON.stringify(validated.capabilities),
1003
1254
  validated.enabled ? 1 : 0,
1004
1255
  validated.created_at,
@@ -1037,6 +1288,7 @@ function parseRow(row) {
1037
1288
  }
1038
1289
  return parseModelProviderProfile({
1039
1290
  ...row,
1291
+ pi_model: row.pi_model === null ? void 0 : JSON.parse(row.pi_model),
1040
1292
  capabilities,
1041
1293
  enabled: row.enabled === 1
1042
1294
  });
@@ -1343,7 +1595,7 @@ async function run(options) {
1343
1595
  path: options.profileDbPath,
1344
1596
  readOnly: true
1345
1597
  });
1346
- let projectionDir;
1598
+ let cleanup;
1347
1599
  try {
1348
1600
  const profile = await profiles.get(options.profileRef);
1349
1601
  if (profile === void 0) {
@@ -1357,33 +1609,15 @@ async function run(options) {
1357
1609
  if (options.expectedBinding !== void 0) {
1358
1610
  assertExactProviderProfileBinding(profile, options.expectedBinding);
1359
1611
  }
1612
+ if (options.runtimeEntry === "pi-prepared") assertPiPreparedProviderProfile(profile);
1613
+ buildPiProviderProjection(profile);
1360
1614
  if (options.validateOnly) return 0;
1361
- const secret = await resolvePiProviderSecret(
1362
- profile,
1363
- () => createSecretStore(
1364
- options.secretServicePrefix,
1365
- options.macosKeychainPath
1366
- )
1367
- );
1368
- projectionDir = await promises.mkdtemp(path2.join(os.tmpdir(), "byok-pi-provider-"));
1369
- await promises.chmod(projectionDir, 448).catch(() => {
1370
- });
1371
- await ensurePiSessionDirectory(options.sessionDir);
1372
- await promises.writeFile(
1373
- path2.join(projectionDir, "models.json"),
1374
- `${JSON.stringify(buildPiProviderProjection(profile))}
1375
- `,
1376
- { mode: 384 }
1377
- );
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
- }),
1385
- stdio: "inherit"
1615
+ const launched = await startPiProvider(profile, options, {
1616
+ ambient: process.env,
1617
+ createSecretStore: () => createSecretStore(options.secretServicePrefix, options.macosKeychainPath)
1386
1618
  });
1619
+ cleanup = launched.cleanup;
1620
+ const child = launched.child;
1387
1621
  const forward = (signal) => {
1388
1622
  if (!child.killed) child.kill(signal);
1389
1623
  };
@@ -1404,9 +1638,7 @@ async function run(options) {
1404
1638
  }
1405
1639
  } finally {
1406
1640
  await profiles.close();
1407
- if (projectionDir) {
1408
- await promises.rm(projectionDir, { recursive: true, force: true });
1409
- }
1641
+ await cleanup?.();
1410
1642
  }
1411
1643
  }
1412
1644
  async function main() {