@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
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_AGENT_SESSION_INTERACTION_LIMITS,
|
|
3
|
+
throwIfAgentOperationAborted
|
|
4
|
+
} from "@agen-ai/agent-runtime";
|
|
5
|
+
import { AcpProviderSessionError } from "../session/errors.js";
|
|
6
|
+
import { folderTrustDecision, GROK_FOLDER_TRUST_METHOD, parseNativeFolderTrustRequest } from "./grokFolderTrust.js";
|
|
7
|
+
import { NativeSessionDecision, sessionDecisionError } from "./request.js";
|
|
8
|
+
import { GROK_PLAN_REVIEW_METHOD, parseNativePlanReviewRequest, planReviewRequest } from "./grokPlanReview.js";
|
|
9
|
+
class SessionInteractionBroker {
|
|
10
|
+
constructor(input) {
|
|
11
|
+
this.input = input;
|
|
12
|
+
input.stopped.addEventListener("abort", this.onStopped, { once: true });
|
|
13
|
+
if (input.stopped.aborted) this.onStopped();
|
|
14
|
+
}
|
|
15
|
+
state = { kind: "idle" };
|
|
16
|
+
requestNumber = 0;
|
|
17
|
+
expectedNativeId;
|
|
18
|
+
prepareContinuation;
|
|
19
|
+
retained;
|
|
20
|
+
entered = /* @__PURE__ */ new Set();
|
|
21
|
+
openingTimer;
|
|
22
|
+
onStopped = () => {
|
|
23
|
+
const cause = this.input.stopped.reason;
|
|
24
|
+
this.stop({ kind: "failed", cause: cause instanceof Error ? cause : new AcpProviderSessionError(
|
|
25
|
+
"acp_session_interaction_native_failed",
|
|
26
|
+
"The native session decision producer stopped."
|
|
27
|
+
) });
|
|
28
|
+
};
|
|
29
|
+
handleRequest = (context) => {
|
|
30
|
+
try {
|
|
31
|
+
const plan = this.input.selection.mapping === "grok-build-1.0.60-session-requests-v1";
|
|
32
|
+
if (context.method !== (plan ? GROK_PLAN_REVIEW_METHOD : GROK_FOLDER_TRUST_METHOD)) throw failure(
|
|
33
|
+
"acp_session_interaction_invalid",
|
|
34
|
+
"The reverse request does not belong to the selected session mapping."
|
|
35
|
+
);
|
|
36
|
+
const native = plan ? parseNativePlanReviewRequest(context.params) : parseNativeFolderTrustRequest(context.params);
|
|
37
|
+
const state = this.state;
|
|
38
|
+
if (state.kind === "stopped") throw state.cause;
|
|
39
|
+
if (state.kind === "idle" || state.kind === "prepared") throw failure(
|
|
40
|
+
"acp_session_interaction_owner_missing",
|
|
41
|
+
"The native decision has no dispatched session opening owner."
|
|
42
|
+
);
|
|
43
|
+
if (this.retained) throw failure(
|
|
44
|
+
"acp_session_interaction_capacity",
|
|
45
|
+
"The connection already retains a native session decision."
|
|
46
|
+
);
|
|
47
|
+
if ("cwd" in native && native.cwd !== state.owner.workingDirectory || this.expectedNativeId !== void 0 && native.sessionId !== this.expectedNativeId) throw failure(
|
|
48
|
+
"acp_session_interaction_binding",
|
|
49
|
+
"The native decision does not match its exact session binding."
|
|
50
|
+
);
|
|
51
|
+
const hostDeadlineAt = state.owner.interactions.humanWait?.kind === "until_withdrawn_or_closed" ? void 0 : Date.now() + state.owner.limits.requestTimeoutMs;
|
|
52
|
+
const requestNumber = ++this.requestNumber;
|
|
53
|
+
const request = "planContent" in native ? planReviewRequest(native, state.owner.sessionId, requestNumber) : folderTrustDecision({ native, sessionId: state.owner.sessionId, requestNumber });
|
|
54
|
+
const descriptorBytes = Buffer.byteLength(JSON.stringify(request), "utf8");
|
|
55
|
+
const nativeBytes = Buffer.byteLength(JSON.stringify(native), "utf8");
|
|
56
|
+
if (descriptorBytes > state.owner.limits.maxRetainedBytes || descriptorBytes + nativeBytes > this.input.limits.maxQueuedBytes) throw failure(
|
|
57
|
+
"acp_session_interaction_capacity",
|
|
58
|
+
"The native session decision exceeds its retained byte budget."
|
|
59
|
+
);
|
|
60
|
+
const decision = new NativeSessionDecision({
|
|
61
|
+
request,
|
|
62
|
+
nativeContext: context,
|
|
63
|
+
...hostDeadlineAt === void 0 ? {} : { hostDeadlineAt },
|
|
64
|
+
...plan ? { prepareContinuation: (input) => {
|
|
65
|
+
if (!this.prepareContinuation) throw failure("acp_continuation_owner_missing", "The plan review has no prepared execution owner.");
|
|
66
|
+
return this.prepareContinuation(input);
|
|
67
|
+
} } : {},
|
|
68
|
+
accepting: () => this.state.kind === "open" || !plan && this.state.kind === "active",
|
|
69
|
+
fail: (cause) => this.stop({ kind: "failed", cause }),
|
|
70
|
+
retire: () => {
|
|
71
|
+
if (this.retained?.decision === decision) this.retained = void 0;
|
|
72
|
+
this.entered.delete(decision);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
this.retained = { decision, nativeId: native.sessionId };
|
|
76
|
+
this.entered.add(decision);
|
|
77
|
+
decision.observeNativeSignal();
|
|
78
|
+
if (this.state.kind === "open" || !plan && this.state.kind === "active") decision.present(state.owner.interactions.onRequest);
|
|
79
|
+
return decision.nativeReply;
|
|
80
|
+
} catch (cause) {
|
|
81
|
+
const error = cause instanceof Error ? cause : failure(
|
|
82
|
+
"acp_session_interaction_invalid",
|
|
83
|
+
"The native session decision could not be admitted.",
|
|
84
|
+
cause
|
|
85
|
+
);
|
|
86
|
+
this.stop({ kind: "failed", cause: error });
|
|
87
|
+
const rejected = Promise.reject(error);
|
|
88
|
+
void rejected.catch(() => {
|
|
89
|
+
});
|
|
90
|
+
return rejected;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
attachContinuationOwner(prepare) {
|
|
94
|
+
if (this.prepareContinuation) throw this.stateError();
|
|
95
|
+
this.prepareContinuation = prepare;
|
|
96
|
+
}
|
|
97
|
+
humanTimeoutMs() {
|
|
98
|
+
if (this.state.kind === "idle" || this.state.kind === "stopped") return DEFAULT_AGENT_SESSION_INTERACTION_LIMITS.requestTimeoutMs;
|
|
99
|
+
return this.state.owner.interactions.humanWait?.kind === "until_withdrawn_or_closed" ? void 0 : this.state.owner.limits.requestTimeoutMs;
|
|
100
|
+
}
|
|
101
|
+
get selectedLifecycle() {
|
|
102
|
+
return this.input.selection.mapping === "grok-build-1.0.60-session-requests-v1";
|
|
103
|
+
}
|
|
104
|
+
prepareOpening(input) {
|
|
105
|
+
throwIfAgentOperationAborted(input.signal);
|
|
106
|
+
if (this.state.kind !== "idle") throw this.stateError();
|
|
107
|
+
const onAbort = () => this.stop({ kind: "failed", cause: input.signal?.reason instanceof Error ? input.signal.reason : new DOMException("The session opening was aborted.", "AbortError") });
|
|
108
|
+
const owner = Object.freeze({
|
|
109
|
+
sessionId: input.sessionId,
|
|
110
|
+
workingDirectory: input.workingDirectory,
|
|
111
|
+
interactions: input.interactions,
|
|
112
|
+
limits: Object.freeze({ ...DEFAULT_AGENT_SESSION_INTERACTION_LIMITS, ...input.interactions.limits }),
|
|
113
|
+
removeAbort: () => input.signal?.removeEventListener("abort", onAbort)
|
|
114
|
+
});
|
|
115
|
+
this.state = { kind: "prepared", owner };
|
|
116
|
+
input.signal?.addEventListener("abort", onAbort, { once: true });
|
|
117
|
+
if (input.signal?.aborted) onAbort();
|
|
118
|
+
this.throwIfStopped();
|
|
119
|
+
}
|
|
120
|
+
onOpeningDispatched() {
|
|
121
|
+
if (this.state.kind !== "prepared") throw this.stateError();
|
|
122
|
+
this.state = this.expectedNativeId === void 0 ? { kind: "dispatched", owner: this.state.owner } : { kind: "bound", owner: this.state.owner, nativeId: this.expectedNativeId };
|
|
123
|
+
this.openingTimer = setTimeout(
|
|
124
|
+
() => this.stop({ kind: "failed", cause: failure(
|
|
125
|
+
"acp_session_interaction_opening_timeout",
|
|
126
|
+
"The native decision opening did not bind before its deadline."
|
|
127
|
+
) }),
|
|
128
|
+
this.input.limits.initializationTimeoutMs
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
bindNativeSession(nativeId) {
|
|
132
|
+
if (this.state.kind !== "dispatched" && this.state.kind !== "prepared") throw this.stateError();
|
|
133
|
+
if (this.retained && this.retained.nativeId !== nativeId) {
|
|
134
|
+
const cause = failure("acp_session_interaction_binding", "The buffered decision claimed a different native session.");
|
|
135
|
+
this.stop({ kind: "failed", cause });
|
|
136
|
+
throw cause;
|
|
137
|
+
}
|
|
138
|
+
this.expectedNativeId = nativeId;
|
|
139
|
+
if (this.state.kind === "dispatched") this.state = { kind: "bound", owner: this.state.owner, nativeId };
|
|
140
|
+
}
|
|
141
|
+
completeOpeningResponse() {
|
|
142
|
+
if (this.state.kind !== "bound") throw this.stateError();
|
|
143
|
+
this.clearOpeningTimer();
|
|
144
|
+
const owner = this.state.owner;
|
|
145
|
+
this.state = { kind: "active", owner, nativeId: this.state.nativeId };
|
|
146
|
+
if (this.input.selection.mapping === "grok-folder-trust-v1") this.retained?.decision.present(owner.interactions.onRequest);
|
|
147
|
+
this.throwIfStopped();
|
|
148
|
+
}
|
|
149
|
+
adopt() {
|
|
150
|
+
if (this.state.kind !== "active") throw this.stateError();
|
|
151
|
+
this.state.owner.removeAbort();
|
|
152
|
+
this.state = { kind: "open", owner: this.state.owner, nativeId: this.state.nativeId };
|
|
153
|
+
this.retained?.decision.present(this.state.owner.interactions.onRequest);
|
|
154
|
+
}
|
|
155
|
+
stop(reason) {
|
|
156
|
+
if (this.state.kind === "stopped") return;
|
|
157
|
+
const cause = reason.kind === "failed" ? reason.cause : failure(
|
|
158
|
+
"acp_session_interaction_closed",
|
|
159
|
+
"The session decision owner was closed."
|
|
160
|
+
);
|
|
161
|
+
if (this.state.kind !== "idle") this.state.owner.removeAbort();
|
|
162
|
+
this.clearOpeningTimer();
|
|
163
|
+
this.state = { kind: "stopped", cause };
|
|
164
|
+
this.retained?.decision.stop(reason.kind === "failed" ? Object.freeze({ kind: "failed", error: sessionDecisionError(
|
|
165
|
+
"session_interaction_failed",
|
|
166
|
+
"The session decision owner failed."
|
|
167
|
+
) }) : Object.freeze({ kind: "session_closed" }), cause);
|
|
168
|
+
if (reason.kind === "failed") this.input.onFailure(cause);
|
|
169
|
+
}
|
|
170
|
+
clearOpeningTimer() {
|
|
171
|
+
if (this.openingTimer) clearTimeout(this.openingTimer);
|
|
172
|
+
this.openingTimer = void 0;
|
|
173
|
+
}
|
|
174
|
+
stateError() {
|
|
175
|
+
return this.state.kind === "stopped" ? this.state.cause : failure(
|
|
176
|
+
"acp_session_interaction_binding",
|
|
177
|
+
"The native session interaction lifecycle is out of order."
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
throwIfStopped() {
|
|
181
|
+
if (this.state.kind === "stopped") throw this.state.cause;
|
|
182
|
+
}
|
|
183
|
+
async join() {
|
|
184
|
+
while (this.entered.size > 0) await Promise.all([...this.entered].map((decision) => decision.join()));
|
|
185
|
+
if (this.state.kind === "stopped") this.input.stopped.removeEventListener("abort", this.onStopped);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function failure(reasonCode, message, cause) {
|
|
189
|
+
return new AcpProviderSessionError(reasonCode, message, cause === void 0 ? void 0 : { cause });
|
|
190
|
+
}
|
|
191
|
+
export {
|
|
192
|
+
SessionInteractionBroker
|
|
193
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type AcpExtensionRegistry } from '@agen-ai/acp-transport';
|
|
2
|
+
import type { AcpSessionInteractionSelection } from './contracts.js';
|
|
3
|
+
export declare function createSessionInteractionExtensionRegistry(selection: AcpSessionInteractionSelection): AcpExtensionRegistry;
|
|
4
|
+
//# sourceMappingURL=extensions.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createAcpExtensionRegistry } from "@agen-ai/acp-transport";
|
|
2
|
+
import {
|
|
3
|
+
GROK_FOLDER_TRUST_METHOD,
|
|
4
|
+
validateNativeFolderTrustRequest,
|
|
5
|
+
validateNativeFolderTrustResult
|
|
6
|
+
} from "./grokFolderTrust.js";
|
|
7
|
+
import {
|
|
8
|
+
GROK_PLAN_REVIEW_METHOD,
|
|
9
|
+
GROK_QUEUE_METHOD,
|
|
10
|
+
GROK_SESSION_NOTIFICATION_METHOD,
|
|
11
|
+
validateNativePlanReviewRequest,
|
|
12
|
+
validateNativePlanReviewResult
|
|
13
|
+
} from "./grokPlanReview.js";
|
|
14
|
+
function sessionNotification(input) {
|
|
15
|
+
return input !== null && typeof input === "object" && !Array.isArray(input) && "sessionId" in input && typeof input.sessionId === "string" && input.sessionId.length > 0 && input.sessionId.length <= 256;
|
|
16
|
+
}
|
|
17
|
+
function createSessionInteractionExtensionRegistry(selection) {
|
|
18
|
+
if (selection.mapping === "grok-build-1.0.60-session-requests-v1") return createAcpExtensionRegistry([
|
|
19
|
+
{
|
|
20
|
+
method: GROK_PLAN_REVIEW_METHOD,
|
|
21
|
+
direction: "inbound_request",
|
|
22
|
+
validateParams: validateNativePlanReviewRequest,
|
|
23
|
+
validateResult: validateNativePlanReviewResult
|
|
24
|
+
},
|
|
25
|
+
{ method: GROK_QUEUE_METHOD, direction: "inbound_notification", validateParams: (input) => sessionNotification(input) && "entries" in input && Array.isArray(input.entries) && input.entries.length <= 256 },
|
|
26
|
+
{ method: GROK_SESSION_NOTIFICATION_METHOD, direction: "inbound_notification", validateParams: (input) => sessionNotification(input) && "update" in input && input.update !== null && typeof input.update === "object" && !Array.isArray(input.update) && "sessionUpdate" in input.update && typeof input.update.sessionUpdate === "string" }
|
|
27
|
+
]);
|
|
28
|
+
return createAcpExtensionRegistry([{
|
|
29
|
+
method: GROK_FOLDER_TRUST_METHOD,
|
|
30
|
+
direction: "inbound_request",
|
|
31
|
+
validateParams: validateNativeFolderTrustRequest,
|
|
32
|
+
validateResult: validateNativeFolderTrustResult
|
|
33
|
+
}]);
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
createSessionInteractionExtensionRegistry
|
|
37
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type AgentProviderConversationId, type AgentSessionDecisionRequest, type AgentSessionId } from '@agen-ai/agent-protocol';
|
|
2
|
+
export declare const GROK_FOLDER_TRUST_METHOD = "_x.ai/folder_trust/request";
|
|
3
|
+
export declare const GROK_FOLDER_TRUST_CAPABILITY_META: Readonly<{
|
|
4
|
+
'x.ai/folderTrust': Readonly<{
|
|
5
|
+
interactive: true;
|
|
6
|
+
}>;
|
|
7
|
+
}>;
|
|
8
|
+
declare const CONFIGURATION_EXPLANATIONS: Readonly<{
|
|
9
|
+
mcp: "Project tool servers";
|
|
10
|
+
plugins: "Project plugins";
|
|
11
|
+
permission: "Project tool-permission rules";
|
|
12
|
+
lsp: "Project language servers";
|
|
13
|
+
envrc: "Project shell environment scripts";
|
|
14
|
+
claude: "Imported compatibility settings";
|
|
15
|
+
hooks: "Project hooks";
|
|
16
|
+
agents: "Project agent definitions";
|
|
17
|
+
roles: "Project role definitions";
|
|
18
|
+
personas: "Project persona definitions";
|
|
19
|
+
workflows: "Project workflows";
|
|
20
|
+
instructions: "Project instructions";
|
|
21
|
+
skills: "Project skills";
|
|
22
|
+
}>;
|
|
23
|
+
type ConfigurationKind = keyof typeof CONFIGURATION_EXPLANATIONS;
|
|
24
|
+
export interface NativeFolderTrustRequest {
|
|
25
|
+
readonly sessionId: AgentProviderConversationId;
|
|
26
|
+
readonly cwd: string;
|
|
27
|
+
readonly workspace: string;
|
|
28
|
+
readonly configKinds: readonly ConfigurationKind[];
|
|
29
|
+
}
|
|
30
|
+
export type NativeFolderTrustResult = Readonly<{
|
|
31
|
+
outcome: 'trust' | 'reject';
|
|
32
|
+
}>;
|
|
33
|
+
export declare function parseNativeFolderTrustRequest(input: unknown): NativeFolderTrustRequest;
|
|
34
|
+
export declare function validateNativeFolderTrustRequest(input: unknown): boolean;
|
|
35
|
+
export declare function validateNativeFolderTrustResult(input: unknown): boolean;
|
|
36
|
+
export declare function folderTrustDecision(input: Readonly<{
|
|
37
|
+
native: NativeFolderTrustRequest;
|
|
38
|
+
sessionId: AgentSessionId;
|
|
39
|
+
requestNumber: number;
|
|
40
|
+
}>): AgentSessionDecisionRequest;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=grokFolderTrust.d.ts.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { posix } from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
parseAgentProviderConversationId,
|
|
4
|
+
parseAgentSessionDecisionRequest
|
|
5
|
+
} from "@agen-ai/agent-protocol";
|
|
6
|
+
import { AcpProviderSessionError } from "../session/errors.js";
|
|
7
|
+
const GROK_FOLDER_TRUST_METHOD = "_x.ai/folder_trust/request";
|
|
8
|
+
const GROK_FOLDER_TRUST_CAPABILITY_META = Object.freeze({
|
|
9
|
+
"x.ai/folderTrust": Object.freeze({ interactive: true })
|
|
10
|
+
});
|
|
11
|
+
const CONFIGURATION_EXPLANATIONS = Object.freeze({
|
|
12
|
+
mcp: "Project tool servers",
|
|
13
|
+
plugins: "Project plugins",
|
|
14
|
+
permission: "Project tool-permission rules",
|
|
15
|
+
lsp: "Project language servers",
|
|
16
|
+
envrc: "Project shell environment scripts",
|
|
17
|
+
claude: "Imported compatibility settings",
|
|
18
|
+
hooks: "Project hooks",
|
|
19
|
+
agents: "Project agent definitions",
|
|
20
|
+
roles: "Project role definitions",
|
|
21
|
+
personas: "Project persona definitions",
|
|
22
|
+
workflows: "Project workflows",
|
|
23
|
+
instructions: "Project instructions",
|
|
24
|
+
skills: "Project skills"
|
|
25
|
+
});
|
|
26
|
+
function configurationKind(input) {
|
|
27
|
+
return typeof input === "string" && Object.hasOwn(CONFIGURATION_EXPLANATIONS, input);
|
|
28
|
+
}
|
|
29
|
+
function exactObject(input, keys) {
|
|
30
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) return false;
|
|
31
|
+
const own = Reflect.ownKeys(input);
|
|
32
|
+
return own.length === keys.length && keys.every((key) => Object.hasOwn(input, key));
|
|
33
|
+
}
|
|
34
|
+
function invalidRequest() {
|
|
35
|
+
return new AcpProviderSessionError("acp_session_interaction_invalid", "The native workspace decision violates the selected mapping.");
|
|
36
|
+
}
|
|
37
|
+
function workspacePath(input) {
|
|
38
|
+
if (typeof input !== "string" || input.length === 0 || Buffer.byteLength(input, "utf8") > 4096 || /[\p{Cc}\p{Cf}]/u.test(input) || !posix.isAbsolute(input) || posix.normalize(input) !== input) {
|
|
39
|
+
throw invalidRequest();
|
|
40
|
+
}
|
|
41
|
+
return input;
|
|
42
|
+
}
|
|
43
|
+
function parseNativeFolderTrustRequest(input) {
|
|
44
|
+
if (!exactObject(input, ["sessionId", "cwd", "workspace", "configKinds"]) || Buffer.byteLength(JSON.stringify(input), "utf8") > 65536 || typeof input.sessionId !== "string" || Buffer.byteLength(input.sessionId, "utf8") > 512 || !Array.isArray(input.configKinds) || input.configKinds.length < 1 || input.configKinds.length > 13) {
|
|
45
|
+
throw invalidRequest();
|
|
46
|
+
}
|
|
47
|
+
const kinds = [];
|
|
48
|
+
for (const kind of input.configKinds) {
|
|
49
|
+
if (!configurationKind(kind) || kinds.includes(kind)) throw invalidRequest();
|
|
50
|
+
kinds.push(kind);
|
|
51
|
+
}
|
|
52
|
+
return Object.freeze({
|
|
53
|
+
sessionId: parseAgentProviderConversationId(input.sessionId),
|
|
54
|
+
cwd: workspacePath(input.cwd),
|
|
55
|
+
workspace: workspacePath(input.workspace),
|
|
56
|
+
configKinds: Object.freeze(kinds)
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function validateNativeFolderTrustRequest(input) {
|
|
60
|
+
try {
|
|
61
|
+
parseNativeFolderTrustRequest(input);
|
|
62
|
+
return true;
|
|
63
|
+
} catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function validateNativeFolderTrustResult(input) {
|
|
68
|
+
return exactObject(input, ["outcome"]) && (input.outcome === "trust" || input.outcome === "reject");
|
|
69
|
+
}
|
|
70
|
+
function folderTrustDecision(input) {
|
|
71
|
+
const detected = input.native.configKinds.map((kind) => CONFIGURATION_EXPLANATIONS[kind]).join("; ");
|
|
72
|
+
return parseAgentSessionDecisionRequest({
|
|
73
|
+
kind: "decision",
|
|
74
|
+
requestId: `session-interaction:${input.requestNumber}`,
|
|
75
|
+
sessionId: input.sessionId,
|
|
76
|
+
title: "Allow workspace configuration?",
|
|
77
|
+
description: `Detected configuration: ${detected}. Workspace configuration can start tools or hooks and affect provider behavior. These detected categories do not restrict the scope of an authorization.`,
|
|
78
|
+
target: { kind: "workspace", path: input.native.workspace },
|
|
79
|
+
choices: [
|
|
80
|
+
{
|
|
81
|
+
choiceId: "grant",
|
|
82
|
+
label: "Allow workspace configuration",
|
|
83
|
+
description: "Authorize configuration for the target workspace, including future configuration. The provider manages persistent trust, which may affect later or sibling sessions using the same profile. This choice does not confirm immediate loading or successful persistence.",
|
|
84
|
+
consequence: {
|
|
85
|
+
kind: "grant",
|
|
86
|
+
scope: "workspace",
|
|
87
|
+
persistence: { kind: "provider_managed", duration: "persistent", mayAffectOtherSessions: true }
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
choiceId: "decline",
|
|
92
|
+
label: "Keep configuration gated",
|
|
93
|
+
description: "Keep workspace configuration gated. Repeat questions may be suppressed for this workspace while this native agent lives. This choice does not revoke an existing grant or promise a persisted denial.",
|
|
94
|
+
consequence: { kind: "decline", configuration: "remains_gated" }
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
export {
|
|
100
|
+
GROK_FOLDER_TRUST_CAPABILITY_META,
|
|
101
|
+
GROK_FOLDER_TRUST_METHOD,
|
|
102
|
+
folderTrustDecision,
|
|
103
|
+
parseNativeFolderTrustRequest,
|
|
104
|
+
validateNativeFolderTrustRequest,
|
|
105
|
+
validateNativeFolderTrustResult
|
|
106
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type AgentProviderConversationId, type AgentSessionId, type AgentSessionPlanReviewRequest, type AgentSessionPlanReviewResponse } from '@agen-ai/agent-protocol';
|
|
2
|
+
import type { AcpJsonValue } from '@agen-ai/acp-transport';
|
|
3
|
+
export declare const GROK_PLAN_REVIEW_METHOD = "_x.ai/exit_plan_mode";
|
|
4
|
+
export declare const GROK_QUEUE_METHOD = "_x.ai/queue/changed";
|
|
5
|
+
export declare const GROK_SESSION_NOTIFICATION_METHOD = "_x.ai/session_notification";
|
|
6
|
+
export interface NativePlanReviewRequest {
|
|
7
|
+
readonly sessionId: AgentProviderConversationId;
|
|
8
|
+
readonly toolCallId: string;
|
|
9
|
+
readonly planContent: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function parseNativePlanReviewRequest(input: unknown): NativePlanReviewRequest;
|
|
12
|
+
export declare function validateNativePlanReviewRequest(input: unknown): boolean;
|
|
13
|
+
export declare function validateNativePlanReviewResult(input: unknown): boolean;
|
|
14
|
+
export declare function planReviewRequest(native: NativePlanReviewRequest, sessionId: AgentSessionId, requestNumber: number): AgentSessionPlanReviewRequest;
|
|
15
|
+
export declare function nativePlanReviewReply(response: AgentSessionPlanReviewResponse): AcpJsonValue;
|
|
16
|
+
//# sourceMappingURL=grokPlanReview.d.ts.map
|
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type AcpJsonValue, type AcpReverseRequestContext } from '@agen-ai/acp-transport';
|
|
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;
|
|
10
|
+
export declare function sessionDecisionError(code: string, message: string): AgentError;
|
|
11
|
+
export declare class NativeSessionDecision {
|
|
12
|
+
private readonly input;
|
|
13
|
+
readonly handle: AgentProviderSessionInteraction;
|
|
14
|
+
readonly nativeReply: Promise<AcpJsonValue>;
|
|
15
|
+
private readonly answer;
|
|
16
|
+
private readonly terminal;
|
|
17
|
+
private readonly presentation;
|
|
18
|
+
private state;
|
|
19
|
+
private callback;
|
|
20
|
+
private callbackSettled;
|
|
21
|
+
private nativeSettled;
|
|
22
|
+
private presented;
|
|
23
|
+
private retired;
|
|
24
|
+
private readonly timer;
|
|
25
|
+
private readonly onNativeAbort;
|
|
26
|
+
constructor(input: Readonly<{
|
|
27
|
+
request: AgentSessionDecisionRequest | AgentSessionPlanReviewRequest;
|
|
28
|
+
nativeContext: Pick<AcpReverseRequestContext, 'id' | 'signal' | 'settled'>;
|
|
29
|
+
hostDeadlineAt?: number;
|
|
30
|
+
prepareContinuation?: PreparePlanContinuation;
|
|
31
|
+
accepting: () => boolean;
|
|
32
|
+
fail: (cause: Error) => void;
|
|
33
|
+
retire: () => void;
|
|
34
|
+
}>);
|
|
35
|
+
observeNativeSignal(): void;
|
|
36
|
+
present(handler: AgentProviderSessionInteractionContext['onRequest']): void;
|
|
37
|
+
stop(terminal: AgentSessionInteractionTerminal, cause: Error): void;
|
|
38
|
+
private finalize;
|
|
39
|
+
private expire;
|
|
40
|
+
private nativeAborted;
|
|
41
|
+
private respond;
|
|
42
|
+
private prepareResponse;
|
|
43
|
+
private maybeRetire;
|
|
44
|
+
join(): Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=request.d.ts.map
|