@lucascouts/claude-agent-acp-plus 0.8.0 → 0.9.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/dist/acp-agent.d.ts +107 -95
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +1050 -818
- package/dist/clear-context-coordinator.d.ts +58 -0
- package/dist/clear-context-coordinator.d.ts.map +1 -0
- package/dist/clear-context-coordinator.js +79 -0
- package/dist/exit-plan.d.ts +42 -0
- package/dist/exit-plan.d.ts.map +1 -0
- package/dist/exit-plan.js +154 -0
- package/dist/file-change-audit.d.ts +56 -0
- package/dist/file-change-audit.d.ts.map +1 -0
- package/dist/file-change-audit.js +379 -0
- package/dist/index.js +24 -1
- package/dist/permissions/effects.d.ts +19 -0
- package/dist/permissions/effects.d.ts.map +1 -0
- package/dist/permissions/effects.js +166 -0
- package/dist/permissions/modes.d.ts +8 -0
- package/dist/permissions/modes.d.ts.map +1 -0
- package/dist/permissions/modes.js +41 -0
- package/dist/permissions/normalization.d.ts +6 -0
- package/dist/permissions/normalization.d.ts.map +1 -0
- package/dist/permissions/normalization.js +100 -0
- package/dist/permissions/options/filesystem.d.ts +9 -0
- package/dist/permissions/options/filesystem.d.ts.map +1 -0
- package/dist/permissions/options/filesystem.js +124 -0
- package/dist/permissions/options/shared.d.ts +39 -0
- package/dist/permissions/options/shared.d.ts.map +1 -0
- package/dist/permissions/options/shared.js +64 -0
- package/dist/permissions/options/shell.d.ts +5 -0
- package/dist/permissions/options/shell.d.ts.map +1 -0
- package/dist/permissions/options/shell.js +100 -0
- package/dist/permissions/options/tools.d.ts +11 -0
- package/dist/permissions/options/tools.d.ts.map +1 -0
- package/dist/permissions/options/tools.js +135 -0
- package/dist/permissions/options.d.ts +5 -0
- package/dist/permissions/options.d.ts.map +1 -0
- package/dist/permissions/options.js +60 -0
- package/dist/permissions/presentation.d.ts +15 -0
- package/dist/permissions/presentation.d.ts.map +1 -0
- package/dist/permissions/presentation.js +83 -0
- package/dist/permissions/response.d.ts +11 -0
- package/dist/permissions/response.d.ts.map +1 -0
- package/dist/permissions/response.js +19 -0
- package/dist/session-config-ids.d.ts +5 -0
- package/dist/session-config-ids.d.ts.map +1 -0
- package/dist/session-config-ids.js +4 -0
- package/dist/session-failure-extension.d.ts +104 -0
- package/dist/session-failure-extension.d.ts.map +1 -0
- package/dist/session-failure-extension.js +352 -0
- package/dist/session-mode.d.ts +66 -0
- package/dist/session-mode.d.ts.map +1 -0
- package/dist/session-mode.js +246 -0
- package/dist/session-titles.d.ts +77 -0
- package/dist/session-titles.d.ts.map +1 -0
- package/dist/session-titles.js +199 -0
- package/dist/tool-result-meta.d.ts +8 -0
- package/dist/tool-result-meta.d.ts.map +1 -0
- package/dist/tool-result-meta.js +19 -0
- package/dist/tools.d.ts +4 -6
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +129 -24
- package/package.json +9 -9
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { NewSessionRequest, SessionConfigOption } from "@agentclientprotocol/sdk";
|
|
2
|
+
import type { PermissionMode, SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
3
|
+
export type ClearContextReset = {
|
|
4
|
+
toolUseId: string;
|
|
5
|
+
plan: string;
|
|
6
|
+
mode: PermissionMode;
|
|
7
|
+
};
|
|
8
|
+
type Usage = {
|
|
9
|
+
inputTokens: number;
|
|
10
|
+
outputTokens: number;
|
|
11
|
+
cachedReadTokens: number;
|
|
12
|
+
cachedWriteTokens: number;
|
|
13
|
+
};
|
|
14
|
+
export type ClearContextTurn = {
|
|
15
|
+
settled: boolean;
|
|
16
|
+
promptUuid: string;
|
|
17
|
+
carriedUsage?: Usage;
|
|
18
|
+
};
|
|
19
|
+
/** The session state needed to replace Claude's private conversation without
|
|
20
|
+
* replacing the public ACP turn. Keeping this projection here makes the
|
|
21
|
+
* restart workflow independent from the agent's much larger Session record. */
|
|
22
|
+
export type ClearContextSession<Turn extends ClearContextTurn = ClearContextTurn> = {
|
|
23
|
+
cwd: string;
|
|
24
|
+
creationParams?: NewSessionRequest;
|
|
25
|
+
accumulatedUsage: Usage;
|
|
26
|
+
models: {
|
|
27
|
+
currentModelId: string;
|
|
28
|
+
};
|
|
29
|
+
configOptions: SessionConfigOption[];
|
|
30
|
+
currentAgent: string;
|
|
31
|
+
fastModeEnabled: boolean;
|
|
32
|
+
activeTurn?: Turn | null;
|
|
33
|
+
turnQueue?: Turn[];
|
|
34
|
+
pendingExitPlanContextReset?: ClearContextReset;
|
|
35
|
+
contextUsedTokens?: number;
|
|
36
|
+
input: {
|
|
37
|
+
push(message: SDKUserMessage): unknown;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export type ClearContextCoordinatorHost<Session extends ClearContextSession<Turn>, Turn extends ClearContextTurn> = {
|
|
41
|
+
currentSession(sessionId: string): Session | undefined;
|
|
42
|
+
closeQueryStream(session: Session): void;
|
|
43
|
+
restartSession(params: NewSessionRequest, options: {
|
|
44
|
+
publicSessionId: string;
|
|
45
|
+
permissionMode: PermissionMode;
|
|
46
|
+
}): Promise<Session>;
|
|
47
|
+
applyFastMode(session: Session, enabled: boolean): Promise<void>;
|
|
48
|
+
publishSessionState(sessionId: string, mode: PermissionMode, configOptions: SessionConfigOption[]): Promise<void>;
|
|
49
|
+
continuationMessage(sessionId: string, plan: string, promptUuid: string): SDKUserMessage;
|
|
50
|
+
ensureConsumer(session: Session, sessionId: string): void;
|
|
51
|
+
logError(message: string, error: unknown): void;
|
|
52
|
+
};
|
|
53
|
+
/** Replace Claude's private conversation and attach the still-pending ACP turn
|
|
54
|
+
* to it. The host owns provider-specific creation and stream mechanics; this
|
|
55
|
+
* coordinator owns the ordering and state transfer invariants. */
|
|
56
|
+
export declare function continuePlanInFreshContext<Turn extends ClearContextTurn, Session extends ClearContextSession<Turn>>(sessionId: string, oldSession: Session, reset: ClearContextReset, host: ClearContextCoordinatorHost<Session, Turn>, signal?: AbortSignal): Promise<void>;
|
|
57
|
+
export {};
|
|
58
|
+
//# sourceMappingURL=clear-context-coordinator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clear-context-coordinator.d.ts","sourceRoot":"","sources":["../src/clear-context-coordinator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { DEFAULT_AGENT_ID, DEFAULT_MODEL_ID, EFFORT_CONFIG_ID } from "./session-config-ids.js";
|
|
2
|
+
function restartParams(session) {
|
|
3
|
+
const originalParams = session.creationParams ?? { cwd: session.cwd, mcpServers: [] };
|
|
4
|
+
const currentEffort = session.configOptions.find((option) => option.id === EFFORT_CONFIG_ID)?.currentValue;
|
|
5
|
+
const originalMeta = originalParams._meta;
|
|
6
|
+
const originalOptions = originalMeta?.claudeCode?.options;
|
|
7
|
+
const unmanagedOptions = { ...(originalOptions ?? {}) };
|
|
8
|
+
delete unmanagedOptions.model;
|
|
9
|
+
delete unmanagedOptions.agent;
|
|
10
|
+
delete unmanagedOptions.effort;
|
|
11
|
+
return {
|
|
12
|
+
...originalParams,
|
|
13
|
+
_meta: {
|
|
14
|
+
...(originalMeta ?? {}),
|
|
15
|
+
claudeCode: {
|
|
16
|
+
...(originalMeta?.claudeCode ?? {}),
|
|
17
|
+
options: {
|
|
18
|
+
...unmanagedOptions,
|
|
19
|
+
...(session.models.currentModelId !== DEFAULT_MODEL_ID
|
|
20
|
+
? { model: session.models.currentModelId }
|
|
21
|
+
: {}),
|
|
22
|
+
...(session.currentAgent !== DEFAULT_AGENT_ID ? { agent: session.currentAgent } : {}),
|
|
23
|
+
...(typeof currentEffort === "string" && currentEffort !== "default"
|
|
24
|
+
? { effort: currentEffort }
|
|
25
|
+
: {}),
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** Replace Claude's private conversation and attach the still-pending ACP turn
|
|
32
|
+
* to it. The host owns provider-specific creation and stream mechanics; this
|
|
33
|
+
* coordinator owns the ordering and state transfer invariants. */
|
|
34
|
+
export async function continuePlanInFreshContext(sessionId, oldSession, reset, host, signal) {
|
|
35
|
+
const assertRestartActive = (session) => {
|
|
36
|
+
if (signal?.aborted || (session && host.currentSession(sessionId) !== session)) {
|
|
37
|
+
throw new Error("Clear-context restart aborted");
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
assertRestartActive();
|
|
41
|
+
const turn = oldSession.activeTurn;
|
|
42
|
+
if (!turn || turn.settled || host.currentSession(sessionId) !== oldSession) {
|
|
43
|
+
throw new Error("Cannot clear context without an active ACP turn");
|
|
44
|
+
}
|
|
45
|
+
const params = restartParams(oldSession);
|
|
46
|
+
host.closeQueryStream(oldSession);
|
|
47
|
+
const freshSession = await host.restartSession(params, {
|
|
48
|
+
publicSessionId: sessionId,
|
|
49
|
+
permissionMode: reset.mode,
|
|
50
|
+
});
|
|
51
|
+
assertRestartActive(freshSession);
|
|
52
|
+
// Do not consume the reset or mutate the turn until a replacement exists.
|
|
53
|
+
// A failed restart must remain distinguishable from a lost query transport.
|
|
54
|
+
turn.carriedUsage = { ...oldSession.accumulatedUsage };
|
|
55
|
+
oldSession.pendingExitPlanContextReset = undefined;
|
|
56
|
+
if (oldSession.fastModeEnabled !== freshSession.fastModeEnabled) {
|
|
57
|
+
try {
|
|
58
|
+
await host.applyFastMode(freshSession, oldSession.fastModeEnabled);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
host.logError("Failed to restore Fast mode after clearing context:", error);
|
|
62
|
+
}
|
|
63
|
+
assertRestartActive(freshSession);
|
|
64
|
+
}
|
|
65
|
+
oldSession.activeTurn = null;
|
|
66
|
+
oldSession.turnQueue = [];
|
|
67
|
+
freshSession.turnQueue = [turn];
|
|
68
|
+
freshSession.contextUsedTokens = 0;
|
|
69
|
+
try {
|
|
70
|
+
await host.publishSessionState(sessionId, reset.mode, freshSession.configOptions);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
host.logError("Failed to publish clear-context session state:", error);
|
|
74
|
+
}
|
|
75
|
+
assertRestartActive(freshSession);
|
|
76
|
+
freshSession.input.push(host.continuationMessage(sessionId, reset.plan, turn.promptUuid));
|
|
77
|
+
assertRestartActive(freshSession);
|
|
78
|
+
host.ensureConsumer(freshSession, sessionId);
|
|
79
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { SessionNotification } from "@agentclientprotocol/sdk";
|
|
2
|
+
import type { SDKResultMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
3
|
+
import { type ClearContextCoordinatorHost, type ClearContextReset, type ClearContextSession, type ClearContextTurn } from "./clear-context-coordinator.js";
|
|
4
|
+
export declare function acceptedPlanToolResult(notification: SessionNotification, toolUseId: string | undefined): SessionNotification;
|
|
5
|
+
type ExitPlanState = {
|
|
6
|
+
toolUseCache: Record<string, {
|
|
7
|
+
name: string;
|
|
8
|
+
} | undefined>;
|
|
9
|
+
pendingExitPlanModeInterruption?: {
|
|
10
|
+
toolUseId: string;
|
|
11
|
+
toolResultSeen: boolean;
|
|
12
|
+
};
|
|
13
|
+
pendingExitPlanContextReset?: ClearContextReset;
|
|
14
|
+
};
|
|
15
|
+
/** Reconcile an SDK user message with the two pending ExitPlanMode lanes and
|
|
16
|
+
* return the accepted plan tool id whose rendered update must be completed. */
|
|
17
|
+
export declare function observeExitPlanToolResults(message: {
|
|
18
|
+
type: string;
|
|
19
|
+
tool_result_meta?: unknown;
|
|
20
|
+
}, content: unknown, state: ExitPlanState): string | undefined;
|
|
21
|
+
export declare function executionDiagnostic(message: SDKResultMessage): string | undefined;
|
|
22
|
+
/** Claude wraps a rejected ExitPlanMode explanation in a Markdown code fence.
|
|
23
|
+
* Strip exactly one complete outer fence for that tool only. */
|
|
24
|
+
export declare function exitPlanModeRawOutput(toolName: string, content: unknown): unknown;
|
|
25
|
+
export type ExitPlanRestartHost<Session extends ClearContextSession<Turn>, Turn extends ClearContextTurn> = Omit<ClearContextCoordinatorHost<Session, Turn>, "publishSessionState" | "continuationMessage"> & {
|
|
26
|
+
sessionUpdate(notification: SessionNotification): Promise<void>;
|
|
27
|
+
destroyReplacement(sessionId: string, session: Session): void;
|
|
28
|
+
settleCancelledTurn(oldSession: Session, turnSession: Session, turn: Turn): void;
|
|
29
|
+
settleFailedTurn(turnSession: Session, turn: Turn, error: unknown): void;
|
|
30
|
+
};
|
|
31
|
+
/** Owns the lifetime of accepted-plan context replacements. In particular, a
|
|
32
|
+
* session cancellation invalidates an in-progress async restart so a late
|
|
33
|
+
* restartSession result cannot recreate a closed public session. */
|
|
34
|
+
export declare class ExitPlanCoordinator<Session extends ClearContextSession<Turn>, Turn extends ClearContextTurn> {
|
|
35
|
+
private readonly host;
|
|
36
|
+
private readonly restarts;
|
|
37
|
+
constructor(host: ExitPlanRestartHost<Session, Turn>);
|
|
38
|
+
cancel(sessionId: string): void;
|
|
39
|
+
restart(sessionId: string, oldSession: Session, reset: ClearContextReset): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=exit-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exit-plan.d.ts","sourceRoot":"","sources":["../src/exit-plan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EAEtB,MAAM,gCAAgC,CAAC;AAGxC,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,mBAAmB,EACjC,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,mBAAmB,CAarB;AAeD,KAAK,aAAa,GAAG;IACnB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;IAC3D,+BAA+B,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAC;IACjF,2BAA2B,CAAC,EAAE,iBAAiB,CAAC;CACjD,CAAC;AA0BF;+EAC+E;AAC/E,wBAAgB,0BAA0B,CACxC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAE,EACrD,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,aAAa,GACnB,MAAM,GAAG,SAAS,CA0BpB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAKjF;AAED;gEACgE;AAChE,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAMjF;AAED,MAAM,MAAM,mBAAmB,CAC7B,OAAO,SAAS,mBAAmB,CAAC,IAAI,CAAC,EACzC,IAAI,SAAS,gBAAgB,IAC3B,IAAI,CACN,2BAA2B,CAAC,OAAO,EAAE,IAAI,CAAC,EAC1C,qBAAqB,GAAG,qBAAqB,CAC9C,GAAG;IACF,aAAa,CAAC,YAAY,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9D,mBAAmB,CAAC,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IACjF,gBAAgB,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CAC1E,CAAC;AAEF;;oEAEoE;AACpE,qBAAa,mBAAmB,CAC9B,OAAO,SAAS,mBAAmB,CAAC,IAAI,CAAC,EACzC,IAAI,SAAS,gBAAgB;IAIjB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;gBAElC,IAAI,EAAE,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC;IAErE,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIzB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;CAiE/F"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { continuePlanInFreshContext, } from "./clear-context-coordinator.js";
|
|
2
|
+
import { parseToolResultMeta } from "./tool-result-meta.js";
|
|
3
|
+
export function acceptedPlanToolResult(notification, toolUseId) {
|
|
4
|
+
const update = notification.update;
|
|
5
|
+
if (!toolUseId ||
|
|
6
|
+
update.sessionUpdate !== "tool_call_update" ||
|
|
7
|
+
update.toolCallId !== toolUseId) {
|
|
8
|
+
return notification;
|
|
9
|
+
}
|
|
10
|
+
const completed = { ...update };
|
|
11
|
+
delete completed.rawOutput;
|
|
12
|
+
delete completed.content;
|
|
13
|
+
return { ...notification, update: { ...completed, status: "completed" } };
|
|
14
|
+
}
|
|
15
|
+
function containsToolResultFor(content, toolUseId) {
|
|
16
|
+
return (Array.isArray(content) &&
|
|
17
|
+
content.some((block) => typeof block === "object" &&
|
|
18
|
+
block !== null &&
|
|
19
|
+
block.type === "tool_result" &&
|
|
20
|
+
block.tool_use_id === toolUseId));
|
|
21
|
+
}
|
|
22
|
+
function rejectedExitPlanToolUseId(content, toolUseCache, rawToolResultMeta) {
|
|
23
|
+
if (!Array.isArray(content))
|
|
24
|
+
return undefined;
|
|
25
|
+
const toolResultMeta = parseToolResultMeta(rawToolResultMeta);
|
|
26
|
+
if (!toolResultMeta)
|
|
27
|
+
return undefined;
|
|
28
|
+
for (const block of content) {
|
|
29
|
+
if (typeof block !== "object" || block === null)
|
|
30
|
+
continue;
|
|
31
|
+
const { type, tool_use_id: toolUseId, is_error: isError } = block;
|
|
32
|
+
if (type === "tool_result" &&
|
|
33
|
+
typeof toolUseId === "string" &&
|
|
34
|
+
isError === true &&
|
|
35
|
+
toolResultMeta.get(toolUseId)?.nonExecutionKind === "user-rejected" &&
|
|
36
|
+
toolUseCache[toolUseId]?.name === "ExitPlanMode") {
|
|
37
|
+
return toolUseId;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
/** Reconcile an SDK user message with the two pending ExitPlanMode lanes and
|
|
43
|
+
* return the accepted plan tool id whose rendered update must be completed. */
|
|
44
|
+
export function observeExitPlanToolResults(message, content, state) {
|
|
45
|
+
if (message.type !== "user")
|
|
46
|
+
return undefined;
|
|
47
|
+
const rejectedToolUseId = rejectedExitPlanToolUseId(content, state.toolUseCache, message.tool_result_meta);
|
|
48
|
+
if (rejectedToolUseId) {
|
|
49
|
+
// The stream is authoritative: resumed queries can lose the short-lived
|
|
50
|
+
// marker installed by canUseTool, while metadata preserves correlation.
|
|
51
|
+
state.pendingExitPlanModeInterruption = {
|
|
52
|
+
toolUseId: rejectedToolUseId,
|
|
53
|
+
toolResultSeen: true,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const pendingInterruption = state.pendingExitPlanModeInterruption;
|
|
57
|
+
if (pendingInterruption && containsToolResultFor(content, pendingInterruption.toolUseId)) {
|
|
58
|
+
pendingInterruption.toolResultSeen = true;
|
|
59
|
+
}
|
|
60
|
+
const pendingReset = state.pendingExitPlanContextReset;
|
|
61
|
+
return pendingReset && containsToolResultFor(content, pendingReset.toolUseId)
|
|
62
|
+
? pendingReset.toolUseId
|
|
63
|
+
: undefined;
|
|
64
|
+
}
|
|
65
|
+
export function executionDiagnostic(message) {
|
|
66
|
+
if (message.subtype === "success") {
|
|
67
|
+
return message.result.startsWith("[ede_diagnostic]") ? message.result : undefined;
|
|
68
|
+
}
|
|
69
|
+
return message.errors.find((error) => error.startsWith("[ede_diagnostic]"));
|
|
70
|
+
}
|
|
71
|
+
/** Claude wraps a rejected ExitPlanMode explanation in a Markdown code fence.
|
|
72
|
+
* Strip exactly one complete outer fence for that tool only. */
|
|
73
|
+
export function exitPlanModeRawOutput(toolName, content) {
|
|
74
|
+
if (toolName !== "ExitPlanMode" || typeof content !== "string") {
|
|
75
|
+
return content;
|
|
76
|
+
}
|
|
77
|
+
const fenced = /^\s*```[^\r\n]*\r?\n([\s\S]*?)\r?\n```\s*$/.exec(content);
|
|
78
|
+
return fenced?.[1] ?? content;
|
|
79
|
+
}
|
|
80
|
+
/** Owns the lifetime of accepted-plan context replacements. In particular, a
|
|
81
|
+
* session cancellation invalidates an in-progress async restart so a late
|
|
82
|
+
* restartSession result cannot recreate a closed public session. */
|
|
83
|
+
export class ExitPlanCoordinator {
|
|
84
|
+
host;
|
|
85
|
+
restarts = new Map();
|
|
86
|
+
constructor(host) {
|
|
87
|
+
this.host = host;
|
|
88
|
+
}
|
|
89
|
+
cancel(sessionId) {
|
|
90
|
+
this.restarts.get(sessionId)?.abort();
|
|
91
|
+
}
|
|
92
|
+
async restart(sessionId, oldSession, reset) {
|
|
93
|
+
this.cancel(sessionId);
|
|
94
|
+
const controller = new AbortController();
|
|
95
|
+
this.restarts.set(sessionId, controller);
|
|
96
|
+
try {
|
|
97
|
+
const clearContextHost = {
|
|
98
|
+
...this.host,
|
|
99
|
+
publishSessionState: async (id, mode, configOptions) => {
|
|
100
|
+
await this.host.sessionUpdate({
|
|
101
|
+
sessionId: id,
|
|
102
|
+
update: { sessionUpdate: "current_mode_update", currentModeId: mode },
|
|
103
|
+
});
|
|
104
|
+
await this.host.sessionUpdate({
|
|
105
|
+
sessionId: id,
|
|
106
|
+
update: { sessionUpdate: "config_option_update", configOptions },
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
continuationMessage: (id, plan, promptUuid) => ({
|
|
110
|
+
type: "user",
|
|
111
|
+
message: {
|
|
112
|
+
role: "user",
|
|
113
|
+
content: [{ type: "text", text: `Implement the following plan:\n\n${plan}` }],
|
|
114
|
+
},
|
|
115
|
+
session_id: id,
|
|
116
|
+
parent_tool_use_id: null,
|
|
117
|
+
origin: { kind: "human" },
|
|
118
|
+
uuid: promptUuid,
|
|
119
|
+
}),
|
|
120
|
+
};
|
|
121
|
+
await continuePlanInFreshContext(sessionId, oldSession, reset, clearContextHost, controller.signal);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
const currentSession = this.host.currentSession(sessionId);
|
|
125
|
+
const replacement = currentSession !== oldSession ? currentSession : undefined;
|
|
126
|
+
if (replacement)
|
|
127
|
+
this.host.destroyReplacement(sessionId, replacement);
|
|
128
|
+
const turn = replacement?.activeTurn ?? oldSession.activeTurn;
|
|
129
|
+
if (turn && !turn.settled) {
|
|
130
|
+
const turnSession = replacement?.activeTurn === turn ? replacement : oldSession;
|
|
131
|
+
if (controller.signal.aborted) {
|
|
132
|
+
this.host.settleCancelledTurn(oldSession, turnSession, turn);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// A provider/session-creation failure is turn-scoped. Settling it
|
|
136
|
+
// here keeps it out of the query consumer's transport-loss catch.
|
|
137
|
+
this.host.settleFailedTurn(turnSession, turn, error);
|
|
138
|
+
}
|
|
139
|
+
oldSession.activeTurn = null;
|
|
140
|
+
oldSession.turnQueue = (oldSession.turnQueue ?? []).filter((queued) => queued !== turn);
|
|
141
|
+
if (replacement) {
|
|
142
|
+
replacement.activeTurn = null;
|
|
143
|
+
replacement.turnQueue = (replacement.turnQueue ?? []).filter((queued) => queued !== turn);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
oldSession.pendingExitPlanContextReset = undefined;
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
if (this.restarts.get(sessionId) === controller) {
|
|
150
|
+
this.restarts.delete(sessionId);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -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"}
|