@finityno/claude-code-acp 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +222 -0
- package/README.md +53 -0
- package/dist/acp-agent.d.ts +105 -0
- package/dist/acp-agent.d.ts.map +1 -0
- package/dist/acp-agent.js +1009 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/lib.d.ts +8 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +8 -0
- package/dist/mcp-server.d.ts +21 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +782 -0
- package/dist/settings.d.ts +123 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +422 -0
- package/dist/subagent-tracker.d.ts +193 -0
- package/dist/subagent-tracker.d.ts.map +1 -0
- package/dist/subagent-tracker.js +292 -0
- package/dist/tools.d.ts +50 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +555 -0
- package/dist/utils.d.ts +32 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +150 -0
- package/package.json +82 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { AgentSideConnection } from "@agentclientprotocol/sdk";
|
|
2
|
+
import { Logger } from "./acp-agent.js";
|
|
3
|
+
/**
|
|
4
|
+
* Subagent status lifecycle
|
|
5
|
+
*/
|
|
6
|
+
export type SubagentStatus = "pending" | "running" | "completed" | "failed" | "cancelled";
|
|
7
|
+
/**
|
|
8
|
+
* Subagent type from Claude Code Task tool
|
|
9
|
+
*/
|
|
10
|
+
export type SubagentType = "Bash" | "general-purpose" | "statusline-setup" | "Explore" | "Plan" | "claude-code-guide" | string;
|
|
11
|
+
/**
|
|
12
|
+
* Tracked subagent information
|
|
13
|
+
*/
|
|
14
|
+
export interface TrackedSubagent {
|
|
15
|
+
/** Unique ID for this subagent (same as tool_use_id) */
|
|
16
|
+
id: string;
|
|
17
|
+
/** Parent session ID where this subagent was spawned */
|
|
18
|
+
parentSessionId: string;
|
|
19
|
+
/** Parent tool use ID if this is a nested subagent */
|
|
20
|
+
parentToolUseId?: string;
|
|
21
|
+
/** Type of subagent */
|
|
22
|
+
subagentType: SubagentType;
|
|
23
|
+
/** Short description of what the subagent is doing */
|
|
24
|
+
description: string;
|
|
25
|
+
/** Full prompt given to the subagent */
|
|
26
|
+
prompt: string;
|
|
27
|
+
/** Model used for this subagent (if specified) */
|
|
28
|
+
model?: "sonnet" | "opus" | "haiku";
|
|
29
|
+
/** Current status */
|
|
30
|
+
status: SubagentStatus;
|
|
31
|
+
/** Timestamp when subagent was created */
|
|
32
|
+
createdAt: number;
|
|
33
|
+
/** Timestamp when subagent started running */
|
|
34
|
+
startedAt?: number;
|
|
35
|
+
/** Timestamp when subagent completed */
|
|
36
|
+
completedAt?: number;
|
|
37
|
+
/** Result from the subagent (if completed) */
|
|
38
|
+
result?: unknown;
|
|
39
|
+
/** Error message (if failed) */
|
|
40
|
+
error?: string;
|
|
41
|
+
/** Whether this subagent is running in background */
|
|
42
|
+
runInBackground: boolean;
|
|
43
|
+
/** Maximum turns allowed */
|
|
44
|
+
maxTurns?: number;
|
|
45
|
+
/** Agent ID returned by Claude Code (for resume capability) */
|
|
46
|
+
agentId?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Subagent event types for notifications
|
|
50
|
+
*/
|
|
51
|
+
export type SubagentEventType = "subagent_started" | "subagent_progress" | "subagent_completed" | "subagent_failed" | "subagent_cancelled";
|
|
52
|
+
/**
|
|
53
|
+
* Metadata for subagent-related notifications
|
|
54
|
+
*/
|
|
55
|
+
export interface SubagentUpdateMeta {
|
|
56
|
+
claudeCode?: {
|
|
57
|
+
subagent: {
|
|
58
|
+
id: string;
|
|
59
|
+
eventType: SubagentEventType;
|
|
60
|
+
subagentType: SubagentType;
|
|
61
|
+
description: string;
|
|
62
|
+
status: SubagentStatus;
|
|
63
|
+
parentSessionId: string;
|
|
64
|
+
parentToolUseId?: string;
|
|
65
|
+
model?: string;
|
|
66
|
+
runInBackground: boolean;
|
|
67
|
+
agentId?: string;
|
|
68
|
+
/** Duration in milliseconds (for completed/failed events) */
|
|
69
|
+
durationMs?: number;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Input structure for the Task tool
|
|
75
|
+
*/
|
|
76
|
+
export interface TaskToolInput {
|
|
77
|
+
description: string;
|
|
78
|
+
prompt: string;
|
|
79
|
+
subagent_type: SubagentType;
|
|
80
|
+
model?: "sonnet" | "opus" | "haiku";
|
|
81
|
+
max_turns?: number;
|
|
82
|
+
run_in_background?: boolean;
|
|
83
|
+
resume?: string;
|
|
84
|
+
}
|
|
85
|
+
export type SubagentEventListener = (subagent: TrackedSubagent, data?: unknown) => void | Promise<void>;
|
|
86
|
+
export interface SubagentStats {
|
|
87
|
+
total: number;
|
|
88
|
+
pending: number;
|
|
89
|
+
running: number;
|
|
90
|
+
completed: number;
|
|
91
|
+
failed: number;
|
|
92
|
+
cancelled: number;
|
|
93
|
+
byType: Record<string, number>;
|
|
94
|
+
averageDurationMs?: number;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* SubagentTracker manages the lifecycle of all subagents spawned via the Task tool
|
|
98
|
+
*/
|
|
99
|
+
export declare class SubagentTracker {
|
|
100
|
+
/** Map of subagent ID to tracked subagent data */
|
|
101
|
+
private subagents;
|
|
102
|
+
/** Map of session ID to subagent IDs in that session */
|
|
103
|
+
private sessionSubagents;
|
|
104
|
+
/** Event listeners for subagent lifecycle events */
|
|
105
|
+
private listeners;
|
|
106
|
+
private client;
|
|
107
|
+
private logger;
|
|
108
|
+
constructor(client?: AgentSideConnection | null, logger?: Logger);
|
|
109
|
+
/**
|
|
110
|
+
* Track a new subagent when Task tool is called
|
|
111
|
+
*/
|
|
112
|
+
trackSubagent(toolUseId: string, sessionId: string, input: TaskToolInput, parentToolUseId?: string): TrackedSubagent;
|
|
113
|
+
/**
|
|
114
|
+
* Mark a subagent as started/running
|
|
115
|
+
*/
|
|
116
|
+
startSubagent(toolUseId: string): Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Mark a subagent as completed successfully
|
|
119
|
+
*/
|
|
120
|
+
completeSubagent(toolUseId: string, result?: unknown, agentId?: string): Promise<void>;
|
|
121
|
+
/**
|
|
122
|
+
* Mark a subagent as failed
|
|
123
|
+
*/
|
|
124
|
+
failSubagent(toolUseId: string, error: string): Promise<void>;
|
|
125
|
+
/**
|
|
126
|
+
* Mark a subagent as cancelled
|
|
127
|
+
*/
|
|
128
|
+
cancelSubagent(toolUseId: string): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Send progress update for a running subagent
|
|
131
|
+
*/
|
|
132
|
+
updateProgress(toolUseId: string, progressData?: unknown): Promise<void>;
|
|
133
|
+
/**
|
|
134
|
+
* Get a tracked subagent by ID
|
|
135
|
+
*/
|
|
136
|
+
getSubagent(toolUseId: string): TrackedSubagent | undefined;
|
|
137
|
+
/**
|
|
138
|
+
* Get all subagents for a session
|
|
139
|
+
*/
|
|
140
|
+
getSessionSubagents(sessionId: string): TrackedSubagent[];
|
|
141
|
+
/**
|
|
142
|
+
* Get all currently running subagents
|
|
143
|
+
*/
|
|
144
|
+
getRunningSubagents(): TrackedSubagent[];
|
|
145
|
+
/**
|
|
146
|
+
* Get all subagents (for debugging/monitoring)
|
|
147
|
+
*/
|
|
148
|
+
getAllSubagents(): TrackedSubagent[];
|
|
149
|
+
/**
|
|
150
|
+
* Check if a tool use ID is a Task tool (subagent)
|
|
151
|
+
*/
|
|
152
|
+
isSubagent(toolUseId: string): boolean;
|
|
153
|
+
/**
|
|
154
|
+
* Add event listener for subagent lifecycle events
|
|
155
|
+
*/
|
|
156
|
+
addEventListener(event: SubagentEventType, listener: SubagentEventListener): void;
|
|
157
|
+
/**
|
|
158
|
+
* Remove event listener
|
|
159
|
+
*/
|
|
160
|
+
removeEventListener(event: SubagentEventType, listener: SubagentEventListener): void;
|
|
161
|
+
/**
|
|
162
|
+
* Clean up completed subagents older than given age (in ms)
|
|
163
|
+
*/
|
|
164
|
+
cleanup(maxAgeMs?: number): number;
|
|
165
|
+
/**
|
|
166
|
+
* Get statistics about subagents
|
|
167
|
+
*/
|
|
168
|
+
getStats(): SubagentStats;
|
|
169
|
+
/**
|
|
170
|
+
* Clear all tracked subagents (useful for testing)
|
|
171
|
+
*/
|
|
172
|
+
clear(): void;
|
|
173
|
+
private emitEvent;
|
|
174
|
+
private sendSubagentNotification;
|
|
175
|
+
private getDuration;
|
|
176
|
+
private countByType;
|
|
177
|
+
private calculateAverageDuration;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Utility to check if a tool input is for the Task tool
|
|
181
|
+
*/
|
|
182
|
+
export declare function isTaskToolInput(input: unknown): input is TaskToolInput;
|
|
183
|
+
/**
|
|
184
|
+
* Extract subagent metadata from Task tool input
|
|
185
|
+
*/
|
|
186
|
+
export declare function extractSubagentMeta(input: TaskToolInput): {
|
|
187
|
+
description: string;
|
|
188
|
+
subagentType: string;
|
|
189
|
+
model?: string;
|
|
190
|
+
runInBackground: boolean;
|
|
191
|
+
maxTurns?: number;
|
|
192
|
+
};
|
|
193
|
+
//# sourceMappingURL=subagent-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subagent-tracker.d.ts","sourceRoot":"","sources":["../src/subagent-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SubagentTracker manages the lifecycle of all subagents spawned via the Task tool
|
|
3
|
+
*/
|
|
4
|
+
export class SubagentTracker {
|
|
5
|
+
constructor(client = null, logger = console) {
|
|
6
|
+
/** Map of subagent ID to tracked subagent data */
|
|
7
|
+
this.subagents = new Map();
|
|
8
|
+
/** Map of session ID to subagent IDs in that session */
|
|
9
|
+
this.sessionSubagents = new Map();
|
|
10
|
+
/** Event listeners for subagent lifecycle events */
|
|
11
|
+
this.listeners = new Map();
|
|
12
|
+
this.client = client;
|
|
13
|
+
this.logger = logger;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Track a new subagent when Task tool is called
|
|
17
|
+
*/
|
|
18
|
+
trackSubagent(toolUseId, sessionId, input, parentToolUseId) {
|
|
19
|
+
const subagent = {
|
|
20
|
+
id: toolUseId,
|
|
21
|
+
parentSessionId: sessionId,
|
|
22
|
+
parentToolUseId,
|
|
23
|
+
subagentType: input.subagent_type,
|
|
24
|
+
description: input.description,
|
|
25
|
+
prompt: input.prompt,
|
|
26
|
+
model: input.model,
|
|
27
|
+
status: "pending",
|
|
28
|
+
createdAt: Date.now(),
|
|
29
|
+
runInBackground: input.run_in_background ?? false,
|
|
30
|
+
maxTurns: input.max_turns,
|
|
31
|
+
};
|
|
32
|
+
this.subagents.set(toolUseId, subagent);
|
|
33
|
+
// Track by session
|
|
34
|
+
if (!this.sessionSubagents.has(sessionId)) {
|
|
35
|
+
this.sessionSubagents.set(sessionId, new Set());
|
|
36
|
+
}
|
|
37
|
+
this.sessionSubagents.get(sessionId).add(toolUseId);
|
|
38
|
+
this.logger.log(`[SubagentTracker] Tracked new subagent: ${toolUseId} (${input.subagent_type}: ${input.description})`);
|
|
39
|
+
return subagent;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Mark a subagent as started/running
|
|
43
|
+
*/
|
|
44
|
+
async startSubagent(toolUseId) {
|
|
45
|
+
const subagent = this.subagents.get(toolUseId);
|
|
46
|
+
if (!subagent) {
|
|
47
|
+
this.logger.error(`[SubagentTracker] Cannot start unknown subagent: ${toolUseId}`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
subagent.status = "running";
|
|
51
|
+
subagent.startedAt = Date.now();
|
|
52
|
+
await this.emitEvent("subagent_started", subagent);
|
|
53
|
+
await this.sendSubagentNotification(subagent, "subagent_started");
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Mark a subagent as completed successfully
|
|
57
|
+
*/
|
|
58
|
+
async completeSubagent(toolUseId, result, agentId) {
|
|
59
|
+
const subagent = this.subagents.get(toolUseId);
|
|
60
|
+
if (!subagent) {
|
|
61
|
+
this.logger.error(`[SubagentTracker] Cannot complete unknown subagent: ${toolUseId}`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
subagent.status = "completed";
|
|
65
|
+
subagent.completedAt = Date.now();
|
|
66
|
+
subagent.result = result;
|
|
67
|
+
if (agentId) {
|
|
68
|
+
subagent.agentId = agentId;
|
|
69
|
+
}
|
|
70
|
+
await this.emitEvent("subagent_completed", subagent);
|
|
71
|
+
await this.sendSubagentNotification(subagent, "subagent_completed");
|
|
72
|
+
this.logger.log(`[SubagentTracker] Subagent completed: ${toolUseId} (duration: ${this.getDuration(subagent)}ms)`);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Mark a subagent as failed
|
|
76
|
+
*/
|
|
77
|
+
async failSubagent(toolUseId, error) {
|
|
78
|
+
const subagent = this.subagents.get(toolUseId);
|
|
79
|
+
if (!subagent) {
|
|
80
|
+
this.logger.error(`[SubagentTracker] Cannot fail unknown subagent: ${toolUseId}`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
subagent.status = "failed";
|
|
84
|
+
subagent.completedAt = Date.now();
|
|
85
|
+
subagent.error = error;
|
|
86
|
+
await this.emitEvent("subagent_failed", subagent);
|
|
87
|
+
await this.sendSubagentNotification(subagent, "subagent_failed");
|
|
88
|
+
this.logger.error(`[SubagentTracker] Subagent failed: ${toolUseId} - ${error}`);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Mark a subagent as cancelled
|
|
92
|
+
*/
|
|
93
|
+
async cancelSubagent(toolUseId) {
|
|
94
|
+
const subagent = this.subagents.get(toolUseId);
|
|
95
|
+
if (!subagent) {
|
|
96
|
+
return; // Silent fail for cancel - may not be tracked
|
|
97
|
+
}
|
|
98
|
+
subagent.status = "cancelled";
|
|
99
|
+
subagent.completedAt = Date.now();
|
|
100
|
+
await this.emitEvent("subagent_cancelled", subagent);
|
|
101
|
+
await this.sendSubagentNotification(subagent, "subagent_cancelled");
|
|
102
|
+
this.logger.log(`[SubagentTracker] Subagent cancelled: ${toolUseId}`);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Send progress update for a running subagent
|
|
106
|
+
*/
|
|
107
|
+
async updateProgress(toolUseId, progressData) {
|
|
108
|
+
const subagent = this.subagents.get(toolUseId);
|
|
109
|
+
if (!subagent || subagent.status !== "running") {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
await this.emitEvent("subagent_progress", subagent, progressData);
|
|
113
|
+
await this.sendSubagentNotification(subagent, "subagent_progress");
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get a tracked subagent by ID
|
|
117
|
+
*/
|
|
118
|
+
getSubagent(toolUseId) {
|
|
119
|
+
return this.subagents.get(toolUseId);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get all subagents for a session
|
|
123
|
+
*/
|
|
124
|
+
getSessionSubagents(sessionId) {
|
|
125
|
+
const ids = this.sessionSubagents.get(sessionId);
|
|
126
|
+
if (!ids)
|
|
127
|
+
return [];
|
|
128
|
+
return Array.from(ids)
|
|
129
|
+
.map((id) => this.subagents.get(id))
|
|
130
|
+
.filter((s) => s !== undefined);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get all currently running subagents
|
|
134
|
+
*/
|
|
135
|
+
getRunningSubagents() {
|
|
136
|
+
return Array.from(this.subagents.values()).filter((s) => s.status === "running");
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get all subagents (for debugging/monitoring)
|
|
140
|
+
*/
|
|
141
|
+
getAllSubagents() {
|
|
142
|
+
return Array.from(this.subagents.values());
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Check if a tool use ID is a Task tool (subagent)
|
|
146
|
+
*/
|
|
147
|
+
isSubagent(toolUseId) {
|
|
148
|
+
return this.subagents.has(toolUseId);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Add event listener for subagent lifecycle events
|
|
152
|
+
*/
|
|
153
|
+
addEventListener(event, listener) {
|
|
154
|
+
if (!this.listeners.has(event)) {
|
|
155
|
+
this.listeners.set(event, new Set());
|
|
156
|
+
}
|
|
157
|
+
this.listeners.get(event).add(listener);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Remove event listener
|
|
161
|
+
*/
|
|
162
|
+
removeEventListener(event, listener) {
|
|
163
|
+
this.listeners.get(event)?.delete(listener);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Clean up completed subagents older than given age (in ms)
|
|
167
|
+
*/
|
|
168
|
+
cleanup(maxAgeMs = 3600000) {
|
|
169
|
+
const now = Date.now();
|
|
170
|
+
let cleanedCount = 0;
|
|
171
|
+
for (const [id, subagent] of this.subagents) {
|
|
172
|
+
if ((subagent.status === "completed" ||
|
|
173
|
+
subagent.status === "failed" ||
|
|
174
|
+
subagent.status === "cancelled") &&
|
|
175
|
+
subagent.completedAt &&
|
|
176
|
+
now - subagent.completedAt > maxAgeMs) {
|
|
177
|
+
this.subagents.delete(id);
|
|
178
|
+
this.sessionSubagents.get(subagent.parentSessionId)?.delete(id);
|
|
179
|
+
cleanedCount++;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return cleanedCount;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get statistics about subagents
|
|
186
|
+
*/
|
|
187
|
+
getStats() {
|
|
188
|
+
const subagents = Array.from(this.subagents.values());
|
|
189
|
+
return {
|
|
190
|
+
total: subagents.length,
|
|
191
|
+
pending: subagents.filter((s) => s.status === "pending").length,
|
|
192
|
+
running: subagents.filter((s) => s.status === "running").length,
|
|
193
|
+
completed: subagents.filter((s) => s.status === "completed").length,
|
|
194
|
+
failed: subagents.filter((s) => s.status === "failed").length,
|
|
195
|
+
cancelled: subagents.filter((s) => s.status === "cancelled").length,
|
|
196
|
+
byType: this.countByType(subagents),
|
|
197
|
+
averageDurationMs: this.calculateAverageDuration(subagents),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Clear all tracked subagents (useful for testing)
|
|
202
|
+
*/
|
|
203
|
+
clear() {
|
|
204
|
+
this.subagents.clear();
|
|
205
|
+
this.sessionSubagents.clear();
|
|
206
|
+
}
|
|
207
|
+
// Private helper methods
|
|
208
|
+
async emitEvent(event, subagent, data) {
|
|
209
|
+
const listeners = this.listeners.get(event);
|
|
210
|
+
if (!listeners)
|
|
211
|
+
return;
|
|
212
|
+
for (const listener of listeners) {
|
|
213
|
+
try {
|
|
214
|
+
await listener(subagent, data);
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
this.logger.error(`[SubagentTracker] Error in event listener:`, err);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async sendSubagentNotification(subagent, eventType) {
|
|
222
|
+
if (!this.client)
|
|
223
|
+
return;
|
|
224
|
+
const notification = {
|
|
225
|
+
sessionId: subagent.parentSessionId,
|
|
226
|
+
update: {
|
|
227
|
+
sessionUpdate: "tool_call_update",
|
|
228
|
+
toolCallId: subagent.id,
|
|
229
|
+
_meta: {
|
|
230
|
+
claudeCode: {
|
|
231
|
+
subagent: {
|
|
232
|
+
id: subagent.id,
|
|
233
|
+
eventType,
|
|
234
|
+
subagentType: subagent.subagentType,
|
|
235
|
+
description: subagent.description,
|
|
236
|
+
status: subagent.status,
|
|
237
|
+
parentSessionId: subagent.parentSessionId,
|
|
238
|
+
parentToolUseId: subagent.parentToolUseId,
|
|
239
|
+
model: subagent.model,
|
|
240
|
+
runInBackground: subagent.runInBackground,
|
|
241
|
+
agentId: subagent.agentId,
|
|
242
|
+
durationMs: this.getDuration(subagent),
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
await this.client.sessionUpdate(notification);
|
|
249
|
+
}
|
|
250
|
+
getDuration(subagent) {
|
|
251
|
+
if (!subagent.startedAt)
|
|
252
|
+
return undefined;
|
|
253
|
+
const endTime = subagent.completedAt ?? Date.now();
|
|
254
|
+
return endTime - subagent.startedAt;
|
|
255
|
+
}
|
|
256
|
+
countByType(subagents) {
|
|
257
|
+
const counts = {};
|
|
258
|
+
for (const s of subagents) {
|
|
259
|
+
counts[s.subagentType] = (counts[s.subagentType] || 0) + 1;
|
|
260
|
+
}
|
|
261
|
+
return counts;
|
|
262
|
+
}
|
|
263
|
+
calculateAverageDuration(subagents) {
|
|
264
|
+
const completed = subagents.filter((s) => (s.status === "completed" || s.status === "failed") && s.startedAt && s.completedAt);
|
|
265
|
+
if (completed.length === 0)
|
|
266
|
+
return undefined;
|
|
267
|
+
const totalDuration = completed.reduce((sum, s) => sum + (s.completedAt - s.startedAt), 0);
|
|
268
|
+
return Math.round(totalDuration / completed.length);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Utility to check if a tool input is for the Task tool
|
|
273
|
+
*/
|
|
274
|
+
export function isTaskToolInput(input) {
|
|
275
|
+
return (typeof input === "object" &&
|
|
276
|
+
input !== null &&
|
|
277
|
+
"prompt" in input &&
|
|
278
|
+
"subagent_type" in input &&
|
|
279
|
+
"description" in input);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Extract subagent metadata from Task tool input
|
|
283
|
+
*/
|
|
284
|
+
export function extractSubagentMeta(input) {
|
|
285
|
+
return {
|
|
286
|
+
description: input.description,
|
|
287
|
+
subagentType: input.subagent_type,
|
|
288
|
+
model: input.model,
|
|
289
|
+
runInBackground: input.run_in_background ?? false,
|
|
290
|
+
maxTurns: input.max_turns,
|
|
291
|
+
};
|
|
292
|
+
}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { PlanEntry, ToolCallContent, ToolCallLocation, ToolKind } from "@agentclientprotocol/sdk";
|
|
2
|
+
import { ToolResultBlockParam, WebSearchToolResultBlockParam } from "@anthropic-ai/sdk/resources";
|
|
3
|
+
export declare const ACP_TOOL_NAME_PREFIX = "mcp__acp__";
|
|
4
|
+
export declare const acpToolNames: {
|
|
5
|
+
read: string;
|
|
6
|
+
edit: string;
|
|
7
|
+
write: string;
|
|
8
|
+
bash: string;
|
|
9
|
+
killShell: string;
|
|
10
|
+
bashOutput: string;
|
|
11
|
+
};
|
|
12
|
+
export declare const EDIT_TOOL_NAMES: string[];
|
|
13
|
+
import { BetaBashCodeExecutionToolResultBlockParam, BetaCodeExecutionToolResultBlockParam, BetaRequestMCPToolResultBlockParam, BetaTextEditorCodeExecutionToolResultBlockParam, BetaToolSearchToolResultBlockParam, BetaWebFetchToolResultBlockParam, BetaWebSearchToolResultBlockParam } from "@anthropic-ai/sdk/resources/beta.mjs";
|
|
14
|
+
import { HookCallback } from "@anthropic-ai/claude-agent-sdk";
|
|
15
|
+
import { Logger } from "./acp-agent.js";
|
|
16
|
+
import { SettingsManager } from "./settings.js";
|
|
17
|
+
interface ToolInfo {
|
|
18
|
+
title: string;
|
|
19
|
+
kind: ToolKind;
|
|
20
|
+
content: ToolCallContent[];
|
|
21
|
+
locations?: ToolCallLocation[];
|
|
22
|
+
}
|
|
23
|
+
interface ToolUpdate {
|
|
24
|
+
title?: string;
|
|
25
|
+
content?: ToolCallContent[];
|
|
26
|
+
locations?: ToolCallLocation[];
|
|
27
|
+
}
|
|
28
|
+
export declare function toolInfoFromToolUse(toolUse: any): ToolInfo;
|
|
29
|
+
export declare function toolUpdateFromToolResult(toolResult: ToolResultBlockParam | BetaWebSearchToolResultBlockParam | BetaWebFetchToolResultBlockParam | WebSearchToolResultBlockParam | BetaCodeExecutionToolResultBlockParam | BetaBashCodeExecutionToolResultBlockParam | BetaTextEditorCodeExecutionToolResultBlockParam | BetaRequestMCPToolResultBlockParam | BetaToolSearchToolResultBlockParam, toolUse: any | undefined): ToolUpdate;
|
|
30
|
+
export type ClaudePlanEntry = {
|
|
31
|
+
content: string;
|
|
32
|
+
status: "pending" | "in_progress" | "completed";
|
|
33
|
+
activeForm: string;
|
|
34
|
+
};
|
|
35
|
+
export declare function planEntries(input: {
|
|
36
|
+
todos: ClaudePlanEntry[];
|
|
37
|
+
}): PlanEntry[];
|
|
38
|
+
export declare function markdownEscape(text: string): string;
|
|
39
|
+
export declare const registerHookCallback: (toolUseID: string, { onPostToolUseHook, }: {
|
|
40
|
+
onPostToolUseHook?: (toolUseID: string, toolInput: unknown, toolResponse: unknown) => Promise<void>;
|
|
41
|
+
}) => void;
|
|
42
|
+
export declare const createPostToolUseHook: (logger?: Logger) => HookCallback;
|
|
43
|
+
/**
|
|
44
|
+
* Creates a PreToolUse hook that checks permissions using the SettingsManager.
|
|
45
|
+
* This runs before the SDK's built-in permission rules, allowing us to enforce
|
|
46
|
+
* our own permission settings for ACP-prefixed tools.
|
|
47
|
+
*/
|
|
48
|
+
export declare const createPreToolUseHook: (settingsManager: SettingsManager, logger?: Logger) => HookCallback;
|
|
49
|
+
export {};
|
|
50
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAGlG,OAAO,EAAE,oBAAoB,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAWlG,eAAO,MAAM,oBAAoB,eAAe,CAAC;AACjD,eAAO,MAAM,YAAY;;;;;;;CAOxB,CAAC;AAEF,eAAO,MAAM,eAAe,UAA0C,CAAC;AAEvE,OAAO,EACL,yCAAyC,EACzC,qCAAqC,EACrC,kCAAkC,EAClC,+CAA+C,EAC/C,kCAAkC,EAClC,gCAAgC,EAChC,iCAAiC,EAClC,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,UAAU,QAAQ;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAED,UAAU,UAAU;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,GAAG,GAAG,QAAQ,CA+V1D;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EACN,oBAAoB,GACpB,iCAAiC,GACjC,gCAAgC,GAChC,6BAA6B,GAC7B,qCAAqC,GACrC,yCAAyC,GACzC,+CAA+C,GAC/C,kCAAkC,GAClC,kCAAkC,EACtC,OAAO,EAAE,GAAG,GAAG,SAAS,GACvB,UAAU,CA4HZ;AAmCD,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;IAChD,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,eAAe,EAAE,CAAA;CAAE,GAAG,SAAS,EAAE,CAM5E;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQnD;AAcD,eAAO,MAAM,oBAAoB,GAC/B,WAAW,MAAM,EACjB,wBAEG;IACD,iBAAiB,CAAC,EAAE,CAClB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,EAClB,YAAY,EAAE,OAAO,KAClB,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,SAKF,CAAC;AAGF,eAAO,MAAM,qBAAqB,GAC/B,SAAQ,MAAgB,KAAG,YAa3B,CAAC;AAEJ;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC9B,iBAAiB,eAAe,EAAE,SAAQ,MAAgB,KAAG,YA2C7D,CAAC"}
|