@ai-sdk/harness-acp 1.0.1 → 1.0.3
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 +19 -0
- package/README.md +13 -0
- package/dist/bridge/host-tool-mcp.mjs +1 -1
- package/dist/bridge/index.mjs +176 -18
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +132 -1
- package/dist/index.js +130 -13
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/acp-harness.ts +20 -0
- package/src/acp-tool-call.ts +124 -0
- package/src/index.ts +19 -0
- package/src/v1/acp-v1-bridge-protocol.ts +38 -2
- package/src/v1/acp-v1-harness.ts +116 -4
- package/src/v1/acp-v1-settings.ts +29 -0
- package/src/v1/acp-v1-turn-start-config.ts +7 -0
- package/src/v1/bridge/create-emit-stream-event.ts +8 -1
- package/src/v1/bridge/index.ts +61 -6
- package/src/v1/bridge/instruction-mapping.ts +132 -0
- package/src/v1/bridge/stream-translator.ts +62 -5
- package/src/v1/index.ts +1 -0
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from './acp-v1-bridge-protocol';
|
|
11
11
|
import type { ACPTextContentBlock } from './acp-v1-prompt';
|
|
12
12
|
import type {
|
|
13
|
+
ACPInstructionMapping,
|
|
13
14
|
ACPPermissionModeMapping,
|
|
14
15
|
ACPSerializableValue,
|
|
15
16
|
} from './acp-v1-settings';
|
|
@@ -20,18 +21,22 @@ export function createACPTurnStartConfig({
|
|
|
20
21
|
builtinTools,
|
|
21
22
|
permissionMode,
|
|
22
23
|
permissionModeMapping,
|
|
24
|
+
mcpServers,
|
|
23
25
|
debug,
|
|
24
26
|
authenticationProfile,
|
|
25
27
|
sessionMeta,
|
|
28
|
+
instructionMapping,
|
|
26
29
|
}: {
|
|
27
30
|
prompt: ReadonlyArray<ACPTextContentBlock>;
|
|
28
31
|
tools: ReadonlyArray<HarnessV1ToolSpec>;
|
|
29
32
|
builtinTools: ReadonlyArray<ACPBuiltinToolMapping>;
|
|
30
33
|
permissionMode: NonNullable<StartMessage['permissionMode']>;
|
|
31
34
|
permissionModeMapping: ACPPermissionModeMapping | undefined;
|
|
35
|
+
mcpServers: Record<string, unknown> | undefined;
|
|
32
36
|
debug: HarnessV1DebugConfig | undefined;
|
|
33
37
|
authenticationProfile: ACPAuthenticationProfileIdentity;
|
|
34
38
|
sessionMeta: Readonly<Record<string, ACPSerializableValue>> | undefined;
|
|
39
|
+
instructionMapping: ACPInstructionMapping | undefined;
|
|
35
40
|
}): ACPTurnStartConfig {
|
|
36
41
|
return {
|
|
37
42
|
version: 1,
|
|
@@ -40,8 +45,10 @@ export function createACPTurnStartConfig({
|
|
|
40
45
|
stableStringify({
|
|
41
46
|
authenticationProfile,
|
|
42
47
|
sessionMeta: sessionMeta ?? null,
|
|
48
|
+
...(instructionMapping == null ? {} : { instructionMapping }),
|
|
43
49
|
builtinTools,
|
|
44
50
|
permissionModeMapping: permissionModeMapping ?? null,
|
|
51
|
+
mcpServers: mcpServers ?? null,
|
|
45
52
|
}),
|
|
46
53
|
)
|
|
47
54
|
.digest('hex'),
|
|
@@ -6,17 +6,20 @@ import type {
|
|
|
6
6
|
HarnessV1BridgeToolWire,
|
|
7
7
|
HarnessV1StreamPart,
|
|
8
8
|
} from '@ai-sdk/harness';
|
|
9
|
+
import type { ACPToolCall } from '../../acp-tool-call';
|
|
9
10
|
import type { ACPBuiltinToolMapping } from '../acp-v1-bridge-protocol';
|
|
10
11
|
import { createACPStreamTranslator } from './stream-translator';
|
|
11
12
|
import { createHostToolCorrelation } from './host-tool-correlation';
|
|
12
13
|
|
|
13
14
|
export function createEmitStreamEvent({
|
|
14
15
|
emit,
|
|
16
|
+
emitToolCallCandidate,
|
|
15
17
|
builtinTools,
|
|
16
18
|
hostToolServerName,
|
|
17
19
|
hostTools,
|
|
18
20
|
}: {
|
|
19
21
|
emit: (event: HarnessV1StreamPart) => void;
|
|
22
|
+
emitToolCallCandidate: (options: { toolCall: ACPToolCall }) => void;
|
|
20
23
|
builtinTools: ReadonlyArray<ACPBuiltinToolMapping>;
|
|
21
24
|
hostToolServerName: string;
|
|
22
25
|
hostTools: ReadonlyArray<HarnessV1BridgeToolWire>;
|
|
@@ -49,7 +52,11 @@ export function createEmitStreamEvent({
|
|
|
49
52
|
}) => void;
|
|
50
53
|
removeHostToolCorrelationInvocation: (options: { token: string }) => void;
|
|
51
54
|
} {
|
|
52
|
-
const translator = createACPStreamTranslator({
|
|
55
|
+
const translator = createACPStreamTranslator({
|
|
56
|
+
emit,
|
|
57
|
+
emitToolCallCandidate,
|
|
58
|
+
builtinTools,
|
|
59
|
+
});
|
|
53
60
|
const correlation = createHostToolCorrelation({
|
|
54
61
|
emitSemanticUpdate: ({ message, rawUpdate }) => {
|
|
55
62
|
translator.update({
|
package/src/v1/bridge/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
} from './acp-diagnostics';
|
|
33
33
|
import { monitorACPAgentStderr } from './agent-stderr-monitor';
|
|
34
34
|
import { createEmitStreamEvent } from './create-emit-stream-event';
|
|
35
|
+
import { resolveACPInstructionConfiguration } from './instruction-mapping';
|
|
35
36
|
import {
|
|
36
37
|
startHostToolRelay,
|
|
37
38
|
type HostToolRelay,
|
|
@@ -230,6 +231,12 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
|
|
|
230
231
|
turn.emit({ type: 'stream-start' });
|
|
231
232
|
const emitStreamEvent = createEmitStreamEvent({
|
|
232
233
|
emit: event => turn.emit(event as BridgeEvent),
|
|
234
|
+
emitToolCallCandidate: ({ toolCall }) => {
|
|
235
|
+
turn.emit({
|
|
236
|
+
type: 'acp-tool-call-candidate',
|
|
237
|
+
toolCall,
|
|
238
|
+
});
|
|
239
|
+
},
|
|
233
240
|
builtinTools: start.builtinTools,
|
|
234
241
|
hostToolServerName: HOST_TOOL_MCP_SERVER_NAME,
|
|
235
242
|
hostTools: start.tools ?? [],
|
|
@@ -320,8 +327,10 @@ async function ensureSession({
|
|
|
320
327
|
authentication: bridgeConfiguration.authentication,
|
|
321
328
|
providerAuthentication: bridgeConfiguration.providerAuthentication,
|
|
322
329
|
sessionMeta: bridgeConfiguration.sessionMeta,
|
|
330
|
+
instructionMapping: start.instructionMapping,
|
|
323
331
|
permissionMode: start.permissionMode,
|
|
324
332
|
permissionModeMapping: start.permissionModeMapping,
|
|
333
|
+
mcpServers: start.mcpServers,
|
|
325
334
|
});
|
|
326
335
|
if (session != null) {
|
|
327
336
|
if (catalogRefreshError != null) throw catalogRefreshError;
|
|
@@ -360,12 +369,18 @@ async function ensureSession({
|
|
|
360
369
|
providerAuthentication: bridgeConfiguration.providerAuthentication,
|
|
361
370
|
gateway,
|
|
362
371
|
});
|
|
372
|
+
const instructionConfiguration = await resolveACPInstructionConfiguration({
|
|
373
|
+
instructions: start.instructions,
|
|
374
|
+
instructionMapping: start.instructionMapping,
|
|
375
|
+
sessionMeta: bridgeConfiguration.sessionMeta,
|
|
376
|
+
environment: createChildEnvironment({ launchEnv }),
|
|
377
|
+
});
|
|
363
378
|
child = spawn(
|
|
364
379
|
`${implementationDir}/node_modules/.bin/${implementation.executable}`,
|
|
365
380
|
[...implementation.args],
|
|
366
381
|
{
|
|
367
382
|
cwd: workDir,
|
|
368
|
-
env:
|
|
383
|
+
env: instructionConfiguration.environment,
|
|
369
384
|
shell: false,
|
|
370
385
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
371
386
|
},
|
|
@@ -437,6 +452,10 @@ async function ensureSession({
|
|
|
437
452
|
authentication,
|
|
438
453
|
});
|
|
439
454
|
}
|
|
455
|
+
const externalMcpServers = createExternalMcpServers({
|
|
456
|
+
mcpServers: start.mcpServers,
|
|
457
|
+
initialization,
|
|
458
|
+
});
|
|
440
459
|
const tools = start.tools ?? [];
|
|
441
460
|
const catalogPath = `${bridgeStateDir}/host-tools.json`;
|
|
442
461
|
await writeFile(catalogPath, JSON.stringify(tools), { mode: 0o600 });
|
|
@@ -446,6 +465,7 @@ async function ensureSession({
|
|
|
446
465
|
});
|
|
447
466
|
|
|
448
467
|
const mcpServers: acp.McpServer[] = [
|
|
468
|
+
...externalMcpServers,
|
|
449
469
|
{
|
|
450
470
|
name: HOST_TOOL_MCP_SERVER_NAME,
|
|
451
471
|
command: process.execPath,
|
|
@@ -478,9 +498,9 @@ async function ensureSession({
|
|
|
478
498
|
sessionId: recoveredSessionId,
|
|
479
499
|
cwd: workDir,
|
|
480
500
|
mcpServers,
|
|
481
|
-
...(
|
|
501
|
+
...(instructionConfiguration.sessionMeta == null
|
|
482
502
|
? {}
|
|
483
|
-
: { _meta:
|
|
503
|
+
: { _meta: instructionConfiguration.sessionMeta }),
|
|
484
504
|
});
|
|
485
505
|
createdSession = createACPRecoveredSession({
|
|
486
506
|
agent: connection.agent,
|
|
@@ -497,7 +517,7 @@ async function ensureSession({
|
|
|
497
517
|
sessionId: recoveredSessionId,
|
|
498
518
|
cwd: workDir,
|
|
499
519
|
mcpServers,
|
|
500
|
-
meta:
|
|
520
|
+
meta: instructionConfiguration.sessionMeta,
|
|
501
521
|
harnessId: bridgeType,
|
|
502
522
|
setHistoricalUpdatesSuppressed: ({ suppressed }) => {
|
|
503
523
|
historicalUpdatesSuppressed = suppressed;
|
|
@@ -518,9 +538,9 @@ async function ensureSession({
|
|
|
518
538
|
.buildSession({
|
|
519
539
|
cwd: workDir,
|
|
520
540
|
mcpServers,
|
|
521
|
-
...(
|
|
541
|
+
...(instructionConfiguration.sessionMeta == null
|
|
522
542
|
? {}
|
|
523
|
-
: { _meta:
|
|
543
|
+
: { _meta: instructionConfiguration.sessionMeta }),
|
|
524
544
|
})
|
|
525
545
|
.start();
|
|
526
546
|
}
|
|
@@ -557,6 +577,41 @@ async function ensureSession({
|
|
|
557
577
|
sessionConfigurationFingerprint = fingerprint;
|
|
558
578
|
}
|
|
559
579
|
|
|
580
|
+
function createExternalMcpServers({
|
|
581
|
+
mcpServers,
|
|
582
|
+
initialization,
|
|
583
|
+
}: {
|
|
584
|
+
mcpServers: Record<string, unknown> | undefined;
|
|
585
|
+
initialization: acp.InitializeResponse;
|
|
586
|
+
}): acp.McpServer[] {
|
|
587
|
+
if (mcpServers == null) return [];
|
|
588
|
+
return Object.entries(mcpServers).map(([name, value]) => {
|
|
589
|
+
if (!isRecord(value)) {
|
|
590
|
+
throw new Error(
|
|
591
|
+
`ACP MCP server ${JSON.stringify(name)} must be configured with an object value.`,
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
if (value.type === 'acp') {
|
|
595
|
+
throw new HarnessBridgeCapabilityUnsupportedError({
|
|
596
|
+
harnessId: bridgeType,
|
|
597
|
+
message:
|
|
598
|
+
'ACP-transport MCP servers require client-side mcp/connect handling, which this harness does not provide.',
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
const mcpCapabilities = initialization.agentCapabilities?.mcpCapabilities;
|
|
602
|
+
if (
|
|
603
|
+
(value.type === 'http' && mcpCapabilities?.http !== true) ||
|
|
604
|
+
(value.type === 'sse' && mcpCapabilities?.sse !== true)
|
|
605
|
+
) {
|
|
606
|
+
throw new HarnessBridgeCapabilityUnsupportedError({
|
|
607
|
+
harnessId: bridgeType,
|
|
608
|
+
message: `The ACP agent does not advertise support for ${value.type.toUpperCase()} MCP servers.`,
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
return { ...value, name } as acp.McpServer;
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
|
|
560
615
|
function unknownUsage() {
|
|
561
616
|
return {
|
|
562
617
|
inputTokens: {
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import type {
|
|
3
|
+
ACPInstructionMapping,
|
|
4
|
+
ACPSerializableValue,
|
|
5
|
+
} from '../acp-v1-settings';
|
|
6
|
+
|
|
7
|
+
const UNSAFE_PATH_SEGMENTS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
8
|
+
|
|
9
|
+
const serializableRecordSchema: z.ZodType<
|
|
10
|
+
Readonly<Record<string, ACPSerializableValue>>
|
|
11
|
+
> = z.record(z.string(), z.json());
|
|
12
|
+
|
|
13
|
+
export async function resolveACPInstructionConfiguration({
|
|
14
|
+
instructions,
|
|
15
|
+
instructionMapping,
|
|
16
|
+
sessionMeta,
|
|
17
|
+
environment,
|
|
18
|
+
}: {
|
|
19
|
+
instructions: string | undefined;
|
|
20
|
+
instructionMapping: ACPInstructionMapping | undefined;
|
|
21
|
+
sessionMeta: Readonly<Record<string, ACPSerializableValue>> | undefined;
|
|
22
|
+
environment: Readonly<Record<string, string | undefined>>;
|
|
23
|
+
}): Promise<{
|
|
24
|
+
sessionMeta: Readonly<Record<string, ACPSerializableValue>> | undefined;
|
|
25
|
+
environment: Record<string, string | undefined>;
|
|
26
|
+
}> {
|
|
27
|
+
const resolvedEnvironment = { ...environment };
|
|
28
|
+
if (
|
|
29
|
+
instructionMapping == null ||
|
|
30
|
+
instructions == null ||
|
|
31
|
+
instructions.length === 0
|
|
32
|
+
) {
|
|
33
|
+
return { sessionMeta, environment: resolvedEnvironment };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
assertSafePath({ path: instructionMapping.path });
|
|
37
|
+
|
|
38
|
+
if (instructionMapping.type === 'session-meta') {
|
|
39
|
+
return {
|
|
40
|
+
sessionMeta: setStringAtPath({
|
|
41
|
+
record: sessionMeta ?? {},
|
|
42
|
+
path: instructionMapping.path,
|
|
43
|
+
value: instructions,
|
|
44
|
+
}),
|
|
45
|
+
environment: resolvedEnvironment,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const serialized = resolvedEnvironment[instructionMapping.variable];
|
|
50
|
+
let configuration: Readonly<Record<string, ACPSerializableValue>> = {};
|
|
51
|
+
if (serialized != null && serialized.length > 0) {
|
|
52
|
+
configuration = parseSerializableRecord({
|
|
53
|
+
serialized,
|
|
54
|
+
variable: instructionMapping.variable,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
resolvedEnvironment[instructionMapping.variable] = JSON.stringify(
|
|
59
|
+
setStringAtPath({
|
|
60
|
+
record: configuration,
|
|
61
|
+
path: instructionMapping.path,
|
|
62
|
+
value: instructions,
|
|
63
|
+
}),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return { sessionMeta, environment: resolvedEnvironment };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/*
|
|
70
|
+
* The bridge runs in an isolated runtime whose dependencies are limited to the
|
|
71
|
+
* ones declared in this directory's package.json, so AI SDK JSON helpers are
|
|
72
|
+
* unavailable here. Schema validation plus a guarded `JSON.parse` provides the
|
|
73
|
+
* same guarantee: nothing but a validated serializable record escapes.
|
|
74
|
+
*/
|
|
75
|
+
function parseSerializableRecord({
|
|
76
|
+
serialized,
|
|
77
|
+
variable,
|
|
78
|
+
}: {
|
|
79
|
+
serialized: string;
|
|
80
|
+
variable: string;
|
|
81
|
+
}): Readonly<Record<string, ACPSerializableValue>> {
|
|
82
|
+
try {
|
|
83
|
+
const result = serializableRecordSchema.safeParse(JSON.parse(serialized));
|
|
84
|
+
if (result.success) return result.data;
|
|
85
|
+
} catch {}
|
|
86
|
+
throw new Error(
|
|
87
|
+
`ACP instruction mapping environment variable ${JSON.stringify(variable)} must contain a JSON object.`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function setStringAtPath({
|
|
92
|
+
record,
|
|
93
|
+
path,
|
|
94
|
+
value,
|
|
95
|
+
}: {
|
|
96
|
+
record: Readonly<Record<string, ACPSerializableValue>>;
|
|
97
|
+
path: ReadonlyArray<string>;
|
|
98
|
+
value: string;
|
|
99
|
+
}): Record<string, ACPSerializableValue> {
|
|
100
|
+
const [key, ...remainingPath] = path;
|
|
101
|
+
if (key == null) return { ...record };
|
|
102
|
+
return {
|
|
103
|
+
...record,
|
|
104
|
+
[key]:
|
|
105
|
+
remainingPath.length === 0
|
|
106
|
+
? value
|
|
107
|
+
: setStringAtPath({
|
|
108
|
+
record: isRecord(record[key]) ? record[key] : {},
|
|
109
|
+
path: remainingPath,
|
|
110
|
+
value,
|
|
111
|
+
}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function assertSafePath({ path }: { path: ReadonlyArray<string> }): void {
|
|
116
|
+
if (
|
|
117
|
+
path.length === 0 ||
|
|
118
|
+
path.some(
|
|
119
|
+
segment => segment.length === 0 || UNSAFE_PATH_SEGMENTS.has(segment),
|
|
120
|
+
)
|
|
121
|
+
) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
'ACP instruction mapping path must contain only safe, non-empty property names.',
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function isRecord(
|
|
129
|
+
value: ACPSerializableValue | undefined,
|
|
130
|
+
): value is Readonly<Record<string, ACPSerializableValue>> {
|
|
131
|
+
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
132
|
+
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { HarnessV1StreamPart } from '@ai-sdk/harness';
|
|
2
2
|
import type { PromptResponse, SessionUpdate } from '@agentclientprotocol/sdk';
|
|
3
|
+
import type {
|
|
4
|
+
ACPToolCall,
|
|
5
|
+
ACPToolCallStatus,
|
|
6
|
+
ACPToolKind,
|
|
7
|
+
} from '../../acp-tool-call';
|
|
3
8
|
import type { ACPBuiltinToolMapping } from '../acp-v1-bridge-protocol';
|
|
4
9
|
|
|
5
10
|
type HarnessUsage = Extract<
|
|
@@ -20,7 +25,6 @@ type ToolState = {
|
|
|
20
25
|
emittedResult: boolean;
|
|
21
26
|
toolName?: string;
|
|
22
27
|
nativeName?: string;
|
|
23
|
-
dynamic?: boolean;
|
|
24
28
|
};
|
|
25
29
|
|
|
26
30
|
export type ACPSessionUpdate = SessionUpdate;
|
|
@@ -28,9 +32,11 @@ export type ACPPromptResponse = PromptResponse;
|
|
|
28
32
|
|
|
29
33
|
export function createACPStreamTranslator({
|
|
30
34
|
emit,
|
|
35
|
+
emitToolCallCandidate,
|
|
31
36
|
builtinTools = [],
|
|
32
37
|
}: {
|
|
33
38
|
emit: (event: HarnessV1StreamPart) => void;
|
|
39
|
+
emitToolCallCandidate?: (options: { toolCall: ACPToolCall }) => void;
|
|
34
40
|
builtinTools?: ReadonlyArray<ACPBuiltinToolMapping>;
|
|
35
41
|
}): {
|
|
36
42
|
update: (
|
|
@@ -228,13 +234,12 @@ export function createACPStreamTranslator({
|
|
|
228
234
|
builtin.nativeName != null && builtin.toolName !== builtin.nativeName
|
|
229
235
|
? builtin.nativeName
|
|
230
236
|
: undefined;
|
|
231
|
-
state.dynamic = false;
|
|
232
237
|
} else {
|
|
233
238
|
state.toolName = createDynamicToolName({
|
|
234
239
|
programmaticName,
|
|
235
240
|
toolCallId: state.toolCallId,
|
|
236
241
|
});
|
|
237
|
-
state
|
|
242
|
+
emitToolCallCandidate?.({ toolCall: createACPToolCall({ state }) });
|
|
238
243
|
}
|
|
239
244
|
emit({
|
|
240
245
|
type: 'tool-call',
|
|
@@ -242,7 +247,6 @@ export function createACPStreamTranslator({
|
|
|
242
247
|
toolName: state.toolName,
|
|
243
248
|
input: stringifyToolInput({ input: state.values.rawInput }),
|
|
244
249
|
providerExecuted: true,
|
|
245
|
-
...(state.dynamic ? { dynamic: true } : {}),
|
|
246
250
|
...(state.nativeName == null ? {} : { nativeName: state.nativeName }),
|
|
247
251
|
});
|
|
248
252
|
state.emittedCall = true;
|
|
@@ -263,7 +267,6 @@ export function createACPStreamTranslator({
|
|
|
263
267
|
toolName: state.toolName!,
|
|
264
268
|
result: createToolResult({ state }),
|
|
265
269
|
...(state.values.status === 'failed' ? { isError: true } : {}),
|
|
266
|
-
...(state.dynamic ? { dynamic: true } : {}),
|
|
267
270
|
});
|
|
268
271
|
state.emittedResult = true;
|
|
269
272
|
pendingToolCallIds.delete(state.toolCallId);
|
|
@@ -697,6 +700,60 @@ function mergeToolUpdate({
|
|
|
697
700
|
if (programmaticName != null) state.values.name = programmaticName;
|
|
698
701
|
}
|
|
699
702
|
|
|
703
|
+
function createACPToolCall({ state }: { state: ToolState }): ACPToolCall {
|
|
704
|
+
const title = getStringProperty({
|
|
705
|
+
value: state.values,
|
|
706
|
+
property: 'title',
|
|
707
|
+
});
|
|
708
|
+
const kind = state.values.kind;
|
|
709
|
+
const status = state.values.status;
|
|
710
|
+
return {
|
|
711
|
+
toolCallId: state.toolCallId,
|
|
712
|
+
title: title ?? state.toolName ?? `Tool ${state.toolCallId}`,
|
|
713
|
+
...(isACPToolKind(kind) ? { kind } : {}),
|
|
714
|
+
...(isACPToolCallStatus(status) ? { status } : {}),
|
|
715
|
+
...(Array.isArray(state.values.content)
|
|
716
|
+
? { content: state.values.content as ACPToolCall['content'] }
|
|
717
|
+
: {}),
|
|
718
|
+
...(Array.isArray(state.values.locations)
|
|
719
|
+
? { locations: state.values.locations as ACPToolCall['locations'] }
|
|
720
|
+
: {}),
|
|
721
|
+
...(Object.prototype.hasOwnProperty.call(state.values, 'rawInput')
|
|
722
|
+
? { rawInput: state.values.rawInput }
|
|
723
|
+
: {}),
|
|
724
|
+
...(Object.prototype.hasOwnProperty.call(state.values, 'rawOutput')
|
|
725
|
+
? { rawOutput: state.values.rawOutput }
|
|
726
|
+
: {}),
|
|
727
|
+
...(state.values._meta === null || isRecord(state.values._meta)
|
|
728
|
+
? { _meta: state.values._meta }
|
|
729
|
+
: {}),
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function isACPToolKind(value: unknown): value is ACPToolKind {
|
|
734
|
+
return (
|
|
735
|
+
value === 'read' ||
|
|
736
|
+
value === 'edit' ||
|
|
737
|
+
value === 'delete' ||
|
|
738
|
+
value === 'move' ||
|
|
739
|
+
value === 'search' ||
|
|
740
|
+
value === 'execute' ||
|
|
741
|
+
value === 'think' ||
|
|
742
|
+
value === 'fetch' ||
|
|
743
|
+
value === 'switch_mode' ||
|
|
744
|
+
value === 'other'
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
function isACPToolCallStatus(value: unknown): value is ACPToolCallStatus {
|
|
749
|
+
return (
|
|
750
|
+
value === 'pending' ||
|
|
751
|
+
value === 'in_progress' ||
|
|
752
|
+
value === 'completed' ||
|
|
753
|
+
value === 'failed'
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
|
|
700
757
|
function emitFileChanges({
|
|
701
758
|
state,
|
|
702
759
|
emit,
|