@lucascouts/claude-agent-acp-plus 0.23.6 → 0.25.0
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 +54 -0
- package/dist/acp-agent.d.ts +85 -2
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +286 -31
- package/dist/classifier-escalation.d.ts +158 -0
- package/dist/classifier-escalation.d.ts.map +1 -0
- package/dist/classifier-escalation.js +562 -0
- package/dist/permissions/options/shared.d.ts +8 -0
- package/dist/permissions/options/shared.d.ts.map +1 -1
- package/dist/permissions/options/shared.js +18 -0
- package/dist/permissions/presentation.d.ts +1 -0
- package/dist/permissions/presentation.d.ts.map +1 -1
- package/dist/permissions/presentation.js +1 -1
- package/dist/permissions/response.d.ts +6 -0
- package/dist/permissions/response.d.ts.map +1 -1
- package/dist/permissions/response.js +19 -0
- package/dist/task-feed.d.ts +195 -0
- package/dist/task-feed.d.ts.map +1 -0
- package/dist/task-feed.js +530 -0
- package/package.json +1 -1
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 016: a call the auto-mode classifier denies asks the operator instead
|
|
3
|
+
* of failing. An approval becomes a grant — memory only, scoped to one
|
|
4
|
+
* session — that the next matching call consumes to skip the classifier.
|
|
5
|
+
*/
|
|
6
|
+
import type { SessionNotification } from "@agentclientprotocol/sdk";
|
|
7
|
+
import type { HookCallback } from "@anthropic-ai/claude-agent-sdk";
|
|
8
|
+
import type { ClaudeAcpAgent, Logger, ToolUpdateMeta } from "./acp-agent.js";
|
|
9
|
+
import { type ClassifierEscalationAnswer } from "./permissions/response.js";
|
|
10
|
+
/**
|
|
11
|
+
* One string per (tool name, input): keys sorted recursively, array order kept,
|
|
12
|
+
* and the top-level Bash `description` dropped — a cosmetic label the model
|
|
13
|
+
* rewrites on retry. `undefined` when the input cannot be serialised (cyclic),
|
|
14
|
+
* which every caller treats as "no grant possible".
|
|
15
|
+
*/
|
|
16
|
+
export declare function fingerprint(toolName: string, input: unknown): string | undefined;
|
|
17
|
+
export type GrantKind = "once" | "session";
|
|
18
|
+
/** The grants of one session. Memory only: nothing here touches the disk. */
|
|
19
|
+
export declare class GrantStore {
|
|
20
|
+
private readonly once;
|
|
21
|
+
private readonly session;
|
|
22
|
+
grantOnce(fp: string): void;
|
|
23
|
+
grantSession(fp: string): void;
|
|
24
|
+
/** Whether a grant matches, without consuming it. */
|
|
25
|
+
has(fp: string): boolean;
|
|
26
|
+
/** A session grant wins and stays; a once grant is deleted when consumed. */
|
|
27
|
+
consume(fp: string): GrantKind | undefined;
|
|
28
|
+
/** Turn end: an unused "Yes" does not outlive its turn. */
|
|
29
|
+
clearOnce(): void;
|
|
30
|
+
/** Session teardown. */
|
|
31
|
+
clear(): void;
|
|
32
|
+
}
|
|
33
|
+
/** How an escalated call ended: the operator's answer, or the fail-closed default. */
|
|
34
|
+
export type EscalationOutcome = "approved" | "rejected" | "cancelled";
|
|
35
|
+
/** A `permission_denied` frame, as the frame handler notes it. */
|
|
36
|
+
export type DeniedFrame = {
|
|
37
|
+
toolUseId: string;
|
|
38
|
+
/** `decision_reason_type`: "classifier", "rule", "mode", … */
|
|
39
|
+
reasonType: string | undefined;
|
|
40
|
+
/** `decision_reason ?? message` — exactly the text today's failed update shows. */
|
|
41
|
+
reason: string;
|
|
42
|
+
/** A subagent's `agent_id`, when the denied call ran inside one. */
|
|
43
|
+
agentId?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Today's `_meta` for the denied call's failed update (tool name, parent
|
|
46
|
+
* stamp, `toolResponse`), built by the frame handler where the session is in
|
|
47
|
+
* reach. Reused verbatim so a rejected update is byte for byte today's.
|
|
48
|
+
*/
|
|
49
|
+
meta?: ToolUpdateMeta;
|
|
50
|
+
/**
|
|
51
|
+
* Whether the client was ever sent this call's `tool_call`. A fallback update
|
|
52
|
+
* for a call the client never saw is dropped, as today's handler drops it.
|
|
53
|
+
*/
|
|
54
|
+
emitted?: boolean;
|
|
55
|
+
};
|
|
56
|
+
type ToolCallUpdate = Extract<SessionNotification["update"], {
|
|
57
|
+
sessionUpdate: "tool_call_update";
|
|
58
|
+
}>;
|
|
59
|
+
export type EscalationRegistryDeps = {
|
|
60
|
+
sessionId: string;
|
|
61
|
+
/** Sends one `tool_call_update` for the denied call to the client. */
|
|
62
|
+
emit: (toolUseId: string, update: ToolCallUpdate) => unknown;
|
|
63
|
+
logger: Logger;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Orders the two signals the CLI sends for one classifier denial — the
|
|
67
|
+
* `permission_denied` frame and the `PermissionDenied` hook, in either order,
|
|
68
|
+
* keyed by tool_use_id — and guarantees the denied call exactly one final
|
|
69
|
+
* `tool_call_update`: never zero (a call left pending), never two.
|
|
70
|
+
*/
|
|
71
|
+
export declare class EscalationRegistry {
|
|
72
|
+
private readonly sessionId;
|
|
73
|
+
private readonly emit;
|
|
74
|
+
private readonly logger;
|
|
75
|
+
private readonly frames;
|
|
76
|
+
private readonly waiters;
|
|
77
|
+
private readonly deadlines;
|
|
78
|
+
private readonly claimed;
|
|
79
|
+
private readonly abandoned;
|
|
80
|
+
private readonly settled;
|
|
81
|
+
constructor(deps: EscalationRegistryDeps);
|
|
82
|
+
/** Frame side. Answers any hook waiting on this id; a classifier frame arms the fallback. */
|
|
83
|
+
noteFrame(frame: DeniedFrame): void;
|
|
84
|
+
/** Hook side. The frame's reason type, or `undefined` after 5 s. Never rejects. */
|
|
85
|
+
awaitReasonType(toolUseId: string): Promise<string | undefined>;
|
|
86
|
+
/**
|
|
87
|
+
* The hook takes ownership: only `resolve` settles this id from now on.
|
|
88
|
+
* `false` when the call was already settled — the hook must not ask then.
|
|
89
|
+
*/
|
|
90
|
+
claim(toolUseId: string): boolean;
|
|
91
|
+
/** The hook gave up (not the classifier's, or no frame in 5 s). */
|
|
92
|
+
abandon(toolUseId: string): void;
|
|
93
|
+
/** Idempotent: the first outcome for an id wins and is emitted once. */
|
|
94
|
+
resolve(toolUseId: string, outcome: EscalationOutcome): void;
|
|
95
|
+
/** True once claimed and until resolved. */
|
|
96
|
+
isEscalated(toolUseId: string): boolean;
|
|
97
|
+
/** True after `resolve(..., "approved")`; the `tool_result` path reads it. */
|
|
98
|
+
wasApproved(toolUseId: string): boolean;
|
|
99
|
+
/** Session teardown: drop every timer and every per-call record. */
|
|
100
|
+
clear(): void;
|
|
101
|
+
private settle;
|
|
102
|
+
private send;
|
|
103
|
+
private clearDeadline;
|
|
104
|
+
}
|
|
105
|
+
/** What the hook asks the operator about: the denied call, as the CLI reported it. */
|
|
106
|
+
export type EscalationRequest = {
|
|
107
|
+
/** The denied call's own tool_use_id — the request references it, never a new one. */
|
|
108
|
+
toolUseId: string;
|
|
109
|
+
toolName: string;
|
|
110
|
+
input: unknown;
|
|
111
|
+
/** The classifier's reason, shown alongside the command. */
|
|
112
|
+
reason: string;
|
|
113
|
+
/** A subagent's `agent_id`, when the denied call ran inside one. */
|
|
114
|
+
agentId?: string;
|
|
115
|
+
};
|
|
116
|
+
/** Asks the operator one question; rejects when the client cannot answer. */
|
|
117
|
+
export type AskOperator = (req: EscalationRequest, signal: AbortSignal) => Promise<ClassifierEscalationAnswer>;
|
|
118
|
+
/**
|
|
119
|
+
* The only bridge from the hook to the client: one ACP permission request with
|
|
120
|
+
* the three classifier answers, referencing the denied call's own tool call
|
|
121
|
+
* (emitted first if the client has not seen it), and attributed to the
|
|
122
|
+
* spawning Agent/Task call when the denial happened inside a subagent.
|
|
123
|
+
*/
|
|
124
|
+
export declare function createAskOperator(agent: ClaudeAcpAgent, sessionId: string): AskOperator;
|
|
125
|
+
export type PermissionDeniedHookDeps = {
|
|
126
|
+
session: {
|
|
127
|
+
sessionId: string;
|
|
128
|
+
classifierGrants: GrantStore;
|
|
129
|
+
};
|
|
130
|
+
logger: Logger;
|
|
131
|
+
escalations: Pick<EscalationRegistry, "awaitReasonType" | "claim" | "abandon" | "resolve">;
|
|
132
|
+
askOperator: AskOperator;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* The `PermissionDenied` hook. A classifier denial becomes one question; "Yes"
|
|
136
|
+
* or "Yes for this session" becomes a grant plus `retry: true`, which only
|
|
137
|
+
* tells the model it may retry — the grant hook is what lets the retry run.
|
|
138
|
+
* Every other path (No, cancel, abort, a failing client, the 590 s bound, an
|
|
139
|
+
* input that cannot be fingerprinted) grants nothing: fail closed.
|
|
140
|
+
*/
|
|
141
|
+
export declare function createPermissionDeniedHook(deps: PermissionDeniedHookDeps): HookCallback;
|
|
142
|
+
/**
|
|
143
|
+
* The `PreToolUse` side: a call matching a grant is allowed before the
|
|
144
|
+
* classifier sees it — measured in Task 1 to skip the classifier. Anything else
|
|
145
|
+
* returns `{}` and the CLI decides as usual.
|
|
146
|
+
*
|
|
147
|
+
* A grant answers a classifier denial, so it applies only while the CLI reports
|
|
148
|
+
* `auto` for this very call (story 029). In any other mode — or with no mode —
|
|
149
|
+
* it is held, not consumed: that mode's own checks decide, and the grant is
|
|
150
|
+
* still there when the session returns to `auto`. The mode is read from the
|
|
151
|
+
* hook input, never from the adapter's session state, which can lag a switch.
|
|
152
|
+
*/
|
|
153
|
+
export declare function createGrantPreToolUseHook(session: {
|
|
154
|
+
sessionId: string;
|
|
155
|
+
classifierGrants: GrantStore;
|
|
156
|
+
}, logger: Logger): HookCallback;
|
|
157
|
+
export {};
|
|
158
|
+
//# sourceMappingURL=classifier-escalation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifier-escalation.d.ts","sourceRoot":"","sources":["../src/classifier-escalation.ts"],"names":[],"mappings":"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"}
|