@ai-sdk/harness-acp 1.0.31 → 1.0.32

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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @ai-sdk/harness-acp
2
2
 
3
+ ## 1.0.32
4
+
5
+ ### Patch Changes
6
+
7
+ - 8961fde: feat(harness): allow changing `model` between turns via call options
8
+ - Updated dependencies [8961fde]
9
+ - Updated dependencies [eb59f2a]
10
+ - @ai-sdk/harness@1.0.94
11
+ - @ai-sdk/provider-utils@5.0.34
12
+
3
13
  ## 1.0.31
4
14
 
5
15
  ### Patch Changes
package/README.md CHANGED
@@ -34,9 +34,10 @@ const codexACP = createACP({
34
34
  packageVersion: '1.1.4',
35
35
  },
36
36
  executable: 'codex-acp',
37
- resolveModel: ({ model }) => ({
38
- env: { CODEX_CONFIG: JSON.stringify({ model }) },
39
- }),
37
+ modelMapping: {
38
+ type: 'session-config-option',
39
+ path: 'model',
40
+ },
40
41
  credentialEnv: ['CODEX_API_KEY', 'OPENAI_API_KEY'],
41
42
  credentialBrokering: ({ env, sandboxEnv }) => {
42
43
  const environmentVariableName = env.CODEX_API_KEY
@@ -99,12 +100,12 @@ ACP supports only `permissionMode: 'allow-all'` because its
99
100
  restrictive modes enable Codex's internal sandbox. A bridge-backed ACP harness
100
101
  requires a sandbox with at least one exposed port.
101
102
 
102
- `resolveModel` is required because each ACP implementation accepts its model in
103
- a different place. It receives the `HarnessAgent` model when a session starts
104
- and can return launch `args`, `env`, or both. Returned arguments replace the
105
- static `args`; returned environment values overlay the static `env`. The
106
- resolver is not called when `HarnessAgent` has no model and the deprecated
107
- `modelId` fallback is also unset.
103
+ `modelMapping` is required because ACP implementations expose different model
104
+ selection operations. Use `session-config-option` with the ACP configuration
105
+ option ID as `path`, or `session-model` with the JSON-RPC request property as
106
+ `path` for implementations such as Grok Build that use the legacy
107
+ `session/set_model` method. No model operation is sent when `HarnessAgent` has
108
+ no model and the deprecated `modelId` fallback is also unset.
108
109
 
109
110
  Use `instructionMapping` when the ACP implementation exposes a native system
110
111
  or developer prompt. A `session-meta` mapping writes `HarnessAgent`
@@ -12,7 +12,7 @@ import {
12
12
  ListToolsRequestSchema,
13
13
  McpError
14
14
  } from "@modelcontextprotocol/sdk/types.js";
15
- var VERSION = true ? "1.0.31" : "0.0.0-test";
15
+ var VERSION = true ? "1.0.32" : "0.0.0-test";
16
16
  function createHostToolMCPServer({
17
17
  tools: tools2,
18
18
  revision = 1,
@@ -631,7 +631,7 @@ function serialiseError(err) {
631
631
  }
632
632
 
633
633
  // src/v1/bridge/index.ts
634
- import * as acp4 from "@agentclientprotocol/sdk";
634
+ import * as acp5 from "@agentclientprotocol/sdk";
635
635
  import { spawn } from "child_process";
636
636
  import { readFile, writeFile as writeFile2 } from "fs/promises";
637
637
  import { fileURLToPath } from "url";
@@ -684,8 +684,7 @@ var bridgeConfigurationSchema = z.object({
684
684
  providerAuthentication: providerAuthenticationSchema.optional(),
685
685
  providerEnvironment: z.record(z.string(), z.string()).optional(),
686
686
  sessionMeta: serializableRecordSchema.optional(),
687
- implementationArgs: z.array(z.string()).optional(),
688
- implementationEnv: z.record(z.string(), z.string()).optional()
687
+ clientCapabilities: serializableRecordSchema.optional()
689
688
  });
690
689
  async function readACPBridgeEnvironment({
691
690
  env
@@ -761,9 +760,13 @@ function createACPInitializeRequest({
761
760
  protocolVersion,
762
761
  clientApp,
763
762
  authentication,
763
+ clientCapabilities: configuredClientCapabilities,
764
764
  supportsBooleanSessionConfigOptions = false
765
765
  }) {
766
- let clientCapabilities = authentication?.clientCapabilities ?? {};
766
+ let clientCapabilities = mergeRecords({
767
+ left: authentication?.clientCapabilities ?? {},
768
+ right: configuredClientCapabilities ?? {}
769
+ });
767
770
  if (supportsBooleanSessionConfigOptions) {
768
771
  clientCapabilities = mergeRecords({
769
772
  left: clientCapabilities,
@@ -2907,8 +2910,34 @@ function unsupported({
2907
2910
  return new HarnessBridgeCapabilityUnsupportedError({ harnessId, message });
2908
2911
  }
2909
2912
 
2910
- // src/v1/bridge/recovered-session.ts
2913
+ // src/v1/bridge/model-mapping.ts
2911
2914
  import * as acp2 from "@agentclientprotocol/sdk";
2915
+ async function configureACPModel({
2916
+ agent,
2917
+ sessionId,
2918
+ model,
2919
+ mapping
2920
+ }) {
2921
+ if (model == null) return;
2922
+ if (mapping == null) {
2923
+ throw new Error("ACP model mapping is required when a model is set.");
2924
+ }
2925
+ if (mapping.type === "session-model") {
2926
+ await agent.request("session/set_model", {
2927
+ sessionId,
2928
+ [mapping.path]: model
2929
+ });
2930
+ return;
2931
+ }
2932
+ await agent.request(acp2.methods.agent.session.setConfigOption, {
2933
+ sessionId,
2934
+ configId: mapping.path,
2935
+ value: model
2936
+ });
2937
+ }
2938
+
2939
+ // src/v1/bridge/recovered-session.ts
2940
+ import * as acp3 from "@agentclientprotocol/sdk";
2912
2941
  function assertACPResumeCapability({
2913
2942
  initialization,
2914
2943
  harnessId
@@ -2940,7 +2969,7 @@ function createACPRecoveredSession({
2940
2969
  }
2941
2970
  updates.clearErrors();
2942
2971
  const response = agent.request(
2943
- acp2.methods.agent.session.prompt,
2972
+ acp3.methods.agent.session.prompt,
2944
2973
  { sessionId, prompt }
2945
2974
  );
2946
2975
  void response.then(
@@ -2961,7 +2990,7 @@ function createACPRecoveredSession({
2961
2990
  }
2962
2991
  updates.clearErrors();
2963
2992
  const response = agent.request(
2964
- acp2.methods.agent.session.prompt,
2993
+ acp3.methods.agent.session.prompt,
2965
2994
  { sessionId, prompt, _meta: meta }
2966
2995
  );
2967
2996
  void response.then(
@@ -3034,7 +3063,7 @@ function createACPRecoveredSessionUpdates() {
3034
3063
  }
3035
3064
 
3036
3065
  // src/v1/bridge/session-lifecycle.ts
3037
- import * as acp3 from "@agentclientprotocol/sdk";
3066
+ import * as acp4 from "@agentclientprotocol/sdk";
3038
3067
  function resolveACPSessionRestorationMethod({
3039
3068
  initialization,
3040
3069
  harnessId
@@ -3074,8 +3103,8 @@ async function restoreACPBridgeSession({
3074
3103
  mcpServers: [...mcpServers],
3075
3104
  ...meta == null ? {} : { _meta: meta }
3076
3105
  };
3077
- const response = method === "resume" ? await agent.request(acp3.methods.agent.session.resume, request) : await agent.request(
3078
- acp3.methods.agent.session.load,
3106
+ const response = method === "resume" ? await agent.request(acp4.methods.agent.session.resume, request) : await agent.request(
3107
+ acp4.methods.agent.session.load,
3079
3108
  request
3080
3109
  );
3081
3110
  discardCapturedHistory();
@@ -3227,6 +3256,12 @@ async function runTurn(start, turn) {
3227
3256
  });
3228
3257
  return;
3229
3258
  }
3259
+ await configureACPModel({
3260
+ agent: connection.agent,
3261
+ sessionId: activeSession.sessionId,
3262
+ model: start.model,
3263
+ mapping: start.modelMapping
3264
+ });
3230
3265
  let rejectCancellationFailure;
3231
3266
  const cancellationFailure = new Promise((_, reject) => {
3232
3267
  rejectCancellationFailure = reject;
@@ -3243,7 +3278,7 @@ async function runTurn(start, turn) {
3243
3278
  if (connection == null) {
3244
3279
  throw new Error("ACP connection closed before cancellation.");
3245
3280
  }
3246
- await connection.agent.notify(acp4.methods.agent.session.cancel, {
3281
+ await connection.agent.notify(acp5.methods.agent.session.cancel, {
3247
3282
  sessionId: activeSession.sessionId
3248
3283
  });
3249
3284
  } catch (error) {
@@ -3419,13 +3454,12 @@ async function ensureSession({
3419
3454
  launchEnv,
3420
3455
  implementationDir,
3421
3456
  privateHome: implementation.privateHome
3422
- }),
3423
- ...bridgeConfiguration.implementationEnv
3457
+ })
3424
3458
  }
3425
3459
  });
3426
3460
  child = spawn(
3427
3461
  `${implementationDir}/${implementation.executablePath}`,
3428
- [...bridgeConfiguration.implementationArgs ?? implementation.args],
3462
+ [...implementation.args],
3429
3463
  {
3430
3464
  cwd: workDir,
3431
3465
  env: instructionConfiguration.environment,
@@ -3446,15 +3480,15 @@ async function ensureSession({
3446
3480
  const input = Writable.toWeb(child.stdin);
3447
3481
  const output = Readable.toWeb(child.stdout);
3448
3482
  const capturedStream = captureACPStream({
3449
- stream: acp4.ndJsonStream(input, output)
3483
+ stream: acp5.ndJsonStream(input, output)
3450
3484
  });
3451
3485
  streamCapture = capturedStream.capture;
3452
- connection = acp4.client({ name: clientApp.name }).onRequest(
3453
- acp4.methods.client.session.requestPermission,
3486
+ connection = acp5.client({ name: clientApp.name }).onRequest(
3487
+ acp5.methods.client.session.requestPermission,
3454
3488
  ({ params }) => activePermissionController?.requestPermission(params) ?? {
3455
3489
  outcome: { outcome: "cancelled" }
3456
3490
  }
3457
- ).onNotification(acp4.methods.client.session.update, ({ params }) => {
3491
+ ).onNotification(acp5.methods.client.session.update, ({ params }) => {
3458
3492
  if (params.sessionId !== recoveredSessionId) return;
3459
3493
  if (historicalUpdatesSuppressed) return;
3460
3494
  recoveredSessionUpdates?.enqueue({
@@ -3464,18 +3498,19 @@ async function ensureSession({
3464
3498
  });
3465
3499
  }).connect(capturedStream.stream);
3466
3500
  const initializeRequest = createACPInitializeRequest({
3467
- protocolVersion: acp4.PROTOCOL_VERSION,
3501
+ protocolVersion: acp5.PROTOCOL_VERSION,
3468
3502
  clientApp,
3469
3503
  authentication,
3504
+ clientCapabilities: bridgeConfiguration.clientCapabilities,
3470
3505
  supportsBooleanSessionConfigOptions: Object.values(
3471
3506
  start.permissionModeMapping ?? {}
3472
3507
  ).some(
3473
3508
  (target) => target?.type === "session-config-option" && typeof target.value === "boolean"
3474
3509
  )
3475
3510
  });
3476
- const initialization = await connection.agent.request(acp4.methods.agent.initialize, initializeRequest);
3511
+ const initialization = await connection.agent.request(acp5.methods.agent.initialize, initializeRequest);
3477
3512
  validateACPProtocolVersion({
3478
- requested: acp4.PROTOCOL_VERSION,
3513
+ requested: acp5.PROTOCOL_VERSION,
3479
3514
  initialization
3480
3515
  });
3481
3516
  if (authentication != null) {
@@ -3523,7 +3558,7 @@ async function ensureSession({
3523
3558
  assertACPResumeCapability({ initialization, harnessId: bridgeType });
3524
3559
  recoveredSessionId = start.recoveryMode.acpSessionId;
3525
3560
  recoveredSessionUpdates = createACPRecoveredSessionUpdates();
3526
- const resumeResponse = await connection.agent.request(acp4.methods.agent.session.resume, {
3561
+ const resumeResponse = await connection.agent.request(acp5.methods.agent.session.resume, {
3527
3562
  sessionId: recoveredSessionId,
3528
3563
  cwd: workDir,
3529
3564
  mcpServers,
@@ -3645,7 +3680,7 @@ async function authenticate({
3645
3680
  initialization,
3646
3681
  methodId: authentication.methodId
3647
3682
  });
3648
- await agent.request(acp4.methods.agent.authenticate, {
3683
+ await agent.request(acp5.methods.agent.authenticate, {
3649
3684
  methodId: authentication.methodId,
3650
3685
  ...authentication.meta == null ? {} : { _meta: authentication.meta }
3651
3686
  });
@@ -3747,7 +3782,7 @@ function promptActiveSession({
3747
3782
  }
3748
3783
  updates.clearErrors();
3749
3784
  const response = agent.request(
3750
- acp4.methods.agent.session.prompt,
3785
+ acp5.methods.agent.session.prompt,
3751
3786
  {
3752
3787
  sessionId: session2.sessionId,
3753
3788
  prompt,