cascade-ai 0.13.1 → 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/README.md +6 -0
- package/dist/cli.cjs +117 -36
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +117 -36
- package/dist/cli.js.map +1 -1
- package/dist/desktop-core.cjs +117 -36
- package/dist/index.cjs +117 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -2
- package/dist/index.d.ts +59 -2
- package/dist/index.js +117 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -236,7 +236,7 @@ var init_audit_logger = __esm({
|
|
|
236
236
|
});
|
|
237
237
|
|
|
238
238
|
// src/constants.ts
|
|
239
|
-
var CASCADE_VERSION = "0.13.
|
|
239
|
+
var CASCADE_VERSION = "0.13.2";
|
|
240
240
|
var CASCADE_CONFIG_DIR = ".cascade";
|
|
241
241
|
var CASCADE_MD_FILE = "CASCADE.md";
|
|
242
242
|
var CASCADE_IGNORE_FILE = ".cascadeignore";
|
|
@@ -3169,6 +3169,13 @@ var BaseTier = class extends EventEmitter__default.default {
|
|
|
3169
3169
|
hierarchyContext = "";
|
|
3170
3170
|
/** Propagated AbortSignal — set by the tier's `execute()` before work begins. */
|
|
3171
3171
|
signal;
|
|
3172
|
+
/**
|
|
3173
|
+
* True for the run's ROOT tier (T3 for Simple, T2 for Moderate, T1 for
|
|
3174
|
+
* Complex). Its own synthesis stream is the user-facing answer and is
|
|
3175
|
+
* tagged `primary` so the desktop renders it live — background workers,
|
|
3176
|
+
* which would interleave, are not tagged.
|
|
3177
|
+
*/
|
|
3178
|
+
isPresenter = false;
|
|
3172
3179
|
constructor(role, id, parentId) {
|
|
3173
3180
|
super();
|
|
3174
3181
|
this.role = role;
|
|
@@ -3176,6 +3183,10 @@ var BaseTier = class extends EventEmitter__default.default {
|
|
|
3176
3183
|
this.parentId = parentId;
|
|
3177
3184
|
this.label = this.id;
|
|
3178
3185
|
}
|
|
3186
|
+
/** Mark this tier as the run's presenter (root tier). */
|
|
3187
|
+
setPresenter(on = true) {
|
|
3188
|
+
this.isPresenter = on;
|
|
3189
|
+
}
|
|
3179
3190
|
getStatus() {
|
|
3180
3191
|
return this.status;
|
|
3181
3192
|
}
|
|
@@ -3910,7 +3921,7 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : "") + textToolSuffix,
|
|
|
3910
3921
|
"T3",
|
|
3911
3922
|
options,
|
|
3912
3923
|
(chunk) => {
|
|
3913
|
-
this.emit("stream:token", { tierId: this.id, text: chunk.text });
|
|
3924
|
+
this.emit("stream:token", { tierId: this.id, text: chunk.text, primary: this.isPresenter });
|
|
3914
3925
|
}
|
|
3915
3926
|
);
|
|
3916
3927
|
let effectiveToolCalls = result.toolCalls ?? [];
|
|
@@ -5371,6 +5382,7 @@ ${peerOutputs}` : "";
|
|
|
5371
5382
|
chunkEnd++;
|
|
5372
5383
|
}
|
|
5373
5384
|
i = chunkEnd;
|
|
5385
|
+
const isLastChunk = chunkEnd >= completed.length;
|
|
5374
5386
|
const prompt = `Summarize these T3 worker outputs for section "${assignment.sectionTitle}" in 2-3 sentences.
|
|
5375
5387
|
${currentSummary ? `
|
|
5376
5388
|
PREVIOUS SUMMARY SO FAR:
|
|
@@ -5380,6 +5392,7 @@ NEW OUTPUTS TO INTEGRATE:
|
|
|
5380
5392
|
` : "\nOUTPUTS:\n"}${chunkText}${peerContext}`;
|
|
5381
5393
|
const messages = [{ role: "user", content: prompt }];
|
|
5382
5394
|
try {
|
|
5395
|
+
const streamFinal = isLastChunk && this.isPresenter ? (chunk) => this.emit("stream:token", { tierId: this.id, text: chunk.text, primary: true }) : void 0;
|
|
5383
5396
|
const result = await this.router.generate("T2", {
|
|
5384
5397
|
messages,
|
|
5385
5398
|
systemPrompt: this.systemPromptOverride + "You are a T2 Manager. Summarize the work of your T3 workers succinctly." + (this.hierarchyContext ? `
|
|
@@ -5387,7 +5400,7 @@ NEW OUTPUTS TO INTEGRATE:
|
|
|
5387
5400
|
HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
5388
5401
|
maxTokens: 500,
|
|
5389
5402
|
...this.sectionModel ? { model: this.sectionModel } : {}
|
|
5390
|
-
});
|
|
5403
|
+
}, streamFinal);
|
|
5391
5404
|
currentSummary = result.content;
|
|
5392
5405
|
} catch (err) {
|
|
5393
5406
|
this.log(`aggregateResults: LLM summarization failed at chunk \u2014 returning raw T3 outputs. Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -5439,14 +5452,11 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
5439
5452
|
...this.sectionModel ? { model: this.sectionModel } : {}
|
|
5440
5453
|
});
|
|
5441
5454
|
const answer = result.content.trim().toUpperCase();
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
}
|
|
5445
|
-
if (answer.includes("NO")) {
|
|
5446
|
-
return { requestId: req.id, approved: false, always: true, decidedBy: "T2", reasoning: "T2 LLM evaluated: inconsistent with section goal" };
|
|
5447
|
-
}
|
|
5455
|
+
const verdict = answer.includes("YES") ? "approve" : answer.includes("NO") ? "deny" : "unsure";
|
|
5456
|
+
(req.trail ??= []).push({ tier: "T2", verdict, reason: `T2: ${verdict === "approve" ? "consistent with section goal" : verdict === "deny" ? "inconsistent with section goal" : "unsure"}` });
|
|
5448
5457
|
return null;
|
|
5449
5458
|
} catch {
|
|
5459
|
+
(req.trail ??= []).push({ tier: "T2", verdict: "unsure", reason: "T2 evaluation failed" });
|
|
5450
5460
|
return null;
|
|
5451
5461
|
}
|
|
5452
5462
|
}
|
|
@@ -6125,7 +6135,7 @@ Instructions:
|
|
|
6125
6135
|
systemPrompt: this.systemPromptOverride + "You are a final output compiler. Summarize and format the task results clearly.",
|
|
6126
6136
|
maxTokens: 8e3
|
|
6127
6137
|
}, (chunk) => {
|
|
6128
|
-
this.emit("stream:token", { tierId: this.id, text: chunk.text });
|
|
6138
|
+
this.emit("stream:token", { tierId: this.id, text: chunk.text, primary: this.isPresenter });
|
|
6129
6139
|
});
|
|
6130
6140
|
return result.content;
|
|
6131
6141
|
}
|
|
@@ -6154,14 +6164,11 @@ Reply with exactly one word: YES, NO, or UNSURE.
|
|
|
6154
6164
|
temperature: 0
|
|
6155
6165
|
});
|
|
6156
6166
|
const answer = result.content.trim().toUpperCase();
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
}
|
|
6160
|
-
if (answer.includes("NO")) {
|
|
6161
|
-
return { requestId: req.id, approved: false, always: true, decidedBy: "T1", reasoning: "T1 evaluated: not consistent with overall task goal" };
|
|
6162
|
-
}
|
|
6167
|
+
const verdict = answer.includes("YES") ? "approve" : answer.includes("NO") ? "deny" : "unsure";
|
|
6168
|
+
(req.trail ??= []).push({ tier: "T1", verdict, reason: `T1: ${verdict === "approve" ? "consistent with overall task goal" : verdict === "deny" ? "not consistent with overall task goal" : "unsure"}` });
|
|
6163
6169
|
return null;
|
|
6164
6170
|
} catch {
|
|
6171
|
+
(req.trail ??= []).push({ tier: "T1", verdict: "unsure", reason: "T1 evaluation failed" });
|
|
6165
6172
|
return null;
|
|
6166
6173
|
}
|
|
6167
6174
|
}
|
|
@@ -8268,6 +8275,8 @@ var CascadeConfigSchema = zod.z.object({
|
|
|
8268
8275
|
privacy: zod.z.object({
|
|
8269
8276
|
paths: zod.z.array(zod.z.object({ pattern: zod.z.string().min(1), policy: zod.z.enum(["local-only"]) })).default([])
|
|
8270
8277
|
}).optional(),
|
|
8278
|
+
/** Routing controls — forceTier pins the root tier, bypassing the classifier. */
|
|
8279
|
+
routing: zod.z.object({ forceTier: zod.z.enum(["auto", "T1", "T2", "T3"]).default("auto") }).optional(),
|
|
8271
8280
|
/**
|
|
8272
8281
|
* T3→T2 reinforcement: when enabled, a worker that discovers its subtask should
|
|
8273
8282
|
* fan out can call the `request_workers` tool to have its T2 manager spawn
|
|
@@ -9542,6 +9551,21 @@ ${last.partialOutput}` : "");
|
|
|
9542
9551
|
const producesArtifact = /\b(?:create|build|implement|generate|write|refactor|rewrite|add|fix|deploy|install|migrate|scaffold|set up|save (?:a|the)|report|\.(?:pdf|md|txt|json|csv|py|js|ts|tsx|jsx|html|docx?))\b/i.test(p);
|
|
9543
9552
|
return inquiry && !producesArtifact;
|
|
9544
9553
|
}
|
|
9554
|
+
/**
|
|
9555
|
+
* Strong, explicit signals that a task needs the full hierarchy (planning +
|
|
9556
|
+
* multiple sections/workers). Deliberately conservative: requires a
|
|
9557
|
+
* build/implementation verb AND either an app/system-scale noun or an
|
|
9558
|
+
* explicit multi-part structure, so ordinary single-file asks (handled as
|
|
9559
|
+
* Simple/Moderate) don't get over-escalated.
|
|
9560
|
+
*/
|
|
9561
|
+
looksClearlyComplex(prompt) {
|
|
9562
|
+
const p = prompt.trim();
|
|
9563
|
+
if (p.length < 24) return false;
|
|
9564
|
+
const buildVerb = /\b(?:build|implement|create|develop|design|scaffold|refactor|migrate|architect|set up|integrate)\b/i.test(p);
|
|
9565
|
+
const scaleNoun = /\b(?:app(?:lication)?|system|platform|service|api|backend|frontend|full[- ]?stack|website|dashboard|pipeline|microservices?|database schema|authentication|end[- ]to[- ]end|codebase|project|multiple files|several (?:files|modules|components)|test suite)\b/i.test(p);
|
|
9566
|
+
const multiPart = /(?:\b(?:and|then|also|plus|as well as)\b.*\b(?:and|then|also)\b)|(?:^|\n)\s*(?:[-*]|\d+[.)])\s+/i.test(p);
|
|
9567
|
+
return buildVerb && (scaleNoun || multiPart);
|
|
9568
|
+
}
|
|
9545
9569
|
// Cache glob scan results per workspace path to avoid repeated I/O.
|
|
9546
9570
|
static globCache = /* @__PURE__ */ new Map();
|
|
9547
9571
|
async countWorkspaceFiles(workspacePath) {
|
|
@@ -9625,10 +9649,16 @@ ${prompt}` : prompt;
|
|
|
9625
9649
|
let verdict;
|
|
9626
9650
|
if (match) {
|
|
9627
9651
|
verdict = match[1] === "simple" ? "Simple" : match[1] === "moderate" ? "Moderate" : "Complex";
|
|
9628
|
-
|
|
9652
|
+
if (verdict !== "Complex" && this.looksClearlyComplex(prompt)) {
|
|
9653
|
+
this.recordDecision("complexity", `Complex \u2014 heuristic floor over classifier "${verdict}": explicit multi-step build/implementation signals (T1 engaged)`);
|
|
9654
|
+
verdict = "Complex";
|
|
9655
|
+
} else {
|
|
9656
|
+
this.recordDecision("complexity", `${verdict} \u2014 classifier: ${reason || "no reason given"}`);
|
|
9657
|
+
}
|
|
9629
9658
|
} else {
|
|
9630
|
-
|
|
9631
|
-
|
|
9659
|
+
const words = prompt.trim().split(/\s+/).length;
|
|
9660
|
+
verdict = words <= 12 ? "Simple" : words >= 40 || this.looksClearlyComplex(prompt) ? "Complex" : "Moderate";
|
|
9661
|
+
this.recordDecision("complexity", `${verdict} \u2014 classifier output unparseable; defaulted by length/signals`);
|
|
9632
9662
|
}
|
|
9633
9663
|
return verdict;
|
|
9634
9664
|
} catch {
|
|
@@ -9680,7 +9710,15 @@ ${prompt}` : prompt;
|
|
|
9680
9710
|
}
|
|
9681
9711
|
escalator.resolveUserDecision(req.id, approved, always);
|
|
9682
9712
|
});
|
|
9683
|
-
const
|
|
9713
|
+
const forceTier = this.config.routing?.forceTier;
|
|
9714
|
+
const forced = forceTier === "T1" ? "Complex" : forceTier === "T2" ? "Moderate" : forceTier === "T3" ? "Simple" : void 0;
|
|
9715
|
+
let complexity;
|
|
9716
|
+
if (forced) {
|
|
9717
|
+
complexity = forced;
|
|
9718
|
+
this.recordDecision("complexity", `${forced} \u2014 manually forced via routing.forceTier=${forceTier}`);
|
|
9719
|
+
} else {
|
|
9720
|
+
complexity = await this.determineComplexity(options.prompt, options.workspacePath || process.cwd(), options.conversationHistory);
|
|
9721
|
+
}
|
|
9684
9722
|
this.telemetry.capture("cascade:session_start", {
|
|
9685
9723
|
complexity,
|
|
9686
9724
|
providerCount: this.config.providers.length,
|
|
@@ -9760,6 +9798,7 @@ ${prompt}` : prompt;
|
|
|
9760
9798
|
try {
|
|
9761
9799
|
if (complexity === "Simple") {
|
|
9762
9800
|
const t3 = new T3Worker(this.router, this.toolRegistry, "root");
|
|
9801
|
+
t3.setPresenter(true);
|
|
9763
9802
|
t3.setHierarchyContext("You are the DIRECT worker for this task. There is no T1 Administrator or T2 Manager involved in this run.");
|
|
9764
9803
|
if (identityPrompt) {
|
|
9765
9804
|
t3.setSystemPromptOverride(identityPrompt);
|
|
@@ -9784,6 +9823,7 @@ ${prompt}` : prompt;
|
|
|
9784
9823
|
this.emit("tier:status", { tierId: "t3-root", status: "COMPLETED", role: "T3" });
|
|
9785
9824
|
} else if (complexity === "Moderate") {
|
|
9786
9825
|
const t2 = new T2Manager(this.router, this.toolRegistry, "root");
|
|
9826
|
+
t2.setPresenter(true);
|
|
9787
9827
|
t2.setHierarchyContext("You are the ROOT Manager for this task. There is no T1 Administrator involved in this run. You are responsible for decomposing the task and managing T3 workers directly.");
|
|
9788
9828
|
if (identityPrompt) {
|
|
9789
9829
|
t2.setSystemPromptOverride(identityPrompt);
|
|
@@ -9833,6 +9873,7 @@ ${prompt}` : prompt;
|
|
|
9833
9873
|
}
|
|
9834
9874
|
} else {
|
|
9835
9875
|
const t1 = new T1Administrator(this.router, this.toolRegistry, this.config);
|
|
9876
|
+
t1.setPresenter(true);
|
|
9836
9877
|
t1.setHierarchyContext("You are the top-level Administrator. You are responsible for the overall plan and supervising multiple T2 Managers.");
|
|
9837
9878
|
if (identityPrompt) {
|
|
9838
9879
|
t1.setSystemPromptOverride(identityPrompt);
|
|
@@ -11290,7 +11331,8 @@ var DashboardSocket = class {
|
|
|
11290
11331
|
socket.on("cascade:run", (payload) => {
|
|
11291
11332
|
if (typeof payload?.prompt === "string" && payload.prompt.trim()) {
|
|
11292
11333
|
const sessionId = typeof payload.sessionId === "string" && payload.sessionId.trim() ? payload.sessionId.trim() : void 0;
|
|
11293
|
-
|
|
11334
|
+
const forceTier = ["T1", "T2", "T3"].includes(payload.forceTier) ? payload.forceTier : void 0;
|
|
11335
|
+
callback(payload.prompt.trim(), payload.model ?? "auto", socket.id, sessionId, forceTier);
|
|
11294
11336
|
}
|
|
11295
11337
|
});
|
|
11296
11338
|
});
|
|
@@ -11317,6 +11359,13 @@ var DashboardServer = class {
|
|
|
11317
11359
|
* of runs the session performed in this server's lifetime.
|
|
11318
11360
|
*/
|
|
11319
11361
|
sessionTaskIds = /* @__PURE__ */ new Map();
|
|
11362
|
+
/**
|
|
11363
|
+
* Tool-approval requests awaiting a user decision from a connected client,
|
|
11364
|
+
* keyed by the request's uuid. The desktop shows a modal on
|
|
11365
|
+
* `permission:user-required` and answers with `permission:decision`; this
|
|
11366
|
+
* map is how that answer reaches the run that's blocked on it.
|
|
11367
|
+
*/
|
|
11368
|
+
pendingApprovals = /* @__PURE__ */ new Map();
|
|
11320
11369
|
port;
|
|
11321
11370
|
host;
|
|
11322
11371
|
workspacePath;
|
|
@@ -11375,17 +11424,18 @@ var DashboardServer = class {
|
|
|
11375
11424
|
}
|
|
11376
11425
|
this.persistConfig();
|
|
11377
11426
|
});
|
|
11378
|
-
this.socket.onCascadeRun(async (prompt, model, socketId, requestedSessionId) => {
|
|
11427
|
+
this.socket.onCascadeRun(async (prompt, model, socketId, requestedSessionId, forceTier) => {
|
|
11379
11428
|
const sessionId = requestedSessionId ?? crypto3.randomUUID();
|
|
11380
11429
|
const abortController = new AbortController();
|
|
11381
11430
|
this.activeControllers.set(sessionId, abortController);
|
|
11382
11431
|
const runPrompt = requestedSessionId ? this.buildContinuationPrompt(sessionId, prompt) : prompt;
|
|
11383
11432
|
const title = this.persistRunStart(sessionId, prompt);
|
|
11384
|
-
|
|
11433
|
+
let cfg = model !== "auto" ? { ...this.config, models: { ...this.config.models, t1: model } } : this.config;
|
|
11434
|
+
if (forceTier) cfg = { ...cfg, routing: { ...cfg.routing, forceTier } };
|
|
11385
11435
|
const cascade = new Cascade(cfg, this.workspacePath, this.store);
|
|
11386
11436
|
this.activeSessions.set(sessionId, cascade);
|
|
11387
11437
|
cascade.on("stream:token", (e) => {
|
|
11388
|
-
this.socket.emitToSocket(socketId, "stream:token", { sessionId, tierId: e.tierId, text: e.text });
|
|
11438
|
+
this.socket.emitToSocket(socketId, "stream:token", { sessionId, tierId: e.tierId, text: e.text, primary: e.primary });
|
|
11389
11439
|
});
|
|
11390
11440
|
cascade.on("tier:status", (e) => {
|
|
11391
11441
|
this.socket.emitToSocket(socketId, "tier:status", { sessionId, ...e });
|
|
@@ -11397,7 +11447,11 @@ var DashboardServer = class {
|
|
|
11397
11447
|
this.socket.emitPeerMessage(e);
|
|
11398
11448
|
});
|
|
11399
11449
|
try {
|
|
11400
|
-
const result = await cascade.run({
|
|
11450
|
+
const result = await cascade.run({
|
|
11451
|
+
prompt: runPrompt,
|
|
11452
|
+
signal: abortController.signal,
|
|
11453
|
+
approvalCallback: this.makeApprovalCallback(sessionId)
|
|
11454
|
+
});
|
|
11401
11455
|
this.recordSessionTask(sessionId, result.taskId);
|
|
11402
11456
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED");
|
|
11403
11457
|
this.socket.emitToSocket(socketId, "session:complete", { sessionId, result });
|
|
@@ -11416,10 +11470,18 @@ var DashboardServer = class {
|
|
|
11416
11470
|
} finally {
|
|
11417
11471
|
this.activeSessions.delete(sessionId);
|
|
11418
11472
|
this.activeControllers.delete(sessionId);
|
|
11473
|
+
this.denyPendingApprovals(sessionId);
|
|
11419
11474
|
}
|
|
11420
11475
|
});
|
|
11421
11476
|
this.socket.onSessionHalt((sessionId) => {
|
|
11422
11477
|
this.activeControllers.get(sessionId)?.abort();
|
|
11478
|
+
this.denyPendingApprovals(sessionId);
|
|
11479
|
+
});
|
|
11480
|
+
this.socket.onApprovalResponse(({ requestId, approved, always }) => {
|
|
11481
|
+
const pending = this.pendingApprovals.get(requestId);
|
|
11482
|
+
if (!pending) return;
|
|
11483
|
+
this.pendingApprovals.delete(requestId);
|
|
11484
|
+
pending.resolve({ approved: !!approved, always: !!always });
|
|
11423
11485
|
});
|
|
11424
11486
|
this.socket.onSessionSteer((message, sessionId, nodeId) => {
|
|
11425
11487
|
const steered = this.steerSessions(message, sessionId, nodeId);
|
|
@@ -11587,6 +11649,29 @@ var DashboardServer = class {
|
|
|
11587
11649
|
list.push(taskId);
|
|
11588
11650
|
this.sessionTaskIds.set(sessionId, list);
|
|
11589
11651
|
}
|
|
11652
|
+
/**
|
|
11653
|
+
* Approval bridge: cascade calls this when a dangerous tool escalates to the
|
|
11654
|
+
* user. The request itself was already pushed to the client via the
|
|
11655
|
+
* `permission:user-required` forward; here we just park a resolver keyed by
|
|
11656
|
+
* the request id and wait for the client's `permission:decision` (handled in
|
|
11657
|
+
* onApprovalResponse). Never auto-approves — an unanswered request stays
|
|
11658
|
+
* pending until the client answers, the run ends, or the escalator's own
|
|
11659
|
+
* timeout denies it.
|
|
11660
|
+
*/
|
|
11661
|
+
makeApprovalCallback(sessionId) {
|
|
11662
|
+
return (request) => new Promise((resolve) => {
|
|
11663
|
+
this.pendingApprovals.set(request.id, { resolve, sessionId });
|
|
11664
|
+
});
|
|
11665
|
+
}
|
|
11666
|
+
/** Deny + clear any approvals still pending for a session (run end / abort). */
|
|
11667
|
+
denyPendingApprovals(sessionId) {
|
|
11668
|
+
for (const [id, pending] of this.pendingApprovals) {
|
|
11669
|
+
if (pending.sessionId === sessionId) {
|
|
11670
|
+
this.pendingApprovals.delete(id);
|
|
11671
|
+
pending.resolve({ approved: false, always: false });
|
|
11672
|
+
}
|
|
11673
|
+
}
|
|
11674
|
+
}
|
|
11590
11675
|
persistRuntimeRow(sessionId, title, status, latestPrompt) {
|
|
11591
11676
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11592
11677
|
const row = { sessionId, title, workspacePath: this.workspacePath, status, startedAt: now, updatedAt: now, latestPrompt, isGlobal: false };
|
|
@@ -11775,15 +11860,6 @@ ${prompt}`;
|
|
|
11775
11860
|
if (sessionId) this.socket.broadcastToRoom(`session:${sessionId}`, "session:halt", payload);
|
|
11776
11861
|
res.json({ success: true, ...payload });
|
|
11777
11862
|
});
|
|
11778
|
-
this.app.post("/api/approve", auth, mutationLimiter, (req, res) => {
|
|
11779
|
-
const body = req.body;
|
|
11780
|
-
const sessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : void 0;
|
|
11781
|
-
const nodeId = typeof body["nodeId"] === "string" ? body["nodeId"] : void 0;
|
|
11782
|
-
const payload = { sessionId, nodeId, requestedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
11783
|
-
this.socket.broadcast("session:approve", payload);
|
|
11784
|
-
if (sessionId) this.socket.broadcastToRoom(`session:${sessionId}`, "session:approve", payload);
|
|
11785
|
-
res.json({ success: true, ...payload });
|
|
11786
|
-
});
|
|
11787
11863
|
this.app.post("/api/inject", auth, mutationLimiter, (req, res) => {
|
|
11788
11864
|
const body = req.body;
|
|
11789
11865
|
const message = typeof body["message"] === "string" ? body["message"] : void 0;
|
|
@@ -12034,7 +12110,7 @@ ${prompt}`;
|
|
|
12034
12110
|
const cascade = new Cascade(this.config, this.workspacePath, this.store);
|
|
12035
12111
|
this.activeSessions.set(sessionId, cascade);
|
|
12036
12112
|
cascade.on("stream:token", (e) => {
|
|
12037
|
-
this.socket.broadcastToRoom(`session:${sessionId}`, "stream:token", { sessionId, tierId: e.tierId, text: e.text });
|
|
12113
|
+
this.socket.broadcastToRoom(`session:${sessionId}`, "stream:token", { sessionId, tierId: e.tierId, text: e.text, primary: e.primary });
|
|
12038
12114
|
});
|
|
12039
12115
|
cascade.on("tier:status", (e) => {
|
|
12040
12116
|
this.socket.broadcastToRoom(`session:${sessionId}`, "tier:status", { sessionId, ...e });
|
|
@@ -12046,7 +12122,11 @@ ${prompt}`;
|
|
|
12046
12122
|
this.socket.emitPeerMessage(e);
|
|
12047
12123
|
});
|
|
12048
12124
|
try {
|
|
12049
|
-
const result = await cascade.run({
|
|
12125
|
+
const result = await cascade.run({
|
|
12126
|
+
prompt: runPrompt,
|
|
12127
|
+
identityId: body.identityId,
|
|
12128
|
+
approvalCallback: this.makeApprovalCallback(sessionId)
|
|
12129
|
+
});
|
|
12050
12130
|
this.recordSessionTask(sessionId, result.taskId);
|
|
12051
12131
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED");
|
|
12052
12132
|
this.socket.broadcast("cost:update", {
|
|
@@ -12064,6 +12144,7 @@ ${prompt}`;
|
|
|
12064
12144
|
});
|
|
12065
12145
|
} finally {
|
|
12066
12146
|
this.activeSessions.delete(sessionId);
|
|
12147
|
+
this.denyPendingApprovals(sessionId);
|
|
12067
12148
|
}
|
|
12068
12149
|
})();
|
|
12069
12150
|
});
|