@ai-sdk/harness-acp 1.0.30 → 1.0.31
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 +15 -0
- package/README.md +10 -0
- package/dist/bridge/host-tool-mcp.mjs +1 -1
- package/dist/bridge/index.mjs +41 -12
- 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 +30 -2
- package/dist/index.js +83 -91
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/acp-harness.ts +8 -2
- package/src/index.ts +2 -0
- package/src/v1/acp-v1-harness.ts +78 -42
- 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 +24 -1
- package/src/v1/acp-v1-skills.ts +15 -56
- package/src/v1/bridge/acp-v1-bridge-environment.ts +8 -0
- package/src/v1/bridge/index.ts +9 -6
- package/src/v1/bridge/package.json +2 -2
- package/src/v1/bridge/pnpm-lock.yaml +57 -55
- package/src/v1/index.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/harness-acp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.31",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -26,16 +26,16 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@ai-sdk/harness": "1.0.
|
|
30
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
29
|
+
"@ai-sdk/harness": "1.0.93",
|
|
30
|
+
"@ai-sdk/provider-utils": "5.0.33",
|
|
31
31
|
"ws": "^8.21.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"zod": "^3.25.76 || ^4.1.8"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@agentclientprotocol/sdk": "1.
|
|
38
|
-
"@modelcontextprotocol/sdk": "1.
|
|
37
|
+
"@agentclientprotocol/sdk": "1.4.0",
|
|
38
|
+
"@modelcontextprotocol/sdk": "1.30.0",
|
|
39
39
|
"@types/node": "22.19.19",
|
|
40
40
|
"@types/ws": "^8.5.13",
|
|
41
41
|
"@vercel/ai-tsconfig": "0.0.0",
|
package/src/acp-harness.ts
CHANGED
|
@@ -49,6 +49,13 @@ export type ACPHarnessSettings<TBuiltinTools extends ToolSet = {}> = {
|
|
|
49
49
|
readonly env?: ACPV1Settings['env'];
|
|
50
50
|
readonly authentication?: ACPV1Settings['authentication'];
|
|
51
51
|
readonly providerAuthentication?: ACPV1Settings['providerAuthentication'];
|
|
52
|
+
/**
|
|
53
|
+
* Maps a HarnessAgent model identifier to implementation launch settings.
|
|
54
|
+
*/
|
|
55
|
+
readonly resolveModel: ACPV1Settings['resolveModel'];
|
|
56
|
+
/**
|
|
57
|
+
* @deprecated Use `model` on `HarnessAgent` instead.
|
|
58
|
+
*/
|
|
52
59
|
readonly modelId?: ACPV1Settings['modelId'];
|
|
53
60
|
readonly skillsDirectory?: ACPV1Settings['skillsDirectory'];
|
|
54
61
|
readonly instructionMapping?: ACPV1Settings['instructionMapping'];
|
|
@@ -103,8 +110,7 @@ const acpResumeStateSchema = z.object({
|
|
|
103
110
|
})
|
|
104
111
|
.optional(),
|
|
105
112
|
initialGuidanceApplied: z.boolean().optional(),
|
|
106
|
-
|
|
107
|
-
skillsFingerprint: z.string().optional(),
|
|
113
|
+
instructionsFingerprint: z.string().optional(),
|
|
108
114
|
skillsDirectory: z.string().optional(),
|
|
109
115
|
});
|
|
110
116
|
|
package/src/index.ts
CHANGED
package/src/v1/acp-v1-harness.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type HarnessV1PortEndpoint,
|
|
10
10
|
type HarnessV1PromptControl,
|
|
11
11
|
type HarnessV1Session,
|
|
12
|
+
type HarnessV1Skill,
|
|
12
13
|
type HarnessV1StreamPart,
|
|
13
14
|
type HarnessV1ToolSpec,
|
|
14
15
|
} from '@ai-sdk/harness';
|
|
@@ -70,13 +71,12 @@ import {
|
|
|
70
71
|
} from './acp-v1-turn-start-config';
|
|
71
72
|
import {
|
|
72
73
|
resolveACPInitialGuidanceApplied,
|
|
73
|
-
shouldMaterializeACPSkills,
|
|
74
74
|
validateACPLifecycleCompatibility,
|
|
75
75
|
type ACPLifecycleData,
|
|
76
76
|
} from './acp-v1-lifecycle';
|
|
77
77
|
import {
|
|
78
78
|
convertHarnessPromptToACPTextBlocks,
|
|
79
|
-
|
|
79
|
+
prependACPInstructionGuidance,
|
|
80
80
|
} from './acp-v1-prompt';
|
|
81
81
|
import type {
|
|
82
82
|
ACPInstructionMapping,
|
|
@@ -88,7 +88,6 @@ import type {
|
|
|
88
88
|
ACPV1Settings,
|
|
89
89
|
} from './acp-v1-settings';
|
|
90
90
|
import {
|
|
91
|
-
createACPSkillsFingerprint,
|
|
92
91
|
materializeACPSkills,
|
|
93
92
|
resolveACPPrivateSessionDirectory,
|
|
94
93
|
resolveACPSkillsDirectory,
|
|
@@ -159,11 +158,11 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
|
|
|
159
158
|
`ACP harnessId must be a stable kebab-case identifier; received ${JSON.stringify(settings.harnessId)}.`,
|
|
160
159
|
);
|
|
161
160
|
}
|
|
162
|
-
const
|
|
163
|
-
validateACPV1Implementation(
|
|
161
|
+
const baseImplementation = createACPV1Implementation({ settings });
|
|
162
|
+
validateACPV1Implementation(baseImplementation);
|
|
164
163
|
const bootstrap = createACPBootstrap({
|
|
165
164
|
harnessId: settings.harnessId,
|
|
166
|
-
implementation,
|
|
165
|
+
implementation: baseImplementation,
|
|
167
166
|
});
|
|
168
167
|
const permissionModeMapping = isCompletePermissionModeMapping({
|
|
169
168
|
value: settings.permissionModeMapping,
|
|
@@ -187,6 +186,24 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
|
|
|
187
186
|
'ACP built-in tool filtering is not available in this initial ACP v1 implementation.',
|
|
188
187
|
});
|
|
189
188
|
}
|
|
189
|
+
const modelId = startOptions.model ?? settings.modelId;
|
|
190
|
+
const modelConfiguration =
|
|
191
|
+
modelId == null ? undefined : settings.resolveModel({ model: modelId });
|
|
192
|
+
const implementation = {
|
|
193
|
+
...baseImplementation,
|
|
194
|
+
...(modelConfiguration?.args == null
|
|
195
|
+
? {}
|
|
196
|
+
: { args: modelConfiguration.args }),
|
|
197
|
+
...(modelConfiguration?.env == null
|
|
198
|
+
? {}
|
|
199
|
+
: {
|
|
200
|
+
env: {
|
|
201
|
+
...baseImplementation.env,
|
|
202
|
+
...modelConfiguration.env,
|
|
203
|
+
},
|
|
204
|
+
}),
|
|
205
|
+
};
|
|
206
|
+
validateACPV1Implementation(implementation);
|
|
190
207
|
const permissionMode = startOptions.permissionMode ?? 'allow-all';
|
|
191
208
|
const env = { ...process.env };
|
|
192
209
|
const authenticationEnvironment = resolveACPAuthenticationEnvironment({
|
|
@@ -381,24 +398,6 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
|
|
|
381
398
|
sessionWorkDir: workDir,
|
|
382
399
|
});
|
|
383
400
|
const bridgeStateDir = `${privateSessionDir}/bridge`;
|
|
384
|
-
const skills = startOptions.skills ?? [];
|
|
385
|
-
const skillsFingerprint = createACPSkillsFingerprint({ skills });
|
|
386
|
-
const shouldMaterializeSkills = shouldMaterializeACPSkills({
|
|
387
|
-
isResume,
|
|
388
|
-
lifecycleState: lifecycleData,
|
|
389
|
-
skillsFingerprint,
|
|
390
|
-
skillsDirectory,
|
|
391
|
-
});
|
|
392
|
-
if (skills.length > 0) {
|
|
393
|
-
await materializeACPSkills({
|
|
394
|
-
sandbox: toolSafeSandboxSession,
|
|
395
|
-
rootDir: skillsDirectory,
|
|
396
|
-
sessionWorkDir: workDir,
|
|
397
|
-
skills,
|
|
398
|
-
shouldMaterialize: shouldMaterializeSkills,
|
|
399
|
-
abortSignal: startOptions.abortSignal,
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
401
|
const report = startOptions.observability?.report;
|
|
403
402
|
const onDiagnostic = report
|
|
404
403
|
? (frame: Parameters<typeof harnessV1DiagnosticFromBridgeFrame>[0]) =>
|
|
@@ -453,7 +452,7 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
|
|
|
453
452
|
harnessId: settings.harnessId,
|
|
454
453
|
channel: attachChannel,
|
|
455
454
|
proc: undefined,
|
|
456
|
-
modelId
|
|
455
|
+
modelId,
|
|
457
456
|
sessionMeta: settings.session?.meta,
|
|
458
457
|
instructionMapping: settings.instructionMapping,
|
|
459
458
|
outputSchemaMapping: settings.outputSchemaMapping,
|
|
@@ -469,7 +468,9 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
|
|
|
469
468
|
isResume: true,
|
|
470
469
|
lifecycleState: lifecycleData,
|
|
471
470
|
}),
|
|
472
|
-
|
|
471
|
+
instructionsFingerprint: lifecycleData.instructionsFingerprint,
|
|
472
|
+
sandbox: toolSafeSandboxSession,
|
|
473
|
+
sessionWorkDir: workDir,
|
|
473
474
|
skillsDirectory,
|
|
474
475
|
acpSessionId: lifecycleData.acpSessionId,
|
|
475
476
|
bridgePort: coords.port,
|
|
@@ -543,7 +544,7 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
|
|
|
543
544
|
}
|
|
544
545
|
const turnStartConfig = validateACPColdSessionConfiguration({
|
|
545
546
|
coldSession,
|
|
546
|
-
modelId
|
|
547
|
+
modelId,
|
|
547
548
|
permissionMode,
|
|
548
549
|
authenticationProfile,
|
|
549
550
|
sessionMeta: settings.session?.meta,
|
|
@@ -607,6 +608,8 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
|
|
|
607
608
|
providerEnvironment:
|
|
608
609
|
sandboxProviderEnvironment == null ? undefined : {},
|
|
609
610
|
sessionMeta: settings.session?.meta,
|
|
611
|
+
implementationArgs: modelConfiguration?.args,
|
|
612
|
+
implementationEnv: modelConfiguration?.env,
|
|
610
613
|
}),
|
|
611
614
|
...sandboxProviderAuthenticationEnvironment,
|
|
612
615
|
BRIDGE_CHANNEL_TOKEN: token,
|
|
@@ -719,7 +722,7 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
|
|
|
719
722
|
harnessId: settings.harnessId,
|
|
720
723
|
channel,
|
|
721
724
|
proc,
|
|
722
|
-
modelId
|
|
725
|
+
modelId,
|
|
723
726
|
sessionMeta: settings.session?.meta,
|
|
724
727
|
instructionMapping: settings.instructionMapping,
|
|
725
728
|
outputSchemaMapping: settings.outputSchemaMapping,
|
|
@@ -735,7 +738,9 @@ export function createACPV1<TBuiltinTools extends ToolSet = {}>({
|
|
|
735
738
|
isResume,
|
|
736
739
|
lifecycleState: lifecycleData,
|
|
737
740
|
}),
|
|
738
|
-
|
|
741
|
+
instructionsFingerprint: lifecycleData?.instructionsFingerprint,
|
|
742
|
+
sandbox: toolSafeSandboxSession,
|
|
743
|
+
sessionWorkDir: workDir,
|
|
739
744
|
skillsDirectory,
|
|
740
745
|
acpSessionId: lifecycleData?.acpSessionId,
|
|
741
746
|
bridgePort: boundPort,
|
|
@@ -1054,7 +1059,9 @@ function createSession({
|
|
|
1054
1059
|
mcpServers,
|
|
1055
1060
|
isMcpToolCall,
|
|
1056
1061
|
initialGuidanceApplied: initialGuidanceAppliedAtStart,
|
|
1057
|
-
|
|
1062
|
+
instructionsFingerprint: instructionsFingerprintAtStart,
|
|
1063
|
+
sandbox,
|
|
1064
|
+
sessionWorkDir,
|
|
1058
1065
|
skillsDirectory,
|
|
1059
1066
|
acpSessionId: acpSessionIdAtStart,
|
|
1060
1067
|
bridgePort,
|
|
@@ -1087,7 +1094,9 @@ function createSession({
|
|
|
1087
1094
|
mcpServers: Record<string, unknown> | undefined;
|
|
1088
1095
|
isMcpToolCall: ((toolCall: ACPToolCall) => boolean) | undefined;
|
|
1089
1096
|
initialGuidanceApplied: boolean;
|
|
1090
|
-
|
|
1097
|
+
instructionsFingerprint: string | undefined;
|
|
1098
|
+
sandbox: SandboxSession;
|
|
1099
|
+
sessionWorkDir: string;
|
|
1091
1100
|
skillsDirectory: string;
|
|
1092
1101
|
acpSessionId: string | undefined;
|
|
1093
1102
|
bridgePort: number;
|
|
@@ -1106,6 +1115,7 @@ function createSession({
|
|
|
1106
1115
|
let stopped = false;
|
|
1107
1116
|
let turnInFlight = turnInFlightAtStart;
|
|
1108
1117
|
let initialGuidanceApplied = initialGuidanceAppliedAtStart;
|
|
1118
|
+
let instructionsFingerprint = instructionsFingerprintAtStart;
|
|
1109
1119
|
let latestACPSessionId = acpSessionIdAtStart;
|
|
1110
1120
|
let latestTurnStartConfig = turnStartConfigAtStart;
|
|
1111
1121
|
|
|
@@ -1374,16 +1384,35 @@ function createSession({
|
|
|
1374
1384
|
...(recoveryStatus == null ? {} : { recovery: recoveryStatus }),
|
|
1375
1385
|
...(restoration == null ? {} : { restoration }),
|
|
1376
1386
|
initialGuidanceApplied,
|
|
1377
|
-
|
|
1378
|
-
skillsFingerprint,
|
|
1387
|
+
...(instructionsFingerprint == null ? {} : { instructionsFingerprint }),
|
|
1379
1388
|
skillsDirectory,
|
|
1380
1389
|
});
|
|
1381
1390
|
|
|
1391
|
+
const synchronizeSkills = async ({
|
|
1392
|
+
skills,
|
|
1393
|
+
abortSignal,
|
|
1394
|
+
}: {
|
|
1395
|
+
skills: ReadonlyArray<HarnessV1Skill>;
|
|
1396
|
+
abortSignal?: AbortSignal;
|
|
1397
|
+
}): Promise<void> => {
|
|
1398
|
+
await materializeACPSkills({
|
|
1399
|
+
sandbox,
|
|
1400
|
+
rootDir: skillsDirectory,
|
|
1401
|
+
sessionWorkDir,
|
|
1402
|
+
skills,
|
|
1403
|
+
abortSignal,
|
|
1404
|
+
});
|
|
1405
|
+
};
|
|
1406
|
+
|
|
1382
1407
|
return {
|
|
1383
1408
|
sessionId,
|
|
1384
1409
|
isResume,
|
|
1385
1410
|
...(modelId == null ? {} : { modelId }),
|
|
1386
1411
|
doPromptTurn: async options => {
|
|
1412
|
+
await synchronizeSkills({
|
|
1413
|
+
skills: options.skills,
|
|
1414
|
+
abortSignal: options.abortSignal,
|
|
1415
|
+
});
|
|
1387
1416
|
if (options.responseFormat?.type === 'json') {
|
|
1388
1417
|
if (options.responseFormat.schema == null) {
|
|
1389
1418
|
throw unsupported({
|
|
@@ -1427,6 +1456,9 @@ function createSession({
|
|
|
1427
1456
|
responseFormat: options.responseFormat,
|
|
1428
1457
|
outputSchemaMapping,
|
|
1429
1458
|
});
|
|
1459
|
+
const nextInstructionsFingerprint = fingerprintValue({
|
|
1460
|
+
value: options.instructions ?? null,
|
|
1461
|
+
});
|
|
1430
1462
|
const control = wireTurn({
|
|
1431
1463
|
emit: options.emit,
|
|
1432
1464
|
abortSignal: options.abortSignal,
|
|
@@ -1439,15 +1471,14 @@ function createSession({
|
|
|
1439
1471
|
turnInFlight = true;
|
|
1440
1472
|
channel.send({
|
|
1441
1473
|
type: 'start',
|
|
1442
|
-
prompt:
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
}),
|
|
1474
|
+
prompt:
|
|
1475
|
+
instructionsFingerprint !== nextInstructionsFingerprint &&
|
|
1476
|
+
(instructionMapping == null || initialGuidanceApplied)
|
|
1477
|
+
? prependACPInstructionGuidance({
|
|
1478
|
+
prompt,
|
|
1479
|
+
instructions: options.instructions,
|
|
1480
|
+
})
|
|
1481
|
+
: prompt,
|
|
1451
1482
|
...(instructionMapping == null
|
|
1452
1483
|
? {}
|
|
1453
1484
|
: {
|
|
@@ -1472,11 +1503,16 @@ function createSession({
|
|
|
1472
1503
|
});
|
|
1473
1504
|
latestTurnStartConfig = turnStartConfig;
|
|
1474
1505
|
initialGuidanceApplied = true;
|
|
1506
|
+
instructionsFingerprint = nextInstructionsFingerprint;
|
|
1475
1507
|
},
|
|
1476
1508
|
});
|
|
1477
1509
|
return control;
|
|
1478
1510
|
},
|
|
1479
1511
|
doContinueTurn: async options => {
|
|
1512
|
+
await synchronizeSkills({
|
|
1513
|
+
skills: options.skills,
|
|
1514
|
+
abortSignal: options.abortSignal,
|
|
1515
|
+
});
|
|
1480
1516
|
if (options.responseFormat?.type === 'json') {
|
|
1481
1517
|
if (options.responseFormat.schema == null) {
|
|
1482
1518
|
throw unsupported({
|
|
@@ -6,8 +6,7 @@ import type {
|
|
|
6
6
|
|
|
7
7
|
export type ACPPromptGuidanceLifecycleState = {
|
|
8
8
|
readonly initialGuidanceApplied?: boolean;
|
|
9
|
-
readonly
|
|
10
|
-
readonly skillsFingerprint?: string;
|
|
9
|
+
readonly instructionsFingerprint?: string;
|
|
11
10
|
readonly skillsDirectory?: string;
|
|
12
11
|
};
|
|
13
12
|
|
|
@@ -47,33 +46,6 @@ export function resolveACPInitialGuidanceApplied({
|
|
|
47
46
|
return lifecycleState?.initialGuidanceApplied ?? true;
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
export function shouldMaterializeACPSkills({
|
|
51
|
-
isResume,
|
|
52
|
-
lifecycleState,
|
|
53
|
-
skillsFingerprint,
|
|
54
|
-
skillsDirectory,
|
|
55
|
-
}: {
|
|
56
|
-
isResume: boolean;
|
|
57
|
-
lifecycleState: ACPPromptGuidanceLifecycleState | undefined;
|
|
58
|
-
skillsFingerprint: string;
|
|
59
|
-
skillsDirectory: string;
|
|
60
|
-
}): boolean {
|
|
61
|
-
if (
|
|
62
|
-
isResume &&
|
|
63
|
-
lifecycleState?.skillsFingerprint != null &&
|
|
64
|
-
lifecycleState.skillsFingerprint !== skillsFingerprint
|
|
65
|
-
) {
|
|
66
|
-
throw new Error(
|
|
67
|
-
'ACP lifecycle state was created with a different set of skills.',
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
return (
|
|
71
|
-
!isResume ||
|
|
72
|
-
lifecycleState?.skillsMaterialized !== true ||
|
|
73
|
-
lifecycleState.skillsDirectory !== skillsDirectory
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
49
|
export function validateACPLifecycleCompatibility({
|
|
78
50
|
harnessId,
|
|
79
51
|
lifecycleHarnessId,
|
package/src/v1/acp-v1-prompt.ts
CHANGED
|
@@ -75,6 +75,21 @@ export type ACPProviderAuthentication = {
|
|
|
75
75
|
};
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
+
export type ACPModelResolution = {
|
|
79
|
+
/**
|
|
80
|
+
* Replaces the implementation's static launch arguments.
|
|
81
|
+
*/
|
|
82
|
+
readonly args?: ReadonlyArray<string>;
|
|
83
|
+
/**
|
|
84
|
+
* Overlays the implementation's static launch environment.
|
|
85
|
+
*/
|
|
86
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export type ACPModelResolver = (options: {
|
|
90
|
+
readonly model: string;
|
|
91
|
+
}) => ACPModelResolution;
|
|
92
|
+
|
|
78
93
|
export type ACPCredentialBrokering = ({
|
|
79
94
|
env,
|
|
80
95
|
sandboxEnv,
|
|
@@ -144,6 +159,13 @@ export type ACPV1Settings = {
|
|
|
144
159
|
readonly env?: Readonly<Record<string, string>>;
|
|
145
160
|
readonly authentication?: ACPAuthentication;
|
|
146
161
|
readonly providerAuthentication?: ACPProviderAuthentication;
|
|
162
|
+
/**
|
|
163
|
+
* Maps a HarnessAgent model identifier to implementation launch settings.
|
|
164
|
+
*/
|
|
165
|
+
readonly resolveModel: ACPModelResolver;
|
|
166
|
+
/**
|
|
167
|
+
* @deprecated Use `model` on `HarnessAgent` instead.
|
|
168
|
+
*/
|
|
147
169
|
readonly modelId?: string;
|
|
148
170
|
/**
|
|
149
171
|
* Native skills directory relative to the ACP implementation's home
|
|
@@ -152,7 +174,8 @@ export type ACPV1Settings = {
|
|
|
152
174
|
readonly skillsDirectory?: string;
|
|
153
175
|
/**
|
|
154
176
|
* Routes HarnessAgent instructions to a runtime-native system or developer
|
|
155
|
-
* prompt.
|
|
177
|
+
* prompt. Changed instructions are prepended to the next user prompt when
|
|
178
|
+
* ACP does not expose a native per-turn instruction update.
|
|
156
179
|
*/
|
|
157
180
|
readonly instructionMapping?: ACPInstructionMapping;
|
|
158
181
|
/**
|
package/src/v1/acp-v1-skills.ts
CHANGED
|
@@ -1,39 +1,12 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import type { HarnessV1Skill } from '@ai-sdk/harness';
|
|
4
|
-
import { writeSkills } from '@ai-sdk/harness/utils';
|
|
4
|
+
import { writeSkills, type WriteSkillsResult } from '@ai-sdk/harness/utils';
|
|
5
5
|
import type { Experimental_SandboxSession } from '@ai-sdk/provider-utils';
|
|
6
6
|
|
|
7
7
|
const ACP_SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
8
8
|
export const DEFAULT_ACP_SKILLS_DIRECTORY = '.agents/skills';
|
|
9
9
|
|
|
10
|
-
export function createACPSkillsFingerprint({
|
|
11
|
-
skills,
|
|
12
|
-
}: {
|
|
13
|
-
skills: ReadonlyArray<HarnessV1Skill>;
|
|
14
|
-
}): string {
|
|
15
|
-
const canonicalSkills = skills
|
|
16
|
-
.map(skill => ({
|
|
17
|
-
name: skill.name,
|
|
18
|
-
description: skill.description,
|
|
19
|
-
content: skill.content,
|
|
20
|
-
files: [...(skill.files ?? [])]
|
|
21
|
-
.map(file => ({
|
|
22
|
-
path: path.posix.normalize(file.path),
|
|
23
|
-
content: file.content,
|
|
24
|
-
}))
|
|
25
|
-
.sort((left, right) =>
|
|
26
|
-
compareCanonicalStrings({ left: left.path, right: right.path }),
|
|
27
|
-
),
|
|
28
|
-
}))
|
|
29
|
-
.sort((left, right) =>
|
|
30
|
-
compareCanonicalStrings({ left: left.name, right: right.name }),
|
|
31
|
-
);
|
|
32
|
-
return createHash('sha256')
|
|
33
|
-
.update(JSON.stringify(canonicalSkills))
|
|
34
|
-
.digest('hex');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
10
|
export function resolveACPPrivateSessionDirectory({
|
|
38
11
|
sandboxHomeDir,
|
|
39
12
|
sessionWorkDir,
|
|
@@ -62,34 +35,30 @@ export async function materializeACPSkills({
|
|
|
62
35
|
rootDir,
|
|
63
36
|
sessionWorkDir,
|
|
64
37
|
skills,
|
|
65
|
-
shouldMaterialize,
|
|
66
38
|
abortSignal,
|
|
67
39
|
}: {
|
|
68
40
|
sandbox: Experimental_SandboxSession;
|
|
69
41
|
rootDir: string;
|
|
70
42
|
sessionWorkDir: string;
|
|
71
43
|
skills: ReadonlyArray<HarnessV1Skill>;
|
|
72
|
-
shouldMaterialize: boolean;
|
|
73
44
|
abortSignal?: AbortSignal;
|
|
74
|
-
}): Promise<
|
|
45
|
+
}): Promise<WriteSkillsResult> {
|
|
75
46
|
validateACPSkills({ skills });
|
|
76
47
|
assertOutsideSessionWorkDir({ rootDir, sessionWorkDir });
|
|
77
48
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
});
|
|
92
|
-
}
|
|
49
|
+
return writeSkills({
|
|
50
|
+
sandbox,
|
|
51
|
+
rootDir,
|
|
52
|
+
skills,
|
|
53
|
+
abortSignal,
|
|
54
|
+
skillNamePattern: ACP_SKILL_NAME_PATTERN,
|
|
55
|
+
invalidSkillNameMessage: ({ name }) =>
|
|
56
|
+
`Invalid ACP skill name ${JSON.stringify(name)}: expected a kebab-case slug.`,
|
|
57
|
+
invalidSkillFilePathMessage: ({ skillName, filePath }) =>
|
|
58
|
+
`Invalid ACP skill file path ${JSON.stringify(filePath)} for skill ${JSON.stringify(
|
|
59
|
+
skillName,
|
|
60
|
+
)}: expected a relative POSIX path without traversal.`,
|
|
61
|
+
});
|
|
93
62
|
}
|
|
94
63
|
|
|
95
64
|
export function resolveACPSkillsDirectory({
|
|
@@ -225,13 +194,3 @@ function assertOutsideSessionWorkDir({
|
|
|
225
194
|
);
|
|
226
195
|
}
|
|
227
196
|
}
|
|
228
|
-
|
|
229
|
-
function compareCanonicalStrings({
|
|
230
|
-
left,
|
|
231
|
-
right,
|
|
232
|
-
}: {
|
|
233
|
-
left: string;
|
|
234
|
-
right: string;
|
|
235
|
-
}): number {
|
|
236
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
237
|
-
}
|
|
@@ -59,6 +59,8 @@ export type ACPBridgeConfiguration = {
|
|
|
59
59
|
readonly providerAuthentication?: ACPResolvedProviderAuthentication;
|
|
60
60
|
readonly providerEnvironment?: Readonly<Record<string, string>>;
|
|
61
61
|
readonly sessionMeta?: Readonly<Record<string, ACPSerializableValue>>;
|
|
62
|
+
readonly implementationArgs?: ReadonlyArray<string>;
|
|
63
|
+
readonly implementationEnv?: Readonly<Record<string, string>>;
|
|
62
64
|
};
|
|
63
65
|
|
|
64
66
|
const bridgeConfigurationSchema: z.ZodType<ACPBridgeConfiguration> = z.object({
|
|
@@ -66,6 +68,8 @@ const bridgeConfigurationSchema: z.ZodType<ACPBridgeConfiguration> = z.object({
|
|
|
66
68
|
providerAuthentication: providerAuthenticationSchema.optional(),
|
|
67
69
|
providerEnvironment: z.record(z.string(), z.string()).optional(),
|
|
68
70
|
sessionMeta: serializableRecordSchema.optional(),
|
|
71
|
+
implementationArgs: z.array(z.string()).optional(),
|
|
72
|
+
implementationEnv: z.record(z.string(), z.string()).optional(),
|
|
69
73
|
});
|
|
70
74
|
|
|
71
75
|
export function createACPBridgeEnvironment({
|
|
@@ -73,6 +77,8 @@ export function createACPBridgeEnvironment({
|
|
|
73
77
|
providerAuthentication,
|
|
74
78
|
providerEnvironment,
|
|
75
79
|
sessionMeta,
|
|
80
|
+
implementationArgs,
|
|
81
|
+
implementationEnv,
|
|
76
82
|
}: ACPBridgeConfiguration): Record<string, string> {
|
|
77
83
|
return {
|
|
78
84
|
[ACP_BRIDGE_CONFIGURATION_ENV]: JSON.stringify({
|
|
@@ -80,6 +86,8 @@ export function createACPBridgeEnvironment({
|
|
|
80
86
|
...(providerAuthentication == null ? {} : { providerAuthentication }),
|
|
81
87
|
...(providerEnvironment == null ? {} : { providerEnvironment }),
|
|
82
88
|
...(sessionMeta == null ? {} : { sessionMeta }),
|
|
89
|
+
...(implementationArgs == null ? {} : { implementationArgs }),
|
|
90
|
+
...(implementationEnv == null ? {} : { implementationEnv }),
|
|
83
91
|
}),
|
|
84
92
|
};
|
|
85
93
|
}
|
package/src/v1/bridge/index.ts
CHANGED
|
@@ -413,15 +413,18 @@ async function ensureSession({
|
|
|
413
413
|
instructions: start.instructions,
|
|
414
414
|
instructionMapping: start.instructionMapping,
|
|
415
415
|
sessionMeta: bridgeConfiguration.sessionMeta,
|
|
416
|
-
environment:
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
416
|
+
environment: {
|
|
417
|
+
...createChildEnvironment({
|
|
418
|
+
launchEnv,
|
|
419
|
+
implementationDir,
|
|
420
|
+
privateHome: implementation.privateHome,
|
|
421
|
+
}),
|
|
422
|
+
...bridgeConfiguration.implementationEnv,
|
|
423
|
+
},
|
|
421
424
|
});
|
|
422
425
|
child = spawn(
|
|
423
426
|
`${implementationDir}/${implementation.executablePath}`,
|
|
424
|
-
[...implementation.args],
|
|
427
|
+
[...(bridgeConfiguration.implementationArgs ?? implementation.args)],
|
|
425
428
|
{
|
|
426
429
|
cwd: workDir,
|
|
427
430
|
env: instructionConfiguration.environment,
|