@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,20 @@
|
|
|
1
|
+
import type { MaybePromise } from '@agen-ai/agent-runtime';
|
|
2
|
+
import type { AcpConnectionFactoryContext, AcpConnectionStreams } from '../../contracts.js';
|
|
3
|
+
export interface NodeAcpExecutionInput {
|
|
4
|
+
readonly executable: string;
|
|
5
|
+
readonly args: readonly string[];
|
|
6
|
+
readonly cwd: string;
|
|
7
|
+
readonly env: Readonly<Record<string, string>>;
|
|
8
|
+
readonly shutdownTimeoutMs: number;
|
|
9
|
+
}
|
|
10
|
+
/** Reserve fresh inert state; resource acquisition and execution belong to start. */
|
|
11
|
+
export interface NodeAcpExecutionBoundary {
|
|
12
|
+
readonly reserve: (input: NodeAcpExecutionInput) => NodeAcpExecutionReservation;
|
|
13
|
+
}
|
|
14
|
+
/** Own the workload before execution, including descendants in other groups or sessions. */
|
|
15
|
+
export interface NodeAcpExecutionReservation {
|
|
16
|
+
readonly start: (context: AcpConnectionFactoryContext) => MaybePromise<AcpConnectionStreams>;
|
|
17
|
+
/** Fence startup, terminate and join the workload and streams; failed release remains retryable. */
|
|
18
|
+
readonly release: () => MaybePromise<void>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=types.d.ts.map
|
|
File without changes
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { AgentProviderDriver } from '@agen-ai/agent-runtime';
|
|
2
2
|
import type { CreateNodeAcpProviderDriverOptions } from './contracts.js';
|
|
3
3
|
export type { NodeAcpLaunchOptions, CreateNodeAcpProviderDriverOptions } from './contracts.js';
|
|
4
|
-
|
|
4
|
+
export type { NodeAcpExecutionInput, NodeAcpExecutionBoundary, NodeAcpExecutionReservation } from './execution/types.js';
|
|
5
|
+
export { NodeAcpExecutionCleanupError, isNodeAcpExecutionCleanupError } from './execution/errors.js';
|
|
6
|
+
/** Uses the supplied inert execution owner, or the default owned POSIX process-group launcher. */
|
|
5
7
|
export declare function createNodeAcpProviderDriver(options: CreateNodeAcpProviderDriverOptions): AgentProviderDriver;
|
|
6
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/node/index.js
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { resolveNodeAcpLaunch } from "./launch.js";
|
|
1
|
+
import { createAcpProviderDriverInternal } from "../driverFactory.js";
|
|
2
|
+
import { resolveNodeAcpLaunch, resolveNodeAcpSessionInteractions } from "./launch.js";
|
|
3
3
|
import { spawnAcpProcess } from "./process.js";
|
|
4
|
+
import { executionReservationSource } from "./execution/owner.js";
|
|
5
|
+
import { NodeAcpExecutionCleanupError, isNodeAcpExecutionCleanupError } from "./execution/errors.js";
|
|
4
6
|
function createNodeAcpProviderDriver(options) {
|
|
5
7
|
const launch = resolveNodeAcpLaunch(options.launch);
|
|
6
|
-
|
|
8
|
+
const boundary = options.executionBoundary;
|
|
9
|
+
if (boundary === void 0 && Object.hasOwn(options, "executionBoundary")) throw new TypeError("An explicitly supplied execution boundary must be valid.");
|
|
10
|
+
const reserve = boundary === void 0 ? void 0 : executionReservationSource(boundary, launch);
|
|
11
|
+
const sessionInteractions = resolveNodeAcpSessionInteractions(options, launch);
|
|
12
|
+
if (sessionInteractions && !reserve) throw new TypeError("Selected Grok session mappings require an exclusive descendant-owning execution boundary.");
|
|
13
|
+
return createAcpProviderDriverInternal({
|
|
7
14
|
providerKey: options.providerKey,
|
|
8
|
-
|
|
15
|
+
connectionSource: reserve ? { kind: "reserved", reserve } : { kind: "factory", create: (context) => spawnAcpProcess(launch, context) },
|
|
9
16
|
...options.unknownExtensionPolicy !== void 0 ? { unknownExtensionPolicy: options.unknownExtensionPolicy } : {},
|
|
17
|
+
...options.permissionMapping !== void 0 ? { permissionMapping: options.permissionMapping } : {},
|
|
10
18
|
...options.limits ? { limits: options.limits } : {},
|
|
11
19
|
...options.configuration ? { configuration: options.configuration } : {},
|
|
12
20
|
...options.authentication ? { authentication: options.authentication } : {},
|
|
13
21
|
...options.host ? { host: options.host } : {},
|
|
14
22
|
...options.mcpServers ? { mcpServers: options.mcpServers } : {},
|
|
15
23
|
...options.sessionMcpBinding ? { sessionMcpBinding: options.sessionMcpBinding } : {}
|
|
16
|
-
});
|
|
24
|
+
}, sessionInteractions);
|
|
17
25
|
}
|
|
18
26
|
export {
|
|
19
|
-
|
|
27
|
+
NodeAcpExecutionCleanupError,
|
|
28
|
+
createNodeAcpProviderDriver,
|
|
29
|
+
isNodeAcpExecutionCleanupError
|
|
20
30
|
};
|
package/dist/node/launch.d.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import type { NodeAcpLaunchOptions } from './contracts.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
readonly cwd: string;
|
|
6
|
-
readonly env: Readonly<Record<string, string>>;
|
|
7
|
-
readonly shutdownTimeoutMs: number;
|
|
8
|
-
}
|
|
1
|
+
import type { CreateNodeAcpProviderDriverOptions, NodeAcpLaunchOptions } from './contracts.js';
|
|
2
|
+
import type { AcpSessionInteractionSelection } from '../sessionInteractions/contracts.js';
|
|
3
|
+
import type { NodeAcpExecutionInput } from './execution/types.js';
|
|
4
|
+
export type ResolvedNodeAcpLaunch = NodeAcpExecutionInput;
|
|
9
5
|
export declare function resolveNodeAcpLaunch(launch: NodeAcpLaunchOptions): ResolvedNodeAcpLaunch;
|
|
6
|
+
export declare function resolveNodeAcpSessionInteractions(options: CreateNodeAcpProviderDriverOptions, launch: ResolvedNodeAcpLaunch): AcpSessionInteractionSelection | undefined;
|
|
10
7
|
//# sourceMappingURL=launch.d.ts.map
|
package/dist/node/launch.js
CHANGED
|
@@ -31,6 +31,18 @@ function resolveNodeAcpLaunch(launch) {
|
|
|
31
31
|
shutdownTimeoutMs
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
|
+
function resolveNodeAcpSessionInteractions(options, launch) {
|
|
35
|
+
const selection = options.sessionInteractions;
|
|
36
|
+
if (selection === void 0 && !Object.hasOwn(options, "sessionInteractions")) return void 0;
|
|
37
|
+
if (!selection || typeof selection !== "object" || Array.isArray(selection) || Reflect.ownKeys(selection).length !== 1 || !Object.hasOwn(selection, "mapping") || !["grok-folder-trust-v1", "grok-build-1.0.60-session-requests-v1"].includes(selection.mapping)) {
|
|
38
|
+
throw new TypeError("ACP session interaction selection must name the supported mapping without additional fields.");
|
|
39
|
+
}
|
|
40
|
+
if (launch.args.length !== 3 || launch.args[0] !== "agent" || launch.args[1] !== "--no-leader" || launch.args[2] !== "stdio") {
|
|
41
|
+
throw new TypeError("Selected Grok session mappings require exact direct arguments: agent --no-leader stdio.");
|
|
42
|
+
}
|
|
43
|
+
return Object.freeze({ mapping: selection.mapping });
|
|
44
|
+
}
|
|
34
45
|
export {
|
|
35
|
-
resolveNodeAcpLaunch
|
|
46
|
+
resolveNodeAcpLaunch,
|
|
47
|
+
resolveNodeAcpSessionInteractions
|
|
36
48
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface AcpPermissionOptionSignature {
|
|
2
|
+
readonly optionId: string;
|
|
3
|
+
readonly kind: 'allow_once' | 'reject_once' | 'allow_always' | 'reject_always';
|
|
4
|
+
}
|
|
5
|
+
export interface AcpPermissionMenuSignature {
|
|
6
|
+
readonly toolKind: 'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'think' | 'fetch' | 'switch_mode' | 'other';
|
|
7
|
+
readonly options: readonly [AcpPermissionOptionSignature, ...AcpPermissionOptionSignature[]];
|
|
8
|
+
}
|
|
9
|
+
/** Complete declarations attest that these exact menus add no unrepresented choice effects. */
|
|
10
|
+
export type AcpPermissionMappingSelection = Readonly<{
|
|
11
|
+
mapping: 'once-only-v1';
|
|
12
|
+
}> | Readonly<{
|
|
13
|
+
mapping: 'grok-build-1.0.50-edit-remembrance-v1';
|
|
14
|
+
}> | Readonly<{
|
|
15
|
+
mapping: 'declared-complete-consequences-v1';
|
|
16
|
+
agentInfo: Readonly<{
|
|
17
|
+
name: string;
|
|
18
|
+
version: string;
|
|
19
|
+
}>;
|
|
20
|
+
menus: readonly [AcpPermissionMenuSignature, ...AcpPermissionMenuSignature[]];
|
|
21
|
+
}>;
|
|
22
|
+
//# sourceMappingURL=contracts.d.ts.map
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AcpJsonObject } from '@agen-ai/acp-transport';
|
|
2
|
+
import { type AgentApprovalOption } from '@agen-ai/agent-protocol';
|
|
3
|
+
import type { AcpPermissionMappingSelection } from './contracts.js';
|
|
4
|
+
export interface MappedPermissionOptions {
|
|
5
|
+
readonly options: readonly AgentApprovalOption[];
|
|
6
|
+
readonly nativeOptions: ReadonlyMap<string, string>;
|
|
7
|
+
readonly warning?: Readonly<{
|
|
8
|
+
code: string;
|
|
9
|
+
message: string;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
export declare function mapAcpPermissionOptions(selection: AcpPermissionMappingSelection, params: AcpJsonObject): MappedPermissionOptions;
|
|
13
|
+
//# sourceMappingURL=mapping.d.ts.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { parseAgentApprovalOptionId } from "@agen-ai/agent-protocol";
|
|
2
|
+
import { AcpProviderSessionError } from "../session/errors.js";
|
|
3
|
+
import { displayText, nativeObject, nativeString } from "../session/native.js";
|
|
4
|
+
const GROK_EDIT_MENU = {
|
|
5
|
+
toolKind: "edit",
|
|
6
|
+
options: [
|
|
7
|
+
{ optionId: "allow-edits-session", kind: "allow_always" },
|
|
8
|
+
{ optionId: "allow-once", kind: "allow_once" },
|
|
9
|
+
{ optionId: "reject-once", kind: "reject_once" }
|
|
10
|
+
]
|
|
11
|
+
};
|
|
12
|
+
function unsupportedConsequence() {
|
|
13
|
+
throw new AcpProviderSessionError("acp_permission_consequence_unsupported", "The selected ACP permission mapping does not represent this complete native menu.");
|
|
14
|
+
}
|
|
15
|
+
function readNativeOptions(params) {
|
|
16
|
+
if (!Array.isArray(params.options) || params.options.length < 1 || params.options.length > 16) {
|
|
17
|
+
throw new AcpProviderSessionError("acp_permission_options_invalid", "ACP permission menus require between one and sixteen options.");
|
|
18
|
+
}
|
|
19
|
+
const ids = /* @__PURE__ */ new Set();
|
|
20
|
+
return params.options.map((value) => {
|
|
21
|
+
const option = nativeObject(value, "permission option");
|
|
22
|
+
const optionId = nativeString(option.optionId, "permission option identity");
|
|
23
|
+
if (!optionId || optionId.length > 256 || optionId !== optionId.trim() || /[\u0000-\u001f\u007f-\u009f]/u.test(optionId)) {
|
|
24
|
+
throw new AcpProviderSessionError("acp_permission_options_invalid", "ACP permission option identities must be bounded canonical strings.");
|
|
25
|
+
}
|
|
26
|
+
if (ids.has(optionId)) throw new AcpProviderSessionError("acp_permission_option_duplicate", "ACP repeated a permission option identity.");
|
|
27
|
+
ids.add(optionId);
|
|
28
|
+
const kind = option.kind;
|
|
29
|
+
if (kind !== "allow_once" && kind !== "reject_once" && kind !== "allow_always" && kind !== "reject_always") {
|
|
30
|
+
throw new AcpProviderSessionError("acp_permission_options_invalid", "ACP permission option kind is unsupported.");
|
|
31
|
+
}
|
|
32
|
+
return { optionId, kind, source: option };
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function matchesMenu(menu, tool, options) {
|
|
36
|
+
return menu.toolKind === tool.kind && menu.options.length === options.length && menu.options.every((option, index) => option.optionId === options[index]?.optionId && option.kind === options[index]?.kind);
|
|
37
|
+
}
|
|
38
|
+
function assertCompleteChoiceFields(params, options) {
|
|
39
|
+
if (Object.keys(params).some((key) => !["sessionId", "toolCall", "options"].includes(key)) || options.some((option) => Object.keys(option.source).some((key) => !["optionId", "name", "kind"].includes(key)))) {
|
|
40
|
+
unsupportedConsequence();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function qualifiedGrokEdit(tool, options) {
|
|
44
|
+
const input = tool.rawInput;
|
|
45
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) return false;
|
|
46
|
+
return matchesMenu(GROK_EDIT_MENU, tool, options) && Object.keys(input).length === 5 && typeof input.file_path === "string" && input.file_path.length > 0 && typeof input.old_string === "string" && typeof input.new_string === "string" && input.variant === "SearchReplace" && input.replace_all === false;
|
|
47
|
+
}
|
|
48
|
+
function mapAcpPermissionOptions(selection, params) {
|
|
49
|
+
const native = readNativeOptions(params);
|
|
50
|
+
const tool = nativeObject(params.toolCall, "permission tool");
|
|
51
|
+
let complete = false;
|
|
52
|
+
let fallback = false;
|
|
53
|
+
if (selection.mapping === "declared-complete-consequences-v1") {
|
|
54
|
+
assertCompleteChoiceFields(params, native);
|
|
55
|
+
if (!selection.menus.some((menu) => matchesMenu(menu, tool, native))) unsupportedConsequence();
|
|
56
|
+
complete = true;
|
|
57
|
+
} else if (selection.mapping === "grok-build-1.0.50-edit-remembrance-v1") {
|
|
58
|
+
if (tool.kind === "edit") {
|
|
59
|
+
assertCompleteChoiceFields(params, native);
|
|
60
|
+
if (!qualifiedGrokEdit(tool, native)) unsupportedConsequence();
|
|
61
|
+
complete = true;
|
|
62
|
+
} else fallback = true;
|
|
63
|
+
}
|
|
64
|
+
const nativeOptions = /* @__PURE__ */ new Map();
|
|
65
|
+
const options = [];
|
|
66
|
+
for (const [index, option] of native.entries()) {
|
|
67
|
+
const remembered = option.kind === "allow_always" || option.kind === "reject_always";
|
|
68
|
+
if (remembered && !complete) continue;
|
|
69
|
+
const approved = option.kind === "allow_once" || option.kind === "allow_always";
|
|
70
|
+
const optionId = parseAgentApprovalOptionId(`acp:option:${index + 1}`);
|
|
71
|
+
nativeOptions.set(optionId, option.optionId);
|
|
72
|
+
options.push({
|
|
73
|
+
optionId,
|
|
74
|
+
label: remembered ? approved ? "Allow and remember" : "Deny and remember" : displayText(option.source.name, approved ? "Allow once" : "Reject once"),
|
|
75
|
+
decision: approved ? "approved" : "denied",
|
|
76
|
+
consequence: remembered ? {
|
|
77
|
+
kind: "provider_managed_remembrance",
|
|
78
|
+
duration: { kind: "unknown" },
|
|
79
|
+
applicability: { kind: "unknown" },
|
|
80
|
+
explanation: "The provider is asked to remember this decision. Duration and future matching are unspecified. Delivery does not establish successful persistence or revocation."
|
|
81
|
+
} : { kind: "scoped", persistence: "once", scope: { kind: "exact_action" } }
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (options.length === 0) {
|
|
85
|
+
if (fallback) unsupportedConsequence();
|
|
86
|
+
throw new AcpProviderSessionError("acp_permission_persistence_unsupported", "ACP offered no supported once-only permission choice.");
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
options,
|
|
90
|
+
nativeOptions,
|
|
91
|
+
...fallback ? { warning: {
|
|
92
|
+
code: "acp_permission_remembrance_unmapped",
|
|
93
|
+
message: "The selected ACP profile does not map remembered choices for this action. Only once-only choices are available."
|
|
94
|
+
} } : {}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export {
|
|
98
|
+
mapAcpPermissionOptions
|
|
99
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AgentApprovalCapability } from '@agen-ai/agent-protocol';
|
|
2
|
+
import type { AcpPermissionMappingSelection } from './contracts.js';
|
|
3
|
+
export declare function resolveAcpPermissionMapping(value: unknown): AcpPermissionMappingSelection;
|
|
4
|
+
export declare function acpPermissionCapability(selection: AcpPermissionMappingSelection): AgentApprovalCapability;
|
|
5
|
+
//# sourceMappingURL=selection.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
function selectionObject(value, keys) {
|
|
2
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || Reflect.ownKeys(value).some((key) => typeof key !== "string" || !keys.includes(key))) {
|
|
3
|
+
throw new TypeError("ACP permission mapping selections must contain only their declared fields.");
|
|
4
|
+
}
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
function canonicalText(value, maximum, identity = false) {
|
|
8
|
+
if (typeof value !== "string" || !value || value !== value.trim() || value.length > maximum || (identity ? /[\p{Cc}\p{Cf}]/u : /[\u0000-\u001f\u007f-\u009f]/u).test(value)) {
|
|
9
|
+
throw new TypeError("ACP permission mapping identities must be bounded canonical strings.");
|
|
10
|
+
}
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
function optionKind(value) {
|
|
14
|
+
switch (value) {
|
|
15
|
+
case "allow_once":
|
|
16
|
+
case "reject_once":
|
|
17
|
+
case "allow_always":
|
|
18
|
+
case "reject_always":
|
|
19
|
+
return value;
|
|
20
|
+
default:
|
|
21
|
+
throw new TypeError("ACP permission option kind is invalid.");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function toolKind(value) {
|
|
25
|
+
switch (value) {
|
|
26
|
+
case "read":
|
|
27
|
+
case "edit":
|
|
28
|
+
case "delete":
|
|
29
|
+
case "move":
|
|
30
|
+
case "search":
|
|
31
|
+
case "execute":
|
|
32
|
+
case "think":
|
|
33
|
+
case "fetch":
|
|
34
|
+
case "switch_mode":
|
|
35
|
+
case "other":
|
|
36
|
+
return value;
|
|
37
|
+
default:
|
|
38
|
+
throw new TypeError("ACP permission tool kind is invalid.");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function boundedNonempty(value, parse) {
|
|
42
|
+
if (!Array.isArray(value) || value.length < 1 || value.length > 16) {
|
|
43
|
+
throw new TypeError("ACP permission declarations require between one and sixteen entries.");
|
|
44
|
+
}
|
|
45
|
+
const first = parse(value[0]);
|
|
46
|
+
return Object.freeze([first, ...Array.from(value.slice(1), parse)]);
|
|
47
|
+
}
|
|
48
|
+
function menuSignature(value) {
|
|
49
|
+
const menu = selectionObject(value, ["toolKind", "options"]);
|
|
50
|
+
const ids = /* @__PURE__ */ new Set();
|
|
51
|
+
const options = boundedNonempty(menu.options, (entry) => {
|
|
52
|
+
const option = selectionObject(entry, ["optionId", "kind"]);
|
|
53
|
+
const optionId = canonicalText(option.optionId, 256);
|
|
54
|
+
if (ids.has(optionId)) throw new TypeError("ACP declared permission option identities must be unique.");
|
|
55
|
+
ids.add(optionId);
|
|
56
|
+
return Object.freeze({ optionId, kind: optionKind(option.kind) });
|
|
57
|
+
});
|
|
58
|
+
return Object.freeze({ toolKind: toolKind(menu.toolKind), options });
|
|
59
|
+
}
|
|
60
|
+
function resolveAcpPermissionMapping(value) {
|
|
61
|
+
if (value === void 0) return Object.freeze({ mapping: "once-only-v1" });
|
|
62
|
+
const selection = selectionObject(value, ["mapping", "agentInfo", "menus"]);
|
|
63
|
+
if (selection.mapping === "once-only-v1" || selection.mapping === "grok-build-1.0.50-edit-remembrance-v1") {
|
|
64
|
+
selectionObject(value, ["mapping"]);
|
|
65
|
+
return Object.freeze({ mapping: selection.mapping });
|
|
66
|
+
}
|
|
67
|
+
if (selection.mapping !== "declared-complete-consequences-v1") throw new TypeError("ACP permission mapping is invalid.");
|
|
68
|
+
const info = selectionObject(selection.agentInfo, ["name", "version"]);
|
|
69
|
+
const agentInfo = Object.freeze({ name: canonicalText(info.name, 256, true), version: canonicalText(info.version, 160, true) });
|
|
70
|
+
const signatures = /* @__PURE__ */ new Set();
|
|
71
|
+
const menus = boundedNonempty(selection.menus, (entry) => {
|
|
72
|
+
const menu = menuSignature(entry);
|
|
73
|
+
const signature = JSON.stringify(menu);
|
|
74
|
+
if (signatures.has(signature)) throw new TypeError("ACP declared permission menus must be unique.");
|
|
75
|
+
signatures.add(signature);
|
|
76
|
+
return menu;
|
|
77
|
+
});
|
|
78
|
+
return Object.freeze({ mapping: selection.mapping, agentInfo, menus });
|
|
79
|
+
}
|
|
80
|
+
function acpPermissionCapability(selection) {
|
|
81
|
+
const once = { persistence: "once", scopeKinds: ["exact_action"] };
|
|
82
|
+
if (selection.mapping === "once-only-v1") {
|
|
83
|
+
return { kind: "supported", modes: [once], providerManagedRemembrance: false };
|
|
84
|
+
}
|
|
85
|
+
if (selection.mapping === "grok-build-1.0.50-edit-remembrance-v1") {
|
|
86
|
+
return { kind: "supported", modes: [once], providerManagedRemembrance: true };
|
|
87
|
+
}
|
|
88
|
+
const options = selection.menus.flatMap((menu) => menu.options);
|
|
89
|
+
const precise = options.some((option) => option.kind === "allow_once" || option.kind === "reject_once");
|
|
90
|
+
const remembered = options.some((option) => option.kind === "allow_always" || option.kind === "reject_always");
|
|
91
|
+
return remembered ? { kind: "supported", modes: precise ? [once] : [], providerManagedRemembrance: true } : { kind: "supported", modes: [once], providerManagedRemembrance: false };
|
|
92
|
+
}
|
|
93
|
+
export {
|
|
94
|
+
acpPermissionCapability,
|
|
95
|
+
resolveAcpPermissionMapping
|
|
96
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { AcpJsonObject, AcpNotificationContext } from '@agen-ai/acp-transport';
|
|
2
|
+
import type { AgentTurnCompletedPayload, AgentTurnId } from '@agen-ai/agent-protocol';
|
|
3
|
+
import { type AgentProviderOutput, type AgentProviderTurnExecution } from '@agen-ai/agent-runtime';
|
|
4
|
+
import type { PreparePlanContinuation } from '../sessionInteractions/request.js';
|
|
5
|
+
import { type SessionQueue } from './queue.js';
|
|
6
|
+
import type { NativeTurn, SessionMessage } from './turn.js';
|
|
7
|
+
/** The selected restored-plan continuation shares the session's turn and output owners. */
|
|
8
|
+
export declare class GrokPlanContinuations {
|
|
9
|
+
private readonly input;
|
|
10
|
+
private readonly historicalPrompts;
|
|
11
|
+
private readonly completedPrompts;
|
|
12
|
+
private continuation;
|
|
13
|
+
constructor(input: Readonly<{
|
|
14
|
+
queue: SessionQueue<SessionMessage>;
|
|
15
|
+
selected: boolean;
|
|
16
|
+
requireUsable: () => void;
|
|
17
|
+
assertNativeSession: (params: AcpJsonObject) => void;
|
|
18
|
+
createNativeTurn: (turnId: AgentTurnId) => NativeTurn;
|
|
19
|
+
execution: (turn: NativeTurn, stream: () => AsyncGenerator<AgentProviderOutput>) => AgentProviderTurnExecution;
|
|
20
|
+
stream: (turn: NativeTurn, ready: Promise<boolean>) => AsyncGenerator<AgentProviderOutput>;
|
|
21
|
+
completeNativeTurn: (turn: NativeTurn, payload: AgentTurnCompletedPayload) => void;
|
|
22
|
+
close: () => Promise<void>;
|
|
23
|
+
}>);
|
|
24
|
+
private replayed;
|
|
25
|
+
private promptIds;
|
|
26
|
+
private rememberPrompts;
|
|
27
|
+
admitOutput(params: AcpJsonObject, update: AcpJsonObject, historical: boolean): boolean;
|
|
28
|
+
admitNotification(context: AcpNotificationContext): void;
|
|
29
|
+
prepareResponse(input: Parameters<PreparePlanContinuation>[0]): ReturnType<PreparePlanContinuation>;
|
|
30
|
+
complete(turn: NativeTurn): void;
|
|
31
|
+
stop(error: Error): void;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=continuation.d.ts.map
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import {
|
|
2
|
+
throwIfAgentOperationAborted
|
|
3
|
+
} from "@agen-ai/agent-runtime";
|
|
4
|
+
import { GROK_QUEUE_METHOD } from "../sessionInteractions/grokPlanReview.js";
|
|
5
|
+
import { AcpProviderSessionError } from "./errors.js";
|
|
6
|
+
import { acpTurnCompletion } from "./completion.js";
|
|
7
|
+
import { nativeObject, nativeString, SESSION_METADATA_UPDATES } from "./native.js";
|
|
8
|
+
import { deferred } from "./queue.js";
|
|
9
|
+
class GrokPlanContinuations {
|
|
10
|
+
constructor(input) {
|
|
11
|
+
this.input = input;
|
|
12
|
+
}
|
|
13
|
+
historicalPrompts = /* @__PURE__ */ new Map();
|
|
14
|
+
completedPrompts = /* @__PURE__ */ new Set();
|
|
15
|
+
continuation;
|
|
16
|
+
replayed(meta) {
|
|
17
|
+
if (meta?.isReplay !== void 0 && typeof meta.isReplay !== "boolean") throw new AcpProviderSessionError(
|
|
18
|
+
"acp_continuation_replay_invalid",
|
|
19
|
+
"The selected native replay marker must be a boolean."
|
|
20
|
+
);
|
|
21
|
+
return meta?.isReplay === true;
|
|
22
|
+
}
|
|
23
|
+
promptIds(values) {
|
|
24
|
+
return values.flatMap((value) => {
|
|
25
|
+
if (value === void 0 || value === null) return [];
|
|
26
|
+
const id = nativeString(value, "prompt identity");
|
|
27
|
+
if (!id || Buffer.byteLength(id, "utf8") > 256) throw new AcpProviderSessionError(
|
|
28
|
+
"acp_continuation_identity_invalid",
|
|
29
|
+
"Native prompt identity exceeds its bound."
|
|
30
|
+
);
|
|
31
|
+
return [id];
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
rememberPrompts(ids) {
|
|
35
|
+
for (const id of ids) if (!this.historicalPrompts.has(id)) this.historicalPrompts.set(id, this.input.queue.reserveState(id));
|
|
36
|
+
}
|
|
37
|
+
admitOutput(params, update, historical) {
|
|
38
|
+
if (!this.input.selected) return true;
|
|
39
|
+
const meta = params._meta === void 0 ? void 0 : nativeObject(params._meta, "continuation metadata");
|
|
40
|
+
const replay = this.replayed(meta);
|
|
41
|
+
const ids = this.promptIds([meta?.promptId, update.prompt_id]);
|
|
42
|
+
if (historical || replay || !this.continuation?.state.dispatched) this.rememberPrompts(ids);
|
|
43
|
+
if (historical || replay) return false;
|
|
44
|
+
if (SESSION_METADATA_UPDATES.has(nativeString(update.sessionUpdate, "session update kind"))) return true;
|
|
45
|
+
if (this.continuation && !this.continuation.turn.terminal) {
|
|
46
|
+
if (!this.continuation.state.promptId || meta?.promptId !== this.continuation.state.promptId) {
|
|
47
|
+
throw new AcpProviderSessionError("acp_continuation_output_mismatch", "Native continuation output requires its exact promoted prompt identity.");
|
|
48
|
+
}
|
|
49
|
+
this.continuation.state.outputObserved = true;
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
admitNotification(context) {
|
|
54
|
+
this.input.requireUsable();
|
|
55
|
+
const params = nativeObject(context.params, "continuation notification");
|
|
56
|
+
this.input.assertNativeSession(params);
|
|
57
|
+
const meta = params._meta === void 0 ? void 0 : nativeObject(params._meta, "continuation metadata");
|
|
58
|
+
const replay = this.replayed(meta);
|
|
59
|
+
const update = context.method === GROK_QUEUE_METHOD ? void 0 : nativeObject(params.update, "native session update");
|
|
60
|
+
const ids = this.promptIds([meta?.promptId, update?.prompt_id, ...context.method === GROK_QUEUE_METHOD ? [params.runningPromptId, ...Array.isArray(params.entries) ? params.entries.map((entry) => nativeObject(entry, "queue entry").id) : []] : []]);
|
|
61
|
+
if (replay || !this.continuation?.state.dispatched) this.rememberPrompts(ids);
|
|
62
|
+
if (replay) return;
|
|
63
|
+
if (context.method === GROK_QUEUE_METHOD) {
|
|
64
|
+
const continuation2 = this.continuation;
|
|
65
|
+
if (!continuation2?.state.dispatched || params.runningPromptId == null) return;
|
|
66
|
+
const id = nativeString(params.runningPromptId, "promoted prompt identity");
|
|
67
|
+
if (params.runningKind !== "prompt" || !id.startsWith("plan-resume-") || this.historicalPrompts.has(id) && continuation2.state.promptId !== id || meta?.promptId != null && meta.promptId !== id || ids.some((value) => value !== id && !this.historicalPrompts.has(value)) || continuation2.state.promptId !== void 0 && continuation2.state.promptId !== id) {
|
|
68
|
+
throw new AcpProviderSessionError("acp_continuation_promotion_invalid", "The native continuation promotion does not identify its fresh owned prompt.");
|
|
69
|
+
}
|
|
70
|
+
if (!continuation2.state.promptId) {
|
|
71
|
+
continuation2.state.promptId = id;
|
|
72
|
+
continuation2.turn.nativeStarted = true;
|
|
73
|
+
continuation2.outcome.resolve(Object.freeze({ kind: "started" }));
|
|
74
|
+
continuation2.ready.resolve(true);
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (update?.sessionUpdate !== "turn_completed") return;
|
|
79
|
+
const continuation = this.continuation;
|
|
80
|
+
if (!continuation?.state.dispatched) {
|
|
81
|
+
if (typeof update.prompt_id === "string" && this.completedPrompts.has(update.prompt_id)) throw new AcpProviderSessionError(
|
|
82
|
+
"acp_continuation_terminal_invalid",
|
|
83
|
+
"The native continuation repeated an already completed terminal."
|
|
84
|
+
);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (update.prompt_id !== continuation.state.promptId) {
|
|
88
|
+
if (typeof update.prompt_id === "string" && this.historicalPrompts.has(update.prompt_id)) return;
|
|
89
|
+
throw new AcpProviderSessionError("acp_continuation_terminal_mismatch", "The terminal does not identify the owned promoted prompt.");
|
|
90
|
+
}
|
|
91
|
+
if (!continuation.state.promptId || continuation.turn.nativeSettled || update.stop_reason !== "end_turn" && update.stop_reason !== "cancelled" || update.stop_reason === "end_turn" && !continuation.state.outputObserved || update.error != null || params.error != null) throw new AcpProviderSessionError(
|
|
92
|
+
"acp_continuation_terminal_invalid",
|
|
93
|
+
"The native continuation did not produce its exact terminal."
|
|
94
|
+
);
|
|
95
|
+
this.input.completeNativeTurn(continuation.turn, acpTurnCompletion({
|
|
96
|
+
kind: "result",
|
|
97
|
+
result: { stopReason: update.stop_reason }
|
|
98
|
+
}, continuation.turn.cancelReason));
|
|
99
|
+
}
|
|
100
|
+
prepareResponse(input) {
|
|
101
|
+
this.input.requireUsable();
|
|
102
|
+
throwIfAgentOperationAborted(input.signal);
|
|
103
|
+
let canceled = false;
|
|
104
|
+
let dispatched = false;
|
|
105
|
+
let owned;
|
|
106
|
+
const outcome = deferred();
|
|
107
|
+
const ready = deferred();
|
|
108
|
+
void outcome.promise.catch(() => {
|
|
109
|
+
});
|
|
110
|
+
void ready.promise.catch(() => {
|
|
111
|
+
});
|
|
112
|
+
const cancel = async () => {
|
|
113
|
+
if (canceled) return;
|
|
114
|
+
canceled = true;
|
|
115
|
+
if (dispatched) {
|
|
116
|
+
await this.input.close();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
outcome.resolve(Object.freeze({ kind: "not_started" }));
|
|
120
|
+
ready.resolve(false);
|
|
121
|
+
if (owned) {
|
|
122
|
+
owned.turn.terminal = true;
|
|
123
|
+
owned.turn.sourceSettled = true;
|
|
124
|
+
owned.turn.mapping.release();
|
|
125
|
+
owned.outcome.resolve(Object.freeze({ kind: "not_started" }));
|
|
126
|
+
owned.ready.resolve(false);
|
|
127
|
+
this.input.queue.wakeReader();
|
|
128
|
+
if (this.continuation === owned) this.continuation = void 0;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
const dispatch = async () => {
|
|
132
|
+
throwIfAgentOperationAborted(input.signal);
|
|
133
|
+
if (canceled || dispatched) throw new AcpProviderSessionError("acp_continuation_dispatch_invalid", "A prepared response can enter only once.");
|
|
134
|
+
if (input.response.action !== "abandon" && !owned) throw new AcpProviderSessionError("acp_continuation_owner_missing", "The native continuation requires its prepared reader.");
|
|
135
|
+
dispatched = true;
|
|
136
|
+
if (owned) owned.state.dispatched = true;
|
|
137
|
+
const result = await input.dispatch();
|
|
138
|
+
if (result.status !== "delivered" && owned) {
|
|
139
|
+
owned.turn.terminal = true;
|
|
140
|
+
owned.turn.mapping.release();
|
|
141
|
+
owned.outcome.resolve(Object.freeze({ kind: "not_started" }));
|
|
142
|
+
owned.ready.resolve(false);
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
};
|
|
146
|
+
if (input.response.action === "abandon") return Object.freeze({ kind: "no_continuation", dispatch, cancel });
|
|
147
|
+
return Object.freeze({
|
|
148
|
+
kind: "native_continuation",
|
|
149
|
+
outcome: outcome.promise,
|
|
150
|
+
dispatch,
|
|
151
|
+
cancel,
|
|
152
|
+
createExecution: ({ turnId, signal }) => {
|
|
153
|
+
if (owned || canceled || dispatched) throw new AcpProviderSessionError("acp_continuation_owner_invalid", "The prepared execution can be acquired only once.");
|
|
154
|
+
throwIfAgentOperationAborted(signal);
|
|
155
|
+
const turn = this.input.createNativeTurn(turnId);
|
|
156
|
+
owned = { turn, outcome, ready, state: { dispatched: false, outputObserved: false } };
|
|
157
|
+
this.continuation = owned;
|
|
158
|
+
const owner = this.input;
|
|
159
|
+
return owner.execution(turn, async function* () {
|
|
160
|
+
yield* owner.stream(turn, ready.promise);
|
|
161
|
+
if (dispatched) await input.nativeSettled;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
complete(turn) {
|
|
167
|
+
if (this.continuation?.turn !== turn) return;
|
|
168
|
+
const promptId = this.continuation.state.promptId;
|
|
169
|
+
if (promptId !== void 0) {
|
|
170
|
+
this.rememberPrompts([promptId]);
|
|
171
|
+
this.completedPrompts.add(promptId);
|
|
172
|
+
}
|
|
173
|
+
this.continuation = void 0;
|
|
174
|
+
}
|
|
175
|
+
stop(error) {
|
|
176
|
+
this.continuation?.ready.reject(error);
|
|
177
|
+
this.continuation?.outcome.reject(error);
|
|
178
|
+
for (const release of this.historicalPrompts.values()) release();
|
|
179
|
+
this.historicalPrompts.clear();
|
|
180
|
+
this.completedPrompts.clear();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
export {
|
|
184
|
+
GrokPlanContinuations
|
|
185
|
+
};
|
package/dist/session/create.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { AgentCapabilities, AgentSessionBinding } from '@agen-ai/agent-prot
|
|
|
2
2
|
import { type AgentProviderCreateSessionInput, type AgentProviderResumeSessionInput, type AgentProviderSession, type AgentProviderSessionContext } from '@agen-ai/agent-runtime';
|
|
3
3
|
import type { AcpInitializedConnection } from '../connection/contracts.js';
|
|
4
4
|
import type { AcpProviderLimits } from '../limits.js';
|
|
5
|
+
import type { AcpPermissionMappingSelection } from '../permissions/contracts.js';
|
|
5
6
|
export interface CreateAcpProviderSessionInput {
|
|
6
7
|
readonly context: AgentProviderCreateSessionInput | AgentProviderResumeSessionInput;
|
|
7
8
|
readonly openingInput: AgentProviderSessionContext;
|
|
@@ -13,9 +14,14 @@ export interface CreateAcpProviderSessionInput {
|
|
|
13
14
|
readonly connection: AcpInitializedConnection;
|
|
14
15
|
readonly capabilities: AgentCapabilities;
|
|
15
16
|
readonly limits: AcpProviderLimits;
|
|
17
|
+
readonly permissionMapping: AcpPermissionMappingSelection;
|
|
16
18
|
readonly now: () => string;
|
|
17
19
|
readonly onClosed: () => void;
|
|
18
20
|
readonly closeResources: () => Promise<void>;
|
|
19
21
|
}
|
|
20
|
-
export
|
|
22
|
+
export interface AcpOpenedProviderSession {
|
|
23
|
+
readonly session: AgentProviderSession;
|
|
24
|
+
readonly cleanup: () => Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export declare function createAcpProviderSession(input: CreateAcpProviderSessionInput): Promise<AcpOpenedProviderSession>;
|
|
21
27
|
//# sourceMappingURL=create.d.ts.map
|
package/dist/session/create.js
CHANGED
|
@@ -6,7 +6,9 @@ async function createAcpProviderSession(input) {
|
|
|
6
6
|
let state;
|
|
7
7
|
try {
|
|
8
8
|
state = new SessionState(input);
|
|
9
|
-
|
|
9
|
+
const session = await state.open();
|
|
10
|
+
const owner = state;
|
|
11
|
+
return Object.freeze({ session, cleanup: () => owner.close() });
|
|
10
12
|
} catch (error) {
|
|
11
13
|
const cleanup = () => state ? state.close() : input.closeResources();
|
|
12
14
|
try {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AcpJsonObject, AcpNotificationContext } from '@agen-ai/acp-transport';
|
|
2
|
+
import type { SessionQueue } from './queue.js';
|
|
3
|
+
import type { GrokChildPermissionWithdrawals } from './requests/grokWithdrawals.js';
|
|
4
|
+
export interface DescendantIdentity {
|
|
5
|
+
readonly parentSessionId: string;
|
|
6
|
+
readonly childSessionId: string;
|
|
7
|
+
readonly subagentId: string;
|
|
8
|
+
readonly attemptId?: string;
|
|
9
|
+
readonly parentPromptId?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Selected native descendants are bounded observations, with no parent output or host authority. */
|
|
12
|
+
export declare class GrokDescendantObservations {
|
|
13
|
+
private readonly input;
|
|
14
|
+
private readonly bySession;
|
|
15
|
+
private readonly bySubagent;
|
|
16
|
+
constructor(input: Readonly<{
|
|
17
|
+
reserveState: SessionQueue<unknown>['reserveState'];
|
|
18
|
+
rootSessionId: () => string | undefined;
|
|
19
|
+
activeTurn: () => boolean;
|
|
20
|
+
observeHistory: (params: AcpJsonObject) => void;
|
|
21
|
+
withdrawals: GrokChildPermissionWithdrawals;
|
|
22
|
+
}>);
|
|
23
|
+
/** Returns true only when this notification is wholly owned by the observation scope. */
|
|
24
|
+
admit(context: AcpNotificationContext, historical: boolean): boolean;
|
|
25
|
+
private spawn;
|
|
26
|
+
private validateQueue;
|
|
27
|
+
stop(): void;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=descendants.d.ts.map
|