@agen-ai/provider-acp 0.4.0 → 0.4.2
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/README.md +515 -12
- package/dist/capabilities.d.ts +3 -1
- package/dist/capabilities.js +5 -4
- package/dist/configuration/catalog.js +0 -2
- package/dist/configuration/errors.js +0 -1
- package/dist/connection/contracts.d.ts +24 -1
- package/dist/connection/errors.js +0 -1
- package/dist/connection/initialize.d.ts +7 -3
- package/dist/connection/initialize.js +64 -19
- package/dist/connection/negotiation.d.ts +3 -1
- package/dist/connection/negotiation.js +22 -1
- package/dist/contracts.d.ts +3 -0
- package/dist/driver.d.ts +1 -1
- package/dist/driver.js +6 -39
- package/dist/driverFactory.d.ts +8 -0
- package/dist/driverFactory.js +48 -0
- package/dist/host/interactions.d.ts +6 -1
- package/dist/host/interactions.js +42 -17
- package/dist/host/leases.js +0 -1
- package/dist/host/router.d.ts +3 -2
- package/dist/host/router.js +23 -21
- package/dist/host/terminals.js +0 -2
- package/dist/index.d.ts +1 -0
- package/dist/instance.d.ts +7 -2
- package/dist/instance.js +113 -28
- package/dist/node/contracts.d.ts +6 -0
- package/dist/node/execution/errors.d.ts +11 -0
- package/dist/node/execution/errors.js +17 -0
- package/dist/node/execution/owner.d.ts +34 -0
- package/dist/node/execution/owner.js +162 -0
- package/dist/node/execution/types.d.ts +20 -0
- package/dist/node/execution/types.js +0 -0
- package/dist/node/index.d.ts +3 -1
- package/dist/node/index.js +16 -6
- package/dist/node/launch.d.ts +5 -8
- package/dist/node/launch.js +13 -1
- package/dist/permissions/contracts.d.ts +22 -0
- package/dist/permissions/contracts.js +0 -0
- package/dist/permissions/mapping.d.ts +13 -0
- package/dist/permissions/mapping.js +99 -0
- package/dist/permissions/selection.d.ts +5 -0
- package/dist/permissions/selection.js +96 -0
- package/dist/session/continuation.d.ts +33 -0
- package/dist/session/continuation.js +185 -0
- package/dist/session/create.d.ts +7 -1
- package/dist/session/create.js +3 -1
- package/dist/session/descendants.d.ts +29 -0
- package/dist/session/descendants.js +97 -0
- package/dist/session/errors.js +0 -1
- package/dist/session/mapping.d.ts +6 -0
- package/dist/session/mapping.js +5 -26
- package/dist/session/native.d.ts +1 -0
- package/dist/session/native.js +8 -0
- package/dist/session/opening.d.ts +1 -0
- package/dist/session/opening.js +3 -1
- package/dist/session/queue.js +0 -1
- package/dist/session/requests/elicitation.d.ts +1 -1
- package/dist/session/requests/elicitation.js +1 -1
- package/dist/session/requests/forms.js +0 -2
- package/dist/session/requests/grokWithdrawals.d.ts +28 -0
- package/dist/session/requests/grokWithdrawals.js +135 -0
- package/dist/session/requests/pending.d.ts +17 -4
- package/dist/session/requests/pending.js +63 -8
- package/dist/session/requests/resolution.js +4 -2
- package/dist/session/resources.js +5 -3
- package/dist/session/session.d.ts +12 -1
- package/dist/session/session.js +197 -47
- package/dist/session/turn.d.ts +6 -1
- package/dist/sessionInteractions/broker.d.ts +50 -0
- package/dist/sessionInteractions/broker.js +193 -0
- package/dist/sessionInteractions/contracts.d.ts +3 -0
- package/dist/sessionInteractions/contracts.js +0 -0
- package/dist/sessionInteractions/extensions.d.ts +4 -0
- package/dist/sessionInteractions/extensions.js +37 -0
- package/dist/sessionInteractions/grokFolderTrust.d.ts +42 -0
- package/dist/sessionInteractions/grokFolderTrust.js +106 -0
- package/dist/sessionInteractions/grokPlanReview.d.ts +16 -0
- package/dist/sessionInteractions/grokPlanReview.js +63 -0
- package/dist/sessionInteractions/request.d.ts +46 -0
- package/dist/sessionInteractions/request.js +173 -0
- package/package.json +4 -4
|
@@ -1,16 +1,37 @@
|
|
|
1
1
|
import type { AcpClient, AcpJsonObject, AcpJsonValue, AcpNotificationContext, AcpReverseRequestContext, AcpRequestOptions } from '@agen-ai/acp-transport';
|
|
2
2
|
import type { AcpHostInteractionScope } from '../host/contracts.js';
|
|
3
3
|
import type { AcpBoundSession } from '../host/router.js';
|
|
4
|
+
import type { SessionInteractionBroker } from '../sessionInteractions/broker.js';
|
|
5
|
+
import type { AcpConnectionFactory } from '../contracts.js';
|
|
6
|
+
import type { NodeAcpExecutionOwner } from '../node/execution/owner.js';
|
|
7
|
+
export type AcpConnectionSource = Readonly<{
|
|
8
|
+
kind: 'factory';
|
|
9
|
+
create: AcpConnectionFactory;
|
|
10
|
+
}> | Readonly<{
|
|
11
|
+
kind: 'reserved';
|
|
12
|
+
reserve: () => NodeAcpExecutionOwner;
|
|
13
|
+
}>;
|
|
14
|
+
export type AcpConnectionAcquisition = Readonly<{
|
|
15
|
+
kind: 'factory';
|
|
16
|
+
create: AcpConnectionFactory;
|
|
17
|
+
}> | Readonly<{
|
|
18
|
+
kind: 'reserved';
|
|
19
|
+
owner: NodeAcpExecutionOwner;
|
|
20
|
+
}>;
|
|
4
21
|
export interface AcpNegotiatedAgent {
|
|
5
22
|
readonly protocolVersion: 1;
|
|
6
23
|
readonly agentInfo?: Readonly<{
|
|
7
24
|
name: string;
|
|
8
25
|
version: string;
|
|
9
26
|
}>;
|
|
27
|
+
/** Bounded compatibility observation retained only by the selected Grok permission profile. */
|
|
28
|
+
readonly agentVersion?: string;
|
|
10
29
|
readonly agentCapabilities: AcpJsonObject;
|
|
11
30
|
readonly authMethods: readonly AcpJsonValue[];
|
|
12
31
|
}
|
|
13
32
|
export interface AcpSessionIngress {
|
|
33
|
+
readonly joinCallbacks: () => Promise<void>;
|
|
34
|
+
readonly onExtensionNotification?: (input: AcpNotificationContext) => void | Promise<void>;
|
|
14
35
|
readonly onNotification: (input: AcpNotificationContext) => void | Promise<void>;
|
|
15
36
|
readonly onPermissionRequest: (input: AcpReverseRequestContext) => Promise<AcpJsonValue>;
|
|
16
37
|
readonly onElicitationRequest: (input: AcpReverseRequestContext) => Promise<AcpJsonValue>;
|
|
@@ -19,10 +40,12 @@ export interface AcpInitializedConnection {
|
|
|
19
40
|
readonly client: AcpClient;
|
|
20
41
|
readonly negotiated: AcpNegotiatedAgent;
|
|
21
42
|
readonly stopped: AbortSignal;
|
|
43
|
+
readonly execution?: NodeAcpExecutionOwner;
|
|
44
|
+
readonly sessionInteractions?: SessionInteractionBroker;
|
|
22
45
|
readonly attachSession: (ingress: AcpSessionIngress) => () => void;
|
|
23
46
|
readonly bindSession: (binding: AcpBoundSession) => void;
|
|
24
47
|
readonly requestWithScope: (method: string, params: AcpJsonValue, scope: AcpHostInteractionScope, options?: AcpRequestOptions) => Promise<AcpJsonValue>;
|
|
25
|
-
readonly hostElicitation?: (context: AcpReverseRequestContext) => Promise<AcpJsonValue>;
|
|
48
|
+
readonly hostElicitation?: (context: AcpReverseRequestContext, observeCallback?: (work: Promise<void>) => void) => Promise<AcpJsonValue>;
|
|
26
49
|
readonly close: () => Promise<void>;
|
|
27
50
|
}
|
|
28
51
|
//# sourceMappingURL=contracts.d.ts.map
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { type AcpUnknownExtensionPolicy } from '@agen-ai/acp-transport';
|
|
2
|
-
import type {
|
|
2
|
+
import type { AcpConnectionFactoryContext } from '../contracts.js';
|
|
3
3
|
import type { AcpProviderLimits } from '../limits.js';
|
|
4
|
-
import type { AcpInitializedConnection } from './contracts.js';
|
|
4
|
+
import type { AcpConnectionAcquisition, AcpInitializedConnection } from './contracts.js';
|
|
5
5
|
import type { AcpAuthenticationMethods, AcpHostFacilities } from '../host/contracts.js';
|
|
6
|
+
import type { AcpSessionInteractionSelection } from '../sessionInteractions/contracts.js';
|
|
7
|
+
import type { AcpPermissionMappingSelection } from '../permissions/contracts.js';
|
|
6
8
|
export declare function initializeAcpConnection(input: {
|
|
7
|
-
readonly
|
|
9
|
+
readonly source: AcpConnectionAcquisition;
|
|
8
10
|
readonly unknownExtensionPolicy: AcpUnknownExtensionPolicy;
|
|
9
11
|
readonly context: AcpConnectionFactoryContext;
|
|
10
12
|
readonly limits: AcpProviderLimits;
|
|
@@ -12,5 +14,7 @@ export declare function initializeAcpConnection(input: {
|
|
|
12
14
|
readonly host?: AcpHostFacilities;
|
|
13
15
|
readonly authentication?: AcpAuthenticationMethods;
|
|
14
16
|
readonly configuration?: 'managed' | 'selectable';
|
|
17
|
+
readonly sessionInteractions?: AcpSessionInteractionSelection;
|
|
18
|
+
readonly permissionMapping?: AcpPermissionMappingSelection;
|
|
15
19
|
}): Promise<AcpInitializedConnection>;
|
|
16
20
|
//# sourceMappingURL=initialize.d.ts.map
|
|
@@ -8,6 +8,11 @@ import { AcpProviderConnectionError } from "./errors.js";
|
|
|
8
8
|
import { acquireAcpLease, releaseOwnedLease } from "./acquire.js";
|
|
9
9
|
import { readNegotiatedAgent } from "./negotiation.js";
|
|
10
10
|
import { AcpHostRouter } from "../host/router.js";
|
|
11
|
+
import { SessionInteractionBroker } from "../sessionInteractions/broker.js";
|
|
12
|
+
import { createSessionInteractionExtensionRegistry } from "../sessionInteractions/extensions.js";
|
|
13
|
+
import { GROK_FOLDER_TRUST_CAPABILITY_META, GROK_FOLDER_TRUST_METHOD } from "../sessionInteractions/grokFolderTrust.js";
|
|
14
|
+
import { awaitHost } from "../host/lifecycle.js";
|
|
15
|
+
import { GROK_PLAN_REVIEW_METHOD, GROK_QUEUE_METHOD, GROK_SESSION_NOTIFICATION_METHOD } from "../sessionInteractions/grokPlanReview.js";
|
|
11
16
|
async function initializeAcpConnection(input) {
|
|
12
17
|
throwIfAgentOperationAborted(input.context.signal);
|
|
13
18
|
const initialization = new AbortController();
|
|
@@ -18,11 +23,14 @@ async function initializeAcpConnection(input) {
|
|
|
18
23
|
"ACP connection acquisition or initialization exceeded its deadline."
|
|
19
24
|
)), input.limits.initializationTimeoutMs);
|
|
20
25
|
let lease;
|
|
26
|
+
const execution = input.source.kind === "reserved" ? input.source.owner : void 0;
|
|
21
27
|
try {
|
|
22
|
-
|
|
28
|
+
const context = { ...input.context, signal: initialization.signal };
|
|
29
|
+
lease = input.source.kind === "factory" ? await acquireAcpLease(input.source.create, context) : await awaitHost(input.source.owner.start(context), initialization.signal);
|
|
23
30
|
try {
|
|
24
31
|
validateLease(lease, input.usedStreams);
|
|
25
32
|
} catch (cause) {
|
|
33
|
+
if (execution) throw cause;
|
|
26
34
|
try {
|
|
27
35
|
await releaseOwnedLease(lease);
|
|
28
36
|
} catch (cleanupError) {
|
|
@@ -33,22 +41,32 @@ async function initializeAcpConnection(input) {
|
|
|
33
41
|
} catch (cause) {
|
|
34
42
|
clearTimeout(timeout);
|
|
35
43
|
input.context.signal?.removeEventListener("abort", onInitializationAbort);
|
|
44
|
+
if (execution) {
|
|
45
|
+
try {
|
|
46
|
+
await execution.cleanup();
|
|
47
|
+
} catch (cleanupError) {
|
|
48
|
+
throw execution.retainedError(cause, cleanupError);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
36
51
|
throw cause;
|
|
37
52
|
}
|
|
38
53
|
const stopped = new AbortController();
|
|
39
54
|
let client;
|
|
40
55
|
let ingress;
|
|
56
|
+
let joinSessionCallbacks;
|
|
41
57
|
let attached = false;
|
|
42
58
|
let closePromise;
|
|
43
59
|
let releasePromise;
|
|
44
60
|
let released = false;
|
|
61
|
+
let sessionInteractions;
|
|
45
62
|
const host = input.host ?? {};
|
|
46
63
|
const router = new AcpHostRouter({
|
|
47
64
|
instanceId: input.context.instanceId,
|
|
48
65
|
host,
|
|
49
66
|
limits: input.limits,
|
|
50
67
|
stopped: stopped.signal,
|
|
51
|
-
ingress: () => ingress
|
|
68
|
+
ingress: () => ingress,
|
|
69
|
+
...input.sessionInteractions ? { humanTimeoutMs: () => sessionInteractions?.humanTimeoutMs() } : {}
|
|
52
70
|
});
|
|
53
71
|
const release = () => {
|
|
54
72
|
if (released) return Promise.resolve();
|
|
@@ -63,11 +81,16 @@ async function initializeAcpConnection(input) {
|
|
|
63
81
|
const stop = (reason) => {
|
|
64
82
|
if (!stopped.signal.aborted) stopped.abort(reason);
|
|
65
83
|
};
|
|
66
|
-
const
|
|
84
|
+
const closeActual = () => {
|
|
67
85
|
if (!closePromise) {
|
|
68
86
|
closePromise = Promise.resolve().then(async () => {
|
|
69
87
|
try {
|
|
70
|
-
const
|
|
88
|
+
const stopping = [router.close(), ...client ? [client.dispose()] : [], release()];
|
|
89
|
+
const cleanup = await Promise.allSettled([
|
|
90
|
+
...stopping,
|
|
91
|
+
...joinSessionCallbacks ? [joinSessionCallbacks()] : [],
|
|
92
|
+
...sessionInteractions ? [sessionInteractions.join()] : []
|
|
93
|
+
]);
|
|
71
94
|
const failures = cleanup.filter((result) => result.status === "rejected");
|
|
72
95
|
if (failures.length) throw new AggregateError(failures.map((result) => result.reason), "ACP connection cleanup failed.");
|
|
73
96
|
} finally {
|
|
@@ -82,21 +105,32 @@ async function initializeAcpConnection(input) {
|
|
|
82
105
|
closePromise = void 0;
|
|
83
106
|
throw error;
|
|
84
107
|
});
|
|
108
|
+
sessionInteractions?.stop({ kind: "session_closed" });
|
|
85
109
|
stop(new AcpProviderConnectionError("acp_provider_connection_closed", "The ACP connection was closed."));
|
|
86
110
|
}
|
|
87
111
|
return closePromise;
|
|
88
112
|
};
|
|
89
|
-
|
|
90
|
-
|
|
113
|
+
execution?.retainCleanup(closeActual);
|
|
114
|
+
const close = () => execution ? execution.cleanup() : closeActual();
|
|
115
|
+
const fail = (error) => {
|
|
116
|
+
if (stopped.signal.aborted) return;
|
|
117
|
+
stop(error);
|
|
91
118
|
void close().catch(() => void 0);
|
|
92
119
|
};
|
|
120
|
+
if (input.sessionInteractions) sessionInteractions = new SessionInteractionBroker({
|
|
121
|
+
stopped: stopped.signal,
|
|
122
|
+
limits: input.limits,
|
|
123
|
+
onFailure: fail,
|
|
124
|
+
selection: input.sessionInteractions
|
|
125
|
+
});
|
|
126
|
+
const onPeerEnd = () => {
|
|
127
|
+
fail(new AcpProviderConnectionError("acp_provider_peer_closed", "The ACP peer closed its stream."));
|
|
128
|
+
};
|
|
93
129
|
const onPeerError = (error) => {
|
|
94
|
-
|
|
95
|
-
void close().catch(() => void 0);
|
|
130
|
+
fail(error);
|
|
96
131
|
};
|
|
97
132
|
const onAbort = () => {
|
|
98
|
-
|
|
99
|
-
void close().catch(() => void 0);
|
|
133
|
+
fail(initialization.signal.reason instanceof Error ? initialization.signal.reason : new DOMException("ACP initialization was aborted.", "AbortError"));
|
|
100
134
|
};
|
|
101
135
|
lease.stdout.once("end", onPeerEnd);
|
|
102
136
|
lease.stdout.once("close", onPeerEnd);
|
|
@@ -114,6 +148,7 @@ async function initializeAcpConnection(input) {
|
|
|
114
148
|
schemaChannel: "stable",
|
|
115
149
|
unknownExtensionPolicy: input.unknownExtensionPolicy,
|
|
116
150
|
outboundRequestIdPrefix: "agenai-acp-",
|
|
151
|
+
...input.sessionInteractions ? { extensions: createSessionInteractionExtensionRegistry(input.sessionInteractions) } : {},
|
|
117
152
|
limits: {
|
|
118
153
|
maxInboundFrameBytes: Math.min(input.limits.maxQueuedBytes, 1024 * 1024),
|
|
119
154
|
maxOutboundFrameBytes: Math.min(input.limits.maxQueuedBytes, 8 * 1024 * 1024),
|
|
@@ -125,6 +160,13 @@ async function initializeAcpConnection(input) {
|
|
|
125
160
|
shutdownDrainMs: 0
|
|
126
161
|
},
|
|
127
162
|
notificationHandlers: {
|
|
163
|
+
...input.sessionInteractions?.mapping === "grok-build-1.0.60-session-requests-v1" ? Object.fromEntries([GROK_QUEUE_METHOD, GROK_SESSION_NOTIFICATION_METHOD].map((method) => [
|
|
164
|
+
method,
|
|
165
|
+
(notification) => {
|
|
166
|
+
if (!ingress?.onExtensionNotification) throw new AcpProviderConnectionError("acp_provider_session_not_attached", "Native continuation output has no bound owner.");
|
|
167
|
+
return ingress.onExtensionNotification(notification);
|
|
168
|
+
}
|
|
169
|
+
])) : {},
|
|
128
170
|
"session/update": (notification) => {
|
|
129
171
|
if (!ingress) throw new AcpProviderConnectionError("acp_provider_session_not_attached", "ACP output has no bound session.");
|
|
130
172
|
return ingress.onNotification(notification);
|
|
@@ -132,6 +174,7 @@ async function initializeAcpConnection(input) {
|
|
|
132
174
|
"elicitation/complete": (notification) => router.complete(notification.params ?? null)
|
|
133
175
|
},
|
|
134
176
|
reverseRequestHandlers: {
|
|
177
|
+
...sessionInteractions ? { [input.sessionInteractions?.mapping === "grok-folder-trust-v1" ? GROK_FOLDER_TRUST_METHOD : GROK_PLAN_REVIEW_METHOD]: sessionInteractions.handleRequest } : {},
|
|
135
178
|
...Object.fromEntries([
|
|
136
179
|
"session/request_permission",
|
|
137
180
|
"elicitation/create",
|
|
@@ -144,38 +187,37 @@ async function initializeAcpConnection(input) {
|
|
|
144
187
|
"terminal/release"
|
|
145
188
|
].map((method) => [method, (request) => router.admit(request)]))
|
|
146
189
|
},
|
|
147
|
-
onFatalError:
|
|
148
|
-
stop(error);
|
|
149
|
-
void close().catch(() => void 0);
|
|
150
|
-
}
|
|
190
|
+
onFatalError: fail
|
|
151
191
|
});
|
|
152
192
|
void client.closed.then((result) => {
|
|
153
|
-
|
|
154
|
-
void close().catch(() => void 0);
|
|
193
|
+
fail(result.error ?? new AcpProviderConnectionError("acp_provider_connection_closed", "The ACP connection stopped."));
|
|
155
194
|
});
|
|
156
195
|
const response = await createAcpStandardSessionOperations(client).initialize({
|
|
157
196
|
protocolVersion: 1,
|
|
158
197
|
clientCapabilities: {
|
|
198
|
+
...input.sessionInteractions?.mapping === "grok-folder-trust-v1" ? { _meta: GROK_FOLDER_TRUST_CAPABILITY_META } : {},
|
|
159
199
|
fs: { readTextFile: !!host.readTextFile, writeTextFile: !!host.writeTextFile },
|
|
160
200
|
terminal: !!host.createTerminal,
|
|
161
201
|
auth: { terminal: !!host.authenticateInTerminal && !!input.authentication?.terminal },
|
|
162
202
|
elicitation: { form: {}, ...host.url ? { url: {} } : {} },
|
|
163
203
|
...input.configuration === "selectable" ? { session: { configOptions: { boolean: {} } } } : {}
|
|
164
204
|
},
|
|
165
|
-
clientInfo: { name: "@agen-ai/provider-acp", version: "0.4.
|
|
205
|
+
clientInfo: { name: "@agen-ai/provider-acp", version: "0.4.2" }
|
|
166
206
|
}, {
|
|
167
207
|
timeoutMs: input.limits.initializationTimeoutMs,
|
|
168
208
|
signal: initialization.signal
|
|
169
209
|
});
|
|
170
210
|
if (stopped.signal.aborted) throw stopped.signal.reason;
|
|
171
|
-
const negotiated = readNegotiatedAgent(response);
|
|
211
|
+
const negotiated = readNegotiatedAgent(response, input.permissionMapping, input.sessionInteractions);
|
|
172
212
|
initialization.signal.removeEventListener("abort", onAbort);
|
|
173
213
|
return Object.freeze({
|
|
174
214
|
client,
|
|
175
215
|
negotiated,
|
|
176
216
|
stopped: stopped.signal,
|
|
217
|
+
...execution ? { execution } : {},
|
|
218
|
+
...sessionInteractions ? { sessionInteractions } : {},
|
|
177
219
|
bindSession: (binding) => router.bindSession(binding),
|
|
178
|
-
...host.form || host.url ? { hostElicitation: (context) => router.sessionElicitation(context) } : {},
|
|
220
|
+
...host.form || host.url ? { hostElicitation: (context, observeCallback) => router.sessionElicitation(context, observeCallback) } : {},
|
|
179
221
|
async requestWithScope(method, params, scope, options = {}) {
|
|
180
222
|
if (!["authenticate", "session/new", "session/load", "session/resume", "session/set_config_option", "session/set_mode"].includes(method)) {
|
|
181
223
|
throw new TypeError("This ACP method does not grant request-scoped host interaction authority.");
|
|
@@ -192,6 +234,7 @@ async function initializeAcpConnection(input) {
|
|
|
192
234
|
if (attached) throw new AcpProviderConnectionError("acp_provider_connection_already_attached", "ACP connections accept exactly one session.");
|
|
193
235
|
attached = true;
|
|
194
236
|
ingress = next;
|
|
237
|
+
joinSessionCallbacks = next.joinCallbacks;
|
|
195
238
|
return () => {
|
|
196
239
|
ingress = void 0;
|
|
197
240
|
};
|
|
@@ -199,9 +242,11 @@ async function initializeAcpConnection(input) {
|
|
|
199
242
|
close
|
|
200
243
|
});
|
|
201
244
|
} catch (cause) {
|
|
245
|
+
if (execution?.cleanupFailure) throw execution.retainedError(cause, execution.cleanupFailure.error);
|
|
202
246
|
try {
|
|
203
247
|
await close();
|
|
204
248
|
} catch (cleanupError) {
|
|
249
|
+
if (execution) throw execution.retainedError(cause, cleanupError);
|
|
205
250
|
throw new AggregateError([cause, cleanupError], "ACP initialization and cleanup failed.");
|
|
206
251
|
}
|
|
207
252
|
throw cause;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { AcpJsonValue } from '@agen-ai/acp-transport';
|
|
2
2
|
import type { AcpNegotiatedAgent } from './contracts.js';
|
|
3
|
+
import type { AcpPermissionMappingSelection } from '../permissions/contracts.js';
|
|
4
|
+
import type { AcpSessionInteractionSelection } from '../sessionInteractions/contracts.js';
|
|
3
5
|
/** The transport has already validated the complete pinned initialize response. */
|
|
4
|
-
export declare function readNegotiatedAgent(response: AcpJsonValue): AcpNegotiatedAgent;
|
|
6
|
+
export declare function readNegotiatedAgent(response: AcpJsonValue, permissionMapping?: AcpPermissionMappingSelection, sessionInteractions?: AcpSessionInteractionSelection): AcpNegotiatedAgent;
|
|
5
7
|
export declare function negotiatedEnvelope(negotiated: AcpNegotiatedAgent): string;
|
|
6
8
|
//# sourceMappingURL=negotiation.d.ts.map
|
|
@@ -11,7 +11,7 @@ function immutableProtocolData(value) {
|
|
|
11
11
|
}
|
|
12
12
|
return value;
|
|
13
13
|
}
|
|
14
|
-
function readNegotiatedAgent(response) {
|
|
14
|
+
function readNegotiatedAgent(response, permissionMapping = { mapping: "once-only-v1" }, sessionInteractions) {
|
|
15
15
|
const value = response;
|
|
16
16
|
if (value.protocolVersion !== 1) {
|
|
17
17
|
throw new AcpProviderConnectionError("acp_provider_version_unsupported", "ACP wire version 1 is required.");
|
|
@@ -27,9 +27,30 @@ function readNegotiatedAgent(response) {
|
|
|
27
27
|
}
|
|
28
28
|
agentInfo = Object.freeze({ name: info.name, version: info.version });
|
|
29
29
|
}
|
|
30
|
+
let agentVersion;
|
|
31
|
+
if (sessionInteractions?.mapping === "grok-build-1.0.60-session-requests-v1") {
|
|
32
|
+
const meta = value._meta;
|
|
33
|
+
if (meta === null || typeof meta !== "object" || Array.isArray(meta) || meta.agentVersion !== "1.0.60") {
|
|
34
|
+
throw new AcpProviderConnectionError("acp_session_profile_mismatch", "The selected session mapping requires exact initialized agentVersion 1.0.60.");
|
|
35
|
+
}
|
|
36
|
+
agentVersion = "1.0.60";
|
|
37
|
+
}
|
|
38
|
+
if (permissionMapping.mapping === "declared-complete-consequences-v1") {
|
|
39
|
+
if (agentInfo?.name !== permissionMapping.agentInfo.name || agentInfo.version !== permissionMapping.agentInfo.version) {
|
|
40
|
+
throw new AcpProviderConnectionError("acp_permission_profile_mismatch", "The initialized ACP identity does not match the selected permission profile.");
|
|
41
|
+
}
|
|
42
|
+
} else if (permissionMapping.mapping === "grok-build-1.0.50-edit-remembrance-v1") {
|
|
43
|
+
const meta = value._meta;
|
|
44
|
+
const version = meta !== null && typeof meta === "object" && !Array.isArray(meta) ? meta.agentVersion : void 0;
|
|
45
|
+
if (typeof version !== "string" || version.length > 160 || version !== version.trim() || /[\p{Cc}\p{Cf}]/u.test(version) || version !== "1.0.50") {
|
|
46
|
+
throw new AcpProviderConnectionError("acp_permission_profile_mismatch", "The selected Grok permission profile requires the exact initialized agentVersion 1.0.50.");
|
|
47
|
+
}
|
|
48
|
+
agentVersion = version;
|
|
49
|
+
}
|
|
30
50
|
return Object.freeze({
|
|
31
51
|
protocolVersion: 1,
|
|
32
52
|
...agentInfo ? { agentInfo } : {},
|
|
53
|
+
...agentVersion ? { agentVersion } : {},
|
|
33
54
|
agentCapabilities: immutableProtocolData(value.agentCapabilities ?? {}),
|
|
34
55
|
authMethods: immutableProtocolData(value.authMethods ?? [])
|
|
35
56
|
});
|
package/dist/contracts.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { AgentSessionHostBinding, MaybePromise } from '@agen-ai/agent-runti
|
|
|
4
4
|
import type { AcpProviderLimits } from './limits.js';
|
|
5
5
|
import type { AcpStandardMcpServer, AcpUnknownExtensionPolicy } from '@agen-ai/acp-transport';
|
|
6
6
|
import type { AcpAuthenticationMethods, AcpHostFacilities } from './host/contracts.js';
|
|
7
|
+
import type { AcpPermissionMappingSelection } from './permissions/contracts.js';
|
|
7
8
|
export type { AcpProviderLimits } from './limits.js';
|
|
8
9
|
export interface AcpConnectionStreams {
|
|
9
10
|
readonly stdout: Readable;
|
|
@@ -51,6 +52,8 @@ export interface CreateAcpProviderDriverOptions {
|
|
|
51
52
|
readonly createConnection: AcpConnectionFactory;
|
|
52
53
|
/** Defaults to standard handling of unknown optional vendor extensions. */
|
|
53
54
|
readonly unknownExtensionPolicy?: AcpUnknownExtensionPolicy;
|
|
55
|
+
/** Defaults to once-only choices; complete profiles require independently qualified native semantics. */
|
|
56
|
+
readonly permissionMapping?: AcpPermissionMappingSelection;
|
|
54
57
|
readonly limits?: Partial<AcpProviderLimits>;
|
|
55
58
|
readonly configuration?: 'managed' | 'selectable';
|
|
56
59
|
readonly authentication?: AcpAuthenticationMethods;
|
package/dist/driver.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AgentProviderDriver } from '@agen-ai/agent-runtime';
|
|
2
2
|
import type { CreateAcpProviderDriverOptions } from './contracts.js';
|
|
3
3
|
export declare function createAcpProviderDriver(options: CreateAcpProviderDriverOptions): AgentProviderDriver;
|
|
4
4
|
//# sourceMappingURL=driver.d.ts.map
|
package/dist/driver.js
CHANGED
|
@@ -1,43 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { defineAgentProviderDriver } from "@agen-ai/agent-runtime";
|
|
3
|
-
import { parseAcpProviderConfiguration } from "./configuration.js";
|
|
4
|
-
import { createAcpProviderInstance } from "./instance.js";
|
|
5
|
-
import { resolveAcpProviderLimits } from "./limits.js";
|
|
6
|
-
import { resolveAuthenticationMapping } from "./auth/methods.js";
|
|
7
|
-
import { resolveHostFacilities } from "./host/validation.js";
|
|
8
|
-
import { resolveAcpMcpServers, resolveAcpSessionMcpBinding } from "./mcp.js";
|
|
1
|
+
import { createAcpProviderDriverInternal } from "./driverFactory.js";
|
|
9
2
|
function createAcpProviderDriver(options) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const limits = resolveAcpProviderLimits(options.limits);
|
|
16
|
-
const configuration = options.configuration ?? "managed";
|
|
17
|
-
if (configuration !== "managed" && configuration !== "selectable") throw new TypeError("ACP configuration mode is invalid.");
|
|
18
|
-
const authentication = resolveAuthenticationMapping(options.authentication);
|
|
19
|
-
const host = resolveHostFacilities(options.host);
|
|
20
|
-
const mcpServers = resolveAcpMcpServers(options.mcpServers);
|
|
21
|
-
const sessionMcpBinding = resolveAcpSessionMcpBinding(options.sessionMcpBinding);
|
|
22
|
-
const usedStreams = /* @__PURE__ */ new WeakSet();
|
|
23
|
-
return defineAgentProviderDriver({
|
|
24
|
-
providerKey,
|
|
25
|
-
supportsMultipleInstances: true,
|
|
26
|
-
parseConfiguration: parseAcpProviderConfiguration,
|
|
27
|
-
createInstance: ({ instanceId }) => createAcpProviderInstance({
|
|
28
|
-
providerKey,
|
|
29
|
-
instanceId,
|
|
30
|
-
createConnection,
|
|
31
|
-
unknownExtensionPolicy,
|
|
32
|
-
limits,
|
|
33
|
-
usedStreams,
|
|
34
|
-
configuration,
|
|
35
|
-
authentication,
|
|
36
|
-
host,
|
|
37
|
-
mcpServers,
|
|
38
|
-
...sessionMcpBinding ? { sessionMcpBinding } : {}
|
|
39
|
-
})
|
|
40
|
-
});
|
|
3
|
+
if (Object.hasOwn(options, "sessionInteractions")) {
|
|
4
|
+
throw new TypeError("ACP session interaction mappings require the Node provider constructor and its exclusive process ownership.");
|
|
5
|
+
}
|
|
6
|
+
if (Object.hasOwn(options, "executionBoundary")) throw new TypeError("Execution boundaries require the Node provider constructor.");
|
|
7
|
+
return createAcpProviderDriverInternal({ ...options, connectionSource: { kind: "factory", create: options.createConnection } });
|
|
41
8
|
}
|
|
42
9
|
export {
|
|
43
10
|
createAcpProviderDriver
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type AgentProviderDriver } from '@agen-ai/agent-runtime';
|
|
2
|
+
import type { CreateAcpProviderDriverOptions } from './contracts.js';
|
|
3
|
+
import type { AcpSessionInteractionSelection } from './sessionInteractions/contracts.js';
|
|
4
|
+
import type { AcpConnectionSource } from './connection/contracts.js';
|
|
5
|
+
export declare function createAcpProviderDriverInternal(options: Omit<CreateAcpProviderDriverOptions, 'createConnection'> & Readonly<{
|
|
6
|
+
connectionSource: AcpConnectionSource;
|
|
7
|
+
}>, sessionInteractions?: AcpSessionInteractionSelection): AgentProviderDriver;
|
|
8
|
+
//# sourceMappingURL=driverFactory.d.ts.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { parseAgentProviderKey } from "@agen-ai/agent-protocol";
|
|
2
|
+
import { defineAgentProviderDriver } from "@agen-ai/agent-runtime";
|
|
3
|
+
import { parseAcpProviderConfiguration } from "./configuration.js";
|
|
4
|
+
import { createAcpProviderInstance } from "./instance.js";
|
|
5
|
+
import { resolveAcpProviderLimits } from "./limits.js";
|
|
6
|
+
import { resolveAuthenticationMapping } from "./auth/methods.js";
|
|
7
|
+
import { resolveHostFacilities } from "./host/validation.js";
|
|
8
|
+
import { resolveAcpMcpServers, resolveAcpSessionMcpBinding } from "./mcp.js";
|
|
9
|
+
import { resolveAcpPermissionMapping } from "./permissions/selection.js";
|
|
10
|
+
function createAcpProviderDriverInternal(options, sessionInteractions) {
|
|
11
|
+
const providerKey = parseAgentProviderKey(options.providerKey);
|
|
12
|
+
const connectionSource = options.connectionSource;
|
|
13
|
+
if (connectionSource.kind === "factory" && typeof connectionSource.create !== "function") throw new TypeError("An ACP connection factory is required.");
|
|
14
|
+
const unknownExtensionPolicy = options.unknownExtensionPolicy === void 0 ? "standard" : options.unknownExtensionPolicy;
|
|
15
|
+
if (unknownExtensionPolicy !== "reject" && unknownExtensionPolicy !== "standard") throw new TypeError("ACP unknown extension policy is invalid.");
|
|
16
|
+
const limits = resolveAcpProviderLimits(options.limits);
|
|
17
|
+
const configuration = options.configuration ?? "managed";
|
|
18
|
+
if (configuration !== "managed" && configuration !== "selectable") throw new TypeError("ACP configuration mode is invalid.");
|
|
19
|
+
const authentication = resolveAuthenticationMapping(options.authentication);
|
|
20
|
+
const host = resolveHostFacilities(options.host);
|
|
21
|
+
const mcpServers = resolveAcpMcpServers(options.mcpServers);
|
|
22
|
+
const sessionMcpBinding = resolveAcpSessionMcpBinding(options.sessionMcpBinding);
|
|
23
|
+
const permissionMapping = resolveAcpPermissionMapping(options.permissionMapping);
|
|
24
|
+
const usedStreams = /* @__PURE__ */ new WeakSet();
|
|
25
|
+
return defineAgentProviderDriver({
|
|
26
|
+
providerKey,
|
|
27
|
+
supportsMultipleInstances: true,
|
|
28
|
+
parseConfiguration: parseAcpProviderConfiguration,
|
|
29
|
+
createInstance: ({ instanceId }) => createAcpProviderInstance({
|
|
30
|
+
providerKey,
|
|
31
|
+
instanceId,
|
|
32
|
+
connectionSource,
|
|
33
|
+
unknownExtensionPolicy,
|
|
34
|
+
limits,
|
|
35
|
+
usedStreams,
|
|
36
|
+
configuration,
|
|
37
|
+
authentication,
|
|
38
|
+
host,
|
|
39
|
+
mcpServers,
|
|
40
|
+
permissionMapping,
|
|
41
|
+
...sessionMcpBinding ? { sessionMcpBinding } : {},
|
|
42
|
+
...sessionInteractions ? { sessionInteractions } : {}
|
|
43
|
+
})
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
createAcpProviderDriverInternal
|
|
48
|
+
};
|
|
@@ -7,15 +7,20 @@ export declare class AcpHostInteractions {
|
|
|
7
7
|
private readonly urls;
|
|
8
8
|
private readonly pendingUrls;
|
|
9
9
|
private readonly leases;
|
|
10
|
+
private readonly forms;
|
|
10
11
|
constructor(input: {
|
|
11
12
|
readonly instanceId: AgentInstanceId;
|
|
12
13
|
readonly host: AcpHostFacilities;
|
|
13
14
|
readonly limits: AcpProviderLimits;
|
|
14
15
|
readonly stopped: AbortSignal;
|
|
16
|
+
readonly humanTimeoutMs?: () => number | undefined;
|
|
15
17
|
});
|
|
16
|
-
request(context: AcpReverseRequestContext, scope: AcpHostInteractionScope, scopeSignal: AbortSignal): Promise<AcpJsonValue>;
|
|
18
|
+
request(context: AcpReverseRequestContext, scope: AcpHostInteractionScope, scopeSignal: AbortSignal, observeCallback?: (work: Promise<void>) => void): Promise<AcpJsonValue>;
|
|
19
|
+
joinRequest(settled: Promise<void>): Promise<void>;
|
|
17
20
|
complete(id: string): Promise<void>;
|
|
18
21
|
private releaseUrl;
|
|
22
|
+
private closeForms;
|
|
23
|
+
private closeOwned;
|
|
19
24
|
close(): Promise<void>;
|
|
20
25
|
closeScope(signal: AbortSignal): Promise<void>;
|
|
21
26
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_AGENT_SESSION_INTERACTION_LIMITS } from "@agen-ai/agent-runtime";
|
|
1
2
|
import { validateAcpFormContent, validateAcpFormSchema } from "../session/requests/forms.js";
|
|
2
3
|
import { awaitHost, hostDeadline } from "./lifecycle.js";
|
|
3
4
|
import { AcpHostLeases } from "./leases.js";
|
|
@@ -7,11 +8,11 @@ class AcpHostInteractions {
|
|
|
7
8
|
this.input = input;
|
|
8
9
|
this.leases = new AcpHostLeases(input.limits.hostOperationTimeoutMs);
|
|
9
10
|
}
|
|
10
|
-
input;
|
|
11
11
|
urls = /* @__PURE__ */ new Map();
|
|
12
12
|
pendingUrls = /* @__PURE__ */ new Map();
|
|
13
13
|
leases;
|
|
14
|
-
|
|
14
|
+
forms = /* @__PURE__ */ new Map();
|
|
15
|
+
async request(context, scope, scopeSignal, observeCallback) {
|
|
15
16
|
const params = hostObject(context.params);
|
|
16
17
|
const message = hostText(params.message, 4e3);
|
|
17
18
|
const signal = AbortSignal.any([context.signal, scopeSignal, this.input.stopped]);
|
|
@@ -19,15 +20,23 @@ class AcpHostInteractions {
|
|
|
19
20
|
if (!this.input.host.form) throw unsupportedHostRequest();
|
|
20
21
|
const schema = freezeHostData(structuredClone(hostObject(params.requestedSchema)));
|
|
21
22
|
validateAcpFormSchema(schema);
|
|
22
|
-
const
|
|
23
|
+
const timeoutMs = scope.kind === "authentication" ? this.input.limits.authenticationTimeoutMs : this.input.humanTimeoutMs ? this.input.humanTimeoutMs() : DEFAULT_AGENT_SESSION_INTERACTION_LIMITS.requestTimeoutMs;
|
|
24
|
+
const lifetime = new AbortController();
|
|
25
|
+
const formSignal = AbortSignal.any([signal, lifetime.signal]);
|
|
26
|
+
const deadline2 = timeoutMs === void 0 ? { signal: formSignal, close() {
|
|
27
|
+
} } : hostDeadline(formSignal, timeoutMs);
|
|
28
|
+
const callback = Promise.resolve().then(() => {
|
|
29
|
+
if (deadline2.signal.aborted) throw deadline2.signal.reason;
|
|
30
|
+
return this.input.host.form({ instanceId: this.input.instanceId, scope, signal: deadline2.signal, message, requestedSchema: schema });
|
|
31
|
+
});
|
|
32
|
+
const actualCallback = callback.then(() => void 0, () => void 0);
|
|
33
|
+
observeCallback?.(actualCallback);
|
|
34
|
+
const completion = Promise.all([actualCallback, context.settled]).then(() => {
|
|
35
|
+
this.forms.delete(context.settled);
|
|
36
|
+
});
|
|
37
|
+
this.forms.set(context.settled, { scopeSignal, lifetime, completion });
|
|
23
38
|
try {
|
|
24
|
-
const result = await awaitHost(
|
|
25
|
-
instanceId: this.input.instanceId,
|
|
26
|
-
scope,
|
|
27
|
-
signal: deadline2.signal,
|
|
28
|
-
message,
|
|
29
|
-
requestedSchema: schema
|
|
30
|
-
}), deadline2.signal);
|
|
39
|
+
const result = await awaitHost(callback, deadline2.signal);
|
|
31
40
|
if (result.action === "accept") {
|
|
32
41
|
const content = hostObject(result.content);
|
|
33
42
|
validateAcpFormContent(schema, content);
|
|
@@ -102,6 +111,9 @@ class AcpHostInteractions {
|
|
|
102
111
|
if (!this.urls.has(id)) deadline.close();
|
|
103
112
|
}
|
|
104
113
|
}
|
|
114
|
+
joinRequest(settled) {
|
|
115
|
+
return this.forms.get(settled)?.completion ?? Promise.resolve();
|
|
116
|
+
}
|
|
105
117
|
async complete(id) {
|
|
106
118
|
const interaction = this.urls.get(id);
|
|
107
119
|
if (!interaction) {
|
|
@@ -128,18 +140,31 @@ class AcpHostInteractions {
|
|
|
128
140
|
interaction.detach();
|
|
129
141
|
this.urls.delete(id);
|
|
130
142
|
}
|
|
131
|
-
async
|
|
132
|
-
|
|
133
|
-
for (const
|
|
134
|
-
this.
|
|
143
|
+
async closeForms(scope) {
|
|
144
|
+
const entries = [...this.forms.values()].filter((entry) => scope === void 0 || entry.scopeSignal === scope);
|
|
145
|
+
for (const entry of entries) entry.lifetime.abort(new DOMException("Host form closed.", "AbortError"));
|
|
146
|
+
const deadline = hostDeadline(new AbortController().signal, this.input.limits.hostOperationTimeoutMs);
|
|
147
|
+
try {
|
|
148
|
+
await awaitHost(Promise.all(entries.map((entry) => entry.completion)), deadline.signal);
|
|
149
|
+
} finally {
|
|
150
|
+
deadline.close();
|
|
151
|
+
}
|
|
135
152
|
}
|
|
136
|
-
async
|
|
137
|
-
await this.leases.close(
|
|
138
|
-
|
|
153
|
+
async closeOwned(scope) {
|
|
154
|
+
const results = await Promise.allSettled([this.leases.close(scope), this.closeForms(scope)]);
|
|
155
|
+
const failures = results.filter((result) => result.status === "rejected");
|
|
156
|
+
if (failures.length) throw new AggregateError(failures.map((result) => result.reason), "ACP host interaction cleanup failed.");
|
|
157
|
+
for (const [id, interaction] of this.urls) if (scope === void 0 || interaction.scopeSignal === scope) {
|
|
139
158
|
interaction.detach();
|
|
140
159
|
this.urls.delete(id);
|
|
141
160
|
}
|
|
142
161
|
}
|
|
162
|
+
close() {
|
|
163
|
+
return this.closeOwned();
|
|
164
|
+
}
|
|
165
|
+
closeScope(signal) {
|
|
166
|
+
return this.closeOwned(signal);
|
|
167
|
+
}
|
|
143
168
|
}
|
|
144
169
|
export {
|
|
145
170
|
AcpHostInteractions
|
package/dist/host/leases.js
CHANGED
package/dist/host/router.d.ts
CHANGED
|
@@ -14,13 +14,14 @@ export declare class AcpHostRouter {
|
|
|
14
14
|
private readonly terminals;
|
|
15
15
|
private readonly requests;
|
|
16
16
|
private binding;
|
|
17
|
-
private pending;
|
|
17
|
+
private readonly pending;
|
|
18
18
|
private bytes;
|
|
19
19
|
constructor(input: {
|
|
20
20
|
readonly instanceId: AgentInstanceId;
|
|
21
21
|
readonly host: AcpHostFacilities;
|
|
22
22
|
readonly limits: AcpProviderLimits;
|
|
23
23
|
readonly stopped: AbortSignal;
|
|
24
|
+
readonly humanTimeoutMs?: () => number | undefined;
|
|
24
25
|
readonly ingress: () => AcpSessionIngress | undefined;
|
|
25
26
|
});
|
|
26
27
|
bindSession(binding: AcpBoundSession): void;
|
|
@@ -41,7 +42,7 @@ export declare class AcpHostRouter {
|
|
|
41
42
|
private requireSession;
|
|
42
43
|
private hostRequest;
|
|
43
44
|
complete(value: AcpJsonValue): Promise<void>;
|
|
44
|
-
sessionElicitation(context: AcpReverseRequestContext): Promise<AcpJsonValue>;
|
|
45
|
+
sessionElicitation(context: AcpReverseRequestContext, observeCallback?: (work: Promise<void>) => void): Promise<AcpJsonValue>;
|
|
45
46
|
close(): Promise<void>;
|
|
46
47
|
}
|
|
47
48
|
//# sourceMappingURL=router.d.ts.map
|