@agen-ai/provider-acp 0.4.1 → 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 +174 -14
- package/dist/capabilities.js +2 -2
- package/dist/connection/contracts.d.ts +3 -1
- package/dist/connection/initialize.js +25 -9
- package/dist/connection/negotiation.d.ts +2 -1
- package/dist/connection/negotiation.js +8 -1
- package/dist/host/interactions.d.ts +6 -1
- package/dist/host/interactions.js +42 -16
- package/dist/host/router.d.ts +3 -2
- package/dist/host/router.js +23 -20
- package/dist/instance.js +19 -9
- package/dist/node/contracts.d.ts +2 -2
- package/dist/node/index.js +1 -1
- package/dist/node/launch.js +3 -3
- package/dist/session/continuation.d.ts +33 -0
- package/dist/session/continuation.js +185 -0
- package/dist/session/descendants.d.ts +29 -0
- package/dist/session/descendants.js +97 -0
- package/dist/session/mapping.js +1 -1
- package/dist/session/native.d.ts +1 -0
- package/dist/session/native.js +8 -0
- package/dist/session/opening.js +1 -1
- package/dist/session/requests/elicitation.d.ts +1 -1
- package/dist/session/requests/elicitation.js +1 -1
- 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 -7
- package/dist/session/requests/resolution.js +4 -2
- package/dist/session/session.d.ts +8 -0
- package/dist/session/session.js +140 -32
- package/dist/session/turn.d.ts +6 -1
- package/dist/sessionInteractions/broker.d.ts +11 -3
- package/dist/sessionInteractions/broker.js +35 -13
- package/dist/sessionInteractions/extensions.d.ts +2 -1
- package/dist/sessionInteractions/extensions.js +21 -1
- package/dist/sessionInteractions/grokFolderTrust.d.ts +0 -1
- package/dist/sessionInteractions/grokFolderTrust.js +0 -1
- package/dist/sessionInteractions/grokPlanReview.d.ts +16 -0
- package/dist/sessionInteractions/grokPlanReview.js +63 -0
- package/dist/sessionInteractions/request.d.ts +15 -7
- package/dist/sessionInteractions/request.js +46 -18
- package/package.json +4 -4
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseAgentProviderConversationId,
|
|
3
|
+
parseAgentSessionPlanReviewRequest
|
|
4
|
+
} from "@agen-ai/agent-protocol";
|
|
5
|
+
import { AcpProviderSessionError } from "../session/errors.js";
|
|
6
|
+
const GROK_PLAN_REVIEW_METHOD = "_x.ai/exit_plan_mode";
|
|
7
|
+
const GROK_QUEUE_METHOD = "_x.ai/queue/changed";
|
|
8
|
+
const GROK_SESSION_NOTIFICATION_METHOD = "_x.ai/session_notification";
|
|
9
|
+
function parseNativePlanReviewRequest(input) {
|
|
10
|
+
if (input === null || typeof input !== "object" || Array.isArray(input) || Reflect.ownKeys(input).length !== 3 || !("sessionId" in input) || !("toolCallId" in input) || !("planContent" in input) || typeof input.sessionId !== "string" || Buffer.byteLength(input.sessionId, "utf8") > 256 || typeof input.toolCallId !== "string" || !input.toolCallId || Buffer.byteLength(input.toolCallId, "utf8") > 256 || typeof input.planContent !== "string" || !input.planContent || Buffer.byteLength(input.planContent, "utf8") > 65536) {
|
|
11
|
+
throw new AcpProviderSessionError("acp_session_interaction_invalid", "The native plan review violates the selected mapping.");
|
|
12
|
+
}
|
|
13
|
+
return Object.freeze({
|
|
14
|
+
sessionId: parseAgentProviderConversationId(input.sessionId),
|
|
15
|
+
toolCallId: input.toolCallId,
|
|
16
|
+
planContent: input.planContent
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function validateNativePlanReviewRequest(input) {
|
|
20
|
+
try {
|
|
21
|
+
parseNativePlanReviewRequest(input);
|
|
22
|
+
return true;
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function validateNativePlanReviewResult(input) {
|
|
28
|
+
if (input === null || typeof input !== "object" || Array.isArray(input) || !("outcome" in input)) return false;
|
|
29
|
+
return input.outcome === "cancelled" ? Reflect.ownKeys(input).length === 2 && "feedback" in input && typeof input.feedback === "string" && Buffer.byteLength(input.feedback, "utf8") <= 16384 : Reflect.ownKeys(input).length === 1 && (input.outcome === "approved" || input.outcome === "abandoned");
|
|
30
|
+
}
|
|
31
|
+
function planReviewRequest(native, sessionId, requestNumber) {
|
|
32
|
+
return parseAgentSessionPlanReviewRequest({
|
|
33
|
+
kind: "plan_review",
|
|
34
|
+
requestId: `session-interaction:${requestNumber}`,
|
|
35
|
+
sessionId,
|
|
36
|
+
title: "Review the restored plan",
|
|
37
|
+
plan: {
|
|
38
|
+
planId: `session-plan:${requestNumber}`,
|
|
39
|
+
revision: "1",
|
|
40
|
+
title: "Restored plan",
|
|
41
|
+
content: { kind: "text", format: "markdown", text: native.planContent }
|
|
42
|
+
},
|
|
43
|
+
choices: [
|
|
44
|
+
{ choiceId: "approve", action: "approve", label: "Approve plan" },
|
|
45
|
+
{ choiceId: "revise", action: "revise", label: "Revise plan" },
|
|
46
|
+
{ choiceId: "abandon", action: "abandon", label: "Abandon plan" }
|
|
47
|
+
]
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function nativePlanReviewReply(response) {
|
|
51
|
+
if (response.action === "revise") return Object.freeze({ outcome: "cancelled", feedback: response.feedback });
|
|
52
|
+
return Object.freeze({ outcome: response.action === "approve" ? "approved" : "abandoned" });
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
GROK_PLAN_REVIEW_METHOD,
|
|
56
|
+
GROK_QUEUE_METHOD,
|
|
57
|
+
GROK_SESSION_NOTIFICATION_METHOD,
|
|
58
|
+
nativePlanReviewReply,
|
|
59
|
+
parseNativePlanReviewRequest,
|
|
60
|
+
planReviewRequest,
|
|
61
|
+
validateNativePlanReviewRequest,
|
|
62
|
+
validateNativePlanReviewResult
|
|
63
|
+
};
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { type AcpJsonValue, type AcpReverseRequestContext } from '@agen-ai/acp-transport';
|
|
2
|
-
import { type AgentError, type AgentSessionDecisionRequest } from '@agen-ai/agent-protocol';
|
|
3
|
-
import { type
|
|
2
|
+
import { type AgentError, type AgentSessionDecisionRequest, type AgentSessionPlanReviewRequest, type AgentSessionPlanReviewResponse } from '@agen-ai/agent-protocol';
|
|
3
|
+
import { type AgentRequestResolutionResult, type AgentProviderSessionInteraction, type AgentPreparedSessionResponse, type AgentProviderSessionInteractionContext, type AgentSessionInteractionTerminal } from '@agen-ai/agent-runtime';
|
|
4
|
+
export type PreparePlanContinuation = (input: Readonly<{
|
|
5
|
+
response: AgentSessionPlanReviewResponse;
|
|
6
|
+
signal: AbortSignal;
|
|
7
|
+
nativeSettled: Promise<void>;
|
|
8
|
+
dispatch: () => Promise<AgentRequestResolutionResult>;
|
|
9
|
+
}>) => AgentPreparedSessionResponse;
|
|
4
10
|
export declare function sessionDecisionError(code: string, message: string): AgentError;
|
|
5
11
|
export declare class NativeSessionDecision {
|
|
6
12
|
private readonly input;
|
|
7
|
-
readonly handle:
|
|
13
|
+
readonly handle: AgentProviderSessionInteraction;
|
|
8
14
|
readonly nativeReply: Promise<AcpJsonValue>;
|
|
9
15
|
private readonly answer;
|
|
10
16
|
private readonly terminal;
|
|
@@ -18,20 +24,22 @@ export declare class NativeSessionDecision {
|
|
|
18
24
|
private readonly timer;
|
|
19
25
|
private readonly onNativeAbort;
|
|
20
26
|
constructor(input: Readonly<{
|
|
21
|
-
request: AgentSessionDecisionRequest;
|
|
22
|
-
nativeContext: Pick<AcpReverseRequestContext, 'id' | 'signal'>;
|
|
23
|
-
|
|
27
|
+
request: AgentSessionDecisionRequest | AgentSessionPlanReviewRequest;
|
|
28
|
+
nativeContext: Pick<AcpReverseRequestContext, 'id' | 'signal' | 'settled'>;
|
|
29
|
+
hostDeadlineAt?: number;
|
|
30
|
+
prepareContinuation?: PreparePlanContinuation;
|
|
24
31
|
accepting: () => boolean;
|
|
25
32
|
fail: (cause: Error) => void;
|
|
26
33
|
retire: () => void;
|
|
27
34
|
}>);
|
|
28
35
|
observeNativeSignal(): void;
|
|
29
|
-
present(handler:
|
|
36
|
+
present(handler: AgentProviderSessionInteractionContext['onRequest']): void;
|
|
30
37
|
stop(terminal: AgentSessionInteractionTerminal, cause: Error): void;
|
|
31
38
|
private finalize;
|
|
32
39
|
private expire;
|
|
33
40
|
private nativeAborted;
|
|
34
41
|
private respond;
|
|
42
|
+
private prepareResponse;
|
|
35
43
|
private maybeRetire;
|
|
36
44
|
join(): Promise<void>;
|
|
37
45
|
}
|
|
@@ -1,36 +1,39 @@
|
|
|
1
1
|
import { AcpRequestError } from "@agen-ai/acp-transport";
|
|
2
2
|
import {
|
|
3
|
-
parseAgentSessionDecisionResponseFor
|
|
3
|
+
parseAgentSessionDecisionResponseFor,
|
|
4
|
+
parseAgentSessionPlanReviewResponseFor
|
|
4
5
|
} from "@agen-ai/agent-protocol";
|
|
5
6
|
import {
|
|
6
7
|
throwIfAgentOperationAborted
|
|
7
8
|
} from "@agen-ai/agent-runtime";
|
|
8
9
|
import { AcpProviderSessionError } from "../session/errors.js";
|
|
9
10
|
import { deferred } from "../session/queue.js";
|
|
11
|
+
import { nativePlanReviewReply } from "./grokPlanReview.js";
|
|
10
12
|
function sessionDecisionError(code, message) {
|
|
11
13
|
return Object.freeze({ category: "invalid_request", code, message, recovery: Object.freeze({ kind: "none" }) });
|
|
12
14
|
}
|
|
13
15
|
class NativeSessionDecision {
|
|
14
16
|
constructor(input) {
|
|
15
17
|
this.input = input;
|
|
16
|
-
|
|
18
|
+
const base = {
|
|
17
19
|
request: input.request,
|
|
18
20
|
signal: this.presentation.signal,
|
|
19
|
-
terminal: this.terminal.promise
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}, (
|
|
21
|
+
terminal: this.terminal.promise
|
|
22
|
+
};
|
|
23
|
+
this.handle = input.request.kind === "plan_review" ? Object.freeze({
|
|
24
|
+
...base,
|
|
25
|
+
kind: "prepared",
|
|
26
|
+
request: input.request,
|
|
27
|
+
prepareResponse: (response) => this.prepareResponse(response)
|
|
28
|
+
}) : Object.freeze({ ...base, kind: "direct", respond: (response) => this.respond(response) });
|
|
29
|
+
this.nativeReply = this.answer.promise;
|
|
30
|
+
void input.nativeContext.settled.then(() => {
|
|
27
31
|
this.nativeSettled = true;
|
|
28
32
|
this.maybeRetire();
|
|
29
|
-
throw cause;
|
|
30
33
|
});
|
|
31
34
|
void this.nativeReply.catch(() => {
|
|
32
35
|
});
|
|
33
|
-
this.timer = setTimeout(() => this.expire(), Math.max(0, input.
|
|
36
|
+
if (input.hostDeadlineAt !== void 0) this.timer = setTimeout(() => this.expire(), Math.max(0, input.hostDeadlineAt - Date.now()));
|
|
34
37
|
input.nativeContext.signal.addEventListener("abort", this.onNativeAbort, { once: true });
|
|
35
38
|
}
|
|
36
39
|
handle;
|
|
@@ -53,7 +56,7 @@ class NativeSessionDecision {
|
|
|
53
56
|
if (this.presented || this.state.kind !== "pending") return;
|
|
54
57
|
this.observeNativeSignal();
|
|
55
58
|
if (this.state.kind !== "pending" || !this.input.accepting()) return;
|
|
56
|
-
if (Date.now() >= this.input.
|
|
59
|
+
if (this.input.hostDeadlineAt !== void 0 && Date.now() >= this.input.hostDeadlineAt) {
|
|
57
60
|
this.expire();
|
|
58
61
|
return;
|
|
59
62
|
}
|
|
@@ -93,7 +96,7 @@ class NativeSessionDecision {
|
|
|
93
96
|
expire() {
|
|
94
97
|
if (this.state.kind === "terminal") return;
|
|
95
98
|
const cause = new AcpProviderSessionError("acp_session_interaction_expired", "The session decision expired before acceptance.");
|
|
96
|
-
this.stop(Object.freeze({ kind: "expired" }), cause);
|
|
99
|
+
this.stop(Object.freeze({ kind: "expired", source: "host_policy" }), cause);
|
|
97
100
|
this.input.fail(cause);
|
|
98
101
|
}
|
|
99
102
|
nativeAborted() {
|
|
@@ -110,9 +113,10 @@ class NativeSessionDecision {
|
|
|
110
113
|
}
|
|
111
114
|
async respond(input) {
|
|
112
115
|
throwIfAgentOperationAborted(input.signal);
|
|
113
|
-
|
|
116
|
+
if (this.input.request.kind !== "decision") throw new TypeError("Plan review requires prepared response ownership.");
|
|
117
|
+
const response = parseAgentSessionDecisionResponseFor(this.input.request, { kind: input.kind, choiceId: input.choiceId });
|
|
114
118
|
this.observeNativeSignal();
|
|
115
|
-
if (Date.now() >= this.input.
|
|
119
|
+
if (this.input.hostDeadlineAt !== void 0 && Date.now() >= this.input.hostDeadlineAt) this.expire();
|
|
116
120
|
if (!this.input.accepting() || this.state.kind === "terminal") {
|
|
117
121
|
return Object.freeze({ status: "rejected", requestState: "unavailable", error: sessionDecisionError(
|
|
118
122
|
"session_interaction_unavailable",
|
|
@@ -120,16 +124,40 @@ class NativeSessionDecision {
|
|
|
120
124
|
) });
|
|
121
125
|
}
|
|
122
126
|
this.answer.resolve(Object.freeze({ outcome: response.choiceId === "grant" ? "trust" : "reject" }));
|
|
123
|
-
this.finalize(Object.freeze({ kind: "answered",
|
|
127
|
+
this.finalize(Object.freeze({ kind: "answered", response }));
|
|
124
128
|
return Object.freeze({ status: "delivered" });
|
|
125
129
|
}
|
|
130
|
+
prepareResponse(input) {
|
|
131
|
+
throwIfAgentOperationAborted(input.signal);
|
|
132
|
+
if (this.input.request.kind !== "plan_review" || !this.input.prepareContinuation) throw new TypeError("This request has no continuation mapping.");
|
|
133
|
+
const response = parseAgentSessionPlanReviewResponseFor(this.input.request, input.response);
|
|
134
|
+
return this.input.prepareContinuation({ response, signal: input.signal, nativeSettled: this.input.nativeContext.settled, dispatch: async () => {
|
|
135
|
+
throwIfAgentOperationAborted(input.signal);
|
|
136
|
+
this.observeNativeSignal();
|
|
137
|
+
if (this.input.hostDeadlineAt !== void 0 && Date.now() >= this.input.hostDeadlineAt) this.expire();
|
|
138
|
+
if (!this.input.accepting() || this.state.kind === "terminal") return Object.freeze({
|
|
139
|
+
status: "rejected",
|
|
140
|
+
requestState: "unavailable",
|
|
141
|
+
error: sessionDecisionError("session_interaction_unavailable", "The plan review is no longer available.")
|
|
142
|
+
});
|
|
143
|
+
this.answer.resolve(nativePlanReviewReply(response));
|
|
144
|
+
this.finalize(Object.freeze({ kind: "answered", response: Object.freeze({
|
|
145
|
+
kind: "plan_review",
|
|
146
|
+
action: response.action,
|
|
147
|
+
choiceId: response.choiceId,
|
|
148
|
+
planId: response.planId,
|
|
149
|
+
revision: response.revision
|
|
150
|
+
}) }));
|
|
151
|
+
return Object.freeze({ status: "delivered" });
|
|
152
|
+
} });
|
|
153
|
+
}
|
|
126
154
|
maybeRetire() {
|
|
127
155
|
if (this.retired || this.state.kind !== "terminal" || !this.nativeSettled || !this.callbackSettled) return;
|
|
128
156
|
this.retired = true;
|
|
129
157
|
this.input.retire();
|
|
130
158
|
}
|
|
131
159
|
async join() {
|
|
132
|
-
await Promise.allSettled([this.
|
|
160
|
+
await Promise.allSettled([this.input.nativeContext.settled, ...this.callback ? [this.callback] : []]);
|
|
133
161
|
}
|
|
134
162
|
}
|
|
135
163
|
function callbackError(cause) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agen-ai/provider-acp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Optional Agent Client Protocol provider for the AgenAI coding-agent runtime.",
|
|
6
6
|
"type": "module",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"./package.json": "./package.json"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@agen-ai/
|
|
31
|
-
"@agen-ai/
|
|
32
|
-
"@agen-ai/agent-
|
|
30
|
+
"@agen-ai/acp-transport": "^0.4.2",
|
|
31
|
+
"@agen-ai/agent-runtime": "^0.4.2",
|
|
32
|
+
"@agen-ai/agent-protocol": "^0.4.2"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"tsup": "^8.5.0"
|