cascade-ai 0.2.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 +676 -0
- package/bin/cascade.js +3 -0
- package/completions/cascade.bash +23 -0
- package/completions/cascade.fish +20 -0
- package/completions/cascade.zsh +32 -0
- package/dist/cli.cjs +10442 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +10395 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +7544 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1578 -0
- package/dist/index.d.ts +1578 -0
- package/dist/index.js +7452 -0
- package/dist/index.js.map +1 -0
- package/dist/keytar-F4YAPN53.node +0 -0
- package/package.json +135 -0
- package/web/dist/assets/index-DO_ICahS.css +1 -0
- package/web/dist/assets/index-qNI_hqt1.js +216 -0
- package/web/dist/assets/react-Cpp6qqoq.js +1 -0
- package/web/dist/assets/reactflow-B1e2RnXD.js +48 -0
- package/web/dist/assets/socketio-BcxXcwBL.js +1 -0
- package/web/dist/index.html +16 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1578 @@
|
|
|
1
|
+
import EventEmitter from 'node:events';
|
|
2
|
+
import { Server } from 'node:http';
|
|
3
|
+
|
|
4
|
+
type ProviderType = 'anthropic' | 'openai' | 'gemini' | 'azure' | 'openai-compatible' | 'ollama';
|
|
5
|
+
interface ModelInfo {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
provider: ProviderType;
|
|
9
|
+
contextWindow: number;
|
|
10
|
+
isVisionCapable: boolean;
|
|
11
|
+
inputCostPer1kTokens: number;
|
|
12
|
+
outputCostPer1kTokens: number;
|
|
13
|
+
maxOutputTokens: number;
|
|
14
|
+
supportsStreaming: boolean;
|
|
15
|
+
isLocal: boolean;
|
|
16
|
+
minSizeB?: number;
|
|
17
|
+
}
|
|
18
|
+
interface ProviderConfig {
|
|
19
|
+
type: ProviderType;
|
|
20
|
+
label?: string;
|
|
21
|
+
apiKey?: string;
|
|
22
|
+
baseUrl?: string;
|
|
23
|
+
deploymentName?: string;
|
|
24
|
+
apiVersion?: string;
|
|
25
|
+
model?: string;
|
|
26
|
+
}
|
|
27
|
+
interface StreamChunk {
|
|
28
|
+
text: string;
|
|
29
|
+
finishReason?: 'stop' | 'length' | 'tool_use' | null;
|
|
30
|
+
usage?: TokenUsage;
|
|
31
|
+
}
|
|
32
|
+
interface TokenUsage {
|
|
33
|
+
inputTokens: number;
|
|
34
|
+
outputTokens: number;
|
|
35
|
+
totalTokens: number;
|
|
36
|
+
estimatedCostUsd: number;
|
|
37
|
+
}
|
|
38
|
+
interface GenerateOptions {
|
|
39
|
+
messages: ConversationMessage[];
|
|
40
|
+
systemPrompt?: string;
|
|
41
|
+
maxTokens?: number;
|
|
42
|
+
temperature?: number;
|
|
43
|
+
tools?: ToolDefinition[];
|
|
44
|
+
images?: ImageAttachment[];
|
|
45
|
+
stream?: boolean;
|
|
46
|
+
}
|
|
47
|
+
interface GenerateResult {
|
|
48
|
+
content: string;
|
|
49
|
+
usage: TokenUsage;
|
|
50
|
+
toolCalls?: ToolCall[];
|
|
51
|
+
finishReason: 'stop' | 'length' | 'tool_use';
|
|
52
|
+
}
|
|
53
|
+
interface ConversationMessage {
|
|
54
|
+
role: 'user' | 'assistant' | 'system' | 'tool';
|
|
55
|
+
content: string | MessageContent[];
|
|
56
|
+
toolCallId?: string;
|
|
57
|
+
toolCalls?: ToolCall[];
|
|
58
|
+
name?: string;
|
|
59
|
+
}
|
|
60
|
+
type MessageContent = {
|
|
61
|
+
type: 'text';
|
|
62
|
+
text: string;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'image';
|
|
65
|
+
image: ImageAttachment;
|
|
66
|
+
} | {
|
|
67
|
+
type: 'tool_result';
|
|
68
|
+
toolCallId: string;
|
|
69
|
+
content: string;
|
|
70
|
+
};
|
|
71
|
+
interface ImageAttachment {
|
|
72
|
+
type: 'base64' | 'url';
|
|
73
|
+
data: string;
|
|
74
|
+
mimeType: 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp';
|
|
75
|
+
}
|
|
76
|
+
interface McpServerConfig$1 {
|
|
77
|
+
name: string;
|
|
78
|
+
command: string;
|
|
79
|
+
args?: string[];
|
|
80
|
+
env?: Record<string, string>;
|
|
81
|
+
}
|
|
82
|
+
interface ToolDefinition {
|
|
83
|
+
name: string;
|
|
84
|
+
description: string;
|
|
85
|
+
inputSchema: Record<string, unknown>;
|
|
86
|
+
}
|
|
87
|
+
interface Message {
|
|
88
|
+
id: string;
|
|
89
|
+
role: 'user' | 'assistant' | 'error' | 'system';
|
|
90
|
+
content: string;
|
|
91
|
+
timestamp: string;
|
|
92
|
+
}
|
|
93
|
+
interface ToolCall {
|
|
94
|
+
id: string;
|
|
95
|
+
name: string;
|
|
96
|
+
input: Record<string, unknown>;
|
|
97
|
+
}
|
|
98
|
+
interface ToolResult {
|
|
99
|
+
toolCallId: string;
|
|
100
|
+
output: string;
|
|
101
|
+
isError: boolean;
|
|
102
|
+
}
|
|
103
|
+
interface ToolExecuteOptions {
|
|
104
|
+
tierId: string;
|
|
105
|
+
sessionId: string;
|
|
106
|
+
requireApproval: boolean;
|
|
107
|
+
saveSnapshot?: (filePath: string, content: string) => Promise<void>;
|
|
108
|
+
sendPeerSync?: (to: string, syncType: PeerSyncType, content: string | Record<string, unknown>) => void;
|
|
109
|
+
getPeerMessages?: () => Array<{
|
|
110
|
+
fromId: string;
|
|
111
|
+
content: unknown;
|
|
112
|
+
timestamp: string;
|
|
113
|
+
}>;
|
|
114
|
+
}
|
|
115
|
+
type TierRole = 'T1' | 'T2' | 'T3';
|
|
116
|
+
type TierStatus = 'IDLE' | 'ACTIVE' | 'COMPLETED' | 'FAILED' | 'ESCALATED';
|
|
117
|
+
type TaskComplexity = 'Simple' | 'Moderate' | 'Complex' | 'Highly Complex';
|
|
118
|
+
interface TierConfig {
|
|
119
|
+
role: TierRole;
|
|
120
|
+
id: string;
|
|
121
|
+
model: ModelInfo;
|
|
122
|
+
parentId?: string;
|
|
123
|
+
}
|
|
124
|
+
type MessageType = 'TASK_ASSIGNMENT' | 'STATUS_UPDATE' | 'RESULT' | 'ESCALATION' | 'PEER_SYNC';
|
|
125
|
+
type MessageStatus = 'IN_PROGRESS' | 'BLOCKED' | 'ESCALATING' | 'COMPLETED' | 'PARTIAL' | 'FAILED' | 'ESCALATED';
|
|
126
|
+
interface CascadeMessage {
|
|
127
|
+
version: '1.0';
|
|
128
|
+
from: string;
|
|
129
|
+
to: string;
|
|
130
|
+
type: MessageType;
|
|
131
|
+
taskId: string;
|
|
132
|
+
timestamp: string;
|
|
133
|
+
payload: MessagePayload;
|
|
134
|
+
}
|
|
135
|
+
type MessagePayload = T1ToT2Assignment | T2ToT3Assignment | StatusUpdate | T2Result | T3Result | EscalationPayload | PeerSyncPayload;
|
|
136
|
+
interface T1ToT2Assignment {
|
|
137
|
+
sectionId: string;
|
|
138
|
+
sectionTitle: string;
|
|
139
|
+
description: string;
|
|
140
|
+
expectedOutput: string;
|
|
141
|
+
constraints: string[];
|
|
142
|
+
t3Subtasks: T3SubtaskSpec[];
|
|
143
|
+
executionMode?: 'parallel' | 'sequential';
|
|
144
|
+
peerT2Ids?: string[];
|
|
145
|
+
}
|
|
146
|
+
interface T3SubtaskSpec {
|
|
147
|
+
subtaskId: string;
|
|
148
|
+
subtaskTitle: string;
|
|
149
|
+
description: string;
|
|
150
|
+
expectedOutput: string;
|
|
151
|
+
constraints: string[];
|
|
152
|
+
peerT3Ids: string[];
|
|
153
|
+
dependsOn?: string[];
|
|
154
|
+
executionMode?: 'parallel' | 'sequential';
|
|
155
|
+
}
|
|
156
|
+
interface T2ToT3Assignment {
|
|
157
|
+
subtaskId: string;
|
|
158
|
+
subtaskTitle: string;
|
|
159
|
+
description: string;
|
|
160
|
+
expectedOutput: string;
|
|
161
|
+
constraints: string[];
|
|
162
|
+
peerT3Ids: string[];
|
|
163
|
+
parentT2: string;
|
|
164
|
+
dependsOn?: string[];
|
|
165
|
+
executionMode?: 'parallel' | 'sequential';
|
|
166
|
+
}
|
|
167
|
+
interface StatusUpdate {
|
|
168
|
+
progressPct: number;
|
|
169
|
+
currentAction: string;
|
|
170
|
+
status: 'IN_PROGRESS' | 'BLOCKED' | 'ESCALATING';
|
|
171
|
+
}
|
|
172
|
+
interface T2Result {
|
|
173
|
+
sectionId: string;
|
|
174
|
+
sectionTitle: string;
|
|
175
|
+
status: 'COMPLETED' | 'PARTIAL' | 'FAILED' | 'ESCALATED';
|
|
176
|
+
t3Results: T3Result[];
|
|
177
|
+
sectionSummary: string;
|
|
178
|
+
issues: string[];
|
|
179
|
+
}
|
|
180
|
+
interface T3ResultPayload {
|
|
181
|
+
subtaskId: string;
|
|
182
|
+
status: 'COMPLETED' | 'FAILED' | 'ESCALATED';
|
|
183
|
+
output: string | Record<string, unknown>;
|
|
184
|
+
testResults: {
|
|
185
|
+
checksRun: string[];
|
|
186
|
+
passed: string[];
|
|
187
|
+
failed: string[];
|
|
188
|
+
};
|
|
189
|
+
issues: string[];
|
|
190
|
+
peerSyncsUsed: string[];
|
|
191
|
+
correctionAttempts: number;
|
|
192
|
+
}
|
|
193
|
+
interface T3Result extends T3ResultPayload {
|
|
194
|
+
}
|
|
195
|
+
interface EscalationPayload {
|
|
196
|
+
raisedBy: string;
|
|
197
|
+
sectionId?: string;
|
|
198
|
+
subtaskId?: string;
|
|
199
|
+
attempted: string[];
|
|
200
|
+
blocker: string;
|
|
201
|
+
needs: string;
|
|
202
|
+
}
|
|
203
|
+
interface PeerSyncPayload {
|
|
204
|
+
senderT3Id: string;
|
|
205
|
+
recipientT3Id: string;
|
|
206
|
+
syncType: PeerSyncType;
|
|
207
|
+
content: string | Record<string, unknown>;
|
|
208
|
+
subtaskId?: string;
|
|
209
|
+
}
|
|
210
|
+
type PeerSyncType = 'SHARE_OUTPUT' | 'RESOLVE_CONFLICT' | 'DIVIDE_WORK' | 'CHECK_ASSUMPTION' | 'SIGNAL_READY';
|
|
211
|
+
interface PeerMessage {
|
|
212
|
+
fromId: string;
|
|
213
|
+
toId: string;
|
|
214
|
+
type: 'SYNC_DATA' | 'BARRIER';
|
|
215
|
+
subtaskId: string;
|
|
216
|
+
syncType?: PeerSyncType;
|
|
217
|
+
payload: unknown;
|
|
218
|
+
timestamp: string;
|
|
219
|
+
}
|
|
220
|
+
interface Session {
|
|
221
|
+
id: string;
|
|
222
|
+
title: string;
|
|
223
|
+
createdAt: string;
|
|
224
|
+
updatedAt: string;
|
|
225
|
+
identityId: string;
|
|
226
|
+
workspacePath: string;
|
|
227
|
+
messages: StoredMessage[];
|
|
228
|
+
metadata: SessionMetadata;
|
|
229
|
+
}
|
|
230
|
+
interface StoredMessage {
|
|
231
|
+
id: string;
|
|
232
|
+
sessionId: string;
|
|
233
|
+
role: 'user' | 'assistant' | 'system';
|
|
234
|
+
content: string;
|
|
235
|
+
timestamp: string;
|
|
236
|
+
tokens?: TokenUsage;
|
|
237
|
+
agentMessages?: CascadeMessage[];
|
|
238
|
+
}
|
|
239
|
+
interface SessionMetadata {
|
|
240
|
+
totalTokens: number;
|
|
241
|
+
totalCostUsd: number;
|
|
242
|
+
modelsUsed: string[];
|
|
243
|
+
toolsUsed: string[];
|
|
244
|
+
taskCount: number;
|
|
245
|
+
branch?: string;
|
|
246
|
+
checkpoint?: SessionCheckpoint;
|
|
247
|
+
}
|
|
248
|
+
interface RuntimeSession {
|
|
249
|
+
sessionId: string;
|
|
250
|
+
title: string;
|
|
251
|
+
workspacePath: string;
|
|
252
|
+
status: 'ACTIVE' | 'COMPLETED' | 'FAILED';
|
|
253
|
+
startedAt: string;
|
|
254
|
+
updatedAt: string;
|
|
255
|
+
latestPrompt?: string;
|
|
256
|
+
isGlobal?: boolean;
|
|
257
|
+
}
|
|
258
|
+
interface RuntimeNode {
|
|
259
|
+
tierId: string;
|
|
260
|
+
sessionId: string;
|
|
261
|
+
parentId?: string;
|
|
262
|
+
role: TierRole;
|
|
263
|
+
label: string;
|
|
264
|
+
status: TierStatus;
|
|
265
|
+
currentAction?: string;
|
|
266
|
+
progressPct?: number;
|
|
267
|
+
updatedAt: string;
|
|
268
|
+
workspacePath?: string;
|
|
269
|
+
isGlobal?: boolean;
|
|
270
|
+
}
|
|
271
|
+
interface RuntimeNodeLog {
|
|
272
|
+
id: string;
|
|
273
|
+
sessionId: string;
|
|
274
|
+
tierId: string;
|
|
275
|
+
role: TierRole;
|
|
276
|
+
label: string;
|
|
277
|
+
status: TierStatus;
|
|
278
|
+
currentAction?: string;
|
|
279
|
+
progressPct?: number;
|
|
280
|
+
timestamp: string;
|
|
281
|
+
workspacePath?: string;
|
|
282
|
+
isGlobal?: boolean;
|
|
283
|
+
}
|
|
284
|
+
type RuntimeScope = 'workspace' | 'global';
|
|
285
|
+
interface RuntimeSnapshotPayload {
|
|
286
|
+
scope?: RuntimeScope;
|
|
287
|
+
source?: string;
|
|
288
|
+
fetchedAt?: string;
|
|
289
|
+
sessions: RuntimeSession[];
|
|
290
|
+
nodes: RuntimeNode[];
|
|
291
|
+
logs: RuntimeNodeLog[];
|
|
292
|
+
}
|
|
293
|
+
interface RuntimeRefreshPayload {
|
|
294
|
+
scope: RuntimeScope;
|
|
295
|
+
}
|
|
296
|
+
interface SessionSubscriptionPayload {
|
|
297
|
+
sessionId: string;
|
|
298
|
+
}
|
|
299
|
+
interface PermissionDecisionPayload extends PermissionDecision {
|
|
300
|
+
}
|
|
301
|
+
interface SessionCheckpoint {
|
|
302
|
+
taskId: string;
|
|
303
|
+
timestamp: string;
|
|
304
|
+
state: Record<string, unknown>;
|
|
305
|
+
}
|
|
306
|
+
interface Identity {
|
|
307
|
+
id: string;
|
|
308
|
+
name: string;
|
|
309
|
+
description?: string;
|
|
310
|
+
avatar?: string;
|
|
311
|
+
createdAt: string;
|
|
312
|
+
defaultModel?: string;
|
|
313
|
+
systemPrompt?: string;
|
|
314
|
+
isDefault: boolean;
|
|
315
|
+
}
|
|
316
|
+
interface CascadeConfig {
|
|
317
|
+
version: '1.0';
|
|
318
|
+
defaultIdentityId?: string;
|
|
319
|
+
providers: ProviderConfig[];
|
|
320
|
+
models: ModelOverrides;
|
|
321
|
+
tools: ToolsConfig;
|
|
322
|
+
hooks: HooksConfig;
|
|
323
|
+
dashboard: DashboardConfig;
|
|
324
|
+
telemetry: TelemetryConfig;
|
|
325
|
+
memory: MemoryConfig;
|
|
326
|
+
tierLimits: TierLimits;
|
|
327
|
+
budget: BudgetConfig;
|
|
328
|
+
theme: string;
|
|
329
|
+
workspace: WorkspaceConfig;
|
|
330
|
+
}
|
|
331
|
+
interface ModelOverrides {
|
|
332
|
+
t1?: string;
|
|
333
|
+
t2?: string;
|
|
334
|
+
t3?: string;
|
|
335
|
+
vision?: string;
|
|
336
|
+
}
|
|
337
|
+
interface ToolsConfig {
|
|
338
|
+
shellAllowlist: string[];
|
|
339
|
+
shellBlocklist: string[];
|
|
340
|
+
requireApprovalFor: string[];
|
|
341
|
+
browserEnabled: boolean;
|
|
342
|
+
mcpServers?: McpServerConfig$1[];
|
|
343
|
+
/**
|
|
344
|
+
* Names of MCP servers (matching McpServerConfig.name) that the user has
|
|
345
|
+
* explicitly trusted. Servers not in this list require interactive
|
|
346
|
+
* approval before they are spawned.
|
|
347
|
+
*/
|
|
348
|
+
mcpTrusted?: string[];
|
|
349
|
+
}
|
|
350
|
+
interface HooksConfig {
|
|
351
|
+
preToolUse?: HookDefinition[];
|
|
352
|
+
postToolUse?: HookDefinition[];
|
|
353
|
+
preTask?: HookDefinition[];
|
|
354
|
+
postTask?: HookDefinition[];
|
|
355
|
+
}
|
|
356
|
+
interface HookDefinition {
|
|
357
|
+
command: string;
|
|
358
|
+
tools?: string[];
|
|
359
|
+
timeout?: number;
|
|
360
|
+
}
|
|
361
|
+
interface DashboardConfig {
|
|
362
|
+
port: number;
|
|
363
|
+
auth: boolean;
|
|
364
|
+
teamMode: 'single' | 'multi';
|
|
365
|
+
secret?: string;
|
|
366
|
+
}
|
|
367
|
+
interface TelemetryConfig {
|
|
368
|
+
enabled: boolean;
|
|
369
|
+
posthogApiKey?: string;
|
|
370
|
+
distinctId?: string;
|
|
371
|
+
}
|
|
372
|
+
interface MemoryConfig {
|
|
373
|
+
maxSessionMessages: number;
|
|
374
|
+
autoSummarizeAt: number;
|
|
375
|
+
retentionDays: number;
|
|
376
|
+
}
|
|
377
|
+
interface TierLimits {
|
|
378
|
+
t1MaxTokens?: number;
|
|
379
|
+
t2MaxTokens?: number;
|
|
380
|
+
t3MaxTokens?: number;
|
|
381
|
+
}
|
|
382
|
+
interface BudgetConfig {
|
|
383
|
+
dailyBudgetUsd?: number;
|
|
384
|
+
sessionBudgetUsd?: number;
|
|
385
|
+
warnAtPct: number;
|
|
386
|
+
}
|
|
387
|
+
interface WorkspaceConfig {
|
|
388
|
+
cascadeMdPath: string;
|
|
389
|
+
configPath: string;
|
|
390
|
+
keystorePath: string;
|
|
391
|
+
auditLogPath: string;
|
|
392
|
+
}
|
|
393
|
+
type ThemeName = 'cascade' | 'dark' | 'light' | 'dracula' | 'nord' | 'solarized';
|
|
394
|
+
interface Theme {
|
|
395
|
+
name: ThemeName;
|
|
396
|
+
colors: ThemeColors;
|
|
397
|
+
}
|
|
398
|
+
interface ThemeColors {
|
|
399
|
+
primary: string;
|
|
400
|
+
secondary: string;
|
|
401
|
+
accent: string;
|
|
402
|
+
success: string;
|
|
403
|
+
warning: string;
|
|
404
|
+
error: string;
|
|
405
|
+
info: string;
|
|
406
|
+
muted: string;
|
|
407
|
+
background: string;
|
|
408
|
+
foreground: string;
|
|
409
|
+
border: string;
|
|
410
|
+
t1Color: string;
|
|
411
|
+
t2Color: string;
|
|
412
|
+
t3Color: string;
|
|
413
|
+
}
|
|
414
|
+
type CascadeEventType = 'task:start' | 'task:complete' | 'task:error' | 'tier:status' | 'tier:result' | 'stream:token' | 'stream:done' | 'tool:approval-request' | 'tool:approval-response' | 'tool:execute' | 'tool:result' | 'cost:update' | 'session:save' | 'escalation' | 'peer:sync';
|
|
415
|
+
interface CascadeEvent<T = unknown> {
|
|
416
|
+
type: CascadeEventType;
|
|
417
|
+
taskId?: string;
|
|
418
|
+
tierId?: string;
|
|
419
|
+
data: T;
|
|
420
|
+
timestamp: string;
|
|
421
|
+
}
|
|
422
|
+
interface ApprovalRequest {
|
|
423
|
+
id: string;
|
|
424
|
+
tierId: string;
|
|
425
|
+
toolName: string;
|
|
426
|
+
input: Record<string, unknown>;
|
|
427
|
+
description: string;
|
|
428
|
+
isDangerous: boolean;
|
|
429
|
+
}
|
|
430
|
+
interface ApprovalResponse {
|
|
431
|
+
id: string;
|
|
432
|
+
approved: boolean;
|
|
433
|
+
always?: boolean;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* A permission request raised by a T3 worker that must be evaluated
|
|
437
|
+
* by T2, then T1, and finally the user if neither tier can decide.
|
|
438
|
+
*/
|
|
439
|
+
interface PermissionRequest {
|
|
440
|
+
/** Unique request ID */
|
|
441
|
+
id: string;
|
|
442
|
+
/** T3 worker that requires the permission */
|
|
443
|
+
requestedBy: string;
|
|
444
|
+
/** T2 manager that owns this T3 worker */
|
|
445
|
+
parentT2Id: string;
|
|
446
|
+
/** Tool being requested */
|
|
447
|
+
toolName: string;
|
|
448
|
+
/** Tool input arguments */
|
|
449
|
+
input: Record<string, unknown>;
|
|
450
|
+
/** Whether the tool is flagged as dangerous */
|
|
451
|
+
isDangerous: boolean;
|
|
452
|
+
/** What the T3 subtask is trying to accomplish */
|
|
453
|
+
subtaskContext: string;
|
|
454
|
+
/** What the parent T2 section's goal is */
|
|
455
|
+
sectionContext: string;
|
|
456
|
+
/** What T1's overall task goal is (injected when escalated to T1) */
|
|
457
|
+
taskContext?: string;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* A decision made at any tier (T2, T1, or USER) about a PermissionRequest.
|
|
461
|
+
*/
|
|
462
|
+
interface PermissionDecision {
|
|
463
|
+
/** ID of the PermissionRequest this responds to */
|
|
464
|
+
requestId: string;
|
|
465
|
+
/** Whether the tool call is approved */
|
|
466
|
+
approved: boolean;
|
|
467
|
+
/**
|
|
468
|
+
* If true, cache this decision for the session so the same tool
|
|
469
|
+
* is not asked about again (section-wide scope for T2, task-wide for T1).
|
|
470
|
+
*/
|
|
471
|
+
always?: boolean;
|
|
472
|
+
/** Which tier made the decision */
|
|
473
|
+
decidedBy: 'T2' | 'T1' | 'USER';
|
|
474
|
+
/** Optional explanation from the evaluating tier */
|
|
475
|
+
reasoning?: string;
|
|
476
|
+
}
|
|
477
|
+
interface AuditEntry {
|
|
478
|
+
id: string;
|
|
479
|
+
sessionId: string;
|
|
480
|
+
timestamp: string;
|
|
481
|
+
tierId: string;
|
|
482
|
+
action: 'tool_call' | 'file_change' | 'agent_decision' | 'approval' | 'escalation' | 'error';
|
|
483
|
+
details: Record<string, unknown>;
|
|
484
|
+
}
|
|
485
|
+
interface ScheduledTask {
|
|
486
|
+
id: string;
|
|
487
|
+
name: string;
|
|
488
|
+
cronExpression: string;
|
|
489
|
+
prompt: string;
|
|
490
|
+
identityId?: string;
|
|
491
|
+
workspacePath?: string;
|
|
492
|
+
createdAt: string;
|
|
493
|
+
lastRun?: string;
|
|
494
|
+
nextRun?: string;
|
|
495
|
+
enabled: boolean;
|
|
496
|
+
}
|
|
497
|
+
interface WebhookConfig {
|
|
498
|
+
url: string;
|
|
499
|
+
events: CascadeEventType[];
|
|
500
|
+
secret?: string;
|
|
501
|
+
headers?: Record<string, string>;
|
|
502
|
+
}
|
|
503
|
+
interface CascadeRunOptions {
|
|
504
|
+
prompt: string;
|
|
505
|
+
images?: ImageAttachment[];
|
|
506
|
+
workspacePath?: string;
|
|
507
|
+
identityId?: string;
|
|
508
|
+
sessionId?: string;
|
|
509
|
+
conversationHistory?: ConversationMessage[];
|
|
510
|
+
streamCallback?: (chunk: StreamChunk) => void;
|
|
511
|
+
approvalCallback?: (request: ApprovalRequest) => Promise<boolean | {
|
|
512
|
+
approved: boolean;
|
|
513
|
+
always: boolean;
|
|
514
|
+
}>;
|
|
515
|
+
}
|
|
516
|
+
interface CascadeRunResult {
|
|
517
|
+
output: string;
|
|
518
|
+
sessionId: string;
|
|
519
|
+
taskId: string;
|
|
520
|
+
usage: TokenUsage;
|
|
521
|
+
t2Results: T2Result[];
|
|
522
|
+
durationMs: number;
|
|
523
|
+
/** Per-tier cost breakdown (USD). Available when the router tracked stats. */
|
|
524
|
+
costByTier?: Record<string, number>;
|
|
525
|
+
/** Per-tier total token counts. Available when the router tracked stats. */
|
|
526
|
+
tokensByTier?: Record<string, number>;
|
|
527
|
+
/** Per-tier cost as a percentage of total spend (0–100). */
|
|
528
|
+
costPercentByTier?: Record<string, number>;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
declare class ModelSelector {
|
|
532
|
+
private availableProviders;
|
|
533
|
+
private availableModels;
|
|
534
|
+
constructor(availableProviders: Set<ProviderType>);
|
|
535
|
+
addDynamicModel(model: ModelInfo): void;
|
|
536
|
+
getAvailableModelsForProvider(provider: ProviderType): ModelInfo[];
|
|
537
|
+
selectForTier(tier: TierRole, overrideModelId?: string, requireVision?: boolean): ModelInfo | null;
|
|
538
|
+
selectVisionModel(): ModelInfo | null;
|
|
539
|
+
getNextFallback(currentModelId: string, tier: TierRole): ModelInfo | null;
|
|
540
|
+
private getPriorityList;
|
|
541
|
+
isProviderAvailable(provider: ProviderType): boolean;
|
|
542
|
+
markProviderUnavailable(provider: ProviderType): void;
|
|
543
|
+
private resolveDynamicModel;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
interface RouterStats {
|
|
547
|
+
totalTokens: number;
|
|
548
|
+
totalCostUsd: number;
|
|
549
|
+
callsByProvider: Record<string, number>;
|
|
550
|
+
callsByTier: Record<string, number>;
|
|
551
|
+
/** Accumulated cost (USD) broken down per tier — useful for budget attribution. */
|
|
552
|
+
costByTier: Record<string, number>;
|
|
553
|
+
/** Accumulated token usage broken down per tier (input + output). */
|
|
554
|
+
tokensByTier: Record<string, number>;
|
|
555
|
+
/** Input and output token counts per tier for granular cost analysis. */
|
|
556
|
+
inputTokensByTier: Record<string, number>;
|
|
557
|
+
outputTokensByTier: Record<string, number>;
|
|
558
|
+
}
|
|
559
|
+
declare class CascadeRouter extends EventEmitter {
|
|
560
|
+
private selector;
|
|
561
|
+
private failover;
|
|
562
|
+
private providers;
|
|
563
|
+
private stats;
|
|
564
|
+
private tierModels;
|
|
565
|
+
private config;
|
|
566
|
+
private sessionCostUsd;
|
|
567
|
+
/**
|
|
568
|
+
* Budget state machine — guards against two concurrent `generate()` calls
|
|
569
|
+
* each firing the warning or both slipping past the hard cap. All
|
|
570
|
+
* transitions happen inside `updateBudgetState()` which is called only
|
|
571
|
+
* from `recordStats`, single-threaded per V8 event loop turn.
|
|
572
|
+
*/
|
|
573
|
+
private budgetState;
|
|
574
|
+
private budgetExceededReason;
|
|
575
|
+
private tpmLimiter;
|
|
576
|
+
/** Thrown when the configured budget is exceeded. */
|
|
577
|
+
static BudgetExceededError: {
|
|
578
|
+
new (msg: string): {
|
|
579
|
+
name: string;
|
|
580
|
+
message: string;
|
|
581
|
+
stack?: string;
|
|
582
|
+
cause?: unknown;
|
|
583
|
+
};
|
|
584
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
585
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
586
|
+
stackTraceLimit: number;
|
|
587
|
+
};
|
|
588
|
+
constructor();
|
|
589
|
+
init(config: CascadeConfig): Promise<void>;
|
|
590
|
+
generate(tier: TierRole, options: GenerateOptions, onChunk?: (chunk: StreamChunk) => void, requireVision?: boolean): Promise<GenerateResult>;
|
|
591
|
+
getModelForTier(tier: TierRole): ModelInfo | undefined;
|
|
592
|
+
/**
|
|
593
|
+
* Cascade Auto: temporarily override the model for a tier.
|
|
594
|
+
* Used by TaskAnalyzer to inject task-optimal models before execution.
|
|
595
|
+
* The override is valid for the current task only — restored by restoreTierModels().
|
|
596
|
+
*/
|
|
597
|
+
overrideTierModel(tier: TierRole, model: ModelInfo): void;
|
|
598
|
+
getSelector(): ModelSelector;
|
|
599
|
+
getStats(): RouterStats;
|
|
600
|
+
/**
|
|
601
|
+
* Returns a human-readable cost summary broken down by tier.
|
|
602
|
+
* Example: { T1: "$0.0120 (2 calls, 1500 tokens)", T2: "$0.0043 (6 calls, 4200 tokens)", ... }
|
|
603
|
+
*/
|
|
604
|
+
getTierCostSummary(): Record<string, string>;
|
|
605
|
+
/**
|
|
606
|
+
* Returns the percentage of total cost attributed to each tier.
|
|
607
|
+
* Useful for identifying which tier is the dominant cost driver.
|
|
608
|
+
*/
|
|
609
|
+
getTierCostPercentages(): Record<string, number>;
|
|
610
|
+
/**
|
|
611
|
+
* Resets all stats — useful between independent task runs in long-lived sessions.
|
|
612
|
+
*/
|
|
613
|
+
resetStats(): void;
|
|
614
|
+
getFailures(): Record<string, string>;
|
|
615
|
+
/**
|
|
616
|
+
* Returns the current session budget cap (USD), or undefined if no cap is set.
|
|
617
|
+
*/
|
|
618
|
+
getSessionBudget(): number | undefined;
|
|
619
|
+
/**
|
|
620
|
+
* Sets (or clears) a runtime session budget cap (USD).
|
|
621
|
+
* Pass null to remove the cap.
|
|
622
|
+
*/
|
|
623
|
+
setSessionBudget(usd: number | null): void;
|
|
624
|
+
/**
|
|
625
|
+
* Returns how much of the session budget has been used (USD).
|
|
626
|
+
*/
|
|
627
|
+
getSessionSpend(): number;
|
|
628
|
+
/**
|
|
629
|
+
* Returns the resolved ModelInfo for a given tier, or null if no model
|
|
630
|
+
* is available (e.g. the required provider is not configured).
|
|
631
|
+
*/
|
|
632
|
+
getTierModel(tier: TierRole): ModelInfo | null;
|
|
633
|
+
/**
|
|
634
|
+
* Returns all models available for the given provider type.
|
|
635
|
+
* Useful for listing configured/usable models per provider.
|
|
636
|
+
*/
|
|
637
|
+
getModelsForProvider(provider: ProviderType): ModelInfo[];
|
|
638
|
+
private detectAvailableProviders;
|
|
639
|
+
private discoverOllamaModels;
|
|
640
|
+
private ensureProvider;
|
|
641
|
+
private getProvider;
|
|
642
|
+
private createProvider;
|
|
643
|
+
private getAnyModelForProvider;
|
|
644
|
+
private recordStats;
|
|
645
|
+
/**
|
|
646
|
+
* Single point of truth for budget state transitions. Called after each
|
|
647
|
+
* recordStats() so warning and hard-stop transitions are evaluated
|
|
648
|
+
* exactly once — previous logic allowed concurrent generate() calls to
|
|
649
|
+
* both fire the warning or both miss the hard stop.
|
|
650
|
+
*/
|
|
651
|
+
private updateBudgetState;
|
|
652
|
+
/**
|
|
653
|
+
* Flip the router to "exceeded" state. Subsequent `generate()` calls will
|
|
654
|
+
* throw BudgetExceededError immediately, and a `budget:exceeded` event is
|
|
655
|
+
* broadcast once so listeners (REPL, dashboard, SDK) can cancel any
|
|
656
|
+
* pending approvals and unwind the run.
|
|
657
|
+
*/
|
|
658
|
+
halt(reason: string): void;
|
|
659
|
+
/** Returns current budget state — useful for tests and dashboard. */
|
|
660
|
+
getBudgetState(): 'ok' | 'warned' | 'exceeded';
|
|
661
|
+
private isRateLimitError;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
declare abstract class BaseTool {
|
|
665
|
+
abstract readonly name: string;
|
|
666
|
+
abstract readonly description: string;
|
|
667
|
+
abstract readonly inputSchema: Record<string, unknown>;
|
|
668
|
+
protected workspaceRoot: string;
|
|
669
|
+
setWorkspaceRoot(root: string): void;
|
|
670
|
+
abstract execute(input: Record<string, unknown>, options: ToolExecuteOptions): Promise<string>;
|
|
671
|
+
isDangerous(): boolean;
|
|
672
|
+
getDefinition(): ToolDefinition;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
interface McpServerConfig {
|
|
676
|
+
name: string;
|
|
677
|
+
command: string;
|
|
678
|
+
args?: string[];
|
|
679
|
+
env?: Record<string, string>;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Gate called before each MCP server is spawned. Lets the caller (REPL or
|
|
683
|
+
* SDK) prompt the user for explicit approval of a subprocess binary.
|
|
684
|
+
* Return `true` to allow, `false` to reject.
|
|
685
|
+
*/
|
|
686
|
+
type McpApprovalCallback = (server: McpServerConfig) => Promise<boolean> | boolean;
|
|
687
|
+
interface McpClientOptions {
|
|
688
|
+
/** Names of servers the user has already trusted (config.mcp.trusted). */
|
|
689
|
+
trustedServers?: string[];
|
|
690
|
+
/** Approval gate invoked when a server is NOT in the trusted list. */
|
|
691
|
+
approvalCallback?: McpApprovalCallback;
|
|
692
|
+
}
|
|
693
|
+
declare class McpClient {
|
|
694
|
+
private clients;
|
|
695
|
+
private tools;
|
|
696
|
+
private trustedServers;
|
|
697
|
+
private approvalCallback;
|
|
698
|
+
constructor(options?: McpClientOptions);
|
|
699
|
+
connect(server: McpServerConfig): Promise<void>;
|
|
700
|
+
disconnect(serverName: string): Promise<void>;
|
|
701
|
+
disconnectAll(): Promise<void>;
|
|
702
|
+
callTool(serverName: string, toolName: string, input: Record<string, unknown>): Promise<string>;
|
|
703
|
+
getToolDefinitions(): ToolDefinition[];
|
|
704
|
+
getConnectedServers(): string[];
|
|
705
|
+
isConnected(serverName: string): boolean;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* A ToolPlugin bundles one or more custom tools that extend Cascade's capabilities.
|
|
710
|
+
* Plugins are loaded via `registerPlugin()` and behave like built-in tools.
|
|
711
|
+
*
|
|
712
|
+
* @example
|
|
713
|
+
* const myPlugin: ToolPlugin = {
|
|
714
|
+
* name: 'my-custom-tools',
|
|
715
|
+
* version: '1.0.0',
|
|
716
|
+
* tools: [new MyCustomTool()],
|
|
717
|
+
* onRegister: (registry) => console.log('Plugin registered'),
|
|
718
|
+
* };
|
|
719
|
+
* registry.registerPlugin(myPlugin);
|
|
720
|
+
*/
|
|
721
|
+
interface ToolPlugin {
|
|
722
|
+
/** Unique plugin identifier */
|
|
723
|
+
name: string;
|
|
724
|
+
/** Semantic version string */
|
|
725
|
+
version: string;
|
|
726
|
+
/** One or more tools this plugin provides */
|
|
727
|
+
tools: BaseTool[];
|
|
728
|
+
/** Called once when the plugin is registered */
|
|
729
|
+
onRegister?: (registry: ToolRegistry) => void;
|
|
730
|
+
}
|
|
731
|
+
declare class ToolRegistry {
|
|
732
|
+
private tools;
|
|
733
|
+
private config;
|
|
734
|
+
private ignoreMatcher;
|
|
735
|
+
private workspaceRoot;
|
|
736
|
+
/** Loaded plugins, keyed by plugin name */
|
|
737
|
+
private plugins;
|
|
738
|
+
constructor(config: ToolsConfig, workspaceRoot?: string);
|
|
739
|
+
register(tool: BaseTool): void;
|
|
740
|
+
/**
|
|
741
|
+
* Register a ToolPlugin, loading all its tools into the registry.
|
|
742
|
+
* Each tool is configured with the current workspace root.
|
|
743
|
+
* Duplicate plugin names are silently ignored.
|
|
744
|
+
*
|
|
745
|
+
* @example
|
|
746
|
+
* registry.registerPlugin(myPlugin);
|
|
747
|
+
*/
|
|
748
|
+
registerPlugin(plugin: ToolPlugin): void;
|
|
749
|
+
/** Returns the names of all registered plugins */
|
|
750
|
+
getRegisteredPlugins(): string[];
|
|
751
|
+
/** Registers all tools from an MCP client */
|
|
752
|
+
registerMcpTools(mcpClient: McpClient): void;
|
|
753
|
+
setIgnoredPaths(patterns: string[]): void;
|
|
754
|
+
getToolDefinitions(): ToolDefinition[];
|
|
755
|
+
getTool(name: string): BaseTool | undefined;
|
|
756
|
+
hasTool(name: string): boolean;
|
|
757
|
+
requiresApproval(toolName: string): boolean;
|
|
758
|
+
isDangerous(toolName: string): boolean;
|
|
759
|
+
execute(toolName: string, input: Record<string, unknown>, options: ToolExecuteOptions): Promise<string>;
|
|
760
|
+
private registerDefaults;
|
|
761
|
+
private isFileOperation;
|
|
762
|
+
private isIgnored;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
declare class MemoryStore {
|
|
766
|
+
private db;
|
|
767
|
+
constructor(dbPath: string);
|
|
768
|
+
createSession(session: Session): void;
|
|
769
|
+
updateSession(id: string, updates: Partial<Session>): void;
|
|
770
|
+
getSession(id: string): Session | null;
|
|
771
|
+
listSessions(identityId?: string, limit?: number): Session[];
|
|
772
|
+
deleteSession(id: string): void;
|
|
773
|
+
deleteAllSessions(): void;
|
|
774
|
+
deleteRuntimeSession(sessionId: string): void;
|
|
775
|
+
deleteAllRuntimeNodes(): void;
|
|
776
|
+
branchSession(originalId: string, newId: string): void;
|
|
777
|
+
upsertRuntimeSession(session: RuntimeSession): void;
|
|
778
|
+
listRuntimeSessions(limit?: number): RuntimeSession[];
|
|
779
|
+
upsertRuntimeNode(node: RuntimeNode): void;
|
|
780
|
+
listRuntimeNodes(sessionId?: string, limit?: number): RuntimeNode[];
|
|
781
|
+
addRuntimeNodeLog(log: RuntimeNodeLog): void;
|
|
782
|
+
listRuntimeNodeLogs(sessionId?: string, tierId?: string, limit?: number): RuntimeNodeLog[];
|
|
783
|
+
addMessage(message: StoredMessage): void;
|
|
784
|
+
getSessionMessages(sessionId: string): StoredMessage[];
|
|
785
|
+
searchMessages(query: string, limit?: number): StoredMessage[];
|
|
786
|
+
createIdentity(identity: Identity): void;
|
|
787
|
+
updateIdentity(id: string, updates: Partial<Identity>): void;
|
|
788
|
+
getIdentity(id: string): Identity | null;
|
|
789
|
+
getDefaultIdentity(): Identity | null;
|
|
790
|
+
listIdentities(): Identity[];
|
|
791
|
+
deleteIdentity(id: string): void;
|
|
792
|
+
saveScheduledTask(task: ScheduledTask): void;
|
|
793
|
+
listScheduledTasks(): ScheduledTask[];
|
|
794
|
+
deleteScheduledTask(id: string): void;
|
|
795
|
+
addAuditEntry(entry: AuditEntry): void;
|
|
796
|
+
getAuditLog(sessionId: string, limit?: number): AuditEntry[];
|
|
797
|
+
addFileSnapshot(sessionId: string, filePath: string, content: string): void;
|
|
798
|
+
getLatestFileSnapshots(sessionId: string): Array<{
|
|
799
|
+
filePath: string;
|
|
800
|
+
content: string;
|
|
801
|
+
}>;
|
|
802
|
+
upsertCachedModel(model: ModelInfo): void;
|
|
803
|
+
getCachedModels(provider?: ProviderType): ModelInfo[];
|
|
804
|
+
clearModelCache(provider?: ProviderType): void;
|
|
805
|
+
getCacheAge(): number;
|
|
806
|
+
private toolResultCache;
|
|
807
|
+
private static CACHEABLE_TOOLS;
|
|
808
|
+
private static TOOL_TTL_MS;
|
|
809
|
+
/**
|
|
810
|
+
* Returns a cached tool result, or null if not cached / expired.
|
|
811
|
+
*/
|
|
812
|
+
getToolResult(toolName: string, input: Record<string, unknown>): string | null;
|
|
813
|
+
/**
|
|
814
|
+
* Stores a tool result in the in-memory cache.
|
|
815
|
+
* Only caches read-only/safe tools (see CACHEABLE_TOOLS).
|
|
816
|
+
*/
|
|
817
|
+
setToolResult(toolName: string, input: Record<string, unknown>, result: string): void;
|
|
818
|
+
/** Invalidate tool cache for a specific tool name, or all tools if omitted. */
|
|
819
|
+
invalidateToolCache(toolName?: string): void;
|
|
820
|
+
close(): void;
|
|
821
|
+
private migrate;
|
|
822
|
+
private deserializeSession;
|
|
823
|
+
private deserializeMessage;
|
|
824
|
+
private deserializeIdentity;
|
|
825
|
+
private deserializeScheduledTask;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
declare class Cascade extends EventEmitter {
|
|
829
|
+
private router;
|
|
830
|
+
private toolRegistry;
|
|
831
|
+
private mcpClient;
|
|
832
|
+
private config;
|
|
833
|
+
private initialized;
|
|
834
|
+
private initPromise?;
|
|
835
|
+
private store?;
|
|
836
|
+
private audit?;
|
|
837
|
+
private telemetry;
|
|
838
|
+
private taskAnalyzer?;
|
|
839
|
+
private toolCreator?;
|
|
840
|
+
constructor(config: CascadeConfig, workspacePath: string, store?: MemoryStore);
|
|
841
|
+
private initOptionalFeatures;
|
|
842
|
+
setStore(store: MemoryStore): void;
|
|
843
|
+
/**
|
|
844
|
+
* Emit an `mcp:approval-required` event and wait up to 30 s for a listener
|
|
845
|
+
* to resolve it via `cascade.resolveMcpApproval(serverName, approved)`.
|
|
846
|
+
*
|
|
847
|
+
* If no listener is attached (e.g. a non-interactive SDK run), the default
|
|
848
|
+
* is to reject — safer than silently spawning an arbitrary subprocess.
|
|
849
|
+
*/
|
|
850
|
+
private pendingMcpApprovals;
|
|
851
|
+
private requestMcpApproval;
|
|
852
|
+
/** Resolve a pending MCP server approval from a REPL / dashboard listener. */
|
|
853
|
+
resolveMcpApproval(serverName: string, approved: boolean): void;
|
|
854
|
+
init(): Promise<void>;
|
|
855
|
+
private looksLikeSimpleArtifactTask;
|
|
856
|
+
private determineComplexity;
|
|
857
|
+
run(options: CascadeRunOptions): Promise<CascadeRunResult>;
|
|
858
|
+
getRouter(): CascadeRouter;
|
|
859
|
+
getToolRegistry(): ToolRegistry;
|
|
860
|
+
/**
|
|
861
|
+
* Tear down MCP connections and flush any pending telemetry so long-lived
|
|
862
|
+
* hosts (REPL, SDK embedders) don't leak child processes. Safe to call
|
|
863
|
+
* multiple times.
|
|
864
|
+
*/
|
|
865
|
+
close(): Promise<void>;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
declare abstract class BaseTier extends EventEmitter {
|
|
869
|
+
readonly id: string;
|
|
870
|
+
readonly role: TierRole;
|
|
871
|
+
protected status: TierStatus;
|
|
872
|
+
protected parentId?: string;
|
|
873
|
+
protected taskId: string;
|
|
874
|
+
protected label: string;
|
|
875
|
+
protected systemPromptOverride: string;
|
|
876
|
+
protected hierarchyContext: string;
|
|
877
|
+
constructor(role: TierRole, id?: string, parentId?: string);
|
|
878
|
+
getStatus(): TierStatus;
|
|
879
|
+
protected setStatus(status: TierStatus): void;
|
|
880
|
+
protected setLabel(label: string): void;
|
|
881
|
+
setSystemPromptOverride(prompt: string): void;
|
|
882
|
+
setHierarchyContext(context: string): void;
|
|
883
|
+
protected sendStatusUpdate(update: StatusUpdate): void;
|
|
884
|
+
protected buildMessage(type: CascadeMessage['type'], to: string, payload: Record<string, unknown>): CascadeMessage;
|
|
885
|
+
protected log(message: string, data?: unknown): void;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
type T2Evaluator = (req: PermissionRequest) => Promise<PermissionDecision | null>;
|
|
889
|
+
type T1Evaluator = (req: PermissionRequest) => Promise<PermissionDecision | null>;
|
|
890
|
+
/**
|
|
891
|
+
* PermissionEscalator manages the hierarchical permission flow for a single task run.
|
|
892
|
+
*
|
|
893
|
+
* Decision cascade:
|
|
894
|
+
* 1. Check session cache (section-wide key `${t2Id}:${toolName}`) → return if hit
|
|
895
|
+
* 2. Ask T2 evaluator → if decision returned, cache + return
|
|
896
|
+
* 3. Ask T1 evaluator → if decision returned, cache + return
|
|
897
|
+
* 4. Emit `permission:user-required` → wait for external decision via `resolveUserDecision()`
|
|
898
|
+
*/
|
|
899
|
+
declare class PermissionEscalator extends EventEmitter {
|
|
900
|
+
/**
|
|
901
|
+
* Session cache keyed by `${t2Id}:${toolName}`.
|
|
902
|
+
* All T3 workers under the same T2 share cached decisions for the same tool.
|
|
903
|
+
*/
|
|
904
|
+
private sessionCache;
|
|
905
|
+
private t2Evaluator?;
|
|
906
|
+
private t1Evaluator?;
|
|
907
|
+
/** Pending user-decision resolvers keyed by request ID */
|
|
908
|
+
private pendingUserDecisions;
|
|
909
|
+
setT2Evaluator(evaluator: T2Evaluator): void;
|
|
910
|
+
setT1Evaluator(evaluator: T1Evaluator): void;
|
|
911
|
+
/**
|
|
912
|
+
* Main entry point. Called by T3Worker instead of emitting `tool:approval-request`.
|
|
913
|
+
* Returns a PermissionDecision from whichever tier was able to decide.
|
|
914
|
+
*/
|
|
915
|
+
requestPermission(req: PermissionRequest): Promise<PermissionDecision>;
|
|
916
|
+
/**
|
|
917
|
+
* Called by the REPL/SDK once the user has made a decision.
|
|
918
|
+
* Only has effect when a request is actually pending.
|
|
919
|
+
*/
|
|
920
|
+
resolveUserDecision(requestId: string, approved: boolean, always?: boolean): void;
|
|
921
|
+
private waitForUserDecision;
|
|
922
|
+
/** Check if there are permissions waiting for user input */
|
|
923
|
+
hasPendingUserDecisions(): boolean;
|
|
924
|
+
/** Deny all pending user decisions (used on task cancel) */
|
|
925
|
+
cancelAllPending(): void;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
declare class ToolCreator {
|
|
929
|
+
private router;
|
|
930
|
+
private registry;
|
|
931
|
+
private createdTools;
|
|
932
|
+
constructor(router: CascadeRouter, registry: ToolRegistry);
|
|
933
|
+
/**
|
|
934
|
+
* Generate a new tool from a description and register it with the ToolRegistry.
|
|
935
|
+
* Returns the tool name if successful, null if generation failed.
|
|
936
|
+
*/
|
|
937
|
+
createTool(description: string, context: string): Promise<string | null>;
|
|
938
|
+
/**
|
|
939
|
+
* Returns the names of all tools created in this session.
|
|
940
|
+
*/
|
|
941
|
+
getCreatedTools(): string[];
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
declare class T1Administrator extends BaseTier {
|
|
945
|
+
private router;
|
|
946
|
+
private toolRegistry;
|
|
947
|
+
private config;
|
|
948
|
+
private t2Managers;
|
|
949
|
+
private escalations;
|
|
950
|
+
private store?;
|
|
951
|
+
private t2PeerBus;
|
|
952
|
+
private permissionEscalator?;
|
|
953
|
+
private toolCreator?;
|
|
954
|
+
/** Stored overall task goal — used when evaluating escalated permissions */
|
|
955
|
+
private taskGoal;
|
|
956
|
+
constructor(router: CascadeRouter, toolRegistry: ToolRegistry, config: CascadeConfig);
|
|
957
|
+
setStore(store: MemoryStore): void;
|
|
958
|
+
/**
|
|
959
|
+
* Inject the shared PermissionEscalator for this task run.
|
|
960
|
+
* Registers T1's evaluator so it can decide when T2 is uncertain.
|
|
961
|
+
*/
|
|
962
|
+
setPermissionEscalator(escalator: PermissionEscalator): void;
|
|
963
|
+
setToolCreator(creator: ToolCreator): void;
|
|
964
|
+
execute(userPrompt: string, images?: ImageAttachment[], systemContext?: string): Promise<{
|
|
965
|
+
output: string;
|
|
966
|
+
t2Results: T2Result[];
|
|
967
|
+
taskId: string;
|
|
968
|
+
complexity: TaskComplexity;
|
|
969
|
+
}>;
|
|
970
|
+
getEscalations(): EscalationPayload[];
|
|
971
|
+
private analyzeImages;
|
|
972
|
+
private decomposeTask;
|
|
973
|
+
private validatePlan;
|
|
974
|
+
private dispatchT2Managers;
|
|
975
|
+
private compileFinalOutput;
|
|
976
|
+
/**
|
|
977
|
+
* T1-level permission evaluator.
|
|
978
|
+
* Uses T1's model with full task context.
|
|
979
|
+
* Returns null only when the model explicitly says UNSURE (triggers user prompt).
|
|
980
|
+
*/
|
|
981
|
+
private evaluatePermissionAtT1;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
interface PeerOutput {
|
|
985
|
+
subtaskId: string;
|
|
986
|
+
fromId: string;
|
|
987
|
+
output: string;
|
|
988
|
+
status: 'COMPLETED' | 'FAILED' | 'ESCALATED';
|
|
989
|
+
timestamp: string;
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* PeerBus enables T3↔T3 and T2↔T2 communication within a task.
|
|
993
|
+
* Each T2Manager creates one PeerBus and shares it with its T3Workers.
|
|
994
|
+
* T1 creates one PeerBus and shares it with its T2Managers.
|
|
995
|
+
*/
|
|
996
|
+
interface BroadcastMessage {
|
|
997
|
+
fromId: string;
|
|
998
|
+
payload: unknown;
|
|
999
|
+
timestamp: string;
|
|
1000
|
+
}
|
|
1001
|
+
declare class PeerBus extends EventEmitter {
|
|
1002
|
+
private outputs;
|
|
1003
|
+
private waiters;
|
|
1004
|
+
private members;
|
|
1005
|
+
private barriers;
|
|
1006
|
+
private broadcastLog;
|
|
1007
|
+
private fileLocks;
|
|
1008
|
+
register(peerId: string): void;
|
|
1009
|
+
/**
|
|
1010
|
+
* Publish output — unblocks any peers waiting on this subtaskId
|
|
1011
|
+
*/
|
|
1012
|
+
publish(fromId: string, subtaskId: string, output: string, status: PeerOutput['status']): void;
|
|
1013
|
+
/**
|
|
1014
|
+
* Wait for a specific subtask's output — resolves immediately if already available
|
|
1015
|
+
*/
|
|
1016
|
+
waitFor(subtaskId: string, timeoutMs?: number): Promise<PeerOutput>;
|
|
1017
|
+
/**
|
|
1018
|
+
* Get output if already available (non-blocking)
|
|
1019
|
+
*/
|
|
1020
|
+
getOutput(subtaskId: string): PeerOutput | undefined;
|
|
1021
|
+
/**
|
|
1022
|
+
* Broadcast a message to all registered peers except sender.
|
|
1023
|
+
* Also logs to broadcastLog so collect() can retrieve recent broadcasts.
|
|
1024
|
+
*/
|
|
1025
|
+
broadcast(fromId: string, payload: unknown): void;
|
|
1026
|
+
/**
|
|
1027
|
+
* Collect all broadcast messages received within a time window.
|
|
1028
|
+
* Useful for T2 announcement gathering — call immediately after triggering broadcasts.
|
|
1029
|
+
*/
|
|
1030
|
+
collect(timeoutMs: number): Promise<BroadcastMessage[]>;
|
|
1031
|
+
/**
|
|
1032
|
+
* Acquire an exclusive file lock — prevents concurrent T3 writes to the same file.
|
|
1033
|
+
* If the file is already locked, waits until the lock is released.
|
|
1034
|
+
*/
|
|
1035
|
+
lockFile(tierId: string, filePath: string, timeoutMs?: number): Promise<void>;
|
|
1036
|
+
/**
|
|
1037
|
+
* Release a file lock — unblocks the next waiter if any.
|
|
1038
|
+
*/
|
|
1039
|
+
releaseFile(tierId: string, filePath: string): void;
|
|
1040
|
+
/**
|
|
1041
|
+
* Wait until a file lock is released (non-acquiring — just observes).
|
|
1042
|
+
* Used by T3s that want to read after another T3 finishes writing.
|
|
1043
|
+
*/
|
|
1044
|
+
waitForFileRelease(filePath: string, timeoutMs?: number): Promise<void>;
|
|
1045
|
+
/**
|
|
1046
|
+
* Check if a file is currently locked (non-blocking).
|
|
1047
|
+
*/
|
|
1048
|
+
isFileLocked(filePath: string): boolean;
|
|
1049
|
+
/**
|
|
1050
|
+
* Clear broadcast log — call between phases to avoid stale announcements.
|
|
1051
|
+
*/
|
|
1052
|
+
clearBroadcastLog(): void;
|
|
1053
|
+
/**
|
|
1054
|
+
* Send a targeted message to a specific peer
|
|
1055
|
+
*/
|
|
1056
|
+
send(fromId: string, toId: string, syncType: PeerSyncType, subtaskId: string, payload: unknown): void;
|
|
1057
|
+
/**
|
|
1058
|
+
* Barrier — wait until N peers have all reached this point
|
|
1059
|
+
* Useful for fan-in synchronization
|
|
1060
|
+
*/
|
|
1061
|
+
barrier(peerId: string, barrierName: string, totalPeers: number): Promise<void>;
|
|
1062
|
+
getAllOutputs(): PeerOutput[];
|
|
1063
|
+
getMembers(): string[];
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
declare class T2Manager extends BaseTier {
|
|
1067
|
+
private router;
|
|
1068
|
+
private toolRegistry;
|
|
1069
|
+
private assignment?;
|
|
1070
|
+
private t3Workers;
|
|
1071
|
+
private escalations;
|
|
1072
|
+
private peerSyncBuffer;
|
|
1073
|
+
private store?;
|
|
1074
|
+
private t3PeerBus;
|
|
1075
|
+
private t2PeerBus?;
|
|
1076
|
+
private permissionEscalator?;
|
|
1077
|
+
private toolCreator?;
|
|
1078
|
+
setPeerBus(bus: PeerBus): void;
|
|
1079
|
+
constructor(router: CascadeRouter, toolRegistry: ToolRegistry, parentId: string);
|
|
1080
|
+
setStore(store: MemoryStore): void;
|
|
1081
|
+
/**
|
|
1082
|
+
* Inject the shared PermissionEscalator for this task run.
|
|
1083
|
+
* The escalator will also be given this T2's evaluator function.
|
|
1084
|
+
*/
|
|
1085
|
+
setPermissionEscalator(escalator: PermissionEscalator): void;
|
|
1086
|
+
setToolCreator(creator: ToolCreator): void;
|
|
1087
|
+
/**
|
|
1088
|
+
* Phase 1 of T2 peer discussion: broadcast this section's plan so sibling T2s
|
|
1089
|
+
* and T1 can detect overlaps and coordinate execution order.
|
|
1090
|
+
* Called BEFORE execute() begins the agent loop.
|
|
1091
|
+
*/
|
|
1092
|
+
announcePlan(assignment: T1ToT2Assignment): void;
|
|
1093
|
+
/**
|
|
1094
|
+
* Phase 2: After this section completes, share the output with sibling T2s
|
|
1095
|
+
* so they can reference it in their final compilation if relevant.
|
|
1096
|
+
*/
|
|
1097
|
+
shareCompletedOutput(sectionId: string, output: string): void;
|
|
1098
|
+
private extractKeywords;
|
|
1099
|
+
receivePeerSync(fromId: string, content: unknown): void;
|
|
1100
|
+
execute(assignment: T1ToT2Assignment, taskId: string): Promise<T2Result>;
|
|
1101
|
+
private decomposeSection;
|
|
1102
|
+
private executeSubtasks;
|
|
1103
|
+
/**
|
|
1104
|
+
* Runs T3 workers respecting dependsOn declarations.
|
|
1105
|
+
*
|
|
1106
|
+
* Uses Kahn's algorithm for topological ordering:
|
|
1107
|
+
* 1. Build an in-degree map from the dependency graph.
|
|
1108
|
+
* 2. Detect cycles — if any exist, break them by removing the offending edge
|
|
1109
|
+
* and logging a warning (so the run degrades gracefully instead of deadlocking).
|
|
1110
|
+
* 3. Execute workers in waves: start all zero-in-degree tasks in parallel,
|
|
1111
|
+
* then reduce in-degrees of their dependents and repeat.
|
|
1112
|
+
*/
|
|
1113
|
+
private runWithDependencies;
|
|
1114
|
+
/**
|
|
1115
|
+
* Detects cyclic dependencies using Kahn's algorithm and breaks them
|
|
1116
|
+
* by removing back-edges. Returns a sanitized copy of assignments.
|
|
1117
|
+
*
|
|
1118
|
+
* A cycle like t1→t2→t3→t1 is broken at the last edge (t3→t1),
|
|
1119
|
+
* meaning t3 will start without waiting for t1, preventing deadlock.
|
|
1120
|
+
*/
|
|
1121
|
+
private breakCycles;
|
|
1122
|
+
private retryT3;
|
|
1123
|
+
private publishSectionOutput;
|
|
1124
|
+
private aggregateResults;
|
|
1125
|
+
private determineStatus;
|
|
1126
|
+
/**
|
|
1127
|
+
* T2-level permission evaluator.
|
|
1128
|
+
* - Safe / non-dangerous tools: auto-approve via rules (no LLM call).
|
|
1129
|
+
* - Dangerous tools: ask T2's LLM whether the action fits the section goal.
|
|
1130
|
+
* - Returns null if the LLM is uncertain (triggers T1 evaluation).
|
|
1131
|
+
*/
|
|
1132
|
+
private evaluatePermissionAtT2;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
declare class T3Worker extends BaseTier {
|
|
1136
|
+
private router;
|
|
1137
|
+
private toolRegistry;
|
|
1138
|
+
private context;
|
|
1139
|
+
private assignment?;
|
|
1140
|
+
private peerSyncBuffer;
|
|
1141
|
+
private store?;
|
|
1142
|
+
private audit?;
|
|
1143
|
+
private tools;
|
|
1144
|
+
/** @deprecated — kept only as fallback when no escalator is attached */
|
|
1145
|
+
private sessionApprovals;
|
|
1146
|
+
private peerBus?;
|
|
1147
|
+
private permissionEscalator?;
|
|
1148
|
+
private toolCreator?;
|
|
1149
|
+
setPeerBus(bus: PeerBus): void;
|
|
1150
|
+
setPermissionEscalator(escalator: PermissionEscalator): void;
|
|
1151
|
+
setToolCreator(creator: ToolCreator): void;
|
|
1152
|
+
constructor(router: CascadeRouter, toolRegistry: ToolRegistry, parentId: string);
|
|
1153
|
+
setStore(store: MemoryStore, sessionId: string): void;
|
|
1154
|
+
execute(assignment: T2ToT3Assignment, taskId: string): Promise<T3Result>;
|
|
1155
|
+
sendToPeer(toId: string, content: unknown): void;
|
|
1156
|
+
requestFromPeer(peerId: string, subtaskId: string): Promise<string>;
|
|
1157
|
+
syncWithPeers(barrierName: string): Promise<void>;
|
|
1158
|
+
receivePeerSync(fromId: string, content: unknown): void;
|
|
1159
|
+
private runAgentLoop;
|
|
1160
|
+
private executeTool;
|
|
1161
|
+
/**
|
|
1162
|
+
* Announce which files this T3 plans to edit, then acquire locks on them
|
|
1163
|
+
* before competing siblings can claim them. T3s working on different files
|
|
1164
|
+
* proceed in full parallel; T3s on the same file serialize automatically.
|
|
1165
|
+
*/
|
|
1166
|
+
private coordinateFileIntents;
|
|
1167
|
+
private requiresArtifact;
|
|
1168
|
+
private extractArtifactPaths;
|
|
1169
|
+
private verifyArtifacts;
|
|
1170
|
+
private selfTest;
|
|
1171
|
+
private correctOutput;
|
|
1172
|
+
private buildSystemPrompt;
|
|
1173
|
+
private buildInitialPrompt;
|
|
1174
|
+
private buildResult;
|
|
1175
|
+
private isFileOperation;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
/**
|
|
1179
|
+
* High-level SDK entry point — loads config from the workspace and runs a task.
|
|
1180
|
+
*
|
|
1181
|
+
* Cascade automatically determines complexity and routes to the appropriate
|
|
1182
|
+
* tier: T3 (simple), T2 (moderate), or the full T1→T2→T3 hierarchy (complex).
|
|
1183
|
+
*
|
|
1184
|
+
* @param prompt - The natural-language task description to execute.
|
|
1185
|
+
* @param options - Optional overrides for run behavior, approval callbacks, etc.
|
|
1186
|
+
* @returns A resolved `CascadeRunResult` with the final output and metadata.
|
|
1187
|
+
*
|
|
1188
|
+
* @example
|
|
1189
|
+
* const result = await runCascade('Refactor the auth module to use JWT', {
|
|
1190
|
+
* workspacePath: '/path/to/my/project',
|
|
1191
|
+
* streamCallback: (chunk) => process.stdout.write(chunk.text ?? ''),
|
|
1192
|
+
* approvalCallback: async (req) => {
|
|
1193
|
+
* console.log(`Approve ${req.toolName}?`);
|
|
1194
|
+
* return { approved: true, always: false };
|
|
1195
|
+
* },
|
|
1196
|
+
* });
|
|
1197
|
+
* console.log(result.output);
|
|
1198
|
+
*/
|
|
1199
|
+
declare function runCascade(prompt: string, options?: Partial<CascadeRunOptions> & {
|
|
1200
|
+
workspacePath?: string;
|
|
1201
|
+
config?: Partial<CascadeConfig>;
|
|
1202
|
+
}): Promise<CascadeRunResult>;
|
|
1203
|
+
/**
|
|
1204
|
+
* Create a `Cascade` instance with custom config — no filesystem config loading.
|
|
1205
|
+
*
|
|
1206
|
+
* Use this in environments where you control the config programmatically
|
|
1207
|
+
* (e.g. tests, serverless, embedded usage).
|
|
1208
|
+
*
|
|
1209
|
+
* @param config - Partial config; missing fields use schema defaults.
|
|
1210
|
+
* @param workspacePath - Workspace root (default: `process.cwd()`).
|
|
1211
|
+
*
|
|
1212
|
+
* @example
|
|
1213
|
+
* const cascade = createCascade({
|
|
1214
|
+
* providers: [{ type: 'anthropic', apiKey: process.env.ANTHROPIC_KEY }],
|
|
1215
|
+
* });
|
|
1216
|
+
* cascade.on('stream:token', (e) => process.stdout.write(e.text));
|
|
1217
|
+
* await cascade.init();
|
|
1218
|
+
* await cascade.run({ prompt: 'Hello world', workspacePath: '.' });
|
|
1219
|
+
*/
|
|
1220
|
+
declare function createCascade(config: Partial<CascadeConfig>, workspacePath?: string): Cascade;
|
|
1221
|
+
/**
|
|
1222
|
+
* Convenience streaming helper — runs a task and delivers tokens via callback.
|
|
1223
|
+
*
|
|
1224
|
+
* @param prompt - The task to execute.
|
|
1225
|
+
* @param onToken - Called with each streamed text token as it arrives.
|
|
1226
|
+
* @param options - Same options as `runCascade`.
|
|
1227
|
+
*
|
|
1228
|
+
* @example
|
|
1229
|
+
* await streamCascade('Summarize this codebase', (token) => {
|
|
1230
|
+
* process.stdout.write(token);
|
|
1231
|
+
* });
|
|
1232
|
+
*/
|
|
1233
|
+
declare function streamCascade(prompt: string, onToken: (text: string) => void, options?: Partial<CascadeRunOptions> & {
|
|
1234
|
+
workspacePath?: string;
|
|
1235
|
+
}): Promise<CascadeRunResult>;
|
|
1236
|
+
|
|
1237
|
+
/**
|
|
1238
|
+
* Keystore with two backends:
|
|
1239
|
+
* 1. OS keychain via `keytar` — preferred when available (macOS Keychain,
|
|
1240
|
+
* Windows Credential Vault, libsecret). No master password required.
|
|
1241
|
+
* 2. AES-256-GCM encrypted file — used when keytar is unavailable or the
|
|
1242
|
+
* caller passes `{ forceFile: true }`. Requires a master password.
|
|
1243
|
+
*
|
|
1244
|
+
* On first successful keytar unlock we silently migrate any existing AES
|
|
1245
|
+
* entries into the OS keychain. The AES file is left in place as a backup
|
|
1246
|
+
* until the user explicitly deletes it via `cascade keys migrate --confirm`.
|
|
1247
|
+
*/
|
|
1248
|
+
declare class Keystore {
|
|
1249
|
+
private storePath;
|
|
1250
|
+
private masterKey;
|
|
1251
|
+
private keytar;
|
|
1252
|
+
private cache;
|
|
1253
|
+
private backend;
|
|
1254
|
+
constructor(storePath: string);
|
|
1255
|
+
/**
|
|
1256
|
+
* Unlock the keystore.
|
|
1257
|
+
*
|
|
1258
|
+
* If `password` is omitted we try keytar only. If keytar is unavailable and
|
|
1259
|
+
* there are AES entries to read, unlock will fail — re-call with a password
|
|
1260
|
+
* to decrypt the file backend.
|
|
1261
|
+
*/
|
|
1262
|
+
unlock(password?: string, opts?: {
|
|
1263
|
+
forceFile?: boolean;
|
|
1264
|
+
}): Promise<void>;
|
|
1265
|
+
/** Synchronous legacy unlock kept for AES-only environments. */
|
|
1266
|
+
unlockSync(password: string): void;
|
|
1267
|
+
lock(): void;
|
|
1268
|
+
isUnlocked(): boolean;
|
|
1269
|
+
/** Report the active backend (`keytar` or `file`) for diagnostics. */
|
|
1270
|
+
getBackend(): 'keytar' | 'file' | null;
|
|
1271
|
+
set(key: string, value: string): Promise<void>;
|
|
1272
|
+
get(key: string): string | undefined;
|
|
1273
|
+
delete(key: string): Promise<void>;
|
|
1274
|
+
listKeys(): string[];
|
|
1275
|
+
private assertUnlocked;
|
|
1276
|
+
private decryptFile;
|
|
1277
|
+
private saveAll;
|
|
1278
|
+
private writeWithSalt;
|
|
1279
|
+
private readRaw;
|
|
1280
|
+
private deriveKey;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
declare class CascadeIgnore {
|
|
1284
|
+
private ig;
|
|
1285
|
+
private loaded;
|
|
1286
|
+
constructor();
|
|
1287
|
+
load(workspacePath: string): Promise<void>;
|
|
1288
|
+
isIgnored(filePath: string, workspacePath?: string): boolean;
|
|
1289
|
+
getPatterns(): string[];
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
interface CascadeMdContent {
|
|
1293
|
+
raw: string;
|
|
1294
|
+
sections: Record<string, string>;
|
|
1295
|
+
systemPrompt: string;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
declare class ConfigManager {
|
|
1299
|
+
private config;
|
|
1300
|
+
private keystore;
|
|
1301
|
+
private ignore;
|
|
1302
|
+
private store;
|
|
1303
|
+
private cascadeMd;
|
|
1304
|
+
private workspacePath;
|
|
1305
|
+
private globalDir;
|
|
1306
|
+
constructor(workspacePath?: string);
|
|
1307
|
+
load(): Promise<void>;
|
|
1308
|
+
getConfig(): CascadeConfig;
|
|
1309
|
+
getKeystore(): Keystore;
|
|
1310
|
+
getIgnore(): CascadeIgnore;
|
|
1311
|
+
getStore(): MemoryStore;
|
|
1312
|
+
getCascadeMd(): CascadeMdContent | null;
|
|
1313
|
+
getWorkspacePath(): string;
|
|
1314
|
+
save(): Promise<void>;
|
|
1315
|
+
updateConfig(updates: Partial<CascadeConfig>): Promise<void>;
|
|
1316
|
+
getApiKey(provider: string): string | undefined;
|
|
1317
|
+
private loadConfig;
|
|
1318
|
+
private injectEnvKeys;
|
|
1319
|
+
private ensureDefaultIdentity;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
interface DashboardSocketOptions {
|
|
1323
|
+
authRequired: boolean;
|
|
1324
|
+
secret: string;
|
|
1325
|
+
corsOrigin?: string | string[];
|
|
1326
|
+
}
|
|
1327
|
+
declare class DashboardSocket {
|
|
1328
|
+
private io;
|
|
1329
|
+
private authRequired;
|
|
1330
|
+
private secret;
|
|
1331
|
+
constructor(httpServer: Server, options: DashboardSocketOptions);
|
|
1332
|
+
broadcastToRoom(room: string, event: string, data: unknown): void;
|
|
1333
|
+
broadcast(event: string, data: unknown): void;
|
|
1334
|
+
emitCascadeEvent(ev: CascadeEvent): void;
|
|
1335
|
+
emitTierStatus(tierId: string, role: string, status: string, sessionId: string, action?: string): void;
|
|
1336
|
+
emitStreamToken(tierId: string, text: string, sessionId: string): void;
|
|
1337
|
+
emitApprovalRequest(request: PermissionRequest): void;
|
|
1338
|
+
onApprovalResponse(callback: (data: PermissionDecisionPayload) => void): void;
|
|
1339
|
+
private setupHandlers;
|
|
1340
|
+
close(): void;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
declare class DashboardServer {
|
|
1344
|
+
private app;
|
|
1345
|
+
private httpServer;
|
|
1346
|
+
private socket;
|
|
1347
|
+
private config;
|
|
1348
|
+
private dashboardSecret;
|
|
1349
|
+
private store;
|
|
1350
|
+
private globalStore;
|
|
1351
|
+
private broadcastTimer;
|
|
1352
|
+
private port;
|
|
1353
|
+
private workspacePath;
|
|
1354
|
+
constructor(config: CascadeConfig, store: MemoryStore, workspacePath?: string);
|
|
1355
|
+
start(): Promise<void>;
|
|
1356
|
+
stop(): Promise<void>;
|
|
1357
|
+
getSocket(): DashboardSocket;
|
|
1358
|
+
/**
|
|
1359
|
+
* Produce a stable dashboard JWT signing secret.
|
|
1360
|
+
*
|
|
1361
|
+
* Order of precedence: explicit config → env var → secret file on disk
|
|
1362
|
+
* (auto-created with 0600 perms). Previously this generated a fresh UUID
|
|
1363
|
+
* on every process start which invalidated all outstanding JWTs.
|
|
1364
|
+
*/
|
|
1365
|
+
private resolveDashboardSecret;
|
|
1366
|
+
/**
|
|
1367
|
+
* Resolve the dashboard password as a bcrypt hash.
|
|
1368
|
+
* Accepts either a pre-hashed `CASCADE_DASHBOARD_PASSWORD_HASH` or a plain
|
|
1369
|
+
* `CASCADE_DASHBOARD_PASSWORD` which is hashed once at startup.
|
|
1370
|
+
*/
|
|
1371
|
+
private resolvePasswordHash;
|
|
1372
|
+
private getGlobalStore;
|
|
1373
|
+
private throttledBroadcast;
|
|
1374
|
+
private broadcastRuntime;
|
|
1375
|
+
private broadcastSessionDetails;
|
|
1376
|
+
watchRuntimeChanges(): void;
|
|
1377
|
+
refreshRuntime(scope?: 'workspace' | 'global'): void;
|
|
1378
|
+
private setupMiddleware;
|
|
1379
|
+
private setupRoutes;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
type TaskRunner = (task: ScheduledTask) => Promise<void>;
|
|
1383
|
+
declare class TaskScheduler {
|
|
1384
|
+
private cronJobs;
|
|
1385
|
+
private store;
|
|
1386
|
+
private runner;
|
|
1387
|
+
constructor(store: MemoryStore, runner: TaskRunner);
|
|
1388
|
+
start(): void;
|
|
1389
|
+
stop(): void;
|
|
1390
|
+
schedule(task: ScheduledTask): void;
|
|
1391
|
+
unschedule(taskId: string): void;
|
|
1392
|
+
add(task: ScheduledTask): void;
|
|
1393
|
+
remove(taskId: string): void;
|
|
1394
|
+
list(): ScheduledTask[];
|
|
1395
|
+
isRunning(taskId: string): boolean;
|
|
1396
|
+
static validateCron(expression: string): boolean;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
declare class HooksRunner {
|
|
1400
|
+
private config;
|
|
1401
|
+
constructor(config: HooksConfig);
|
|
1402
|
+
runPreToolUse(toolName: string, input: Record<string, unknown>): Promise<HookResult>;
|
|
1403
|
+
runPostToolUse(toolName: string, output: string): Promise<HookResult>;
|
|
1404
|
+
runPreTask(prompt: string): Promise<HookResult>;
|
|
1405
|
+
runPostTask(output: string, durationMs: number): Promise<HookResult>;
|
|
1406
|
+
private runHooks;
|
|
1407
|
+
}
|
|
1408
|
+
interface HookResult {
|
|
1409
|
+
success: boolean;
|
|
1410
|
+
output: string;
|
|
1411
|
+
error?: string;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
/** Structured log levels for audit entries */
|
|
1415
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
1416
|
+
/** Extended audit entry with log level */
|
|
1417
|
+
interface AuditEntryWithLevel extends AuditEntry {
|
|
1418
|
+
level: LogLevel;
|
|
1419
|
+
}
|
|
1420
|
+
declare class AuditLogger {
|
|
1421
|
+
private store;
|
|
1422
|
+
private sessionId;
|
|
1423
|
+
private minLevel;
|
|
1424
|
+
constructor(store: MemoryStore, sessionId: string, minLevel?: LogLevel);
|
|
1425
|
+
debug(tierId: string, action: AuditEntry['action'], details: Record<string, unknown>): void;
|
|
1426
|
+
info(tierId: string, action: AuditEntry['action'], details: Record<string, unknown>): void;
|
|
1427
|
+
warn(tierId: string, action: AuditEntry['action'], details: Record<string, unknown>): void;
|
|
1428
|
+
error(tierId: string, err: Error | unknown, context?: Record<string, unknown>): void;
|
|
1429
|
+
/** Backward-compatible generic log (defaults to info level) */
|
|
1430
|
+
log(tierId: string, action: AuditEntry['action'], details: Record<string, unknown>): void;
|
|
1431
|
+
toolCall(tierId: string, toolName: string, input: Record<string, unknown>): void;
|
|
1432
|
+
fileChange(tierId: string, filePath: string, operation: string): void;
|
|
1433
|
+
agentDecision(tierId: string, decision: string, reasoning?: string): void;
|
|
1434
|
+
approval(tierId: string, toolName: string, approved: boolean, decidedBy?: string): void;
|
|
1435
|
+
escalation(tierId: string, blocker: string, needs: string): void;
|
|
1436
|
+
getLog(limit?: number): AuditEntry[];
|
|
1437
|
+
/**
|
|
1438
|
+
* Formats an audit entry as a single-line JSON string (e.g. for log aggregation).
|
|
1439
|
+
*/
|
|
1440
|
+
formatStructured(entry: AuditEntryWithLevel): string;
|
|
1441
|
+
private logAt;
|
|
1442
|
+
private shouldLog;
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
interface TelemetryEvents {
|
|
1446
|
+
'cascade:session_start': {
|
|
1447
|
+
complexity?: TaskComplexity;
|
|
1448
|
+
providerCount: number;
|
|
1449
|
+
cascadeAutoEnabled: boolean;
|
|
1450
|
+
toolCreationEnabled: boolean;
|
|
1451
|
+
};
|
|
1452
|
+
'cascade:session_end': {
|
|
1453
|
+
durationMs: number;
|
|
1454
|
+
taskCount: number;
|
|
1455
|
+
totalTokens: number;
|
|
1456
|
+
totalCostUsd: number;
|
|
1457
|
+
};
|
|
1458
|
+
'cascade:task_complete': {
|
|
1459
|
+
complexity: TaskComplexity;
|
|
1460
|
+
tier: TierRole | 'simple';
|
|
1461
|
+
durationMs: number;
|
|
1462
|
+
tokenCount: number;
|
|
1463
|
+
costUsd: number;
|
|
1464
|
+
t2Count: number;
|
|
1465
|
+
t3Count: number;
|
|
1466
|
+
};
|
|
1467
|
+
'cascade:task_failed': {
|
|
1468
|
+
tier: TierRole;
|
|
1469
|
+
errorType: string;
|
|
1470
|
+
complexity?: TaskComplexity;
|
|
1471
|
+
};
|
|
1472
|
+
'cascade:tool_executed': {
|
|
1473
|
+
toolName: string;
|
|
1474
|
+
tier: TierRole;
|
|
1475
|
+
success: boolean;
|
|
1476
|
+
durationMs?: number;
|
|
1477
|
+
};
|
|
1478
|
+
'cascade:tool_created': {
|
|
1479
|
+
name: string;
|
|
1480
|
+
description: string;
|
|
1481
|
+
};
|
|
1482
|
+
'cascade:model_selected': {
|
|
1483
|
+
tier: TierRole;
|
|
1484
|
+
modelId: string;
|
|
1485
|
+
provider: string;
|
|
1486
|
+
reason: 'config_override' | 'cascade_auto' | 'priority_list' | 'fallback';
|
|
1487
|
+
complexity?: number;
|
|
1488
|
+
};
|
|
1489
|
+
'cascade:peer_sync': {
|
|
1490
|
+
syncType: string;
|
|
1491
|
+
tier: 'T2' | 'T3';
|
|
1492
|
+
participantCount: number;
|
|
1493
|
+
};
|
|
1494
|
+
'cascade:escalation': {
|
|
1495
|
+
fromTier: TierRole;
|
|
1496
|
+
toTier: TierRole | 'user';
|
|
1497
|
+
toolName: string;
|
|
1498
|
+
approved: boolean;
|
|
1499
|
+
};
|
|
1500
|
+
'cascade:provider_failover': {
|
|
1501
|
+
from: string;
|
|
1502
|
+
to: string;
|
|
1503
|
+
reason: string;
|
|
1504
|
+
};
|
|
1505
|
+
'cascade:t2_overlap_detected': {
|
|
1506
|
+
sectionCount: number;
|
|
1507
|
+
overlapCount: number;
|
|
1508
|
+
switchedToSequential: boolean;
|
|
1509
|
+
};
|
|
1510
|
+
'cascade:file_lock_contention': {
|
|
1511
|
+
filePath: string;
|
|
1512
|
+
waitMs: number;
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
type TelemetryEventName = keyof TelemetryEvents;
|
|
1516
|
+
declare class Telemetry {
|
|
1517
|
+
private client;
|
|
1518
|
+
private enabled;
|
|
1519
|
+
private distinctId;
|
|
1520
|
+
constructor(config: TelemetryConfig, distinctId: string);
|
|
1521
|
+
private init;
|
|
1522
|
+
/**
|
|
1523
|
+
* Capture a typed telemetry event. Silently no-ops if telemetry is disabled.
|
|
1524
|
+
*/
|
|
1525
|
+
capture<E extends TelemetryEventName>(event: E, properties: TelemetryEvents[E]): void;
|
|
1526
|
+
capture(event: string, properties?: Record<string, unknown>): void;
|
|
1527
|
+
shutdown(): Promise<void>;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
declare const CASCADE_VERSION = "0.1.2";
|
|
1531
|
+
declare const CASCADE_CONFIG_DIR = ".cascade";
|
|
1532
|
+
declare const CASCADE_MD_FILE = "CASCADE.md";
|
|
1533
|
+
declare const CASCADE_IGNORE_FILE = ".cascadeignore";
|
|
1534
|
+
declare const CASCADE_CONFIG_FILE = ".cascade/config.json";
|
|
1535
|
+
declare const CASCADE_KEYSTORE_FILE = ".cascade/keystore.enc";
|
|
1536
|
+
declare const CASCADE_AUDIT_FILE = ".cascade/audit.log";
|
|
1537
|
+
declare const CASCADE_DB_FILE = ".cascade/memory.db";
|
|
1538
|
+
declare const CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
|
|
1539
|
+
declare const GLOBAL_CONFIG_DIR = ".cascade-ai";
|
|
1540
|
+
declare const GLOBAL_DB_FILE = "memory.db";
|
|
1541
|
+
declare const GLOBAL_KEYSTORE_FILE = "keystore.enc";
|
|
1542
|
+
declare const GLOBAL_RUNTIME_DB_FILE = "runtime.db";
|
|
1543
|
+
declare const DEFAULT_DASHBOARD_PORT = 4891;
|
|
1544
|
+
declare const DEFAULT_API_PORT = 4892;
|
|
1545
|
+
declare const DEFAULT_CONTEXT_LIMIT = 200000;
|
|
1546
|
+
declare const DEFAULT_AUTO_SUMMARIZE_AT = 150000;
|
|
1547
|
+
declare const DEFAULT_MAX_SESSION_MESSAGES = 1000;
|
|
1548
|
+
declare const DEFAULT_RETENTION_DAYS = 90;
|
|
1549
|
+
declare const MODELS: Record<string, ModelInfo>;
|
|
1550
|
+
declare const T1_MODEL_PRIORITY: string[];
|
|
1551
|
+
declare const T2_MODEL_PRIORITY: string[];
|
|
1552
|
+
declare const T3_MODEL_PRIORITY: string[];
|
|
1553
|
+
declare const VISION_MODEL_PRIORITY: string[];
|
|
1554
|
+
declare const COMPLEXITY_T2_COUNT: Record<string, [number, number]>;
|
|
1555
|
+
declare const THEME_NAMES: ThemeName[];
|
|
1556
|
+
declare const DEFAULT_THEME: ThemeName;
|
|
1557
|
+
declare const OLLAMA_BASE_URL = "http://localhost:11434";
|
|
1558
|
+
declare const LM_STUDIO_BASE_URL = "http://localhost:1234";
|
|
1559
|
+
declare const AZURE_BASE_URL_TEMPLATE = "https://{resource}.openai.azure.com";
|
|
1560
|
+
declare const TOOL_NAMES: {
|
|
1561
|
+
readonly SHELL: "shell";
|
|
1562
|
+
readonly FILE_READ: "file_read";
|
|
1563
|
+
readonly FILE_WRITE: "file_write";
|
|
1564
|
+
readonly FILE_EDIT: "file_edit";
|
|
1565
|
+
readonly FILE_DELETE: "file_delete";
|
|
1566
|
+
readonly FILE_LIST: "file_list";
|
|
1567
|
+
readonly GIT: "git";
|
|
1568
|
+
readonly GITHUB: "github";
|
|
1569
|
+
readonly BROWSER: "browser";
|
|
1570
|
+
readonly IMAGE_ANALYZE: "image_analyze";
|
|
1571
|
+
readonly PDF_CREATE: "pdf_create";
|
|
1572
|
+
readonly RUN_CODE: "run_code";
|
|
1573
|
+
readonly PEER_MESSAGE: "peer_message";
|
|
1574
|
+
};
|
|
1575
|
+
declare const DEFAULT_APPROVAL_REQUIRED: string[];
|
|
1576
|
+
declare const PROVIDER_DISPLAY_NAMES: Record<ProviderType, string>;
|
|
1577
|
+
|
|
1578
|
+
export { AZURE_BASE_URL_TEMPLATE, type ApprovalRequest, type ApprovalResponse, type AuditEntry, AuditLogger, type BudgetConfig, CASCADE_AUDIT_FILE, CASCADE_CONFIG_DIR, CASCADE_CONFIG_FILE, CASCADE_DASHBOARD_SECRET_FILE, CASCADE_DB_FILE, CASCADE_IGNORE_FILE, CASCADE_KEYSTORE_FILE, CASCADE_MD_FILE, CASCADE_VERSION, COMPLEXITY_T2_COUNT, Cascade, type CascadeConfig, type CascadeEvent, type CascadeEventType, CascadeIgnore, type CascadeMessage, CascadeRouter, type CascadeRunOptions, type CascadeRunResult, ConfigManager, type ConversationMessage, DEFAULT_API_PORT, DEFAULT_APPROVAL_REQUIRED, DEFAULT_AUTO_SUMMARIZE_AT, DEFAULT_CONTEXT_LIMIT, DEFAULT_DASHBOARD_PORT, DEFAULT_MAX_SESSION_MESSAGES, DEFAULT_RETENTION_DAYS, DEFAULT_THEME, type DashboardConfig, DashboardServer, type EscalationPayload, GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_RUNTIME_DB_FILE, type GenerateOptions, type GenerateResult, type HookDefinition, type HooksConfig, HooksRunner, type Identity, type ImageAttachment, Keystore, LM_STUDIO_BASE_URL, MODELS, McpClient, type McpServerConfig$1 as McpServerConfig, type MemoryConfig, MemoryStore, type Message, type MessageContent, type MessagePayload, type MessageStatus, type MessageType, type ModelInfo, type ModelOverrides, OLLAMA_BASE_URL, PROVIDER_DISPLAY_NAMES, type PeerMessage, type PeerSyncPayload, type PeerSyncType, type PermissionDecision, type PermissionDecisionPayload, type PermissionRequest, type ProviderConfig, type ProviderType, type RuntimeNode, type RuntimeNodeLog, type RuntimeRefreshPayload, type RuntimeScope, type RuntimeSession, type RuntimeSnapshotPayload, type ScheduledTask, type Session, type SessionCheckpoint, type SessionMetadata, type SessionSubscriptionPayload, type StatusUpdate, type StoredMessage, type StreamChunk, T1Administrator, type T1ToT2Assignment, T1_MODEL_PRIORITY, T2Manager, type T2Result, type T2ToT3Assignment, T2_MODEL_PRIORITY, type T3Result, type T3ResultPayload, type T3SubtaskSpec, T3Worker, T3_MODEL_PRIORITY, THEME_NAMES, TOOL_NAMES, type TaskComplexity, TaskScheduler, Telemetry, type TelemetryConfig, type Theme, type ThemeColors, type ThemeName, type TierConfig, type TierLimits, type TierRole, type TierStatus, type TokenUsage, type ToolCall, type ToolDefinition, type ToolExecuteOptions, ToolRegistry, type ToolResult, type ToolsConfig, VISION_MODEL_PRIORITY, type WebhookConfig, type WorkspaceConfig, createCascade, runCascade, streamCascade };
|