@byok-sdk/client 0.2.0 → 0.3.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 +45 -5
- package/dist/adapters/claude/claude-adapter.d.ts +3 -0
- package/dist/adapters/codex/codex-adapter.d.ts +1 -0
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +216 -58
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/pi/pi-adapter.d.ts +22 -0
- package/dist/adapters/provider-credential-environment.d.ts +18 -0
- package/dist/bin/byok-agent.js +1725 -760
- package/dist/bin/byok-agent.js.map +1 -1
- package/dist/bin/byok-approval-mcp.js +2 -2
- package/dist/bin/byok-approval-mcp.js.map +1 -1
- package/dist/daemon/assertion-client.d.ts +68 -0
- package/dist/daemon/capabilities-client.d.ts +48 -0
- package/dist/daemon/control-protocol.d.ts +81 -4
- package/dist/daemon/create-daemon.d.ts +169 -1
- package/dist/daemon/daemon-owner.d.ts +35 -0
- package/dist/daemon/device-assertion-signer.d.ts +41 -0
- package/dist/daemon/device-keys.d.ts +15 -13
- package/dist/daemon/observer.d.ts +68 -3
- package/dist/daemon/presence-publisher.d.ts +69 -0
- package/dist/daemon/skill-pack-installer.d.ts +116 -0
- package/dist/daemon/task-runner.d.ts +129 -3
- package/dist/index.d.ts +22 -3
- package/dist/index.js +1818 -265
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +29 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -5,16 +5,56 @@ WebSocket or long poll, dispatches to local Claude Code, Codex, or pi adapters,
|
|
|
5
5
|
and exposes authenticated local diagnostics/control commands.
|
|
6
6
|
|
|
7
7
|
The package installs `byok-agent` and `byok-approval-mcp` binaries. Provider
|
|
8
|
-
credentials are not read by the dispatch plane; `@byok-sdk/keys` is separate
|
|
8
|
+
credentials are not read by the dispatch plane; `@byok-sdk/keys` is a separate
|
|
9
|
+
install and keeps a zero dependency edge to this package.
|
|
9
10
|
|
|
10
|
-
Pi is a required exact npm dependency and runs as an external Node subprocess
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
Pi is a required exact npm dependency and runs as an external Node subprocess.
|
|
12
|
+
For an authoritative BYOK `dispatchSelection`, configure `piByokLauncher` with
|
|
13
|
+
the separately installed `byok-pi-provider-launcher`, the local non-secret
|
|
14
|
+
profile database path, and a stable Pi session directory. The client passes
|
|
15
|
+
only those paths plus provider/model ids; the launcher alone reads the OS
|
|
16
|
+
credential when required and spawns Pi. Both custody paths must be absolute;
|
|
17
|
+
missing launcher configuration fails closed.
|
|
18
|
+
|
|
19
|
+
Claude Code and Codex remain user-installed runtimes and use their own login
|
|
20
|
+
state. Hosts that only need runtime detection/composition can import the
|
|
14
21
|
transport-free adapter surface:
|
|
15
22
|
|
|
16
23
|
```ts
|
|
17
24
|
import { PiAdapter, ClaudeAdapter, CodexAdapter } from '@byok-sdk/client/adapters';
|
|
18
25
|
```
|
|
19
26
|
|
|
27
|
+
Claude tasks can select operator-owned local stdio MCP servers by logical id.
|
|
28
|
+
The toolset selector carries no MCP command or connector credential:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { createDaemon } from '@byok-sdk/client';
|
|
32
|
+
|
|
33
|
+
createDaemon({
|
|
34
|
+
// ...normal device and transport configuration
|
|
35
|
+
mcpToolsets: {
|
|
36
|
+
'salesko.prospecting': {
|
|
37
|
+
mcpServers: {
|
|
38
|
+
'salesko-connectors': {
|
|
39
|
+
command: '/opt/salesko/bin/connector-mcp',
|
|
40
|
+
args: ['--profile', 'default'],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The map accepts only `command` and `args`; put OAuth tokens, cookies, and other
|
|
49
|
+
secrets behind the local MCP process's own credential broker. Toolset offers
|
|
50
|
+
for Pi or Codex are declined because those adapters do not yet expose a strict
|
|
51
|
+
task-scoped MCP configuration boundary.
|
|
52
|
+
|
|
53
|
+
For a concrete private host composition, see the
|
|
54
|
+
[`examples/salesko-connector-broker`](../../examples/salesko-connector-broker)
|
|
55
|
+
reference. It keeps `@byok-sdk/client` credential-blind while combining
|
|
56
|
+
OS-backed refresh-token custody, a PKCE desktop Google OAuth flow, exact domain
|
|
57
|
+
policy, a real read-only Gmail metadata adapter, and a closed metadata-only MCP
|
|
58
|
+
result.
|
|
59
|
+
|
|
20
60
|
MIT licensed. Node.js 22.19.0 or newer.
|
|
@@ -3,6 +3,8 @@ import { type RuntimeAdapter, type RuntimeCapabilities, type RuntimeDetectResult
|
|
|
3
3
|
import { type ResolvedBin } from './resolve-bin';
|
|
4
4
|
import { type ResolvedApprovalMcpBin } from './resolve-approval-mcp-bin';
|
|
5
5
|
import { type SpawnFn } from './process-client';
|
|
6
|
+
/** The MCP server NAME this adapter registers `byok-approval-mcp` under in the generated `--mcp-config` (arbitrary, local to this file) — combined with {@link APPROVAL_TOOL_NAME} (imported, single-sourced from `bin/approval-mcp-server.ts` so the two can never independently drift) to form the `mcp__<server>__<tool>` identifier `--permission-prompt-tool` expects. */
|
|
7
|
+
export declare const APPROVAL_MCP_SERVER_NAME = "byokapproval";
|
|
6
8
|
export interface ClaudeAdapterOptions {
|
|
7
9
|
/** Override bin resolution — tests substitute the fake-claude fixture script. */
|
|
8
10
|
resolveBin?: () => ResolvedBin;
|
|
@@ -112,6 +114,7 @@ export interface ClaudeAdapterOptions {
|
|
|
112
114
|
*/
|
|
113
115
|
export declare class ClaudeAdapter implements RuntimeAdapter {
|
|
114
116
|
private readonly options;
|
|
117
|
+
readonly supportsDispatchSelection = true;
|
|
115
118
|
readonly id = "claude";
|
|
116
119
|
constructor(options?: ClaudeAdapterOptions);
|
|
117
120
|
detect(): Promise<RuntimeDetectResult>;
|
|
@@ -52,6 +52,7 @@ export interface CodexAdapterOptions {
|
|
|
52
52
|
*/
|
|
53
53
|
export declare class CodexAdapter implements RuntimeAdapter {
|
|
54
54
|
private readonly options;
|
|
55
|
+
readonly supportsDispatchSelection = true;
|
|
55
56
|
readonly id = "codex";
|
|
56
57
|
constructor(options?: CodexAdapterOptions);
|
|
57
58
|
detect(): Promise<RuntimeDetectResult>;
|
package/dist/adapters/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type { RuntimeAdapter, RuntimeCapabilities, RuntimeDetectResult, } from '../types';
|
|
2
2
|
export type { RuntimeEnvironmentRequirements } from '../daemon/environment';
|
|
3
3
|
export { PiAdapter } from './pi/pi-adapter';
|
|
4
|
-
export type { PiAdapterOptions } from './pi/pi-adapter';
|
|
4
|
+
export type { PiAdapterOptions, PiByokLauncherConfig } from './pi/pi-adapter';
|
|
5
5
|
export { PI_PACKAGE_NAME } from './pi/resolve-bin';
|
|
6
6
|
export { ClaudeAdapter } from './claude/claude-adapter';
|
|
7
7
|
export type { ClaudeAdapterOptions } from './claude/claude-adapter';
|
package/dist/adapters/index.js
CHANGED
|
@@ -441,13 +441,8 @@ var PiRpcClient = class {
|
|
|
441
441
|
}
|
|
442
442
|
};
|
|
443
443
|
|
|
444
|
-
// src/adapters/
|
|
445
|
-
var
|
|
446
|
-
var DETECT_TIMEOUT_MS = 5e3;
|
|
447
|
-
function errorMessage(err) {
|
|
448
|
-
return err instanceof Error ? err.message : String(err);
|
|
449
|
-
}
|
|
450
|
-
var KNOWN_PROVIDER_ENV_VARS = [
|
|
444
|
+
// src/adapters/provider-credential-environment.ts
|
|
445
|
+
var PROVIDER_CREDENTIAL_ENV_NAMES = [
|
|
451
446
|
"ANTHROPIC_API_KEY",
|
|
452
447
|
"ANTHROPIC_OAUTH_TOKEN",
|
|
453
448
|
"OPENAI_API_KEY",
|
|
@@ -458,24 +453,66 @@ var KNOWN_PROVIDER_ENV_VARS = [
|
|
|
458
453
|
"MISTRAL_API_KEY",
|
|
459
454
|
"OPENROUTER_API_KEY",
|
|
460
455
|
"XAI_API_KEY",
|
|
461
|
-
// Confirmed against the installed pi's own docs/providers.md ("ZAI |
|
|
462
|
-
// `ZAI_API_KEY` | `zai`") and exercised live against real GLM traffic
|
|
463
|
-
// during this task's acceptance run — omitting it made `authPresent`
|
|
464
|
-
// silently false for a perfectly valid, working z.ai/GLM setup.
|
|
465
456
|
"ZAI_API_KEY"
|
|
466
457
|
];
|
|
458
|
+
var PROVIDER_CREDENTIAL_ENV_DENY_NAMES = [
|
|
459
|
+
...PROVIDER_CREDENTIAL_ENV_NAMES,
|
|
460
|
+
"ANT_LING_API_KEY",
|
|
461
|
+
"NVIDIA_API_KEY",
|
|
462
|
+
"CEREBRAS_API_KEY",
|
|
463
|
+
"CLOUDFLARE_API_KEY",
|
|
464
|
+
"AI_GATEWAY_API_KEY",
|
|
465
|
+
"ZAI_CODING_CN_API_KEY",
|
|
466
|
+
"OPENCODE_API_KEY",
|
|
467
|
+
"RADIUS_API_KEY",
|
|
468
|
+
"FIREWORKS_API_KEY",
|
|
469
|
+
"TOGETHER_API_KEY",
|
|
470
|
+
"BASETEN_API_KEY",
|
|
471
|
+
"KIMI_API_KEY",
|
|
472
|
+
"MINIMAX_API_KEY",
|
|
473
|
+
"MINIMAX_CN_API_KEY",
|
|
474
|
+
"QWEN_TOKEN_PLAN_API_KEY",
|
|
475
|
+
"QWEN_TOKEN_PLAN_CN_API_KEY",
|
|
476
|
+
"XIAOMI_API_KEY",
|
|
477
|
+
"XIAOMI_TOKEN_PLAN_CN_API_KEY",
|
|
478
|
+
"XIAOMI_TOKEN_PLAN_AMS_API_KEY",
|
|
479
|
+
"XIAOMI_TOKEN_PLAN_SGP_API_KEY",
|
|
480
|
+
"AWS_ACCESS_KEY_ID",
|
|
481
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
482
|
+
"AWS_SESSION_TOKEN",
|
|
483
|
+
"GOOGLE_APPLICATION_CREDENTIALS",
|
|
484
|
+
// Reserved by the keys-owned Pi projection. It must never be inherited
|
|
485
|
+
// from the daemon; the launcher deletes any ambient copy and injects only
|
|
486
|
+
// the exact credential it just resolved from OS custody.
|
|
487
|
+
"PI_PROVIDER_API_KEY"
|
|
488
|
+
];
|
|
489
|
+
function withoutProviderCredentials(env) {
|
|
490
|
+
const sanitized = { ...env };
|
|
491
|
+
for (const name of PROVIDER_CREDENTIAL_ENV_DENY_NAMES) {
|
|
492
|
+
delete sanitized[name];
|
|
493
|
+
}
|
|
494
|
+
return sanitized;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// src/adapters/pi/pi-adapter.ts
|
|
498
|
+
var execFileAsync = promisify(execFile);
|
|
499
|
+
var DETECT_TIMEOUT_MS = 5e3;
|
|
500
|
+
function errorMessage(err) {
|
|
501
|
+
return err instanceof Error ? err.message : String(err);
|
|
502
|
+
}
|
|
467
503
|
var PiAdapter = class {
|
|
468
504
|
constructor(options = {}) {
|
|
469
505
|
this.options = options;
|
|
470
506
|
}
|
|
471
507
|
options;
|
|
472
508
|
id = "pi";
|
|
509
|
+
supportsDispatchSelection = true;
|
|
473
510
|
async detect() {
|
|
474
511
|
try {
|
|
475
512
|
const bin = this.resolveBin();
|
|
476
513
|
const { stdout, stderr } = await execFileAsync(bin.command, ["--version"], { timeout: DETECT_TIMEOUT_MS });
|
|
477
514
|
const version = stdout.trim() || stderr.trim();
|
|
478
|
-
const authPresent =
|
|
515
|
+
const authPresent = PROVIDER_CREDENTIAL_ENV_NAMES.some((name) => process.env[name] !== void 0);
|
|
479
516
|
return { present: true, version, authPresent };
|
|
480
517
|
} catch {
|
|
481
518
|
return { present: false };
|
|
@@ -494,7 +531,7 @@ var PiAdapter = class {
|
|
|
494
531
|
* variable beyond the platform baseline (`daemon/environment.ts`).
|
|
495
532
|
*/
|
|
496
533
|
environmentRequirements() {
|
|
497
|
-
return { credentialNames:
|
|
534
|
+
return { credentialNames: PROVIDER_CREDENTIAL_ENV_NAMES };
|
|
498
535
|
}
|
|
499
536
|
async start(task, ctx) {
|
|
500
537
|
if (typeof task.instruction !== "string") {
|
|
@@ -506,12 +543,45 @@ var PiAdapter = class {
|
|
|
506
543
|
}
|
|
507
544
|
const bin = this.resolveBin();
|
|
508
545
|
const resumeSessionId = task.sessionRef;
|
|
509
|
-
const
|
|
546
|
+
const piArgs = ["--mode", "rpc", ...resumeSessionId ? ["--session", resumeSessionId] : [], ...mapping.args];
|
|
547
|
+
const selection = task.dispatchSelection;
|
|
548
|
+
let command = bin.command;
|
|
549
|
+
let args = piArgs;
|
|
550
|
+
if (selection !== void 0) {
|
|
551
|
+
if (selection.lane !== "byok" || selection.runtimeId !== "pi") {
|
|
552
|
+
throw new PolicyUnsupportedError(
|
|
553
|
+
`pi adapter cannot execute ${selection.lane} selection for runtime ${selection.runtimeId}`
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
const launcher = this.options.byokLauncher;
|
|
557
|
+
if (launcher === void 0) {
|
|
558
|
+
throw new PolicyUnsupportedError(
|
|
559
|
+
"pi BYOK selection requires a configured credential-custody launcher"
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
command = launcher.command;
|
|
563
|
+
args = [
|
|
564
|
+
...launcher.args ?? [],
|
|
565
|
+
"--pi-bin",
|
|
566
|
+
bin.command,
|
|
567
|
+
"--profile-db",
|
|
568
|
+
launcher.profileDbPath,
|
|
569
|
+
"--session-dir",
|
|
570
|
+
launcher.sessionDir,
|
|
571
|
+
...launcher.secretServicePrefix ? ["--secret-service-prefix", launcher.secretServicePrefix] : [],
|
|
572
|
+
"--provider",
|
|
573
|
+
selection.providerId,
|
|
574
|
+
"--model",
|
|
575
|
+
selection.modelId,
|
|
576
|
+
"--",
|
|
577
|
+
...piArgs
|
|
578
|
+
];
|
|
579
|
+
}
|
|
510
580
|
const rpc = new PiRpcClient({
|
|
511
|
-
command
|
|
581
|
+
command,
|
|
512
582
|
args,
|
|
513
583
|
cwd: ctx.workspaceDir,
|
|
514
|
-
env: ctx.env,
|
|
584
|
+
env: selection === void 0 ? ctx.env : withoutProviderCredentials(ctx.env),
|
|
515
585
|
spawnFn: this.options.spawnFn
|
|
516
586
|
});
|
|
517
587
|
const response = await rpc.send({ type: "prompt", message: task.instruction });
|
|
@@ -530,7 +600,7 @@ var PiAdapter = class {
|
|
|
530
600
|
throw err;
|
|
531
601
|
}
|
|
532
602
|
}
|
|
533
|
-
return new PiSession(sessionRef, rpc);
|
|
603
|
+
return new PiSession(sessionRef, rpc, selection);
|
|
534
604
|
}
|
|
535
605
|
resolveBin() {
|
|
536
606
|
return (this.options.resolveBin ?? resolvePiBin)();
|
|
@@ -558,12 +628,14 @@ async function resolveFreshSessionId(rpc) {
|
|
|
558
628
|
);
|
|
559
629
|
}
|
|
560
630
|
var PiSession = class {
|
|
561
|
-
constructor(sessionRef, rpc) {
|
|
631
|
+
constructor(sessionRef, rpc, selection) {
|
|
562
632
|
this.sessionRef = sessionRef;
|
|
563
633
|
this.rpc = rpc;
|
|
634
|
+
this.selection = selection;
|
|
564
635
|
}
|
|
565
636
|
sessionRef;
|
|
566
637
|
rpc;
|
|
638
|
+
selection;
|
|
567
639
|
get events() {
|
|
568
640
|
const rpc = this.rpc;
|
|
569
641
|
return {
|
|
@@ -592,6 +664,12 @@ var PiSession = class {
|
|
|
592
664
|
if (typeof task.instruction !== "string") {
|
|
593
665
|
throw new PolicyUnsupportedError("pi adapter only supports string instructions in M0 (no blob-ref fetch yet)");
|
|
594
666
|
}
|
|
667
|
+
const requestedSelection = task.dispatchSelection;
|
|
668
|
+
if (requestedSelection !== void 0 && (this.selection?.lane !== "byok" || requestedSelection.lane !== "byok" || requestedSelection.runtimeId !== "pi" || requestedSelection.providerId !== this.selection.providerId || requestedSelection.modelId !== this.selection.modelId)) {
|
|
669
|
+
throw new PolicyUnsupportedError(
|
|
670
|
+
"pi persistent session cannot change its authoritative BYOK provider/model selection"
|
|
671
|
+
);
|
|
672
|
+
}
|
|
595
673
|
await this.rpc.send({ type: "prompt", message: task.instruction, streamingBehavior: "followUp" });
|
|
596
674
|
}
|
|
597
675
|
async interrupt() {
|
|
@@ -1016,7 +1094,7 @@ var APPROVAL_TOOL_NAME = "approval_prompt";
|
|
|
1016
1094
|
var APPROVAL_MCP_SERVER_NAME = "byokapproval";
|
|
1017
1095
|
var execFileAsync2 = promisify(execFile);
|
|
1018
1096
|
var DETECT_TIMEOUT_MS2 = 5e3;
|
|
1019
|
-
async function
|
|
1097
|
+
async function cleanupMcpConfigDir(dir) {
|
|
1020
1098
|
if (!dir) return;
|
|
1021
1099
|
await promises.rm(dir, { recursive: true, force: true }).catch(() => {
|
|
1022
1100
|
});
|
|
@@ -1026,6 +1104,7 @@ var ClaudeAdapter = class {
|
|
|
1026
1104
|
this.options = options;
|
|
1027
1105
|
}
|
|
1028
1106
|
options;
|
|
1107
|
+
supportsDispatchSelection = true;
|
|
1029
1108
|
id = "claude";
|
|
1030
1109
|
async detect() {
|
|
1031
1110
|
const bin = this.resolveBin();
|
|
@@ -1039,7 +1118,13 @@ var ClaudeAdapter = class {
|
|
|
1039
1118
|
}
|
|
1040
1119
|
}
|
|
1041
1120
|
capabilities() {
|
|
1042
|
-
return {
|
|
1121
|
+
return {
|
|
1122
|
+
steer: false,
|
|
1123
|
+
resume: true,
|
|
1124
|
+
approvalInteractive: true,
|
|
1125
|
+
mcpToolsets: true,
|
|
1126
|
+
permissionModes: ["auto", "readonly", "plan", "confirm"]
|
|
1127
|
+
};
|
|
1043
1128
|
}
|
|
1044
1129
|
/**
|
|
1045
1130
|
* M5: deliberate product-boundary decision, not an oversight — byok's
|
|
@@ -1065,42 +1150,51 @@ var ClaudeAdapter = class {
|
|
|
1065
1150
|
if (!mapping.ok) {
|
|
1066
1151
|
throw new PolicyUnsupportedError(mapping.reason ?? "policy rejected by claude adapter");
|
|
1067
1152
|
}
|
|
1068
|
-
|
|
1153
|
+
const modelId = subscriptionModel(task, "claude");
|
|
1154
|
+
let mcpConfigDir;
|
|
1155
|
+
const taskMcpServers = ctx.mcpServers ?? {};
|
|
1156
|
+
const needsMcpConfig = mapping.needsApprovalMcp || Object.keys(taskMcpServers).length > 0;
|
|
1069
1157
|
if (mapping.needsApprovalMcp) {
|
|
1070
1158
|
if (!ctx.approvalChannel) {
|
|
1071
1159
|
throw new PolicyUnsupportedError(
|
|
1072
1160
|
'claude adapter requires policy.mode "confirm" to be started with an approval channel (TaskContext.approvalChannel) \u2014 none was provided'
|
|
1073
1161
|
);
|
|
1074
1162
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1163
|
+
if (Object.prototype.hasOwnProperty.call(taskMcpServers, APPROVAL_MCP_SERVER_NAME)) {
|
|
1164
|
+
throw new PolicyUnsupportedError(
|
|
1165
|
+
`MCP server name "${APPROVAL_MCP_SERVER_NAME}" is reserved by the claude approval channel`
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
if (needsMcpConfig) {
|
|
1170
|
+
mcpConfigDir = await promises.mkdtemp(path3.join(os.tmpdir(), "byok-mcp-"));
|
|
1171
|
+
await promises.chmod(mcpConfigDir, 448).catch(() => {
|
|
1078
1172
|
});
|
|
1079
|
-
const mcpConfigPath = path3.join(
|
|
1080
|
-
const
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1173
|
+
const mcpConfigPath = path3.join(mcpConfigDir, "mcp-config.json");
|
|
1174
|
+
const mcpServers = { ...taskMcpServers };
|
|
1175
|
+
if (mapping.needsApprovalMcp) {
|
|
1176
|
+
const approvalChannel = ctx.approvalChannel;
|
|
1177
|
+
if (!approvalChannel) throw new Error("unreachable: approval channel checked above");
|
|
1178
|
+
const approvalMcpBin = (this.options.resolveApprovalMcpBin ?? resolveApprovalMcpBin)();
|
|
1179
|
+
mcpServers[APPROVAL_MCP_SERVER_NAME] = {
|
|
1180
|
+
command: approvalMcpBin.command,
|
|
1181
|
+
args: approvalMcpBin.args,
|
|
1182
|
+
env: {
|
|
1183
|
+
BYOK_STORE_DIR: approvalChannel.storeDir,
|
|
1184
|
+
BYOK_PRODUCT_ID: approvalChannel.productId,
|
|
1185
|
+
BYOK_TASK_ID: approvalChannel.taskId,
|
|
1186
|
+
BYOK_APPROVAL_TIMEOUT_MS: String(approvalChannel.timeoutMs)
|
|
1091
1187
|
}
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
await promises.writeFile(mcpConfigPath, JSON.stringify(
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
await promises.writeFile(mcpConfigPath, JSON.stringify({ mcpServers }), { mode: 384 });
|
|
1095
1191
|
mapping.args = [
|
|
1096
1192
|
...mapping.args,
|
|
1097
|
-
"--permission-prompt-tool",
|
|
1098
|
-
`mcp__${APPROVAL_MCP_SERVER_NAME}__${APPROVAL_TOOL_NAME}`,
|
|
1193
|
+
...mapping.needsApprovalMcp ? ["--permission-prompt-tool", `mcp__${APPROVAL_MCP_SERVER_NAME}__${APPROVAL_TOOL_NAME}`] : [],
|
|
1099
1194
|
"--mcp-config",
|
|
1100
1195
|
mcpConfigPath,
|
|
1101
|
-
//
|
|
1102
|
-
//
|
|
1103
|
-
// the only MCP server this invocation should ever see.
|
|
1196
|
+
// The generated file is the complete task-scoped MCP authority.
|
|
1197
|
+
// Never merge ambient user/project MCP configuration into it.
|
|
1104
1198
|
"--strict-mcp-config"
|
|
1105
1199
|
];
|
|
1106
1200
|
}
|
|
@@ -1117,6 +1211,7 @@ var ClaudeAdapter = class {
|
|
|
1117
1211
|
// "Error: When using --print, --output-format=stream-json requires
|
|
1118
1212
|
// --verbose", before spawning any model call.
|
|
1119
1213
|
"--verbose",
|
|
1214
|
+
...modelId ? ["--model", modelId] : [],
|
|
1120
1215
|
...resumeSessionId ? ["--resume", resumeSessionId] : [],
|
|
1121
1216
|
...mapping.args
|
|
1122
1217
|
];
|
|
@@ -1124,7 +1219,7 @@ var ClaudeAdapter = class {
|
|
|
1124
1219
|
command: bin.command,
|
|
1125
1220
|
args,
|
|
1126
1221
|
cwd: ctx.workspaceDir,
|
|
1127
|
-
env: ctx.env,
|
|
1222
|
+
env: withoutProviderCredentials(ctx.env),
|
|
1128
1223
|
spawnFn: this.options.spawnFn
|
|
1129
1224
|
});
|
|
1130
1225
|
client.writeUserMessage(task.instruction);
|
|
@@ -1133,17 +1228,24 @@ var ClaudeAdapter = class {
|
|
|
1133
1228
|
sessionRef = await client.waitForInit();
|
|
1134
1229
|
} catch (err) {
|
|
1135
1230
|
client.kill();
|
|
1136
|
-
await
|
|
1231
|
+
await cleanupMcpConfigDir(mcpConfigDir);
|
|
1137
1232
|
throw err;
|
|
1138
1233
|
}
|
|
1139
1234
|
if (resumeSessionId !== void 0 && sessionRef !== resumeSessionId) {
|
|
1140
1235
|
client.kill();
|
|
1141
|
-
await
|
|
1236
|
+
await cleanupMcpConfigDir(mcpConfigDir);
|
|
1142
1237
|
throw new Error(
|
|
1143
1238
|
`claude --resume echoed a different session id than requested (requested ${resumeSessionId}, got ${sessionRef}) \u2014 refusing to continue in a possibly-wrong session (fail-closed)`
|
|
1144
1239
|
);
|
|
1145
1240
|
}
|
|
1146
|
-
return new ClaudeSession(
|
|
1241
|
+
return new ClaudeSession(
|
|
1242
|
+
sessionRef,
|
|
1243
|
+
client,
|
|
1244
|
+
ctx.workspaceDir,
|
|
1245
|
+
ctx.approvalChannel,
|
|
1246
|
+
mcpConfigDir,
|
|
1247
|
+
modelId
|
|
1248
|
+
);
|
|
1147
1249
|
}
|
|
1148
1250
|
/**
|
|
1149
1251
|
* `claude auth status --json` is claude's OWN non-secret login-state
|
|
@@ -1177,19 +1279,31 @@ var ClaudeAdapter = class {
|
|
|
1177
1279
|
return (this.options.resolveBin ?? resolveClaudeBin)();
|
|
1178
1280
|
}
|
|
1179
1281
|
};
|
|
1282
|
+
function subscriptionModel(task, runtimeId) {
|
|
1283
|
+
const selection = task.dispatchSelection;
|
|
1284
|
+
if (selection === void 0) return void 0;
|
|
1285
|
+
if (selection.lane !== "subscription" || selection.runtimeId !== runtimeId) {
|
|
1286
|
+
throw new PolicyUnsupportedError(
|
|
1287
|
+
`claude adapter cannot execute ${selection.lane} selection for runtime ${selection.runtimeId}`
|
|
1288
|
+
);
|
|
1289
|
+
}
|
|
1290
|
+
return selection.modelId;
|
|
1291
|
+
}
|
|
1180
1292
|
var ClaudeSession = class {
|
|
1181
|
-
constructor(sessionRef, client, workspaceDir, approvalChannel,
|
|
1293
|
+
constructor(sessionRef, client, workspaceDir, approvalChannel, mcpConfigDir, modelId) {
|
|
1182
1294
|
this.sessionRef = sessionRef;
|
|
1183
1295
|
this.client = client;
|
|
1184
1296
|
this.workspaceDir = workspaceDir;
|
|
1185
1297
|
this.approvalChannel = approvalChannel;
|
|
1186
|
-
this.
|
|
1298
|
+
this.mcpConfigDir = mcpConfigDir;
|
|
1299
|
+
this.modelId = modelId;
|
|
1187
1300
|
}
|
|
1188
1301
|
sessionRef;
|
|
1189
1302
|
client;
|
|
1190
1303
|
workspaceDir;
|
|
1191
1304
|
approvalChannel;
|
|
1192
|
-
|
|
1305
|
+
mcpConfigDir;
|
|
1306
|
+
modelId;
|
|
1193
1307
|
correlation = createToolUseCorrelation();
|
|
1194
1308
|
get events() {
|
|
1195
1309
|
const client = this.client;
|
|
@@ -1240,6 +1354,12 @@ var ClaudeSession = class {
|
|
|
1240
1354
|
if (typeof task.instruction !== "string") {
|
|
1241
1355
|
throw new PolicyUnsupportedError("claude adapter only supports string instructions in M2 (no blob-ref fetch yet)");
|
|
1242
1356
|
}
|
|
1357
|
+
const requestedModel = subscriptionModel(task, "claude");
|
|
1358
|
+
if (requestedModel !== void 0 && requestedModel !== this.modelId) {
|
|
1359
|
+
throw new PolicyUnsupportedError(
|
|
1360
|
+
`claude persistent session cannot change model from ${this.modelId ?? "(legacy default)"} to ${requestedModel}`
|
|
1361
|
+
);
|
|
1362
|
+
}
|
|
1243
1363
|
this.client.writeUserMessage(task.instruction);
|
|
1244
1364
|
}
|
|
1245
1365
|
/**
|
|
@@ -1259,7 +1379,7 @@ var ClaudeSession = class {
|
|
|
1259
1379
|
}
|
|
1260
1380
|
async close() {
|
|
1261
1381
|
this.client.kill();
|
|
1262
|
-
await
|
|
1382
|
+
await cleanupMcpConfigDir(this.mcpConfigDir);
|
|
1263
1383
|
}
|
|
1264
1384
|
/**
|
|
1265
1385
|
* M4 Phase 3: routes into the out-of-band approval channel `start()`
|
|
@@ -1583,6 +1703,7 @@ var CodexAdapter = class {
|
|
|
1583
1703
|
this.options = options;
|
|
1584
1704
|
}
|
|
1585
1705
|
options;
|
|
1706
|
+
supportsDispatchSelection = true;
|
|
1586
1707
|
id = "codex";
|
|
1587
1708
|
async detect() {
|
|
1588
1709
|
const bin = this.resolveBin();
|
|
@@ -1655,17 +1776,20 @@ ${withStreams.stderr ?? ""}`);
|
|
|
1655
1776
|
if (!mapping.ok) {
|
|
1656
1777
|
throw new PolicyUnsupportedError(mapping.reason ?? "policy rejected by codex adapter");
|
|
1657
1778
|
}
|
|
1779
|
+
const modelId = subscriptionModel2(task);
|
|
1658
1780
|
const bin = this.resolveBin();
|
|
1659
1781
|
const queue = new AsyncQueue();
|
|
1660
1782
|
const recordUnmapped = makeUnmappedFrameRecorder(/* @__PURE__ */ new Map());
|
|
1661
1783
|
const workspaceDir = await resolveRealWorkspaceDir(ctx.workspaceDir);
|
|
1784
|
+
const runtimeEnv = withoutProviderCredentials(ctx.env);
|
|
1662
1785
|
const { sessionRef, runner } = await runCodexTurn({
|
|
1663
1786
|
command: bin.command,
|
|
1664
1787
|
resumeRef: task.sessionRef,
|
|
1665
1788
|
instruction: task.instruction,
|
|
1789
|
+
modelId,
|
|
1666
1790
|
policyArgs: mapping.args,
|
|
1667
1791
|
cwd: ctx.workspaceDir,
|
|
1668
|
-
env:
|
|
1792
|
+
env: runtimeEnv,
|
|
1669
1793
|
spawnFn: this.options.spawnFn,
|
|
1670
1794
|
workspaceDir,
|
|
1671
1795
|
queue,
|
|
@@ -1682,7 +1806,8 @@ ${withStreams.stderr ?? ""}`);
|
|
|
1682
1806
|
queue,
|
|
1683
1807
|
recordUnmapped,
|
|
1684
1808
|
initialRunner: runner,
|
|
1685
|
-
preparedGit: ctx.gitWorkspace !== void 0
|
|
1809
|
+
preparedGit: ctx.gitWorkspace !== void 0,
|
|
1810
|
+
modelId
|
|
1686
1811
|
});
|
|
1687
1812
|
}
|
|
1688
1813
|
resolveBin() {
|
|
@@ -1703,12 +1828,25 @@ function makeUnmappedFrameRecorder(counts) {
|
|
|
1703
1828
|
}
|
|
1704
1829
|
};
|
|
1705
1830
|
}
|
|
1706
|
-
function buildArgv(resumeRef, policyArgs, instruction, preparedGit = false) {
|
|
1831
|
+
function buildArgv(resumeRef, policyArgs, instruction, modelId, preparedGit = false) {
|
|
1707
1832
|
const base = resumeRef !== void 0 ? ["exec", "resume", resumeRef] : ["exec"];
|
|
1708
|
-
return [
|
|
1833
|
+
return [
|
|
1834
|
+
...base,
|
|
1835
|
+
"--json",
|
|
1836
|
+
...modelId ? ["--model", modelId] : [],
|
|
1837
|
+
...preparedGit ? [] : ["--skip-git-repo-check"],
|
|
1838
|
+
...policyArgs,
|
|
1839
|
+
instruction
|
|
1840
|
+
];
|
|
1709
1841
|
}
|
|
1710
1842
|
async function runCodexTurn(params) {
|
|
1711
|
-
const argv = buildArgv(
|
|
1843
|
+
const argv = buildArgv(
|
|
1844
|
+
params.resumeRef,
|
|
1845
|
+
params.policyArgs,
|
|
1846
|
+
params.instruction,
|
|
1847
|
+
params.modelId,
|
|
1848
|
+
params.preparedGit
|
|
1849
|
+
);
|
|
1712
1850
|
let firstLineSettled = false;
|
|
1713
1851
|
let resolveFirstLine;
|
|
1714
1852
|
let rejectFirstLine;
|
|
@@ -1809,6 +1947,7 @@ var CodexSession = class {
|
|
|
1809
1947
|
queue;
|
|
1810
1948
|
recordUnmapped;
|
|
1811
1949
|
preparedGit;
|
|
1950
|
+
modelId;
|
|
1812
1951
|
currentRunner;
|
|
1813
1952
|
closed = false;
|
|
1814
1953
|
constructor(options) {
|
|
@@ -1820,6 +1959,7 @@ var CodexSession = class {
|
|
|
1820
1959
|
this.queue = options.queue;
|
|
1821
1960
|
this.recordUnmapped = options.recordUnmapped;
|
|
1822
1961
|
this.preparedGit = options.preparedGit;
|
|
1962
|
+
this.modelId = options.modelId;
|
|
1823
1963
|
this.currentRunner = options.initialRunner;
|
|
1824
1964
|
void this.forgetRunnerOnceClosed(options.initialRunner);
|
|
1825
1965
|
}
|
|
@@ -1880,6 +2020,13 @@ var CodexSession = class {
|
|
|
1880
2020
|
if (!mapping.ok) {
|
|
1881
2021
|
throw new PolicyUnsupportedError(mapping.reason ?? "policy rejected by codex adapter");
|
|
1882
2022
|
}
|
|
2023
|
+
const requestedModel = subscriptionModel2(task);
|
|
2024
|
+
if (requestedModel !== void 0 && requestedModel !== this.modelId) {
|
|
2025
|
+
throw new PolicyUnsupportedError(
|
|
2026
|
+
`codex persistent session cannot change model from ${this.modelId ?? "(legacy default)"} to ${requestedModel}`
|
|
2027
|
+
);
|
|
2028
|
+
}
|
|
2029
|
+
const modelId = this.modelId;
|
|
1883
2030
|
const resumeRef = this.sessionRef;
|
|
1884
2031
|
let sessionRef;
|
|
1885
2032
|
let runner;
|
|
@@ -1888,9 +2035,10 @@ var CodexSession = class {
|
|
|
1888
2035
|
command: this.command,
|
|
1889
2036
|
resumeRef,
|
|
1890
2037
|
instruction: task.instruction,
|
|
2038
|
+
modelId,
|
|
1891
2039
|
policyArgs: mapping.args,
|
|
1892
2040
|
cwd: this.workspaceDir,
|
|
1893
|
-
env: this.env,
|
|
2041
|
+
env: withoutProviderCredentials(this.env),
|
|
1894
2042
|
spawnFn: this.spawnFn,
|
|
1895
2043
|
workspaceDir: this.workspaceDir,
|
|
1896
2044
|
queue: this.queue,
|
|
@@ -1950,6 +2098,16 @@ var CodexSession = class {
|
|
|
1950
2098
|
);
|
|
1951
2099
|
}
|
|
1952
2100
|
};
|
|
2101
|
+
function subscriptionModel2(task) {
|
|
2102
|
+
const selection = task.dispatchSelection;
|
|
2103
|
+
if (selection === void 0) return void 0;
|
|
2104
|
+
if (selection.lane !== "subscription" || selection.runtimeId !== "codex") {
|
|
2105
|
+
throw new PolicyUnsupportedError(
|
|
2106
|
+
`codex adapter cannot execute ${selection.lane} selection for runtime ${selection.runtimeId}`
|
|
2107
|
+
);
|
|
2108
|
+
}
|
|
2109
|
+
return selection.modelId;
|
|
2110
|
+
}
|
|
1953
2111
|
|
|
1954
2112
|
export { ClaudeAdapter, CodexAdapter, PI_PACKAGE_NAME, PiAdapter };
|
|
1955
2113
|
//# sourceMappingURL=index.js.map
|