@ai-sdk/harness-acp 1.0.30 → 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 +25 -0
- package/README.md +11 -0
- package/dist/bridge/host-tool-mcp.mjs +1 -1
- package/dist/bridge/index.mjs +94 -30
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/bridge/package.json +2 -2
- package/dist/bridge/pnpm-lock.yaml +57 -55
- package/dist/index.d.ts +26 -2
- package/dist/index.js +121 -104
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/acp-harness.ts +9 -2
- package/src/index.ts +1 -0
- package/src/v1/acp-v1-bridge-protocol.ts +27 -6
- package/src/v1/acp-v1-harness.ts +85 -47
- package/src/v1/acp-v1-lifecycle.ts +1 -29
- package/src/v1/acp-v1-prompt.ts +1 -1
- package/src/v1/acp-v1-settings.ts +20 -1
- package/src/v1/acp-v1-skills.ts +15 -56
- package/src/v1/acp-v1-turn-start-config.ts +7 -3
- package/src/v1/bridge/acp-v1-bridge-environment.ts +4 -0
- package/src/v1/bridge/index.ts +16 -5
- package/src/v1/bridge/model-mapping.ts +35 -0
- package/src/v1/bridge/package.json +2 -2
- package/src/v1/bridge/pnpm-lock.yaml +57 -55
- package/src/v1/bridge/protocol-configuration.ts +6 -2
- package/src/v1/implementation.ts +8 -0
- package/src/v1/index.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
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
|
+
|
|
13
|
+
## 1.0.31
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- 7608210: feat(harness): add `model` parameter to `HarnessAgent` instead of having each harness adapter support it on their own constructor functions
|
|
18
|
+
- f7bd978: chore(harness): update underlying adapter SDKs to their latest versions
|
|
19
|
+
- 14d4fc0: feat(harness): allow changing harness settings between turns via `prepareCall()` support on `HarnessAgent`
|
|
20
|
+
- Updated dependencies [cc9f6ce]
|
|
21
|
+
- Updated dependencies [7608210]
|
|
22
|
+
- Updated dependencies [6f8a2d7]
|
|
23
|
+
- Updated dependencies [14d4fc0]
|
|
24
|
+
- Updated dependencies [90192f1]
|
|
25
|
+
- @ai-sdk/harness@1.0.93
|
|
26
|
+
- @ai-sdk/provider-utils@5.0.33
|
|
27
|
+
|
|
3
28
|
## 1.0.30
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -34,6 +34,10 @@ const codexACP = createACP({
|
|
|
34
34
|
packageVersion: '1.1.4',
|
|
35
35
|
},
|
|
36
36
|
executable: 'codex-acp',
|
|
37
|
+
modelMapping: {
|
|
38
|
+
type: 'session-config-option',
|
|
39
|
+
path: 'model',
|
|
40
|
+
},
|
|
37
41
|
credentialEnv: ['CODEX_API_KEY', 'OPENAI_API_KEY'],
|
|
38
42
|
credentialBrokering: ({ env, sandboxEnv }) => {
|
|
39
43
|
const environmentVariableName = env.CODEX_API_KEY
|
|
@@ -96,6 +100,13 @@ ACP supports only `permissionMode: 'allow-all'` because its
|
|
|
96
100
|
restrictive modes enable Codex's internal sandbox. A bridge-backed ACP harness
|
|
97
101
|
requires a sandbox with at least one exposed port.
|
|
98
102
|
|
|
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.
|
|
109
|
+
|
|
99
110
|
Use `instructionMapping` when the ACP implementation exposes a native system
|
|
100
111
|
or developer prompt. A `session-meta` mapping writes `HarnessAgent`
|
|
101
112
|
instructions below the ACP session request's `_meta` field. A
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
ListToolsRequestSchema,
|
|
13
13
|
McpError
|
|
14
14
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
15
|
-
var VERSION = true ? "1.0.
|
|
15
|
+
var VERSION = true ? "1.0.32" : "0.0.0-test";
|
|
16
16
|
function createHostToolMCPServer({
|
|
17
17
|
tools: tools2,
|
|
18
18
|
revision = 1,
|
package/dist/bridge/index.mjs
CHANGED
|
@@ -157,6 +157,7 @@ var ENV_TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
|
157
157
|
var WS_OPEN = 1;
|
|
158
158
|
async function runBridge(options) {
|
|
159
159
|
const { bridgeType: bridgeType2, bridgeStateDir: bridgeStateDir2, onStart, onStop, onDestroy } = options;
|
|
160
|
+
const teardownGraceMs = options.turnTeardownGraceMs ?? 1e4;
|
|
160
161
|
const expectedToken = options.token ?? procEnv.BRIDGE_CHANNEL_TOKEN ?? "";
|
|
161
162
|
const bridgeWsPort = options.port ?? parseInt(procEnv.BRIDGE_WS_PORT ?? "0", 10);
|
|
162
163
|
const bridgeMetaPath = `${bridgeStateDir2}/bridge-meta.json`;
|
|
@@ -173,6 +174,7 @@ async function runBridge(options) {
|
|
|
173
174
|
let isFirstTurn = true;
|
|
174
175
|
let turnAbort;
|
|
175
176
|
let currentUserMessages;
|
|
177
|
+
let activeTurn;
|
|
176
178
|
let debugConfig;
|
|
177
179
|
let consoleCaptureInstalled = false;
|
|
178
180
|
const envDebugEnabled = ENV_TRUTHY.has(
|
|
@@ -363,10 +365,28 @@ async function runBridge(options) {
|
|
|
363
365
|
const handleInbound = async (msg, ws) => {
|
|
364
366
|
switch (msg.type) {
|
|
365
367
|
case "start": {
|
|
368
|
+
for (; ; ) {
|
|
369
|
+
const pendingTurn = activeTurn;
|
|
370
|
+
if (pendingTurn == null) break;
|
|
371
|
+
turnAbort?.abort();
|
|
372
|
+
currentUserMessages?.close(
|
|
373
|
+
new Error("A new bridge turn replaced the active turn.")
|
|
374
|
+
);
|
|
375
|
+
let graceTimer;
|
|
376
|
+
const settled = await Promise.race([
|
|
377
|
+
pendingTurn.then(() => true),
|
|
378
|
+
new Promise((resolve) => {
|
|
379
|
+
graceTimer = setTimeout(() => resolve(false), teardownGraceMs);
|
|
380
|
+
graceTimer.unref?.();
|
|
381
|
+
})
|
|
382
|
+
]);
|
|
383
|
+
clearTimeout(graceTimer);
|
|
384
|
+
if (!settled) break;
|
|
385
|
+
}
|
|
386
|
+
let turnFinished;
|
|
387
|
+
const thisTurn = new Promise((resolve) => turnFinished = resolve);
|
|
388
|
+
activeTurn = thisTurn;
|
|
366
389
|
activeSocket = ws;
|
|
367
|
-
currentUserMessages?.close(
|
|
368
|
-
new Error("A new bridge turn replaced the active turn.")
|
|
369
|
-
);
|
|
370
390
|
const firstTurn = isFirstTurn;
|
|
371
391
|
isFirstTurn = false;
|
|
372
392
|
eventLog = [];
|
|
@@ -423,8 +443,12 @@ async function runBridge(options) {
|
|
|
423
443
|
if (currentUserMessages === userMessages) {
|
|
424
444
|
currentUserMessages = void 0;
|
|
425
445
|
}
|
|
426
|
-
|
|
427
|
-
|
|
446
|
+
if (activeTurn === thisTurn) {
|
|
447
|
+
activeTurn = void 0;
|
|
448
|
+
currentTurnState = "waiting";
|
|
449
|
+
void writeBridgeMeta("waiting");
|
|
450
|
+
}
|
|
451
|
+
turnFinished();
|
|
428
452
|
}
|
|
429
453
|
return;
|
|
430
454
|
}
|
|
@@ -607,7 +631,7 @@ function serialiseError(err) {
|
|
|
607
631
|
}
|
|
608
632
|
|
|
609
633
|
// src/v1/bridge/index.ts
|
|
610
|
-
import * as
|
|
634
|
+
import * as acp5 from "@agentclientprotocol/sdk";
|
|
611
635
|
import { spawn } from "child_process";
|
|
612
636
|
import { readFile, writeFile as writeFile2 } from "fs/promises";
|
|
613
637
|
import { fileURLToPath } from "url";
|
|
@@ -659,7 +683,8 @@ var bridgeConfigurationSchema = z.object({
|
|
|
659
683
|
authentication: authenticationSchema.optional(),
|
|
660
684
|
providerAuthentication: providerAuthenticationSchema.optional(),
|
|
661
685
|
providerEnvironment: z.record(z.string(), z.string()).optional(),
|
|
662
|
-
sessionMeta: serializableRecordSchema.optional()
|
|
686
|
+
sessionMeta: serializableRecordSchema.optional(),
|
|
687
|
+
clientCapabilities: serializableRecordSchema.optional()
|
|
663
688
|
});
|
|
664
689
|
async function readACPBridgeEnvironment({
|
|
665
690
|
env
|
|
@@ -735,9 +760,13 @@ function createACPInitializeRequest({
|
|
|
735
760
|
protocolVersion,
|
|
736
761
|
clientApp,
|
|
737
762
|
authentication,
|
|
763
|
+
clientCapabilities: configuredClientCapabilities,
|
|
738
764
|
supportsBooleanSessionConfigOptions = false
|
|
739
765
|
}) {
|
|
740
|
-
let clientCapabilities =
|
|
766
|
+
let clientCapabilities = mergeRecords({
|
|
767
|
+
left: authentication?.clientCapabilities ?? {},
|
|
768
|
+
right: configuredClientCapabilities ?? {}
|
|
769
|
+
});
|
|
741
770
|
if (supportsBooleanSessionConfigOptions) {
|
|
742
771
|
clientCapabilities = mergeRecords({
|
|
743
772
|
left: clientCapabilities,
|
|
@@ -2881,8 +2910,34 @@ function unsupported({
|
|
|
2881
2910
|
return new HarnessBridgeCapabilityUnsupportedError({ harnessId, message });
|
|
2882
2911
|
}
|
|
2883
2912
|
|
|
2884
|
-
// src/v1/bridge/
|
|
2913
|
+
// src/v1/bridge/model-mapping.ts
|
|
2885
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";
|
|
2886
2941
|
function assertACPResumeCapability({
|
|
2887
2942
|
initialization,
|
|
2888
2943
|
harnessId
|
|
@@ -2914,7 +2969,7 @@ function createACPRecoveredSession({
|
|
|
2914
2969
|
}
|
|
2915
2970
|
updates.clearErrors();
|
|
2916
2971
|
const response = agent.request(
|
|
2917
|
-
|
|
2972
|
+
acp3.methods.agent.session.prompt,
|
|
2918
2973
|
{ sessionId, prompt }
|
|
2919
2974
|
);
|
|
2920
2975
|
void response.then(
|
|
@@ -2935,7 +2990,7 @@ function createACPRecoveredSession({
|
|
|
2935
2990
|
}
|
|
2936
2991
|
updates.clearErrors();
|
|
2937
2992
|
const response = agent.request(
|
|
2938
|
-
|
|
2993
|
+
acp3.methods.agent.session.prompt,
|
|
2939
2994
|
{ sessionId, prompt, _meta: meta }
|
|
2940
2995
|
);
|
|
2941
2996
|
void response.then(
|
|
@@ -3008,7 +3063,7 @@ function createACPRecoveredSessionUpdates() {
|
|
|
3008
3063
|
}
|
|
3009
3064
|
|
|
3010
3065
|
// src/v1/bridge/session-lifecycle.ts
|
|
3011
|
-
import * as
|
|
3066
|
+
import * as acp4 from "@agentclientprotocol/sdk";
|
|
3012
3067
|
function resolveACPSessionRestorationMethod({
|
|
3013
3068
|
initialization,
|
|
3014
3069
|
harnessId
|
|
@@ -3048,8 +3103,8 @@ async function restoreACPBridgeSession({
|
|
|
3048
3103
|
mcpServers: [...mcpServers],
|
|
3049
3104
|
...meta == null ? {} : { _meta: meta }
|
|
3050
3105
|
};
|
|
3051
|
-
const response = method === "resume" ? await agent.request(
|
|
3052
|
-
|
|
3106
|
+
const response = method === "resume" ? await agent.request(acp4.methods.agent.session.resume, request) : await agent.request(
|
|
3107
|
+
acp4.methods.agent.session.load,
|
|
3053
3108
|
request
|
|
3054
3109
|
);
|
|
3055
3110
|
discardCapturedHistory();
|
|
@@ -3201,6 +3256,12 @@ async function runTurn(start, turn) {
|
|
|
3201
3256
|
});
|
|
3202
3257
|
return;
|
|
3203
3258
|
}
|
|
3259
|
+
await configureACPModel({
|
|
3260
|
+
agent: connection.agent,
|
|
3261
|
+
sessionId: activeSession.sessionId,
|
|
3262
|
+
model: start.model,
|
|
3263
|
+
mapping: start.modelMapping
|
|
3264
|
+
});
|
|
3204
3265
|
let rejectCancellationFailure;
|
|
3205
3266
|
const cancellationFailure = new Promise((_, reject) => {
|
|
3206
3267
|
rejectCancellationFailure = reject;
|
|
@@ -3217,7 +3278,7 @@ async function runTurn(start, turn) {
|
|
|
3217
3278
|
if (connection == null) {
|
|
3218
3279
|
throw new Error("ACP connection closed before cancellation.");
|
|
3219
3280
|
}
|
|
3220
|
-
await connection.agent.notify(
|
|
3281
|
+
await connection.agent.notify(acp5.methods.agent.session.cancel, {
|
|
3221
3282
|
sessionId: activeSession.sessionId
|
|
3222
3283
|
});
|
|
3223
3284
|
} catch (error) {
|
|
@@ -3388,11 +3449,13 @@ async function ensureSession({
|
|
|
3388
3449
|
instructions: start.instructions,
|
|
3389
3450
|
instructionMapping: start.instructionMapping,
|
|
3390
3451
|
sessionMeta: bridgeConfiguration.sessionMeta,
|
|
3391
|
-
environment:
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3452
|
+
environment: {
|
|
3453
|
+
...createChildEnvironment({
|
|
3454
|
+
launchEnv,
|
|
3455
|
+
implementationDir,
|
|
3456
|
+
privateHome: implementation.privateHome
|
|
3457
|
+
})
|
|
3458
|
+
}
|
|
3396
3459
|
});
|
|
3397
3460
|
child = spawn(
|
|
3398
3461
|
`${implementationDir}/${implementation.executablePath}`,
|
|
@@ -3417,15 +3480,15 @@ async function ensureSession({
|
|
|
3417
3480
|
const input = Writable.toWeb(child.stdin);
|
|
3418
3481
|
const output = Readable.toWeb(child.stdout);
|
|
3419
3482
|
const capturedStream = captureACPStream({
|
|
3420
|
-
stream:
|
|
3483
|
+
stream: acp5.ndJsonStream(input, output)
|
|
3421
3484
|
});
|
|
3422
3485
|
streamCapture = capturedStream.capture;
|
|
3423
|
-
connection =
|
|
3424
|
-
|
|
3486
|
+
connection = acp5.client({ name: clientApp.name }).onRequest(
|
|
3487
|
+
acp5.methods.client.session.requestPermission,
|
|
3425
3488
|
({ params }) => activePermissionController?.requestPermission(params) ?? {
|
|
3426
3489
|
outcome: { outcome: "cancelled" }
|
|
3427
3490
|
}
|
|
3428
|
-
).onNotification(
|
|
3491
|
+
).onNotification(acp5.methods.client.session.update, ({ params }) => {
|
|
3429
3492
|
if (params.sessionId !== recoveredSessionId) return;
|
|
3430
3493
|
if (historicalUpdatesSuppressed) return;
|
|
3431
3494
|
recoveredSessionUpdates?.enqueue({
|
|
@@ -3435,18 +3498,19 @@ async function ensureSession({
|
|
|
3435
3498
|
});
|
|
3436
3499
|
}).connect(capturedStream.stream);
|
|
3437
3500
|
const initializeRequest = createACPInitializeRequest({
|
|
3438
|
-
protocolVersion:
|
|
3501
|
+
protocolVersion: acp5.PROTOCOL_VERSION,
|
|
3439
3502
|
clientApp,
|
|
3440
3503
|
authentication,
|
|
3504
|
+
clientCapabilities: bridgeConfiguration.clientCapabilities,
|
|
3441
3505
|
supportsBooleanSessionConfigOptions: Object.values(
|
|
3442
3506
|
start.permissionModeMapping ?? {}
|
|
3443
3507
|
).some(
|
|
3444
3508
|
(target) => target?.type === "session-config-option" && typeof target.value === "boolean"
|
|
3445
3509
|
)
|
|
3446
3510
|
});
|
|
3447
|
-
const initialization = await connection.agent.request(
|
|
3511
|
+
const initialization = await connection.agent.request(acp5.methods.agent.initialize, initializeRequest);
|
|
3448
3512
|
validateACPProtocolVersion({
|
|
3449
|
-
requested:
|
|
3513
|
+
requested: acp5.PROTOCOL_VERSION,
|
|
3450
3514
|
initialization
|
|
3451
3515
|
});
|
|
3452
3516
|
if (authentication != null) {
|
|
@@ -3494,7 +3558,7 @@ async function ensureSession({
|
|
|
3494
3558
|
assertACPResumeCapability({ initialization, harnessId: bridgeType });
|
|
3495
3559
|
recoveredSessionId = start.recoveryMode.acpSessionId;
|
|
3496
3560
|
recoveredSessionUpdates = createACPRecoveredSessionUpdates();
|
|
3497
|
-
const resumeResponse = await connection.agent.request(
|
|
3561
|
+
const resumeResponse = await connection.agent.request(acp5.methods.agent.session.resume, {
|
|
3498
3562
|
sessionId: recoveredSessionId,
|
|
3499
3563
|
cwd: workDir,
|
|
3500
3564
|
mcpServers,
|
|
@@ -3616,7 +3680,7 @@ async function authenticate({
|
|
|
3616
3680
|
initialization,
|
|
3617
3681
|
methodId: authentication.methodId
|
|
3618
3682
|
});
|
|
3619
|
-
await agent.request(
|
|
3683
|
+
await agent.request(acp5.methods.agent.authenticate, {
|
|
3620
3684
|
methodId: authentication.methodId,
|
|
3621
3685
|
...authentication.meta == null ? {} : { _meta: authentication.meta }
|
|
3622
3686
|
});
|
|
@@ -3718,7 +3782,7 @@ function promptActiveSession({
|
|
|
3718
3782
|
}
|
|
3719
3783
|
updates.clearErrors();
|
|
3720
3784
|
const response = agent.request(
|
|
3721
|
-
|
|
3785
|
+
acp5.methods.agent.session.prompt,
|
|
3722
3786
|
{
|
|
3723
3787
|
sessionId: session2.sessionId,
|
|
3724
3788
|
prompt,
|