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/cli.js
CHANGED
|
@@ -58,7 +58,7 @@ var __export = (target, all) => {
|
|
|
58
58
|
var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SECRET_FILE, GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_RUNTIME_DB_FILE, DEFAULT_DASHBOARD_PORT, DEFAULT_CONTEXT_LIMIT, DEFAULT_AUTO_SUMMARIZE_AT, MODELS, T1_MODEL_PRIORITY, T2_MODEL_PRIORITY, T3_MODEL_PRIORITY, VISION_MODEL_PRIORITY, COMPLEXITY_T2_COUNT, THEME_NAMES, DEFAULT_THEME, OLLAMA_BASE_URL, LM_STUDIO_BASE_URL, AZURE_BASE_URL_TEMPLATE, TOOL_NAMES, DEFAULT_APPROVAL_REQUIRED;
|
|
59
59
|
var init_constants = __esm({
|
|
60
60
|
"src/constants.ts"() {
|
|
61
|
-
CASCADE_VERSION = "0.13.
|
|
61
|
+
CASCADE_VERSION = "0.13.2";
|
|
62
62
|
CASCADE_CONFIG_FILE = ".cascade/config.json";
|
|
63
63
|
CASCADE_DB_FILE = ".cascade/memory.db";
|
|
64
64
|
CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
|
|
@@ -2989,6 +2989,8 @@ var CascadeConfigSchema = z.object({
|
|
|
2989
2989
|
privacy: z.object({
|
|
2990
2990
|
paths: z.array(z.object({ pattern: z.string().min(1), policy: z.enum(["local-only"]) })).default([])
|
|
2991
2991
|
}).optional(),
|
|
2992
|
+
/** Routing controls — forceTier pins the root tier, bypassing the classifier. */
|
|
2993
|
+
routing: z.object({ forceTier: z.enum(["auto", "T1", "T2", "T3"]).default("auto") }).optional(),
|
|
2992
2994
|
/**
|
|
2993
2995
|
* T3→T2 reinforcement: when enabled, a worker that discovers its subtask should
|
|
2994
2996
|
* fan out can call the `request_workers` tool to have its T2 manager spawn
|
|
@@ -4974,6 +4976,13 @@ var BaseTier = class extends EventEmitter {
|
|
|
4974
4976
|
hierarchyContext = "";
|
|
4975
4977
|
/** Propagated AbortSignal — set by the tier's `execute()` before work begins. */
|
|
4976
4978
|
signal;
|
|
4979
|
+
/**
|
|
4980
|
+
* True for the run's ROOT tier (T3 for Simple, T2 for Moderate, T1 for
|
|
4981
|
+
* Complex). Its own synthesis stream is the user-facing answer and is
|
|
4982
|
+
* tagged `primary` so the desktop renders it live — background workers,
|
|
4983
|
+
* which would interleave, are not tagged.
|
|
4984
|
+
*/
|
|
4985
|
+
isPresenter = false;
|
|
4977
4986
|
constructor(role, id, parentId) {
|
|
4978
4987
|
super();
|
|
4979
4988
|
this.role = role;
|
|
@@ -4981,6 +4990,10 @@ var BaseTier = class extends EventEmitter {
|
|
|
4981
4990
|
this.parentId = parentId;
|
|
4982
4991
|
this.label = this.id;
|
|
4983
4992
|
}
|
|
4993
|
+
/** Mark this tier as the run's presenter (root tier). */
|
|
4994
|
+
setPresenter(on = true) {
|
|
4995
|
+
this.isPresenter = on;
|
|
4996
|
+
}
|
|
4984
4997
|
getStatus() {
|
|
4985
4998
|
return this.status;
|
|
4986
4999
|
}
|
|
@@ -5716,7 +5729,7 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : "") + textToolSuffix,
|
|
|
5716
5729
|
"T3",
|
|
5717
5730
|
options,
|
|
5718
5731
|
(chunk) => {
|
|
5719
|
-
this.emit("stream:token", { tierId: this.id, text: chunk.text });
|
|
5732
|
+
this.emit("stream:token", { tierId: this.id, text: chunk.text, primary: this.isPresenter });
|
|
5720
5733
|
}
|
|
5721
5734
|
);
|
|
5722
5735
|
let effectiveToolCalls = result.toolCalls ?? [];
|
|
@@ -7177,6 +7190,7 @@ ${peerOutputs}` : "";
|
|
|
7177
7190
|
chunkEnd++;
|
|
7178
7191
|
}
|
|
7179
7192
|
i = chunkEnd;
|
|
7193
|
+
const isLastChunk = chunkEnd >= completed.length;
|
|
7180
7194
|
const prompt = `Summarize these T3 worker outputs for section "${assignment.sectionTitle}" in 2-3 sentences.
|
|
7181
7195
|
${currentSummary ? `
|
|
7182
7196
|
PREVIOUS SUMMARY SO FAR:
|
|
@@ -7186,6 +7200,7 @@ NEW OUTPUTS TO INTEGRATE:
|
|
|
7186
7200
|
` : "\nOUTPUTS:\n"}${chunkText}${peerContext}`;
|
|
7187
7201
|
const messages = [{ role: "user", content: prompt }];
|
|
7188
7202
|
try {
|
|
7203
|
+
const streamFinal = isLastChunk && this.isPresenter ? (chunk) => this.emit("stream:token", { tierId: this.id, text: chunk.text, primary: true }) : void 0;
|
|
7189
7204
|
const result = await this.router.generate("T2", {
|
|
7190
7205
|
messages,
|
|
7191
7206
|
systemPrompt: this.systemPromptOverride + "You are a T2 Manager. Summarize the work of your T3 workers succinctly." + (this.hierarchyContext ? `
|
|
@@ -7193,7 +7208,7 @@ NEW OUTPUTS TO INTEGRATE:
|
|
|
7193
7208
|
HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
7194
7209
|
maxTokens: 500,
|
|
7195
7210
|
...this.sectionModel ? { model: this.sectionModel } : {}
|
|
7196
|
-
});
|
|
7211
|
+
}, streamFinal);
|
|
7197
7212
|
currentSummary = result.content;
|
|
7198
7213
|
} catch (err) {
|
|
7199
7214
|
this.log(`aggregateResults: LLM summarization failed at chunk \u2014 returning raw T3 outputs. Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -7245,14 +7260,11 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
7245
7260
|
...this.sectionModel ? { model: this.sectionModel } : {}
|
|
7246
7261
|
});
|
|
7247
7262
|
const answer = result.content.trim().toUpperCase();
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
}
|
|
7251
|
-
if (answer.includes("NO")) {
|
|
7252
|
-
return { requestId: req.id, approved: false, always: true, decidedBy: "T2", reasoning: "T2 LLM evaluated: inconsistent with section goal" };
|
|
7253
|
-
}
|
|
7263
|
+
const verdict = answer.includes("YES") ? "approve" : answer.includes("NO") ? "deny" : "unsure";
|
|
7264
|
+
(req.trail ??= []).push({ tier: "T2", verdict, reason: `T2: ${verdict === "approve" ? "consistent with section goal" : verdict === "deny" ? "inconsistent with section goal" : "unsure"}` });
|
|
7254
7265
|
return null;
|
|
7255
7266
|
} catch {
|
|
7267
|
+
(req.trail ??= []).push({ tier: "T2", verdict: "unsure", reason: "T2 evaluation failed" });
|
|
7256
7268
|
return null;
|
|
7257
7269
|
}
|
|
7258
7270
|
}
|
|
@@ -7934,7 +7946,7 @@ Instructions:
|
|
|
7934
7946
|
systemPrompt: this.systemPromptOverride + "You are a final output compiler. Summarize and format the task results clearly.",
|
|
7935
7947
|
maxTokens: 8e3
|
|
7936
7948
|
}, (chunk) => {
|
|
7937
|
-
this.emit("stream:token", { tierId: this.id, text: chunk.text });
|
|
7949
|
+
this.emit("stream:token", { tierId: this.id, text: chunk.text, primary: this.isPresenter });
|
|
7938
7950
|
});
|
|
7939
7951
|
return result.content;
|
|
7940
7952
|
}
|
|
@@ -7963,14 +7975,11 @@ Reply with exactly one word: YES, NO, or UNSURE.
|
|
|
7963
7975
|
temperature: 0
|
|
7964
7976
|
});
|
|
7965
7977
|
const answer = result.content.trim().toUpperCase();
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
}
|
|
7969
|
-
if (answer.includes("NO")) {
|
|
7970
|
-
return { requestId: req.id, approved: false, always: true, decidedBy: "T1", reasoning: "T1 evaluated: not consistent with overall task goal" };
|
|
7971
|
-
}
|
|
7978
|
+
const verdict = answer.includes("YES") ? "approve" : answer.includes("NO") ? "deny" : "unsure";
|
|
7979
|
+
(req.trail ??= []).push({ tier: "T1", verdict, reason: `T1: ${verdict === "approve" ? "consistent with overall task goal" : verdict === "deny" ? "not consistent with overall task goal" : "unsure"}` });
|
|
7972
7980
|
return null;
|
|
7973
7981
|
} catch {
|
|
7982
|
+
(req.trail ??= []).push({ tier: "T1", verdict: "unsure", reason: "T1 evaluation failed" });
|
|
7974
7983
|
return null;
|
|
7975
7984
|
}
|
|
7976
7985
|
}
|
|
@@ -11066,6 +11075,21 @@ ${last.partialOutput}` : "");
|
|
|
11066
11075
|
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);
|
|
11067
11076
|
return inquiry && !producesArtifact;
|
|
11068
11077
|
}
|
|
11078
|
+
/**
|
|
11079
|
+
* Strong, explicit signals that a task needs the full hierarchy (planning +
|
|
11080
|
+
* multiple sections/workers). Deliberately conservative: requires a
|
|
11081
|
+
* build/implementation verb AND either an app/system-scale noun or an
|
|
11082
|
+
* explicit multi-part structure, so ordinary single-file asks (handled as
|
|
11083
|
+
* Simple/Moderate) don't get over-escalated.
|
|
11084
|
+
*/
|
|
11085
|
+
looksClearlyComplex(prompt) {
|
|
11086
|
+
const p = prompt.trim();
|
|
11087
|
+
if (p.length < 24) return false;
|
|
11088
|
+
const buildVerb = /\b(?:build|implement|create|develop|design|scaffold|refactor|migrate|architect|set up|integrate)\b/i.test(p);
|
|
11089
|
+
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);
|
|
11090
|
+
const multiPart = /(?:\b(?:and|then|also|plus|as well as)\b.*\b(?:and|then|also)\b)|(?:^|\n)\s*(?:[-*]|\d+[.)])\s+/i.test(p);
|
|
11091
|
+
return buildVerb && (scaleNoun || multiPart);
|
|
11092
|
+
}
|
|
11069
11093
|
// Cache glob scan results per workspace path to avoid repeated I/O.
|
|
11070
11094
|
static globCache = /* @__PURE__ */ new Map();
|
|
11071
11095
|
async countWorkspaceFiles(workspacePath) {
|
|
@@ -11149,10 +11173,16 @@ ${prompt}` : prompt;
|
|
|
11149
11173
|
let verdict;
|
|
11150
11174
|
if (match) {
|
|
11151
11175
|
verdict = match[1] === "simple" ? "Simple" : match[1] === "moderate" ? "Moderate" : "Complex";
|
|
11152
|
-
|
|
11176
|
+
if (verdict !== "Complex" && this.looksClearlyComplex(prompt)) {
|
|
11177
|
+
this.recordDecision("complexity", `Complex \u2014 heuristic floor over classifier "${verdict}": explicit multi-step build/implementation signals (T1 engaged)`);
|
|
11178
|
+
verdict = "Complex";
|
|
11179
|
+
} else {
|
|
11180
|
+
this.recordDecision("complexity", `${verdict} \u2014 classifier: ${reason || "no reason given"}`);
|
|
11181
|
+
}
|
|
11153
11182
|
} else {
|
|
11154
|
-
|
|
11155
|
-
|
|
11183
|
+
const words = prompt.trim().split(/\s+/).length;
|
|
11184
|
+
verdict = words <= 12 ? "Simple" : words >= 40 || this.looksClearlyComplex(prompt) ? "Complex" : "Moderate";
|
|
11185
|
+
this.recordDecision("complexity", `${verdict} \u2014 classifier output unparseable; defaulted by length/signals`);
|
|
11156
11186
|
}
|
|
11157
11187
|
return verdict;
|
|
11158
11188
|
} catch {
|
|
@@ -11204,7 +11234,15 @@ ${prompt}` : prompt;
|
|
|
11204
11234
|
}
|
|
11205
11235
|
escalator.resolveUserDecision(req.id, approved, always);
|
|
11206
11236
|
});
|
|
11207
|
-
const
|
|
11237
|
+
const forceTier = this.config.routing?.forceTier;
|
|
11238
|
+
const forced = forceTier === "T1" ? "Complex" : forceTier === "T2" ? "Moderate" : forceTier === "T3" ? "Simple" : void 0;
|
|
11239
|
+
let complexity;
|
|
11240
|
+
if (forced) {
|
|
11241
|
+
complexity = forced;
|
|
11242
|
+
this.recordDecision("complexity", `${forced} \u2014 manually forced via routing.forceTier=${forceTier}`);
|
|
11243
|
+
} else {
|
|
11244
|
+
complexity = await this.determineComplexity(options.prompt, options.workspacePath || process.cwd(), options.conversationHistory);
|
|
11245
|
+
}
|
|
11208
11246
|
this.telemetry.capture("cascade:session_start", {
|
|
11209
11247
|
complexity,
|
|
11210
11248
|
providerCount: this.config.providers.length,
|
|
@@ -11284,6 +11322,7 @@ ${prompt}` : prompt;
|
|
|
11284
11322
|
try {
|
|
11285
11323
|
if (complexity === "Simple") {
|
|
11286
11324
|
const t3 = new T3Worker(this.router, this.toolRegistry, "root");
|
|
11325
|
+
t3.setPresenter(true);
|
|
11287
11326
|
t3.setHierarchyContext("You are the DIRECT worker for this task. There is no T1 Administrator or T2 Manager involved in this run.");
|
|
11288
11327
|
if (identityPrompt) {
|
|
11289
11328
|
t3.setSystemPromptOverride(identityPrompt);
|
|
@@ -11308,6 +11347,7 @@ ${prompt}` : prompt;
|
|
|
11308
11347
|
this.emit("tier:status", { tierId: "t3-root", status: "COMPLETED", role: "T3" });
|
|
11309
11348
|
} else if (complexity === "Moderate") {
|
|
11310
11349
|
const t2 = new T2Manager(this.router, this.toolRegistry, "root");
|
|
11350
|
+
t2.setPresenter(true);
|
|
11311
11351
|
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.");
|
|
11312
11352
|
if (identityPrompt) {
|
|
11313
11353
|
t2.setSystemPromptOverride(identityPrompt);
|
|
@@ -11357,6 +11397,7 @@ ${prompt}` : prompt;
|
|
|
11357
11397
|
}
|
|
11358
11398
|
} else {
|
|
11359
11399
|
const t1 = new T1Administrator(this.router, this.toolRegistry, this.config);
|
|
11400
|
+
t1.setPresenter(true);
|
|
11360
11401
|
t1.setHierarchyContext("You are the top-level Administrator. You are responsible for the overall plan and supervising multiple T2 Managers.");
|
|
11361
11402
|
if (identityPrompt) {
|
|
11362
11403
|
t1.setSystemPromptOverride(identityPrompt);
|
|
@@ -15357,7 +15398,8 @@ var DashboardSocket = class {
|
|
|
15357
15398
|
socket.on("cascade:run", (payload) => {
|
|
15358
15399
|
if (typeof payload?.prompt === "string" && payload.prompt.trim()) {
|
|
15359
15400
|
const sessionId = typeof payload.sessionId === "string" && payload.sessionId.trim() ? payload.sessionId.trim() : void 0;
|
|
15360
|
-
|
|
15401
|
+
const forceTier = ["T1", "T2", "T3"].includes(payload.forceTier) ? payload.forceTier : void 0;
|
|
15402
|
+
callback(payload.prompt.trim(), payload.model ?? "auto", socket.id, sessionId, forceTier);
|
|
15361
15403
|
}
|
|
15362
15404
|
});
|
|
15363
15405
|
});
|
|
@@ -15387,6 +15429,13 @@ var DashboardServer = class {
|
|
|
15387
15429
|
* of runs the session performed in this server's lifetime.
|
|
15388
15430
|
*/
|
|
15389
15431
|
sessionTaskIds = /* @__PURE__ */ new Map();
|
|
15432
|
+
/**
|
|
15433
|
+
* Tool-approval requests awaiting a user decision from a connected client,
|
|
15434
|
+
* keyed by the request's uuid. The desktop shows a modal on
|
|
15435
|
+
* `permission:user-required` and answers with `permission:decision`; this
|
|
15436
|
+
* map is how that answer reaches the run that's blocked on it.
|
|
15437
|
+
*/
|
|
15438
|
+
pendingApprovals = /* @__PURE__ */ new Map();
|
|
15390
15439
|
port;
|
|
15391
15440
|
host;
|
|
15392
15441
|
workspacePath;
|
|
@@ -15445,17 +15494,18 @@ var DashboardServer = class {
|
|
|
15445
15494
|
}
|
|
15446
15495
|
this.persistConfig();
|
|
15447
15496
|
});
|
|
15448
|
-
this.socket.onCascadeRun(async (prompt, model, socketId, requestedSessionId) => {
|
|
15497
|
+
this.socket.onCascadeRun(async (prompt, model, socketId, requestedSessionId, forceTier) => {
|
|
15449
15498
|
const sessionId = requestedSessionId ?? randomUUID();
|
|
15450
15499
|
const abortController = new AbortController();
|
|
15451
15500
|
this.activeControllers.set(sessionId, abortController);
|
|
15452
15501
|
const runPrompt = requestedSessionId ? this.buildContinuationPrompt(sessionId, prompt) : prompt;
|
|
15453
15502
|
const title = this.persistRunStart(sessionId, prompt);
|
|
15454
|
-
|
|
15503
|
+
let cfg = model !== "auto" ? { ...this.config, models: { ...this.config.models, t1: model } } : this.config;
|
|
15504
|
+
if (forceTier) cfg = { ...cfg, routing: { ...cfg.routing, forceTier } };
|
|
15455
15505
|
const cascade = new Cascade(cfg, this.workspacePath, this.store);
|
|
15456
15506
|
this.activeSessions.set(sessionId, cascade);
|
|
15457
15507
|
cascade.on("stream:token", (e) => {
|
|
15458
|
-
this.socket.emitToSocket(socketId, "stream:token", { sessionId, tierId: e.tierId, text: e.text });
|
|
15508
|
+
this.socket.emitToSocket(socketId, "stream:token", { sessionId, tierId: e.tierId, text: e.text, primary: e.primary });
|
|
15459
15509
|
});
|
|
15460
15510
|
cascade.on("tier:status", (e) => {
|
|
15461
15511
|
this.socket.emitToSocket(socketId, "tier:status", { sessionId, ...e });
|
|
@@ -15467,7 +15517,11 @@ var DashboardServer = class {
|
|
|
15467
15517
|
this.socket.emitPeerMessage(e);
|
|
15468
15518
|
});
|
|
15469
15519
|
try {
|
|
15470
|
-
const result = await cascade.run({
|
|
15520
|
+
const result = await cascade.run({
|
|
15521
|
+
prompt: runPrompt,
|
|
15522
|
+
signal: abortController.signal,
|
|
15523
|
+
approvalCallback: this.makeApprovalCallback(sessionId)
|
|
15524
|
+
});
|
|
15471
15525
|
this.recordSessionTask(sessionId, result.taskId);
|
|
15472
15526
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED");
|
|
15473
15527
|
this.socket.emitToSocket(socketId, "session:complete", { sessionId, result });
|
|
@@ -15486,10 +15540,18 @@ var DashboardServer = class {
|
|
|
15486
15540
|
} finally {
|
|
15487
15541
|
this.activeSessions.delete(sessionId);
|
|
15488
15542
|
this.activeControllers.delete(sessionId);
|
|
15543
|
+
this.denyPendingApprovals(sessionId);
|
|
15489
15544
|
}
|
|
15490
15545
|
});
|
|
15491
15546
|
this.socket.onSessionHalt((sessionId) => {
|
|
15492
15547
|
this.activeControllers.get(sessionId)?.abort();
|
|
15548
|
+
this.denyPendingApprovals(sessionId);
|
|
15549
|
+
});
|
|
15550
|
+
this.socket.onApprovalResponse(({ requestId, approved, always }) => {
|
|
15551
|
+
const pending = this.pendingApprovals.get(requestId);
|
|
15552
|
+
if (!pending) return;
|
|
15553
|
+
this.pendingApprovals.delete(requestId);
|
|
15554
|
+
pending.resolve({ approved: !!approved, always: !!always });
|
|
15493
15555
|
});
|
|
15494
15556
|
this.socket.onSessionSteer((message, sessionId, nodeId) => {
|
|
15495
15557
|
const steered = this.steerSessions(message, sessionId, nodeId);
|
|
@@ -15657,6 +15719,29 @@ var DashboardServer = class {
|
|
|
15657
15719
|
list.push(taskId);
|
|
15658
15720
|
this.sessionTaskIds.set(sessionId, list);
|
|
15659
15721
|
}
|
|
15722
|
+
/**
|
|
15723
|
+
* Approval bridge: cascade calls this when a dangerous tool escalates to the
|
|
15724
|
+
* user. The request itself was already pushed to the client via the
|
|
15725
|
+
* `permission:user-required` forward; here we just park a resolver keyed by
|
|
15726
|
+
* the request id and wait for the client's `permission:decision` (handled in
|
|
15727
|
+
* onApprovalResponse). Never auto-approves — an unanswered request stays
|
|
15728
|
+
* pending until the client answers, the run ends, or the escalator's own
|
|
15729
|
+
* timeout denies it.
|
|
15730
|
+
*/
|
|
15731
|
+
makeApprovalCallback(sessionId) {
|
|
15732
|
+
return (request) => new Promise((resolve) => {
|
|
15733
|
+
this.pendingApprovals.set(request.id, { resolve, sessionId });
|
|
15734
|
+
});
|
|
15735
|
+
}
|
|
15736
|
+
/** Deny + clear any approvals still pending for a session (run end / abort). */
|
|
15737
|
+
denyPendingApprovals(sessionId) {
|
|
15738
|
+
for (const [id, pending] of this.pendingApprovals) {
|
|
15739
|
+
if (pending.sessionId === sessionId) {
|
|
15740
|
+
this.pendingApprovals.delete(id);
|
|
15741
|
+
pending.resolve({ approved: false, always: false });
|
|
15742
|
+
}
|
|
15743
|
+
}
|
|
15744
|
+
}
|
|
15660
15745
|
persistRuntimeRow(sessionId, title, status, latestPrompt) {
|
|
15661
15746
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15662
15747
|
const row = { sessionId, title, workspacePath: this.workspacePath, status, startedAt: now, updatedAt: now, latestPrompt, isGlobal: false };
|
|
@@ -15845,15 +15930,6 @@ ${prompt}`;
|
|
|
15845
15930
|
if (sessionId) this.socket.broadcastToRoom(`session:${sessionId}`, "session:halt", payload);
|
|
15846
15931
|
res.json({ success: true, ...payload });
|
|
15847
15932
|
});
|
|
15848
|
-
this.app.post("/api/approve", auth, mutationLimiter, (req, res) => {
|
|
15849
|
-
const body = req.body;
|
|
15850
|
-
const sessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : void 0;
|
|
15851
|
-
const nodeId = typeof body["nodeId"] === "string" ? body["nodeId"] : void 0;
|
|
15852
|
-
const payload = { sessionId, nodeId, requestedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
15853
|
-
this.socket.broadcast("session:approve", payload);
|
|
15854
|
-
if (sessionId) this.socket.broadcastToRoom(`session:${sessionId}`, "session:approve", payload);
|
|
15855
|
-
res.json({ success: true, ...payload });
|
|
15856
|
-
});
|
|
15857
15933
|
this.app.post("/api/inject", auth, mutationLimiter, (req, res) => {
|
|
15858
15934
|
const body = req.body;
|
|
15859
15935
|
const message = typeof body["message"] === "string" ? body["message"] : void 0;
|
|
@@ -16104,7 +16180,7 @@ ${prompt}`;
|
|
|
16104
16180
|
const cascade = new Cascade(this.config, this.workspacePath, this.store);
|
|
16105
16181
|
this.activeSessions.set(sessionId, cascade);
|
|
16106
16182
|
cascade.on("stream:token", (e) => {
|
|
16107
|
-
this.socket.broadcastToRoom(`session:${sessionId}`, "stream:token", { sessionId, tierId: e.tierId, text: e.text });
|
|
16183
|
+
this.socket.broadcastToRoom(`session:${sessionId}`, "stream:token", { sessionId, tierId: e.tierId, text: e.text, primary: e.primary });
|
|
16108
16184
|
});
|
|
16109
16185
|
cascade.on("tier:status", (e) => {
|
|
16110
16186
|
this.socket.broadcastToRoom(`session:${sessionId}`, "tier:status", { sessionId, ...e });
|
|
@@ -16116,7 +16192,11 @@ ${prompt}`;
|
|
|
16116
16192
|
this.socket.emitPeerMessage(e);
|
|
16117
16193
|
});
|
|
16118
16194
|
try {
|
|
16119
|
-
const result = await cascade.run({
|
|
16195
|
+
const result = await cascade.run({
|
|
16196
|
+
prompt: runPrompt,
|
|
16197
|
+
identityId: body.identityId,
|
|
16198
|
+
approvalCallback: this.makeApprovalCallback(sessionId)
|
|
16199
|
+
});
|
|
16120
16200
|
this.recordSessionTask(sessionId, result.taskId);
|
|
16121
16201
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED");
|
|
16122
16202
|
this.socket.broadcast("cost:update", {
|
|
@@ -16134,6 +16214,7 @@ ${prompt}`;
|
|
|
16134
16214
|
});
|
|
16135
16215
|
} finally {
|
|
16136
16216
|
this.activeSessions.delete(sessionId);
|
|
16217
|
+
this.denyPendingApprovals(sessionId);
|
|
16137
16218
|
}
|
|
16138
16219
|
})();
|
|
16139
16220
|
});
|