@ai-sdk/harness-opencode 1.0.88 → 1.0.92

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.
@@ -1,4 +1,7 @@
1
- import type { HarnessV1RequestTransformation } from '@ai-sdk/harness';
1
+ import type {
2
+ HarnessV1RequestTransformation,
3
+ HarnessV1RequestTransformationSources,
4
+ } from '@ai-sdk/harness';
2
5
  import {
3
6
  createCredentialRequestTransformation,
4
7
  getAiGatewayAuthFromEnv,
@@ -11,47 +14,91 @@ export const OPENCODE_CREDENTIAL_ENVIRONMENT_VARIABLES = [
11
14
  'ANTHROPIC_AUTH_TOKEN',
12
15
  ] as const;
13
16
 
14
- export function createOpenCodeRequestTransformations(
15
- env: Record<string, string>,
16
- auth: OpenCodeResolvedAuthenticationMode,
17
- ): HarnessV1RequestTransformation[] {
18
- switch (auth) {
17
+ export function createOpenCodeRequestTransformations({
18
+ env: environment,
19
+ sandboxEnv: sandboxEnvironment,
20
+ auth: authenticationMode,
21
+ }: HarnessV1RequestTransformationSources<OpenCodeResolvedAuthenticationMode>): HarnessV1RequestTransformation[] {
22
+ switch (authenticationMode) {
19
23
  case 'ai-gateway':
20
- return env.AI_GATEWAY_API_KEY
24
+ return environment.AI_GATEWAY_API_KEY &&
25
+ sandboxEnvironment.AI_GATEWAY_API_KEY
21
26
  ? [
22
27
  createCredentialRequestTransformation({
23
- baseUrl: env.AI_GATEWAY_BASE_URL,
24
- headers: {
25
- Authorization: `Bearer ${env.AI_GATEWAY_API_KEY}`,
28
+ matchUrl: environment.AI_GATEWAY_BASE_URL,
29
+ matchHeaders: {
30
+ 'x-api-key': sandboxEnvironment.AI_GATEWAY_API_KEY,
31
+ },
32
+ transformHeaders: {
33
+ Authorization: `Bearer ${environment.AI_GATEWAY_API_KEY}`,
34
+ },
35
+ }),
36
+ createCredentialRequestTransformation({
37
+ matchUrl: environment.AI_GATEWAY_BASE_URL,
38
+ matchHeaders: {
39
+ Authorization: `Bearer ${sandboxEnvironment.AI_GATEWAY_API_KEY}`,
40
+ },
41
+ transformHeaders: {
42
+ Authorization: `Bearer ${environment.AI_GATEWAY_API_KEY}`,
26
43
  },
27
44
  }),
28
45
  ]
29
46
  : [];
30
47
  case 'openai':
31
- return env.OPENAI_API_KEY
48
+ return environment.OPENAI_API_KEY && sandboxEnvironment.OPENAI_API_KEY
32
49
  ? [
33
50
  createCredentialRequestTransformation({
34
- baseUrl: env.OPENAI_BASE_URL ?? 'https://api.openai.com/v1',
35
- headers: { Authorization: `Bearer ${env.OPENAI_API_KEY}` },
51
+ matchUrl:
52
+ environment.OPENAI_BASE_URL ?? 'https://api.openai.com/v1',
53
+ matchHeaders: {
54
+ Authorization: `Bearer ${sandboxEnvironment.OPENAI_API_KEY}`,
55
+ },
56
+ transformHeaders: {
57
+ Authorization: `Bearer ${environment.OPENAI_API_KEY}`,
58
+ },
36
59
  }),
37
60
  ]
38
61
  : [];
39
62
  case 'anthropic': {
40
- const headers: Record<string, string> = {};
41
- if (env.ANTHROPIC_API_KEY) {
42
- headers['x-api-key'] = env.ANTHROPIC_API_KEY;
63
+ const matchUrl =
64
+ environment.ANTHROPIC_BASE_URL ?? 'https://api.anthropic.com';
65
+ const transformations: HarnessV1RequestTransformation[] = [];
66
+
67
+ if (
68
+ environment.ANTHROPIC_API_KEY != null &&
69
+ sandboxEnvironment.ANTHROPIC_API_KEY != null
70
+ ) {
71
+ transformations.push(
72
+ createCredentialRequestTransformation({
73
+ matchUrl,
74
+ matchHeaders: {
75
+ 'x-api-key': sandboxEnvironment.ANTHROPIC_API_KEY,
76
+ },
77
+ transformHeaders: {
78
+ 'x-api-key': environment.ANTHROPIC_API_KEY,
79
+ },
80
+ }),
81
+ );
43
82
  }
44
- if (env.ANTHROPIC_AUTH_TOKEN) {
45
- headers.Authorization = `Bearer ${env.ANTHROPIC_AUTH_TOKEN}`;
83
+
84
+ if (
85
+ environment.ANTHROPIC_AUTH_TOKEN != null &&
86
+ sandboxEnvironment.ANTHROPIC_AUTH_TOKEN != null
87
+ ) {
88
+ transformations.push(
89
+ createCredentialRequestTransformation({
90
+ matchUrl,
91
+ matchHeaders: {
92
+ Authorization: `Bearer ${sandboxEnvironment.ANTHROPIC_AUTH_TOKEN}`,
93
+ },
94
+ transformHeaders: {
95
+ Authorization: `Bearer ${environment.ANTHROPIC_AUTH_TOKEN}`,
96
+ },
97
+ }),
98
+ );
46
99
  }
47
- return Object.keys(headers).length === 0
48
- ? []
49
- : [
50
- createCredentialRequestTransformation({
51
- baseUrl: env.ANTHROPIC_BASE_URL ?? 'https://api.anthropic.com',
52
- headers,
53
- }),
54
- ];
100
+
101
+ return transformations;
55
102
  }
56
103
  }
57
104
  }
@@ -23,6 +23,7 @@ import {
23
23
  import {
24
24
  applyCredentialForwarding,
25
25
  classifyDiskLog,
26
+ createSandboxCredentialEnvironment,
26
27
  experimental_createBridgeUserMessageSubmitter,
27
28
  createBridgeErrorHandler,
28
29
  createBridgeStartupError,
@@ -30,7 +31,6 @@ import {
30
31
  forwardBridgeProcessStream,
31
32
  getRestrictedSandboxSession,
32
33
  markBridgeStarting,
33
- maskSandboxCredentials,
34
34
  resolveSandboxDefaultWorkingDirectory,
35
35
  resolveSandboxHomeDir,
36
36
  SandboxChannel,
@@ -226,6 +226,7 @@ const openCodeBridgeCoordsSchema = z.object({
226
226
  const openCodeResumeStateSchema = z.object({
227
227
  openCodeSessionId: z.string().optional(),
228
228
  bridge: openCodeBridgeCoordsSchema.optional(),
229
+ sandboxCredentialEnvironment: z.record(z.string(), z.string()).optional(),
229
230
  });
230
231
 
231
232
  type OpenCodeBridgeCoords = z.infer<typeof openCodeBridgeCoordsSchema>;
@@ -270,6 +271,23 @@ export function createOpenCode(
270
271
  sandboxSession,
271
272
  abortSignal: startOpts.abortSignal,
272
273
  });
274
+ const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;
275
+ const isResume = lifecycleState != null;
276
+ const isContinue = startOpts.continueFrom != null;
277
+ const resumeData =
278
+ isResume && typeof lifecycleState?.data === 'object'
279
+ ? (lifecycleState.data as {
280
+ openCodeSessionId?: unknown;
281
+ bridge?: OpenCodeBridgeCoords;
282
+ sandboxCredentialEnvironment?: Record<string, string>;
283
+ })
284
+ : undefined;
285
+ const resumeSessionId =
286
+ typeof resumeData?.openCodeSessionId === 'string' &&
287
+ resumeData.openCodeSessionId.length > 0
288
+ ? resumeData.openCodeSessionId
289
+ : undefined;
290
+ const coords = resumeData?.bridge;
273
291
  const authenticationMode = resolveOpenCodeAuthenticationMode({
274
292
  auth: settings.auth,
275
293
  model: settings.model,
@@ -281,24 +299,35 @@ export function createOpenCode(
281
299
  provider: settings.provider,
282
300
  });
283
301
  let sandboxAuthEnvironment = resolvedAuthEnvironment;
302
+ let sandboxCredentialEnvironment: Record<string, string> | undefined;
303
+ let credentialsBrokered = false;
284
304
  if (
285
305
  'addRequestTransformations' in sandboxSession &&
286
306
  sandboxSession.addRequestTransformations != null
287
307
  ) {
288
- const requestTransformations = createOpenCodeRequestTransformations(
289
- resolvedAuthEnvironment,
290
- authenticationMode,
291
- );
308
+ sandboxCredentialEnvironment =
309
+ resumeData?.sandboxCredentialEnvironment ??
310
+ (await createSandboxCredentialEnvironment({
311
+ environment: resolvedAuthEnvironment,
312
+ credentialEnvironmentVariables:
313
+ OPENCODE_CREDENTIAL_ENVIRONMENT_VARIABLES,
314
+ credentialForwarding: settings.credentialForwarding,
315
+ }));
316
+ sandboxAuthEnvironment = {
317
+ ...resolvedAuthEnvironment,
318
+ ...sandboxCredentialEnvironment,
319
+ };
320
+ const requestTransformations = createOpenCodeRequestTransformations({
321
+ env: resolvedAuthEnvironment,
322
+ sandboxEnv: sandboxAuthEnvironment,
323
+ auth: authenticationMode,
324
+ });
292
325
  if (requestTransformations.length > 0) {
293
326
  await sandboxSession.addRequestTransformations(
294
327
  requestTransformations,
295
328
  );
296
329
  }
297
- sandboxAuthEnvironment = maskSandboxCredentials({
298
- environment: resolvedAuthEnvironment,
299
- credentialEnvironmentVariables:
300
- OPENCODE_CREDENTIAL_ENVIRONMENT_VARIABLES,
301
- });
330
+ credentialsBrokered = true;
302
331
  } else {
303
332
  warnCredentialBrokeringUnavailable();
304
333
  }
@@ -306,23 +335,6 @@ export function createOpenCode(
306
335
  defaultWorkingDirectory,
307
336
  BOOTSTRAP_DIR,
308
337
  );
309
- const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;
310
- const isResume = lifecycleState != null;
311
- const isContinue = startOpts.continueFrom != null;
312
- const resumeData =
313
- isResume && typeof lifecycleState?.data === 'object'
314
- ? (lifecycleState.data as {
315
- openCodeSessionId?: unknown;
316
- bridge?: OpenCodeBridgeCoords;
317
- })
318
- : undefined;
319
- const resumeSessionId =
320
- typeof resumeData?.openCodeSessionId === 'string' &&
321
- resumeData.openCodeSessionId.length > 0
322
- ? resumeData.openCodeSessionId
323
- : undefined;
324
- const coords = resumeData?.bridge;
325
-
326
338
  const workDir = startOpts.sessionWorkDir;
327
339
  const sessionDataDir = `${defaultWorkingDirectory}/.agent-runs/${startOpts.sessionId}`;
328
340
  const bridgeStateDir = `${sessionDataDir}/bridge`;
@@ -386,6 +398,7 @@ export function createOpenCode(
386
398
  bridgePort: coords.port,
387
399
  bridgeToken: coords.token,
388
400
  sandboxId,
401
+ sandboxCredentialEnvironment,
389
402
  debug: startOpts.observability?.debug,
390
403
  permissionMode: startOpts.permissionMode,
391
404
  builtinToolFiltering: startOpts.builtinToolFiltering,
@@ -434,12 +447,14 @@ export function createOpenCode(
434
447
  abortSignal: startOpts.abortSignal,
435
448
  })
436
449
  : undefined;
437
- const forwardedAuthEnvironment = await applyCredentialForwarding({
438
- environment: sandboxAuthEnvironment,
439
- credentialEnvironmentVariables:
440
- OPENCODE_CREDENTIAL_ENVIRONMENT_VARIABLES,
441
- credentialForwarding: settings.credentialForwarding,
442
- });
450
+ const forwardedAuthEnvironment = credentialsBrokered
451
+ ? sandboxAuthEnvironment
452
+ : await applyCredentialForwarding({
453
+ environment: sandboxAuthEnvironment,
454
+ credentialEnvironmentVariables:
455
+ OPENCODE_CREDENTIAL_ENVIRONMENT_VARIABLES,
456
+ credentialForwarding: settings.credentialForwarding,
457
+ });
443
458
  const env = {
444
459
  ...forwardedAuthEnvironment,
445
460
  AI_SDK_HARNESS_CLIENT_APP: OPENCODE_CLIENT_APP,
@@ -552,6 +567,7 @@ export function createOpenCode(
552
567
  bridgePort: boundPort,
553
568
  bridgeToken: token,
554
569
  sandboxId,
570
+ sandboxCredentialEnvironment,
555
571
  debug: startOpts.observability?.debug,
556
572
  permissionMode: startOpts.permissionMode,
557
573
  builtinToolFiltering: startOpts.builtinToolFiltering,
@@ -779,6 +795,7 @@ function createSession({
779
795
  bridgePort,
780
796
  bridgeToken,
781
797
  sandboxId,
798
+ sandboxCredentialEnvironment,
782
799
  debug,
783
800
  permissionMode,
784
801
  builtinToolFiltering,
@@ -798,6 +815,7 @@ function createSession({
798
815
  bridgePort: number;
799
816
  bridgeToken: string;
800
817
  sandboxId: string | undefined;
818
+ sandboxCredentialEnvironment: Record<string, string> | undefined;
801
819
  debug: HarnessV1DebugConfig | undefined;
802
820
  permissionMode: HarnessV1PermissionMode | undefined;
803
821
  builtinToolFiltering: HarnessV1BuiltinToolFiltering | undefined;
@@ -1092,6 +1110,9 @@ function createSession({
1092
1110
  ...(latestOpenCodeSessionId
1093
1111
  ? { openCodeSessionId: latestOpenCodeSessionId }
1094
1112
  : {}),
1113
+ ...(sandboxCredentialEnvironment == null
1114
+ ? {}
1115
+ : { sandboxCredentialEnvironment }),
1095
1116
  bridge: {
1096
1117
  port: bridgePort,
1097
1118
  token: bridgeToken,
@@ -1185,11 +1206,20 @@ function createSession({
1185
1206
  channel.close();
1186
1207
  }
1187
1208
 
1209
+ const lifecycleData =
1210
+ data != null && typeof data === 'object' && !Array.isArray(data)
1211
+ ? { ...(data as Record<string, unknown>) }
1212
+ : {};
1188
1213
  const payload: HarnessV1ResumeSessionState = {
1189
1214
  type: 'resume-session',
1190
1215
  harnessId: 'opencode',
1191
1216
  specificationVersion: 'harness-v1',
1192
- data: (data ?? {}) as HarnessV1ResumeSessionState['data'],
1217
+ data: {
1218
+ ...lifecycleData,
1219
+ ...(sandboxCredentialEnvironment == null
1220
+ ? {}
1221
+ : { sandboxCredentialEnvironment }),
1222
+ } as HarnessV1ResumeSessionState['data'],
1193
1223
  };
1194
1224
  return payload;
1195
1225
  },
@@ -1209,6 +1239,9 @@ function createSession({
1209
1239
  ...(latestOpenCodeSessionId
1210
1240
  ? { openCodeSessionId: latestOpenCodeSessionId }
1211
1241
  : {}),
1242
+ ...(sandboxCredentialEnvironment == null
1243
+ ? {}
1244
+ : { sandboxCredentialEnvironment }),
1212
1245
  bridge: {
1213
1246
  port: bridgePort,
1214
1247
  token: bridgeToken,