-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathprovider.ts
More file actions
140 lines (127 loc) · 5.78 KB
/
Copy pathprovider.ts
File metadata and controls
140 lines (127 loc) · 5.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/**
* Provider abstraction for AI agent tools.
*
* Only HookProvider ships today (Claude Code). Transcript-polling and push-based
* provider types will be added when a real second provider (Codex, Goose,
* Discord, etc.) actually lands, derived from that provider's needs rather than
* speculation.
*/
import type { TeamProvider } from './teamProvider.js';
// ── Normalized Events (all provider types produce these) ──────
export type AgentEvent =
| {
kind: 'toolStart';
toolId: string;
toolName: string;
input?: unknown;
/** True when the tool was spawned to run in the background (e.g. Claude's
* `run_in_background` on Agent/Task). Handlers use this to suppress ghost
* sub-agent characters for teammate spawns. */
runInBackground?: boolean;
}
| { kind: 'toolEnd'; toolId: string }
| {
kind: 'turnEnd';
/** True when the turn ended because the agent went idle waiting on the
* user (Claude's Notification(idle_prompt)) rather than simply finishing
* its response (Stop). Drives the "Waiting for input" vs "Done" label.
* Absent/false = the agent finished its turn (Done). */
awaitingInput?: boolean;
}
| {
kind: 'subagentStart';
parentToolId: string;
toolId: string;
toolName: string;
input?: unknown;
runInBackground?: boolean;
}
| { kind: 'subagentEnd'; parentToolId: string; toolId: string }
| {
kind: 'subagentTurnEnd';
parentToolId: string;
/** 'idle' = subagent is idle and ready for more work; 'completed' = subagent
* reported its task done. Some providers emit only one; both route to the
* same handler but with different downstream cleanup. */
reason: 'idle' | 'completed';
}
| { kind: 'progress'; toolId: string; data: unknown }
| { kind: 'permissionRequest' }
| {
kind: 'sessionStart';
source?: string;
/** For external-session adoption: path to the session's transcript file
* (if the provider uses one). Undefined for providers without transcripts. */
transcriptPath?: string;
/** Working directory the session was started in. Used to match pending
* external sessions against known workspace folders. */
cwd?: string;
/** Character palette index (0-5). Used by the webview to pick a sprite. */
palette?: number;
}
| { kind: 'sessionEnd'; reason?: string };
// ── Hook-based Provider (CLIs with hooks APIs) ────────────────
export interface HookProvider {
readonly kind: 'hook';
readonly id: string;
readonly displayName: string;
/** Protocol version. Server refuses to dispatch events from a provider whose
* version it doesn't understand. Bump on every breaking change to AgentEvent
* / TeamProvider / HookProvider. Start at 1. */
readonly protocolVersion: number;
/** Normalize a raw hook event payload into an AgentEvent.
* Each CLI sends different JSON (Claude: snake_case, Copilot: camelCase, etc.)
* The provider translates to the common AgentEvent format.
* Return null for events we should ignore. */
normalizeHookEvent(raw: Record<string, unknown>): {
sessionId: string;
event: AgentEvent;
} | null;
/** Install hook scripts that POST to our server. */
installHooks(serverUrl: string, authToken: string): Promise<void>;
/** Remove installed hook scripts. */
uninstallHooks(): Promise<void>;
/** Check if hooks are currently installed. */
areHooksInstalled(): Promise<boolean>;
/** Format tool status for display (e.g., "Read" -> "Reading foo.ts") */
formatToolStatus(toolName: string, input?: unknown): string;
/** Tools that don't trigger permission timers */
readonly permissionExemptTools: ReadonlySet<string>;
/** Tools that spawn sub-agent characters */
readonly subagentToolNames: ReadonlySet<string>;
/** Tools that should show the "reading" character animation instead of "typing".
* The provider classifies tools as read-like or write-like; the webview renders
* the animation. Allows new providers to override without webview edits. */
readonly readingTools: ReadonlySet<string>;
/** Terminal name prefix used when launching this CLI. Used by the extension to
* match VS Code terminals to agents for heuristic adoption. */
readonly terminalNamePrefix?: string;
// ── Optional file fallback (heuristic mode) ──
/** Session directories to scan. Undefined = no file fallback. */
getSessionDirs?(workspacePath: string): string[];
/** Root directories containing every session this provider may have started
* (across all workspaces). Used by global session discovery / "Watch All
* Sessions". Each returned dir contains subdirs whose entries are session
* transcript files. Undefined = this provider doesn't support global scan. */
getAllSessionRoots?(): string[];
/** Glob pattern for session files (e.g., '*.jsonl'). */
readonly sessionFilePattern?: string;
/** Parse one line of a transcript file into an AgentEvent. */
parseTranscriptLine?(line: string): AgentEvent | null;
/** Build CLI launch command for +Agent button. */
buildLaunchCommand?(
sessionId: string,
cwd: string,
opts?: { bypassPermissions?: boolean },
): {
command: string;
args: string[];
env?: Record<string, string>;
};
// ── Optional team/subagent extension (Agent Teams on Claude; empty for single-agent CLIs) ──
/** Optional reference to a TeamProvider. When set, the hook handler registers team-aware
* branches (subagent routing, teammate discovery, permission forwarding, etc.). */
readonly team?: TeamProvider;
}
// TODO(provider type taxonomy): FileProvider (polling-only CLIs) and StreamProvider
// (push-based external services) will be added alongside the first real second provider