@agentclientprotocol/claude-agent-acp 0.67.0 → 0.69.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.
@@ -0,0 +1,56 @@
1
+ import { type HookCallback, type McpSdkServerConfigWithInstance } from "@anthropic-ai/claude-agent-sdk";
2
+ export declare const AGENT_FILE_CHANGE_REPORT_CAPABILITY = "agentFileChangeReport";
3
+ export declare const FILE_CHANGE_AUDIT_SERVER_NAME = "claude_agent_acp";
4
+ export declare const FILE_CHANGE_AUDIT_TOOL_NAME = "report_changed_files";
5
+ export declare const FILE_CHANGE_AUDIT_WIRE_TOOL_NAME = "mcp__claude_agent_acp__report_changed_files";
6
+ export declare const AGENT_FILE_CHANGE_REPORT_MAX_BYTES: number;
7
+ export type FileChangeAuditTurnState = {
8
+ requestId: string;
9
+ phase: "requested" | "collecting" | "finished";
10
+ };
11
+ export type FileChangeAuditWorkspace = {
12
+ cwd: string;
13
+ additionalDirectories: string[];
14
+ };
15
+ export type AgentFileChangeReport = {
16
+ paths: string[];
17
+ complete: boolean;
18
+ uncertainty?: string;
19
+ };
20
+ export type AgentFileChangeReportResult = {
21
+ version: 1;
22
+ requestId: string;
23
+ } & ({
24
+ status: "reported";
25
+ paths: string[];
26
+ declaredComplete: boolean;
27
+ truncated: boolean;
28
+ uncertainty?: string;
29
+ } | {
30
+ status: "unavailable";
31
+ reason: FileChangeReportUnavailableReason;
32
+ });
33
+ export type FileChangeReportUnavailableReason = "cancelled" | "timeout" | "invalidOutput" | "notReported" | "providerError";
34
+ type FileChangeAuditSupportOptions = {
35
+ cwd: string;
36
+ additionalDirectories: string[];
37
+ getActiveState: () => FileChangeAuditTurnState | undefined;
38
+ publish: (result: AgentFileChangeReportResult) => Promise<void>;
39
+ logError: (message: string) => void;
40
+ };
41
+ export type FileChangeAuditSupport = {
42
+ mcpServer: McpSdkServerConfigWithInstance;
43
+ preToolUseHook: HookCallback;
44
+ stopHook: HookCallback;
45
+ finishUnavailable: (state: FileChangeAuditTurnState, reason: FileChangeReportUnavailableReason) => Promise<void>;
46
+ };
47
+ export declare function agentFileChangeReportRequestId(meta: unknown): string | undefined;
48
+ export declare function supportsAgentFileChangeReport(capabilities: unknown): boolean;
49
+ export declare function agentFileChangeReportMeta(result: AgentFileChangeReportResult): Record<string, unknown>;
50
+ export declare function createFileChangeAuditTurnState(requestId: string): FileChangeAuditTurnState;
51
+ export declare function isFileChangeAuditReportPhase(state: FileChangeAuditTurnState | undefined): boolean;
52
+ export declare function isFileChangeAuditTool(toolName: string): boolean;
53
+ export declare function containsFileChangeAuditMarker(text: string): boolean;
54
+ export declare function createFileChangeAuditSupport(options: FileChangeAuditSupportOptions): FileChangeAuditSupport;
55
+ export {};
56
+ //# sourceMappingURL=file-change-audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-change-audit.d.ts","sourceRoot":"","sources":["../src/file-change-audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,8BAA8B,EAEpC,MAAM,gCAAgC,CAAC;AAKxC,eAAO,MAAM,mCAAmC,0BAA0B,CAAC;AAC3E,eAAO,MAAM,6BAA6B,qBAAqB,CAAC;AAChE,eAAO,MAAM,2BAA2B,yBAAyB,CAAC;AAClE,eAAO,MAAM,gCAAgC,gDAA0E,CAAC;AAKxH,eAAO,MAAM,kCAAkC,QAAa,CAAC;AAG7D,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,GAAG,YAAY,GAAG,UAAU,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,CACA;IACE,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GACD;IACE,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,iCAAiC,CAAC;CAC3C,CACJ,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAC3C,WAAW,GAAG,SAAS,GAAG,eAAe,GAAG,aAAa,GAAG,eAAe,CAAC;AAQ9E,KAAK,6BAA6B,GAAG;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,cAAc,EAAE,MAAM,wBAAwB,GAAG,SAAS,CAAC;IAC3D,OAAO,EAAE,CAAC,MAAM,EAAE,2BAA2B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,SAAS,EAAE,8BAA8B,CAAC;IAC1C,cAAc,EAAE,YAAY,CAAC;IAC7B,QAAQ,EAAE,YAAY,CAAC;IACvB,iBAAiB,EAAE,CACjB,KAAK,EAAE,wBAAwB,EAC/B,MAAM,EAAE,iCAAiC,KACtC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC;AAoBF,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAiBhF;AAED,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAW5E;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,2BAA2B,GAClC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB;AAYD,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,MAAM,GAAG,wBAAwB,CAK1F;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,wBAAwB,GAAG,SAAS,GAAG,OAAO,CAEjG;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,6BAA6B,GACrC,sBAAsB,CAmJxB"}
@@ -0,0 +1,379 @@
1
+ import { createSdkMcpServer, } from "@anthropic-ai/claude-agent-sdk";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { z } from "zod";
5
+ export const AGENT_FILE_CHANGE_REPORT_CAPABILITY = "agentFileChangeReport";
6
+ export const FILE_CHANGE_AUDIT_SERVER_NAME = "claude_agent_acp";
7
+ export const FILE_CHANGE_AUDIT_TOOL_NAME = "report_changed_files";
8
+ export const FILE_CHANGE_AUDIT_WIRE_TOOL_NAME = `mcp__${FILE_CHANGE_AUDIT_SERVER_NAME}__${FILE_CHANGE_AUDIT_TOOL_NAME}`;
9
+ const FILE_CHANGE_AUDIT_MARKER = "claude-agent-acp-file-change-audit";
10
+ const MAX_REPORTED_PATHS = 1024;
11
+ const MAX_REPORTED_PATH_LENGTH = 4096;
12
+ export const AGENT_FILE_CHANGE_REPORT_MAX_BYTES = 256 * 1024;
13
+ const REQUEST_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
14
+ const JETBRAINS_META_KEY = "jetbrains";
15
+ const AIR_META_KEY = "air";
16
+ const AIR_EXTENSION_VERSION = 1;
17
+ const AIR_EXTENSION_CAPABILITIES_KEY = "capabilities";
18
+ const AIR_AGENT_FILE_CHANGE_REPORT_KEY = "agentFileChangeReport";
19
+ const reportInputSchema = {
20
+ paths: z
21
+ .array(z.string())
22
+ .describe("Workspace file paths changed during this turn. Use absolute paths or paths relative to cwd."),
23
+ complete: z
24
+ .boolean()
25
+ .describe("True only when paths contains every workspace file changed during this turn."),
26
+ uncertainty: z
27
+ .string()
28
+ .trim()
29
+ .min(1)
30
+ .max(2000)
31
+ .optional()
32
+ .describe("Why the report may be incomplete or uncertain."),
33
+ };
34
+ export function agentFileChangeReportRequestId(meta) {
35
+ const air = airExtension(meta);
36
+ const request = air?.agentFileChangeReportRequest;
37
+ if (!request || typeof request !== "object" || Array.isArray(request))
38
+ return undefined;
39
+ const requestRecord = request;
40
+ const requestKeys = Object.keys(requestRecord);
41
+ if (requestKeys.length !== 2 ||
42
+ !requestKeys.includes("version") ||
43
+ !requestKeys.includes("requestId") ||
44
+ requestRecord.version !== 1)
45
+ return undefined;
46
+ const requestId = requestRecord.requestId;
47
+ return typeof requestId === "string" && REQUEST_ID_PATTERN.test(requestId)
48
+ ? requestId
49
+ : undefined;
50
+ }
51
+ export function supportsAgentFileChangeReport(capabilities) {
52
+ if (!capabilities || typeof capabilities !== "object" || Array.isArray(capabilities)) {
53
+ return false;
54
+ }
55
+ const air = airExtension(capabilities._meta);
56
+ const advertised = air?.[AIR_EXTENSION_CAPABILITIES_KEY];
57
+ return (air?.version === AIR_EXTENSION_VERSION &&
58
+ Array.isArray(advertised) &&
59
+ advertised.includes(AGENT_FILE_CHANGE_REPORT_CAPABILITY));
60
+ }
61
+ export function agentFileChangeReportMeta(result) {
62
+ return {
63
+ [JETBRAINS_META_KEY]: {
64
+ [AIR_META_KEY]: {
65
+ version: AIR_EXTENSION_VERSION,
66
+ [AIR_AGENT_FILE_CHANGE_REPORT_KEY]: result,
67
+ },
68
+ },
69
+ };
70
+ }
71
+ function airExtension(meta) {
72
+ if (!meta || typeof meta !== "object" || Array.isArray(meta))
73
+ return undefined;
74
+ const jetbrains = meta[JETBRAINS_META_KEY];
75
+ if (!jetbrains || typeof jetbrains !== "object" || Array.isArray(jetbrains))
76
+ return undefined;
77
+ const air = jetbrains[AIR_META_KEY];
78
+ return air && typeof air === "object" && !Array.isArray(air)
79
+ ? air
80
+ : undefined;
81
+ }
82
+ export function createFileChangeAuditTurnState(requestId) {
83
+ return {
84
+ requestId,
85
+ phase: "requested",
86
+ };
87
+ }
88
+ export function isFileChangeAuditReportPhase(state) {
89
+ return state?.phase === "collecting" || state?.phase === "finished";
90
+ }
91
+ export function isFileChangeAuditTool(toolName) {
92
+ return toolName === FILE_CHANGE_AUDIT_WIRE_TOOL_NAME;
93
+ }
94
+ export function containsFileChangeAuditMarker(text) {
95
+ return text.includes(`<${FILE_CHANGE_AUDIT_MARKER}>`);
96
+ }
97
+ export function createFileChangeAuditSupport(options) {
98
+ const workspace = normalizeWorkspace(options.cwd, options.additionalDirectories);
99
+ const finishUnavailable = async (state, reason) => {
100
+ // Claim the terminal outcome before the first await. The report tool, a
101
+ // Stop hook, cancellation, and stream teardown can race, but only the
102
+ // winner may publish for this request id. Publication remains fail-open:
103
+ // an audit transport failure must never hold the user prompt open.
104
+ if (state.phase === "finished")
105
+ return;
106
+ state.phase = "finished";
107
+ try {
108
+ await options.publish({
109
+ version: 1,
110
+ requestId: state.requestId,
111
+ status: "unavailable",
112
+ reason,
113
+ });
114
+ }
115
+ catch (error) {
116
+ options.logError(`Failed to publish unavailable report ${state.requestId}: ${error}`);
117
+ }
118
+ };
119
+ const tool = {
120
+ name: FILE_CHANGE_AUDIT_TOOL_NAME,
121
+ description: "Internal audit tool. Call it only when a stop-hook message explicitly requests the file-change audit.",
122
+ inputSchema: reportInputSchema,
123
+ _meta: {
124
+ "anthropic/alwaysLoad": true,
125
+ "claude/endTurn": true,
126
+ },
127
+ handler: async (report) => {
128
+ const state = options.getActiveState();
129
+ if (!state || state.phase !== "collecting") {
130
+ return {
131
+ isError: true,
132
+ content: [
133
+ {
134
+ type: "text",
135
+ text: `<${FILE_CHANGE_AUDIT_MARKER}>No file-change audit is active.</${FILE_CHANGE_AUDIT_MARKER}>`,
136
+ },
137
+ ],
138
+ };
139
+ }
140
+ // Flip before publishing: a client transport failure must not make the
141
+ // model retry the report and produce duplicates. Delivery is audit-only
142
+ // and fail-open; the normal turn must still finish.
143
+ state.phase = "finished";
144
+ const normalized = normalizeReportedPaths(report.paths, workspace);
145
+ const result = fitReportedAgentFileChangeReport({
146
+ version: 1,
147
+ requestId: state.requestId,
148
+ status: "reported",
149
+ paths: normalized.paths,
150
+ declaredComplete: report.complete && !normalized.truncated,
151
+ truncated: normalized.truncated,
152
+ ...(report.uncertainty ? { uncertainty: report.uncertainty } : {}),
153
+ });
154
+ try {
155
+ await options.publish(result);
156
+ }
157
+ catch (error) {
158
+ options.logError(`Failed to publish file-change report ${state.requestId}: ${error}`);
159
+ }
160
+ return {
161
+ content: [
162
+ {
163
+ type: "text",
164
+ text: `<${FILE_CHANGE_AUDIT_MARKER}>File-change audit recorded.</${FILE_CHANGE_AUDIT_MARKER}>`,
165
+ },
166
+ ],
167
+ structuredContent: result,
168
+ };
169
+ },
170
+ };
171
+ // canUseTool is not guaranteed to run for tools that the current permission
172
+ // mode auto-allows (notably bypassPermissions). A PreToolUse hook is the
173
+ // enforcement boundary that keeps the hidden continuation read-only in every
174
+ // mode: it can submit the report, but cannot inspect or mutate more files.
175
+ const preToolUseHook = async (input) => {
176
+ if (input.hook_event_name !== "PreToolUse")
177
+ return {};
178
+ const state = options.getActiveState();
179
+ const isReportTool = isFileChangeAuditTool(input.tool_name);
180
+ if (!isReportTool && !isFileChangeAuditReportPhase(state))
181
+ return {};
182
+ const allowReport = state?.phase === "collecting" && isReportTool;
183
+ return {
184
+ hookSpecificOutput: {
185
+ hookEventName: "PreToolUse",
186
+ permissionDecision: allowReport ? "allow" : "deny",
187
+ ...(!allowReport
188
+ ? {
189
+ permissionDecisionReason: "Only the internal file-change report is allowed during the audit.",
190
+ }
191
+ : {}),
192
+ },
193
+ };
194
+ };
195
+ const stopHook = async (input) => {
196
+ if (input.hook_event_name !== "Stop")
197
+ return {};
198
+ const state = options.getActiveState();
199
+ if (!state || state.phase === "finished")
200
+ return {};
201
+ // A Stop can be only a pause while a background generator is still
202
+ // running. Keep the request pending so a later final Stop can include its
203
+ // writes instead of declaring a prematurely complete path set.
204
+ if (state.phase === "requested" && (input.background_tasks?.length ?? 0) > 0)
205
+ return {};
206
+ if (state.phase === "collecting" || input.stop_hook_active) {
207
+ await finishUnavailable(state, "notReported");
208
+ return {};
209
+ }
210
+ // Set the phase before returning the continuation so every following SDK
211
+ // message is hidden even if the next stream starts immediately.
212
+ state.phase = "collecting";
213
+ return {
214
+ hookSpecificOutput: {
215
+ hookEventName: "Stop",
216
+ additionalContext: `<${FILE_CHANGE_AUDIT_MARKER}>` +
217
+ `Before stopping, call ${FILE_CHANGE_AUDIT_WIRE_TOOL_NAME} exactly once. ` +
218
+ "Report every workspace file changed during this user turn, including files changed " +
219
+ "by commands, generators, version-control operations, and child processes. " +
220
+ "Do not call any other tool and do not emit user-facing prose. " +
221
+ "Set complete to false and explain uncertainty when you are not sure the list is complete." +
222
+ `</${FILE_CHANGE_AUDIT_MARKER}>`,
223
+ },
224
+ };
225
+ };
226
+ return {
227
+ mcpServer: createSdkMcpServer({
228
+ name: FILE_CHANGE_AUDIT_SERVER_NAME,
229
+ version: "1.0.0",
230
+ tools: [tool],
231
+ alwaysLoad: true,
232
+ }),
233
+ preToolUseHook,
234
+ stopHook,
235
+ finishUnavailable,
236
+ };
237
+ }
238
+ function normalizeWorkspace(cwd, additionalDirectories) {
239
+ const normalizedCwd = canonicalizeWorkspaceRoot(path.resolve(cwd));
240
+ const seen = new Set([pathKey(normalizedCwd)]);
241
+ const normalizedAdditionalDirectories = [];
242
+ for (const directory of additionalDirectories) {
243
+ const normalized = canonicalizeWorkspaceRoot(path.resolve(normalizedCwd, directory));
244
+ const key = pathKey(normalized);
245
+ if (seen.has(key))
246
+ continue;
247
+ seen.add(key);
248
+ normalizedAdditionalDirectories.push(normalized);
249
+ }
250
+ return {
251
+ cwd: normalizedCwd,
252
+ additionalDirectories: normalizedAdditionalDirectories,
253
+ };
254
+ }
255
+ function normalizeReportedPaths(reportedPaths, workspace) {
256
+ const roots = [workspace.cwd, ...workspace.additionalDirectories];
257
+ const seen = new Set();
258
+ const result = [];
259
+ let totalBytes = 0;
260
+ let truncated = false;
261
+ for (const reportedPath of reportedPaths) {
262
+ if (reportedPath.trim().length === 0 ||
263
+ reportedPath.length > MAX_REPORTED_PATH_LENGTH ||
264
+ hasControlCharacter(reportedPath)) {
265
+ truncated = true;
266
+ continue;
267
+ }
268
+ let normalized;
269
+ try {
270
+ normalized = canonicalizeReportedPath(path.resolve(workspace.cwd, reportedPath));
271
+ }
272
+ catch {
273
+ truncated = true;
274
+ continue;
275
+ }
276
+ if (normalized.length > MAX_REPORTED_PATH_LENGTH || hasControlCharacter(normalized)) {
277
+ truncated = true;
278
+ continue;
279
+ }
280
+ if (!roots.some((root) => isWithinRoot(normalized, root))) {
281
+ truncated = true;
282
+ continue;
283
+ }
284
+ const key = pathKey(normalized);
285
+ if (roots.some((root) => pathKey(root) === key)) {
286
+ truncated = true;
287
+ continue;
288
+ }
289
+ if (seen.has(key)) {
290
+ continue;
291
+ }
292
+ const pathBytes = Buffer.byteLength(normalized, "utf8");
293
+ if (result.length >= MAX_REPORTED_PATHS ||
294
+ totalBytes + pathBytes > AGENT_FILE_CHANGE_REPORT_MAX_BYTES) {
295
+ truncated = true;
296
+ continue;
297
+ }
298
+ seen.add(key);
299
+ result.push(normalized);
300
+ totalBytes += pathBytes;
301
+ }
302
+ return { paths: result, truncated };
303
+ }
304
+ /** Keep the complete serialized report within AIR's wire limit. */
305
+ function fitReportedAgentFileChangeReport(report) {
306
+ const paths = [...report.paths];
307
+ let fitted = report;
308
+ while (Buffer.byteLength(JSON.stringify(fitted), "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_BYTES) {
309
+ if (paths.length === 0) {
310
+ // The fixed fields and bounded uncertainty are far below the cap. Keep
311
+ // this loud if the wire contract changes without updating this helper.
312
+ throw new Error("Agent file-change report exceeds the wire limit without paths");
313
+ }
314
+ paths.pop();
315
+ fitted = {
316
+ ...report,
317
+ paths: [...paths],
318
+ declaredComplete: false,
319
+ truncated: true,
320
+ };
321
+ }
322
+ return fitted;
323
+ }
324
+ function isWithinRoot(candidate, root) {
325
+ const relative = path.relative(root, candidate);
326
+ return !path.isAbsolute(relative) && !relative.startsWith(`..${path.sep}`) && relative !== "..";
327
+ }
328
+ /**
329
+ * Resolve filesystem aliases in a workspace root, including macOS' /tmp ->
330
+ * /private/tmp alias. For a root that does not exist yet, resolve its nearest
331
+ * existing ancestor and retain the missing suffix.
332
+ */
333
+ function canonicalizeWorkspaceRoot(value) {
334
+ return canonicalizeFromExistingAncestor(value);
335
+ }
336
+ /**
337
+ * Canonicalize the parent but not the leaf itself. A changed path may be
338
+ * deleted, or it may be a symlink whose node (rather than target) changed.
339
+ */
340
+ function canonicalizeReportedPath(value) {
341
+ const parent = canonicalizeFromExistingAncestor(path.dirname(value));
342
+ return path.resolve(parent, path.basename(value));
343
+ }
344
+ function canonicalizeFromExistingAncestor(value) {
345
+ const original = path.resolve(value);
346
+ let current = original;
347
+ const missingSegments = [];
348
+ while (true) {
349
+ try {
350
+ const canonical = fs.realpathSync.native(current);
351
+ return path.resolve(canonical, ...missingSegments.reverse());
352
+ }
353
+ catch (error) {
354
+ if (!isMissingPathError(error))
355
+ return original;
356
+ }
357
+ const parent = path.dirname(current);
358
+ if (parent === current)
359
+ return original;
360
+ missingSegments.push(path.basename(current));
361
+ current = parent;
362
+ }
363
+ }
364
+ function isMissingPathError(error) {
365
+ if (typeof error !== "object" || error === null || !("code" in error))
366
+ return false;
367
+ return error.code === "ENOENT" || error.code === "ENOTDIR";
368
+ }
369
+ function pathKey(value) {
370
+ return process.platform === "win32" ? value.toLowerCase() : value;
371
+ }
372
+ function hasControlCharacter(value) {
373
+ for (let index = 0; index < value.length; index++) {
374
+ const code = value.charCodeAt(index);
375
+ if (code <= 0x1f || (code >= 0x7f && code <= 0x9f))
376
+ return true;
377
+ }
378
+ return false;
379
+ }
@@ -0,0 +1,104 @@
1
+ import type { ClientCapabilities, SessionNotification } from "@agentclientprotocol/sdk";
2
+ import { getSessionMessages, type SDKAssistantMessageError } from "@anthropic-ai/claude-agent-sdk";
3
+ export declare function airSessionFailureCapabilityMeta(...additionalCapabilities: string[]): {
4
+ jetbrains: {
5
+ air: {
6
+ version: number;
7
+ capabilities: string[];
8
+ };
9
+ };
10
+ };
11
+ export type ClaudeFailureKind = "advisory" | "auth_required" | "bad_request" | "budget_exhausted" | "context_exhausted" | "internal_error" | "overloaded" | "provider_error" | "quota_exhausted" | "rate_limited" | "transport_lost" | "worker_shutdown";
12
+ type AirSessionFailureCategory = "connection" | "access" | "limit" | "request" | "service" | "unknown";
13
+ type AirSessionFailureSeverity = "warning" | "error";
14
+ type AirSessionFailureAction = "retry" | "login" | "new_session";
15
+ type SessionFailureRecoveryPolicy = "never" | "next_attempt" | "real_model_success" | "auth_status" | "runtime_rebind";
16
+ export type PublishedSessionFailure = {
17
+ id: string;
18
+ revision: number;
19
+ kind: ClaudeFailureKind;
20
+ category: AirSessionFailureCategory;
21
+ turnId?: string;
22
+ severity: AirSessionFailureSeverity;
23
+ title: string;
24
+ details?: string;
25
+ actions: AirSessionFailureAction[];
26
+ recoveryPolicy: SessionFailureRecoveryPolicy;
27
+ };
28
+ type SessionFailureOptions = {
29
+ turnId?: string;
30
+ sessionScoped?: boolean;
31
+ title?: string;
32
+ details?: string;
33
+ severity?: AirSessionFailureSeverity;
34
+ };
35
+ export type SessionFailureState = {
36
+ epoch: string;
37
+ revisions: Map<string, number>;
38
+ active: Map<string, PublishedSessionFailure>;
39
+ nextIncident?: number;
40
+ lastNotice?: {
41
+ title: string;
42
+ id: string;
43
+ };
44
+ };
45
+ type Logger = {
46
+ error: (...args: unknown[]) => void;
47
+ };
48
+ export declare function createSessionFailureState(): SessionFailureState;
49
+ export declare function sessionFailureMeta(failure: PublishedSessionFailure): {
50
+ jetbrains: {
51
+ air: {
52
+ version: number;
53
+ sessionFailure: {
54
+ actions: AirSessionFailureAction[];
55
+ details?: string | undefined;
56
+ id: string;
57
+ revision: number;
58
+ category: AirSessionFailureCategory;
59
+ severity: AirSessionFailureSeverity;
60
+ title: string;
61
+ };
62
+ };
63
+ };
64
+ };
65
+ /** `getSessionMessages` deliberately exposes only the API message and strips
66
+ * transcript-level `error` / `isApiErrorMessage` fields. The SDK exports the
67
+ * exact stable prefixes used by its synthetic usage-limit errors, so replay
68
+ * can recover this one typed failure without matching arbitrary model prose. */
69
+ export declare function assistantMessageText(apiMessage: unknown): string | undefined;
70
+ export declare function isSyntheticUsageLimitMessage(apiMessage: unknown): boolean;
71
+ export declare function activeUsageLimitMessage(messages: Awaited<ReturnType<typeof getSessionMessages>>): {
72
+ uuid: string;
73
+ title: string;
74
+ } | undefined;
75
+ export declare function supportsAirSessionFailures(capabilities?: ClientCapabilities): boolean;
76
+ /** Owns the AIR failure lifecycle for both the live consumer and history replay.
77
+ * Published revisions become active only after delivery succeeds; recovery only
78
+ * removes internal active state because transcript records are historical. */
79
+ export declare class SessionFailureController {
80
+ private readonly sessionId;
81
+ private readonly state;
82
+ private readonly capabilities?;
83
+ private readonly isCurrent;
84
+ private readonly sendUpdate;
85
+ private readonly logger;
86
+ constructor(options: {
87
+ sessionId: string;
88
+ state: SessionFailureState;
89
+ capabilities?: ClientCapabilities;
90
+ isCurrent: () => boolean;
91
+ sendUpdate: (notification: SessionNotification) => Promise<void>;
92
+ logger: Logger;
93
+ });
94
+ private isSupported;
95
+ private emit;
96
+ recordActive(failure: PublishedSessionFailure): void;
97
+ clear(shouldClear?: (failure: PublishedSessionFailure) => boolean): Promise<boolean>;
98
+ prepare(kind: ClaudeFailureKind, failureOptions?: SessionFailureOptions): Promise<PublishedSessionFailure | undefined>;
99
+ publish(kind: ClaudeFailureKind, failureOptions?: SessionFailureOptions): Promise<void>;
100
+ restore(id: string, kind: ClaudeFailureKind, title: string, active?: boolean): Promise<boolean>;
101
+ }
102
+ export declare function providerFailureCategory(errorKind?: SDKAssistantMessageError, isUsageLimit?: boolean): ClaudeFailureKind;
103
+ export {};
104
+ //# sourceMappingURL=session-failure-extension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-failure-extension.d.ts","sourceRoot":"","sources":["../src/session-failure-extension.ts"],"names":[],"mappings":"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"}