ycodium-acp-executor 0.2.2488
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 +127 -0
- package/dist/adapter-support.d.ts +26 -0
- package/dist/adapter-support.d.ts.map +1 -0
- package/dist/adapter-support.js +79 -0
- package/dist/adapter-support.js.map +1 -0
- package/dist/approvals.d.ts +39 -0
- package/dist/approvals.d.ts.map +1 -0
- package/dist/approvals.js +69 -0
- package/dist/approvals.js.map +1 -0
- package/dist/config-option-wiring.d.ts +71 -0
- package/dist/config-option-wiring.d.ts.map +1 -0
- package/dist/config-option-wiring.js +71 -0
- package/dist/config-option-wiring.js.map +1 -0
- package/dist/config-options.d.ts +75 -0
- package/dist/config-options.d.ts.map +1 -0
- package/dist/config-options.js +171 -0
- package/dist/config-options.js.map +1 -0
- package/dist/connection.d.ts +34 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +374 -0
- package/dist/connection.js.map +1 -0
- package/dist/errors.d.ts +21 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +41 -0
- package/dist/errors.js.map +1 -0
- package/dist/event-stream.d.ts +15 -0
- package/dist/event-stream.d.ts.map +1 -0
- package/dist/event-stream.js +81 -0
- package/dist/event-stream.js.map +1 -0
- package/dist/events.d.ts +69 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +205 -0
- package/dist/events.js.map +1 -0
- package/dist/executor.d.ts +84 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +1179 -0
- package/dist/executor.js.map +1 -0
- package/dist/handshake.d.ts +24 -0
- package/dist/handshake.d.ts.map +1 -0
- package/dist/handshake.js +26 -0
- package/dist/handshake.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/jsonrpc.d.ts +12 -0
- package/dist/jsonrpc.d.ts.map +1 -0
- package/dist/jsonrpc.js +42 -0
- package/dist/jsonrpc.js.map +1 -0
- package/dist/line-buffer.d.ts +14 -0
- package/dist/line-buffer.d.ts.map +1 -0
- package/dist/line-buffer.js +51 -0
- package/dist/line-buffer.js.map +1 -0
- package/dist/mutex.d.ts +13 -0
- package/dist/mutex.d.ts.map +1 -0
- package/dist/mutex.js +44 -0
- package/dist/mutex.js.map +1 -0
- package/dist/probe.d.ts +74 -0
- package/dist/probe.d.ts.map +1 -0
- package/dist/probe.js +121 -0
- package/dist/probe.js.map +1 -0
- package/dist/protocol.d.ts +8 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +6 -0
- package/dist/protocol.js.map +1 -0
- package/dist/runtime-mode.d.ts +49 -0
- package/dist/runtime-mode.d.ts.map +1 -0
- package/dist/runtime-mode.js +123 -0
- package/dist/runtime-mode.js.map +1 -0
- package/dist/runtime-model.d.ts +157 -0
- package/dist/runtime-model.d.ts.map +1 -0
- package/dist/runtime-model.js +601 -0
- package/dist/runtime-model.js.map +1 -0
- package/dist/session-update.d.ts +42 -0
- package/dist/session-update.d.ts.map +1 -0
- package/dist/session-update.js +63 -0
- package/dist/session-update.js.map +1 -0
- package/dist/tool-presentation.d.ts +17 -0
- package/dist/tool-presentation.d.ts.map +1 -0
- package/dist/tool-presentation.js +209 -0
- package/dist/tool-presentation.js.map +1 -0
- package/dist/turn-usage.d.ts +18 -0
- package/dist/turn-usage.d.ts.map +1 -0
- package/dist/turn-usage.js +59 -0
- package/dist/turn-usage.js.map +1 -0
- package/dist/types.d.ts +116 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +3 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +11 -0
- package/dist/util.js.map +1 -0
- package/dist/vendor-hooks.d.ts +112 -0
- package/dist/vendor-hooks.d.ts.map +1 -0
- package/dist/vendor-hooks.js +4 -0
- package/dist/vendor-hooks.js.map +1 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# ycodium-acp-executor
|
|
2
|
+
|
|
3
|
+
The Agent Client Protocol (ACP) client that Ycodium's own agent plugins run on, published so
|
|
4
|
+
your plugin can use it too. Point it at the stdio streams of any ACP-speaking CLI and it
|
|
5
|
+
gives you a session that emits Ycodium executor events: assistant text, tool calls, plans,
|
|
6
|
+
approvals, structured questions, token usage.
|
|
7
|
+
|
|
8
|
+
If the CLI you want to add to Ycodium speaks ACP, you should not have to write framing,
|
|
9
|
+
handshakes, permission round trips or cancellation yourself. This package is that layer. It has
|
|
10
|
+
no dependency on Ycodium or on Effect.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
npm install --save-dev ycodium-acp-executor
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Bundle it into your plugin. Only `@ycodium-ai/plugin-api` stays external, because the host
|
|
19
|
+
provides that one (see the [plugin-api README](https://www.npmjs.com/package/@ycodium-ai/plugin-api)).
|
|
20
|
+
With esbuild:
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
await esbuild.build({
|
|
24
|
+
entryPoints: ["src/index.ts"],
|
|
25
|
+
outfile: "dist/index.js",
|
|
26
|
+
bundle: true,
|
|
27
|
+
external: ["@ycodium-ai/plugin-api", "@ycodium-ai/plugin-api/*"],
|
|
28
|
+
format: "esm",
|
|
29
|
+
platform: "node",
|
|
30
|
+
target: "node24",
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
It is an ES module for Node. The package depends on `@agentclientprotocol/sdk` and
|
|
35
|
+
`zod` and re-exports the SDK types you need, so you do not have to install the SDK separately.
|
|
36
|
+
|
|
37
|
+
## Build an ACP agent plugin
|
|
38
|
+
|
|
39
|
+
An agent plugin declares an executor with `definePlugin` from `@ycodium-ai/plugin-api`. This
|
|
40
|
+
package covers the ACP half: turning a spawned CLI into a session.
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { nodeStreamsFromHandle } from "@ycodium-ai/plugin-api/process-stdin";
|
|
44
|
+
import { createAcpExecutor, parseAcpResumeCursor } from "ycodium-acp-executor";
|
|
45
|
+
|
|
46
|
+
// Inside your ExecutorInstance.startSession(input):
|
|
47
|
+
const handle = await host.process.spawn({
|
|
48
|
+
program: "my-agent",
|
|
49
|
+
args: ["acp"],
|
|
50
|
+
cwd,
|
|
51
|
+
owner: { bindingId: host.bindingId },
|
|
52
|
+
});
|
|
53
|
+
const { readable, writable } = nodeStreamsFromHandle(handle);
|
|
54
|
+
|
|
55
|
+
const executor = createAcpExecutor({
|
|
56
|
+
readable,
|
|
57
|
+
writable,
|
|
58
|
+
cwd,
|
|
59
|
+
threadId: input.threadId,
|
|
60
|
+
provider: "myAgent",
|
|
61
|
+
providerInstanceId: host.instanceId,
|
|
62
|
+
clientInfo: { name: "ycodium", version: "1.0.0" },
|
|
63
|
+
log: host.log,
|
|
64
|
+
resumeSessionId: parseAcpResumeCursor(input.resumeCursor, 1)?.sessionId,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const started = await executor.start(); // initialize, then session/new or session/load
|
|
68
|
+
for await (const event of executor.events) {
|
|
69
|
+
// Forward each event to the executor events your plugin exposes.
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// In sendTurn / interruptTurn / stop:
|
|
73
|
+
await executor.prompt({ text: "Explain this repository", turnId });
|
|
74
|
+
await executor.cancel(turnId);
|
|
75
|
+
await executor.dispose();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`executor.events` yields `ExecutorEvent` values with the same shape as the type of that name in
|
|
79
|
+
`@ycodium-ai/plugin-api/executor`. This package does not import plugin-api, so the two types are
|
|
80
|
+
structurally compatible rather than shared.
|
|
81
|
+
|
|
82
|
+
What the executor handles for you:
|
|
83
|
+
|
|
84
|
+
- The `initialize` handshake, session creation, and resume through `session/load` or
|
|
85
|
+
`session/resume` (`resumeMethod`).
|
|
86
|
+
- Prompt turns, cancellation with a grace period, and settling a turn exactly once.
|
|
87
|
+
- Permission requests, mapped to approval requests you answer with `respondToRequest`, with an
|
|
88
|
+
optional `autoApprove` for the host's own approval levels.
|
|
89
|
+
- Model and effort selection, using whichever route the agent advertises: session models or
|
|
90
|
+
configuration options.
|
|
91
|
+
- Runtime and native modes, through `setNativeMode`.
|
|
92
|
+
- Tool calls, plan updates and content deltas as executor events.
|
|
93
|
+
- Token usage and context window readings.
|
|
94
|
+
|
|
95
|
+
## Vendor differences go in hooks
|
|
96
|
+
|
|
97
|
+
Do not fork the state machine for one CLI's quirks. Pass `hooks` (`AcpVendorHooks`) instead:
|
|
98
|
+
|
|
99
|
+
- `permissionUserInput` turns a permission request into a structured question.
|
|
100
|
+
- `onExtensionRequest` and `onExtensionNotification` handle vendor-private methods.
|
|
101
|
+
- `readContextWindow` reads context usage that the CLI records somewhere other than ACP.
|
|
102
|
+
- `promptSettlement` lets a turn end on a signal other than the prompt response.
|
|
103
|
+
- `resolveRequestedModelId` and `resolveDisplayModelId` map model names.
|
|
104
|
+
|
|
105
|
+
## Probing a CLI
|
|
106
|
+
|
|
107
|
+
Before a session exists, an instance usually has to report whether the CLI is installed, its
|
|
108
|
+
version and its models:
|
|
109
|
+
|
|
110
|
+
- `probeCliVersion`, `parseCliVersion` and `collectProbeProcess` run a CLI and read its output.
|
|
111
|
+
- `openAcpProbeHandshake` opens a throwaway connection over the CLI's stdio, runs `initialize`, and
|
|
112
|
+
lets you send further requests (`session/new`, `session/list`) with one timeout.
|
|
113
|
+
- `acpModelChoicesFromConfigOptions`, `acpModelChoicesFromSessionModels`, `findAcpEffortConfig`,
|
|
114
|
+
`acpAvailableModes` and `acpAgentVersion` read models, effort levels, modes and the agent
|
|
115
|
+
version out of those answers.
|
|
116
|
+
|
|
117
|
+
## Lower level
|
|
118
|
+
|
|
119
|
+
`createAcpConnection` is the newline-delimited JSON-RPC transport underneath, and the package
|
|
120
|
+
re-exports the SDK's method tables and request and response types (`CLIENT_METHODS`,
|
|
121
|
+
`AGENT_METHODS`, `PromptRequest`, `SessionNotification`, and so on). Reach for it only if
|
|
122
|
+
`createAcpExecutor` does not fit; everything above is built on it.
|
|
123
|
+
|
|
124
|
+
## Stability
|
|
125
|
+
|
|
126
|
+
Ycodium's own agent plugins (ACP, DSH, Grok, Kimi Code) depend on this package, so its exports
|
|
127
|
+
change deliberately. It is versioned and released with the Ycodium plugins as `0.2.<n>`.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 从 `AcpAdapterSupport.ts` 搬来的纯函数。只改类型,业务规则没改。
|
|
3
|
+
*/
|
|
4
|
+
import type { AcpApprovalDecision, AcpApprovalOption, AcpPermissionOption, AcpRequestPermissionParams } from "./types.ts";
|
|
5
|
+
export declare function acpPermissionOutcome(decision: AcpApprovalDecision): string;
|
|
6
|
+
export declare function selectAcpPermissionOptionId(request: {
|
|
7
|
+
readonly options: ReadonlyArray<AcpPermissionOption>;
|
|
8
|
+
}, decision: Exclude<AcpApprovalDecision, "cancel">): string | undefined;
|
|
9
|
+
export declare function acpApprovalOptions(request: {
|
|
10
|
+
readonly options: ReadonlyArray<AcpPermissionOption>;
|
|
11
|
+
}): ReadonlyArray<AcpApprovalOption>;
|
|
12
|
+
export declare function selectAutoApprovedAcpPermissionOption(request: {
|
|
13
|
+
readonly options: ReadonlyArray<AcpPermissionOption>;
|
|
14
|
+
}): string | undefined;
|
|
15
|
+
export declare function parseAcpResumeCursor(raw: unknown, schemaVersion: number): {
|
|
16
|
+
sessionId: string;
|
|
17
|
+
} | undefined;
|
|
18
|
+
export declare function acpPromptSettlementBelongsToContext(input: {
|
|
19
|
+
readonly liveAcpSessionId: string;
|
|
20
|
+
readonly expectedAcpSessionId: string;
|
|
21
|
+
readonly liveActiveTurnId: string | undefined;
|
|
22
|
+
readonly liveSessionActiveTurnId: string | undefined;
|
|
23
|
+
readonly turnId: string;
|
|
24
|
+
}): boolean;
|
|
25
|
+
export declare function isAcpRequestPermissionParams(value: unknown): value is AcpRequestPermissionParams;
|
|
26
|
+
//# sourceMappingURL=adapter-support.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-support.d.ts","sourceRoot":"","sources":["../src/adapter-support.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EACV,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,0BAA0B,EAC3B,MAAM,YAAY,CAAC;AAEpB,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,CAW1E;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAA;CAAE,EACjE,QAAQ,EAAE,OAAO,CAAC,mBAAmB,EAAE,QAAQ,CAAC,GAC/C,MAAM,GAAG,SAAS,CAUpB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC;CACtD,GAAG,aAAa,CAAC,iBAAiB,CAAC,CAoBnC;AAED,wBAAgB,qCAAqC,CAAC,OAAO,EAAE;IAC7D,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC;CACtD,GAAG,MAAM,GAAG,SAAS,CAQrB;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,aAAa,EAAE,MAAM,GACpB;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAMnC;AAED,wBAAgB,mCAAmC,CAAC,KAAK,EAAE;IACzD,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,uBAAuB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAKV;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,0BAA0B,CAgBhG"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export function acpPermissionOutcome(decision) {
|
|
2
|
+
switch (decision) {
|
|
3
|
+
case "acceptForSession":
|
|
4
|
+
case "acceptAlways":
|
|
5
|
+
return "allow-always";
|
|
6
|
+
case "accept":
|
|
7
|
+
return "allow-once";
|
|
8
|
+
case "decline":
|
|
9
|
+
default:
|
|
10
|
+
return "reject-once";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function selectAcpPermissionOptionId(request, decision) {
|
|
14
|
+
const kind = decision === "acceptForSession" || decision === "acceptAlways"
|
|
15
|
+
? "allow_always"
|
|
16
|
+
: decision === "accept"
|
|
17
|
+
? "allow_once"
|
|
18
|
+
: "reject_once";
|
|
19
|
+
const options = request.options.filter((entry) => entry.kind === kind);
|
|
20
|
+
const optionId = options.length === 1 ? options[0]?.optionId : undefined;
|
|
21
|
+
return optionId?.trim() ? optionId : undefined;
|
|
22
|
+
}
|
|
23
|
+
export function acpApprovalOptions(request) {
|
|
24
|
+
const options = [{ decision: "cancel", label: "Cancel" }];
|
|
25
|
+
for (const option of request.options) {
|
|
26
|
+
const decision = option.kind === "allow_once"
|
|
27
|
+
? "accept"
|
|
28
|
+
: option.kind === "allow_always"
|
|
29
|
+
? "acceptForSession"
|
|
30
|
+
: option.kind === "reject_once"
|
|
31
|
+
? "decline"
|
|
32
|
+
: undefined;
|
|
33
|
+
if (decision !== undefined &&
|
|
34
|
+
selectAcpPermissionOptionId(request, decision) !== undefined &&
|
|
35
|
+
option.name.trim().length > 0) {
|
|
36
|
+
options.push({ decision, label: option.name });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return options;
|
|
40
|
+
}
|
|
41
|
+
export function selectAutoApprovedAcpPermissionOption(request) {
|
|
42
|
+
if (new Set(request.options.map((option) => option.kind)).size !== request.options.length) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
return (selectAcpPermissionOptionId(request, "acceptForSession") ??
|
|
46
|
+
selectAcpPermissionOptionId(request, "accept"));
|
|
47
|
+
}
|
|
48
|
+
export function parseAcpResumeCursor(raw, schemaVersion) {
|
|
49
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw))
|
|
50
|
+
return undefined;
|
|
51
|
+
const record = raw;
|
|
52
|
+
if (record["schemaVersion"] !== schemaVersion)
|
|
53
|
+
return undefined;
|
|
54
|
+
if (typeof record["sessionId"] !== "string" || !record["sessionId"].trim())
|
|
55
|
+
return undefined;
|
|
56
|
+
return { sessionId: record["sessionId"].trim() };
|
|
57
|
+
}
|
|
58
|
+
export function acpPromptSettlementBelongsToContext(input) {
|
|
59
|
+
return (input.liveAcpSessionId === input.expectedAcpSessionId &&
|
|
60
|
+
(input.liveActiveTurnId === input.turnId || input.liveSessionActiveTurnId === input.turnId));
|
|
61
|
+
}
|
|
62
|
+
export function isAcpRequestPermissionParams(value) {
|
|
63
|
+
if (typeof value !== "object" || value === null) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const record = value;
|
|
67
|
+
if (typeof record["sessionId"] !== "string") {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const toolCall = record["toolCall"];
|
|
71
|
+
if (typeof toolCall !== "object" || toolCall === null) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
if (typeof toolCall["toolCallId"] !== "string") {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
return Array.isArray(record["options"]);
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=adapter-support.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-support.js","sourceRoot":"","sources":["../src/adapter-support.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AcpApprovalDecision, AcpRequestPermissionParams } from "./types.ts";
|
|
2
|
+
import type { AcpPermissionRequest } from "./runtime-model.ts";
|
|
3
|
+
export type PermissionOutcome = {
|
|
4
|
+
readonly outcome: "cancelled";
|
|
5
|
+
} | {
|
|
6
|
+
readonly outcome: "selected";
|
|
7
|
+
readonly optionId: string;
|
|
8
|
+
};
|
|
9
|
+
export type PendingApproval = {
|
|
10
|
+
readonly requestId: string;
|
|
11
|
+
readonly request: AcpRequestPermissionParams;
|
|
12
|
+
readonly permissionRequest: AcpPermissionRequest;
|
|
13
|
+
readonly turnId: string | undefined;
|
|
14
|
+
settled: boolean;
|
|
15
|
+
readonly resolve: (outcome: PermissionOutcome) => void;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* 按 requestId 分开的审批门。不是全会话一个 resolver。
|
|
19
|
+
* 答复 / 取消 / 断连三者竞争只完成一次。
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* 审批闸:每个请求一份记录,不是全会话一个 resolver。
|
|
23
|
+
*
|
|
24
|
+
* `singleResolver` 只给测试用——把闸退回「全会话一个 resolver」的错误实现,好证明
|
|
25
|
+
* 那条测试确实测得着。生产路径不传它。它是显式参数而不是环境变量,正因为环境变量
|
|
26
|
+
* 在任何地方被设都会生效,而参数只有调用点能给。
|
|
27
|
+
*/
|
|
28
|
+
export declare function createApprovalGate(input?: {
|
|
29
|
+
readonly singleResolver?: boolean;
|
|
30
|
+
}): {
|
|
31
|
+
add: (entry: PendingApproval) => void;
|
|
32
|
+
get: (requestId: string) => PendingApproval | undefined;
|
|
33
|
+
list: () => PendingApproval[];
|
|
34
|
+
settle: (entry: PendingApproval, outcome: PermissionOutcome) => boolean;
|
|
35
|
+
settleAll: (outcome: PermissionOutcome) => ReadonlyArray<PendingApproval>;
|
|
36
|
+
};
|
|
37
|
+
export type ApprovalGate = ReturnType<typeof createApprovalGate>;
|
|
38
|
+
export declare function approvalOutcomeForDecision(decision: AcpApprovalDecision, optionId: string | undefined): PermissionOutcome;
|
|
39
|
+
//# sourceMappingURL=approvals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../src/approvals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE/D,MAAM,MAAM,iBAAiB,GACzB;IAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GACjC;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,0BAA0B,CAAC;IAC7C,QAAQ,CAAC,iBAAiB,EAAE,oBAAoB,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;CACxD,CAAC;AAEF;;;GAGG;AACH;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE;IAAE,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE;iBAkB1D,eAAe,KAAG,IAAI;qBAPlB,MAAM,KAAG,eAAe,GAAG,SAAS;gBAP3C,eAAe,EAAE;oBAsBX,eAAe,WAAW,iBAAiB,KAAG,OAAO;yBAahD,iBAAiB,KAAG,aAAa,CAAC,eAAe,CAAC;EAkB/E;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEjE,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,iBAAiB,CAInB"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 按 requestId 分开的审批门。不是全会话一个 resolver。
|
|
3
|
+
* 答复 / 取消 / 断连三者竞争只完成一次。
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 审批闸:每个请求一份记录,不是全会话一个 resolver。
|
|
7
|
+
*
|
|
8
|
+
* `singleResolver` 只给测试用——把闸退回「全会话一个 resolver」的错误实现,好证明
|
|
9
|
+
* 那条测试确实测得着。生产路径不传它。它是显式参数而不是环境变量,正因为环境变量
|
|
10
|
+
* 在任何地方被设都会生效,而参数只有调用点能给。
|
|
11
|
+
*/
|
|
12
|
+
export function createApprovalGate(input) {
|
|
13
|
+
const pending = new Map();
|
|
14
|
+
let single;
|
|
15
|
+
const list = () => {
|
|
16
|
+
if (input?.singleResolver) {
|
|
17
|
+
return single ? [single] : [];
|
|
18
|
+
}
|
|
19
|
+
return [...pending.values()];
|
|
20
|
+
};
|
|
21
|
+
const get = (requestId) => {
|
|
22
|
+
if (input?.singleResolver) {
|
|
23
|
+
return single;
|
|
24
|
+
}
|
|
25
|
+
return pending.get(requestId);
|
|
26
|
+
};
|
|
27
|
+
const add = (entry) => {
|
|
28
|
+
if (input?.singleResolver) {
|
|
29
|
+
single = entry;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
pending.set(entry.requestId, entry);
|
|
33
|
+
};
|
|
34
|
+
const settle = (entry, outcome) => {
|
|
35
|
+
if (entry.settled) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
entry.settled = true;
|
|
39
|
+
pending.delete(entry.requestId);
|
|
40
|
+
if (single?.requestId === entry.requestId) {
|
|
41
|
+
single = undefined;
|
|
42
|
+
}
|
|
43
|
+
entry.resolve(outcome);
|
|
44
|
+
return true;
|
|
45
|
+
};
|
|
46
|
+
const settleAll = (outcome) => {
|
|
47
|
+
const entries = list();
|
|
48
|
+
const settled = [];
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
if (settle(entry, outcome)) {
|
|
51
|
+
settled.push(entry);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return settled;
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
add,
|
|
58
|
+
get,
|
|
59
|
+
list,
|
|
60
|
+
settle,
|
|
61
|
+
settleAll,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function approvalOutcomeForDecision(decision, optionId) {
|
|
65
|
+
return decision !== "cancel" && optionId !== undefined
|
|
66
|
+
? { outcome: "selected", optionId }
|
|
67
|
+
: { outcome: "cancelled" };
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=approvals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvals.js","sourceRoot":"","sources":["../src/approvals.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AACH;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAA6C;IAC9E,MAAM,OAAO,GAAG,IAAI,GAAG,EAA2B,CAAC;IACnD,IAAI,MAAmC,CAAC;IAExC,MAAM,IAAI,GAAG,GAAsB,EAAE;QACnC,IAAI,KAAK,EAAE,cAAc,EAAE,CAAC;YAC1B,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChC,CAAC;QACD,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/B,CAAC,CAAC;IAEF,MAAM,GAAG,GAAG,CAAC,SAAiB,EAA+B,EAAE;QAC7D,IAAI,KAAK,EAAE,cAAc,EAAE,CAAC;YAC1B,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAChC,CAAC,CAAC;IAEF,MAAM,GAAG,GAAG,CAAC,KAAsB,EAAQ,EAAE;QAC3C,IAAI,KAAK,EAAE,cAAc,EAAE,CAAC;YAC1B,MAAM,GAAG,KAAK,CAAC;YACf,OAAO;QACT,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,CAAC,KAAsB,EAAE,OAA0B,EAAW,EAAE;QAC7E,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;QACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAChC,IAAI,MAAM,EAAE,SAAS,KAAK,KAAK,CAAC,SAAS,EAAE,CAAC;YAC1C,MAAM,GAAG,SAAS,CAAC;QACrB,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,CAAC,OAA0B,EAAkC,EAAE;QAC/E,MAAM,OAAO,GAAG,IAAI,EAAE,CAAC;QACvB,MAAM,OAAO,GAAsB,EAAE,CAAC;QACtC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;gBAC3B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IAEF,OAAO;QACL,GAAG;QACH,GAAG;QACH,IAAI;QACJ,MAAM;QACN,SAAS;KACV,CAAC;AACJ,CAAC;AAID,MAAM,UAAU,0BAA0B,CACxC,QAA6B,EAC7B,QAA4B;IAE5B,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,SAAS;QACpD,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE;QACnC,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACP 会话报回来的 `configOptions`,这只手对每一类的处置登记。
|
|
3
|
+
*
|
|
4
|
+
* **这张表为什么是三态,不是编译期穷尽**:`category` 是**开放联合**——协议原文
|
|
5
|
+
* "Clients MUST handle missing or unknown categories gracefully",且 `_` 开头留给厂商
|
|
6
|
+
* 自定义。开放集做不了穷尽检查,所以:
|
|
7
|
+
*
|
|
8
|
+
* - `consumed: true` —— 已经接到生效点,`via` 写明接在哪。
|
|
9
|
+
* - `consumed: false` —— **知道有、还没接**,`note` 写明为什么。
|
|
10
|
+
* - 表外 —— 上游或厂商报了从没见过的一类,`unhandledConfigCategories` 挑出来记一条诊断。
|
|
11
|
+
*
|
|
12
|
+
* 第三态是这张表存在的理由。在此之前只挑 `model` 一项,其余整个丢掉、零痕迹:
|
|
13
|
+
* 2026-09-19 实测 `@agentclientprotocol/claude-agent-acp@0.79.0` 同时报了
|
|
14
|
+
* `mode` / `model` / `thought_level` 三项,接了两项,力度档那一整格因此在界面上缺席,
|
|
15
|
+
* 而没有任何日志说过这件事——只有手工连一次协议才看得见。
|
|
16
|
+
*
|
|
17
|
+
* **2026-09-20 从 `apps/server/src/provider/acp/configOptionWiring.ts` 搬来,三行处置
|
|
18
|
+
* 跟着变了**:宿主那份记的是宿主消费点,而宿主的 ACP 路本来就是留给 cursor 的,
|
|
19
|
+
* cursor 废弃删除之后整片成了孤儿。搬过来必须按**这只手**的实情重填——
|
|
20
|
+
* `thought_level` / `model_option` 在这里是接了的(`findAcpEffortConfig` 两处都认),
|
|
21
|
+
* `model_config` 原来只有 CursorProvider 消费,现在没有消费点。
|
|
22
|
+
*
|
|
23
|
+
* @module @ycodium/acp-executor/config-option-wiring
|
|
24
|
+
*/
|
|
25
|
+
import type { AcpSessionConfigOption } from "./runtime-model.ts";
|
|
26
|
+
/**
|
|
27
|
+
* 今天见过的几类。
|
|
28
|
+
*
|
|
29
|
+
* 加一类到这里不会自动接上任何东西——`consumed: true` 必须同时有真的接线,
|
|
30
|
+
* 配套测试钉着这一点。
|
|
31
|
+
*/
|
|
32
|
+
export declare const ACP_CONFIG_CATEGORY_HANDLING: {
|
|
33
|
+
readonly mode: {
|
|
34
|
+
readonly consumed: true;
|
|
35
|
+
readonly via: "runtime-mode.ts:acpAvailableModes / acpCurrentModeId / acpModeConfigId";
|
|
36
|
+
};
|
|
37
|
+
readonly model: {
|
|
38
|
+
readonly consumed: true;
|
|
39
|
+
readonly via: "runtime-model.ts:extractModelConfigId → executor.ts:applyRequestedModel";
|
|
40
|
+
};
|
|
41
|
+
readonly thought_level: {
|
|
42
|
+
readonly consumed: true;
|
|
43
|
+
readonly via: "config-options.ts:findAcpEffortConfig → executor.ts:applyRequestedEffort";
|
|
44
|
+
};
|
|
45
|
+
readonly model_option: {
|
|
46
|
+
readonly consumed: true;
|
|
47
|
+
readonly via: "config-options.ts:findAcpEffortConfig(候选里的最高优先级,压过 thought_level)";
|
|
48
|
+
};
|
|
49
|
+
readonly model_config: {
|
|
50
|
+
readonly consumed: false;
|
|
51
|
+
readonly note: "只有宿主的 CursorProvider 消费过(context / fast / thinking 三个开关)。Cursor 2026-09-20 废弃删除,这一类现在没有消费点;没见过别家的真实载荷,先记账不猜形状。";
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
/** 一条表外 category 的诊断材料。 */
|
|
55
|
+
export type UnhandledConfigCategory = {
|
|
56
|
+
readonly category: string;
|
|
57
|
+
/** 报出它的那个选项 id,方便直接去 CLI 那边对。 */
|
|
58
|
+
readonly optionId: string;
|
|
59
|
+
/** `_` 开头是厂商扩展,不是 ACP spec 新增。两者处置不同,所以分开标。 */
|
|
60
|
+
readonly vendorExtension: boolean;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* 挑出这次会话里从没登记过的 category。
|
|
64
|
+
*
|
|
65
|
+
* 缺席 `category` 的选项不算漏接——协议明说它是 UX 提示、可以没有,按 id 用的那些
|
|
66
|
+
* (`findSessionConfigOption`)不依赖它。
|
|
67
|
+
*/
|
|
68
|
+
export declare function unhandledConfigCategories(configOptions: ReadonlyArray<AcpSessionConfigOption> | null | undefined): ReadonlyArray<UnhandledConfigCategory>;
|
|
69
|
+
/** 表里登记了、但还没接到生效点的 category。界面缺一格的已知来源。 */
|
|
70
|
+
export declare function declaredButUnconsumedCategories(): ReadonlyArray<string>;
|
|
71
|
+
//# sourceMappingURL=config-option-wiring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-option-wiring.d.ts","sourceRoot":"","sources":["../src/config-option-wiring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAiBjE;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;CAyBY,CAAC;AAKtD,2BAA2B;AAC3B,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,iCAAiC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+CAA+C;IAC/C,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,aAAa,CAAC,sBAAsB,CAAC,GAAG,IAAI,GAAG,SAAS,GACtE,aAAa,CAAC,uBAAuB,CAAC,CAiBxC;AAED,2CAA2C;AAC3C,wBAAgB,+BAA+B,IAAI,aAAa,CAAC,MAAM,CAAC,CAIvE"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/** 协议给厂商自定义留的前缀。这类不算「上游新增」,诊断里分开说。 */
|
|
2
|
+
const VENDOR_CATEGORY_PREFIX = "_";
|
|
3
|
+
/**
|
|
4
|
+
* 今天见过的几类。
|
|
5
|
+
*
|
|
6
|
+
* 加一类到这里不会自动接上任何东西——`consumed: true` 必须同时有真的接线,
|
|
7
|
+
* 配套测试钉着这一点。
|
|
8
|
+
*/
|
|
9
|
+
export const ACP_CONFIG_CATEGORY_HANDLING = {
|
|
10
|
+
mode: {
|
|
11
|
+
consumed: true,
|
|
12
|
+
via: "runtime-mode.ts:acpAvailableModes / acpCurrentModeId / acpModeConfigId",
|
|
13
|
+
},
|
|
14
|
+
model: {
|
|
15
|
+
consumed: true,
|
|
16
|
+
via: "runtime-model.ts:extractModelConfigId → executor.ts:applyRequestedModel",
|
|
17
|
+
},
|
|
18
|
+
thought_level: {
|
|
19
|
+
consumed: true,
|
|
20
|
+
via: "config-options.ts:findAcpEffortConfig → executor.ts:applyRequestedEffort",
|
|
21
|
+
},
|
|
22
|
+
model_option: {
|
|
23
|
+
consumed: true,
|
|
24
|
+
// 精确说法:它不是**识别器**,是**优先级**。`looksLikeEffortOption` 先筛出候选,
|
|
25
|
+
// 标了 `model_option` 的在候选里排第一(厂商自己标的最准),排在 id 叫 `effort`
|
|
26
|
+
// 的和标 `thought_level` 的前面。光有这个 category 而 id/name 不像力度档的,
|
|
27
|
+
// 压根进不了候选。
|
|
28
|
+
via: "config-options.ts:findAcpEffortConfig(候选里的最高优先级,压过 thought_level)",
|
|
29
|
+
},
|
|
30
|
+
model_config: {
|
|
31
|
+
consumed: false,
|
|
32
|
+
note: "只有宿主的 CursorProvider 消费过(context / fast / thinking 三个开关)。Cursor 2026-09-20 废弃删除,这一类现在没有消费点;没见过别家的真实载荷,先记账不猜形状。",
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
/** 登记在表里的 category 名单。 */
|
|
36
|
+
const KNOWN_CATEGORIES = new Set(Object.keys(ACP_CONFIG_CATEGORY_HANDLING));
|
|
37
|
+
/**
|
|
38
|
+
* 挑出这次会话里从没登记过的 category。
|
|
39
|
+
*
|
|
40
|
+
* 缺席 `category` 的选项不算漏接——协议明说它是 UX 提示、可以没有,按 id 用的那些
|
|
41
|
+
* (`findSessionConfigOption`)不依赖它。
|
|
42
|
+
*/
|
|
43
|
+
export function unhandledConfigCategories(configOptions) {
|
|
44
|
+
if (!configOptions)
|
|
45
|
+
return [];
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
const out = [];
|
|
48
|
+
for (const option of configOptions) {
|
|
49
|
+
const category = option.category?.trim();
|
|
50
|
+
if (!category)
|
|
51
|
+
continue;
|
|
52
|
+
if (KNOWN_CATEGORIES.has(category))
|
|
53
|
+
continue;
|
|
54
|
+
if (seen.has(category))
|
|
55
|
+
continue;
|
|
56
|
+
seen.add(category);
|
|
57
|
+
out.push({
|
|
58
|
+
category,
|
|
59
|
+
optionId: option.id.trim(),
|
|
60
|
+
vendorExtension: category.startsWith(VENDOR_CATEGORY_PREFIX),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
/** 表里登记了、但还没接到生效点的 category。界面缺一格的已知来源。 */
|
|
66
|
+
export function declaredButUnconsumedCategories() {
|
|
67
|
+
return Object.entries(ACP_CONFIG_CATEGORY_HANDLING)
|
|
68
|
+
.filter(([, handling]) => !handling.consumed)
|
|
69
|
+
.map(([category]) => category);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=config-option-wiring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-option-wiring.js","sourceRoot":"","sources":["../src/config-option-wiring.ts"],"names":[],"mappings":"AA0BA,sCAAsC;AACtC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAcnC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,IAAI,EAAE;QACJ,QAAQ,EAAE,IAAI;QACd,GAAG,EAAE,wEAAwE;KAC9E;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,IAAI;QACd,GAAG,EAAE,yEAAyE;KAC/E;IACD,aAAa,EAAE;QACb,QAAQ,EAAE,IAAI;QACd,GAAG,EAAE,0EAA0E;KAChF;IACD,YAAY,EAAE;QACZ,QAAQ,EAAE,IAAI;QACd,0DAA0D;QAC1D,wDAAwD;QACxD,0DAA0D;QAC1D,WAAW;QACX,GAAG,EAAE,mEAAmE;KACzE;IACD,YAAY,EAAE;QACZ,QAAQ,EAAE,KAAK;QACf,IAAI,EAAE,gHAAgH;KACvH;CACkD,CAAC;AAEtD,0BAA0B;AAC1B,MAAM,gBAAgB,GAAwB,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,CAAC;AAWjG;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CACvC,aAAuE;IAEvE,IAAI,CAAC,aAAa;QAAE,OAAO,EAAE,CAAC;IAC9B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAA8B,EAAE,CAAC;IAC1C,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC;QACzC,IAAI,CAAC,QAAQ;YAAE,SAAS;QACxB,IAAI,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,SAAS;QAC7C,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,SAAS;QACjC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnB,GAAG,CAAC,IAAI,CAAC;YACP,QAAQ;YACR,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE;YAC1B,eAAe,EAAE,QAAQ,CAAC,UAAU,CAAC,sBAAsB,CAAC;SAC7D,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,+BAA+B;IAC7C,OAAO,MAAM,CAAC,OAAO,CAAC,4BAA4B,CAAC;SAChD,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;SAC5C,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 从 ACP 会话回包的 `configOptions` 里认出思考档位(力度档)。
|
|
3
|
+
*
|
|
4
|
+
* **为什么住在这个包**:认法与翻法此前有两份,互不知道——`CursorProvider` 能把
|
|
5
|
+
* configOptions 里的力度档翻成界面画得出的 optionDescriptor;`agent-dsh` 的探测建了
|
|
6
|
+
* 会话、却只从同一份回包里取模型清单。结果同一种数据在 Cursor 上有力度档,在别的
|
|
7
|
+
* ACP 手上整格缺席。插件 import 不到宿主,所以通用那半只能落在这个两边都够得着的包里。
|
|
8
|
+
*
|
|
9
|
+
* **不归一化档位值**:Cursor 那份把 `extra-high` 折成 `xhigh`、认不出的一律丢掉,那是
|
|
10
|
+
* 它对齐自身固定集合的需要,留在 `CursorProvider`。通用这份照 agent 报什么算什么——
|
|
11
|
+
* 宿主 `ProviderOptionDescriptor` 的 option id 是自由字符串,归一化只会把 agent 真能收
|
|
12
|
+
* 的档位吃掉(`claude-agent-acp` 报的 `default` 就会中招),而写回时原样发回去才自洽。
|
|
13
|
+
*
|
|
14
|
+
* 入参收 `unknown`:插件侧拿到的是 raw JSON,宿主侧拿到的是 schema 类型,两边结构相同,
|
|
15
|
+
* 在这里各自不用先转一道。
|
|
16
|
+
*
|
|
17
|
+
* @module config-options
|
|
18
|
+
*/
|
|
19
|
+
/** 力度档里的一个可选值。 */
|
|
20
|
+
export type AcpConfigChoice = {
|
|
21
|
+
readonly value: string;
|
|
22
|
+
/**
|
|
23
|
+
* agent 给的名字,**缺席就是缺席**——模型清单那边名字没报时不设 `name`,不拿 slug
|
|
24
|
+
* 冒充(宿主拿它当「CLI 说得出的名字」)。要给人看的那一行用 {@link label}。
|
|
25
|
+
*/
|
|
26
|
+
readonly name?: string;
|
|
27
|
+
/** 给人看的一行:`name` 有就是它,没有退回 `value`。力度档那一格用这个。 */
|
|
28
|
+
readonly label: string;
|
|
29
|
+
/** agent 给这一档写的说明,有就带上(dsh 每档都有)。 */
|
|
30
|
+
readonly description?: string;
|
|
31
|
+
/** agent 报的 `currentValue` 命中这一档。 */
|
|
32
|
+
readonly isDefault?: boolean;
|
|
33
|
+
};
|
|
34
|
+
/** 认出来的力度档。 */
|
|
35
|
+
export type AcpEffortConfig = {
|
|
36
|
+
/**
|
|
37
|
+
* 报出它的那个 configOption id。写回走 `session/set_config_option` 时用的是它,
|
|
38
|
+
* 不是我们自己起的名字——各家 id 不同(`effort` / `reasoning` / 厂商自定义)。
|
|
39
|
+
*/
|
|
40
|
+
readonly configId: string;
|
|
41
|
+
/** agent 自己给的标题,当界面那一格的 label;空就由调用方兜底。 */
|
|
42
|
+
readonly label: string;
|
|
43
|
+
readonly choices: ReadonlyArray<AcpConfigChoice>;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* 挑出力度档那一项。
|
|
47
|
+
*
|
|
48
|
+
* 优先级照 `CursorProvider` 既有那套:厂商自己标的 `model_option` 最准,其次是 id 就叫
|
|
49
|
+
* `effort` 的,再次是标了 `thought_level` 的,最后退回第一个长得像的。多家同时命中时
|
|
50
|
+
* 这个顺序决定谁赢,改它会改掉 Cursor 今天的行为。
|
|
51
|
+
*/
|
|
52
|
+
export declare function findAcpEffortConfig(configOptions: unknown): AcpEffortConfig | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* 从同一份 `configOptions` 里认出模型清单那一项,展平成可选值。
|
|
55
|
+
*
|
|
56
|
+
* 认法与力度档那边共用一套展平(平的 `{value,name}` 与按组分的
|
|
57
|
+
* `{group,name,options:[…]}` 都认,`currentValue` 标成默认)。此前这段只活在
|
|
58
|
+
* `agent-dsh` 的 `dshProbeModels` 里——`agent-acp` 要报清单时照抄一份就是第二个答案,
|
|
59
|
+
* 所以搬来这里,两条路读同一份。
|
|
60
|
+
*
|
|
61
|
+
* 返回 `undefined` 与空数组不是一回事:前者是「这份回包里认不出模型那一项」,后者是
|
|
62
|
+
* 「认出来了但一个选项都没有」。调用方据此决定报不报 `models`。
|
|
63
|
+
*/
|
|
64
|
+
export declare function acpModelChoicesFromConfigOptions(configOptions: unknown): ReadonlyArray<AcpConfigChoice> | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* 另一种报法:`session/new` 回包的 `models.availableModels`(ACP 的 UNSTABLE 那一位)。
|
|
67
|
+
*
|
|
68
|
+
* 两种报法都是标准的,各家挑一种——`claude-agent-acp` 与 dsh 走 `configOptions`,也有
|
|
69
|
+
* 走这一位的。只认一种就会对另一半的手整个报不出清单,所以两种都认,由调用方按顺序试。
|
|
70
|
+
*
|
|
71
|
+
* `collectSessionModelIds` 只取 id(够 `session/set_model` 用),这里要连名字和默认一起,
|
|
72
|
+
* 所以另起一个而不是改它——那个的调用方不需要多余字段。
|
|
73
|
+
*/
|
|
74
|
+
export declare function acpModelChoicesFromSessionModels(models: unknown): ReadonlyArray<AcpConfigChoice> | undefined;
|
|
75
|
+
//# sourceMappingURL=config-options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-options.d.ts","sourceRoot":"","sources":["../src/config-options.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,kBAAkB;AAClB,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,qCAAqC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,eAAe;AACf,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;CAClD,CAAC;AAmEF;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CA0BvF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gCAAgC,CAC9C,aAAa,EAAE,OAAO,GACrB,aAAa,CAAC,eAAe,CAAC,GAAG,SAAS,CAO5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,OAAO,GACd,aAAa,CAAC,eAAe,CAAC,GAAG,SAAS,CAqB5C"}
|